config

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2026 License: Apache-2.0 Imports: 45 Imported by: 0

Documentation

Overview

Package config holds the harness's process-wide defaults. It is the only place a model slug or an endpoint is written down, so changing the default model is a one-line edit rather than a search across the tree.

Index

Constants

View Source
const (
	KeyBashBackgroundAfter     = "bash.background_after_seconds"
	DefaultBashBackgroundAfter = 30
	BashBackgroundAfterHint    = "" /* 177-byte string literal not displayed */
)

The foreground-command handoff is a session setting rather than a tool timeout: it decides when the conversation moves on while the same process keeps running. Zero deliberately leaves the older timeout-only posture.

View Source
const (
	// DefaultModel is the harness's default. DeepSeek V4 Flash Latest is the
	// floating alias, so a new dated snapshot is picked up without a code change.
	// 1M context, ~$0.09/M in and ~$0.18/M out, and it advertises both
	// structured_outputs and reasoning on OpenRouter.
	//
	// A first-prompt stall on this default names `/model` rather than hanging
	// silent (F42). The slug itself is not swapped for a more expensive one:
	// the cheap default stays, and the stall is what must recover visibly.
	DefaultModel = "~deepseek/deepseek-v4-flash-latest"

	// DefaultVoiceModel is the independent speech-to-text slot. Voice never
	// enters the talk/work router: OpenRouter exposes it through the dedicated
	// audio transcription endpoint.
	DefaultVoiceModel = "qwen/qwen3-asr-flash-2026-02-10"

	// DefaultBaseURL is OpenRouter's OpenAI-compatible endpoint.
	DefaultBaseURL = catalog.DefaultBaseURL

	// DefaultSiteURL, DefaultSiteName and DefaultSiteCategories are the
	// OpenRouter app-attribution values a RELEASE binary reports under
	// (HTTP-Referer, X-OpenRouter-Title, X-OpenRouter-Categories); a staging,
	// dev or source build reports as its own app, which
	// provider.RunningApp names. They are INTERPOLATED FROM internal/provider
	// AND NOT RE-SPELLED, because the package that writes the headers is the
	// one place the values may live; two copies of an app's identity is how one
	// product's usage ends up on two dashboard pages. Nothing resolves them from
	// settings or the environment — the app a request names is a fact about the
	// build, not an operator's preference.
	DefaultSiteURL        = provider.AppURL
	DefaultSiteName       = provider.AppName
	DefaultSiteCategories = provider.AppCategories
	// Preserve the names master exposed before the chat-v2 rollout, for any
	// caller still reaching them by the old spelling.
	OpenRouterAppURL  = DefaultSiteURL
	OpenRouterAppName = DefaultSiteName

	// DefaultDocumentEngine walks the deliberate local -> free -> rail-gated
	// OCR ladder. The other accepted values pin one rung and never fall through.
	DefaultDocumentEngine = "auto"

	// DefaultTimeout is generous because a reasoning pass can run for minutes on
	// a wide task even when the answer is short.
	DefaultTimeout = 300 * time.Second

	// DefaultReasoning IS ABSENCE, and so is [DefaultExecReasoning]. Nothing
	// about how hard a model thinks travels on a request this harness was not
	// told to shape: [provider.EffortNone] sends no `reasoning` object at all
	// and the model answers at its own published default.
	//
	// It used to be [provider.EffortOff], which is not silence but a REQUEST —
	// `{"reasoning":{"enabled":false}}` — chosen here on a latency argument
	// about planning and on an executor ablation about convergence. Both were
	// measurements of two models on two task shapes, and neither is a fact
	// about the model an operator points this binary at today: the same field
	// is a 400 on an endpoint that cannot switch thinking off, and a silent
	// downgrade on one that can. A harness that has not been asked for a level
	// asks for none.
	//
	// CODEAF_REASONING and CODEAF_EXEC_REASONING are unchanged and still take
	// off|low|medium|high — including `off`, which is how somebody who wants
	// the thinking pass actually suppressed says so and gets exactly the
	// request this default used to make on their behalf.
	DefaultReasoning = provider.EffortNone

	// DefaultExecReasoning is absence for the reason above: planning and
	// execution are different calls, and neither of them is a call this harness
	// has an opinion about the depth of.
	DefaultExecReasoning = provider.EffortNone

	// DefaultSpineSamples draws the spine more than once. It is the only call
	// whose framing every later pass inherits, so an unlucky draw does not
	// degrade the graph slightly — it replaces it. The samples run at the same
	// time, so this costs no wall clock and a fraction of a cent.
	DefaultSpineSamples = 3

	// DefaultMaxDepth bounds recursion. Depth is the only cost of decomposition
	// that is genuinely serial — a whole level expands in four call-rounds
	// however wide it is — so this is the guard that actually protects latency.
	DefaultMaxDepth = 2

	// DefaultNodeBudget is the ceiling the model cannot argue with. Every other
	// stop condition is pressure applied through a prompt; this one is
	// arithmetic, and it is what guarantees the recursion terminates.
	DefaultNodeBudget = 60

	// DefaultDailyBudgetUSD is the policy rail across every task using the
	// resident store. Token slices shape leaves internally; dollars decide when
	// new work needs the user's word. Zero disables the rail.
	//
	// IT IS DELIBERATELY LARGE — a backstop against a runaway, never a budget.
	// It sat at $20 and a single ordinary day of agent work reached it, so the
	// rail stopped being the thing that catches a loop and became the thing
	// that interrupts work: a person who had chosen no number at all was being
	// asked to raise a ceiling they never set. A rail nobody chose must only
	// fire where nobody would defend the spend, and $500 in one day is that
	// place. The number a person actually budgets with is the one they write
	// into the row themselves — and 0 there removes the rail entirely.
	DefaultDailyBudgetUSD = 500.0

	// DefaultPracticeBudgetUSD is the daily carve-out reserved for self-origin
	// curiosity work. The global rail remains an additional ceiling.
	//
	// IT IS A CARVE-OUT AND NOT A RAIL, which is why 0 reads the opposite way
	// here than it does on every other money row in this file: 0 is no practice
	// at all (internal/resident's WithPracticeLoop switches the loop off), never
	// unbounded practice. There is no way to spell "practice without a ceiling"
	// and that is deliberate — self-origin work runs while nobody is watching,
	// so it is the one pocket that always has a bottom. Raised with the rail
	// above so the carve-out is a real slice of a real day.
	DefaultPracticeBudgetUSD = 50.0

	DefaultPracticeIdle = 20 * time.Minute

	// DefaultBriefAfter keeps ordinary short breaks silent. A longer absence
	// earns one folded arrival summary when background life actually happened.
	DefaultBriefAfter = 4 * time.Hour

	// DefaultSwarm is whether cooperative decomposition is armed with nobody
	// having said anything about it. It is TRUE from the swarm-road wave on;
	// [Config.Swarm] carries the whole argument, and `CODEAF_SWARM=0` is the
	// escape hatch.
	DefaultSwarm = true
)
View Source
const (
	// KeyCrewAllowed is the allowed-models rule. PROFILE-ONLY for the worker
	// row's own reason: a repository that could widen it could send a
	// visitor's work, and their credit, to a model nobody on that machine chose.
	KeyCrewAllowed = "models.crew.allowed"
	// KeyCrewCap is the daily cap on what crews spend, in dollars; absent or
	// zero is no cap.
	KeyCrewCap = "models.crew.cap"
	// KeyCrewTaskCap is the most one task may spend, in dollars; absent or
	// zero is [CrewTaskCapDefault].
	KeyCrewTaskCap = "models.crew.task_cap"
	// KeyCrewFreeRoutes is whether the crew may use providers' free pools
	// (a `:free` route of a model). PROFILE-ONLY and OFF unless somebody
	// turned it on: a free pool may log or train on what it is sent, which is
	// a choice about a person's code, not a price.
	KeyCrewFreeRoutes = "models.crew.free_routes"
	// KeyCrewProvidersOff is the connected providers a person turned off for
	// the crew, a list of provider ids; absent is every provider on. It is a
	// row of its own beside the allowed rule and never a `-x` inside it
	// (crewroute's providers.go says why), and PROFILE-ONLY for the allowed
	// rule's reason.
	KeyCrewProvidersOff = "models.crew.providers.off"
	// KeyCrewRouteWorker, KeyCrewRoutePlanner and KeyCrewRouteChecker hold a
	// seat's pinned ROUTE, as the whole pin `model@provider`, beside the tier
	// row that holds the model alone. The route used to be written into the
	// tier row itself, and a build from before routed crews reads that row as
	// a model id and would send `model@provider` to the provider; kept apart,
	// an older build reads a valid id and simply takes its default route.
	// The whole pin is kept so a route is applied only to the model it was
	// chosen for ([CrewPinAt]). PROFILE-ONLY, like the rows beside them.
	KeyCrewRouteWorker  = "models.crew.route.worker"
	KeyCrewRoutePlanner = "models.crew.route.planner"
	KeyCrewRouteChecker = "models.crew.route.checker"
)

Crew rows, spelled once.

View Source
const (
	CrewCheckCeilingTimes = 3.0
	CrewCheckCeilingFloor = 0.05
)

The checker's ceiling: how many times its estimate, and the least it is. The manual quotes both (internal/manual's truth_test.go holds it to them).

View Source
const (
	// ProjectConfigDir is the per-repository settings directory.
	//
	// THE LIVE DIRECTORY IS .codeaf. When its config.json is absent, reads still
	// accept the former .aforge-v3/config.json, but writes always name this live // legacy-name
	// path and nothing rewrites a person's repository on its own.
	ProjectConfigDir = ".codeaf"

	// ProjectConfigFile is the one file inside it this layer reads.
	ProjectConfigFile = "config.json"
)
View Source
const (
	ModelEnv      = "CODEAF_MODEL"
	PlanModelEnv  = "CODEAF_PLAN_MODEL"
	CheckModelEnv = "CODEAF_CHECK_MODEL"
)

ModelEnv, PlanModelEnv, and CheckModelEnv are the variables the seats read. They are spelled here once because the ladder, Load, and receipts name them.

View Source
const (
	// CategoryModels is what runs the work: one row per role the router has,
	// then the capability models beside them.
	CategoryModels = "models"
	// CategorySpending is every dollar the product will spend without asking,
	// and NOTHING that is not a dollar.
	CategorySpending = "spending"
	// CategorySafety is what codeaf may do without asking you first: the
	// approval gate, its exceptions, the model that stands in for you, and the
	// two clocks that answer when nobody does.
	CategorySafety = "safety"
	// CategoryTasks is how work you can walk away from is run — how it starts,
	// how it is checked, how much of it happens at once, and on whose hands.
	CategoryTasks = "tasks"
	// CategoryTeams is what every team inherits when it says nothing of its
	// own: who answers a member's question, what a team may spend in a day,
	// and how deep teams may nest (teamdefaults.go).
	CategoryTeams = "teams"
	// CategoryPractice is what codeaf does with its own time, and what it
	// remembers of yours.
	CategoryPractice = "memory & practice"
	// CategoryInterface is how the surface draws itself, and how it signs the
	// work that leaves the machine.
	CategoryInterface = "interface"
)

Category names are the faint lowercase words the sheet may announce a section with (15). There were seven, and five of them were labels doing structure's job: `rhythm`, `documents & vision` and `sharing` each announced two rows or one, which is a header naming a mechanism rather than a section a reader could otherwise not place. Deleting them is 15's own test — the rows still read, because position and spacing already said what the word said.

SPENDING IS MONEY AND NOTHING ELSE, and that is why there are six words here rather than four. `spending` had grown to hold twenty rows answering four different questions — what may it spend, what may it run without asking, how does it run tasks, and which workers exist — so a person looking for "how much may it spend" read about load averages and repair rounds first. The three questions are three sections now, and docs/design/spending/DESIGN.md is the argument: a category is what a row is ABOUT, and a category that answers four questions is a drawer rather than a section.

View Source
const (
	CategoryMoney      = CategorySpending
	CategoryLearning   = CategoryPractice
	CategoryAppearance = CategoryInterface
)

The old spellings, kept as aliases so a surface that still names one keeps compiling while it is being ported. They are the same four words; nothing resolves to a group that no longer exists.

View Source
const (
	KeyDailyBudget    = "daily_budget_usd"
	KeyPlanConsent    = "plan_consent_usd"
	KeyPracticeBudget = "practice_budget_usd"
	KeyPracticeIdle   = "practice_idle"
	KeyBriefAfter     = "brief_after"
	KeyTenureAfter    = "tenure_after"
	KeyDocumentEngine = "document_engine"
	KeyVisionModel    = "vision_model"
	// KeyModelPool is the stored word the pool's resolver takes: the same three
	// answers the CODEAF_MODEL_POOL pin and a CI environment may give it
	// (internal/pool/poolcfg). The resolver beside the search rows is the one
	// place the process environment is read for the pool.
	KeyModelPool = "model_pool"
	// KeyModelPoolPublicKey is the trusted key a fetched pool index is checked
	// under: base64 text beside the pool row it narrows, and empty for the
	// key the binary carries. The environment pin CODEAF_MODEL_POOL_PUBLIC_KEY
	// outranks it, through the same resolver.
	KeyModelPoolPublicKey = "models.pool.public_key"
	KeyAttributionModel   = "attribution.model"
	KeySplitPct           = "split_pct"

	// The two rows the v3 chat surface keeps on disk BESIDE the conversation:
	// what was typed, and what was half-typed. They are one pair of questions —
	// "may codeaf remember my own words between sessions" — and they are two
	// rows rather than one because they answer it at different depths: history
	// is every prompt ever submitted from this machine, the draft is the single
	// unsent sentence in front of you right now, and a person who wants the
	// second without the first (or the reverse) is not confused.
	KeyHistoryEnabled = "history.enabled"
	KeyDraftPersist   = "draft.persist"

	// KeyTelemetry is the anonymous-usage switch: one row, default on, off
	// turns the whole pipe (see internal/telemetry). It sits beside the
	// history row because they answer the same question at different
	// depths — "may codeaf record what happened on this machine" — and
	// the session counters count either way: only the sending asks.
	KeyTelemetry = "telemetry"

	// The v3 session's own keys. They are DOTTED where the older ones are
	// snake_case because they name a path into a settings tree the file writer
	// will eventually hold — tools.approval is a map, models.roles is a map —
	// and the flat text rows below are the readable stand-in until it lands.
	KeyToolApprovalMode = "tools.approvalMode"
	KeyToolApprovals    = "tools.approval"
	// KeyBashApprovals is the ordered rule list for the one tool whose arguments
	// are a language. The row above answers per TOOL — "never ask me about read"
	// — and there is no useful per-tool answer for bash: a person who allowed the
	// tool would have allowed every command it will ever be handed. This row is
	// where the answer can name the command, which is what internal/approval's
	// bash patterns are for and where the consent card's "always, this command"
	// lands (approvalmemory.go).
	KeyBashApprovals = "tools.bashPatterns"

	// KeyGuardian turns on the small model that answers a tool prompt before you
	// are asked (internal/session's guardian.go). It is named under `approval.`
	// rather than beside the two `tools.` rows above because it is not a rule
	// about tools at all: it is a statement about WHO ANSWERS — the person, or a
	// model standing in for them — and grouping it with the rule rows would file
	// it as one more exception in a list of exceptions.
	KeyGuardian = "approval.guardian"
	// KeyConsentTimeout is how long an approval question counts down before it
	// PAUSES and keeps waiting. It never answers for the person — silence is
	// not a no (F41) — and it stops the moment a key is pressed, because a
	// person who has started reading is a person who is going to answer.
	//
	// Seconds, not a duration string, for the reason [KeyTaskAutoApprove] is
	// spelled that way: the number is small and read at a glance off a line that
	// is counting it down. 0 turns the clock off and the question waits from
	// the start, which is what a person who reads every prompt wants.
	KeyConsentTimeout = "approval.timeout_seconds"

	// KeyPromptProfile is how much codeaf puts in front of the model before a
	// person has typed: the whole page and the whole tool list, or the lean
	// pair a small window can afford (internal/session's promptprofile.go).
	//
	// IT IS A ROW BECAUSE THE DERIVATION CAN BE WRONG. The profile is settled
	// from the model's context window and that is right almost every time, but
	// an endpoint that reports a window its loaded model does not really have
	// leaves the person with no way to say so, and a derived state with no row
	// anywhere is a state nobody can read off a screen. `auto` is the default
	// and keeps the derivation; the other two words are the person overruling
	// it, in the same words [EnvPromptProfile] takes.
	KeyPromptProfile = "prompt.profile"

	KeyTierLowModel  = "models.tiers.low"
	KeyTierHighModel = "models.tiers.high"
	// KeyTierWorkerModel is the seat that does the work — the worker of every
	// task, the parts it hands out, the nodes of an adaptive run
	// ([roles.TierWorker]). It reads from the PROFILE ALONE, unlike the two rows
	// above it, for the reason the task model row does: a repository that could
	// answer this could send a visitor's work — and their credit — to a model
	// they never picked, by being cloned.
	KeyTierWorkerModel = "models.tiers.worker"
	// KeyTierReflexModel is the third tier, and the only one with a model in it
	// out of the box. It is read TWICE A TURN by the routing and extraction
	// calls the reflex tier exists for (internal/reflex), which is a rhythm no
	// other auxiliary call has: a model that costs a tenth of a cent a call is
	// free on the low tier and is real money here. So the row ships pointed at
	// a model that costs near nothing rather than at "follows the conversation"
	// — a person who never opens the sheet gets the cheap thing, and a person
	// who clears the row gets the conversation's own model, deliberately.
	KeyTierReflexModel = "models.tiers.reflex"
	// KeyTierMastermindModel is the fourth tier and the only one whose model is
	// chosen for THINKING rather than for a price. Two roles ride it — the
	// planner that amends an adaptive run's plan after every node, and the
	// designer that writes a harness page everybody afterwards runs — and both
	// were on the careful-work tier beside the check on finished work, which made
	// one figure answer two unrelated bills: the careful calls are many and short,
	// these are few and decide what all the other calls do.
	//
	// It is the one row whose value may carry a LEVEL as well as a model
	// (`moonshotai/kimi-k3:low`), because it is the one row where how hard the
	// model thinks is the point. Every tier row accepts the notation —
	// [ValidateTierValue] is the same gate on all five — but this is the one the
	// shipped crew writes it into.
	KeyTierMastermindModel = "models.tiers.mastermind"
	// KeyMouse is whether the surface reports the mouse at all. ON is the
	// default ([DefaultMouse]), because hover, click and the wheel are v3's own
	// language and the thing they cost is bought back by a key: an alt-screen
	// app that reports the mouse OWNS every drag, so the terminal's native text
	// selection dies the moment reporting starts — and `ctrl+s` hands the
	// pointer back for as long as somebody is dragging with it.
	//
	// THIS COMMENT SAID "OFF IS THE DEFAULT" while [DefaultMouse] said on, and a
	// stale sentence here is the expensive kind: it is the first thing anybody
	// reads when the answer to "does codeaf ask for the mouse at all" decides
	// whether a pointer bug is in this program or in the terminal. One source of
	// truth — [MouseModes] states the choice and [DefaultMouse] states the
	// answer, and this row's doc may not contradict either.
	KeyMouse = "ui.mouse"
	// KeyTimestamps is how much of the clock the v3 conversation carries: a
	// footer under every finished turn, only the coarse marks where the
	// conversation was put down and picked up again, or nothing at all
	// (internal/tui3's render.go). It is named beside the mouse row because it
	// is the same kind of question — how much furniture the transcript draws —
	// and it is a CHOICE rather than a bool because "less" and "none" are two
	// different answers a reader gives for two different reasons.
	KeyTimestamps = "ui.timestamps"

	// KeyTaskColumn is whether the v3 chat opens with the task roster's column
	// standing beside the conversation. It is a BOOLEAN — the column has no
	// middle rung: its two narrower tiers are decided by the frame's own width,
	// and the one answer a person gives it by hand is whether the column is
	// there at all.
	//
	// A person who put v3's column away has said
	// nothing whatever about v2's three rungs.
	KeyTaskColumn = "ui.task_column"
	// KeyQuickSwitch is whether the conversation switcher (internal/tui3's
	// hop.go) SWITCHES ON EACH PRESS of `ctrl+tab` — that chord lands you in the
	// previous conversation at once and pressing again keeps going — or opens as
	// a card that waits for `enter`. It is a BOOLEAN because the two behaviours
	// are the whole of the choice: there is no third rung between "the key is
	// the switch" and "the key is the menu".
	//
	// IT IS `ctrl+tab` AND NOT THE BINDING, which this doc said for a long while
	// and which the code has never done ([app.hopKey] reads the setting only for
	// the alias and its reverse). The binding always browses: it is the chord
	// everybody has, so it is the one that has to behave the same on every
	// machine, and a card that waits for `enter` is the behaviour that needs
	// nothing from the terminal. Neither gesture watches for a modifier being
	// RELEASED — ordinary terminals do not report that at all (hop.go).
	KeyQuickSwitch = "ui.quick_switch"
	// KeyHints is whether the v3 chat shows its earned hints — the one-line tips
	// in the slot above the message box that each retire once the key or command
	// they name has been used (internal/tui3's notice.go) — and, with them, the
	// one-line what's-new notes a new build may say. It is one row and not two
	// because a person who has silenced the tips has said they know the surface,
	// and being told about features is the same conversation.
	KeyHints = "ui.hints"
	// KeyWork controls whether completed turn machinery starts folded or open.
	KeyWork = "ui.work"
	// KeyIcons selects the step icon repertoire independently of colour.
	KeyIcons = "ui.icons"
	// KeyTaskAudit is whether an independent auditor verifies each task node
	// before its work may merge (internal/session's task_audit.go). It sits
	// beside the guardian because both spend a model on the person's behalf:
	// the guardian to answer, the auditor to check.
	KeyTaskAudit = "task.audit"
	// KeyReplyGuard is whether a reply that has stopped being language is cut
	// and asked again (internal/provider's streamguard.go). It is a row because
	// the cut is a JUDGEMENT about somebody else's text, and a person who writes
	// in four alphabets, or who asks for pages of repeated output outside a code
	// fence, is entitled to say they would rather see whatever arrives.
	//
	// The silence watchdog beside it has no row: a request that produced nothing
	// at all has failed by any reading.
	KeyReplyGuard = "reply.guard"
	// KeyTaskStart is what a bare `/task <brief>` COSTS TO SHAPE, and it is no
	// longer a question about shape at all (internal/tui3's taskcommand.go).
	// Every answer starts ONE WORKER. What the answers differ in is whether a
	// small sizing call reads the brief first, because a yes from that call is
	// what arms the worker to hand the work out mid-run once it has opened the
	// material and found the width is real (internal/session's task_divide.go).
	//
	// It is a row because the question is not really about one task. Somebody who
	// does not want the sizing call on their bill is refusing it every time, and
	// that is a preference stated once rather than on every `/task`.
	//
	// THE PLANNED-GRAPH ANSWER IS GONE, and with it the last way a preference
	// could open an adaptive run behind somebody's back. A `/task` takes one road
	// now — one worker that divides itself from the material — and there is no
	// second road left to prefer: no chat door reaches the planner at all, not a
	// setting, not a hand on the belt, and not a form of words somebody types
	// (internal/session's loop.go). A profile still holding the retired word reads
	// as the default, silently, the way any word this build does not know reads
	// ([TaskStartAt]).
	KeyTaskStart = "task.start"
	// KeyMemoryEnabled is whether this build remembers anything across
	// conversations at all (internal/session's memory.go): the pre-turn router
	// that decides which remembered lines a turn needs, the post-turn pass that
	// decides whether the exchange held anything worth keeping, and the
	// `remember` tool the model reaches for. Off is a conversation that starts
	// knowing nothing about you, and that makes not one extra call.
	KeyMemoryEnabled = "memory.enabled"

	// KeyStandingBackground is whether this machine's own scheduler keeps
	// standing items current when no codeaf window is open (internal/standing's
	// watch.go). It is dotted with the other v3 keys, under `standing.` because
	// that is the thing it is about, and it is PROFILE-ONLY — deliberately
	// absent from [ProjectKeys]: installing a timer is a change to somebody's
	// machine, and a checked-in file that could make one is a repository
	// arranging to run a program on every laptop that clones it.
	//
	// ON IS THE DEFAULT and the row is the only place it is ever asked. What
	// the row READS is derived from the timer itself rather than from this
	// value, so the sheet cannot say `on` over a machine where nothing is
	// installed; this value is the person's INTENT, which is what the launch
	// repair reads before it puts a drifted timer back
	// ([BackgroundChecksWantedAt]).
	KeyStandingBackground = "standing.background"

	KeyModelRoles = "models.roles"
	// KeyModelFallbacks is the ordered list of models a conversation moves to
	// when no endpoint serving the one it is on will accept the request at all
	// (internal/provider's endpoints.go). Comma-separated slugs, first tried
	// first. Empty lets the catalog pick the nearest same-class model instead.
	KeyModelFallbacks = "models.fallbacks"
	KeySpendRail      = "session.spendRailUSD"

	// KeyRouting is how a session asks the router to choose among the endpoints
	// serving one model (internal/provider's velocity.go). A model is not one
	// machine: the same id is fanned over several endpoints that answer at very
	// different speeds for the same price, and this row is which of those
	// differences the session is willing to pay attention to.
	KeyRouting = "routing"

	// KeyLaneGuard is whether one slow answer may be rescued by asking a second
	// lane the same question while the first is still thinking (internal/lane's
	// watch.go). It is a separate row from [KeyRouting] because it answers a
	// different question: routing says WHICH lane a request prefers, and this
	// says whether a request that has already gone wrong is allowed to spend a
	// little more to come back on time.
	//
	// It is on by default. The extra call is capped at one per answer and at a
	// tenth of what the session spends, and it is off under price routing,
	// where nobody is buying seconds at all.
	KeyLaneGuard = "lane.guard"

	// KeyTaskAutoApprove is the countdown a proposed task waits before it
	// starts on its own (internal/session's task.go). It is named under `task.`
	// rather than beside the approval rows for the reason the guardian row is
	// named apart from them: this is not a rule about what may run, it is HOW
	// LONG YOU GET to say something about work that is going to run either way.
	//
	// Seconds, not a duration string, because the number is small and read at a
	// glance under a bar that is counting it down — "5" is the row, "5s" would
	// be the row pretending to be a unit it never varies.
	KeyTaskAutoApprove = "task.autoapprove_seconds"

	// KeyTaskRepairRounds is how many times a task whose work came back
	// INCOMPLETE is sent back to close the gaps before it lands as a failure
	// (internal/session's task_audit.go). It is named under `task.` beside the
	// countdown and the audit row because it answers their question in the third
	// currency: those say how long you get to redirect work and who checks it,
	// this says HOW MANY TIMES a piece of work that nearly landed is allowed to
	// finish itself.
	//
	// A count rather than a word, and 0 turns it off: the number is the whole
	// setting, and the person who wants the old behaviour — one pass, and a gap
	// is a dead node — writes 0 rather than learning a vocabulary. It is small on
	// purpose. Each round is another worker and another check on the same node's
	// bill, and a loop that could run five times is a loop that can spend five
	// times without anybody watching.
	KeyTaskRepairRounds = "task.repair_rounds"

	// KeyTaskParallel is how many tasks may RUN AT ONCE (internal/session's
	// task_run.go). It is named under `task.` with the countdown and the repair
	// count because it answers their question in the fourth currency: those say
	// how long you get to redirect work, how many times it may finish itself,
	// and whose hands it is in — this says HOW MUCH OF IT happens at the same
	// time.
	//
	// BLANK IS NO LIMIT, and that is the default. The number of tasks was never
	// the resource: what runs out is this machine's cores and memory — the two
	// rows below — and the model provider's rate limit, which the adapter reads
	// off 429s and adapts to on its own (internal/provider's limiter.go). This
	// row exists for the person who wants a number anyway, and it is a QUEUE and
	// not a refusal: work past the cap waits and starts when a slot frees.
	KeyTaskParallel = "task.parallel"

	// KeyTaskMaxLoad is the load average PER CORE at or above which no new task
	// is started (internal/session's task_pressure.go). It is one of the two
	// real ceilings the cap above stopped pretending to be.
	//
	// Per core, so that the number means the same thing on a laptop and on a
	// workstation: 1.5 is "half again as many runnable threads as there are
	// cores to run them", which is where the scheduler starts handing out slices
	// rather than running work and one more build makes every build slower. 0
	// turns the check off. It holds STARTS only — nothing already running is
	// ever touched, so the pressure drains on its own.
	KeyTaskMaxLoad = "task.max_load"

	// KeyTaskMinFreeMB is the floor of available memory below which no new task
	// is started (internal/session's task_pressure.go), in mebibytes, and the
	// other half of the ceiling above.
	//
	// It reads the kernel's MemAvailable — what a new process could actually get
	// — and not free memory, which on a working machine is near zero by design
	// because the page cache has the rest. 0 turns the check off.
	KeyTaskMinFreeMB = "task.min_free_mb"

	// KeyTaskModel is the model a task runs on when the conversation does not
	// name one for it (internal/session's taskmodel.go). It is named under
	// `task.` beside the countdown rather than among the `models.` rows because
	// it is not a rule about how a call is made: it is WHOSE HANDS the work that
	// leaves this conversation ends up in, which is the same subject the
	// countdown and the audit rows are about.
	//
	// Blank is the conversation's own model, which is what every task ran on
	// before the row existed: a node is the same worker doing the same job
	// somewhere quieter.
	KeyTaskModel = "task.model"

	// KeyTaskSettle is WHO DECIDES a task that finished but that nobody could
	// check (internal/session's task_contract.go). It sits under `task.` beside
	// the audit row because it is the other end of that row's question: the audit
	// says whether the work is checked at all, and this says what happens when
	// the check came back with nothing.
	//
	// `ask` is the default and puts the decision on the landed card, where the
	// person answers it with one press. `auto` hands it to the chat: the landing
	// note tells the model to read the work and settle it, and to come back only
	// when it genuinely cannot tell. Both leave the same three answers available
	// to both of them — this row changes who is asked first, and nothing else.
	KeyTaskSettle = "task.settle"

	// The web-search rows. They are four rather than one because they answer
	// four separable questions: WHERE a lookup goes, and the three credentials
	// that change what "where" can mean. A person with no key still searches —
	// internal/search's last rung takes none — so the keys are an upgrade and
	// never a prerequisite, and none of the three has to be answered for the
	// session to be able to look something up.
	KeySearchProvider = "search.provider"
	KeyExaKey         = "search.exaKey"
	KeyFirecrawlKey   = "search.firecrawlKey"
	KeyJinaKey        = "search.jinaKey"

	// The two rows that let a person connect their Google account
	// (internal/connect). They are a PAIR and neither is useful alone: an
	// application id names the application asking, and its secret proves the
	// ask came from it, so a build holding one of them can offer exactly
	// nothing. The session reads them together for that reason
	// ([GoogleOAuthClientAt]), and offers the connect tools only when both are
	// answered.
	//
	// Answering them REPLACES the registration this build ships with
	// (connect_defaults.go): a person or a deployment that wants its own
	// application asking — its own quota, its own consent screen, its own
	// revocation — writes it here or exports it, and nothing else changes.
	KeyGoogleOAuthClient = "google_oauth_client"
	KeyGoogleOAuthSecret = "google_oauth_secret"

	// The one row that lets a person replace the Slack application this build
	// ships with (connect_defaults.go). It is ONE ROW AND NOT A PAIR because
	// Slack's PKCE registration has no secret. An organisation that registers
	// its own internal application to escape the outside-Marketplace throttle
	// pastes that application's id here.
	KeySlackOAuthClient = "slack_oauth_client"

	// The four context-law knobs. Fill is how much of a model's window any
	// agent may use before compaction fires — and WHETHER A PERSON SET IT is
	// itself part of the law, because a conversation nobody has pinned folds
	// against a line derived from its model's window instead
	// (ctxbudget.PinnedFillPercent, internal/session's compactThresholdOf);
	// the reserve is the room every
	// call keeps for its answer and its reasoning; the working set caps what
	// an agent keeps quoted in front of itself however large the window is;
	// and reuse caps how many times over it may re-send that working set.
	KeyContextFill       = "context_fill_pct"
	KeyCompletionReserve = "completion_reserve"
	KeyWorkingSet        = "working_set_tokens"
	KeyContextReuse      = "context_reuse_pct"
)

Persisted keys are also the json field names in the profile's config.json. KeyDailyBudget keeps the name /budget default already writes.

View Source
const (
	GuardianOff = "off"
	GuardianOn  = "on"
)

The guardian row's two answers. It is a CHOICE and not a bool for the reason every other two-word row here is one: "off/on" is what the sheet renders and what the file holds, and a person reading their config.json back should find a word they chose rather than a `true` they have to remember the question for.

View Source
const (
	MouseOff = "off"
	MouseOn  = "on"
)
View Source
const (
	TaskAuditOff = "off"
	TaskAuditOn  = "on"
)
View Source
const (
	ReplyGuardOff = "off"
	ReplyGuardOn  = "on"
)
View Source
const (
	// TaskSettleAsk puts it in front of the person, on the landed card.
	TaskSettleAsk = "ask"
	// TaskSettleAuto hands it to the chat, which reads the work and decides.
	TaskSettleAuto = "auto"
)

The two answers to KeyTaskSettle: who settles a task that finished with nobody able to say whether it holds.

View Source
const (
	TaskStartSized  = "sized"
	TaskStartSingle = "single"
)

The two answers to KeyTaskStart, and they are not two settings but one question asked once instead of on every `/task`: what a bare `/task` pays to find out before its one worker starts.

sized      one worker, with the sizing call read over the brief first. A yes
           from it arms that worker to hand the work out as it goes, once it
           has opened the material and found the width is real — so wide work
           runs wide without a planner ever being asked to guess at it.
single     one worker, and the sizing call is not made at all. The work can
           still divide itself, but only off what its own brief already says
           (internal/splitgate), because nothing was read over it.

THERE WERE FOUR, AND BOTH THE RETIRED ONES NAMED A CARD OR A ROAD THAT IS GONE. `ask` went with the two-row chooser a yes from the sizing call used to raise; `adaptive` went with the planned-graph road itself, which a chat turn may no longer open at all. Neither retirement is allowed to be felt: a profile still holding either word reads as the default, silently, because TaskStartAt treats a word this build does not know as no answer at all — and being told that a preference set months ago is now an error is the one thing a retirement must never do. NEITHER RETIRED WORD IS SPELLED HERE, and `ask` set that precedent: a constant for a word nothing may write, nothing may offer and nothing may resolve to is a name the next reader has to be told is not a mode. The retirement needs no constant to work — it is TaskStartModes not containing the word, which is what the sheet, [writeChoice] and TaskStartAt all read.

View Source
const (
	TimestampsFooters    = "footers"
	TimestampsSeparators = "separators"
	TimestampsOff        = "off"
)

The timestamps row's three answers, and they are a LADDER rather than three unrelated pictures: each rung draws strictly less of the clock than the one above it.

footers      the turn footer (· 14:02 · 2m12s · 3 tools · $0.04 ·) AND the
             gap and day marks, which are what a footer is read against
separators   only the marks: where the conversation was put down for ten
             minutes, and where a day ended
off          no clock at all

The middle rung exists because the two features answer the same question at different costs: a reader who wants to know that yesterday's exchange was yesterday does not necessarily want a line of figures under every turn, and a footer with no day mark above it would be a time with no date.

View Source
const (
	WorkFold = "fold"
	WorkOpen = "open"
)
View Source
const (
	IconsAuto  = "auto"
	IconsRich  = "rich"
	IconsPlain = "plain"
)

IconModes keeps the normal rich presentation and its compatibility floor selectable without changing the terminal's colour or animation settings.

View Source
const (
	MemoryOff = "off"
	MemoryOn  = "on"
)
View Source
const (
	// PromptProfileAuto derives the profile from the model's context window,
	// which is what every session did before this row existed.
	PromptProfileAuto = "auto"
	// PromptProfileLean is the shorter page and the shorter tool list, whatever
	// window the model reports.
	PromptProfileLean = "lean"
	// PromptProfileFull is every law on the page and every ordinary verb in the
	// tool block, whatever window the model reports.
	PromptProfileFull = "full"
)

The prompt profile's three answers, and they are three rather than two because the honest default is not a size at all: it is "work it out". A row offering only `lean` and `full` would force everybody to hold an opinion about a figure the catalog already knows.

View Source
const (
	BackgroundOff = "off"
	BackgroundOn  = "on"
)

The background-checks row's two answers.

View Source
const (
	// RoutingLatency asks for the currently-fastest endpoint.
	RoutingLatency = "latency"
	// RoutingPrice asks for the cheapest one that can serve the request.
	RoutingPrice = "price"
	// RoutingSimple sends no preference of ours at all: when no provider is
	// pinned the router's own default answers, and a pinned provider is the
	// whole request.
	RoutingSimple = "simple"
	// RoutingOff sends no preference at all, and stops measuring with it.
	RoutingOff = "off"
)

The routing row's four answers. They are spelled here rather than imported from internal/provider for the reason DocumentEngines is: a settings key's vocabulary is a string on disk, and it must not change because a package renamed a constant.

View Source
const (
	// LaneAuto lets the belief pick a lane per answer. It is the default and it
	// is what an unset row reads as.
	LaneAuto = "auto"
	// LaneOpenRouter asks for no lane at all and lets the router balance on
	// price, which is what this build did before it held an opinion.
	LaneOpenRouter = "openrouter"
)
View Source
const (
	ModelTierLow  = "low"
	ModelTierHigh = "high"
	// ModelTierWorker is the seat that does the work ([roles.TierWorker]).
	ModelTierWorker = "worker"
	// ModelTierReflex is the per-turn tier ([roles.TierReflex]).
	ModelTierReflex = "reflex"
	// ModelTierMastermind is the thinking tier ([roles.TierMastermind]).
	ModelTierMastermind = "mastermind"
)

The two tier names internal/roles resolves auxiliary calls under. They are spelled here rather than imported for the reason DocumentEngines is: the registry is a settings surface, and a settings key is a string on disk that must not change spelling because a package renamed a constant.

View Source
const (
	DefaultReflexModel = "google/gemini-2.5-flash"
	DefaultLowModel    = "deepseek/deepseek-v4-flash-0731"
)

THE TWO ROWS THAT ARE NOT CREW SEATS arrive pointed at a model. The reflex and small-work tiers carry the calls a conversation makes on its own behalf — twice a turn for the reflex — so a person who never opened the sheet gets a model that costs near nothing rather than the one they are talking to. The three crew seats have no shipped model at all: an unpinned seat is routed per task (crew.go), and a build-chosen worker, planner or checker would be the implicit fallback the router exists to remove.

Each is a bare OpenRouter id, spelled ONCE here and read by every caller through TierModelAt. Blank is still an answer on both: a row a person emptied on purpose reads empty and follows the model the person is talking to, which is roles.Resolve's floor. UNSET and CLEARED are different answers here (TierModelAt says how). The low row is pinned to a DATED build on purpose: the bare `deepseek/deepseek-v4-flash` id resolves to the April build, and the July build costs the same.

View Source
const (
	// DefaultPlanConsentUSD is where ambition stops being cheap. Below it a
	// plan simply runs, because asking about a small errand is the nagging
	// nobody wants; above it the user is quoted a count and a price and gets to
	// say no first. It is deliberately far under the daily rail: the rail is a
	// stop after the fact, and this is the moment before.
	//
	// IT WAS $3 AND $3 IS THE PRICE OF AN ORDINARY PIECE OF WORK, so the
	// question fired on nearly every plan and became a keystroke the person
	// owed rather than a decision they made — which is the failure mode a
	// consent gate cannot survive, because a question asked every time is a
	// question nobody reads. At $100 the gate fires on the plans a person
	// would genuinely want to see priced first. 0 never asks.
	DefaultPlanConsentUSD = 100.0

	// DefaultTenureAfter is the clean-firing count a standing charter needs
	// before it earns tenure.
	DefaultTenureAfter = 3

	// DefaultAttributionModel names the model in the `Assisted-by` line by
	// default, because the line is provenance and the model is the part of it
	// somebody auditing the history later actually wants. The signature itself
	// has no row and no off: work the person did not type is always readable as
	// such (internal/exec's AttributionLaw).
	DefaultAttributionModel = true

	// The divider clamps so neither pane can be set into uselessness. The TUI
	// reads these so the drag, the [ ] nudge, and the sheet agree.
	DefaultSplitPct = 80
	MinSplitPct     = 25
	MaxSplitPct     = 85

	// DefaultHistoryEnabled remembers what was typed, because a prompt is the
	// most expensive sentence in the product to re-type and the up arrow is the
	// cheapest way to get it back. The file is a recall list and nothing else —
	// internal/history caps it and never sends it anywhere.
	DefaultHistoryEnabled = true

	// DefaultTelemetry is on, because the events are coarse counts the
	// contract allows (docs/TELEMETRY.md) and the notice names them before
	// the first byte leaves the machine. One row turns the pipe off, and
	// DO_NOT_TRACK answers the same question for anyone who arrives with
	// the ecosystem's own word for it.
	DefaultTelemetry = true

	// DefaultDraftPersist keeps the unsent sentence across a restart, for the
	// reason a text field in any other application does: the draft is the
	// PERSON's, not the session's, and losing it to a crash or a closed window
	// is the surface throwing away the only thing on screen it did not write.
	DefaultDraftPersist = true

	// DefaultToolApprovalMode opens new conversations in YOLO unless a saved
	// profile, project or conversation choice supplies another posture.
	DefaultToolApprovalMode = "allow"

	// DefaultGuardian is off because choosing YOLO does not appoint a model
	// to answer approval questions. The guardian remains an explicit choice.
	DefaultGuardian = GuardianOff

	// DefaultSpendRailUSD is 0 — no per-session ceiling. The rail that is on by
	// default is the DAILY one, because that is the number a person actually
	// budgets; a session ceiling is for the sitting somebody wants to box in,
	// and a default would box in every sitting at a number nobody chose.
	DefaultSpendRailUSD = 0.0

	// DefaultTaskAutoApprove is fifteen seconds, and the direction it is wrong in
	// is the whole choice. A task proposal is not a permission question — the
	// model has already groomed the work and the brief, and the countdown is the
	// person's window to REDIRECT it or wave it off. Five seconds once looked
	// long enough to read a title and summary and reach for a key; a real card,
	// with its where line and three answers, was gone before the person's no.
	// Fifteen keeps silence as approval without making every task a keystroke the
	// person owes, and gives the actual card enough time to read.
	DefaultTaskAutoApprove = 15

	// DefaultTaskRepairRounds is ONE, and one is the whole argument. The failure
	// this exists for is a piece of work that came back nearly right — a report
	// covering ten of the eleven companies it was asked for — and died, leaving
	// the person to type the whole task again by hand. One round is what turns
	// that into a task that finishes: the same worktree, the same brief, and the
	// gaps in front of a fresh worker.
	//
	// It is not two, and it is not five. A second round buys much less than the
	// first — work that is still wrong after being told exactly what is missing
	// is work whose brief is wrong, and no number of rounds fixes a brief — while
	// every round costs another worker and another check on the same node. So the
	// default closes the near-misses and stops.
	DefaultTaskRepairRounds = 1

	// DefaultTaskParallel is NO LIMIT, and the change of mind it records is
	// worth the sentence. The frontier used to hold two tasks at once, and two
	// was a guess standing in for a resource nobody had measured — it left a
	// sixteen-core machine idle behind a queue of ready work, and it was still
	// one too many on a laptop already carrying somebody else's compile. The
	// number of tasks is not what runs out. So the count became a row a person
	// may set when they want one, the ceilings became the two below, and the
	// default is the honest one: as much as the machine and the provider will
	// carry.
	DefaultTaskParallel = 0

	// DefaultTaskMaxLoad is one and a half runnable threads per core, which is
	// the number an earlier incident settled on: codeaf pinning a laptop's fan
	// by running real compilers and real test suites beside each other
	// (internal/exec's governor.go carries the same figure for the same class of
	// work). Below it the machine is busy; at it, the scheduler is handing out
	// slices and one more task makes every task slower.
	DefaultTaskMaxLoad = 1.5

	// DefaultTaskMinFreeMB is a gibibyte and a half, which is roughly what one
	// more task needs to be worth starting: a child agent, a git worktree, and
	// whatever build it is about to run. Starting one under that floor is how a
	// machine reaches the OOM killer, and what the OOM killer takes is not the
	// task that was too many — it is whichever process was largest, which on a
	// developer's machine is usually theirs.
	DefaultTaskMinFreeMB = 1536

	// DefaultConsentTimeout is ten seconds of reminder, and it is a different
	// number from the one above because it is a different KIND of clock. The
	// task countdown runs toward the permissive answer, so it is kept short
	// enough to notice. This one used to run toward the refusal (F41) and
	// does not: at expiry the question pauses and keeps waiting. Ten seconds
	// is long enough to read a command and a rule; after that the card stays
	// up until somebody answers.
	DefaultConsentTimeout = 10

	// DefaultSearchProvider pins nothing. Auto is the only default that stays
	// right as a person's keys change: the day they paste an Exa key the
	// searches move to Exa without a second row being touched, and the day it
	// expires they keep searching instead of getting an error from a plug they
	// pinned six months ago and forgot.
	DefaultSearchProvider = SearchProviderAuto

	// DefaultTaskColumn stands the v3 task column up on a session that has never
	// been told otherwise: THE COLUMN TEACHES BY EXISTING — it is how a person
	// finds out that this chat runs work you can walk away from. Once they put it away we never stand it up again on
	// their behalf — the strip is what keeps running work reachable from a frame
	// with no column on it (internal/tui3's taskstrip.go).
	DefaultTaskColumn = true

	// DefaultQuickSwitch controls immediate switching with ctrl+tab where the
	// terminal can deliver it. alt+k always browses before opening a chat.
	DefaultQuickSwitch = true

	// DefaultHints shows the v3 chat's tips to a profile that has never said
	// otherwise. The tips retire themselves the moment each is acted on, so the
	// default costs a veteran one line per gesture they already know, once.
	DefaultHints = true
)

Defaults the registry owns beyond the ones config.go already declares.

View Source
const (
	// KeyResponseAttempts is N: how many times ONE request is tried on its own
	// tier before the wire is given up on. The first try is included.
	KeyResponseAttempts = "response.attempts"
	// KeyResponseLiftAfter is K: how many checks must read finished work and
	// find gaps in it, on the same tier and with the wire ruled out, before a
	// stronger model is bought.
	KeyResponseLiftAfter = "response.lift_after"
	// KeyResponseLiftCap is what that stronger model may cost ONE piece of
	// work, in dollars. 0 is no cap.
	KeyResponseLiftCap = "response.lift_cap_usd"
)

The keys the boundary's numbers persist under. They are named `response.` because that is what the boundary reads — one response, and what kind of failure it was — rather than `task.` or `model.`, either of which would put the row beside the wrong question (internal/taxonomy).

View Source
const (
	PlanPausedWait     = "wait"
	PlanPausedUseMeter = "use pay-as-you-go"
)
View Source
const (
	KeySSHControlPersist = "ssh.control_persist_seconds"
	KeySSHServerAlive    = "ssh.server_alive_seconds"
	KeySSHServerMisses   = "ssh.server_alive_misses"
	KeySSHIPQoS          = "ssh.ip_qos"

	DefaultSSHControlPersist = 5 * 60
	DefaultSSHServerAlive    = 3
	DefaultSSHServerMisses   = 3
	DefaultSSHIPQoS          = "lowdelay"
)
View Source
const (
	KeyTeamsQuestionsUp = "teams.questions_up"
	KeyTeamsCapUSDDay   = "teams.cap_usd_day"
	KeyTeamsDepthLimit  = "teams.depth_limit"
	KeyTeamsSubSharePct = "teams.sub_share_pct"
	KeyTeamsWake        = "teams.wake"
)

The five rows' keys.

View Source
const (
	DefaultTeamsQuestionsUp = true
	DefaultTeamsCapUSDDay   = 0.0
	DefaultTeamsDepthLimit  = 3
	DefaultTeamsSubSharePct = 50
	// DefaultTeamsWake is on: a directive that waited for the person to type
	// in the member's tab would make a manager a mailbox.
	DefaultTeamsWake = true
)

The built-in defaults.

QUESTIONS GO UP by default, because that is the ruling's goal: a person who talks to the top manager and steps away should be asked only what no manager could answer. A day's cap is OFF by default, for the reason every rail in this build ships large or off (DefaultSpendRailUSD): a cap nobody chose would stop work nobody asked to stop. Depth is three levels, the ruling's "about three". A new sub-team gets half its parent's cap.

View Source
const APIKeyEnv = "OPENROUTER_API_KEY"

APIKeyEnv is the environment variable that outranks the profile's key. It is spelled once here because three surfaces name it to a person: the missing-key sentence at the door, the settings row's pin, and the first-run page.

View Source
const AttributionModelHint = "On, commits say `Assisted-by: CodeAF (<model>)`; off, `Assisted-by: CodeAF`."

AttributionModelHint is the `attribution.model` row's hint: what the line says with the row on, and what it says with the row off.

View Source
const (
	// BestModelWord is the one spelling that means "the strongest advertised
	// model for this modality", resolved from the catalog at call time.
	BestModelWord = "best"
)
View Source
const CrewAuto = "auto"

CrewAuto is the word a seat reads when it is not pinned.

View Source
const CrewCommand = "/crew"

CrewCommand is the one door a person reaches the crew through, spelled once so a sentence naming it cannot drift from the command that answers.

View Source
const CrewTaskCapDefault = 5.0

CrewTaskCapDefault is the per-task limit when none is set.

View Source
const DefaultBackground = BackgroundOn

DefaultBackground is on.

View Source
const DefaultLaneGuard = true

DefaultLaneGuard is on: a slow answer is worth one extra call to rescue, and the budget around it is what keeps that true rather than a hope.

View Source
const DefaultMemory = MemoryOn

DefaultMemory is on.

View Source
const DefaultMouse = MouseOn

DefaultMouse is on.

View Source
const DefaultPromptProfile = PromptProfileAuto

DefaultPromptProfile is auto.

View Source
const DefaultReplyGuard = ReplyGuardOn

DefaultReplyGuard is on.

View Source
const DefaultRouting = RoutingSimple

DefaultRouting is simple: what a person asked for is what goes on the wire, and nothing else does.

IT WAS `latency` UNTIL THIS BUILD, and the reason it moved is that the choosing was not visible. Sorting by speed brings a whole apparatus with it — a ranking this process keeps, a price ceiling, refusals learned from earlier answers, a pin retired against a belief saved from an earlier run — and each of those is a decision nobody watched being made. What the picker showed, what was chosen, and what the record said were three answers to one question. Under this row they are one answer: with no lane pinned the request carries no preference at all and the router's own default routing answers it, and with a lane pinned that pin is the whole request. `latency` and `price` are both still here for somebody who wants the apparatus, one word away (internal/provider's velocity.go).

View Source
const DefaultTaskAudit = TaskAuditOn

DefaultTaskAudit is on.

View Source
const DefaultTaskSettle = TaskSettleAsk

DefaultTaskSettle is ask.

View Source
const DefaultTaskStart = TaskStartSized

DefaultTaskStart is sized: one worker that knows whether it is allowed to divide, which is the shape the measured road is built around.

View Source
const DefaultTimestamps = TimestampsFooters

DefaultTimestamps is the footers. When a turn took two minutes and cost four cents, those are facts about work the person paid for, and a transcript that never says when anything happened cannot be read back a day later.

View Source
const DefaultWork = WorkFold
View Source
const EnvPromptProfile = "CODEAF_PROMPT_PROFILE"

EnvPromptProfile pins the profile for one launch, in the same three words the row takes. It is spelled here and read from here by internal/session's promptprofile.go, because a pin the sheet renders read-only and a pin the engine obeys must be one string or they drift.

View Source
const FreeChatModel = "qwen/qwen3.8-27b:free"

FreeChatModel is the build's chat bottom rung while a known account is low.

View Source
const KeyAPIKey = "api_key"

KeyAPIKey is the profile field the provider key lives in. It predates the settings registry — the background timer's copy of the environment key has always landed here — and the registry's row (settings.go) writes the same field, so a key pasted on the first run, one typed into /settings and one copied from the shell are one value in one place.

View Source
const KeyChatModel = "model.talk"

KeyChatModel is where that choice is written in the profile's config.json.

It is the SETTINGS ROW'S OWN KEY — ModelSettingKey for the talk slot — rather than a name of this file's invention, so if the registry ever persists its model rows itself the two halves land on one key instead of two. It is spelled as a constant because a key is a constant; [TestChatModelKeyIsTheRow] pins the two spellings together.

View Source
const KeyEffort = "effort"

KeyEffort is where the install's rung is written in the profile's config.json.

It is spelled bare rather than under a `model.` or `task.` prefix because it is not about one slot or one kind of work: it is the default for all of them, which is exactly what the resolver's last rung means.

View Source
const KeySetupSeen = "setup_seen_at"

KeySetupSeen is the profile field that says the setup has been shown. It is a timestamp rather than a bool for the one thing a bool cannot answer later — which features arrived after this person's first day — and it lives in config.json beside KeyAPIKey rather than in a stamp file because it is a fact about the profile, and the profile has one file.

It is NOT a settings row: nothing about it is a preference, and a row a person could edit would be a way to be greeted twice.

View Source
const LaneSlotTalk = "talk"

LaneSlotTalk is the ONE slot the settings registry carries a row for: the conversation. Every slot has a key — a task worker can be pinned by the same grammar — but a panel with nine lane rows on it would be a panel about endpoints rather than about models, and the other eight are set from the picker on the model they belong to.

View Source
const MoneyFloor = 0.0001

MoneyFloor is the smallest amount any surface writes as a figure: a hundredth of a cent, four places after the point. It is the number SubCent compares against AND the number it prints, so it is spelled once.

View Source
const NoLimitWord = "no limit"

noLimitWord is what a money row says about itself when its number is zero.

A DOLLAR ROW READING "$0" IS THE ONE PLACE THE EMPTINESS LAW CANNOT REACH. Zero on these rows is not an absence — it is the person's own instruction, and the instruction it spells is the OPPOSITE of what "$0" reads as at a glance: "no money at all" where the code means "no ceiling at all". Every other kind of row has a word for its off state (Setting.EmptyLabel) and a dollar row cannot borrow one, because its reading is a formatted number and therefore never empty. So the word goes where a row already says the dim true thing beside its value: the receipt.

It is EXPORTED because the surfaces say it too — the v3 spend place's pointer line and the settings tab's `today` reading both have to spell "nothing bounds this" and there is one spelling of it.

View Source
const ProfileDirEnv = "CODEAF_PROFILE_DIR"

ProfileDirEnv is the variable that moves the whole profile — the key, the settings file, the measured behaviour — somewhere else. It is what an isolated run sets, and it is spelled here once so every reader of the profile asks the same question.

View Source
const ProjectProgramLinks = "program.links"

ProjectProgramLinks is a key only a project file answers, and no settings row: the folders git ignores that a program's copy of this repository links in from the person's checkout, comma-separated (`"node_modules, .env"`) or a list (ProjectConfig.Names), an empty one linking none. It is a fact about the repository — which ignored folders its build and tests need — and nobody's profile has an answer to it, so it is read straight off the project file (internal/session's programcopy.go) rather than through the registry the rows above are.

View Source
const SearchProviderAuto = "auto"

SearchProviderAuto is the row's default: no pin, and internal/search walks its own ladder — the keyed plug when its key is present, the zero-key plug otherwise. It is spelled here rather than as the empty string because a choice row has to have a word for "I did not choose", and "" would render as a blank cell nobody can tell from a broken read.

View Source
const UnitInLabel = "-"

UnitInLabel is the Setting.Unit of a row whose own LABEL names what is counted: `tasks at once 3` needs no `tasks` on the end, and `task repair rounds 1 round` would be the row saying `rounds` twice. It reads as nothing and it is not nothing — it is the row saying it was looked at.

Variables

View Source
var BackgroundModes = []string{BackgroundOn, BackgroundOff}

BackgroundModes lists them, on first — which is the default. Something you asked to happen every morning is something you asked to happen on the mornings you do not open a terminal, and a person who wanted it only while they were sitting there says so here.

View Source
var CrewAccounts func(profileDir, provider string) (CrewAccountState, bool)

CrewAccounts is the OPTIONAL adapter that primes route health for providers that can report their account state. Nil — the default — is providers that cannot, whose health is learned from their calls alone.

View Source
var CrewCatalog func() []catalog.Model

CrewCatalog is how the catalog reaches crew routing: the binary holding the catalog sets it ONCE AT START-UP, from its non-blocking read, and never a fetch — a task is routed on whatever the catalog already holds. Nil, and a func answering no rows, are ordinary states rather than errors: the router then has no candidates, and a task's crew comes from its rescue ladder — the last crew that worked here, the model the person is talking to.

View Source
var CrewCheckCeilingAction = "the check stopped at its spend ceiling of $%.2f, " +
	crewCeilingTimesWord() + " times its estimate, before it finished"

CrewCheckCeilingAction is the sentence a check its ceiling ends says, with the ceiling's dollars. The multiplier is spelled from CrewCheckCeilingTimes, so the sentence cannot promise a figure the guard does not hold.

View Source
var CrewHistory = func(profileDir string) CrewDay {
	log := router.ReadCrewLog(ProfilePath(profileDir, ""), time.Now())
	return CrewDay{SpentUSD: log.SpentUSD, Offsets: log.Offsets, CostFactor: log.CostFactor, Learned: crewLearned(log.Quality)}
}

CrewHistory is how the router's log reaches crew routing: today's crew spend and the learned offsets, read out of the profile's router-events log (internal/router's crew.go). It is a variable so a test can hand a day of its own; nil reads as a day with nothing spent and nothing learned — the state of a fresh install, not an error.

View Source
var CrewLastGood = func(profileDir string) *router.CrewRecord {
	return router.ReadCrewLog(ProfilePath(profileDir, ""), time.Now()).LastGood
}

CrewLastGood is the crew of the newest task whose result was kept.

View Source
var CrewRouteHistory = func(profileDir string) []router.CrewRouteOutcome {
	return router.ReadCrewLog(ProfilePath(profileDir, ""), time.Now()).Routes
}

CrewRouteHistory is how route outcomes reach the health reading: the router log's recent first-call outcomes. A variable so a test can hand its own.

View Source
var DocumentEngines = []string{"auto", "local", "free", "ocr"}

DocumentEngines are the four rungs CODEAF_DOC_ENGINE accepts.

View Source
var EffortChoices = func() []string {
	choices := make([]string, 0, len(effort.Rungs)+1)
	choices = append(choices, "auto")
	for _, rung := range effort.Rungs {
		choices = append(choices, rung.String())
	}
	return choices
}()

EffortChoices offers the provider default followed by the five explicit levels. Auto means no reasoning override, not disabled reasoning.

View Source
var ErrCrewAtCap = errors.New("today's crew spend has reached the daily cap")

ErrCrewAtCap is a crew asked for with the day's crew spend already at the daily cap. The decision still comes back: the chat asks the person, and a headless run refuses unless told `-yes-spend`.

View Source
var ErrCrewLastProvider = errors.New("at least one provider must stay on")

ErrCrewLastProvider is the refusal to turn off the last provider on: a crew with nothing to route through is not a narrower crew but no crew, and the way to say "none of these" is to disconnect them, which /connect does.

View Source
var ErrCrewNoRoutableProvider = errors.New("at least one provider that can route a seat must stay on")

ErrCrewNoRoutableProvider is the same refusal one step wider: the providers left on would route no seat — only a custom endpoint, say, which a pin reaches and the router never picks — while every seat is not pinned to a provider still on.

View Source
var ErrNoAPIKey = errors.New(APIKeyEnv + " (or OPENAI_API_KEY) is required")

ErrNoAPIKey is what Load returns when nothing — the two variables, the profile file — holds a key. It is a value rather than a sentence so that a door can tell "no key" from "config broken": the first is a person who has not set up yet, and the interactive chat opens anyway and asks them (LoadKeyless); the second stops the launch, whoever is watching.

View Source
var GuardianModes = []string{GuardianOff, GuardianOn}

GuardianModes lists them, off first — which is also the default, and the order the row widens in.

View Source
var MemoryModes = []string{MemoryOn, MemoryOff}

MemoryModes lists them, on first — which is the default. A colleague who forgot every preference you stated the moment you closed the window would be one you had to brief again every morning, and the calls that carry this are the cheapest the surface makes.

View Source
var ModelPoolChoices = []string{"on", "read", "off"}

ModelPoolChoices are the answers the pool row accepts, and the same words the pool's resolver reads from its environment pin.

ModelTiers lists the five tier words in the order a settings surface renders them, cheapest first. It is roles.Tiers spelled as the words on disk, and [tierKeyFor] is total over it.

View Source
var MouseModes = []string{MouseOn, MouseOff}

MouseModes lists them, on first — which is also the default: hover, click and the wheel are the surface's own language. Selecting text does not die for it — ctrl+s hands the pointer to the terminal for as long as somebody is dragging with it, and ctrl+b copies from the keyboard — but a person who wants the terminal's plain drag at all times turns the row off.

Shift+drag is the answer this hint used to give, and it was the wrong one: it is true, it is a fact about terminals rather than about this product, and the terminals that have it disagree about the modifier (Option in iTerm2). A key this surface owns is a key this surface can print.

View Source
var OperatorEnvPins = []string{
	"CODEAF_BASE_URL",

	"CODEAF_CODEX_ISSUER",
	"CODEAF_CODEX_BACKEND",

	"CODEAF_CHECK_MODEL",

	"CODEAF_HOME",

	"CODEAF_RELAY",

	"CODEAF_FURROW",

	"CODEAF_PROFILE_DIR",

	"CODEAF_MODEL_API",
	"CODEAF_MODEL_TOKEN",

	"CODEAF_DELEGATE_RUN",

	"CODEAF_PROGRAM_HOLD_FD",

	"CODEAF_INBOX",

	"CODEAF_NO_UPDATE_CHECK",
	"CODEAF_GITHUB_API",
	"CODEAF_GITHUB_DOWNLOAD",

	"CODEAF_INSTALL_NAME",

	"CODEAF_CALL_LOG",
	"CODEAF_CALL_LOG_BODIES",

	"CODEAF_TELEMETRY_ENDPOINT",

	"CODEAF_DEBUG",
	"CODEAF_TRACE_MAX_MB",
	"CODEAF_TRACE_KEEP",
	"CODEAF_MODELS",
	"CODEAF_REASONING",
	"CODEAF_EXEC_REASONING",

	"CODEAF_EXEC_TURNS",
	"CODEAF_EXEC_BUDGET",
	"CODEAF_EXEC_TIMEOUT",

	"CODEAF_MAX_HOURS",
	"CODEAF_MAX_COST",
	"CODEAF_SPINE_SAMPLES",
	"CODEAF_MAX_DEPTH",
	"CODEAF_NODE_BUDGET",
	"CODEAF_SKILL_DIR",
	"CODEAF_SKILLS_BIN",
	"CODEAF_RTK",
	"CODEAF_RTK_BIN",
	"CODEAF_PREAUTHORIZE_SPEND",

	"CODEAF_WIRE_LOG",

	"CODEAF_QUESTION_DEMO",

	"CODEAF_SUITE_DIRLOCK_PATH",

	"CODEAF_GROWTH_GATE",

	"CODEAF_SWARM",

	"CODEAF_SPLITGATE",

	"CODEAF_MECHANISM",

	"CODEAF_QUORUM",

	"CODEAF_EXIT_CODES",

	"CODEAF_RESPONSE_ATTEMPTS",
	"CODEAF_RESPONSE_LIFT_AFTER",
	"CODEAF_RESPONSE_LIFT_CAP",

	"CODEAF_QUESTION_DEMO",

	"CODEAF_TASK_BELT",

	"CODEAF_PLANDB_BIN",

	"CODEAF_MODEL_POOL_RELAY_URL",
	"CODEAF_MODEL_POOL_URL",
	"CODEAF_MODEL_POOL_SUBMIT_URL",
	"CODEAF_MODEL_POOL_MIRROR_URL",
	"CODEAF_MODEL_POOL_TTL",

	"CODEAF_ALLOW_PROVIDER_KEYS_IN_SHELL",
}

OperatorEnvPins is the explicit allowlist of environment variables that are plumbing rather than settings: endpoints, credentials, profile roots, and planner internals. They are listed read-only in the sheet's environment footer and never become editable rows.

ProjectKeys is the allowlist: the rows that may be answered by a repository.

It is an allowlist rather than "whatever the registry has" for two reasons. A checked-in file must not be able to move somebody's daily budget or point their vision model at a model they pay for — those are the PERSON's rows. And an unrecognised key here is not an error (a later version's row, another tool's section), so without a list of what this build honors there would be no way to tell a row that does not apply yet from a row that never will.

PromptProfileModes lists them, auto first, which is the default.

View Source
var ReplyGuardModes = []string{ReplyGuardOn, ReplyGuardOff}

ReplyGuardModes lists them, on first — which is also the default. A reply that has come apart is worth almost nothing and costs the whole of the next request, because it goes back into the conversation and the model reads its own soup before writing more.

RoutingModes lists them, simple first — which is also the default, and the order the row cycles in.

View Source
var SSHIPQoSChoices = []string{"lowdelay", "af21", "none"}
View Source
var SearchProviders = []string{SearchProviderAuto, "firecrawl", "duckduckgo", "exa", "jina-search"}

SearchProviders are the answers the search row accepts.

The names are STRINGS HERE and not search.RegisteredSearch, for the reason DocumentEngines is a literal: a settings value is a word on disk, and a list derived from a registry would silently change what a person's saved answer means the day a plug is renamed or one is added. The cost is that a new plug needs a line here to be pinnable — which is the right cost, because a plug nobody can name in the sheet is still reachable through auto. jina-search is named here too so every registered search plug is pinnable.

SettingCategories is the render order of the sheet. Spending leads the three new sections because "what may it spend" is the question people arrive with; safety and tasks follow it in the order the design's own hierarchy names.

View Source
var TaskAuditModes = []string{TaskAuditOn, TaskAuditOff}

TaskAuditModes lists them, on first — which is also the default: a node that verifies its own work is the whole point of the verified frontier, and the audit's cost is the price of trusting what merges.

View Source
var TaskSettleModes = []string{TaskSettleAsk, TaskSettleAuto}

TaskSettleModes lists them, ask first — which is also the default. Deciding on somebody's behalf is a thing they say yes to, never a thing they get by saying nothing.

TaskStartModes lists what may be chosen, the default first. The retired word is not in it, and that absence is the whole mechanism — the sheet's choices, the writer's validation and the resolver all read this one list.

TimestampModes lists them richest first, which is also the default order the row widens in.

View Source
var ToolApprovalModes = []string{"prompt", "allow", "deny"}

ToolApprovalModes are the three answers the tool gate can be set to, in the order they widen: ask about everything, run everything, refuse everything. They are internal/approval's own words — the registry must not invent a fourth spelling for a decision that package already names.

View Source
var WorkModes = []string{WorkFold, WorkOpen}

Functions

func APIKeyAt

func APIKeyAt(profileDir string) string

APIKeyAt is the key a session opened on this profile would talk with, in Load's own order: the OpenRouter variable, the OpenAI one, then the profile file. It is the reading the settings row and the first-run setup share, so neither can say "no key" while Load would have found one.

func APIKeyConfigured

func APIKeyConfigured(profileDir string) bool

APIKeyConfigured is whether a session opened on this profile has a key to talk with, from anywhere Load would look.

func APIKeySourceAt

func APIKeySourceAt(profileDir string) string

APIKeySourceAt names the rung APIKeyAt resolved without exposing the key. An empty result means no key was found.

func APIKeySourceForModel

func APIKeySourceForModel(profileDir string, sources modelsource.Set, model string) string

APIKeySourceForModel explains the default provider's credential only when that provider actually serves the model. Connected providers have their own key ladders, so borrowing the default explanation would name another key.

func ActiveConnectionFor added in v0.3.0

func ActiveConnectionFor(model string, sources modelsource.Set) (modelsource.Connected, bool)

ActiveConnectionFor answers the service a conversation's live model answers on. THE ACTIVE CONNECTION IS DERIVED FROM THE CONVERSATION'S MODEL, NOT THE SHARED PROFILE: a caller holding the model this conversation actually runs ([app.model] in the talk surface, the deferred target while a move waits out a working turn) must not be routed through ChatModelAt, which is the LAST model any conversation settled on and is written asynchronously by the engine host — two tabs on different connections would read each other's. The blank-model and empty-set guards are the only logic here; everything else is [Set.For], which answers with the default service when no connected Written prefix matches — so the switcher that hands this set around reaches the default service the same way the conversation itself does. False only when there are no services at all, or the conversation has settled on no model yet.

func ApplyTeamDefault

func ApplyTeamDefault(profileDir, key, raw string) error

ApplyTeamDefault writes one `teams.` row in profileDir the way the settings tab writes it: the registry row's own Apply, so the bands, the empty cap and the refusal words are the row's and not a second copy. A key that is not one of the five defaults is refused and nothing is written. The settings tab over --host asks the engine to call this on the engine's profile, which is the file the far session's teams actually inherit.

func AssistedByModelAt

func AssistedByModelAt(profileDir, model string) string

AssistedByModelAt is the model a door hands the attribution line: this model when the `attribution.model` row is on, and nothing when it is off, which leaves the line bare. It is the ONE place the row is turned into a name, so every door that builds a leaf loop answers it the same way.

func AttributionModelAt

func AttributionModelAt(profileDir string) bool

AttributionModelAt resolves whether the `Assisted-by` line codeaf signs its commits with names the model. A malformed pin reads as the default rather than refusing a launch over a name.

func BackgroundChecksAt

func BackgroundChecksAt(profileDir string) string

BackgroundChecksAt resolves the background-checks row to its word, default on. It is the person's INTENT and not a reading of the machine: what the settings row shows is derived from the timer ([Settings.build]), and this is what the row was last told.

func BackgroundChecksWantedAt

func BackgroundChecksWantedAt(profileDir string) bool

BackgroundChecksWantedAt is BackgroundChecksAt as the bool the launch reads before it repairs a timer that has drifted off a program that moved (cmd/codeaf's chatv3_process.go). A person who turned the row off is a person whose machine must stay as they left it.

func BashApprovalsAt

func BashApprovalsAt(profileDir string) string

BashApprovalsAt resolves the bash rules row as the person wrote it. The text is the record; ParseBashApprovals is how a caller reads it.

func BashBackgroundAfterAt

func BashBackgroundAfterAt(profileDir string) int

BashBackgroundAfterAt resolves the foreground handoff clock in seconds. A persisted zero is the person's answer, not an absent value.

func BashShapes

func BashShapes(line string) []string

BashShapes derives the shapes one command line could be remembered as, in the order the card offers them: the command's own words first, then the program alone, then the line itself.

The order is not width. `git status*` is narrower than `git *` and leads anyway, because it is the shape a person pressing always on git status usually means, and the widest reading of what they did is the one that has to be read carefully rather than the one that is offered first. The LINE is last for the reason the head of this file gives.

A COMPOUND LINE DERIVES NOTHING. An allow rule vouches only for a command it matches whole (internal/approval's bash.go), so every shape drawn off `cd /tmp && rm -rf build` would be a rule that cannot fire, and offering one would be offering somebody a choice between three things that do nothing.

The result is never longer than [bashShapeCount], and where it is not empty its last entry is the trimmed line — which is what lets the card say "just this line" about that entry and mean it.

func BestMediaModel

func BestMediaModel(models *catalog.Catalog, modality string) string

BestMediaModel resolves the documented preference order for one modality against what the catalog advertises right now.

func BriefAfterAt

func BriefAfterAt(profileDir string) (time.Duration, error)

BriefAfterAt resolves the absence that earns an arrival brief.

func BudgetConfigPath

func BudgetConfigPath(profileDir string) string

BudgetConfigPath is config.json in codeaf's state root unless CODEAF_PROFILE_DIR supplies the same alternate root used by measured profiles.

func CandidateMediaModel

func CandidateMediaModel(models *catalog.Catalog, modality string) string

CandidateMediaModel is the CATALOG rung of the use-time resolver: the best model the catalog advertises for one modality, by the documented preference order and then by the catalog's own order.

It is deliberately not BestMediaModel. That one answers a person who typed "best" and falls back to the most expensive advertised row, because price is the only quality signal a catalog row carries and somebody who asked for the best has asked to be spent on. A fallback nobody asked for must not reach for the most expensive thing on the shelf.

func CatalogHTTPClient

func CatalogHTTPClient(service modelsource.Connected) *http.Client

CatalogHTTPClient is the one door onto a model service's listing transport. Codex needs the profile's rotating bearer and account headers; every ordinary OpenAI-compatible service keeps the catalog's default client by answering nil.

func CatalogOptionsFor

func CatalogOptionsFor(service modelsource.Connected, profileDir string) catalog.Options

CatalogOptionsFor is the one construction door for a connected service's catalog compartment. Keeping the source identity, address, key, profile and account-aware client together makes it impossible for a new listing road to send Codex's persisted sentinel through the generic HTTP client.

func ChatDefaultAt

func ChatDefaultAt(profileDir string) string

ChatDefaultAt is the one bottom rung for a newly opened conversation and a headless work seat. Explicit model rows are resolved above this function.

func ChatModelAt

func ChatModelAt(profileDir string) string

ChatModelAt is the conversation model this profile last settled on, or empty when nobody has chosen one. Empty is not a failure and never a model name: the caller falls through to its own default, which is what an unconfigured install has always opened on.

func CheckEnvModel

func CheckEnvModel() string

CheckEnvModel is the check seat's environment rung alone, for the chat's runs: a conversation has no flag, and CODEAF_CHECK_MODEL reaches a `/task` run the way the manual says it reaches a headless one. Empty when unset.

func ClampSplitPct

func ClampSplitPct(pct int) int

ClampSplitPct keeps the divider inside the usable band. Zero stays "unset" and resolves to the default at layout time.

func ClearCrewPin

func ClearCrewPin(profileDir string, seat crewroute.Seat) error

ClearCrewPin unpins one seat: the row is removed, and the seat is routed.

func ClearCrewPins

func ClearCrewPins(profileDir string) error

ClearCrewPins unpins every seat in one write.

func ClientConfigFor

func ClientConfigFor(sources modelsource.Set, model string) provider.Config

ClientConfigFor assembles the provider settings for one model out of the services a caller already holds.

IT IS THE ONE PLACE A KEY AND A BASE URL BECOME A provider.Config, and that is the law rather than a convenience: six sites used to compose that literal themselves, so a level that belongs beside the slug travelled inside it and a second service would have reached none of them. A caller that holds a whole profile wants Config.ClientConfig; this door is for internal/session, whose own Config carries the account and nothing else. THE MODEL ID NOW DECIDES THE ACCOUNT, and nothing above this line chooses a key or a base URL.

func CodexConnectionWord

func CodexConnectionWord(email, plan string, outcome modelsource.Outcome) string

CodexConnectionWord is the browser road's shared success sentence. Missing account details stay absent, and a fallback list is named without inventing an account or plan.

func CodexRememberedModels

func CodexRememberedModels(service modelsource.Connected, profileDir string) []catalog.Model

CodexRememberedModels is the Codex catalog this profile remembers for the connected service, every row with the window it accepts.

A ROW REMEMBERED BEFORE #1383 CARRIES NO WINDOW, because the connection that wrote it dropped the listing's `context_window`. For a model the fallback list names, the fallback's figure stands in: it is the same observation of the same account listing that ConnectCodex remembers when the listing cannot be reached, so it is a fact this build already holds about that id rather than a guess about a model nobody has heard back from. Any other id keeps its zero, which every reader already treats as "nobody can say". It is the one door both readers of a Codex catalog take — the process shelf an engine's sessions ask for a window, and the surface's own rows — so a profile connected by an older build reads 272k the next time it opens, without connecting again.

func CompletionReserveAt

func CompletionReserveAt(profileDir string) int

CompletionReserveAt resolves the answer-and-reasoning reserve the same way.

func ConnectCodex

func ConnectCodex(ctx context.Context, profileDir string, tokens codexauth.Tokens) (modelsource.Outcome, error)

ConnectCodex keeps a completed browser sign-in, persists its service row and seeds the picker from the account's own visible model list.

func ConnectService

func ConnectService(ctx context.Context, profileDir string, row PersistedSource, src modelsource.Source, authors []string) (modelsource.Outcome, error)

ConnectService proves a key and says what it reaches, and writes nothing on any answer but yes. Ten seconds; a person is watching this one.

func ConnectionDetailWords

func ConnectionDetailWords(words string, limit int) string

ConnectionDetailWords keeps a vendor's sentence on one bounded line. A cut retreats to a word boundary so the shared outcome never ends in half a word.

func ConnectionOutcomeWord

func ConnectionOutcomeWord(service string, outcome modelsource.Outcome) string

ConnectionOutcomeWord is the one person-facing account of a model-service key check. Both the chat panel and the terminal command call this function so a service cannot be accepted in one voice and refused in another.

func ConsentTimeoutAt

func ConsentTimeoutAt(profileDir string) int

ConsentTimeoutAt resolves the approval countdown, in seconds. 0 is a clock that is off: the question waits from the start. A positive number is how long the reminder runs before the card pauses; it never answers no.

It tests ok before it tests the number for the reason TaskAutoApproveAt does: a persisted 0 is a person who turned the clock off, not an absence.

func ContextFillAt

func ContextFillAt(profileDir string) int

ContextFillAt resolves the fill law: environment pin, then the persisted row, then the package default. A malformed pin reads as the default.

func ContextReuseAt

func ContextReuseAt(profileDir string) int

ContextReuseAt resolves the cumulative re-send allowance the same way.

func Credentials

func Credentials(profileDir string) []string

Credentials is every credential this profile is configured with, in the exact values a call would carry — the key the models are talked to with, the search keys, an application secret somebody pasted for their own Google registration.

IT EXISTS SO THAT A RECORD OF A RUN CAN PROMISE A PERSON THEIR KEY IS NOT IN IT. The debug record (internal/trace) redacts credentials by shape — `Bearer …`, an `sk-…` token, a field spelled `authorization` — and a shape is only ever the keys somebody thought of: a Google `AIza…`, a Groq `gsk_…` or a self-hosted endpoint's plain token would land verbatim in a folder a person is about to attach to a bug report. The exact values are the only thing that catches a key whose shape nobody has seen, and this is where a process asks for them.

THE SETTINGS REGISTRY IS THE SOURCE, and this walks it rather than keeping a second list: a row that marks itself `Secret` is a credential, and a credential row added later is covered here the day it is added, with no second place to remember. Values are read the way every one of those rows reads them — the row's own environment variable first, then the profile file — and the empty ones are left out, so an unconfigured machine registers nothing and every record stays exactly as it was written.

func CreditsKeyPrint

func CreditsKeyPrint(key string) string

CreditsKeyPrint makes a stable identifier for a key without persisting the key.

func CreditsLowAt

func CreditsLowAt(profileDir string) bool

CreditsLowAt reads only a known-low record; damaged and missing records are unknown.

func CreditsNeedRead

func CreditsNeedRead(profileDir, key string) bool

CreditsNeedRead asks again at launch for a missing, changed, or low record.

func CrewAllowedAt

func CrewAllowedAt(profileDir string) crewroute.Allowed

CrewAllowedAt is the allowed-models rule. A rule that does not parse — a hand edit — reads as the default, the way a retired choice reads everywhere else on this sheet; the writer refuses one.

func CrewCallPrice

func CrewCallPrice(model string) (prompt, completion, cacheRead float64, ok bool)

CrewCallPrice is a model's prices per token — prompt, completion, cache read — as the catalog or the evidence table knows them. ok is false only for a price NOBODY KNOWS: a model the catalog does not list, lists with no price, or lists at nothing without being a free pool. A free pool's price is known, and it is nothing.

func CrewCallPriceAt

func CrewCallPriceAt(profileDir string) func(model string) (prompt, completion, cacheRead float64, ok bool)

CrewCallPriceAt is CrewCallPrice on one profile's connections: a call SENT THROUGH A SUBSCRIPTION PLAN OR TO A MODEL ON THIS MACHINE bills nothing per token, so it is priced at nothing whatever the catalog lists for the model — a z-ai coding plan answers `z-ai/…` ids the catalog prices as metered. The connections are read once, on the first call priced.

func CrewCandidateNames

func CrewCandidateNames(profileDir string) []string

CrewCandidateNames is what a logged decision was made among.

ONLY WHAT COULD SIT A SEAT IS NAMED: a speech, image or embedding model the catalog lists is no candidate for a crew, and a log row naming it would claim the router weighed it.

func CrewCandidatesAt

func CrewCandidatesAt(profileDir string) []crewroute.Candidate

CrewCandidatesAt is what the router may pick from on this profile: every catalog model the allowed rule admits, with every route a connected provider offers it on — plans and local models first, then a direct connection, then the default service, which is the order a tie between routes of equal cost is broken in. A model no connected provider reaches is not a candidate, however cheap.

A provider the person turned off carries none of those routes (crewroute.ProvidersOff), so a model only it reached is no candidate either — the route cost the router weighs is always a route it may take.

func CrewCapAt

func CrewCapAt(profileDir string) float64

CrewCapAt is the daily cap on crew spend, zero for none.

func CrewFreeRoutesAt

func CrewFreeRoutesAt(profileDir string) bool

CrewFreeRoutesAt is whether the crew may route to providers' free pools. Absent is off.

A FREE ROUTE IS A ROUTE, NOT A MODEL. On, a model's free pool joins its other routes (crewroute.Free) and is weighed at what it is expected to cost — its refusals included — and a seat on it falls through to the same model's paid route before any other model. Off, the free pools are not routes at all; a person can still pin one by name.

func CrewGapsAt

func CrewGapsAt(profileDir string) []crewroute.Gap

CrewGapsAt names what the allowed models leave uncovered, for the panel's one-line warning. A pinned seat is judged by its pin alone, read from the catalog whether or not a route to it is healthy right now, because the pin is what the seat runs.

func CrewHealthySend

func CrewHealthySend(profileDir, send, chatModel string) string

CrewHealthySend is the model an auxiliary call — a summary, a brief, a landing's answer, anything that is not a crew seat's own call nor the person's turn — should ask instead of send, when send's route is one route health says will not answer: quarantined or cooling, on an account out of credit, on a provider whose key was refused. It is the router's standing worker pick, then the seat's rescue; send itself when its route is healthy or nothing better is reachable.

AN AUXILIARY CALL NEVER PROBES. Only a task's first seat call asks an account out of credit again (RouteCrew); a helper that did would spend a refusal on every summary.

func CrewLogAt

func CrewLogAt(profileDir string) router.CrewLog

CrewLogAt is the router log's account of crews on this profile, for the panel: today's spend and tasks and the recent ones.

func CrewPinAllowed

func CrewPinAllowed(profileDir string, pin CrewPin) error

CrewPinAllowed says why a pin may not be written, or nil. The model must be one the allowed rule admits — by its figures when the catalog knows it, by name otherwise — and a pinned provider must be connected and must reach it.

func CrewPinsAt

func CrewPinsAt(profileDir string) map[crewroute.Seat]CrewPin

CrewPinsAt is every pinned seat.

func CrewProvidersOffAt

func CrewProvidersOffAt(profileDir string) crewroute.ProvidersOff

CrewProvidersOffAt is the providers turned off for the crew. A row that does not read — a hand edit — reads as none off, the way a rule that does not parse reads as the default.

func CrewRecordOf

func CrewRecordOf(d crewroute.Decision, repo, title string) router.CrewRecord

CrewRecordOf is one decision as the router's log keeps it.

func CrewRescue

func CrewRescue(profileDir string, class crewroute.Class, seat crewroute.Seat, chatModel string) []crewroute.Pick

CrewRescue is the last of a seat's ladder, after everything the router offered: the seat as the last crew that completed a task on this install ran it, then the model the person is talking to — each only when its route is healthy and the model can sit the seat. The router's own rungs come first (crewroute.Decision.Ladder).

func CrewRouteAnswers

func CrewRouteAnswers(profileDir, send string) bool

CrewRouteAnswers is whether route health expects send's route to answer: not quarantined or cooling, not on an account out of credit, not on a provider whose key was refused. A profile with nothing wrong on it answers at once, without reading its providers.

func CrewSeatCeilings

func CrewSeatCeilings(d crewroute.Decision) map[crewroute.Seat]float64

CrewSeatCeilings is each seat's own spend ceiling on one task, keyed by the seat rather than its model: the CHECKER'S, a few times what it is expected to cost and never under a floor. A check is the seat whose length nothing else bounds — it reads until it is satisfied — and one that ran to eleven times its estimate on a dear model was the whole of a day's overshoot. A seat on a route that bills nothing has no ceiling.

func CrewSeatKey

func CrewSeatKey(seat crewroute.Seat) string

CrewSeatKey is the registry row a seat's pin is written in — the settings sheet's one way of telling a seat's row from the other tier rows.

func CrewSeatTier

func CrewSeatTier(seat crewroute.Seat) string

CrewSeatTier is the tier row a seat's pin is written in.

func CrewSpendCap

func CrewSpendCap(profileDir string, withDaily bool) (float64, string)

CrewSpendCap is the day's cap a crew's seat calls are held to, and the one sentence a call it stops ends on: the crew's own daily cap (CrewCapAt), or — where withDaily says the run is bound by it — the day's spending limit (DailyBudgetUSDAt), whichever is lower. Zero is no cap.

func CrewTaskCapAction

func CrewTaskCapAction(capUSD float64) string

CrewTaskCapAction is the sentence a call the per-task limit stops ends on.

func CrewTaskCapAt

func CrewTaskCapAt(profileDir string) float64

CrewTaskCapAt is the most one task may spend, in dollars: the stored figure when it is above zero, CrewTaskCapDefault otherwise. There is always one.

func CrewTaskMoney

func CrewTaskMoney(usd float64) string

CrewTaskMoney is a per-task limit in words: whole dollars without cents ($5), anything else to the cent ($2.50).

func CrewTaskSpendCap

func CrewTaskSpendCap(profileDir string) (float64, string)

CrewTaskSpendCap is the per-task limit every priced call of one task is held to (CrewTaskCapAt) and the sentence a call it stops ends on. It is not lifted by --yes-spend: that flag answers the day's questions, not this.

func CrewTierSeat

func CrewTierSeat(tier string) (crewroute.Seat, bool)

CrewTierSeat is the seat a tier row pins, false for a tier that is not a crew seat (reflex, small work).

func DailyBudgetConfigured

func DailyBudgetConfigured(profileDir string) bool

DailyBudgetConfigured is whether a person has answered the daily ceiling: the environment pins it, or the profile file holds it.

func DailyBudgetUSD

func DailyBudgetUSD() (float64, error)

DailyBudgetUSD resolves the dollar rail without requiring a provider key. Status-only commands use it even when they never construct a model client.

func DailyBudgetUSDAt

func DailyBudgetUSDAt(profileDir string) (float64, error)

DailyBudgetUSDAt resolves env → persisted config → built-in default. The env remains the explicit headless override; /budget default writes the middle layer used by both chat and one-shot runs.

func DefaultEffortAt

func DefaultEffortAt(profileDir string) effort.Rung

DefaultEffortAt is the rung this profile last settled on, or effort.Ship when nobody has chosen one.

Missing settings use the shipped default; saved choices remain authoritative. Legacy "off" files still mean absence and are displayed as auto.

func DisconnectService

func DisconnectService(profileDir, id string) error

DisconnectService removes one service row and its stored credential. Codex's rotating tokens live in their owner-only sibling file and leave with it.

func DocumentEngineAt

func DocumentEngineAt(profileDir string) (string, error)

DocumentEngineAt resolves the document-reading rung.

func DraftPersistAt

func DraftPersistAt(profileDir string) bool

DraftPersistAt resolves whether the v3 chat surface keeps the unsent draft on disk between sessions. Shaped exactly like HistoryEnabledAt.

func EffortWord

func EffortWord(rung effort.Rung) string

EffortWord names absence as auto in settings and persisted configuration.

func EnsurePersistedAPIKey

func EnsurePersistedAPIKey(profileDir string) (bool, string, error)

EnsurePersistedAPIKey copies the session's environment key into the profile config exactly once, so the standing watch can authenticate after the shell that ratified it is gone. It never overwrites a key already on disk, and the file ends owner-readable only. Returns whether a key was newly persisted and the path that holds it.

func ExaKeyAt

func ExaKeyAt(profileDir string) string

ExaKeyAt resolves the Exa credential: the environment first, then the sheet, then empty — and empty is a working configuration, not a fault.

func FallbackMediaModel

func FallbackMediaModel(modality string) string

FallbackMediaModel is the curated rung, and empty for a modality this build has no remembered name for.

func FirecrawlKeyAt

func FirecrawlKeyAt(profileDir string) string

FirecrawlKeyAt resolves the optional Firecrawl ceiling credential the same way.

func FirstPrompt

func FirstPrompt(profileDir string) bool

FirstPrompt reports whether this profile has never chosen a conversation model. A fresh install's first typed line is this: ChatModelAt is empty and the talk slot is still the build default. The hedge uses it so a stall there names `/model` instead of sitting silent until the ninety-second first-token cut (F42).

func FormatBashApprovals

func FormatBashApprovals(rules []BashRule) string

FormatBashApprovals writes the rules back as the row's own text, quoting only the globs that need it. A glob that reads plainly stays plain: the row is a line a person edits by hand, and quotes around every entry would be a file format wearing a settings row's clothes.

func GoogleOAuthClientAt

func GoogleOAuthClientAt(profileDir string) (id, secret string)

GoogleOAuthClientAt resolves the Google registration: the environment first, then the sheet, then the registration this build ships with (connect_defaults.go, which states why a desktop client's secret may be compiled in at all). So the answer is never empty, and every build can offer to connect an account without a person filling in a registration form first.

IT ANSWERS BOTH HALVES OR NEITHER IS WORTH HAVING, which is why it is one call and not two. An id without its secret cannot ask Google for anything, so a caller handed half a pair would have to write the same "and the other one" check every reader of these rows already needs; here it is written once, and the caller's test is the one it should be — is the id there.

EACH HALF WALKS THE RUNGS ALONE, which is the shape the rows already had: the two are separate values a person copies from two separate boxes, and each is resolved by the same credentialAt every other credential uses. The one edge that leaves is a person who answers ONE half of their own registration — they get their id against this build's secret, which Google refuses — and the cure is the obvious one, answer the other half too, which is what the sheet's hint on both rows already says.

func GuardianAt

func GuardianAt(profileDir string) string

GuardianAt resolves whether a small model answers a tool prompt before the person is asked. An unrecognised persisted value reads as the default, which is off — a garbled setting must never be the one that appoints a stand-in.

func GuardianEnabledAt

func GuardianEnabledAt(profileDir string) bool

GuardianEnabledAt is GuardianAt as the bool internal/session's Config takes. The two exist separately because the row's value is a WORD — that is what the sheet renders and what the file holds — and the seam on the other side is a switch; one function answering both would have to pick which of those it lies about.

func HeadlessToolApprovalModeAt added in v0.4.0

func HeadlessToolApprovalModeAt(workspace, profileDir string) (string, error)

HeadlessToolApprovalModeAt requires an explicit setting to open an unwatched gate. Invalid saved values retain the ordinary reader's conservative fallback.

func HintsAt

func HintsAt(profileDir string) bool

HintsAt resolves whether the v3 chat shows its tips, default on. A row that will not parse reads as the default rather than as off, for TaskColumnAt's reason: a garbled row must not quietly take a newcomer's only pointers away.

func HistoryEnabledAt

func HistoryEnabledAt(profileDir string) bool

HistoryEnabledAt resolves whether the v3 chat surface records what was typed into ~/.codeaf/v3/history.jsonl. A malformed pin reads as the default rather than refusing a launch over a recall list.

func IconsAt

func IconsAt(profileDir string) string

IconsAt resolves the step icon preference. Unknown values use detection rather than turning off the normal rich presentation.

func InstallLaneRows

func InstallLaneRows(profileDir string)

InstallLaneRows hands this profile's routing posture to the process-wide knobs the transport reads it from. It uses the RESOLVER'S entrance so loading a row already in force never forgets a retirement the wire earned; only a person's own act belongs at provider.RepinLane.

THE ROUTING ROW IS ONE OF THEM, and it is here rather than only on the session's own config because of the clients nobody hands one to. The harness, the subharness, `read_document`, `view_image` and a panel's members are all assembled through Config.ClientConfig, which carries no routing answer — so before this line they ran on the default whatever a person had written, and one of them could retire a person's own pin, process-wide, before any wire was asked (internal/provider's velocity.go says what that cost). It is the CHOICE and not the resolved default, so an unwritten row installs nothing and every client falls to the shipped row together (provider.DefaultRouting).

func InstallPersistedEnv

func InstallPersistedEnv(profileDir string)

InstallPersistedEnv exports the persisted value of every knob whose only reader is the process environment, so a choice made in the sheet survives a relaunch without a second lookup path. A variable the user actually set is never overwritten — the environment still wins.

func InstallRoutingRow

func InstallRoutingRow(profileDir string)

InstallRoutingRow hands the routing row ALONE to the transport, and it is the half of InstallLaneRows the settings panel calls by itself: somebody cycles `routing`, the row is written, and the very next request has to go out under it. The lane rows beside it are untouched because nothing about them changed — re-stating a pin here would be a resolver's write nobody asked for.

func IsFreeModel

func IsFreeModel(id string, priced bool, prompt, completion, request float64) bool

IsFreeModel accepts either the published free suffix or a catalog row whose three charges are known zeros. An unknown price never claims to be free.

func JinaKeyAt

func JinaKeyAt(profileDir string) string

JinaKeyAt resolves the Jina credential the same way.

func LaneAt

func LaneAt(profileDir, slot string) string

LaneAt is the lane one slot is held to, default LaneAuto. A blank or unreadable row reads as auto rather than as a pin nobody can see.

func LaneBorrowAt

func LaneBorrowAt(profileDir, slot string) bool

LaneBorrowAt is whether a pinned slot may be borrowed away from when its lane is slow. It is false unless somebody said so: a pin means the machine they named, and widening it on their behalf is not this row's to do.

func LaneBorrowKey

func LaneBorrowKey(slot string) string

LaneBorrowKey is the row beside it: whether a PINNED lane may still be borrowed away from when it is slow. It is a second key rather than a fourth word in the first because the two are independent — borrowing means nothing under `auto` — and because a pin that quietly stopped being a pin the day somebody turned rescuing on would be a promise this build did not keep.

func LaneGuardAt

func LaneGuardAt(profileDir string) bool

LaneGuardAt resolves the speed-guard row. A malformed row reads as the default rather than quietly turning the rescue off — a person who never touched this row has not asked to wait.

func LanePinAt

func LanePinAt(profileDir, slot string) provider.LanePin

LanePinAt is one slot's lane row resolved into the answer the transport takes. The three states of the row are the three states of the pin, and a row nobody has written is `auto` — the belief chooses per answer.

func LanePinned

func LanePinned(profileDir, slot string) (string, bool)

LanePinned is the lane named by a slot's row, and false when the row names no machine — which is every reading of `auto` and of `openrouter`.

func LaneRowWord

func LaneRowWord(profileDir, slot string) string

LaneRowWord is one slot's lane row as a person reads it: `auto`, `openrouter`, `pinned: cloudflare`, or `pinned: cloudflare, borrow when slow`. It is here rather than in a surface because the same words are what WriteLaneRow reads back, and two spellings of one row is how a row stops round-tripping.

func LaneSettingKey

func LaneSettingKey(slot string) string

LaneSettingKey is the row holding one slot's lane: `auto`, `openrouter`, or a lane's own name as the wire spells it.

func LivePinsAt

func LivePinsAt(profileDir string) string

LivePinsAt is the pinned-roles row AS IT IS ACTED ON: the person's own text with the pins this build will never consult taken out.

THE ROW DRAWS WHAT IS TRUE. ModelRolesAt is the raw stored string and stays that, because the writer needs the text to rewrite; but a row that DISPLAYED it would draw `compaction: some/model` as a live pin while the role list under it had no such line, and the person would be reading a setting that does nothing. What they are told instead is RetiredPinNote, next to the row.

func LogCrewDecision

func LogCrewDecision(profileDir, call string, d crewroute.Decision, repo, title string)

LogCrewDecision writes one task's crew decision into the profile's router log, under the call id the outcome will settle.

func LogCrewOutcome

func LogCrewOutcome(profileDir, call string, d crewroute.Decision, repo, title, outcome string, costUSD float64)

LogCrewOutcome settles it: accepted, redone stronger, or not kept.

func LogCrewRoute

func LogCrewRoute(profileDir, call string, d crewroute.Decision, repo, title string, seat crewroute.Seat, pick crewroute.Pick, kind string, until time.Time)

LogCrewRoute writes one seat's first-call outcome on its route: answered (kind empty), or failed with a route-failure kind and when the route said it may be asked again.

func LooksLikeAPIKey

func LooksLikeAPIKey(key string) bool

LooksLikeAPIKey is the shape check the first-run setup applies to a pasted key, and it is deliberately only a shape check: it spends no network call, because the setup runs before a person has agreed to spend anything. An OpenRouter key reads `sk-or-v1-…`; an OpenAI-shaped key, which Load also accepts from the environment, reads `sk-…`. Whitespace inside is a paste that picked up a line break, which is the one thing worth refusing here rather than discovering as a 401 on the first turn.

func MarkSetupSeen

func MarkSetupSeen(profileDir string, at time.Time) error

MarkSetupSeen records that the setup was shown now, through the same atomic writer every setting uses.

func MediaSlotModelAt

func MediaSlotModelAt(profileDir, slot string) string

MediaSlotModelAt is ONE CAPABILITY SLOT'S MODEL, in the order every other persisted knob resolves in: the operator's environment variable, then the row the settings sheet wrote into the profile, then nothing.

Nothing is the honest last answer rather than a curated name. This function answers "what did a person choose", and the ladder that turns no choice into a working model is the resolver's business (cmd/codeaf's chatv3_media.go, and CandidateMediaModel behind it) — a default invented here would be a rung the resolver could not tell from a deliberate pin.

A slot that is not a capability slot answers nothing at all: "talk" is the conversation, and reading CODEAF_MODEL through this door would let a media resolver quietly take the chat model for a modality it cannot serve.

func MemoryAt

func MemoryAt(profileDir string) string

MemoryAt resolves the memory row to its word, default on.

func MemoryEnabledAt

func MemoryEnabledAt(profileDir string) bool

MemoryEnabledAt is MemoryAt as the bool the v3 door reads before it opens a brain at all: memory off is a session handed no store, which is what makes "no block and no calls" a property of the wiring rather than a branch every caller has to remember (internal/session's memory.go states the law).

func MigrateCrew

func MigrateCrew(profileDir string) (string, error)

MigrateCrew writes a retired crew's reading down, once, and splits any old model@provider tier row into a model row and a route row. The split alone answers no line because it changes no route the person chose.

THE LINE IS SAID ONCE BECAUSE THE MIGRATION IS DONE ONCE: after it, the retired rows are gone and nothing is left to say it about. It names what stayed pinned, because a person whose crew changed shape under them is owed the half that did not.

func ModelCandidates

func ModelCandidates(models *catalog.Catalog, slot string) []catalog.Model

ModelCandidates is the shared capability gate for every slot in the model palette. Keeping music's TTS exclusion here makes discovery and runtime resolution agree about what can occupy that slot.

func ModelFallbacksAt

func ModelFallbacksAt(profileDir string) string

ModelFallbacksAt resolves the fallback chain as the person wrote it.

func ModelMatches

func ModelMatches(models *catalog.Catalog, slot, word string, limit int) []string

ModelMatches returns the models in slot that word could mean, best first and at most limit of them. Exactly one result is an unambiguous resolution; more than one means the word was genuinely ambiguous and the caller should ask. Scoring is contains-and-prefix on purpose: subsequence fuzziness is fine for a palette a human is watching, and far too loose for a word lifted out of a sentence.

func ModelPoolAt added in v0.3.0

func ModelPoolAt(profileDir string) poolcfg.Config

ModelPoolAt resolves how the Model Pool behaves: the stored mode word, the stored public key and the pool's environment names, through poolcfg.Resolve. IT IS THE ONE PLACE THE PROCESS ENVIRONMENT IS READ FOR THE POOL — poolcfg itself takes the environment as a function, and a caller with its own (the `codeaf pool` verb, whose tests inject one) resolves ModelPoolSettingAt and ModelPoolPublicKeySettingAt against its own lookup rather than calling this.

func ModelPoolPublicKeySettingAt added in v0.3.0

func ModelPoolPublicKeySettingAt(profileDir string) string

ModelPoolPublicKeySettingAt is the stored word the pool key row holds: base64 text, or empty for the key the binary carries. It is the second setting ModelPoolAt resolves.

func ModelPoolResolved added in v0.4.0

func ModelPoolResolved(profileDir string, lookup func(string) (string, bool)) poolcfg.Config

ModelPoolResolved is ModelPoolAt with the environment injected, for the verbs whose tests hand one in. It is where the telemetry off switch reaches the pool: the environment rungs (CODEAF_TELEMETRY, DO_NOT_TRACK) are read by the resolver through lookup, and the two rungs that live on disk — the project file and the profile row that `codeaf telemetry off` writes — are read here and applied with poolcfg.Config.Quieted. The rows, not the pin: a caller that injected an environment must get the answer for THAT environment's CODEAF_TELEMETRY, not the one the harness happens to export (the fall-through [telemetryRowsOff] describes is the one exception).

func ModelPoolSettingAt added in v0.3.0

func ModelPoolSettingAt(profileDir string) string

ModelPoolSettingAt is the stored word the pool row holds: one of the three choices, or empty for a person who has never answered. It is the argument the pool's resolver takes, and the raw half of ModelPoolAt.

func ModelRolesAt

func ModelRolesAt(profileDir string) string

ModelRolesAt resolves the per-role pins as the person wrote them.

func ModelSettingKey

func ModelSettingKey(slot string) string

ModelSettingKey is the registry key fronting one model slot.

func ModifyCrewAllowed

func ModifyCrewAllowed(profileDir string, add bool, word string) error

ModifyCrewAllowed is `/crew models +x` and `-x`: the rule with one word added or taken away.

func MouseAt

func MouseAt(profileDir string) string

MouseAt resolves the mouse row to its word, default off.

func MouseEnabledAt

func MouseEnabledAt(profileDir string) bool

MouseEnabledAt is MouseAt as the bool the surface's View takes — the same word/switch split GuardianEnabledAt documents.

func OffsetKey

func OffsetKey(repo string, class crewroute.Class) string

OffsetKey is the key CrewDay.Offsets is read under.

func ParseCrewSeat

func ParseCrewSeat(word string) (crewroute.Seat, bool)

ParseCrewSeat reads a person's word for a seat. Only the three seat names are accepted: the tier words behind them are machinery.

func ParseModelFallbacks

func ParseModelFallbacks(raw string) []string

ParseModelFallbacks reads the row into an ordered list of model slugs.

It is comma-separated and NOT a pair list, unlike the two model rows above it: there is no key here, only an order, and the order is the whole content. A slug may carry a colon of its own (`…/model:free`), which is exactly why this splits on commas and nothing else.

Blank entries are dropped and duplicates collapse to their first appearance, so a trailing comma or a name written twice is a tidy-up rather than an error. It cannot fail: this row names models, and whether a model exists is a question only the provider can answer.

func ParseModelRoles

func ParseModelRoles(raw string) (map[string]string, error)

ParseModelRoles reads `title:openai/gpt-5-mini` into role → model. The value is split at the FIRST colon only, because a model slug can carry one of its own (`…/model:free`).

A PIN FOR A WORD THAT IS NOT A ROLE IS DROPPED, NOT REFUSED — see [parseModelRoles], which is this and the names it dropped.

func ParseToolApprovals

func ParseToolApprovals(raw string) (map[string]string, error)

ParseToolApprovals reads `read:allow, bash:prompt` into the map internal/approval's Load takes as its "tools" section.

The flat text is a STAND-IN. The structured map — per-tool rules, and the ordered bash pattern list beside them — lands with the settings file that can hold a nested shape; until then a person needs some way to say "never ask me about read", and one line they can read back beats a nested editor nobody has written yet. The parse is strict about the action for the reason approval.ParseAction is: a typo that was silently dropped would be a rule somebody thinks is protecting them.

func PersistedAPIKey

func PersistedAPIKey(profileDir string) string

PersistedAPIKey reads the api_key stored in the profile config file. It is the last rung of Load's key resolution: a timer-driven `codeaf wake` runs with no shell environment, so the profile file is the only place a key can survive to reach it.

func PlanConsentUSDAt

func PlanConsentUSDAt(profileDir string) (float64, error)

PlanConsentUSDAt resolves the estimate above which a plan asks first.

func PracticeBudgetUSDAt

func PracticeBudgetUSDAt(profileDir string) (float64, error)

PracticeBudgetUSDAt resolves the daily self-practice carve-out.

func PracticeIdleAt

func PracticeIdleAt(profileDir string) (time.Duration, error)

PracticeIdleAt resolves the quiet period before self-practice.

func ProfileDir

func ProfileDir() string

ProfileDir is the profile this process reads and writes. Empty is the ordinary answer and means the state root's own profile; the readers below take it as such, so a caller never has to know what the default expands to.

func ProfilePath

func ProfilePath(profileDir, name string) string

ProfilePath names a file this profile keeps, and is THE ONE PLACE THAT KNOWS WHAT AN EMPTY PROFILE DIRECTORY MEANS.

AN EMPTY PROFILE DIRECTORY IS THE NORMAL CASE, NOT THE ABSENT CASE, AND ABSENCE IS A HOSTED WINDOW. ProfileDir carries CODEAF_PROFILE_DIR, which almost nobody exports, so the empty string is what very nearly every launch passes down here — and it has always meant "the profile where it always is", codeaf's own state root, which internal/home owns and CODEAF_HOME moves. A caller that reads emptiness as "there is no profile" and goes quiet is therefore silent on the ordinary launch and loud only on the rare one, which is the exact inversion this function exists to stop being retyped: it has cost the model picker's lane pin, the settings pair on the belt, the status line's crew segment and the notices' own memory, each found separately.

func ProjectBoolAt

func ProjectBoolAt(cwd, profileDir, key string) (bool, error)

ProjectBoolAt is ProjectStringAt for the two on/off rows.

func ProjectConfigPath

func ProjectConfigPath(cwd string) string

ProjectConfigPath is the file this layer reads for one workspace. An empty workspace has no path and therefore no layer.

func ProjectFloatAt

func ProjectFloatAt(cwd, profileDir, key string) (float64, error)

ProjectFloatAt is ProjectStringAt for the dollar row.

func ProjectKeyAllowed

func ProjectKeyAllowed(key string) bool

ProjectKeyAllowed reports whether a row may live in a project file.

func ProjectStringAt

func ProjectStringAt(cwd, profileDir, key string) (string, error)

ProjectStringAt resolves one text row for a workspace: the project file's answer when it has one, otherwise the profile reader that already owns the row, otherwise that reader's default. A missing project file is silent; a broken one stops the caller with the path in the message.

func PromptProfileAt

func PromptProfileAt(profileDir string) string

PromptProfileAt resolves the prompt-profile row to its word: the environment pin, then the persisted row, then `auto`.

AN UNRECOGNISED PIN IS NOT A PIN, and that is where this differs from the bool rows above, which read a malformed pin as their default. The engine already rules it that way (internal/session's promptprofile.go says so at length, after internal/splitgate's Mode): a stale or mistyped word in somebody's shell must not quietly move a conversation onto the other arm, and it must not quietly cancel the row they did choose either. So a word this list does not have falls through to the row, exactly as an unset variable does.

func QuickSwitchAt

func QuickSwitchAt(profileDir string) bool

QuickSwitchAt resolves whether the conversation switcher's chord switches on each press, default on. A row that will not parse reads as the default rather than as off, for TaskColumnAt's reason: a garbled row must not quietly slow a gesture down.

func ReasoningProfileSeam

func ReasoningProfileSeam(models *catalog.Catalog) func(string) (provider.ReasoningProfile, bool)

ReasoningProfileSeam hands the catalog's published reasoning profile to the adapter in the adapter's own vocabulary. A nil catalog answers "unknown", exactly as its SupportsParameter does.

func RememberBashApproval

func RememberBashApproval(profileDir, match string) error

RememberBashApproval appends one allow rule for the SHAPE a person has just picked on a consent card.

It takes the shape and not the line. The card banks what was CHOSEN (bashshapes.go derives the offer, tui3's consent.go puts it), so the argument here is `git status*` as readily as `git status --short`, and this function asks nothing about which of the two it was handed: a rule is a glob, and the person read the glob before they pressed the key. What it does ask is whether the glob is one worth writing down — [bashShapeHolds] — because a shape that pins nothing down is a row entry that answers for every command there is.

Four refusals, and each of them is internal/approval's law rather than this file's caution:

  • A COMPOUND SHAPE CANNOT BE REMEMBERED. An allow rule vouches only for a single command it matches whole (bash.go's matching law), so a rule written for `cd /tmp && rm -rf build` could never fire. Writing it anyway would put a line in somebody's settings that says they approved something and does nothing at all.
  • A SHAPE THAT NAMES NO COMMAND IS NOT A RULE, and neither is one whose only word is sudo (bashshapes.go states both).
  • A SHAPE THE ROW ALREADY ALLOWS IS NOT WRITTEN AGAIN, whether the rule that allows it is this exact glob or a broader one somebody wrote by hand.
  • A SHAPE THE ROW ALREADY DENIES OR ASKS ABOUT IS LEFT ALONE, and the caller is told. First match wins, so an allow appended after a standing deny is a rule that never runs; the standing rule is a decision the person made in the settings sheet, and a keystroke on a card does not overturn it.

A LINE IS STORED AS THE GLOB IT IS. The dialect has no escape for '*', so a command line containing one is remembered as a pattern with a wildcard in it — `ls *.go` approved is `ls *.go` allowed. That is the honest reading of the line the person saw and approved, it stays bounded to a single non-compound command, and the critical-command table still asks about the shapes that destroy a disk whatever this row says.

func RememberToolApproval

func RememberToolApproval(profileDir, tool, action string) error

RememberToolApproval merges one tool's answer into the person's tool exceptions row — their file, their row, one entry changed and everything else left exactly as they typed it.

An entry that already says this is not written at all: the caller gets nil and the file is not touched, which is what makes pressing always twice a no-op rather than a row that grows.

See this file's head for the one surprising consequence: while a REPOSITORY answers tools.approval, its row replaces the person's whole at launch (projectconfig.go, law 2), so a preference written here takes effect everywhere except inside that repository.

func RenameConnectionModels added in v0.3.0

func RenameConnectionModels(profileDir, oldWritten, newWritten string) (changed []string, err error)

RenameConnectionModels carries a connection rename across every STORED model id the profile holds under the old Written name: the crew's five tier rows, the fallback chain, the role pins and the capability slots. The conversation slot's id is not stored vocabulary here — routing keys on the Written prefix of a model id, so a connection renamed from homelab to lab leaves every homelab/... id answering on a service that no longer exists, resolving to no service and handing itself to the default one: a silent misroute that only fails at send.

THE NAME IS THE ONLY THING THAT MOVES, IN ONE WRITE. The id is re-spelled under the new prefix — the SAME model, spelled the new way — and nothing else about a row is touched; every re-prefixed value passes the tier gate (ValidateTierValue) and the whole decision lands through one [writeProfileValues], so a failure leaves the profile byte-identical. A tier row that was never held stays never held: writing one would convert an inherited tier into a pinned one. Only a row whose value actually changes is written.

Node pins (the plan and work model recorded on already-created tasks) and journaled role bindings are historical records of what ran, and are not rewritten; store.SetRoleBinding has no caller outside the store.

func ReplyGuardAt

func ReplyGuardAt(profileDir string) string

ReplyGuardAt resolves the reply-guard row to its word, default on.

func ReplyGuardEnabledAt

func ReplyGuardEnabledAt(profileDir string) bool

ReplyGuardEnabledAt is ReplyGuardAt as the bool the session's Config takes. The two exist separately for the reason the guardian's pair does: the row's value is a WORD, and the seam on the other side is a switch.

func ReprefixModelID added in v0.3.0

func ReprefixModelID(id, oldWritten, newWritten string) string

ReprefixModelID rewrites one model id whose connection segment is the old Written name. Ids on other services, and bare ids, come back unchanged.

func ResolveMediaModel

func ResolveMediaModel(models *catalog.Catalog, modality, word string) (string, error)

ResolveMediaModel reads one media tool's model argument. An empty word means the caller keeps its slot default. "best" resolves the preference order. Any other word is resolved inside the modality, and a name that belongs to a different modality is refused by naming what it actually makes.

func ResolveSources

func ResolveSources(profileDir, defaultKey, defaultBase string) modelsource.Set

ResolveSources builds the whole registry: the synthesised default service first, then every persisted row this build still knows.

func ResponseAttemptsAt

func ResponseAttemptsAt(profileDir string) float64

ResponseAttemptsAt resolves N. A pin below one is nonsense — a request that is never sent — and reads as the default rather than as an instruction.

── WHAT N MEANS CHANGED, AND THE ROW DID NOT ───────────────────────────────

It was a count of sends and it is a MULTIPLIER ON THE DEADLINE (docs/design/recovery/DESIGN.md §4, taxonomy.Limits.Patience): `3` is three times the role's own give-up — four and a half minutes on a conversation's turn rather than ninety seconds — and never three identical requests. The key keeps its name because the QUESTION a person is answering when they turn it is unchanged: how hard should this try before it tells me it could not. What it no longer buys is the one thing that never helped, which is the same bytes sent again to the machine that has just refused them.

The default is one, where it was three: three was the count this build shipped with, and one is the measured give-up with nothing multiplied on top. A person who had written `3` into their profile when three was the default is asking for three times the patience now — which is a reading of their row this change cannot avoid and says so in its change entry.

func ResponseLiftAfterAt

func ResponseLiftAfterAt(profileDir string) int

ResponseLiftAfterAt resolves K, the same way.

func ResponseLiftCapAt

func ResponseLiftCapAt(profileDir string) float64

ResponseLiftCapAt resolves the cap, in dollars.

A PERSISTED 0 IS A VALUE AND NOT AN ABSENCE, for TaskAutoApproveAt's reason: 0 means no cap, and somebody who wrote it must not find one back in the morning.

func ResponseLimitsAt

func ResponseLimitsAt(profileDir string) taxonomy.Limits

ResponseLimitsAt resolves the whole of taxonomy.Limits for a profile: environment pin, then the persisted row, then the package default, which is the order every other number in this file resolves in.

IT IS ONE READER AND NOT THREE, because the three numbers are one policy and a caller that resolved two of them would be running a boundary nobody configured. The backoff is not among them: it is derived from the attempt count's own schedule and there has never been a reason to turn it apart from the count.

func RestoreCrewState

func RestoreCrewState(profileDir string, state CrewState) error

RestoreCrewState writes a CrewState back, IN ONE FILE WRITE: a row that was absent is removed, and every other row gets its old bytes.

func RetiredPinNote

func RetiredPinNote(raw string) string

RetiredPinNote is what a surface with a person in front of it says about pins this build will not act on, and "" when there are none. It is a sentence and not a log line, because the only reader who wants it is the one looking at the row it is about.

func RetiredRowNote

func RetiredRowNote(key string) string

RetiredRowNote is the sentence a surface prints for a key the loader reported unread because the row it belonged to is gone, and empty for every other key: an unknown key still gets the surface's generic sentence.

func RouteCrew

func RouteCrew(profileDir string, ask CrewAsk) (crewroute.Decision, error)

RouteCrew picks one task's crew on this profile: the seats pinned there or for this task run their pins, and every other seat is routed.

The class is read first, so the learned offset for this repository and this class of work can move the price of a point before the seats are picked. A day at its cap still answers with the crew it would run, beside ErrCrewAtCap; a seat nothing allowed can sit is a crewroute.NoCandidateError.

func RoutingAt

func RoutingAt(profileDir string) string

RoutingAt resolves the routing row to the word in force, DefaultRouting when nobody wrote one. An unreadable or unknown word falls back to the default rather than to off: a garbled row must not quietly stop a session sending what it would otherwise send, and off is a real answer somebody chooses rather than one they arrive at by accident.

func RoutingChoiceAt

func RoutingChoiceAt(profileDir string) string

RoutingChoiceAt is the routing row A PERSON ACTUALLY WROTE, empty when they have written nothing readable.

It is the same read as RoutingAt without the fallback, and the two are both needed because they answer different questions. A settings sheet asks "what is in force?" and must be told the shipped row, which is what an unset row does. The adapter asks "did somebody CHOOSE?", and it must be able to hear no — which is what lets a client that was handed nothing fall to the row this process installed, while an explicit word still wins over both (internal/provider's velocity.go).

func RoutingWord

func RoutingWord(raw string) string

RoutingWord is what an already-read routing row is IN FORCE as: the word when somebody wrote one this build knows, and DefaultRouting otherwise.

A surface that holds the row in hand asks this rather than deciding for itself what an unknown word means, so the shipped row is stated once (internal/tui3's palette.go reads it for the sentence on the `auto` row).

func SaveTaskColumn

func SaveTaskColumn(profileDir string, open bool) error

SaveTaskColumn records what the person did to the column with their hands.

It is EXPORTED because the surface writes it from a chord, and it is the v3 half of the same bargain: this is the one interface row whose value is normally chosen by a keystroke rather than by visiting the sheet, so the key needs a door to disk that goes through the same writer the row's own does. Two doors, one validation.

func SearchOptionsAt

func SearchOptionsAt(profileDir string) search.Options

SearchOptionsAt is the one mapping from profile rows to the search layer's input. Auto becomes an absent pin because the resolver treats absence as the instruction to walk its ladder; credentials retain their environment-first resolution from the rows above.

func SearchProviderAt

func SearchProviderAt(profileDir string) string

SearchProviderAt resolves where a web search goes: the persisted row when it names a provider this build accepts, otherwise auto. An unrecognised value reads as auto rather than as an error, which is the direction a garbled setting may be wrong in — auto still searches.

func SearchProviderHintAt

func SearchProviderHintAt(profileDir string) string

SearchProviderHintAt explains what the searching row means right now. It is recomputed when the sheet rebuilds after a write, so the row describes the next search without turning every rendered frame into a config-file read.

func SetCrewAllowed

func SetCrewAllowed(profileDir, raw string) error

SetCrewAllowed writes the rule in its canonical spelling, refusing one that does not parse and one that would leave a pinned seat outside it.

func SetCrewAllowedRule

func SetCrewAllowedRule(profileDir string, rule crewroute.Allowed) error

SetCrewAllowedRule writes a rule the panel composed (crewroute.Allowed's edits) through the rule's one writer, so a checklist tick is refused for exactly the reason the typed form would be — a pinned seat it would leave outside.

func SetCrewCap

func SetCrewCap(profileDir, raw string) error

SetCrewCap writes the cap: a dollar amount, or `none`.

func SetCrewFreeRoutes

func SetCrewFreeRoutes(profileDir string, on bool) error

SetCrewFreeRoutes turns the free routes on or off.

func SetCrewPin

func SetCrewPin(profileDir string, seat crewroute.Seat, raw string) error

SetCrewPin pins one seat, IN ONE FILE WRITE. `auto` or blank unpins it.

A PIN OUTSIDE THE ALLOWED MODELS IS REFUSED WITH THE REASON, never written and quietly ignored: the rule is the person's own and a pin that broke it would be the one decision on the panel that contradicted another. A pin naming a provider must name one that is connected, for the same reason.

func SetCrewProviderOn

func SetCrewProviderOn(profileDir, id string, on bool) error

SetCrewProviderOn turns one connected provider on or off for the crew, IN ONE FILE WRITE, refusing to turn off the last one on (ErrCrewLastProvider) and a provider that is not connected.

THE ROW KEEPS ONLY CONNECTED PROVIDERS. An id left behind by a connection since removed is dropped on the next write, so a provider disconnected and connected again is on — the same as any connection made after the row was written — and the row never grows a list of names nobody can see.

func SetCrewTaskCap

func SetCrewTaskCap(profileDir, raw string) error

SetCrewTaskCap writes the per-task limit: a dollar amount above zero. A task always has a limit, so `none` and zero are refused.

func SetLane

func SetLane(profileDir, slot, value string) error

SetLane writes one slot's lane. An empty word clears the row back to auto, which is the same thing said two ways and both of them arrive here.

func SetLaneBorrow

func SetLaneBorrow(profileDir, slot string, borrow bool) error

SetLaneBorrow writes the borrow flag beside a pin.

func SettingsGeneration

func SettingsGeneration() uint64

SettingsGeneration is the number of persisted settings writes this process has made. A reader that holds a snapshot keeps the value it read at, and rebuilds when the two differ.

func SetupSeenAt

func SetupSeenAt(profileDir string) time.Time

SetupSeenAt is when the setup was last shown, or the zero time. Skipping with esc counts as shown — the marker records that the person met it, not that they answered it.

func SlackOAuthClientAt

func SlackOAuthClientAt(profileDir string) string

SlackOAuthClientAt resolves the Slack registration: the environment first, then the sheet, then the public application this build ships with. The answer is never empty, so every build can offer Slack without asking a person to register an application first.

func SourceKeyAt

func SourceKeyAt(profileDir string, row PersistedSource, src modelsource.Source) string

SourceKeyAt is APIKeyAt's generalisation. The default service keeps its existing three-rung implementation; another service reads its conventional variable, its stored key, then the variable a person named.

func SpendRailUSDAt

func SpendRailUSDAt(profileDir string) float64

SpendRailUSDAt resolves one conversation's own ceiling. 0 is off.

func SpentFigure

func SpentFigure(usd float64) string

SpentFigure is how a SPEND is written, which is not how a LIMIT is written.

A limit is a figure somebody typed and [formatDollars] writes it back the shortest way that is still the same number — right for a config file and right for `$500`. A spend is a measurement nobody chose, and the shortest honest form of one is a disaster: four fifths of a tenth of a cent came out of the provider as 0.0005688764200000001 and went onto the row exactly like that, twenty-two digits of float noise where a person wanted to read a price. So a spend is cents, and four decimals under a cent — the same ladder the surface's own money word uses, so the receipt and the figure beside it agree.

AND A SPEND OF NOTHING SAYS NOTHING. The emptiness law lives here rather than at each caller so there is one answer to "how is a spend written" and not two: `$0.00` and `$0.0000` are claims nobody earned, and a headless footer that ended `0s · 0 nodes · $0.0000` made three of them on the one line a person reads to find out what happened. It is exported for the doors outside this package — the headless footer, doctor, the notebook — for the same reason.

func SubCent

func SubCent(usd float64) string

SubCent is how a positive amount SMALLER THAN A CENT is written, everywhere.

ONE RULE, BECAUSE TWO DREW ONE FIGURE TWO WAYS. The chat's own money word gained a floor so a real spend too small for four places reads `<$0.0001`, while SpentFigure kept a bare four places and wrote the same spend as `$0.0000`. The Spending tab then said `today <$0.0001` on one row and `$0.0000 today` on the next, about the same day: four zeros for money that was spent, which is the emptiness law's failure turned inside out. Every sub-cent figure is written here now, and the floor never rounds to a lie.

func TaskAdmissionLimitsAt

func TaskAdmissionLimitsAt(profileDir string, maxLoad float64, minFreeMB int) (float64, int)

TaskAdmissionLimitsAt overlays current persisted ceilings on the caller's startup values. Missing keys preserve those values, including explicit zero. Both settings come from one read so a newly created gate sees one profile.

func TaskAuditAt

func TaskAuditAt(profileDir string) string

TaskAuditAt resolves the audit row to its word, default on.

func TaskAuditEnabledAt

func TaskAuditEnabledAt(profileDir string) bool

TaskAuditEnabledAt is TaskAuditAt as the bool the session's Config takes.

func TaskAutoApproveAt

func TaskAutoApproveAt(profileDir string) int

TaskAutoApproveAt resolves the task countdown, in seconds. 0 is a clock that is off: the proposal waits for an answer instead of starting itself.

A persisted 0 is a VALUE and not an absence, which is why the reader tests ok before it tests the number: a person who turned the clock off must not find it back at fifteen the next morning.

func TaskColumnAt

func TaskColumnAt(profileDir string) bool

TaskColumnAt resolves whether the v3 chat stands its task column up, default on. A row that will not parse reads as the default rather than as off, for TimestampsAt's reason: a garbled row must not quietly take the session's record of its own work off the screen.

func TaskMaxLoadAt

func TaskMaxLoadAt(profileDir string) float64

TaskMaxLoadAt resolves the per-core load average above which no new task is started. 0 turns the check off.

func TaskMinFreeMBAt

func TaskMinFreeMBAt(profileDir string) int

TaskMinFreeMBAt resolves the available-memory floor under starting a task, in mebibytes. 0 turns the check off.

func TaskModelAt

func TaskModelAt(profileDir string) string

TaskModelAt resolves the model tasks run on, as the person wrote it. Empty is the ordinary answer and means "the conversation's own": the row is an override, and whether the name in it exists is a question only the catalog can answer (internal/session resolves it against one).

func TaskParallelAt

func TaskParallelAt(profileDir string) int

TaskParallelAt resolves how many tasks may run at once. 0 is no limit, and no limit is the default.

A persisted 0 is a VALUE and not an absence, for TaskAutoApproveAt's reason — though here the value and the default agree, so the test that matters is the other direction: a person who wrote 2 must not find it gone.

func TaskRepairRoundsAt

func TaskRepairRoundsAt(profileDir string) int

TaskRepairRoundsAt resolves how many repair rounds a task gets, default one.

A persisted 0 is a VALUE and not an absence, for TaskAutoApproveAt's reason: a person who turned the loop off must not find it back on in the morning.

func TaskSettleAt

func TaskSettleAt(profileDir string) string

TaskSettleAt resolves the row to its word, default ask. A value this build does not recognise reads as the default rather than as an error, on the rule TaskStartAt states: this row decides who is asked about somebody's work, and a typo in a config file must not start answering on their behalf.

func TaskStartAt

func TaskStartAt(profileDir string) string

TaskStartAt resolves KeyTaskStart: the persisted row, else the default. A value this build does not recognise reads as the default rather than as an error, because the row decides what a command does and a typo in a config file must not be a command that refuses.

THAT RULE IS ALSO HOW A RETIREMENT IS PAID FOR. `adaptive` and `ask` were both answers here once; taking a word out of TaskStartModes is the whole of retiring it, because a profile that still holds one falls through this loop and reads as DefaultTaskStart — no migration, no error, and nothing said to somebody about a preference they set months ago.

func TelemetryAt

func TelemetryAt(profileDir string) bool

TelemetryAt resolves whether the anonymous-usage pipe is on, default on. The environment pin wins over the project file over the profile config over the built-in default — the same ladder every other on/off row walks, with the working directory's own file between the pin and the profile because a repository may answer for itself what a machine answers for everybody (see ProjectKeys). A pin or file that will not parse reads as the default rather than refusing a launch over a count nobody can see.

func TelemetryAtIn

func TelemetryAtIn(cwd, profileDir string) bool

TelemetryAtIn is TelemetryAt with a working directory whose project file may hold the answer. An empty cwd has no project layer, and a project file that cannot be loaded is skipped for the same reason a malformed one is — the row reads as if nobody had written it.

func TelemetryOffReason

func TelemetryOffReason(cwd, profileDir string) (off bool, reason string)

TelemetryOffReason answers what turned the pipe off, in the words the telemetry command prints beside the off reading: the environment pin, the project file, the profile config, or nothing at all when the answer is on. It is the accessor the binary calls to learn “did config turn telemetry off”, and the reason is returned with it because a switch that went quiet without saying why is a row nobody can audit.

func TenureAfterAt

func TenureAfterAt(profileDir string) int

TenureAfterAt resolves the clean-firing count that earns a charter tenure.

func TierModelAt

func TierModelAt(profileDir, tier string) string

TierModelAt resolves the model one tier runs on. Empty means the tier follows the session's own model, which is internal/roles' floor.

A CREW SEAT'S TIER — worker, mastermind (the planner), high (the checker) — reads its pin, or the router's standing pick when it has none (TierSeatAt), so the calls that ride those tiers outside a task follow the crew without a second place to set it.

THE OTHER TWO ROWS KEEP THE RULE THEY HAVE ALWAYS HAD: UNSET AND CLEARED ARE DIFFERENT ANSWERS. A profile that has never held the key gets this build's own near-free model (DefaultReflexModel, DefaultLowModel), because a call made twice a turn is a bill nobody agreed to; a row somebody emptied ON PURPOSE reads empty and follows the conversation, because refusing to let them turn it off would make a default into a rule.

The value may carry a level (`moonshotai/kimi-k3:low`) and IS RETURNED WHOLE. Splitting is roles.SplitEffort's job at the point of resolution, because a settings surface wants the string the person wrote and a request wants the two halves apart.

func TimestampsAt

func TimestampsAt(profileDir string) string

TimestampsAt resolves the timestamps row to its word, default footers. An unknown word reads as the default rather than as off, for RoutingAt's reason: a garbled row must not quietly take a fact off the screen.

func ToolApprovalModeAt

func ToolApprovalModeAt(profileDir string) string

ToolApprovalModeAt resolves the blanket answer the tool gate starts from. An absent setting uses YOLO; an unrecognised persisted value still asks, so a garbled setting does not widen a previously selected posture.

func ToolApprovalsAt

func ToolApprovalsAt(profileDir string) string

ToolApprovalsAt resolves the per-tool exceptions as the person wrote them. The text is the record; ParseToolApprovals is how a caller reads it.

func ValidateTierValue

func ValidateTierValue(value string) error

ValidateTierValue refuses every suffix except the three thinking levels. Tier rows own the `:<level>` notation, so accepting an unknown suffix here would silently turn a misspelling into a model id and defer a clear settings error until a provider call much later.

func VisionModelAt

func VisionModelAt(profileDir string) string

VisionModelAt resolves the image-inspection proxy slot. Empty means resolve from the live catalog at use.

func WithoutSeatPin

func WithoutSeatPin(configured provider.Config) provider.Config

WithoutSeatPin is the settings a raw request path takes: the same account, the same model, and the seat's thinking level dropped.

The document path builds its own raw body with no reasoning object, so a seat pin left on the client would make the raw request and the model-call row describe different calls. This door is exported because internal/session reads documents through the same shape and may not spell the adapter's effort vocabulary itself: its effort law (effortguard_test.go) treats a file that names provider.Effort as claiming a depth, while the document path is declining to carry one.

func WorkAt

func WorkAt(profileDir string) string

WorkAt resolves the completed-work presentation, default fold.

func WorkingSetAt

func WorkingSetAt(profileDir string) int

WorkingSetAt resolves the cap on the live working set the same way.

func WriteAPIKey

func WriteAPIKey(profileDir, key string) error

WriteAPIKey persists a key a person handed over, through the same atomic writer every other setting uses. The file is created owner-readable only (writeProfileValues), which is the property EnsurePersistedAPIKey tightens after the fact on a file that predated any secret in it.

func WriteChatModel

func WriteChatModel(profileDir, slug string) error

WriteChatModel records the choice. It goes through the one writer every persisted row goes through, so the rest of the file — the gate, the rail, somebody else's rows — survives untouched (budget.go's writeProfileValue).

func WriteCreditsReading

func WriteCreditsReading(profileDir, key string, reading credits.Reading) error

WriteCreditsReading atomically replaces machine state. Dollars and the key stay out of the file; a successful rename wakes the live crew's generation.

func WriteDailyBudgetUSD

func WriteDailyBudgetUSD(profileDir string, amount float64) error

WriteDailyBudgetUSD atomically persists the default rail while preserving any unrelated future keys in the JSON object.

func WriteDefaultEffort

func WriteDefaultEffort(profileDir string, rung effort.Rung) error

WriteDefaultEffort records the choice.

It goes through [writeChoice] — the same validating writer every other choice row uses — so an unknown rung is refused in the same words as every other bad choice, rather than being written and then read back as the shipped default forever, which looks exactly like the write having been ignored.

func WriteLaneRow

func WriteLaneRow(profileDir, slot, raw string) error

WriteLaneRow takes what LaneRowWord says and puts it back: `auto`, `openrouter`, a bare lane name, or either `pinned:` form.

func WriteSources

func WriteSources(profileDir string, rows []PersistedSource) error

WriteSources atomically replaces the non-default service rows while keeping every unrelated profile setting.

func WriteTelemetry

func WriteTelemetry(profileDir string, on bool) error

WriteTelemetry persists the person's own answer to the telemetry row — the writer `codeaf telemetry on|off` goes through, so the command and the settings sheet write the same file the same way and cannot drift.

Types

type BashRule

type BashRule struct {
	Match  string
	Action string
}

BashRule is one entry of the bash rules row: a glob in internal/approval's restricted dialect ('*' and literal text, nothing else) and the answer it carries.

It is a config-side twin of approval.Rule rather than that type re-exported, for the reason [targetField] in the surface is a twin of session's glossField: this one is a line of text a person edits in a settings sheet, that one is a decision surface, and one type serving both would be one reason to change both.

func ParseBashApprovals

func ParseBashApprovals(raw string) ([]BashRule, error)

ParseBashApprovals reads the row into the ordered rule list internal/approval matches in order.

The shape is one rule per entry, entries separated by commas or newlines, each entry an ACTION and then the command glob it answers for:

allow git status*, allow "npm test -- --grep=a,b", deny rm -rf *

The action leads because it is the short, fixed half: a person scanning the row is looking for the word deny, and a command line is long enough to push it off the end of a line if it went first. The glob may be QUOTED in Go's own syntax, and has to be when it carries a comma, a newline or a quote of its own — a shell line contains anything, and a separator that a command can also contain is a row that reads back as two rules nobody wrote. Unquoted is accepted and is what the row looks like nine times in ten.

ORDER IS THE AUTHOR'S PRIORITY STATEMENT and is preserved exactly: the policy takes the first rule that matches, so a deny somebody put at the top of the row stays at the top of the row.

type Config

type Config struct {
	APIKey  string
	BaseURL string
	// UnreadProfileKeys is the sorted result of the profile reader registry check.
	UnreadProfileKeys []string
	// Sources is the resolved service set. Empty preserves every scalar
	// construction that predates services through [modelsource.Set.OrDefault].
	Sources modelsource.Set
	Model   string
	// PlanModel is the model that structures work — the task graph, replans,
	// contracts, the delivery gate. Empty means the work model plans too, which
	// is the default and the kill switch: with no plan slot configured exactly
	// one client exists and nothing about the single-model path changes.
	PlanModel  string
	VoiceModel string
	// Media model fields are capability slots. Empty means resolve at use
	// from the live catalog, rather than trusting a floating default slug.
	ImageModel        string
	SpeechModel       string
	MusicModel        string
	VideoModel        string
	VisionModel       string
	DocumentEngine    string
	Timeout           time.Duration
	Reasoning         provider.Effort
	ExecReasoning     provider.Effort
	SpineSamples      int
	MaxDepth          int
	NodeBudget        int
	DailyBudgetUSD    float64
	PracticeBudgetUSD float64
	// PlanConsentUSD is the estimate above which a planned job asks before it
	// starts. Zero never asks.
	PlanConsentUSD float64
	PracticeIdle   time.Duration
	BriefAfter     time.Duration

	// Swarm is the cooperative-decomposition mode, and it is ON by default
	// ([DefaultSwarm]). On, a worker gains a verb for handing work back when
	// its brief turns out to hold more than one worker's share: the resident's
	// leaves get `request_split` and may end their run early by naming two or
	// more ownable parts plus the evidence that revealed them, and a v3 task's
	// worker gets `divide_work`, which splits the task into parts under it and
	// keeps the coordination (internal/session's task_divide.go). On also feeds
	// measured capacity statistics (overrun base rates from the journal) into
	// the sizing and split judgments.
	//
	// WHY ON IS THE DEFAULT NOW. It shipped off because nobody had measured
	// what a division costs when it does not pay. The bench corpus then
	// measured it (bench/swarm/AB-REPORT.md), and the answer was that the
	// expense is not division — it is division of work that was never wide. So
	// the wave that landed the mode also landed the gate that refuses narrow
	// work (internal/splitgate), and with the gate in front of it the mode
	// costs nothing below the width floor and is worth 1.15×–1.95× wall clock
	// above it. A capability that is free when it does not apply belongs on.
	//
	// OFF IS STILL EXACTLY TODAY'S BEHAVIOUR, and `CODEAF_SWARM=0` is how
	// somebody asks for it: workers run their brief to settlement and growth is
	// failure-driven only (overrun, revision, JIT). Everything gated by Swarm
	// is inert when it is off.
	Swarm bool

	// Quorum is the two-verifier gate: when a deliverable passes the judge,
	// two cheap validators independently verify it against the original ask.
	// Both must ACCEPT; any REJECT buys one revision round, then the result
	// commits unconditionally. Off (the default) is the judge's pass as the
	// final word — byte-identical to before this existed.
	Quorum bool

	// Panel is the set of models a run may route across, from CODEAF_MODELS. An
	// empty panel is the default and is the kill switch: with no panel the
	// harness builds the same single adapter it always did and no routing code
	// runs at all.
	Panel router.Panel

	// ProfileDir holds measured executor behaviour. Empty means ~/.codeaf.
	ProfileDir string

	// Models is the model catalog every adapter built from this config consults
	// before it shapes a request — today, to decide whether a reasoning knob may
	// travel at all. Nil is honest and safe: the adapter then knows nothing
	// about any model and sends only what the operator asked for explicitly,
	// which is what every caller did before the catalog was wired in.
	//
	// It is set by the surfaces that load a catalog anyway (chat, run, doctor)
	// rather than loaded here, because a config that fetched would make building
	// a client a network operation.
	Models *catalog.Catalog
}

Config is the resolved runtime configuration.

IT CARRIES NO GENERATION KNOB IT WAS NOT GIVEN. There is no output cap here and no sampling field: a request this config builds names the model, the messages and what the call needs to work, and every generation parameter the operator did not ask for is ABSENT from the body, so the provider's own default answers. The one field that looks like an exception is not one — Config.Reasoning and Config.ExecReasoning default to provider.EffortNone, which sends nothing, and carry a level only when CODEAF_REASONING or CODEAF_EXEC_REASONING said so.

AN OPERATOR OR EMBEDDER MAY STILL SIZE A CALL, with ai.WithMaxTokens on that one request — the seam is open and the adapter honours it (internal/provider). What is gone is this file deciding a ceiling for every call in the process, and, with it, every errand in this tree that used to reach for that option on the harness's behalf.

func Load

func Load() (Config, error)

Load resolves configuration from the environment, falling back to the defaults above. Only the API key has no default; everything else runs unconfigured.

func LoadKeyless

func LoadKeyless() (Config, error)

LoadKeyless is Load for a launch that can collect the key itself: the interactive chat, whose provider screen connects OpenRouter or takes a pasted key (internal/tui3's firstrun.go). Everything else resolves exactly as Load resolves it, and APIKey is simply empty until the person hands one over.

AND FOR A PASS THAT WILL PROBABLY DO NOTHING, which is the second legitimate caller and the one nobody expects: the standing tick (cmd/codeaf's v3StandingTicker), run every five minutes by whichever of a window or the operating system's timer gets there first. Most passes decline the lock or find every item asleep, and a pass that will do nothing costs nothing and needs nothing — so it builds keyless and carries whatever key was there through to the one moment a judgment actually calls a model, where a machine with none says so on that item's own row.

A door that has nobody to ask AND something to spend — --once, an engine, a pipe — still has no business calling this: it would fail on its first request instead of at the door, where the sentence can be read.

func (Config) Adapter

func (c Config) Adapter(model string) (*provider.Client, error)

Adapter is the ONE place a model client is built. The model id decides the source; the source decides the key and the address. Nothing above this line knows either.

func (Config) Client

func (c Config) Client() (router.Client, error)

Client builds what the planner and the executor call.

With no panel configured this is the single adapter it has always been, built exactly as before — that is the kill switch, and it is the default. With a panel it is a router over one adapter per model, which satisfies the same interface, so nothing above this line changes.

func (Config) ClientConfig

func (c Config) ClientConfig(model string) provider.Config

ClientConfig is this profile's answer to "how do I talk to that model". It is what every client outside this package is built from; a caller sets only what legitimately differs — a timeout, a routing strategy, or seams its own surface owns — never APIKey and never BaseURL.

func (Config) ClientFor

func (c Config) ClientFor(model string) (router.Client, error)

ClientFor builds a client for an explicitly chosen model. With no panel it is exactly the single-model adapter it always was. With a panel the bare model id is pinned as the router's opener while the whole value builds every adapter, so a thinking level stays with the seat without becoming part of the provider's slug.

func (Config) Context

func (c Config) Context(ctx context.Context, task string) context.Context

Context stamps the run's provider knobs onto ctx: the effort the operator chose, and a cache key derived from the task so every call in one run asks for the same warm instance.

func (Config) DocumentClient

func (c Config) DocumentClient() (*provider.Client, error)

DocumentClient is direct for the same reason as VisionClient: read_document selects an explicit parser engine and model at the leaf boundary, and a second router substitution would make both capability and cost opaque.

func (Config) ExecContext

func (c Config) ExecContext(ctx context.Context) context.Context

ExecContext layers the executor's reasoning level over a planning context. Planning and execution are different kinds of call — one structures, the other works — so the economy that makes planning fast must not travel into the loop, where a model with reasoning suppressed stops writing anything down and never converges.

func (Config) MediaClient

func (c Config) MediaClient() (*provider.MediaClient, error)

MediaClient builds the non-chat OpenRouter endpoint client with the same bearer key, base URL, attribution, timeout, and transport configuration.

func (Config) PlanModelResolved

func (c Config) PlanModelResolved() string

PlanModelResolved is the model planning-class calls run on: the plan slot when the operator set one, otherwise the work model.

func (Config) PlanSplit

func (c Config) PlanSplit() bool

PlanSplit reports whether planning runs on a different model than the work.

func (Config) ResolveImageModel

func (c Config) ResolveImageModel(models *catalog.Catalog) string

ResolveImageModel applies the runtime preference order: an explicit slot, Seedream 5.0 Pro then Krea 2 Medium Turbo as each is advertised, then the catalog's first image output model. Catalog's offline fallbacks make the last resort usable while keeping the ordinary path free of unverified defaults.

func (Config) ResolveMusicModel

func (c Config) ResolveMusicModel(models *catalog.Catalog) string

ResolveMusicModel prefers the Lyria 3 clip row, then Lyria 3 Pro, then the first music/audio model that is not recognizably a TTS model. AN UNAVAILABLE CAPABILITY STAYS OFF THE BELT: with no published row it returns nothing.

func (Config) ResolveSpeechModel

func (c Config) ResolveSpeechModel(models *catalog.Catalog) string

ResolveSpeechModel prefers Fish Audio S2.1 Pro, then Fish Audio S1, then Kokoro, then OpenAI mini TTS as each is advertised, then the first speech-output model. The person's own saved row wins over all of it: a default is only a default.

func (Config) ResolveVideoModel

func (c Config) ResolveVideoModel(models *catalog.Catalog) string

ResolveVideoModel prefers Seedance 2.5, then Seedance 2.0 Mini as each is advertised, then the catalog's first exact video-output model.

func (Config) ResolveVisionModel

func (c Config) ResolveVisionModel(models *catalog.Catalog, talkModel, workModel string) string

ResolveVisionModel applies the inspection-proxy order at the moment a leaf starts: an explicit environment slot, the live talk and work choices when each advertises image input, Qwen VL when advertised, then the first model with image input. Unlike generation slots, an unadvertised default is never invented: no result means view_image can preserve its calm refusal.

func (Config) VisionClient

func (c Config) VisionClient() (*provider.Client, error)

VisionClient is deliberately direct rather than panel-routed. view_image resolves one capability-qualified model and overrides this client's default per call; routing it again could substitute a text-only model and would make the proxy attribution dishonest.

type CrewAccountState

type CrewAccountState struct {
	CreditKnown bool
	CreditUSD   float64
	ResetAt     time.Time
}

CrewAccountState is what a provider adapter can say about an account before any call is made: its remaining credit, and when its limit resets.

type CrewAsk

type CrewAsk struct {
	Task crewroute.Task
	// Effort is the one-task word: best, cheap, or the knee.
	Effort crewroute.Effort
	// Pins are ONE-TASK pins, laid over the profile's: `--pin` and the seat
	// flags. They never persist.
	Pins map[crewroute.Seat]CrewPin
	// Sends are seats a door has already filled with an id to send as it
	// stands — a flag, a variable — which the router treats as pins.
	Sends map[crewroute.Seat]string
	// Stronger is the crew that ran, for a redo that asks for a stronger one.
	Stronger *crewroute.Decision
	// Again is the crew that ran and never started, for a redo of it: the
	// next-best models at the same cost, not a stronger crew.
	Again *crewroute.Decision
	// ChatModel is the model the person is talking to — proven reachable —
	// the last rung of a seat's ladder when nothing routed can start.
	ChatModel string
	// Repo keys the learned offset: a repository whose work of one class was
	// redone stronger starts that class a step higher.
	Repo string
}

CrewAsk is one task's request for a crew.

type CrewDay

type CrewDay struct {
	SpentUSD float64
	Offsets  map[string]int
	// CostFactor is the learned estimate factor per class
	// ([router.CrewLog.CostFactor]).
	CostFactor map[string]float64
	// Learned is this install's learned quality moves, keyed
	// [crewroute.LearnKey] ([router.CrewLog.Quality]).
	Learned map[string]float64
}

CrewDay is what the log says about crews: today's spend, and the learned escalation offset per repository and class.

type CrewOffer

type CrewOffer struct {
	Model crewroute.Model
	// Routes are every connected provider reaching it, in [CrewCandidatesAt]'s
	// order — plans and local first, the default service last — and the
	// cheapest route is the first one.
	Routes []crewroute.Route
	// Served is whether a provider that is ON reaches it: a model only a
	// provider turned off reaches is still listed, and is not allowed.
	Served bool
	// Allowed is whether the rule admits the model on at least one route
	// through a provider that is on.
	Allowed bool
}

CrewOffer is one model a seat could be pinned to on this profile: a model some connected provider reaches, WHETHER OR NOT THE ALLOWED RULE ADMITS IT.

THE PICKER LISTS WHAT THE RULE LEAVES OUT, AND SAYS SO. A list that hid every model outside the rule would answer "why can't I pick kimi?" with silence; one that shows it dim, with the one key that lets it in, answers the question on the row where it was asked (SetCrewPin still refuses a pin outside the rule — the panel's key widens the rule first, out loud).

func CrewOffersAt

func CrewOffersAt(profileDir string) []CrewOffer

CrewOffersAt is every model a connected provider reaches, allowed or not, in the catalog's order. It is CrewCandidatesAt asked under `all` and then read against the rule in force, so the two can never disagree about which route reaches what.

type CrewPin

type CrewPin struct {
	Model    string
	Provider string
}

CrewPin is one pinned seat: a model id, and the provider route it was pinned to when somebody wrote `model@provider`.

func CrewPinAt

func CrewPinAt(profileDir string, seat crewroute.Seat) (CrewPin, bool)

CrewPinAt is one seat's pin, false when the seat is auto.

A row that says `auto`, a row that is empty and a retired preset word are all auto; any model id is a pin. A row that does not parse is auto too, and the panel says so, because a seat that silently ran a half-read id would be the one thing worse than a seat that ignores it.

func ParseCrewPin

func ParseCrewPin(raw string) (pin CrewPin, auto bool, err error)

ParseCrewPin reads a written pin. Blank and `auto` are not pins — auto is true — and a pin whose model carries a thinking level (`vendor/model:high`) is checked by the gate every tier row shares (ValidateTierValue).

func (CrewPin) String

func (p CrewPin) String() string

String is the pin the way a person writes and reads it: `model[@provider]`.

type CrewProvider

type CrewProvider struct {
	// ID is the connection's own id — `openrouter`, `z-ai`, `codex`,
	// `ollama` — and the word an `@provider` pin names.
	ID string
	// Name is how the panel says it.
	Name string
	// Written is the id prefix that sends a call to this connection.
	Written string
	// Kind is how it bills: metered, a subscription plan, or local.
	Kind crewroute.RouteKind
	// Serves is the models a plan door serves, empty for every model the
	// vendor lists.
	Serves []string
	// On is whether the crew may route through it: every connection is, until
	// a person turns it off ([SetCrewProviderOn]).
	On bool
	// contains filtered or unexported fields
}

CrewProvider is one connected provider as the crew can use it.

func CrewProvidersAt

func CrewProvidersAt(profileDir string) []CrewProvider

CrewProvidersAt is every provider the crew can route through: each connection this profile has with a key, in the person's own order, the default service first when it has one. It is DERIVED, never a setting — connecting a provider is what adds it.

type CrewState

type CrewState struct {
	// contains filtered or unexported fields
}

CrewState is the crew's persisted rows as they stand — the three seats, the allowed rule, the cap, the providers turned off and the free-routes switch, raw — which is what the panel's undo puts back.

IT IS THE ROWS AND NOT A READING OF THEM. An undo that re-wrote what the readers made of the rows would turn a hand-written `auto` into an absent row, or a preset's legacy row into a pin; putting the bytes back is the only undo that restores exactly what was there.

func CrewStateAt

func CrewStateAt(profileDir string) CrewState

CrewStateAt reads the crew's rows as they stand.

type ErrCrewUnreachable

type ErrCrewUnreachable struct{ Action string }

ErrCrewUnreachable is a crew nothing reachable can form; its text is the one action ([crewHealth.crewAction]).

func (ErrCrewUnreachable) Error

func (e ErrCrewUnreachable) Error() string

type ModelSlot

type ModelSlot struct {
	// Slot is the word the models door and [SettingsOptions.SetModel] take. For
	// the three roles the engine still holds a client for it is the legacy
	// engine word; for the other two it is the role itself.
	Slot string

	// Label is the plain word. Never the machinery name: nobody has an
	// "orchestrate model", they have a model that does the conversation.
	Label string

	// Role is the router role this row binds, empty for a capability slot.
	Role store.ModelRole

	// Follows names the row whose model this one uses while it is unset. It is
	// read as the row's empty reading, so an unset row says what will actually
	// run instead of showing a blank a reader would have to guess at.
	Follows string

	// Held says the running engine holds a client for this slot and can be
	// asked what it is on right now. The two roles nothing resolves yet are
	// not held, and asking the engine for them would get the conversation
	// model back — a wrong answer rendered as a confident one.
	Held bool
}

ModelSlot is one row of the models group: what it binds, what it is called in the product's language (14), and what it falls back to when it is unset.

func ModelSlotFor

func ModelSlotFor(slot string) (ModelSlot, bool)

ModelSlotFor finds one row of the models group by its slot word.

func ModelSlots

func ModelSlots() []ModelSlot

ModelSlots is the models group, in the order a person reaches for it: the five roles in the router's own ladder order first, because that is the order the ladder is described in and the order the first three are touched in, then the capability models.

type PersistedSource

type PersistedSource struct {
	ID      string `json:"id"`
	Written string `json:"written"`
	Region  string `json:"region"`
	Address string `json:"address,omitempty"`
	Key     string `json:"key,omitempty"`
	KeyEnv  string `json:"key_env,omitempty"`
	// KeyOptional records an explicitly checked anonymous custom endpoint.
	// Vendored providers never inherit this per-instance choice.
	KeyOptional bool `json:"key_optional,omitempty"`
	// Door is the billing road explicitly proved at connect time. Empty belongs
	// to a pre-door row and resolves to its old metered address, never to a new
	// subscription road that has not been proved for that key.
	Door string `json:"door,omitempty"`
	// PlanPaused is the person's per-service answer. Empty is wait, preserving
	// the fixed-price account unless they deliberately opt into metered spend.
	PlanPaused string `json:"when_plan_paused,omitempty"`
	// Listed records what the service itself answered at connect time. Nil is an
	// older row that still follows the vendored hint; false and true override it.
	Listed *bool `json:"listed,omitempty"`
	Order  int   `json:"order"`
}

PersistedSource is one non-default service in the profile. Address is kept only for custom services; a vendored row derives it from its region.

func PersistedSources

func PersistedSources(profileDir string) []PersistedSource

PersistedSources returns no services for every unreadable profile shape. A newer or damaged row must not prevent an older build from starting.

func PrepareCustomSource added in v0.3.0

func PrepareCustomSource(profileDir, address, written string) PersistedSource

PrepareCustomSource is the one door that mints a custom connection's persisted row, and both doors onto a connection (/connect and the Providers tab) call it before ConnectService, which stays the one validate, probe and persist path. written is the name the person gave or empty for the default the host slug answers; the id is minted from the name that will actually be used, first instance keeping the vendored id and later ones taking a numeric tiebreak when the slug is already taken.

type ProjectConfig

type ProjectConfig struct {
	// contains filtered or unexported fields
}

ProjectConfig is one loaded project layer. The zero value is the empty layer, which is what a directory with no settings has and what every read falls through.

func LoadProjectConfig

func LoadProjectConfig(cwd string) (ProjectConfig, error)

LoadProjectConfig reads <cwd>/.codeaf/config.json, falling back to the legacy project file only when the current file is absent.

Absent is empty; unreadable, unparseable, or written in the nested shape is an error naming the path (law 3). Keys this build does not know are kept and ignored, the way the profile writer preserves unrelated keys: a file is allowed to be from a later version, or to carry another tool's section.

func (ProjectConfig) Bool

func (p ProjectConfig) Bool(key string) (bool, bool, error)

Bool reads one on/off row. The words the sheet shows — on, off, yes, no — are accepted beside true and false, because a person writing this file by hand has only ever seen the words.

func (ProjectConfig) Float

func (p ProjectConfig) Float(key string) (float64, bool, error)

Float reads one dollar row. A quoted amount is accepted for the reason the words are above; a negative or non-finite one is refused with the row named.

func (ProjectConfig) Has

func (p ProjectConfig) Has(key string) bool

Has reports whether the project file answers this row at all.

func (ProjectConfig) Names

func (p ProjectConfig) Names(key string) ([]string, bool, error)

Names reads one row that lists names, written either as comma-separated text (`"node_modules, .env"`) or as a JSON list of text — the list is what a person hand-writing the file reaches for, and a shape that read as nothing would be a setting that silently does not apply (law 3). Each name is trimmed and a blank one dropped, so an empty text is an empty list.

func (ProjectConfig) Path

func (p ProjectConfig) Path() string

Path is the file this layer came from, empty when there is no workspace.

func (ProjectConfig) ResolveBool

func (p ProjectConfig) ResolveBool(profileDir, key string) (bool, error)

ResolveBool answers one on/off row through the ladder.

The environment is checked FIRST and outranks the project file, which is the only place in this file the order is not simply project-over-profile: these two rows are pinned (CODEAF_HISTORY, CODEAF_DRAFT_PERSIST), and a pin is the operator speaking about this process. An unreadable pin is not a choice at all — it falls through to the project file, and then to the profile reader, which lands on the default exactly as it always did.

func (ProjectConfig) ResolveFloat

func (p ProjectConfig) ResolveFloat(profileDir, key string) (float64, error)

ResolveFloat answers one dollar row through the ladder. Today that is the session ceiling, which has no environment pin by design.

func (ProjectConfig) ResolveString

func (p ProjectConfig) ResolveString(profileDir, key string) (string, error)

ResolveString answers one text row through the whole ladder: the project file, then the profile reader that already owns this row, then that reader's built-in default.

AN EMPTY PROJECT VALUE IS AN ANSWER AND NOT AN ABSENCE. `"models.tiers.low": ""` is how a repository says "ignore whatever this machine has pinned and let the small calls follow the conversation" — the alternative, treating empty as unset, would leave a repository unable to turn a personal setting OFF.

func (ProjectConfig) String

func (p ProjectConfig) String(key string) (string, bool, error)

String reads one text row.

The two MAP rows may be written either as the flat `read:allow, bash:prompt` text the settings sheet stores or as a JSON object — the same map, and the object is what a person hand-writing a settings file reaches for first. Whichever shape it arrives in it REPLACES the profile's answer entirely (law 2); the object is flattened to the flat text so exactly one parser (ParseToolApprovals, ParseModelRoles) reads it downstream.

type SSHTransport

type SSHTransport struct {
	ControlPersistSeconds int
	ServerAliveSeconds    int
	ServerAliveMisses     int
	IPQoS                 string
}

SSHTransport is the local surface's policy for the ssh process carrying a hosted conversation. These are settings rather than environment-only pins because a person on a slow or unusually filtered network may need to change them, and the settings registry is the one supported path for such choices.

func SSHTransportAt

func SSHTransportAt(profileDir string) SSHTransport

SSHTransportAt resolves all four knobs from the profile's one config file. A malformed hand edit falls back one field at a time rather than disabling every transport improvement because one value could not be read.

type Seat

type Seat struct {
	Role     SeatRole
	Model    string
	Source   SeatSource
	Provider string
	// contains filtered or unexported fields
}

Seat is one seat's answer: the model, where it came from, and the provider it runs on when the router or a pin named one.

func TierSeatAt

func TierSeatAt(profileDir, tier string) Seat

TierSeatAt is ONE TIER ROW READ AS A SEAT, for the surfaces that seat roles rather than run a door: the conversation's role map (cmd/codeaf's v3Crew), the settings sheet's rows, and a run's crew factory for a seat its door did not name.

A CREW SEAT'S ROW — worker, planner (mastermind), checker (high) — is its pin when one is written, and otherwise the router's pick for work of no particular class ([standingCrewSeat]): the calls that ride those tiers without a task in front of them are routed too, never handed a model this build chose for everybody.

THE OTHER TWO ROWS — reflex and small work — are not crew seats and read the way they always have: a row somebody WROTE is theirs, a row they CLEARED reads empty and follows the conversation (TierModelAt argues why UNSET and CLEARED are different answers), and a key never held reads this build's own near-free model — or, on an account known to be low, a free one ([freeTierModelAt]).

func (Seat) Describe

func (s Seat) Describe() string

Describe is one seat in a receipt's voice:

worker z-ai/glm-5.3-flash (routed)
checker moonshotai/kimi-k3 (pinned)

THE EMPTINESS LAW: a seat with no model says so in words rather than leaving a gap somebody has to interpret.

func (Seat) Env

func (s Seat) Env() string

Env is the variable that fills this seat.

func (Seat) Flag

func (s Seat) Flag() string

Flag is the flag that fills this seat.

func (Seat) Line

func (s Seat) Line() string

Line is one seat labelled, for the door that seats only one — `exec`, which executes and never plans.

func (Seat) Report

func (s Seat) Report() string

Report is what a door prints for one seat.

func (Seat) Rung

func (s Seat) Rung() string

Rung is the answering rung as a receipt says it: the flag or the variable by name, and otherwise the rung's own word. Naming the flag and the variable rather than saying "flag" and "env" costs nothing and tells a reader which of the two they would have to change.

type SeatFlags

type SeatFlags struct {
	Model      string
	PlanModel  string
	CheckModel string
}

SeatFlags are the three seat flags a door took.

type SeatRole

type SeatRole string

SeatRole is which of the three seats one answer fills, by the name a person reads. It is on the Seat so a seat can name its own flag and variable.

const (
	// SeatWork is the worker: the model that does the work.
	SeatWork SeatRole = "worker"
	// SeatPlan is the planner: plans, replans, contracts, the delivery gate.
	SeatPlan SeatRole = "planner"
	// SeatCheck is the checker: the model that reads finished work.
	SeatCheck SeatRole = "checker"
)

type SeatSource

type SeatSource string

SeatSource is the rung that answered. It is an enum rather than a sentence because a caller putting it in JSON is making a promise a script parses.

const (
	// SeatFlag is `--model`, `--plan-model` or `--check-model`: the most recent
	// thing the person said, and it always wins.
	SeatFlag SeatSource = "flag"
	// SeatEnv is the environment: automation's override, set once for a
	// campaign instead of threaded onto every invocation.
	SeatEnv SeatSource = "env"
	// SeatPinned is a pin: the profile's own (/crew pin) or a one-task `--pin`.
	SeatPinned SeatSource = "pinned"
	// SeatRouted is the router's pick for this task.
	SeatRouted SeatSource = "routed"
	// SeatDefault is this build's own model for a tier that is not a crew
	// seat — reflex and small work — on a profile that never wrote the row.
	SeatDefault SeatSource = "default"
)

type Seats

type Seats struct {
	Work  Seat
	Plan  Seat
	Check Seat
	// Crew is the router's decision for this task — the class it read, every
	// seat's pick and the estimate — nil only when no router was asked.
	Crew *crewroute.Decision
}

Seats is one run's crew as the door resolved it, and the router's decision beside it when a seat was routed.

func ResolveSeats

func ResolveSeats(profileDir string, flags SeatFlags, ask CrewAsk) (Seats, error)

ResolveSeats climbs the ladder for all three seats and routes every seat nothing named, for the task in ask.

A flag and a variable are handed to the router AS PINS FOR THIS TASK, so the decision it answers — the crew line, the estimate, the logged row — is the crew that actually runs, and a seat the flag filled still says `--model` on the receipt. A value is handed on WHOLE: a flag may read `moonshotai/kimi-k3:low`, and the level is split off where every level is, at the point of the call (roles.SplitEffort).

The error is the router's: a seat nothing allowed can sit, or ErrCrewAtCap — beside which the seats still come back, because what a door does at the cap (ask, refuse, or be told to spend) is the door's.

func (Seats) Line

func (s Seats) Line() string

Line is the one line a headless run opens with.

func (Seats) Report

func (s Seats) Report() string

Report is what a door prints: the models line, and under it the crew's own line — the class the task was read as and the estimate — when it was routed.

func (Seats) Sentence

func (s Seats) Sentence() string

Sentence is the crew, unlabelled:

worker z-ai/glm-5.3-flash (routed) · planner z-ai/glm-5.3-flash (routed) · checker moonshotai/kimi-k3 (pinned)

ONE SHAPE AND NOT TWO: every run says every seat and names the rung beside each, so a script parsing this has one grammar to know.

type Setting

type Setting struct {
	Key      string
	Category string
	Label    string
	Hint     string
	Kind     SettingKind

	// Unit is WHAT THIS ROW'S NUMBER IS COUNTED IN, and it lives here beside
	// the default rather than in the surface that draws the row. `ssh reuse
	// 300` is a row nobody can decide — three hundred seconds, connections,
	// kilobytes? — and a panel that spelled the `s` for itself would be a
	// second place for that answer to live, drifting the day somebody widened
	// the row. One source: the registry says what the number is, every surface
	// renders it ([Setting.Reading]).
	//
	// THE SYMBOLS ATTACH AND THE WORDS DO NOT. `300s`, `60%`, `20m` are one
	// token in every terminal font, the way the two SettingDuration rows beside
	// them already read; `1536 MB` and `65536 tok` are two words and read as
	// two ([unitAttaches] is the whole list).
	//
	// A ROW WHOSE READING ALREADY CARRIES ITS UNIT LEAVES THIS EMPTY — a
	// duration writes `20m`, a dollar row writes `$5`, [formatPercent] writes
	// its own `%`. A row whose LABEL already names what is counted declares
	// [UnitInLabel] rather than nothing, so "somebody decided this row needs no
	// suffix" and "nobody has looked at this row yet" are different states and
	// the completeness test can tell them apart.
	Unit string

	// UnitOne is [Setting.Unit] at exactly one — `1 clean firing` rather than
	// `1 clean firings`. Empty means the unit reads the same at every number,
	// which is true of every symbol and of `tok`, `MB` and `per core`.
	UnitOne string

	// Slot is the model role a SettingModel row fronts.
	Slot string

	// Choices lists the accepted values of a SettingChoice row.
	Choices []string

	// Env pins the row from the environment: while it is set the value is
	// read-only and the surface says which variable owns it.
	Env string

	// EnvDefault only seeds a value the user has never chosen — the model
	// slots work this way, so a pinned default never freezes the row.
	EnvDefault string

	// PrefsField names the chat-prefs json field this row fronts when the
	// value lives beside the graph rather than in the profile config.
	PrefsField string

	// EmptyLabel reads for a text row whose value is unset.
	EmptyLabel string

	// Secret marks a row whose value is a credential. Such a row READS MASKED
	// — its own reader returns the mask, so every surface that renders a value
	// renders the mask without having to know the row is special — and its
	// writer treats the mask as "unchanged" (see writeCredential), so an editor
	// that opens on the displayed value and is saved unedited cannot overwrite
	// the key with a row of bullets.
	//
	// The flag is here rather than a [SettingKind] because a credential edits
	// exactly like text: the difference is what it shows, not what it accepts,
	// and a fourth kind would make every switch in every surface grow an arm
	// that did the same thing SettingText already does. A surface that wants to
	// suppress its own echo while typing reads this.
	Secret bool
	// contains filtered or unexported fields
}

Setting is one row: what it is called, what it reads now, and what happens when the user changes it.

func (Setting) Accepts

func (s Setting) Accepts() string

Accepts is what this row will take, in the words its own writer refuses in.

It lives here rather than in the surfaces because it is the WRITER'S sentence read forwards: "that's not a dollar amount" and "an amount in dollars" are one fact, and a caller that spelled the second for itself would drift from the first the day a parser widened. A panel with a picker never asks; a tool putting the row in front of a model that has to type a value does, and it is the difference between one call and three.

func (Setting) Apply

func (s Setting) Apply(raw string) error

Apply validates, persists, and lands the live effect. A pinned row refuses calmly rather than writing a value the environment would keep overriding.

func (Setting) PinnedBy

func (s Setting) PinnedBy() (string, bool)

PinnedBy names the environment variable holding this row read-only.

func (Setting) Reading

func (s Setting) Reading() string

Reading is Setting.Value with the row's unit on the end — the string a surface DRAWS, where Value is the string an editor opens on.

The two are separate because a unit is a fact about the number and never part of it: an edit box that opened on `300s` would be asking a person to type the `s` back, and the writers refuse anything that is not a bare figure.

func (Setting) Receipt

func (s Setting) Receipt() string

Receipt is the dim fact that belongs beside this row's value — today's spend beside the day's ceiling, the price tier beside a model. It is a receipt and never a second value: it is derived from a live read, it is never editable, and a row with nothing true to add returns the empty string rather than a placeholder (13, and 10.2.8's rule against inventing a reading).

func (Setting) SelfService

func (s Setting) SelfService() bool

SelfService reports whether a model changing settings on the person's behalf may write this row. A surface rendering the sheet can read it too — a row the chat will refuse is a fact worth showing beside the row.

func (Setting) SelfServiceRefusal

func (s Setting) SelfServiceRefusal() string

SelfServiceRefusal is the sentence a guarded row answers a model with, and the empty string for a row the model may write.

It names the row twice — the person's label and the key — because the two readers of this sentence want different halves: the person recognises the label they see in the panel, and the model needs the key to be sure it is talking about the same row when it explains itself. And it says where to go, because a refusal that does not name the door is a refusal that ends the conversation.

func (Setting) Value

func (s Setting) Value() string

Value is the row's current reading, already formatted for display.

type SettingGroup

type SettingGroup struct {
	Title string
	Rows  []Setting
}

SettingGroup is one rendered category.

type SettingKind

type SettingKind int

SettingKind decides how a row reads, edits, and validates.

const (
	// SettingModel opens the existing capability-filtered model picker.
	SettingModel SettingKind = iota
	SettingDollars
	SettingDuration
	SettingPercent
	SettingCount
	SettingBool
	SettingChoice
	SettingText
)

type Settings

type Settings struct {
	// contains filtered or unexported fields
}

Settings is the built registry.

func NewSettings

func NewSettings(options SettingsOptions) *Settings

NewSettings builds the registry against one profile directory and whatever live seams the caller can supply. Missing seams make a row read-only rather than absent, so the sheet always shows the complete surface.

ONE ROW IS THE EXCEPTION and [Settings.build] states it where it happens: the divider has nowhere to be written without SettingsOptions.SaveSplitPct, and a read-only divider is not a row somebody can read — it is a control whose only behaviour is to refuse.

func (*Settings) EnvironmentPins

func (s *Settings) EnvironmentPins() []string

EnvironmentPins lists the operator plumbing currently set, for the sheet's read-only footer.

func (*Settings) Groups

func (s *Settings) Groups() []SettingGroup

Groups returns the rows grouped for rendering, skipping empty categories.

func (*Settings) ModelSlotBindings

func (s *Settings) ModelSlotBindings() map[string]string

ModelSlotBindings is WHICH MODEL EACH ROLE SLOT IS BOUND TO RIGHT NOW, keyed by the slot word (ModelSlot.Slot).

It is the join SCREEN 2c's model table asks for: a page holding a model id out of the spending ledger wants the role a person could go and change, which is the binding, and never the auxiliary word one call gave itself.

A SLOT NOTHING HAS BOUND IS ABSENT, not present and empty. That is what lets a caller tell "this model is the one execution runs on" from "nothing answers for planning yet", which are the two halves of the design's own table — and it is why this reads [modelSlotReading] rather than Setting.Value, whose empty reading is the row's `follows execution` label rather than a blank.

THE CAPABILITY SLOTS ARE NOT IN IT. Drawing, speaking, composing, filming and voice are knobs with readers rather than router roles ([Settings.modelRow] makes the split), and none of them is a role a text model's bill could be attributed to.

func (*Settings) PersistedKeys

func (s *Settings) PersistedKeys() []string

PersistedKeys lists the rows whose current value is written down in the profile's config.json rather than resolved from a built-in default. The order is the registry's own row order, so a caller rendering in that order can walk both lists together.

A row fronting a store this registry does not own — anything with a Setting.PrefsField, meaning the model slots and the chat divider, which live beside the graph — is never listed, even if a hand-edited config.json happens to carry a key by that name. Absence from THIS file is not evidence about another one, and a provenance chip that guessed would be exactly the estimate 10.2.8 bans.

An unreadable or absent config.json returns no keys: a file that is not there yet is a profile with nothing written down, which is the truth.

func (*Settings) Row

func (s *Settings) Row(key string) (Setting, bool)

Row finds one row by key.

func (*Settings) Rows

func (s *Settings) Rows() []Setting

Rows returns every row in category order.

type SettingsOptions

type SettingsOptions struct {
	ProfileDir string

	// ModelValue and SetModel front the model slots. They stay in the chat
	// prefs file; the registry does not migrate them.
	ModelValue func(slot string) string
	SetModel   func(slot, slug string) error

	// SplitPct and SaveSplitPct front the chat/task divider.
	SplitPct     func() int
	SaveSplitPct func(pct int)

	// RoleModel answers what a router role is bound to right now, for the roles
	// the engine holds no client for. Nil is the honest state of this build —
	// nothing resolves verify or scribe yet — and those rows then read as the
	// role they follow instead of as a guess.
	RoleModel func(role string) (string, bool)

	// SpentTodayUSD is the day's spend, for the receipt beside the day's
	// ceiling. The bool separates "spent nothing" from "nobody counted"
	// (10.2.8); nil leaves the receipt off rather than printing $0.00.
	SpentTodayUSD func() (float64, bool)

	// SpentThisSessionUSD is what the conversation in front of the reader has
	// spent, for the receipt beside its own ceiling. Nil on every door that is
	// not a live conversation, and the bool carries the same distinction
	// [SettingsOptions.SpentTodayUSD] carries: not counted is not zero.
	SpentThisSessionUSD func() (float64, bool)

	// ModelCost is what the provider table knows about one model's price. It is
	// a hint beside a model row and never a filter: an unpriced model is the
	// offline case, not a bad model. [ModelCostHint] is the derivation the
	// wiring lane hands in.
	ModelCost func(slug string) string

	// BackgroundChecks is this machine's own scheduler as internal/standing
	// drives it: the launchd agent or systemd user timer that runs `codeaf tick`
	// every [standing.Interval] so standing items are checked with no window
	// open. It is what the `standing.background` row reads and writes.
	//
	// NIL LEAVES THE ROW OUT ALTOGETHER, which is this codebase's law about a
	// capability that cannot work rather than a shortcut: a Windows host, a
	// build with no store, a registry built to read defaults out of a profile
	// nobody has — none of them has a timer to turn on, and a row present and
	// refusing every write would be a switch wired to nothing. The divider row
	// is absent for the same reason ([Settings.build]).
	BackgroundChecks standing.Watch

	// Applied fires after a row is successfully written, so a process holding
	// its own copy of a value can honor the change without waiting for a
	// relaunch.
	Applied func(key string)
}

SettingsOptions supplies the live seams the registry cannot reach on its own: the running model slots and the surface's own divider preference.

type TeamDefaults

type TeamDefaults struct {
	QuestionsUp bool
	// CapUSDDay is dollars per local day for a team and everything under it;
	// 0 is no cap.
	CapUSDDay float64
	// DepthLimit is how many levels a chain of teams may have, the top
	// counting as one.
	DepthLimit int
	// SubSharePct is the whole percentage of its parent's cap a new sub-team
	// is given.
	SubSharePct int
	// Wake is whether team traffic wakes an idle conversation: a directive its
	// member, a member's reply its manager.
	Wake bool
}

TeamDefaults is the five rows resolved: the persisted value where there is one inside its band, and the built-in default everywhere else.

func TeamDefaultsAt

func TeamDefaultsAt(profileDir string) TeamDefaults

TeamDefaultsAt resolves the five rows in one read of the profile, for internal/teams' [teams.DefaultsAt]. A value outside its band reads as the default rather than refusing a team over a hand-edited file.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL