Repository navigation
santos/dev lands on dev: crew family, Pareto crewing, the Model Pool, custom connections (#1108) - #1194
Merged
Conversation
…vocabulary `questionOtherKey` routed keys by hand and bound a short list, so in a question's own answer box the usual text-editing keys did nothing: cmd-left/right never reached the ends of the line, cmd-delete never killed to the line start, and opt-delete/ctrl-delete never deleted a word — while the message box directly beneath the question answered to every one of them. The box now reads the surface's one word-and-line vocabulary (`editorMotion`/`editorWordKill`, editkeys.go) the way every other box does, and gains the line kill's two names (`ctrl+u`, `super+backspace`). `ctrl+w` stays out on purpose: it shuts the tab in front. The manual gains the box on the list of boxes that answer the caret vocabulary, `questions.md` says so at the `something else…` row, and a new test walks every chord family through the row's own box. Co-Authored-By: aforge <[email protected]>
Co-Authored-By: codeaf <[email protected]>
Co-Authored-By: codeaf <[email protected]>
DefaultCrewSource is now the value CrewSourceAt returns and its test expects, so the const is no longer dead exported API; the all-family comment and manual say plainly that the worker dial moves once and max's mastermind sits on the ceiling model on purpose; the vendor test drops its redundant struct and says vendor where it means vendor; the manual gains the all-family table. Co-Authored-By: codeaf <[email protected]>
The open table's rationale now says 'Why the open ids', so its referent does not dangle after the all-family table was slotted above it. Co-Authored-By: codeaf <[email protected]>
Co-Authored-By: codeaf <[email protected]>
…/crew-open-all-toggle
The TUI picker landed while the config half was still in flight, so it read and wrote models.crew.source itself through a bridge. With the config half merged, that bridge is deleted: the picker reads config.CrewSourceAt, writes config.SetCrewSource (new, the row's own writer beside ApplyCrew), and draws the preset rows through config.CrewModelsForSource. The derivation-gate entry now names CrewSourceAt, which is the row's actual reader and is reached from tui3. Co-Authored-By: codeaf <[email protected]>
CrewConfigured now treats models.crew.source like the tier rows: a person who chose a family and never pinned a tier has an opinion the first-run setup must not paper over with a preset. Shipped defaults still apply only to a profile that answered nothing. Co-Authored-By: codeaf <[email protected]>
frugal's mastermind, balanced's careful and mastermind, and the shipped defaults (which ARE the balanced row) move onto the roster we chose seat by seat: glm-5.3 plans the cheap crew, kimi-k3 works as the second-vendor check and the planner above glm-5.3-flash. Max keeps its mastermind on the same model as balanced because a preset may not carry a thinking level (the seat ladder's law), so the level stays an operator's to add. Manual, the pinned-table test, and the seat ladder's expectations follow. Co-Authored-By: codeaf <[email protected]>
Co-Authored-By: codeaf <[email protected]>
…'s line Three things the full suite caught after the open table moved. A profile that answered models.crew.source and no tier row ran the open constants, so the family said all and the ladder did open: defaultTierModel now resolves the default crew in the chosen family. The new row had no line in the settings skin, which the tab test requires. And the crew row's one line grew long enough to wrap mid-phrase, hiding the balanced line from the screen test. Co-Authored-By: codeaf <[email protected]>
The all-family comment claimed the open table was byte-for-byte untouched, which this very diff contradicts: the clause is gone. The preset lines named open models above frontier ids under 'all', on the exact screen where someone decides to spend frontier money: crewLines is keyed by family now and CrewLineFor resolves both the picker rows and the settings panel's crew sentence through the family on screen. /crew's listing drew open ids under 'all' for the same reason. SetCrewSource and the settings row share one accepted-set spelling, the tombstone comments are gone, and the em dashes the house law bans are out. Co-Authored-By: codeaf <[email protected]>
The all-family max line named astra as the planner, but max's mastermind is the worker's own fable: the line and the table disagreed on the spend screen. The six preset lines are now locked by a test, which is how the all lines slipped. CrewSummary's doc example still showed the old balanced row. Two em dashes and a stale onboarding quote are out. The manual's open-ids rationale no longer claims the mastermind is never a bigger model while the row above it pays for glm-5.3, and it is re-dated for the seats this change re-picked. Co-Authored-By: codeaf <[email protected]>
The line-locking test pinned only the all family while this wave rewrote two open lines, so a wrong open line would pass everything: both families are pinned now. The family row's comment was left half-rewrapped and said step where it means word. And /crew's plain-text listing now names the pool whenever it is not the shipped one, so an all listing is not byte-indistinguishable from an open one. Co-Authored-By: codeaf <[email protected]>
CrewLineFor's doc claimed an unknown family yields empty, which would make it a wrong is-this-family-known probe: only an unknown preset yields empty. Two test gaps the round named: the chooser now asserts the preset rows answer in the staged family (a header-only check would pass an all header over open rows), and the family row's refusal branch is pinned the way the crew row's already was. Co-Authored-By: codeaf <[email protected]>
The cross-check found what the kimi rounds did not. First-run setup showed open models and then wrote frontier ones, because crewPickSeats and crewChoiceWord were open-hardwired while commitSetupCrew writes through ApplyCrew, which reads the family: a person agreed to one price and paid another. Both resolve through the family now, with a test. The hint, the manual and a test comment all said a family flip writes nothing and seats only move when you pick a crew. defaultTierModel made that false for a seat nobody pinned, which is the behaviour its own comment argues for, so the prose now says it: unpinned seats move with the family, written rows keep their ids, and a test flips a fresh profile and asserts the frontier ids. Then the smaller ones: the change entry names the crews that read custom after the roster moved; two em dashes on re-flowed manual lines are gone; three comments narrating the config half of the wave are deleted; the source fold is spelled once in normalCrewSource instead of three ways; and the chooser's row is called family, the word the panel and the manual already use. Co-Authored-By: codeaf <[email protected]>
A blocker and the majors the kimi rounds could not see. ONE DECISION WAS TWO FILE WRITES: the chooser wrote the family row and then the five tier rows, so a reader between them saw the family set to all over open ids, and it pinned source=open on a profile that never answered. config.ApplyCrewUnder lands family and preset in one writeProfileValues, and the family row rides along only when it changes. Setup showed open models and wrote frontier ones, because crewPickSeats and crewChoiceWord were open-hardwired while commitSetupCrew writes through the family. The KeyCrew hint still described the old open roster. The manual still denied any closed model a preset, which this change made false, and both surfaces still said max's mastermind adds a generation behavior when no id carries a level. Four seat examples named qwen3.8-27b as a frugal seat that never existed. The const keeps its own godoc, and the last PR-history clauses are gone. Co-Authored-By: codeaf <[email protected]>
Opus round three was almost entirely this: the blocker fix moved the chooser onto one write and left the old two-write story in a doc comment on SetCrewSource, the tui3 file header, the family section, and a test. All four now say what happens. One em dash in the new ApplyCrewUnder comment is gone. The fold is one function that both reads and writers share, so normalCrewSource's one-place claim is true rather than aspirational. The crew row's hint stops spelling the open models by hand and points at the family row instead. Co-Authored-By: codeaf <[email protected]>
The setup chooser read the profile three times per frame: crewChoiceWord took a family it never used, since the screen's own words cover all three presets, and setupCrewRows is drawn every frame. It takes the preset alone again, and its fallback says why it does not read. ApplyCrewUnder's comment claimed a profile stays free to follow a later default, when it writes all five tier rows. The Default*Model comment carried a second date for one pick. The crew row's hint pointed at itself. And the unwritten-seat test pins against the all table instead of typing the ids a second time. Co-Authored-By: codeaf <[email protected]>
A real one first: the chooser marked a preset in force by the SAVED word alone, so on an untouched profile one step of left still wore the selected ground and the accent while the row listed frontier models the profile does not run. The picker keeps the saved family beside the saved word, a row is in force only when both match, and the custom line obeys the same test. A test flips the family and asserts the balanced row stops wearing the mark; it fails without the fix. Then the setup screen reads the family once, arriving at the door, instead of a profile read inside the draw; one date for one pick; and the manual's new preset bullets use colons where they copied the UI's separator. Co-Authored-By: codeaf <[email protected]>
…, and an in-flight settle holds the question down (#1077) A landing question somebody answered was re-raised byte-identical on every node move while the node stayed unverified, because the raise door consulted nothing about the recorded answer — one card was answered twelve times in an hour. The raise now owes the record what the replay already owed it: - TaskNotice carries Settling, the name of the resolution in flight over a landed node, copied from the graph's own claim fields where notices are built. A notice that names one raises nothing and withdraws nothing; resettle hands the claim back inside its locked write, so a settle's own terminal notice still raises. - landingQuestion consults decidedAlready and a re-raised card leads with the answer's fate — `accepted 18:20 · nobody could check it` — instead of reading as though the last answer was ignored. An answer to a replayed card records its decision now, so the record survives restarts. - A killed re-audit and the failed merge-round roads release the claim before the notice is built, so the question comes back with the failure as its fate rather than being held down or swallowed. - tui3 draws the reason row when it is news the subject's done card cannot have, instead of suppressing it whenever the subject card is in the transcript. Co-Authored-By: codeaf <[email protected]>
Co-Authored-By: codeaf <[email protected]>
The custom line is about the SAVED crew, so it shows whichever family is staged: gating it on the staged family dropped a row the height still counted. ApplyCrew is one resolve-and-write again, delegating to ApplyCrewUnder, so the two cannot drift. ApplyCrewUnder's doc no longer describes the two-write version it replaced; a test comment that said the crew row is the one place the source is written is corrected; builtinTierModel says what it is reachable for; a comment reflows; and the change entry qualifies when a family flip reads custom. Co-Authored-By: codeaf <[email protected]>
Round seven found no blocker and no major. A comment that repeated its own sentence is fixed; the default-preservation test says what it holds (the family, with the roster move owned by the change entry) instead of claiming byte-identical output; the roster comment names which seats moved on which date; the manual says the same; and the family row's UI entry no longer claims a placement the map does not control. Co-Authored-By: codeaf <[email protected]>
…ecordable answers, plain words - resettle hands back only the settle's own claim, so a merge round's guard survives a settle landing under it; and both claim doors now refuse on the other's field — a round and a settle are both work in the node's own working copy, and neither may start over the other. - a decider change during a flight is a new fact: `let codeaf decide` or `take it back` pressed mid-re-audit redraws the card with the flight's stamp on it, rather than drawing `codeaf is deciding` for five minutes after the person took it back. - a landing answer whose synthesized question has no head (the restore race) records nothing — a record the gate can never match is a line nobody can act on — while the answered event still goes out. - the stamp says who answered in the words a person reads: another window, the dial, an earlier decision, the asker. Co-Authored-By: codeaf <[email protected]>
Co-authored-by: codeaf <[email protected]>
…n's wall (#1183) * docs: change note for the auditor's calls naming themselves * docs: the change entry says what the pool's record actually carries It asserted the pool row's contents from the brief and left a reader unable to tell whether `internal/pool/record` was inspected or skipped. The row is one per judged seat score — metric, seat, model, score, judge, door, size, day — written by Recorder.Record from judge scores at the pool's own record command, never from a provider call, with no call count and no cost in it. The entry now says that, and says plainly that no file there changed. Co-Authored-By: codeaf <[email protected]> * docs: stamp the auditor-purpose note with its PR number Co-Authored-By: Claude Opus 4.8 <[email protected]> * session: the settle turn runs under a bound, not the run's wall A landed-but-unverified task under `task.settle = auto` wakes the conversation's own turn and tells it to read the work and settle it. That turn was measured running 28 minutes of 49 tool-call rounds on the high-tier model, stopped only by the run's wall, over a tree that was already clean. These tests hold the settle turn to the checker's own contract: a call ceiling, a share of the run's money when a Steward is armed, the bound and the count on the hand-back, one call for a clean tree with no declared check, and no ceiling for an ordinary turn. Co-Authored-By: codeaf <[email protected]> * session: bound the settle turn at the wake, and say why it came back The turn a landing note wakes under `task.settle = auto` is the one that has to read the work and settle it, and it ran with no bound of its own. The wake is now marked a settle wake where the note is minted, and the turn it starts opens the checker's own window on its context and stops at `settleCallCeiling` provider calls — or at its share of the run's money when a Steward is armed. When a bound trips, the node is handed back with the reason and the count on its report (a new reason beside `taskAskTimeReason`), not a silent stop. A node whose check saw a clean tree with no declared check carries both facts onto the node and settles after one call. A conversation's ordinary turn keeps no ceiling. Co-Authored-By: codeaf <[email protected]> * docs: the change entry for the settle turn's bound Co-Authored-By: codeaf <[email protected]> * session: the settle turn tests build against the bound Co-Authored-By: codeaf <[email protected]> * session: the settle bound's count says one call in the singular Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs: stamp the settle-turn-bound note with its PR number Co-Authored-By: Claude Opus 4.8 <[email protected]> --------- Co-authored-by: codeaf <[email protected]> Co-authored-by: Claude Opus 4.8 <[email protected]>
…'s kept branch and verdict (#1184) The chat-side counterpart to #1182: a /task node that ended failed or unverified kept its deliverable on its own task/<slug> branch (keptWork) and did not merge home. The tasks tool text, the recovered-graph note, and the TaskIndexEntry record now name that kept branch and the verdict word (session.TaskFailed/TaskUnverified) — the same vocabulary #1182 put on the headless envelope. keptBranchOf gates on the merge word (mergeAborted/Conflicted/Kept) so only genuinely-kept work names a branch. tui3 rendering is untouched; the rail/page read the words off the record. Co-authored-by: codeaf <[email protected]> Co-authored-by: Claude Opus 4.8 <[email protected]>
…ard (#1185) TestARunOfIdenticalEffectsEndsNothingByItself failed on its len(rounds) < 2 assertion in a full-package run under load and passed alone. It was not the leash: the run's allowance is 60 minutes and the run lasts 0.21s, so the deadline branch above the repeat branch in childRun.trip never fires, the step budget is 200 against seven steps, and every save resets r.idle. What ended the turn was the turn's OWN repetition guard. Forty byte-identical write calls are a loop by looped.go's rule, which keys on the call signature (callSignature hashes the raw argument text), so the guard hands the turn over at its seventh write, and whether the drain reached a second repeat checkpoint before that turn closed was already a race without any load at all. The test now leaves one identical EFFECT and forty distinct call SIGNATURES: the saves write an empty file, so the leash's fingerprint of it cannot race the next truncating write, and the JSON key order turns over between calls, so no two signatures match and the guard cannot fire. Measured on this tree, the round count moves from 3 against a floor of 2 to 19. It also holds the behaviour effects.pardon exists for, which nothing pinned before: the reader is asked once per run of identical effects and never on every step. Removing that reset gives 38 rounds against a ceiling of 21. Co-authored-by: codeaf <[email protected]> Co-authored-by: Claude Opus 5 <[email protected]>
…anch and verdict (#1186) * cmd/codeaf: the exec and run envelopes must name a non-verified node's kept branch and verdict (failing tests) The tests for the last gap in the #1182/#1184 vocabulary: an exec or run run that ends non-verified carries kept_branch (the workspace's own branch) and verdict (the record's word) on its --json envelope. They name the shared seam (buildResultEnvelope/runResult) as the place one change covers both doors, pin the failed-broke-with-nothing and no-repository/detached cases, and an end-to-end smoke run proves the door passes its workspace through. Red on purpose: buildExecEnvelope takes no workspace and subharnessRun has no workspace field. Co-authored-by: codeaf <[email protected]> * cmd/codeaf: the exec and run envelopes name a non-verified node's kept branch and verdict The last gap in the #1182/#1184 vocabulary. An exec run leaves its landing for the pool's judge with State session.TaskUnverified (exec.go) and a run door run does the same on every ending that ran (subharness_run.go), but neither door's --json envelope said where the run's work stood or what verdict left it there. Both now say the two facts do --json has said since #1182, at the shared buildResultEnvelope/runResult seam (envelope.go) so one merge covers all three verbs and do's own emission moves onto it unchanged. execVerdict (exec.go) reads the word off the same condition the pending landing uses: a run the landing refuses — never started, or broke with no text and no artifacts — says session.TaskFailed; everything else that ran says session.TaskUnverified. The run door's sayEnvelope is unverified on both its endings (the landing is written whatever the stop) and sayFailedEnvelope is failed: that road writes no landing at all. kept_branch is the workspace's own branch via keptBranchIn (extracted from errandKeptBranch, do.go), empty for a detached HEAD or a non-repo, and absent with the key when no verdict stands. The run door's workspace rides subharnessRun, resolved at the door from space.Root() exactly as do resolves its own. exec passes space.Root() at the buildExecEnvelope call site; the end-to-end smoke pins the door wiring. Co-authored-by: codeaf <[email protected]> * docs: the envelope's kept_branch and verdict cover all three headless doors HEADLESS.md's two rows said the keys ride codeaf do alone; they now say what exec and run publish — unverified on every run that left a pending landing, failed on one that broke with nothing to show — and the change entry records what moved (#1185). Co-authored-by: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]>
* docs: what a --yolo task turn waited on when a bash job backgrounded Co-Authored-By: codeaf <[email protected]> * session: bound a worker's park on a promoted command Co-Authored-By: codeaf <[email protected]> * docs: change note for the park's own bound Co-Authored-By: codeaf <[email protected]> * docs: stamp park-bound change note (#1187) Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]>
#1191) * tui3: a door at rest with a turning task subtree says idle Failing test first, before the render-site fix: the status row's state word must say `working` while the conversation's own turn is over and a task it started is still turning. It currently says `idle`. Co-Authored-By: codeaf <[email protected]> * tui3: the status word says working while a task subtree turns A door at rest whose work outlived its turn now says `working` from [app.stateWord], reading the surface's own frame-safe signal ([app.frontSignal]) rather than `a.state`, whose behavioural reads are unchanged. The word matches the tab strip's; the figures are untouched. Co-Authored-By: codeaf <[email protected]> * tui3: assertions, manual and note for the working word roomstatus_test.go's fixture is a door at rest whose node is still turning, so the conversation's own reading after leaving the room is the working word, not a bare idle. The manual gains a row for the at-rest `working` and loses the sentence that called the conversation idle while a node worked; a note and a changelog entry record the word, the render site and why the count is app.frontSignal and not runningTasks. Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]>
* session: a failing test for a job subtree over its CPU bound The test states a spinner storm and an honest parallel verify as injected readings — 16 loops then 64, against 34 processes and 8.5 cores — and demands the storm be cut with a record while the honest peak survives. Nothing bounds a job subtree's CPU or process count today, so it fails. Co-Authored-By: codeaf <[email protected]> * session: bound a job subtree's CPU and process count A run whose model spawns busy loops as bash jobs spends no tokens, so its dollar cap never fires while the box burns. #1158 bounded a run's memory and nothing bounded a job subtree's CPU or process count. The subtree #1155 records at launch (a process group with its leader's identity) now has a bound: a portable Go-side watch over the subtree's cumulative CPU and live process count. The CPU RATE is the signal that separates an honest peak (34 processes, 8.5 cores) from the storm (16 loops, then 64 — fewer processes but far more CPU); a much higher process ceiling catches the fork storm the rate alone would miss. A subtree over the ceiling for three consecutive readings is cut and the run is handed the record 'the job subtree was not settled within its bound', the family #1183 and #1187 speak in. Co-Authored-By: codeaf <[email protected]> * docs: stamp cpu-bound change note (#1190) Co-Authored-By: codeaf <[email protected]> * session: draw the CPU ceiling from the effective cores, not the machine Under a cgroup CPU quota (where cells run) or an affinity mask a subtree tops out at a few cores, so max(0.6*NumCPU, 12) could never trip: the 64-loop storm passed the CPU rule and, at 64 processes, the 256-process backstop too. Draw the ceiling from the effective cores (min of NumCPU, cgroup cpu.max/cfs quota walked from the process's own cgroup as #1162 does for memory, and sched_getaffinity), share 0.6, floor 1; lower the process backstop to 128 (about 4x the honest peak). New internal/processgroup effective-cores reader, injectable for the seam test. Co-Authored-By: codeaf <[email protected]> * session: the CPU rule applies only when no quota or affinity binds A share of a cell's four-core quota (2.4 cores) would cut every honest go test inside the cell: under a binding quota the quota is already the ceiling and #1187's park bound returns a wedged turn, so the CPU rate rule is off there and only the 128-process backstop applies. When nothing binds, the ceiling is 0.6 x NumCPU as before. quotaBinds() decides it (effective cores < machine cores). Widen the trip window to 15 readings (30 s) so an honest build burst is never cut. Remove the now unused core floor. Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]>
…is set (#1192) Co-authored-by: codeaf <[email protected]>
GitHub Actions exports CI=true, and the pool resolver reads a set CI word as read-only when neither the environment word nor the stored setting answers. Under the runner the push errand therefore never started and TestWirePoolIndexStartsTheRefreshAndThePush counted one errand instead of two on every pull-request run of santos/dev, while passing on every laptop. The test is about the DEFAULT mode, so it empties CI for its duration; t.Setenv puts the runner's own value back afterwards. Co-Authored-By: Claude Fable 5.1 <[email protected]>
On Linux a job's shell is left a zombie while the detached sweep runs (reapShell: waitid with WNOWAIT before cmd.Wait), and Group.Alive probed the group with kill(-pgid, 0), which a zombie answers. Every finished background job therefore read as a group still alive, was sent SIGTERM, and waited out the whole two-second termination grace before it was reported done: TestTurnBoundaryReportsRunningAndOneTerminalTransition took 4.17 s on santos/dev against 2.15 s on dev, and the pull-request runner tipped it over its four-second bound on every run today. Alive now reads the group's members from /proc on Linux and counts only processes that are not zombies, reading the leader first so a running job never walks the table. Off Linux the shell is reaped before any sweep looks at its group, so the signal probe stays. Contract tests: a finished job is reported done before the grace (red without the fix, at 2.01 s), a group whose only member is a zombie leader is not alive, and a zombie leader with a live detached child stays alive until the child is gone. Co-Authored-By: Claude Fable 5.1 <[email protected]>
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This was referenced Sep 18, 2026
AbirAbbas
added a commit
that referenced
this pull request
Sep 18, 2026
…1195) modelConnectionStatus renamed every custom-id row to the connection's Written word, and the catalog template's Written is the bare id, so a person who had connected nothing yet saw `custom` where the manual, the README and #1107 say `Custom OpenAI-compatible API`. Only a connected instance is called what the person called it now; the contract test draws both rows and is red without the fix. Found verifying the staging build of #1194. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This was referenced Sep 18, 2026
AbirAbbas
pushed a commit
that referenced
this pull request
Sep 21, 2026
… task page and rail, goroutine ownership at close (#1108) Integration branch santos/dev, held as a closed slice at d39a4e3 plus the empty-prompt guard for generate_image (3a1f80f). Everything through #1192 had already reached dev as #1194; this carries #1193 and #1203 through #1331. The worker harness: one loop under /task and codeaf do with a plan store per run, a plan seat, a work seat, a check seat and a review round after each piece of work, off unless CODEAF_TASK_BELT=bash is set, so the default road does not move. Stopping and limits: a person's stop reaches a run, a time or dollar limit ends a run and names itself, a run is handed what is left of the conversation's limits, a worker that stops making progress is told once and then ended. The task page and the rail: the plan as a tree, a run's row with its parts under it, step rows cut from the commands as they ran, a press on a run's row opens its page in under 0.2 seconds. The engine host and sign-in, the profile's composed writes across two processes, unread config keys said once. Nothing keeps writing after the process closes: the place sweep, the standing ticker, the usage writers, the pool judge sweep and the catalog warm are each owned, stopped and joined, and a law lists every goroutine a conversation process starts. The heavy-suite lock is a kernel file lock that follows the suite. The lazy lexer registry, one fuzzy matcher for every picker, the drafted-with footer on the CodeAF repository. Reviewed 2026-09-21: gates green locally and in CI once #1332 taught the touched-packages walk about nested modules; the default road verified live on both roads against a dev baseline; the one regression the slice introduced (generate_image had lost its empty-prompt guard) fixed on the slice with a contract test. Co-authored-by: Santosh kumar <[email protected]> Co-Authored-By: Claude Fable 5.1 <[email protected]>
santoshkumarradha
added a commit
that referenced
this pull request
Oct 25, 2026
* tui3, config: the gate row lands on the conversation you are in, and /status names the gate (#1063) The three Safety rows — `ask before running`, the tool exceptions and the shell command rules — wrote the profile and left the running gate alone, while the YOLO badge read that profile live. So the badge and the gate disagreed in both directions: `allow` lit the badge over a gate that kept asking, and `prompt` put it out over a gate that was still wide open, which is the false-safety claim #322 and #325 exist to prevent. `applySetting` now pushes those three rows through `ApplyApprovals` — the seam /permissions already drops a banked rule through — and refreshes the badge on the same keystroke, on the branch where the push landed. A rebuild that could not be done is said out loud on the panel's foot line in /permissions' own words, `saved · from the next session`, and the badge stays with the gate in force rather than following the file. And /status carries the posture it never carried: `approvals prompt`, `allow` or `deny`, in the `ask before running` row's own words, in the note, in --json and on the phone's status sheet, from the one posture reading the badge uses. The status row keeps its negative-space badge and the segment is no longer written into the page twice. The note's label column is measured from the widest label it carries, so three crew assertions that spelled `"\ncrew"` plus exactly five spaces were assertions about every other row on the page. They read the crew line by its label now. Co-authored-by: Claude Fable 5.1 <[email protected]> * attribution: the footer names CodeAF, and still links to agentfield.ai (#1075) * attribution: the footer names CodeAF, and still links to agentfield.ai The pull request, issue and comment footers read "Drafted with agentfield ai". They now read "Drafted with CodeAF"; the link, its utm parameters and the "reviewed and owned by the author" tail are unchanged. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GNBScbyNAbbEQ5jn9G6auy * docs: the change entry for the CodeAF footer Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GNBScbyNAbbEQ5jn9G6auy --------- Co-authored-by: Claude Opus 5 <[email protected]> * session, prompt, tui3: a wrong completion check is answered with [no change], and the answer stands (#1065 round 1) (#1067) * session, prompt: a wrong completion check is answered with [no change], and the answer stands (#1065 round 1) The end-of-turn reader saw 600 bytes of the last message, so a long table read as cut off; the carry-on told the model to explain itself, and the explanation became the only answer left in view under the fold. The reader now sees up to 8 KB of the last words, and a cut it still has to make ends with `[clipped by codeaf: N of M bytes]`, which its ask says is never evidence of a cut-off answer. The carry-on lead says who wrote the note and offers one exact reply, `[no change]`: it ends carrying on for the ask without another reading, the transcript keeps it for the model, and neither replay nor the live feed draws it — so the settled answer before it is what the fold finds. system.md trades `# Tone` and `# Delivery` for `# The answer`, `# Words or work`, `# When corrected` and `# Messages from codeaf`, and the two lines that pushed action on questions are qualified. The lean page keeps all four; the prefix waivers rise by the 1,705 bytes that cost, dated and named. Co-Authored-By: Claude Opus 5 <[email protected]> * docs: the change entry takes the pull request's own number Co-Authored-By: Claude Opus 5 <[email protected]> * session: a quiet landing is asked about the notes it was handed, not the page that names them The worker's system page now names [silent] and [stuck] under `# Messages from codeaf`, so a search of the whole request found the tag on the page. childWasHanded reads the conversation and leaves the page out. Co-Authored-By: Claude Opus 5 <[email protected]> * session, prompt: [no change] answers the note it follows, and a long ask leaves the last words cut by name (#1065 round 1) Self-review of #1067 found the token honoured on the wrong replies. The gate read the turn's carry-on count, so a model that made calls in answer to the note and then said [no change] ended the turn unread on an answer those calls may have made stale, and a token after a load or ask nudge counted too. It now reads the transcript: the token must be the very next reply to the reader's own lead (answeredNoteUnchanged), and the dead declined flag is gone. An ask up to the whole digest rides in its head, so eight kilobytes of last words behind a long ask reached the backstop clip and ended in a bare `…`. checkpointClipSaid takes the room that is left. A turn that ends on the token no longer starts the route judge or hands the token to the memory reflex, and a later reading's last words skip it. `# Messages from codeaf` named `[still asked]`, which only the reader ever receives, and left out `[stuck]` and `[held]`, which the conversation does; it now names the tags that arrive and says any other opening, such as `[image #1]`, is the person's. The waivers sit on the measured 1,779 bytes, and the manual and the change entry say what the gate actually honours. Co-Authored-By: Claude Opus 5 <[email protected]> * prompt: the new sections keep the rules the old two carried, and say what stays in view (#1065 round 1) Owner review of #1067. Replacing `# Tone` and `# Delivery` had dropped five rules; each is back inside the new sections: match the person's task and vocabulary, choose the safe option under uncertainty, evidence and blocking details complete, never a compiling scaffold or a narrowed test, never half-solved work. `# The answer` said only the last message reaches the person, which `# Planning` contradicts: the plan and the per-batch sentence are shown while a turn runs. It now says that once a turn ends only its last message stays in view. `# Words or work` sat beside `## Work or words`, which spends the same two words on hand-off sizing; it is `# Answer or change`, and it says an answer asked for in chat is written in the reply, not a file, unless the person asks for one. The waivers sit on the new measurement, 2,035 bytes over dev on both arms. Co-Authored-By: Claude Opus 5 <[email protected]> * prompt: the messages-from-codeaf list names every note codeaf writes The system prompt now distinguishes conversation user-role notes from private reader and tool-result markers. A structural test follows the production constants, and the byte waivers record the exact fixed and lean measurements. Co-Authored-By: Claude Fable 5.1 <[email protected]> * session: no change skips judgment only when it answers the reader The completion loop now uses the transcript context before suppressing the route judge or substituting the prior answer for memory. Tests cover the withdrawn and ordinary-token memory paths, and the manual separates counting from display. Co-Authored-By: Claude Fable 5.1 <[email protected]> * test: the model stub replays the no-change fold The OpenAI-compatible stub can now drive the real binary through eleven read rounds, a long table, a rejecting reader, and the exact token reply. The recipe records the tmux setup and the expected log and screen evidence. Co-Authored-By: Claude Fable 5.1 <[email protected]> * prompt: the standing-news tag stays with the event, not on the page The messages-from-codeaf list had named [something you set up fired], and TestTheSteeringLineReadsAsNewsAndNotAsARequest refused it: that line says under the news itself what it is and what not to do about it, so the page's copy is one law bought on every request of every turn for a turn most sessions never have. The list keeps the compaction tags, the structural test now files the standing frame with the private voices the page must not name, and the waivers fall to the new measurement (fixed 55,280, lean 47,055). Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: Abir Abbas <[email protected]> * Clean internal experiment records and revive test/ux for codeaf (#1068) * chore: clean internal experiment records and revive test/ux for codeaf Public-repository cleanup ahead of opening the docs to outside readers. - Remove four internal DOE/experiment record directories that reference private issue and pull-request numbers and are not current-product documentation: docs/design/pin-doe, plan-gate-doe, turn-wall-share-doe, and workspace-foundation. - Neutralize internal machine references in benchmark and design docs: the bench host ("spark"), its secrets.env path, and a personal working branch name, all now generic placeholders. - Revive test/ux/, the user-facing journey suite, for the renamed product: collapse the removed v1/v2 dual-surface logic into the single v3 surface, repoint the binary to cmd/codeaf and state to ~/.codeaf/CODEAF_*, and delete gate.sh whose premise was the v1-vs-v2 parity table. Co-Authored-By: codeaf <[email protected]> * chore: drop internal conversation-runtime wave records, keep the measurement contract The conversation-runtime directory was a local development wave: day-by-day status, /private/tmp paths, "owner authorized" process notes, draft PR numbers, and local change logs. None of it is current-product documentation a public reader needs. Keep PARETO.md, which is the benchmark's cited measurement contract, trimmed of its dangling calibration link and owner language. Co-Authored-By: codeaf <[email protected]> * chore: neutralize the remaining bench-host and work-machine references A wider sweep found the internal bench host ("Spark"), its secrets path (fleet/secrets.env), and a scratch ops directory still named in live docs, bench READMEs and bench scripts that the earlier redaction had not touched. Rename the host to "benchhost"/"the bench host", drop the secrets path, and replace "the laptop" as the owner's own machine with a neutral "work machine" where it meant the internal one. The manual's first-person "the laptop you are sitting at" is public-facing and left alone. Co-Authored-By: codeaf <[email protected]> * chore: fix review findings in the public-cleanup diff - PERF.md: drop a private tracker issue reference (#846). - PARETO.md: restore a complete sentence dropped when the calibration link was removed, and align tense with the remaining present-tense claim. - home-mission-control/DESIGN.md: drop the internal "fleet" system name and an owner-framing phrase, consistent with the secrets-path scrub elsewhere. Co-Authored-By: codeaf <[email protected]> * docs/design: the two records live code still cites stay in the tree plan-gate-doe/REPORT.md is the measured evidence behind the plan-gate default that twelve comments in cmd/codeaf, internal/config, internal/session and internal/splitgate point at, and turn-wall-share-doe is the run internal/session/turnwall.go says it was added from. CLAUDE.md keeps the record on purpose for exactly this reason, and neither folder names a machine, a secret or a private address. Co-Authored-By: Claude Fable 5.1 <[email protected]> * test/ux: the revived journey suite goes back out #991 removed it because it drove the v1 and v2 surfaces, and the revival did not change that: its journal verb reads graph.db tables (messages, nodes, usage, commands) that the v3 chat never writes, lib.sh still waits for the resident, nothing runs it from the Makefile or a workflow, and its only evidence was bash -n. docs/JOURNEY.md says internal/e2e is the one suite that drives the binary, and that stays true. A suite that has been run green against bin/codeaf can come back on its own pull request with a target that runs it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: entry for #1068 Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: the #1068 entry says why the journey suite is out, correctly The first draft said the v3 chat never writes the graph.db tables the suite reads. Running the suite showed both message rows landing, so that sentence was wrong; what is true is that nothing runs the suite, it had never been run, and its first journey fails on its first screen check (#1072). Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: codeaf <[email protected]> Co-authored-by: Abir Abbas <[email protected]> Co-authored-by: Claude Fable 5.1 <[email protected]> * session: task commits are authored by the bot account, not codeaf@localhost The author codeaf stamped on the commits it lands was `codeaf <codeaf@localhost>`, which no GitHub account can link, so every branch codeaf landed failed the CLA check. It now writes `codeaf <[email protected]>` - the identity the Co-Authored-By trailer already used - and keeps `codeaf@localhost` and `aforge@localhost` recognised as legacy by taskCommitIdentity. GLM 5.3's review caught an unmarked retired name in permissions.md that reddened the namelaw gate, and seven stale test fixtures; both fixed. Laws gate, session, exec and cmd are green. Co-Authored-By: codeaf <[email protected]> * README: show the pinned-version install line the release test requires (#1103) * README: show the pinned-version install line the release test requires The Install rewrite dropped the VERSION=<tag> pin example, and TestPublishedPinExamplesGiveVersionToBash reads every published install surface for `| VERSION=` because `VERSION=<tag> curl ...` is an easy mistake to copy — it sets the variable for curl instead of the install script. The pin goes back inline on the same host the section already uses, with the releases page still linked for tags. Co-Authored-By: codeaf <[email protected]> * docs: the change entry for the README pin example Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]> * telemetry: anonymous usage counts, off with CODEAF_TELEMETRY=off (#1095) * telemetry: the package behind anonymous usage counting, per the wire contract Four typed events (first_run, session_started, session_ended, fault) with the contract's allowlisted, bucketed props; the opt-out ladder with its off reasons; a hashed install identity at 0700/0600; a fault fingerprint over codeaf function names only; a JSON-lines spool with a deadline-bounded flush of up to 50 events per POST, 7-day age drops and a 1000-line cap; the notice constant with NoticeShown/MarkNoticeShown gating the flush; and Show() for a future codeaf telemetry show. A library only: nothing in cmd/codeaf, internal/session or internal/config reads it yet. Co-Authored-By: codeaf <[email protected]> * telemetry: tests, doc, and contract fixes for the usage-count package Adds the package's tests — bucketing edges, the opt-out ladder, install identity, the notice, fingerprinting, the spool against an httptest relay (cap, age drop, 50-per-POST splitting, a hard deadline against a hanging server, silence on failure) and the privacy law that no sentinel string reaches the wire. docs/TELEMETRY.md documents the notice, what is sent and how to turn it off, with a test that fails if the doc's property table drifts from the Go allowlist. Fixes four drifts from the wire contract found running those tests: the <1m duration bucket was missing, the fingerprint's argument stripping cut too much, the rc channel suffix must be a single integer, and under go test a config-off rung outranks the test rung in the reason reported. Co-Authored-By: codeaf <[email protected]> * telemetry: fix review defects 1-8 with tests Fingerprint stability (last-( cut, .go:/main./created-by frame rules), counted-batch flush, guarded and multi-process-safe spool, BucketDuration edges, Configure-gated write paths, O_EXCL install id, install.json preservation, buildinfo.Dirty accessor. Co-Authored-By: codeaf <[email protected]> * installer: record the install locally and print the telemetry notice Following the telemetry wire contract: a successful install now writes telemetry/install.json under the state root (dir 0700, file 0600) with the install method, channel and UTC timestamp, leaves any existing install_id alone, and makes no request the download steps did not already make. At the very end the installer prints the contract's notice to stderr after one blank line, or one line saying telemetry is off when CODEAF_TELEMETRY/DO_NOT_TRACK opts out. README gains a plain Telemetry section pointing at docs/TELEMETRY.md, and a new test/installer-telemetry.sh proves the marker, the notice and the opt-out without a network. Co-Authored-By: codeaf <[email protected]> * installer: four review corrections to the telemetry commit The review of 71ff95a6b found four things. Revert the one line that changed `if [[ -z "${TAG:-}" ]]; then` back to `${TAG:-}`: under set -u an unset TAG must reach api_problem, not crash. In write_install_marker the channel lands in JSON verbatim and CHANNEL can come from the environment, so only stable, rc, staging or dev is recorded and anything else becomes "unknown" — with test cases for a valid channel and for we"ird, both asserting the marker parses (python3 -m json.tool, or jq, else a strict channel check). Fence the README Telemetry notice in a ```text block so Markdown keeps its alignment, wording byte for byte. Clear the two shellcheck findings in test/installer-telemetry.sh with one block disable for SC2016/SC2034 — ok() evals those strings, so the quoting is deliberate — and export CODEAF_TELEMETRY/DO_NOT_TRACK where a subshell needs them, without weakening any assertion. Co-Authored-By: codeaf <[email protected]> * release-downloads: daily PostHog report of public GitHub release download counts A scheduled workflow reads the repository's releases from the public GitHub API and sends one codeaf:release_downloads event per binary asset (codeaf-<os>-<arch>[.exe]) — sidecars like checksums.txt are not reported. Each event carries release_tag, channel (stable or the prerelease suffix), prerelease, os, arch, the cumulative download_count and the UTC snapshot_date; the event id is derived from tag, asset and date, so a same-day rerun deduplicates instead of counting twice. No code in the binary; counts come only from public release data. Co-Authored-By: codeaf <[email protected]> * release-downloads: tests cover the workflow, docs and pagination The dry-run unittests now also assert the identifiers the wire spec spells out where the script and the workflow spell them: the schedule, dispatch, contents-read permissions, built-in token, project-key secret and host variable in .github/workflows/release-downloads.yml, the public-data paragraph in docs/rules/promotion.md, stdlib-only imports, pagination across API pages, the host override and the /batch/ endpoint, sidecar skipping, draft skipping, and the channel words stable, rc, dev and staging. Co-Authored-By: codeaf <[email protected]> * release-downloads: four review corrections to the script Channel detection now follows the tag grammar in cmd/codeaf-release/version.go: stable, rc, dev and staging tags match their four patterns and anything else reports "unknown", so dev-20260917-09299019bcdc is no longer mislabelled "stable". --dry-run works without CODEAF_POSTHOG_PROJECT_KEY, the local and test path: the batch prints with api_key "<unset>" and exits 0, and only the sending path stops with the notice. Every event carries "$geoip_disable": true next to "$process_person_profile": false, since the sender is a CI runner whose location is meaningless. The module docstring now names release-downloads.yml, the file that exists. The fixture holds one tag of each shape plus an unrecognised one, and the tests assert the channel for each, including the two real dev and staging tags. Co-Authored-By: codeaf <[email protected]> * release-downloads: test runs on Python 3.9 too ScriptSourceTest read sys.stdlib_module_names unguarded; the attribute arrived in Python 3.10, so the suite errored on macOS stock python3 (3.9.6). Guarded with getattr so the pinned import-set check stands on its own where the attribute is absent; 32 tests pass on 3.9, 3.10 and 3.14. Co-Authored-By: codeaf <[email protected]> * installer telemetry: the notice is read from docs/TELEMETRY.md The test read .telemetry-contract.md, a working note that is not in the repository and never will be, so it aborted with "telemetry contract is missing" in a clean checkout. It now extracts the fenced block under "## The notice" in docs/TELEMETRY.md — the same bytes as the Notice constant in internal/telemetry/notice.go — and holds the installer's TELEMETRY_NOTICE to it as before; a new assertion holds the README's telemetry fence to it too, byte for byte, so the invariant the suite existed to guard now has its third leg. Comments in scripts/install.sh, internal/telemetry/telemetry.go and this test now name docs/TELEMETRY.md as the source of the notice. No Go code changed and no Go tests were run. shellcheck is clean; the suite's 33 passing assertions include the two new notice checks, and the same two stat(1)-related assertions that already failed on this GNU machine before the change still fail with identical output. Co-Authored-By: codeaf <[email protected]> * release-downloads: the download-counts paragraph lives in docs/TELEMETRY.md docs/rules/promotion.md was the only home the paragraph had, and it came with a fallback pinning the branch to the state before the telemetry doc existed. The paragraph moves, byte for byte, into docs/TELEMETRY.md as a final "## Download counts" section beside the rest of the telemetry story, and promotion.md returns to its origin/dev content, so this branch now touches no release docs. test_no_telemetry_doc_on_this_branch asserted docs/TELEMETRY.md did not exist and is deleted. The two tests that read the paragraph read it where it now lives: both require a "## Download counts" section in docs/TELEMETRY.md naming .github/workflows/release-downloads.yml, scripts/release_downloads.py and the checksums.txt sidecar, and the promotion.md fallback is gone. No Go code changed and no Go tests were run; the property table in docs/TELEMETRY.md is untouched. The suite passes: 31 tests, OK. Co-Authored-By: codeaf <[email protected]> * installer telemetry: the mode check asks GNU stat first On Linux `stat -f` succeeds and reports the filesystem, so the BSD-first order never reached the GNU form and both permission checks failed. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01CqfhxLrrBNQxwPNvEgoBGR * telemetry: refuse to send to the production relay from a test binary Co-Authored-By: codeaf <[email protected]> * telemetry: testHome points the endpoint at a dead loopback Co-Authored-By: codeaf <[email protected]> * telemetry: the orphan-adoption test names the dead relay it now gets Co-Authored-By: codeaf <[email protected]> * telemetry: a test pins that no test binary reaches the production relay Co-Authored-By: codeaf <[email protected]> * telemetry: three tests pin the test-binary relay refusal Each asserts through real calls, not a re-implemented effect: the newRelay override lands after testHome and a flush reaches that recorder; a default-endpoint flush is refused like a dead relay (zero round trips, event still spooled); forcing the ladder on cannot reopen the path. Co-Authored-By: codeaf <[email protected]> * telemetry: allocation-free session counters Snapshot returns the SessionStats fields the SessionEnded constructor takes, so the wiring hands the struct straight over. Cost is stored as integer micro-dollars and divided back once, in Snapshot. Count* never ask whether telemetry is enabled and never allocate. Co-Authored-By: codeaf <[email protected]> * telemetry: gofmt the counters test One alignment fix in the many-goroutine test; no behaviour change. Co-Authored-By: codeaf <[email protected]> * config: telemetry setting row (CODEAF_TELEMETRY) Key "telemetry", on|off, default on, env pin CODEAF_TELEMETRY, in the interface group beside the history row. TelemetryAtIn walks the ladder — pin, project file, profile config, default — and TelemetryOffReason answers whether config turned the pipe off and names which rung did. The row sits in ProjectKeys so a repository may answer for itself. Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the telemetry command, and the setting it writes Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: session lifecycle events at execute (work in progress) Configure, notice, first_run and session_started at the start of every session command, session_ended and a one-second flush at its exit. The end event does not yet carry the session counters. * provider: count answered model calls and their spend at the log door * session: count sealed turns and executed tool calls * cmd/codeaf: the session-ended event carries the run's counters telemetryEnd builds the end event from telemetry.Snapshot so turns, model calls, tool calls and cost reach the spool; the stop-reason switch becomes a map over the exit ladder; and both lifecycle constructors are gated on the ladder being on so a disabled run touches no state. Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the counters test says the tally is process-wide The exact turn, call and cost assertions hold for one run of the package; a repeated run counts into the same atomic tally again. Co-Authored-By: codeaf <[email protected]> * guard: a fault hook Recover calls, for a reporter above this package Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the fault event at both crash sites, off the current session Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: a test that the crash site spools a fault and not the panic text Co-Authored-By: codeaf <[email protected]> * docs: finish the anonymous usage counts contract and add the 1095 changelog entry * guard: the fault hook sits behind an atomic pointer A goroutine recovering a panic read the hook while a test on another goroutine wrote it, which the race detector reported. SetOnFault stores it once; Recover loads it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: a task's interrupt word comes from the signal, not the cancelled context stopSignals cancels the signal context at the end of every errand, so the flag read from its Err said interrupted for a run that finished cleanly. Co-Authored-By: Claude Fable 5.1 <[email protected]> * tui3, installer: the telemetry row has a tab, and a custom install writes no marker TestEverySettingRowHasATab wants every registry row on the settings panel. TestCustomInstallOutsideTheStateRootDoesNotRunAdoption wants an install outside the state root to leave the login's state folders alone, so the install marker is written only when the root exists or the install is in it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: the counters test zeroes the process tally first Other tests in the package drive model and tool calls through the doors that count, so the exact bands held only when the test ran alone. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: the version test drives the telemetry lifecycle, not execute execute starts writers under the state root that outlive the test, and the temp dir's cleanup raced them. The lifecycle alone is what the test pins. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: gofmt the lifecycle test Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: codeaf <[email protected]> Co-authored-by: Claude Fable 5.1 <[email protected]> * session: the carry-ladder fixtures wait for the mark's drawing, not race the scheduler (#1104) * session: the carry-ladder fixtures wait for the mark's drawing, not race the scheduler TestASuccessfulHandoffJournalsTheRungThatSuppliedTheBrief failed once in a full make check at line 118: the worker's brief was the running model's draft, not the mastermind's written handoff. THE CAUSE IS NOT A STOLEN STEP. Reproduced under load (60 CPU burners, the fixture run 1500 times), every request the fixture makes is either the turn's own or one the aside answers: the queue carries only `round`, `draft` and `writer`, and the errands beside the turn (title, namer, the mark's drawing) never touch it. What moves is the ROUND COUNT — 41 to 54 across the runs — because the mark's drawing is a sidecar (sidecar.go) whose landing the scheduler places, and the turn is never held for it. On a busy machine the turn runs its rounds off faster than the drawing's goroutine is scheduled onto a core, the gap between the net firing and the drawing landing outruns the slack the script carries ([checkpointSlack] says so in its own words), the writer's ask lands PAST the end of the 53-step script, the scripted completer answers it with its past-the-end line `(unscripted)` — which is not prose — the handoff rung reads degenerate, and the carry ladder falls onto the draft. That is #392 exactly, one mechanism along. WHAT FIXES IT IS WAITING FOR THE READING. checkpointAgent now puts a watch on the turn's own context ([watchReadings]) and the handover fixtures submit through [watchedContext], so [answerWhenQuiet] holds each request that carries the belt until every reading beside it has landed — the order a real turn has, where a real model spends seconds on a step and the small readings land inside it. The gap is then one boundary whatever else the machine is doing, and a script long enough to cross the mark is long enough. THE REGRESSION TEST holds the drawing back by a fixed wall time — standing in for a scheduler that does not reach it in time — and insists on the behaviour: the turn does not spend its script racing the drawing, and the writer's rung still supplies the brief. It fails without the watch (the turn makes 54 rounds) and passes with it. Co-Authored-By: codeaf <[email protected]> * docs: the change entry for the carry-ladder drawing race Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]> * docs: the install proxy is live, and the manual says so (#1076) * docs: the install proxy is live, and the manual says so `https://agentfield.ai/get/codeaf` has served `scripts/install.sh` since 2026-09-16, the day the repository went public, so the compiled-in manual and docs/GUIDE.md stop saying it is "not serving yet" and stop calling the repository private. The proxied line goes first, as the README already has it; the raw GitHub line is described as the script under it. Both pages say to pipe into `bash`, not `sh`, because the script uses pipefail and `[[ ]]` and dash rejects them, and both state the channel path (`/dev`, `/staging`, `/rc`) beside the flags. The manual also says what the proxy refuses: a script without its channel line exactly once, or a body that is not a shell script, is a 502 rather than the wrong thing. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: entry for #1076 Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Fable 5.1 <[email protected]> * A launch says a newer codeaf is out, and /update installs it and restarts (#1060) * The release-tag grammar has one home, and it is internal/update Stable, rc, dev and staging tags are parsed, compared and bumped in exactly one place now. cmd/codeaf-release keeps its next, kind and prune behaviour and its tests unchanged; it reads the grammar from the new package rather than owning a second copy of it. A second parser is the thing that drifts: the updater about to land needs the same answer to "is this tag newer than that one" that the release tool gives, and one source of truth is the only way they cannot disagree. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * internal/update: look up a release, check it, and replace this binary with it The launch check answers only for a build that could sensibly be behind: a stable tag, or a release candidate whose stable line has since been published. A dev or staging build, a make build sha and an unstamped binary make no request at all, because a channel build is deliberately newer than stable and a source build belongs to whoever built it. The answer is cached for a day beside the profile, and every failure is silence. The install downloads into the target's own directory, matches the sha256 from checksums.txt, and only then renames over the running file, so a refused download, a wrong checksum or an unwritable directory all leave the original binary exactly where it was and no temp file behind. The API token never leaves the API host: the redirect policy strips it the moment a redirect changes host, which Go does not do on its own for another port or a subdomain. Windows has no atomic rename over a running image, so it goes through ReplaceFileW and puts the original back when Windows reports the one failure that moves it. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * tui3: a launch says a newer codeaf is out, and /update installs it The launch line is one dim transcript note carrying both versions, /update, and the curl line, because the command is what the owner asked to be given. It arrives as a command rather than an opening read, so the first frame is drawn before the network is asked anything and a slow or refused answer draws nothing. /update — /upgrade is the same road — names the tag, then the checksum and where it landed, then quits with a restart plan for the door to carry out. Already newest says so and stays. It refuses while a turn or a task is running, and once it has started, one shared guard keeps a second /update and a new turn from beginning underneath it: the restart must never quit on top of live work, and the home page's Enter roads go through the same guard chat's does. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * codeaf update, and the door that restarts the same conversation codeaf update --check prints one line and exits 0 when this release is newest, 3 when a newer one exists, 1 when it could not ask. Without --check it installs stable, or whatever --rc, --dev, --staging or --version names, choosing a channel build by its own publish time the way the installer does. A binary built from source refuses in place and names its path rather than replacing something a developer is holding. The restart happens here and not inside Bubble Tea: the surface fills a slot, the door reads it once the frame is down and the terminal is back, and then execs the new binary with this launch's own arguments plus --session pointing at this transcript, so the conversation reopens where it was. A linked-local engine is asked to retire only after the surface has detached, and it still refuses while another window or any live work is there. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * remote: a window that says it is leaving needs no torn-link grace The twenty-second grace exists for a terminal that was killed without saying so, and a deliberate detach is the opposite of that — the window has just announced its departure. Carrying the grace anyway left a cleanly closed window looking attached for two more call deadlines, which kept a freshly installed codeaf waiting behind the engine the old one had already left. It clears only once the last view is gone. Turns, tasks, unanswered questions, other views and live work all still refuse retirement, and a refusal still never stops the work it protects. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * The installer and the updater agree on what a channel tag looks like Two roads now install codeaf, and they were reading the same release list by different rules: the shell accepted any dev-* or staging-* tag and semver parts with leading zeroes, while internal/update accepts only the shapes the release workflow actually publishes. A malformed tag newer than a real one would send the two roads to different builds. One helper carries the grammar to both channel selection and the line that names the channel, and an unrecognised tag for the asked-for channel now says so instead of installing whatever came back. The publish-time ordering #1056 added is untouched: only which candidates are eligible changed. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * The manual stops saying that nothing self-updates That sentence was true until this change and is now the opposite of true, so it goes, and a section written in the words people bring takes its place: is there a newer version, how do I update codeaf, why does it say this every time I start. It states who gets the launch line and who never does, the opt-out variable, the day the cached answer lasts, the shell exit codes, and both refusals — a build made from source and an executable nobody may write. The command table names /update and /upgrade with the argument they take. Four probes reach the new section, and the remote page keeps its own asker's words in its heading so the new section cannot take that question from it. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * docs/changes: the update check and /update, for #1060 Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: --check answers for the channel it was asked about, and a newer build is never downgraded Two things a hand verification caught. --check parsed whatever tag it had selected with the stable grammar alone, so --dev, --staging and --rc could never be "newer" and every one of them answered "you are on the newest codeaf" and exited 0 while a newer build sat in the list. The check now keeps its channel all the way through the decision and always names the tag it is talking about: stable and rc compare as semver, a dev or staging tag is the same tag or a different one, and --version is equal or it is not. And a build AHEAD of the newest stable was quietly installed backwards — the check said newest, the install downgraded it. Both roads refuse now, naming both versions and the deliberate way past: this codeaf is v0.3.0, ahead of the newest stable v0.2.0 — pass --version v0.2.0 to install it anyway An explicit tag IS that deliberate road, so --version and /update <tag> still install exactly what was named. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: a failure names the asset and the status, and ends with the curl line A download that failed printed the whole fallback URL, which put the retired product name on somebody's screen. The line names the asset it asked for and the status it got back, and nothing about which repository answered — the compatibility attempts still happen, they are just not the person's business. Every terminal failure now ends with the curl line, which D9 asked for and the download path had been missing. The / list declared no argument for /update while the command page documented one, so the list gains the second row the way /cache has one. The manual section now says what the code does about a build ahead of the newest stable, where it had said such a build "gets nothing" — true of the launch line, and false of the install until this wave. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: the curl line is agentfield.ai/get/codeaf, the supported road The launch line, the refusals and the terminal door offered the raw GitHub address of scripts/install.sh. The repository is public and agentfield.ai/get/codeaf has served that script from main since 2026-09-16, so the constant now spells the supported line, the manual's update section names it in place of "the curl line below", and the change entry says so. Asked for on the pull request by Santosh. Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Opus 5 (1M context) <[email protected]> * a run's own scratch note no longer fails the delivery gate (#1096) * codeaf: a run's own note is offered outside the tree it is judged in leafOutputHint invited a job root to write its overflow document at a workspace-relative name (exec.SuggestPathFor's task-2-x1-toolchain-checks.md shape), so a run working in a person's directory left that note untracked at the root of their working tree. The delivery gate holds the run's record — the artifact registry, joined by jobArtifacts, plus the delivered tree — against the request, read the note as part of what was handed over, and refused the delivery; the run then ended incomplete over complete work. There is no separate mechanical clean-tree check to exempt, so the fix is the writer's: where the workspace belongs to the person, the root's file invitation points at the run's scratch beside its recorders and job logs, and the message keeps carrying the deliverable. * codeaf: a person's own file still joins the record the gate reads TestARootLeafInAPersonsDirectoryPutsItsNotesInScratch baselined the tree after the note was already written, so its landing read could never have observed anything: the record stayed empty because of the ordering, not because of the address. The baseline now happens first, in the executor's own order, and a hint that ever points into the tree again fails on the observation, not only on the spelling. And the other half of the law is pinned down: a file that turns up in the person's tree while the leaf runs — even at a name wearing the run's own task-<n>-<slug>.md shape — is still observed and still lands in the record the delivery gate is held to. Only the run's own note moved; nothing else about what the tree says was loosened. Co-Authored-By: codeaf <[email protected]> * docs: the change note for the root's overflow file moving out of the tree --------- Co-authored-by: codeaf <[email protected]> * chat: the wordmark is drawn CodeAF, four rows, capitals on the d's cap line (#1106) `┌─┐ / ├─┤ / └─┘` is not an `a`, it is two closed bowls of equal size stacked on each other, and two equal closed bowls are an `8`. That was the second-to-last glyph of the product's own name on the first screen of every fresh install. The owner's answer is to have the two letters at the end stand up out of the word as capitals: `A` is an apex, a crossbar and two legs; `F` is the one letter whose case is told by its arms alone, so its top arm is the longer one. A CAPITAL'S TOP IS THE ASCENDER'S TOP, and three rows of box-drawing could not give it that. A bar is drawn at the middle of its cell and a stem reaches the cell's edge, so the `d` stood half a row over any capital however it was formed — and a stem in the bottom row hung half a row under the bowls' baseline the same way. The fourth row is that half a row made whole: the x-height letters live in the lower three and leave the top row blank; the capitals and the `d`'s ascender start in the top row, the ascender as the half-stroke `╷` so its top is the capitals' bar; and every stem that ends on the baseline ends as `╵`, which stops where `└─┘` does. A `│` in the bottom row is a descender and nothing else. THE WORD IS SPELLED `codeaf` AND DRAWN `CodeAF`. `product` is unchanged, the glyph table is still keyed by its lowercase letters, and a terminal that cannot draw boxes still gets the word `codeaf`. The greeting's centring test read the unit's left edge off the wordmark's first row, which is now the cap line the x-height letters leave blank; it reads the second row. The one manual page that draws the wordmark had been drawing the retired `aforge` letterforms under the label "the codeaf wordmark" — the name law walks text and cannot see a name spelled in box-drawing — and draws the shipped forms now. Co-authored-by: Claude Fable 5.1 <[email protected]> * A real release download finishes: the 3-second check clock no longer covers the asset (#1110) * update: a release download gets its own clock, not the check's The release client was built as http.Client{Timeout: 3s} because three seconds is right for the launch check, and Go's Client.Timeout covers the WHOLE exchange including every body byte. The same client then downloaded the release asset, so a 57 MB download on a healthy link — about eight seconds here — was cut off three seconds in and a person read `read the release response: context deadline exceeded`. Client.Timeout is gone. The transport bounds only what has made no progress: a 5 s dial, a 5 s TLS handshake, a 10 s wait for response headers. Every call now takes its deadline from its context. The launch notice and `--check` share the one CheckTimeout constant, install metadata gets 10 s per request, and the asset gets a 30-second no-byte stall window plus a 15-minute ceiling — a dead link fails with a sentence, a slow one finishes. No Go deadline text reaches a person. A stall says `downloading codeaf-linux-amd64 stalled — no bytes for 30 s`, the ceiling says `took longer than 15 minutes`, and late headers say `did not start arriving within 10 s`. The stall window, the ceiling and the metadata window are injectable, so the tests prove the clocks with real slow, silent and trickling servers rather than waiting out the shipped ones. Co-Authored-By: Claude Fable 5.1 <[email protected]> * update: both doors read the one check constant `codeaf update` and the chat surface each spelled `3*time.Second` when they built the release client, which is how a figure meant for one small request came to govern an install. Both now pass update.CheckTimeout, and the three seconds live in exactly one place. The terminal's `--check` takes the client's Check, which is the whole-exchange check deadline; a plain `codeaf update` keeps Select, so the metadata requests an install makes get the ten seconds they need. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: the update download's own clock (#1110) Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Fable 5.1 <[email protected]> * docs/changes: v0.2.1 rolled up into CHANGELOG.md (#1113) The 33 entries that landed on dev since the v0.2.0 roll-up are the ## v0.2.1 section of CHANGELOG.md now; docs/changes/unreleased/ keeps only this pull request's own entry. Stable v0.2.1 takes its notes from that section. Co-Authored-By: Claude Fable 5.1 <[email protected]> * README: telemetry folds under Docs (#1115) The 23-line Telemetry section between Install and the product tour becomes one collapsed block at the end of Docs: a plain summary line, the notice verbatim, one sentence and the link to docs/TELEMETRY.md. The notice block is unchanged, so it still matches the binary, the installer and docs/TELEMETRY.md. Replaces #1112. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01NoeSVN8nqmwNVRXExgoUNN * santos/dev lands on dev: crew family, Pareto crewing, the Model Pool, custom connections (#1194) Lands the integration branch santos/dev (#1108, head 6db5125b2, 152 commits over dev) on dev as one squash, plus two fixes that kept its touched-packages job red: the pool start-up test empties the CI word it is not about (649bb4bfd), and a zombie leader no longer keeps a job's process group alive on Linux, which had delayed every finished background job by the two-second termination grace since #1155 (053e6a90f). #1108 carries the full account; the 94 change entries under docs/changes/unreleased/ are the per-change record. The commits folded in, oldest first: 5c413ef7b Keep archived evidence readable while tasks finish 3705a0cfe Document recovery evidence retrieval change 7834cc7d0 tui3, manual: a question's own answer box answers to the whole caret vocabulary 99d66e98c task: Add open/all source toggle to crew config c1e4cb57f docs: the change entry for the crew model family toggle e63b43927 crew: round-1 review fixes 304a5a223 crew: round-2 review fix 65477c0c5 task: Add open/all selector to /crew TUI picker 9189ef764 Merge branch 'task/add-open-all-selector-to-crew-tu-9e8cc0' into feat/crew-open-all-toggle ba9c12d61 crew: reconcile the picker onto config's source helpers ea05bc0f5 crew: a chosen family counts as an answered crew e1a037e0a Merge remote-tracking branch 'origin/dev' into feat/crew-open-all-toggle f6c2924a7 crew: the open table takes the picks we landed on eff44b542 docs: the change entry carries the open-table move and the family gate 71ce9bec0 crew: the family reaches the fallback, the settings skin, and the row's line b8ca5f513 crew: round-1 review fixes on the new table 8db5708ef crew: round-2 review fixes ce9759ef4 crew: round-3 review fixes 956e1f514 crew: round-4 review fixes 37ad27f4e crew: fixes from the first Opus round 6ffb95813 crew: the fixes from the Opus confirmation round 72b027436 crew: the comments the atomic write left behind 70d0e205b crew: round-four fixes from the Opus review 234300e37 Merge branch 'dev' into feat/crew-open-all-toggle 894292f28 crew: round-five fixes from the Opus review fba77f92d An answered landing carries its answer's fate when it is raised again, and an in-flight settle holds the question down (#1077) e69445058 Changelog entry for the landing-answer fate (#1086) 64ab7eae0 crew: round-six fixes from the Opus review 070016419 crew: round-seven fixes from the Opus review af09aa852 The review round's four findings: claim ownership, decider changes, recordable answers, plain words c24af0248 crew: round-eight fixes from the Opus review 289d372b9 The tests the landing's answer carries: fate, hold-down, redraw, claim doors 7e38346a9 The take-down comes before every mint, and the tui3 walk reads the newest card dbe1be03e The review rounds' last word: the release owns its claim, and the docs say what the code does 4cc4945a7 The Opus review: a release owns its generation, and every failed round still says its word a5cce0dde The done card says who is deciding, so the clause is a duplication, not the fact 6bd276e6c Merge fix/1077-landing-reraise into santos/dev 271016938 Merge feat/crew-open-all-toggle into santos/dev b459399d3 Merge fix/question-answer-box-caret-keys into santos/dev a38492026 Merge codex/recovery-evidence into santos/dev dc22e6461 cmd/codeaf: the role-door tests read the shipped mastermind instead of a copy of it 39f528f70 release: the pin-example check reads the guide the examples moved to 6439d3cb8 session, tui3: gofmt the three files santos/dev merged unformatted 854037967 tui3: a question's answer box kills to the end of the line on ctrl+k d93ec61ea session: the late-attach proposal test holds the work until the replay is read a81ead4de session: the landing-fate tests read today's clock instead of a fixed date 8fe4fff38 provider: the retry-avoid list rides the call's context into the knobs a266da3e2 provider: the retry-avoid veto narrows the wire through one field law 197d89334 exec: a node retry asks the router to avoid the lanes that failed it 2d02cc0b9 provider: the set-servable law's doc comment keeps its paragraph break e1d0e7524 exec: the retry test names its helper and spells its wall in time units e7d65a517 docs: the change note for the retry that avoids the failed upstream 11decbd92 Merge fix/provider-failover into santos/dev c099a51c2 Merge dev into santos/dev e47d92bde provider, exec: the lane a failure names is a wire fact provider reads (#1105) 396be97be Merge dev into santos/dev 3b97f50c3 Merge dev into santos/dev ea3b5c9a0 telemetry: the send path drops events that cannot name their version 573b288e4 cmd/codeaf: keep smoke runs from sending usage counts e8d627c6b docs: the change note for the usage-count audit fixes 11bec8d80 chat: a hosted session's usage counts are read off the events it receives b9d75b39e chat: the hosted-count tee is tested against the tally it must produce 39fa08d3d telemetry: stamp a version in the lifecycle tests 681e2adf1 chat: count the hosted boot agent once, and conversations opened beside 825ef2c41 connect, settings: named, multiple, switchable custom connections (#1092) f4d3f9a1a Merge branch 'dev' into fix/telemetry-audit 76fec37e3 ci: the Model Pool mirror workflow and the signature check it runs 2fb296bf1 crew: Pareto crewing, the learned picker and the Model Pool (#1093) a065ebb1b Merge remote-tracking branch 'origin/dev' into santos/dev 80ac250a0 Merge remote-tracking branch 'origin/feat/pool-mirror-workflow' into santos/dev 6092393ab Merge remote-tracking branch 'origin/fix/telemetry-audit' into santos/dev ef808dda4 session: a decision card under --yolo takes its default (#1117) 70a9e1f6e session: a task's thread stops its parts before it closes, and a machinery cut says so (#1118) c4df8f364 crew: an auto seat's rung names the pick that ran, once, for both doors (#1119) a9fcf9bc8 crew: a bare auto row under picked from = catalog reads the catalog's figures alone (#1120) 13be0dce4 relay: accept the exec and run doors, not only task and do (#1121) 90f29aec1 changes: name the #1120 note by its PR and stamp the pr field 0b77926ca do: seat from the settings the run will use (#1122) 32148d87d changes: stamp pr: 1120 on the #1120 note 113ed628e pool: a launch's start-up errands stop when the process closes (#1124) 6c98ddca3 verify, codeaf: a launch compiles fewer patterns and a surface holds fewer Ps (#1125) 50fe6a3e4 pool: the min_installs floor counts installs on the client (#1127) 076cde8ae relay: fold a resubmitted row once by its nonce (#1128) 8339ae71b relay: carry the acceptable metric beside role_quality (#1129) 0367c441b outbox: a refused row leaves the outbox and the rest of its batch is sent (#1130) 09aecd2f1 relay: the index holds only the cells that meet the min_installs floor (#1131) ae7c05dcd outbox: a Send compacts the file to the rows it still holds (#1133) 9e24f72d8 pool: the errand tracker's critical sections unlock from a defer (#1134) 358a569aa relay: fold a submit batch with one read and one write per distinct cell and day (#1136) a1d2eb1f7 pool: judge landed-but-unjudged runs on the next chat start (#1132) 3c6722859 verify: take the finished-tree check at a project cloned into a subdirectory (#1135) 263625b63 exec: a headless run leaves a pending judge record at its tail (#1137) 8be373d39 run: the headless task door leaves a pending judge record (#1138) a336b246e do: leave a pending landing for the Model Pool's judge (#1139) 8820a5659 cmd/codeaf: one readPendingRows test helper, not two (#1140) 49760fb45 pool: show and verify print both metrics the index carries (#1141) 6eaeb25c9 pool: status shows the pending judge records and the last sweep (#1142) 7c00b5f2c codeaf: a test's crash fixture stays in a profile the test owns (#1145) fc2eb357a manual: the status row's ledger is the tree's, not the conversation's (#1143) a06b53a91 test: pin the delivery gate over a grown job root (#1147) 7231347eb resident: the wall refuses a first replan round it cannot hold (#1144) 5e257557f cmd/codeaf: the package's test floor clears a profile the environment named 4dd1c562f Revert "cmd/codeaf: the package's test floor clears a profile the environment named" 9075f863c pool: the seed index is regenerated from the relay by seedgen (#1148) a5c44f3d2 pool: show --cells lists the held index's cells with installs and dims (#1149) 825996739 cmd/codeaf: the package test floor clears a profile the environment named (#1150) 81568aa82 session: report gitignored build products a run leaves, without gating on them (#1152) da4910593 internal/tui3: the opening hint test owns its profile and pins both doors 72de3bca7 docs/changes: #1151 — the opening hint test owns its profile 499083d1c Revert "docs/changes: #1151 — the opening hint test owns its profile" beb31ecf7 Revert "internal/tui3: the opening hint test owns its profile and pins both doors" 25b8b7dea version: an unstamped binary names the condition, not a Makefile target (#1151) c833b423a pool: a dropped row keeps the relay's reason and status shows it (#1154) cbb7d6d9e processgroup: verify a job group's identity before signalling it (#1155) efe498ed0 internal/tui3: the opening hint test owns its profile and pins both doors (#1153) 17b3b0e40 do: a continuation continues the plan it has instead of re-planning (#1156) 22cf3ce17 changes: correct the #1155 and #1156 notes' pr fields (#1157) 8e85b0fe7 cmd/codeaf: a surface runs under a soft memory limit drawn from the machine (#1158) ef1e92756 task: status row accounting (#1159) 44f572e26 pool: status's index line says what this run's fetch did to the cache (#1161) 3600fc9a5 pool: the pool's copy of a seat's id is the bare vendor/id (#1163) a2ebd1397 cmd/codeaf: no test can push pool rows to the default relay (#1164) 4fb7f0546 docs: the road a chat door bash push took under --yolo (#1160) 69219a1e1 cmd/codeaf: the soft memory limit is the smallest real bound, not the host s memory (#1162) 2b155b4b4 relay: bound judge severity, clamp adjusted scores, publish mean and sd on one scale (#1165) 2fb55324a relay: purge fixture vendors and refuse them at the door (#1167) 57504187a exec: a job's shell cannot reach the tmux server hosting codeaf (#1166) eb7b46559 internal/session: an unattended run may not move a branch it did not create (#1168) 80f770639 pool: status says the identity, the judged total and that nothing has landed (#1169) 1cb7333fa exec: an exec run's worker spend reaches the usage ledger (#1170) 96bb9402e changes: the #1169 note's title parses and reads as one line (#1172) 627236d95 config: the task door honors a stored crew word, and a pin survives the learned pick (#1171) 593092be6 internal/tui3: a test may not read a profile it did not create (#1173) ad8be1ed7 session: a landing card under --yolo takes its default instead of parking the run (#1174) 03d49a678 gitignore: the .furrow ids the engine writes into a workspace (#1175) 46edbdd32 internal/tui3: gofmt the profile-law helpers from #1173 (#1176) 3179e3611 relay: ALLOWED_VENDORS names the catalog's vendors and codeaf (#1177) 397a145a6 docs: why a settled /task leaves commits on task/<slug>, not the worktree branch (#1178) 915ff3a76 pool: regenerate the seed index from the relay's first clean publish (#1180) d9b42430f cmd/codeaf: the model warm stops when the process closes (#1179) bd2dcf334 session: the auditor's calls say they are the auditor's and which node they check (#1181) c6ed47c5a task: json unverified branch (#1182) 360352ad4 session: a settle turn runs under the checker's own bound, not the run's wall (#1183) 814e1af0e session: the tasks text and record line name a failed/unverified node's kept branch and verdict (#1184) 7c25a26d9 internal/session: the identical-effects test stops riding the turn guard (#1185) 6e7d0ed44 cmd/codeaf: exec and run envelopes name a non-verified node's kept branch and verdict (#1186) f4079d716 session: bound a worker's park on a promoted command (#1187) 994689fe7 docs: the model pool runbook names the Workers Paid plan and what it costs (#1188) c315be13e docs: the runbook's plan line says metered, not capped, and nothing more (#1189) 2e80c60d8 internal/tui3: the status word says working while a task subtree turns (#1191) 99363ca8f session: bound a job subtree's CPU and process count (#1190) 6db5125b2 docs: what the audit check seat can execute, and where that boundary is set (#1192) 649bb4bfd cmd/codeaf: the pool start-up test empties the CI word it is not about 053e6a90f processgroup: a zombie leader does not keep a job's group alive b4e66f515 docs/changes: the entry for #1194 Co-Authored-By: Santosh kumar <[email protected]> Co-Authored-By: Claude Fable 5.1 <[email protected]> * ci: Model Pool mirror workflow (signed index onto the model-pool branch) (#1116) * ci: the Model Pool mirror workflow and the signature check it runs Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs: the change entry for the Model Pool mirror workflow --------- Co-authored-by: Claude Fable 5.1 <[email protected]> Co-authored-by: santoshkumarradha <[email protected]> * chat: the unconnected custom row on /connect keeps its vendored name (#1195) modelConnectionStatus renamed every custom-id row to the connection's Written word, and the catalog template's Written is the bare id, so a person who had connected nothing yet saw `custom` where the manual, the README and #1107 say `Custom OpenAI-compatible API`. Only a connected instance is called what the person called it now; the contract test draws both rows and is red without the fix. Found verifying the staging build of #1194. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: v0.3.0 rolled up into CHANGELOG.md (#1196) 95 loose entries — the crew family and Pareto crewing, the Model Pool and its relay, custom connections, the telemetry audit, the update door's download clock, and the fixes that followed — become the `## v0.3.0` section the stable release reads its notes from. Co-Authored-By: Claude Fable 5.1 <[email protected]> * ci: a changed directory in a nested module is not a package of the root module (#1332) The touched-packages job and the Makefile's test-touched target both derived the test set as every directory holding a changed .go file, and handed a directory under its own go.mod to go test, which answers "main module does not contain package" and fails the job as a setup error. Both walks now stop at the first go.mod above the directory and skip nested modules, so the bench fixtures under bench/bashloop/fixtures are left alone and make pr-ready derives the set the gate will. Verified on the 621-file diff that surfaced it: 43 directories to 36, the seven dropped are exactly the fixtures, and the two halves print the same list. Co-Authored-By: Claude Fable 5.1 <[email protected]> * santos/dev lands on dev: the worker harness, stopping and limits, the task page and rail, goroutine ownership at close (#1108) Integration branch santos/dev, held as a closed slice at d39a4e308 plus the empty-prompt guard for generate_image (3a1f80f9c). Everything through #1192 had already reached dev as #1194; this carries #1193 and #1203 through #1331. The worker harness: one loop under /task and codeaf do with a plan store per run, a plan seat, a work seat, a check seat and a review round after each piece of work, off unless CODEAF_TASK_BELT=bash is set, so the default road does not move. Stopping and limits: a person's stop reaches a run, a time or dollar limit ends a run and names itself, a run is handed what is left of the conversation's limits, a worker that stops making progress is told once and then ended. The task page and the rail: the plan as a tree, a run's row with its parts under it, step rows cut from the commands as they ran, a press on a run's row opens its page in under 0.2 seconds. The engine host and sign-in, the profile's composed writes across two processes, unread config keys said once. Nothing keeps writing after the process closes: the place sweep, the standing ticker, the usage writers, the pool judge sweep and the catalog warm are each owned, stopped and joined, and a law lists every goroutine a conversation process starts. The heavy-suite lock is a kernel file lock that follows the suite. The lazy lexer registry, one fuzzy matcher for every picker, the drafted-with footer on the CodeAF repository. Reviewed 2026-09-21: gates green locally and in CI once #1332 taught the touched-packages walk about nested modules; the default road verified live on both roads against a dev baseline; the one regression the slice introduced (generate_image had lost its empty-prompt guard) fixed on the slice with a contract test. Co-authored-by: Santosh kumar <[email protected]> Co-Authored-By: Claude Fable 5.1 <[email protected]> * a dev build installs beside codeaf as devaf and follows the dev channel (#1333) * install: --name installs the binary under another file name beside codeaf The installer takes --name WORD and CODEAF_INS…
santoshkumarradha
added a commit
that referenced
this pull request
Oct 25, 2026
* Honor chat-requested models for task workers and new conversations (#1935) Preserve explicit worker model selection for joined tasks and descendants. Add model-selected independent and teammate chats, atomic launch claims, updated tool guidance, and regression coverage. Validated all five live OpenRouter launch scenarios with DeepSeek v4.1 Flash and Kimi K3. All PR checks passed. Refs #1839. * Answer and verify the four skill capability questions (#1938) Add direct skill definition, capability and troubleshooting answers, including memory-independent behavior. Pin the four original queries and verify tool-result delivery through real hosted terminal chats with memory saving on and off. All eight live cases passed using exact deepseek/deepseek-v4.1-flash. All PR checks passed. Closes #1937. * Make All show the current conversation tabs (#1939) * Scope All to the current conversation tab strip * Keep screenshot launches guarded and use rendered count grammar * Guard the screenshot terminal option command * Expect current-tab scope in the All navigation hint * Scope the All wall's copy and empty state to open tabs The in-wall All chip now says the grid holds the tabs in this view rather than saved conversations, and an empty scoped grid says no conversations are open here instead of promising saved ones will appear. wallEmptyText is shared by the live grid and the currently unused wallEmptyRow, so the two stay in step. The dock test now proves the label and the mark each open the wall scoped to open tabs, and the render test pins the scoped empty line. Assisted-by: CodeAF (deepseek-v4.1-flash-1017) Co-Authored-By: CodeAF <[email protected]> * Make open tabs a property of the open wall closeWall now clears wall.openTabs with wall.on, so no reader after a close can mistake a closed scoped wall for an open one. wallOpen still reads the scope before it closes the wall, which is where the team is kept. Assisted-by: CodeAF (deepseek-v4.1-flash-1017) Co-Authored-By: CodeAF <[email protected]> * Open an overview unpicked A pick outlived a close that was not esc, so a card picked in the wall left the next overview in selection mode - and in the All scope it could be a card nobody had picked there. Every overview now opens with nothing picked. Assisted-by: CodeAF (deepseek-v4.1-flash-1017) Co-Authored-By: CodeAF <[email protected]> * Name the All door as the tabs in view in the manual places.md still promised all saved conversations from ▦ All, and keys.md listed alt+v, /wall and the strip door as one thing. They are two views since this change. Assisted-by: CodeAF (deepseek-v4.1-flash-1017) Co-Authored-By: CodeAF <[email protected]> --------- Co-authored-by: CodeAF <[email protected]> * Run the same program when connecting to another machine (#1940) * tui3, config: the gate row lands on the conversation you are in, and /status names the gate (#1063) The three Safety rows — `ask before running`, the tool exceptions and the shell command rules — wrote the profile and left the running gate alone, while the YOLO badge read that profile live. So the badge and the gate disagreed in both directions: `allow` lit the badge over a gate that kept asking, and `prompt` put it out over a gate that was still wide open, which is the false-safety claim #322 and #325 exist to prevent. `applySetting` now pushes those three rows through `ApplyApprovals` — the seam /permissions already drops a banked rule through — and refreshes the badge on the same keystroke, on the branch where the push landed. A rebuild that could not be done is said out loud on the panel's foot line in /permissions' own words, `saved · from the next session`, and the badge stays with the gate in force rather than following the file. And /status carries the posture it never carried: `approvals prompt`, `allow` or `deny`, in the `ask before running` row's own words, in the note, in --json and on the phone's status sheet, from the one posture reading the badge uses. The status row keeps its negative-space badge and the segment is no longer written into the page twice. The note's label column is measured from the widest label it carries, so three crew assertions that spelled `"\ncrew"` plus exactly five spaces were assertions about every other row on the page. They read the crew line by its label now. Co-authored-by: Claude Fable 5.1 <[email protected]> * attribution: the footer names CodeAF, and still links to agentfield.ai (#1075) * attribution: the footer names CodeAF, and still links to agentfield.ai The pull request, issue and comment footers read "Drafted with agentfield ai". They now read "Drafted with CodeAF"; the link, its utm parameters and the "reviewed and owned by the author" tail are unchanged. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GNBScbyNAbbEQ5jn9G6auy * docs: the change entry for the CodeAF footer Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GNBScbyNAbbEQ5jn9G6auy --------- Co-authored-by: Claude Opus 5 <[email protected]> * session, prompt, tui3: a wrong completion check is answered with [no change], and the answer stands (#1065 round 1) (#1067) * session, prompt: a wrong completion check is answered with [no change], and the answer stands (#1065 round 1) The end-of-turn reader saw 600 bytes of the last message, so a long table read as cut off; the carry-on told the model to explain itself, and the explanation became the only answer left in view under the fold. The reader now sees up to 8 KB of the last words, and a cut it still has to make ends with `[clipped by codeaf: N of M bytes]`, which its ask says is never evidence of a cut-off answer. The carry-on lead says who wrote the note and offers one exact reply, `[no change]`: it ends carrying on for the ask without another reading, the transcript keeps it for the model, and neither replay nor the live feed draws it — so the settled answer before it is what the fold finds. system.md trades `# Tone` and `# Delivery` for `# The answer`, `# Words or work`, `# When corrected` and `# Messages from codeaf`, and the two lines that pushed action on questions are qualified. The lean page keeps all four; the prefix waivers rise by the 1,705 bytes that cost, dated and named. Co-Authored-By: Claude Opus 5 <[email protected]> * docs: the change entry takes the pull request's own number Co-Authored-By: Claude Opus 5 <[email protected]> * session: a quiet landing is asked about the notes it was handed, not the page that names them The worker's system page now names [silent] and [stuck] under `# Messages from codeaf`, so a search of the whole request found the tag on the page. childWasHanded reads the conversation and leaves the page out. Co-Authored-By: Claude Opus 5 <[email protected]> * session, prompt: [no change] answers the note it follows, and a long ask leaves the last words cut by name (#1065 round 1) Self-review of #1067 found the token honoured on the wrong replies. The gate read the turn's carry-on count, so a model that made calls in answer to the note and then said [no change] ended the turn unread on an answer those calls may have made stale, and a token after a load or ask nudge counted too. It now reads the transcript: the token must be the very next reply to the reader's own lead (answeredNoteUnchanged), and the dead declined flag is gone. An ask up to the whole digest rides in its head, so eight kilobytes of last words behind a long ask reached the backstop clip and ended in a bare `…`. checkpointClipSaid takes the room that is left. A turn that ends on the token no longer starts the route judge or hands the token to the memory reflex, and a later reading's last words skip it. `# Messages from codeaf` named `[still asked]`, which only the reader ever receives, and left out `[stuck]` and `[held]`, which the conversation does; it now names the tags that arrive and says any other opening, such as `[image #1]`, is the person's. The waivers sit on the measured 1,779 bytes, and the manual and the change entry say what the gate actually honours. Co-Authored-By: Claude Opus 5 <[email protected]> * prompt: the new sections keep the rules the old two carried, and say what stays in view (#1065 round 1) Owner review of #1067. Replacing `# Tone` and `# Delivery` had dropped five rules; each is back inside the new sections: match the person's task and vocabulary, choose the safe option under uncertainty, evidence and blocking details complete, never a compiling scaffold or a narrowed test, never half-solved work. `# The answer` said only the last message reaches the person, which `# Planning` contradicts: the plan and the per-batch sentence are shown while a turn runs. It now says that once a turn ends only its last message stays in view. `# Words or work` sat beside `## Work or words`, which spends the same two words on hand-off sizing; it is `# Answer or change`, and it says an answer asked for in chat is written in the reply, not a file, unless the person asks for one. The waivers sit on the new measurement, 2,035 bytes over dev on both arms. Co-Authored-By: Claude Opus 5 <[email protected]> * prompt: the messages-from-codeaf list names every note codeaf writes The system prompt now distinguishes conversation user-role notes from private reader and tool-result markers. A structural test follows the production constants, and the byte waivers record the exact fixed and lean measurements. Co-Authored-By: Claude Fable 5.1 <[email protected]> * session: no change skips judgment only when it answers the reader The completion loop now uses the transcript context before suppressing the route judge or substituting the prior answer for memory. Tests cover the withdrawn and ordinary-token memory paths, and the manual separates counting from display. Co-Authored-By: Claude Fable 5.1 <[email protected]> * test: the model stub replays the no-change fold The OpenAI-compatible stub can now drive the real binary through eleven read rounds, a long table, a rejecting reader, and the exact token reply. The recipe records the tmux setup and the expected log and screen evidence. Co-Authored-By: Claude Fable 5.1 <[email protected]> * prompt: the standing-news tag stays with the event, not on the page The messages-from-codeaf list had named [something you set up fired], and TestTheSteeringLineReadsAsNewsAndNotAsARequest refused it: that line says under the news itself what it is and what not to do about it, so the page's copy is one law bought on every request of every turn for a turn most sessions never have. The list keeps the compaction tags, the structural test now files the standing frame with the private voices the page must not name, and the waivers fall to the new measurement (fixed 55,280, lean 47,055). Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: Abir Abbas <[email protected]> * Clean internal experiment records and revive test/ux for codeaf (#1068) * chore: clean internal experiment records and revive test/ux for codeaf Public-repository cleanup ahead of opening the docs to outside readers. - Remove four internal DOE/experiment record directories that reference private issue and pull-request numbers and are not current-product documentation: docs/design/pin-doe, plan-gate-doe, turn-wall-share-doe, and workspace-foundation. - Neutralize internal machine references in benchmark and design docs: the bench host ("spark"), its secrets.env path, and a personal working branch name, all now generic placeholders. - Revive test/ux/, the user-facing journey suite, for the renamed product: collapse the removed v1/v2 dual-surface logic into the single v3 surface, repoint the binary to cmd/codeaf and state to ~/.codeaf/CODEAF_*, and delete gate.sh whose premise was the v1-vs-v2 parity table. Co-Authored-By: codeaf <[email protected]> * chore: drop internal conversation-runtime wave records, keep the measurement contract The conversation-runtime directory was a local development wave: day-by-day status, /private/tmp paths, "owner authorized" process notes, draft PR numbers, and local change logs. None of it is current-product documentation a public reader needs. Keep PARETO.md, which is the benchmark's cited measurement contract, trimmed of its dangling calibration link and owner language. Co-Authored-By: codeaf <[email protected]> * chore: neutralize the remaining bench-host and work-machine references A wider sweep found the internal bench host ("Spark"), its secrets path (fleet/secrets.env), and a scratch ops directory still named in live docs, bench READMEs and bench scripts that the earlier redaction had not touched. Rename the host to "benchhost"/"the bench host", drop the secrets path, and replace "the laptop" as the owner's own machine with a neutral "work machine" where it meant the internal one. The manual's first-person "the laptop you are sitting at" is public-facing and left alone. Co-Authored-By: codeaf <[email protected]> * chore: fix review findings in the public-cleanup diff - PERF.md: drop a private tracker issue reference (#846). - PARETO.md: restore a complete sentence dropped when the calibration link was removed, and align tense with the remaining present-tense claim. - home-mission-control/DESIGN.md: drop the internal "fleet" system name and an owner-framing phrase, consistent with the secrets-path scrub elsewhere. Co-Authored-By: codeaf <[email protected]> * docs/design: the two records live code still cites stay in the tree plan-gate-doe/REPORT.md is the measured evidence behind the plan-gate default that twelve comments in cmd/codeaf, internal/config, internal/session and internal/splitgate point at, and turn-wall-share-doe is the run internal/session/turnwall.go says it was added from. CLAUDE.md keeps the record on purpose for exactly this reason, and neither folder names a machine, a secret or a private address. Co-Authored-By: Claude Fable 5.1 <[email protected]> * test/ux: the revived journey suite goes back out #991 removed it because it drove the v1 and v2 surfaces, and the revival did not change that: its journal verb reads graph.db tables (messages, nodes, usage, commands) that the v3 chat never writes, lib.sh still waits for the resident, nothing runs it from the Makefile or a workflow, and its only evidence was bash -n. docs/JOURNEY.md says internal/e2e is the one suite that drives the binary, and that stays true. A suite that has been run green against bin/codeaf can come back on its own pull request with a target that runs it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: entry for #1068 Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: the #1068 entry says why the journey suite is out, correctly The first draft said the v3 chat never writes the graph.db tables the suite reads. Running the suite showed both message rows landing, so that sentence was wrong; what is true is that nothing runs the suite, it had never been run, and its first journey fails on its first screen check (#1072). Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: codeaf <[email protected]> Co-authored-by: Abir Abbas <[email protected]> Co-authored-by: Claude Fable 5.1 <[email protected]> * session: task commits are authored by the bot account, not codeaf@localhost The author codeaf stamped on the commits it lands was `codeaf <codeaf@localhost>`, which no GitHub account can link, so every branch codeaf landed failed the CLA check. It now writes `codeaf <[email protected]>` - the identity the Co-Authored-By trailer already used - and keeps `codeaf@localhost` and `aforge@localhost` recognised as legacy by taskCommitIdentity. GLM 5.3's review caught an unmarked retired name in permissions.md that reddened the namelaw gate, and seven stale test fixtures; both fixed. Laws gate, session, exec and cmd are green. Co-Authored-By: codeaf <[email protected]> * README: show the pinned-version install line the release test requires (#1103) * README: show the pinned-version install line the release test requires The Install rewrite dropped the VERSION=<tag> pin example, and TestPublishedPinExamplesGiveVersionToBash reads every published install surface for `| VERSION=` because `VERSION=<tag> curl ...` is an easy mistake to copy — it sets the variable for curl instead of the install script. The pin goes back inline on the same host the section already uses, with the releases page still linked for tags. Co-Authored-By: codeaf <[email protected]> * docs: the change entry for the README pin example Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]> * telemetry: anonymous usage counts, off with CODEAF_TELEMETRY=off (#1095) * telemetry: the package behind anonymous usage counting, per the wire contract Four typed events (first_run, session_started, session_ended, fault) with the contract's allowlisted, bucketed props; the opt-out ladder with its off reasons; a hashed install identity at 0700/0600; a fault fingerprint over codeaf function names only; a JSON-lines spool with a deadline-bounded flush of up to 50 events per POST, 7-day age drops and a 1000-line cap; the notice constant with NoticeShown/MarkNoticeShown gating the flush; and Show() for a future codeaf telemetry show. A library only: nothing in cmd/codeaf, internal/session or internal/config reads it yet. Co-Authored-By: codeaf <[email protected]> * telemetry: tests, doc, and contract fixes for the usage-count package Adds the package's tests — bucketing edges, the opt-out ladder, install identity, the notice, fingerprinting, the spool against an httptest relay (cap, age drop, 50-per-POST splitting, a hard deadline against a hanging server, silence on failure) and the privacy law that no sentinel string reaches the wire. docs/TELEMETRY.md documents the notice, what is sent and how to turn it off, with a test that fails if the doc's property table drifts from the Go allowlist. Fixes four drifts from the wire contract found running those tests: the <1m duration bucket was missing, the fingerprint's argument stripping cut too much, the rc channel suffix must be a single integer, and under go test a config-off rung outranks the test rung in the reason reported. Co-Authored-By: codeaf <[email protected]> * telemetry: fix review defects 1-8 with tests Fingerprint stability (last-( cut, .go:/main./created-by frame rules), counted-batch flush, guarded and multi-process-safe spool, BucketDuration edges, Configure-gated write paths, O_EXCL install id, install.json preservation, buildinfo.Dirty accessor. Co-Authored-By: codeaf <[email protected]> * installer: record the install locally and print the telemetry notice Following the telemetry wire contract: a successful install now writes telemetry/install.json under the state root (dir 0700, file 0600) with the install method, channel and UTC timestamp, leaves any existing install_id alone, and makes no request the download steps did not already make. At the very end the installer prints the contract's notice to stderr after one blank line, or one line saying telemetry is off when CODEAF_TELEMETRY/DO_NOT_TRACK opts out. README gains a plain Telemetry section pointing at docs/TELEMETRY.md, and a new test/installer-telemetry.sh proves the marker, the notice and the opt-out without a network. Co-Authored-By: codeaf <[email protected]> * installer: four review corrections to the telemetry commit The review of 71ff95a6b found four things. Revert the one line that changed `if [[ -z "${TAG:-}" ]]; then` back to `${TAG:-}`: under set -u an unset TAG must reach api_problem, not crash. In write_install_marker the channel lands in JSON verbatim and CHANNEL can come from the environment, so only stable, rc, staging or dev is recorded and anything else becomes "unknown" — with test cases for a valid channel and for we"ird, both asserting the marker parses (python3 -m json.tool, or jq, else a strict channel check). Fence the README Telemetry notice in a ```text block so Markdown keeps its alignment, wording byte for byte. Clear the two shellcheck findings in test/installer-telemetry.sh with one block disable for SC2016/SC2034 — ok() evals those strings, so the quoting is deliberate — and export CODEAF_TELEMETRY/DO_NOT_TRACK where a subshell needs them, without weakening any assertion. Co-Authored-By: codeaf <[email protected]> * release-downloads: daily PostHog report of public GitHub release download counts A scheduled workflow reads the repository's releases from the public GitHub API and sends one codeaf:release_downloads event per binary asset (codeaf-<os>-<arch>[.exe]) — sidecars like checksums.txt are not reported. Each event carries release_tag, channel (stable or the prerelease suffix), prerelease, os, arch, the cumulative download_count and the UTC snapshot_date; the event id is derived from tag, asset and date, so a same-day rerun deduplicates instead of counting twice. No code in the binary; counts come only from public release data. Co-Authored-By: codeaf <[email protected]> * release-downloads: tests cover the workflow, docs and pagination The dry-run unittests now also assert the identifiers the wire spec spells out where the script and the workflow spell them: the schedule, dispatch, contents-read permissions, built-in token, project-key secret and host variable in .github/workflows/release-downloads.yml, the public-data paragraph in docs/rules/promotion.md, stdlib-only imports, pagination across API pages, the host override and the /batch/ endpoint, sidecar skipping, draft skipping, and the channel words stable, rc, dev and staging. Co-Authored-By: codeaf <[email protected]> * release-downloads: four review corrections to the script Channel detection now follows the tag grammar in cmd/codeaf-release/version.go: stable, rc, dev and staging tags match their four patterns and anything else reports "unknown", so dev-20260917-09299019bcdc is no longer mislabelled "stable". --dry-run works without CODEAF_POSTHOG_PROJECT_KEY, the local and test path: the batch prints with api_key "<unset>" and exits 0, and only the sending path stops with the notice. Every event carries "$geoip_disable": true next to "$process_person_profile": false, since the sender is a CI runner whose location is meaningless. The module docstring now names release-downloads.yml, the file that exists. The fixture holds one tag of each shape plus an unrecognised one, and the tests assert the channel for each, including the two real dev and staging tags. Co-Authored-By: codeaf <[email protected]> * release-downloads: test runs on Python 3.9 too ScriptSourceTest read sys.stdlib_module_names unguarded; the attribute arrived in Python 3.10, so the suite errored on macOS stock python3 (3.9.6). Guarded with getattr so the pinned import-set check stands on its own where the attribute is absent; 32 tests pass on 3.9, 3.10 and 3.14. Co-Authored-By: codeaf <[email protected]> * installer telemetry: the notice is read from docs/TELEMETRY.md The test read .telemetry-contract.md, a working note that is not in the repository and never will be, so it aborted with "telemetry contract is missing" in a clean checkout. It now extracts the fenced block under "## The notice" in docs/TELEMETRY.md — the same bytes as the Notice constant in internal/telemetry/notice.go — and holds the installer's TELEMETRY_NOTICE to it as before; a new assertion holds the README's telemetry fence to it too, byte for byte, so the invariant the suite existed to guard now has its third leg. Comments in scripts/install.sh, internal/telemetry/telemetry.go and this test now name docs/TELEMETRY.md as the source of the notice. No Go code changed and no Go tests were run. shellcheck is clean; the suite's 33 passing assertions include the two new notice checks, and the same two stat(1)-related assertions that already failed on this GNU machine before the change still fail with identical output. Co-Authored-By: codeaf <[email protected]> * release-downloads: the download-counts paragraph lives in docs/TELEMETRY.md docs/rules/promotion.md was the only home the paragraph had, and it came with a fallback pinning the branch to the state before the telemetry doc existed. The paragraph moves, byte for byte, into docs/TELEMETRY.md as a final "## Download counts" section beside the rest of the telemetry story, and promotion.md returns to its origin/dev content, so this branch now touches no release docs. test_no_telemetry_doc_on_this_branch asserted docs/TELEMETRY.md did not exist and is deleted. The two tests that read the paragraph read it where it now lives: both require a "## Download counts" section in docs/TELEMETRY.md naming .github/workflows/release-downloads.yml, scripts/release_downloads.py and the checksums.txt sidecar, and the promotion.md fallback is gone. No Go code changed and no Go tests were run; the property table in docs/TELEMETRY.md is untouched. The suite passes: 31 tests, OK. Co-Authored-By: codeaf <[email protected]> * installer telemetry: the mode check asks GNU stat first On Linux `stat -f` succeeds and reports the filesystem, so the BSD-first order never reached the GNU form and both permission checks failed. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01CqfhxLrrBNQxwPNvEgoBGR * telemetry: refuse to send to the production relay from a test binary Co-Authored-By: codeaf <[email protected]> * telemetry: testHome points the endpoint at a dead loopback Co-Authored-By: codeaf <[email protected]> * telemetry: the orphan-adoption test names the dead relay it now gets Co-Authored-By: codeaf <[email protected]> * telemetry: a test pins that no test binary reaches the production relay Co-Authored-By: codeaf <[email protected]> * telemetry: three tests pin the test-binary relay refusal Each asserts through real calls, not a re-implemented effect: the newRelay override lands after testHome and a flush reaches that recorder; a default-endpoint flush is refused like a dead relay (zero round trips, event still spooled); forcing the ladder on cannot reopen the path. Co-Authored-By: codeaf <[email protected]> * telemetry: allocation-free session counters Snapshot returns the SessionStats fields the SessionEnded constructor takes, so the wiring hands the struct straight over. Cost is stored as integer micro-dollars and divided back once, in Snapshot. Count* never ask whether telemetry is enabled and never allocate. Co-Authored-By: codeaf <[email protected]> * telemetry: gofmt the counters test One alignment fix in the many-goroutine test; no behaviour change. Co-Authored-By: codeaf <[email protected]> * config: telemetry setting row (CODEAF_TELEMETRY) Key "telemetry", on|off, default on, env pin CODEAF_TELEMETRY, in the interface group beside the history row. TelemetryAtIn walks the ladder — pin, project file, profile config, default — and TelemetryOffReason answers whether config turned the pipe off and names which rung did. The row sits in ProjectKeys so a repository may answer for itself. Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the telemetry command, and the setting it writes Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: session lifecycle events at execute (work in progress) Configure, notice, first_run and session_started at the start of every session command, session_ended and a one-second flush at its exit. The end event does not yet carry the session counters. * provider: count answered model calls and their spend at the log door * session: count sealed turns and executed tool calls * cmd/codeaf: the session-ended event carries the run's counters telemetryEnd builds the end event from telemetry.Snapshot so turns, model calls, tool calls and cost reach the spool; the stop-reason switch becomes a map over the exit ladder; and both lifecycle constructors are gated on the ladder being on so a disabled run touches no state. Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the counters test says the tally is process-wide The exact turn, call and cost assertions hold for one run of the package; a repeated run counts into the same atomic tally again. Co-Authored-By: codeaf <[email protected]> * guard: a fault hook Recover calls, for a reporter above this package Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the fault event at both crash sites, off the current session Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: a test that the crash site spools a fault and not the panic text Co-Authored-By: codeaf <[email protected]> * docs: finish the anonymous usage counts contract and add the 1095 changelog entry * guard: the fault hook sits behind an atomic pointer A goroutine recovering a panic read the hook while a test on another goroutine wrote it, which the race detector reported. SetOnFault stores it once; Recover loads it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: a task's interrupt word comes from the signal, not the cancelled context stopSignals cancels the signal context at the end of every errand, so the flag read from its Err said interrupted for a run that finished cleanly. Co-Authored-By: Claude Fable 5.1 <[email protected]> * tui3, installer: the telemetry row has a tab, and a custom install writes no marker TestEverySettingRowHasATab wants every registry row on the settings panel. TestCustomInstallOutsideTheStateRootDoesNotRunAdoption wants an install outside the state root to leave the login's state folders alone, so the install marker is written only when the root exists or the install is in it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: the counters test zeroes the process tally first Other tests in the package drive model and tool calls through the doors that count, so the exact bands held only when the test ran alone. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: the version test drives the telemetry lifecycle, not execute execute starts writers under the state root that outlive the test, and the temp dir's cleanup raced them. The lifecycle alone is what the test pins. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: gofmt the lifecycle test Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: codeaf <[email protected]> Co-authored-by: Claude Fable 5.1 <[email protected]> * session: the carry-ladder fixtures wait for the mark's drawing, not race the scheduler (#1104) * session: the carry-ladder fixtures wait for the mark's drawing, not race the scheduler TestASuccessfulHandoffJournalsTheRungThatSuppliedTheBrief failed once in a full make check at line 118: the worker's brief was the running model's draft, not the mastermind's written handoff. THE CAUSE IS NOT A STOLEN STEP. Reproduced under load (60 CPU burners, the fixture run 1500 times), every request the fixture makes is either the turn's own or one the aside answers: the queue carries only `round`, `draft` and `writer`, and the errands beside the turn (title, namer, the mark's drawing) never touch it. What moves is the ROUND COUNT — 41 to 54 across the runs — because the mark's drawing is a sidecar (sidecar.go) whose landing the scheduler places, and the turn is never held for it. On a busy machine the turn runs its rounds off faster than the drawing's goroutine is scheduled onto a core, the gap between the net firing and the drawing landing outruns the slack the script carries ([checkpointSlack] says so in its own words), the writer's ask lands PAST the end of the 53-step script, the scripted completer answers it with its past-the-end line `(unscripted)` — which is not prose — the handoff rung reads degenerate, and the carry ladder falls onto the draft. That is #392 exactly, one mechanism along. WHAT FIXES IT IS WAITING FOR THE READING. checkpointAgent now puts a watch on the turn's own context ([watchReadings]) and the handover fixtures submit through [watchedContext], so [answerWhenQuiet] holds each request that carries the belt until every reading beside it has landed — the order a real turn has, where a real model spends seconds on a step and the small readings land inside it. The gap is then one boundary whatever else the machine is doing, and a script long enough to cross the mark is long enough. THE REGRESSION TEST holds the drawing back by a fixed wall time — standing in for a scheduler that does not reach it in time — and insists on the behaviour: the turn does not spend its script racing the drawing, and the writer's rung still supplies the brief. It fails without the watch (the turn makes 54 rounds) and passes with it. Co-Authored-By: codeaf <[email protected]> * docs: the change entry for the carry-ladder drawing race Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]> * docs: the install proxy is live, and the manual says so (#1076) * docs: the install proxy is live, and the manual says so `https://agentfield.ai/get/codeaf` has served `scripts/install.sh` since 2026-09-16, the day the repository went public, so the compiled-in manual and docs/GUIDE.md stop saying it is "not serving yet" and stop calling the repository private. The proxied line goes first, as the README already has it; the raw GitHub line is described as the script under it. Both pages say to pipe into `bash`, not `sh`, because the script uses pipefail and `[[ ]]` and dash rejects them, and both state the channel path (`/dev`, `/staging`, `/rc`) beside the flags. The manual also says what the proxy refuses: a script without its channel line exactly once, or a body that is not a shell script, is a 502 rather than the wrong thing. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: entry for #1076 Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Fable 5.1 <[email protected]> * A launch says a newer codeaf is out, and /update installs it and restarts (#1060) * The release-tag grammar has one home, and it is internal/update Stable, rc, dev and staging tags are parsed, compared and bumped in exactly one place now. cmd/codeaf-release keeps its next, kind and prune behaviour and its tests unchanged; it reads the grammar from the new package rather than owning a second copy of it. A second parser is the thing that drifts: the updater about to land needs the same answer to "is this tag newer than that one" that the release tool gives, and one source of truth is the only way they cannot disagree. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * internal/update: look up a release, check it, and replace this binary with it The launch check answers only for a build that could sensibly be behind: a stable tag, or a release candidate whose stable line has since been published. A dev or staging build, a make build sha and an unstamped binary make no request at all, because a channel build is deliberately newer than stable and a source build belongs to whoever built it. The answer is cached for a day beside the profile, and every failure is silence. The install downloads into the target's own directory, matches the sha256 from checksums.txt, and only then renames over the running file, so a refused download, a wrong checksum or an unwritable directory all leave the original binary exactly where it was and no temp file behind. The API token never leaves the API host: the redirect policy strips it the moment a redirect changes host, which Go does not do on its own for another port or a subdomain. Windows has no atomic rename over a running image, so it goes through ReplaceFileW and puts the original back when Windows reports the one failure that moves it. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * tui3: a launch says a newer codeaf is out, and /update installs it The launch line is one dim transcript note carrying both versions, /update, and the curl line, because the command is what the owner asked to be given. It arrives as a command rather than an opening read, so the first frame is drawn before the network is asked anything and a slow or refused answer draws nothing. /update — /upgrade is the same road — names the tag, then the checksum and where it landed, then quits with a restart plan for the door to carry out. Already newest says so and stays. It refuses while a turn or a task is running, and once it has started, one shared guard keeps a second /update and a new turn from beginning underneath it: the restart must never quit on top of live work, and the home page's Enter roads go through the same guard chat's does. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * codeaf update, and the door that restarts the same conversation codeaf update --check prints one line and exits 0 when this release is newest, 3 when a newer one exists, 1 when it could not ask. Without --check it installs stable, or whatever --rc, --dev, --staging or --version names, choosing a channel build by its own publish time the way the installer does. A binary built from source refuses in place and names its path rather than replacing something a developer is holding. The restart happens here and not inside Bubble Tea: the surface fills a slot, the door reads it once the frame is down and the terminal is back, and then execs the new binary with this launch's own arguments plus --session pointing at this transcript, so the conversation reopens where it was. A linked-local engine is asked to retire only after the surface has detached, and it still refuses while another window or any live work is there. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * remote: a window that says it is leaving needs no torn-link grace The twenty-second grace exists for a terminal that was killed without saying so, and a deliberate detach is the opposite of that — the window has just announced its departure. Carrying the grace anyway left a cleanly closed window looking attached for two more call deadlines, which kept a freshly installed codeaf waiting behind the engine the old one had already left. It clears only once the last view is gone. Turns, tasks, unanswered questions, other views and live work all still refuse retirement, and a refusal still never stops the work it protects. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * The installer and the updater agree on what a channel tag looks like Two roads now install codeaf, and they were reading the same release list by different rules: the shell accepted any dev-* or staging-* tag and semver parts with leading zeroes, while internal/update accepts only the shapes the release workflow actually publishes. A malformed tag newer than a real one would send the two roads to different builds. One helper carries the grammar to both channel selection and the line that names the channel, and an unrecognised tag for the asked-for channel now says so instead of installing whatever came back. The publish-time ordering #1056 added is untouched: only which candidates are eligible changed. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * The manual stops saying that nothing self-updates That sentence was true until this change and is now the opposite of true, so it goes, and a section written in the words people bring takes its place: is there a newer version, how do I update codeaf, why does it say this every time I start. It states who gets the launch line and who never does, the opt-out variable, the day the cached answer lasts, the shell exit codes, and both refusals — a build made from source and an executable nobody may write. The command table names /update and /upgrade with the argument they take. Four probes reach the new section, and the remote page keeps its own asker's words in its heading so the new section cannot take that question from it. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * docs/changes: the update check and /update, for #1060 Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: --check answers for the channel it was asked about, and a newer build is never downgraded Two things a hand verification caught. --check parsed whatever tag it had selected with the stable grammar alone, so --dev, --staging and --rc could never be "newer" and every one of them answered "you are on the newest codeaf" and exited 0 while a newer build sat in the list. The check now keeps its channel all the way through the decision and always names the tag it is talking about: stable and rc compare as semver, a dev or staging tag is the same tag or a different one, and --version is equal or it is not. And a build AHEAD of the newest stable was quietly installed backwards — the check said newest, the install downgraded it. Both roads refuse now, naming both versions and the deliberate way past: this codeaf is v0.3.0, ahead of the newest stable v0.2.0 — pass --version v0.2.0 to install it anyway An explicit tag IS that deliberate road, so --version and /update <tag> still install exactly what was named. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: a failure names the asset and the status, and ends with the curl line A download that failed printed the whole fallback URL, which put the retired product name on somebody's screen. The line names the asset it asked for and the status it got back, and nothing about which repository answered — the compatibility attempts still happen, they are just not the person's business. Every terminal failure now ends with the curl line, which D9 asked for and the download path had been missing. The / list declared no argument for /update while the command page documented one, so the list gains the second row the way /cache has one. The manual section now says what the code does about a build ahead of the newest stable, where it had said such a build "gets nothing" — true of the launch line, and false of the install until this wave. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: the curl line is agentfield.ai/get/codeaf, the supported road The launch line, the refusals and the terminal door offered the raw GitHub address of scripts/install.sh. The repository is public and agentfield.ai/get/codeaf has served that script from main since 2026-09-16, so the constant now spells the supported line, the manual's update section names it in place of "the curl line below", and the change entry says so. Asked for on the pull request by Santosh. Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Opus 5 (1M context) <[email protected]> * a run's own scratch note no longer fails the delivery gate (#1096) * codeaf: a run's own note is offered outside the tree it is judged in leafOutputHint invited a job root to write its overflow document at a workspace-relative name (exec.SuggestPathFor's task-2-x1-toolchain-checks.md shape), so a run working in a person's directory left that note untracked at the root of their working tree. The delivery gate holds the run's record — the artifact registry, joined by jobArtifacts, plus the delivered tree — against the request, read the note as part of what was handed over, and refused the delivery; the run then ended incomplete over complete work. There is no separate mechanical clean-tree check to exempt, so the fix is the writer's: where the workspace belongs to the person, the root's file invitation points at the run's scratch beside its recorders and job logs, and the message keeps carrying the deliverable. * codeaf: a person's own file still joins the record the gate reads TestARootLeafInAPersonsDirectoryPutsItsNotesInScratch baselined the tree after the note was already written, so its landing read could never have observed anything: the record stayed empty because of the ordering, not because of the address. The baseline now happens first, in the executor's own order, and a hint that ever points into the tree again fails on the observation, not only on the spelling. And the other half of the law is pinned down: a file that turns up in the person's tree while the leaf runs — even at a name wearing the run's own task-<n>-<slug>.md shape — is still observed and still lands in the record the delivery gate is held to. Only the run's own note moved; nothing else about what the tree says was loosened. Co-Authored-By: codeaf <[email protected]> * docs: the change note for the root's overflow file moving out of the tree --------- Co-authored-by: codeaf <[email protected]> * chat: the wordmark is drawn CodeAF, four rows, capitals on the d's cap line (#1106) `┌─┐ / ├─┤ / └─┘` is not an `a`, it is two closed bowls of equal size stacked on each other, and two equal closed bowls are an `8`. That was the second-to-last glyph of the product's own name on the first screen of every fresh install. The owner's answer is to have the two letters at the end stand up out of the word as capitals: `A` is an apex, a crossbar and two legs; `F` is the one letter whose case is told by its arms alone, so its top arm is the longer one. A CAPITAL'S TOP IS THE ASCENDER'S TOP, and three rows of box-drawing could not give it that. A bar is drawn at the middle of its cell and a stem reaches the cell's edge, so the `d` stood half a row over any capital however it was formed — and a stem in the bottom row hung half a row under the bowls' baseline the same way. The fourth row is that half a row made whole: the x-height letters live in the lower three and leave the top row blank; the capitals and the `d`'s ascender start in the top row, the ascender as the half-stroke `╷` so its top is the capitals' bar; and every stem that ends on the baseline ends as `╵`, which stops where `└─┘` does. A `│` in the bottom row is a descender and nothing else. THE WORD IS SPELLED `codeaf` AND DRAWN `CodeAF`. `product` is unchanged, the glyph table is still keyed by its lowercase letters, and a terminal that cannot draw boxes still gets the word `codeaf`. The greeting's centring test read the unit's left edge off the wordmark's first row, which is now the cap line the x-height letters leave blank; it reads the second row. The one manual page that draws the wordmark had been drawing the retired `aforge` letterforms under the label "the codeaf wordmark" — the name law walks text and cannot see a name spelled in box-drawing — and draws the shipped forms now. Co-authored-by: Claude Fable 5.1 <[email protected]> * A real release download finishes: the 3-second check clock no longer covers the asset (#1110) * update: a release download gets its own clock, not the check's The release client was built as http.Client{Timeout: 3s} because three seconds is right for the launch check, and Go's Client.Timeout covers the WHOLE exchange including every body byte. The same client then downloaded the release asset, so a 57 MB download on a healthy link — about eight seconds here — was cut off three seconds in and a person read `read the release response: context deadline exceeded`. Client.Timeout is gone. The transport bounds only what has made no progress: a 5 s dial, a 5 s TLS handshake, a 10 s wait for response headers. Every call now takes its deadline from its context. The launch notice and `--check` share the one CheckTimeout constant, install metadata gets 10 s per request, and the asset gets a 30-second no-byte stall window plus a 15-minute ceiling — a dead link fails with a sentence, a slow one finishes. No Go deadline text reaches a person. A stall says `downloading codeaf-linux-amd64 stalled — no bytes for 30 s`, the ceiling says `took longer than 15 minutes`, and late headers say `did not start arriving within 10 s`. The stall window, the ceiling and the metadata window are injectable, so the tests prove the clocks with real slow, silent and trickling servers rather than waiting out the shipped ones. Co-Authored-By: Claude Fable 5.1 <[email protected]> * update: both doors read the one check constant `codeaf update` and the chat surface each spelled `3*time.Second` when they built the release client, which is how a figure meant for one small request came to govern an install. Both now pass update.CheckTimeout, and the three seconds live in exactly one place. The terminal's `--check` takes the client's Check, which is the whole-exchange check deadline; a plain `codeaf update` keeps Select, so the metadata requests an install makes get the ten seconds they need. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: the update download's own clock (#1110) Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Fable 5.1 <[email protected]> * docs/changes: v0.2.1 rolled up into CHANGELOG.md (#1113) The 33 entries that landed on dev since the v0.2.0 roll-up are the ## v0.2.1 section of CHANGELOG.md now; docs/changes/unreleased/ keeps only this pull request's own entry. Stable v0.2.1 takes its notes from that section. Co-Authored-By: Claude Fable 5.1 <[email protected]> * README: telemetry folds under Docs (#1115) The 23-line Telemetry section between Install and the product tour becomes one collapsed block at the end of Docs: a plain summary line, the notice verbatim, one sentence and the link to docs/TELEMETRY.md. The notice block is unchanged, so it still matches the binary, the installer and docs/TELEMETRY.md. Replaces #1112. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01NoeSVN8nqmwNVRXExgoUNN * santos/dev lands on dev: crew family, Pareto crewing, the Model Pool, custom connections (#1194) Lands the integration branch santos/dev (#1108, head 6db5125b2, 152 commits over dev) on dev as one squash, plus two fixes that kept its touched-packages job red: the pool start-up test empties the CI word it is not about (649bb4bfd), and a zombie leader no longer keeps a job's process group alive on Linux, which had delayed every finished background job by the two-second termination grace since #1155 (053e6a90f). #1108 carries the full account; the 94 change entries under docs/changes/unreleased/ are the per-change record. The commits folded in, oldest first: 5c413ef7b Keep archived evidence readable while tasks finish 3705a0cfe Document recovery evidence retrieval change 7834cc7d0 tui3, manual: a question's own answer box answers to the whole caret vocabulary 99d66e98c task: Add open/all source toggle to crew config c1e4cb57f docs: the change entry for the crew model family toggle e63b43927 crew: round-1 review fixes 304a5a223 crew: round-2 review fix 65477c0c5 task: Add open/all selector to /crew TUI picker 9189ef764 Merge branch 'task/add-open-all-selector-to-crew-tu-9e8cc0' into feat/crew-open-all-toggle ba9c12d61 crew: reconcile the picker onto config's source helpers ea05bc0f5 crew: a chosen family counts as an answered crew e1a037e0a Merge remote-tracking branch 'origin/dev' into feat/crew-open-all-toggle f6c2924a7 crew: the open table takes the picks we landed on eff44b542 docs: the change entry carries the open-table move and the family gate 71ce9bec0 crew: the family reaches the fallback, the settings skin, and the row's line b8ca5f513 crew: round-1 review fixes on the new table 8db5708ef crew: round-2 review fixes ce9759ef4 crew: round-3 review fixes 956e1f514 crew: round-4 review fixes 37ad27f4e crew: fixes from the first Opus round 6ffb95813 crew: the fixes from the Opus confirmation round 72b027436 crew: the comments the atomic write left behind 70d0e205b crew: round-four fixes from the Opus review 234300e37 Merge branch 'dev' into feat/crew-open-all-toggle 894292f28 crew: round-five fixes from the Opus review fba77f92d An answered landing carries its answer's fate when it is raised again, and an in-flight settle holds the question down (#1077) e69445058 Changelog entry for the landing-answer fate (#1086) 64ab7eae0 crew: round-six fixes from the Opus review 070016419 crew: round-seven fixes from the Opus review af09aa852 The review round's four findings: claim ownership, decider changes, recordable answers, plain words c24af0248 crew: round-eight fixes from the Opus review 289d372b9 The tests the landing's answer carries: fate, hold-down, redraw, claim doors 7e38346a9 The take-down comes before every mint, and the tui3 walk reads the newest card dbe1be03e The review rounds' last word: the release owns its claim, and the docs say what the code does 4cc4945a7 The Opus review: a release owns its generation, and every failed round still says its word a5cce0dde The done card says who is deciding, so the clause is a duplication, not the fact 6bd276e6c Merge fix/1077-landing-reraise into santos/dev 271016938 Merge feat/crew-open-all-toggle into santos/dev b459399d3 Merge fix/question-answer-box-caret-keys into santos/dev a38492026 Merge codex/recovery-evidence into santos/dev dc22e6461 cmd/codeaf: the role-door tests read the shipped mastermind instead of a copy of it 39f528f70 release: the pin-example check reads the guide the examples moved to 6439d3cb8 session, tui3: gofmt the three files santos/dev merged unformatted 854037967 tui3: a question's answer box kills to the end of the line on ctrl+k d93ec61ea session: the late-attach proposal test holds the work until the replay is read a81ead4de session: the landing-fate tests read today's clock instead of a fixed date 8fe4fff38 provider: the retry-avoid list rides the call's context into the knobs a266da3e2 provider: the retry-avoid veto narrows the wire through one field law 197d89334 exec: a node retry asks the router to avoid the lanes that failed it 2d02cc0b9 provider: the set-servable law's doc comment keeps its paragraph break e1d0e7524 exec: the retry test names its helper and spells its wall in time units e7d65a517 docs: the change note for the retry that avoids the failed upstream 11decbd92 Merge fix/provider-failover into santos/dev c099a51c2 Merge dev into santos/dev e47d92bde provider, exec: the lane a failure names is a wire fact provider reads (#1105) 396be97be Merge dev into santos/dev 3b97f50c3 Merge dev into santos/dev ea3b5c9a0 telemetry: the send path drops events that cannot name their version 573b288e4 cmd/codeaf: keep smoke runs from sending usage counts e8d627c6b docs: the change note for the usage-count audit fixes 11bec8d80 chat: a hosted session's usage counts are read off the events it receives b9d75b39e chat: the hosted-count tee is tested against the tally it must produce 39fa08d3d telemetry: stamp a version in the lifecycle tests 681e2adf1 chat: count the hosted boot agent once, and conversations opened beside 825ef2c41 connect, settings: named, multiple, switchable custom connections (#1092) f4d3f9a1a Merge branch 'dev' into fix/telemetry-audit 76fec37e3 ci: the Model Pool mirror workflow and the signature check it runs 2fb296bf1 crew: Pareto crewing, the learned picker and the Model Pool (#1093) a065ebb1b Merge remote-tracking branch 'origin/dev' into santos/dev 80ac250a0 Merge remote-tracking branch 'origin/feat/pool-mirror-workflow' into santos/dev 6092393ab Merge remote-tracking branch 'origin/fix/telemetry-audit' into santos/dev ef808dda4 session: a decision card under --yolo takes its default (#1117) 70a9e1f6e session: a task's thread stops its parts before it closes, and a machinery cut says so (#1118) c4df8f364 crew: an auto seat's rung names the pick that ran, once, for both doors (#1119) a9fcf9bc8 crew: a bare auto row under picked from = catalog reads the catalog's figures alone (#1120) 13be0dce4 relay: accept the exec and run doors, not only task and do (#1121) 90f29aec1 changes: name the #1120 note by its PR and stamp the pr field 0b77926ca do: seat from the settings the run will use (#1122) 32148d87d changes: stamp pr: 1120 on the #1120 note 113ed628e pool: a launch's start-up errands stop when the process closes (#1124) 6c98ddca3 verify, codeaf: a launch compiles fewer patterns and a surface holds fewer Ps (#1125) 50fe6a3e4 pool: the min_installs floor counts installs on the client (#1127) 076cde8ae relay: fold a resubmitted row once by its nonce (#1128) 8339ae71b relay: carry the acceptable metric beside role_quality (#1129) 0367c441b outbox: a refused row leaves the outbox and the rest of its batch is sent (#1130) 09aecd2f1 relay: the index holds only the cells that meet the min_installs floor (#1131) ae7c05dcd outbox: a Send compacts the file to the rows it still holds (#1133) 9e24f72d8 pool: the errand tracker's critical sections unlock from a defer (#1134) 358a569aa relay: fold a submit batch with one read and one write per distinct cell and day (#1136) a1d2eb1f7 pool: judge landed-but-unjudged runs on the next chat start (#1132) 3c6722859 verify: take the finished-tree check at a project cloned into a subdirectory (#1135) 263625b63 exec: a headless run leaves a pending judge record at its tail (#1137) 8be373d39 run: the headless task door leaves a pending judge record (#1138) a336b246e do: leave a pending landing for the Model Pool's judge (#1139) 8820a5659 cmd/codeaf: one readPendingRows test helper, not two (#1140) 49760fb45 pool: show and verify print both metrics the index carries (#1141) 6eaeb25c9 pool: status shows the pending judge records and the last sweep (#1142) 7c00b5f2c codeaf: a test's crash fixture stays in a profile the test owns (#1145) fc2eb357a manual: the status row's ledger is the tree's, not the conversation's (#1143) a06b53a91 test: pin the delivery gate over a grown job root (#1147) 7231347eb resident: the wall refuses a first replan round it cannot hold (#1144) 5e257557f cmd/codeaf: the package's test floor clears a profile the environment named 4dd1c562f Revert "cmd/codeaf: the package's test floor clears a profile the environment named" 9075f863c pool: the seed index is regenerated from the relay by seedgen (#1148) a5c44f3d2 pool: show --cells lists the held index's cells with installs and dims (#1149) 825996739 cmd/codeaf: the package test floor clears a profile the environment named (#1150) 81568aa82 session: report gitignored build products a run leaves, without gating on them (#1152) da4910593 internal/tui3: the opening hint test owns its profile and pins both doors 72de3bca7 docs/changes: #1151 — the opening hint test owns its profile 499083d1c Revert "docs/changes: #1151 — the opening hint test owns its profile" beb31ecf7 Revert "internal/tui3: the opening hint test owns its profile and pins both doors" 25b8b7dea version: an unstamped binary names the condition, not a Makefile target (#1151) c833b423a pool: a dropped row keeps the relay's reason and status shows it (#1154) cbb7d6d9e processgroup: verify a job group's identity before signalling it (#1155) efe498ed0 internal/tui3: the opening hint test owns its profile and pins both doors (#1153) 17b3b0e40 do: a continuation continues the plan it has instead of re-planning (#1156) 22cf3ce17 changes: correct the #1155 and #1156 notes' pr fields (#1157) 8e85b0fe7 cmd/codeaf: a surface runs under a soft memory limit drawn from the machine (#1158) ef1e92756 task: status row accounting (#1159) 44f572e26 pool: status's index line says what this run's fetch did to the cache (#1161) 3600fc9a5 pool: the pool's copy of a seat's id is the bare vendor/id (#1163) a2ebd1397 cmd/codeaf: no test can push pool rows to the default relay (#1164) 4fb7f0546 docs: the road a chat door bash push took under --yolo (#1160) 69219a1e1 cmd/codeaf: the soft memory limit is the smallest real bound, not the host s memory (#1162) 2b155b4b4 relay: bound judge severity, clamp adjusted scores, publish mean and sd on one scale (#1165) 2fb55324a relay: purge fixture vendors and refuse them at the door (#1167) 57504187a exec: a job's shell cannot reach the tmux server hosting codeaf (#1166) eb7b46559 internal/session: an unattended run may not move a branch it did not create (#1168) 80f770639 pool: status says the identity, the judged total and that nothing has landed (#1169) 1cb7333fa exec: an exec run's worker spend reaches the usage ledger (#1170) 96bb9402e changes: the #1169 note's title parses and reads as one line (#1172) 627236d95 config: the task door honors a stored crew word, and a pin survives the learned pick (#1171) 593092be6 internal/tui3: a test may not read a profile it did not create (#1173) ad8be1ed7 session: a landing card under --yolo takes its default instead of parking the run (#1174) 03d49a678 gitignore: the .furrow ids the engine writes into a workspace (#1175) 46edbdd32 internal/tui3: gofmt the profile-law helpers from #1173 (#1176) 3179e3611 relay: ALLOWED_VENDORS names the catalog's vendors and codeaf (#1177) 397a145a6 docs: why a settled /task leaves commits on task/<slug>, not the worktree branch (#1178) 915ff3a76 pool: regenerate the seed index from the relay's first clean publish (#1180) d9b42430f cmd/codeaf: the model warm stops when the process closes (#1179) bd2dcf334 session: the auditor's calls say they are the auditor's and which node they check (#1181) c6ed47c5a task: json unverified branch (#1182) 360352ad4 session: a settle turn runs under the checker's own bound, not the run's wall (#1183) 814e1af0e session: the tasks text and record line name a failed/unverified node's kept branch and verdict (#1184) 7c25a26d9 internal/session: the identical-effects test stops riding the turn guard (#1185) 6e7d0ed44 cmd/codeaf: exec and run envelopes name a non-verified node's kept branch and verdict (#1186) f4079d716 session: bound a worker's park on a promoted command (#1187) 994689fe7 docs: the model pool runbook names the Workers Paid plan and what it costs (#1188) c315be13e docs: the runbook's plan line says metered, not capped, and nothing more (#1189) 2e80c60d8 internal/tui3: the status word says working while a task subtree turns (#1191) 99363ca8f session: bound a job subtree's CPU and process count (#1190) 6db5125b2 docs: what the audit check seat can execute, and where that boundary is set (#1192) 649bb4bfd cmd/codeaf: the pool start-up test empties the CI word it is not about 053e6a90f processgroup: a zombie leader does not keep a job's group alive b4e66f515 docs/changes: the entry for #1194 Co-Authored-By: Santosh kumar <[email protected]> Co-Authored-By: Claude Fable 5.1 <[email protected]> * ci: Model Pool mirror workflow (signed index onto the model-pool branch) (#1116) * ci: the Model Pool mirror workflow and the signature check it runs Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs: the change entry for the Model Pool mirror workflow --------- Co-authored-by: Claude Fable 5.1 <[email protected]> Co-authored-by: santoshkumarradha <[email protected]> * chat: the unconnected custom row on /connect keeps its vendored name (#1195) modelConnectionStatus renamed every custom-id row to the connection's Written word, and the catalog template's Written is the bare id, so a person who had connected nothing yet saw `custom` where the manual, the README and #1107 say `Custom OpenAI-compatible API`. Only a connected instance is called what the person called it now; the contract test draws both rows and is red without the fix. Found verifying the staging build of #1194. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: v0.3.0 rolled up into CHANGELOG.md (#1196) 95 loose entries — the crew family and Pareto crewing, the Model Pool and its relay, custom connections, the telemetry audit, the update door's download clock, and the fixes that followed — become the `## v0.3.0` section the stable release reads its notes from. Co-Authored-By: Claude Fable 5.1 <[email protected]> * ci: a changed directory in a nested modu…
AbirAbbas
added a commit
that referenced
this pull request
Oct 28, 2026
…t saving (#2009) * Honor chat-requested models for task workers and new conversations (#1935) Preserve explicit worker model selection for joined tasks and descendants. Add model-selected independent and teammate chats, atomic launch claims, updated tool guidance, and regression coverage. Validated all five live OpenRouter launch scenarios with DeepSeek v4.1 Flash and Kimi K3. All PR checks passed. Refs #1839. * Answer and verify the four skill capability questions (#1938) Add direct skill definition, capability and troubleshooting answers, including memory-independent behavior. Pin the four original queries and verify tool-result delivery through real hosted terminal chats with memory saving on and off. All eight live cases passed using exact deepseek/deepseek-v4.1-flash. All PR checks passed. Closes #1937. * Make All show the current conversation tabs (#1939) * Scope All to the current conversation tab strip * Keep screenshot launches guarded and use rendered count grammar * Guard the screenshot terminal option command * Expect current-tab scope in the All navigation hint * Scope the All wall's copy and empty state to open tabs The in-wall All chip now says the grid holds the tabs in this view rather than saved conversations, and an empty scoped grid says no conversations are open here instead of promising saved ones will appear. wallEmptyText is shared by the live grid and the currently unused wallEmptyRow, so the two stay in step. The dock test now proves the label and the mark each open the wall scoped to open tabs, and the render test pins the scoped empty line. Assisted-by: CodeAF (deepseek-v4.1-flash-1017) Co-Authored-By: CodeAF <[email protected]> * Make open tabs a property of the open wall closeWall now clears wall.openTabs with wall.on, so no reader after a close can mistake a closed scoped wall for an open one. wallOpen still reads the scope before it closes the wall, which is where the team is kept. Assisted-by: CodeAF (deepseek-v4.1-flash-1017) Co-Authored-By: CodeAF <[email protected]> * Open an overview unpicked A pick outlived a close that was not esc, so a card picked in the wall left the next overview in selection mode - and in the All scope it could be a card nobody had picked there. Every overview now opens with nothing picked. Assisted-by: CodeAF (deepseek-v4.1-flash-1017) Co-Authored-By: CodeAF <[email protected]> * Name the All door as the tabs in view in the manual places.md still promised all saved conversations from ▦ All, and keys.md listed alt+v, /wall and the strip door as one thing. They are two views since this change. Assisted-by: CodeAF (deepseek-v4.1-flash-1017) Co-Authored-By: CodeAF <[email protected]> --------- Co-authored-by: CodeAF <[email protected]> * Run the same program when connecting to another machine (#1940) * tui3, config: the gate row lands on the conversation you are in, and /status names the gate (#1063) The three Safety rows — `ask before running`, the tool exceptions and the shell command rules — wrote the profile and left the running gate alone, while the YOLO badge read that profile live. So the badge and the gate disagreed in both directions: `allow` lit the badge over a gate that kept asking, and `prompt` put it out over a gate that was still wide open, which is the false-safety claim #322 and #325 exist to prevent. `applySetting` now pushes those three rows through `ApplyApprovals` — the seam /permissions already drops a banked rule through — and refreshes the badge on the same keystroke, on the branch where the push landed. A rebuild that could not be done is said out loud on the panel's foot line in /permissions' own words, `saved · from the next session`, and the badge stays with the gate in force rather than following the file. And /status carries the posture it never carried: `approvals prompt`, `allow` or `deny`, in the `ask before running` row's own words, in the note, in --json and on the phone's status sheet, from the one posture reading the badge uses. The status row keeps its negative-space badge and the segment is no longer written into the page twice. The note's label column is measured from the widest label it carries, so three crew assertions that spelled `"\ncrew"` plus exactly five spaces were assertions about every other row on the page. They read the crew line by its label now. Co-authored-by: Claude Fable 5.1 <[email protected]> * attribution: the footer names CodeAF, and still links to agentfield.ai (#1075) * attribution: the footer names CodeAF, and still links to agentfield.ai The pull request, issue and comment footers read "Drafted with agentfield ai". They now read "Drafted with CodeAF"; the link, its utm parameters and the "reviewed and owned by the author" tail are unchanged. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GNBScbyNAbbEQ5jn9G6auy * docs: the change entry for the CodeAF footer Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GNBScbyNAbbEQ5jn9G6auy --------- Co-authored-by: Claude Opus 5 <[email protected]> * session, prompt, tui3: a wrong completion check is answered with [no change], and the answer stands (#1065 round 1) (#1067) * session, prompt: a wrong completion check is answered with [no change], and the answer stands (#1065 round 1) The end-of-turn reader saw 600 bytes of the last message, so a long table read as cut off; the carry-on told the model to explain itself, and the explanation became the only answer left in view under the fold. The reader now sees up to 8 KB of the last words, and a cut it still has to make ends with `[clipped by codeaf: N of M bytes]`, which its ask says is never evidence of a cut-off answer. The carry-on lead says who wrote the note and offers one exact reply, `[no change]`: it ends carrying on for the ask without another reading, the transcript keeps it for the model, and neither replay nor the live feed draws it — so the settled answer before it is what the fold finds. system.md trades `# Tone` and `# Delivery` for `# The answer`, `# Words or work`, `# When corrected` and `# Messages from codeaf`, and the two lines that pushed action on questions are qualified. The lean page keeps all four; the prefix waivers rise by the 1,705 bytes that cost, dated and named. Co-Authored-By: Claude Opus 5 <[email protected]> * docs: the change entry takes the pull request's own number Co-Authored-By: Claude Opus 5 <[email protected]> * session: a quiet landing is asked about the notes it was handed, not the page that names them The worker's system page now names [silent] and [stuck] under `# Messages from codeaf`, so a search of the whole request found the tag on the page. childWasHanded reads the conversation and leaves the page out. Co-Authored-By: Claude Opus 5 <[email protected]> * session, prompt: [no change] answers the note it follows, and a long ask leaves the last words cut by name (#1065 round 1) Self-review of #1067 found the token honoured on the wrong replies. The gate read the turn's carry-on count, so a model that made calls in answer to the note and then said [no change] ended the turn unread on an answer those calls may have made stale, and a token after a load or ask nudge counted too. It now reads the transcript: the token must be the very next reply to the reader's own lead (answeredNoteUnchanged), and the dead declined flag is gone. An ask up to the whole digest rides in its head, so eight kilobytes of last words behind a long ask reached the backstop clip and ended in a bare `…`. checkpointClipSaid takes the room that is left. A turn that ends on the token no longer starts the route judge or hands the token to the memory reflex, and a later reading's last words skip it. `# Messages from codeaf` named `[still asked]`, which only the reader ever receives, and left out `[stuck]` and `[held]`, which the conversation does; it now names the tags that arrive and says any other opening, such as `[image #1]`, is the person's. The waivers sit on the measured 1,779 bytes, and the manual and the change entry say what the gate actually honours. Co-Authored-By: Claude Opus 5 <[email protected]> * prompt: the new sections keep the rules the old two carried, and say what stays in view (#1065 round 1) Owner review of #1067. Replacing `# Tone` and `# Delivery` had dropped five rules; each is back inside the new sections: match the person's task and vocabulary, choose the safe option under uncertainty, evidence and blocking details complete, never a compiling scaffold or a narrowed test, never half-solved work. `# The answer` said only the last message reaches the person, which `# Planning` contradicts: the plan and the per-batch sentence are shown while a turn runs. It now says that once a turn ends only its last message stays in view. `# Words or work` sat beside `## Work or words`, which spends the same two words on hand-off sizing; it is `# Answer or change`, and it says an answer asked for in chat is written in the reply, not a file, unless the person asks for one. The waivers sit on the new measurement, 2,035 bytes over dev on both arms. Co-Authored-By: Claude Opus 5 <[email protected]> * prompt: the messages-from-codeaf list names every note codeaf writes The system prompt now distinguishes conversation user-role notes from private reader and tool-result markers. A structural test follows the production constants, and the byte waivers record the exact fixed and lean measurements. Co-Authored-By: Claude Fable 5.1 <[email protected]> * session: no change skips judgment only when it answers the reader The completion loop now uses the transcript context before suppressing the route judge or substituting the prior answer for memory. Tests cover the withdrawn and ordinary-token memory paths, and the manual separates counting from display. Co-Authored-By: Claude Fable 5.1 <[email protected]> * test: the model stub replays the no-change fold The OpenAI-compatible stub can now drive the real binary through eleven read rounds, a long table, a rejecting reader, and the exact token reply. The recipe records the tmux setup and the expected log and screen evidence. Co-Authored-By: Claude Fable 5.1 <[email protected]> * prompt: the standing-news tag stays with the event, not on the page The messages-from-codeaf list had named [something you set up fired], and TestTheSteeringLineReadsAsNewsAndNotAsARequest refused it: that line says under the news itself what it is and what not to do about it, so the page's copy is one law bought on every request of every turn for a turn most sessions never have. The list keeps the compaction tags, the structural test now files the standing frame with the private voices the page must not name, and the waivers fall to the new measurement (fixed 55,280, lean 47,055). Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Opus 5 <[email protected]> Co-authored-by: Abir Abbas <[email protected]> * Clean internal experiment records and revive test/ux for codeaf (#1068) * chore: clean internal experiment records and revive test/ux for codeaf Public-repository cleanup ahead of opening the docs to outside readers. - Remove four internal DOE/experiment record directories that reference private issue and pull-request numbers and are not current-product documentation: docs/design/pin-doe, plan-gate-doe, turn-wall-share-doe, and workspace-foundation. - Neutralize internal machine references in benchmark and design docs: the bench host ("spark"), its secrets.env path, and a personal working branch name, all now generic placeholders. - Revive test/ux/, the user-facing journey suite, for the renamed product: collapse the removed v1/v2 dual-surface logic into the single v3 surface, repoint the binary to cmd/codeaf and state to ~/.codeaf/CODEAF_*, and delete gate.sh whose premise was the v1-vs-v2 parity table. Co-Authored-By: codeaf <[email protected]> * chore: drop internal conversation-runtime wave records, keep the measurement contract The conversation-runtime directory was a local development wave: day-by-day status, /private/tmp paths, "owner authorized" process notes, draft PR numbers, and local change logs. None of it is current-product documentation a public reader needs. Keep PARETO.md, which is the benchmark's cited measurement contract, trimmed of its dangling calibration link and owner language. Co-Authored-By: codeaf <[email protected]> * chore: neutralize the remaining bench-host and work-machine references A wider sweep found the internal bench host ("Spark"), its secrets path (fleet/secrets.env), and a scratch ops directory still named in live docs, bench READMEs and bench scripts that the earlier redaction had not touched. Rename the host to "benchhost"/"the bench host", drop the secrets path, and replace "the laptop" as the owner's own machine with a neutral "work machine" where it meant the internal one. The manual's first-person "the laptop you are sitting at" is public-facing and left alone. Co-Authored-By: codeaf <[email protected]> * chore: fix review findings in the public-cleanup diff - PERF.md: drop a private tracker issue reference (#846). - PARETO.md: restore a complete sentence dropped when the calibration link was removed, and align tense with the remaining present-tense claim. - home-mission-control/DESIGN.md: drop the internal "fleet" system name and an owner-framing phrase, consistent with the secrets-path scrub elsewhere. Co-Authored-By: codeaf <[email protected]> * docs/design: the two records live code still cites stay in the tree plan-gate-doe/REPORT.md is the measured evidence behind the plan-gate default that twelve comments in cmd/codeaf, internal/config, internal/session and internal/splitgate point at, and turn-wall-share-doe is the run internal/session/turnwall.go says it was added from. CLAUDE.md keeps the record on purpose for exactly this reason, and neither folder names a machine, a secret or a private address. Co-Authored-By: Claude Fable 5.1 <[email protected]> * test/ux: the revived journey suite goes back out #991 removed it because it drove the v1 and v2 surfaces, and the revival did not change that: its journal verb reads graph.db tables (messages, nodes, usage, commands) that the v3 chat never writes, lib.sh still waits for the resident, nothing runs it from the Makefile or a workflow, and its only evidence was bash -n. docs/JOURNEY.md says internal/e2e is the one suite that drives the binary, and that stays true. A suite that has been run green against bin/codeaf can come back on its own pull request with a target that runs it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: entry for #1068 Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: the #1068 entry says why the journey suite is out, correctly The first draft said the v3 chat never writes the graph.db tables the suite reads. Running the suite showed both message rows landing, so that sentence was wrong; what is true is that nothing runs the suite, it had never been run, and its first journey fails on its first screen check (#1072). Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: codeaf <[email protected]> Co-authored-by: Abir Abbas <[email protected]> Co-authored-by: Claude Fable 5.1 <[email protected]> * session: task commits are authored by the bot account, not codeaf@localhost The author codeaf stamped on the commits it lands was `codeaf <codeaf@localhost>`, which no GitHub account can link, so every branch codeaf landed failed the CLA check. It now writes `codeaf <[email protected]>` - the identity the Co-Authored-By trailer already used - and keeps `codeaf@localhost` and `aforge@localhost` recognised as legacy by taskCommitIdentity. GLM 5.3's review caught an unmarked retired name in permissions.md that reddened the namelaw gate, and seven stale test fixtures; both fixed. Laws gate, session, exec and cmd are green. Co-Authored-By: codeaf <[email protected]> * README: show the pinned-version install line the release test requires (#1103) * README: show the pinned-version install line the release test requires The Install rewrite dropped the VERSION=<tag> pin example, and TestPublishedPinExamplesGiveVersionToBash reads every published install surface for `| VERSION=` because `VERSION=<tag> curl ...` is an easy mistake to copy — it sets the variable for curl instead of the install script. The pin goes back inline on the same host the section already uses, with the releases page still linked for tags. Co-Authored-By: codeaf <[email protected]> * docs: the change entry for the README pin example Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]> * telemetry: anonymous usage counts, off with CODEAF_TELEMETRY=off (#1095) * telemetry: the package behind anonymous usage counting, per the wire contract Four typed events (first_run, session_started, session_ended, fault) with the contract's allowlisted, bucketed props; the opt-out ladder with its off reasons; a hashed install identity at 0700/0600; a fault fingerprint over codeaf function names only; a JSON-lines spool with a deadline-bounded flush of up to 50 events per POST, 7-day age drops and a 1000-line cap; the notice constant with NoticeShown/MarkNoticeShown gating the flush; and Show() for a future codeaf telemetry show. A library only: nothing in cmd/codeaf, internal/session or internal/config reads it yet. Co-Authored-By: codeaf <[email protected]> * telemetry: tests, doc, and contract fixes for the usage-count package Adds the package's tests — bucketing edges, the opt-out ladder, install identity, the notice, fingerprinting, the spool against an httptest relay (cap, age drop, 50-per-POST splitting, a hard deadline against a hanging server, silence on failure) and the privacy law that no sentinel string reaches the wire. docs/TELEMETRY.md documents the notice, what is sent and how to turn it off, with a test that fails if the doc's property table drifts from the Go allowlist. Fixes four drifts from the wire contract found running those tests: the <1m duration bucket was missing, the fingerprint's argument stripping cut too much, the rc channel suffix must be a single integer, and under go test a config-off rung outranks the test rung in the reason reported. Co-Authored-By: codeaf <[email protected]> * telemetry: fix review defects 1-8 with tests Fingerprint stability (last-( cut, .go:/main./created-by frame rules), counted-batch flush, guarded and multi-process-safe spool, BucketDuration edges, Configure-gated write paths, O_EXCL install id, install.json preservation, buildinfo.Dirty accessor. Co-Authored-By: codeaf <[email protected]> * installer: record the install locally and print the telemetry notice Following the telemetry wire contract: a successful install now writes telemetry/install.json under the state root (dir 0700, file 0600) with the install method, channel and UTC timestamp, leaves any existing install_id alone, and makes no request the download steps did not already make. At the very end the installer prints the contract's notice to stderr after one blank line, or one line saying telemetry is off when CODEAF_TELEMETRY/DO_NOT_TRACK opts out. README gains a plain Telemetry section pointing at docs/TELEMETRY.md, and a new test/installer-telemetry.sh proves the marker, the notice and the opt-out without a network. Co-Authored-By: codeaf <[email protected]> * installer: four review corrections to the telemetry commit The review of 71ff95a6b found four things. Revert the one line that changed `if [[ -z "${TAG:-}" ]]; then` back to `${TAG:-}`: under set -u an unset TAG must reach api_problem, not crash. In write_install_marker the channel lands in JSON verbatim and CHANNEL can come from the environment, so only stable, rc, staging or dev is recorded and anything else becomes "unknown" — with test cases for a valid channel and for we"ird, both asserting the marker parses (python3 -m json.tool, or jq, else a strict channel check). Fence the README Telemetry notice in a ```text block so Markdown keeps its alignment, wording byte for byte. Clear the two shellcheck findings in test/installer-telemetry.sh with one block disable for SC2016/SC2034 — ok() evals those strings, so the quoting is deliberate — and export CODEAF_TELEMETRY/DO_NOT_TRACK where a subshell needs them, without weakening any assertion. Co-Authored-By: codeaf <[email protected]> * release-downloads: daily PostHog report of public GitHub release download counts A scheduled workflow reads the repository's releases from the public GitHub API and sends one codeaf:release_downloads event per binary asset (codeaf-<os>-<arch>[.exe]) — sidecars like checksums.txt are not reported. Each event carries release_tag, channel (stable or the prerelease suffix), prerelease, os, arch, the cumulative download_count and the UTC snapshot_date; the event id is derived from tag, asset and date, so a same-day rerun deduplicates instead of counting twice. No code in the binary; counts come only from public release data. Co-Authored-By: codeaf <[email protected]> * release-downloads: tests cover the workflow, docs and pagination The dry-run unittests now also assert the identifiers the wire spec spells out where the script and the workflow spell them: the schedule, dispatch, contents-read permissions, built-in token, project-key secret and host variable in .github/workflows/release-downloads.yml, the public-data paragraph in docs/rules/promotion.md, stdlib-only imports, pagination across API pages, the host override and the /batch/ endpoint, sidecar skipping, draft skipping, and the channel words stable, rc, dev and staging. Co-Authored-By: codeaf <[email protected]> * release-downloads: four review corrections to the script Channel detection now follows the tag grammar in cmd/codeaf-release/version.go: stable, rc, dev and staging tags match their four patterns and anything else reports "unknown", so dev-20260917-09299019bcdc is no longer mislabelled "stable". --dry-run works without CODEAF_POSTHOG_PROJECT_KEY, the local and test path: the batch prints with api_key "<unset>" and exits 0, and only the sending path stops with the notice. Every event carries "$geoip_disable": true next to "$process_person_profile": false, since the sender is a CI runner whose location is meaningless. The module docstring now names release-downloads.yml, the file that exists. The fixture holds one tag of each shape plus an unrecognised one, and the tests assert the channel for each, including the two real dev and staging tags. Co-Authored-By: codeaf <[email protected]> * release-downloads: test runs on Python 3.9 too ScriptSourceTest read sys.stdlib_module_names unguarded; the attribute arrived in Python 3.10, so the suite errored on macOS stock python3 (3.9.6). Guarded with getattr so the pinned import-set check stands on its own where the attribute is absent; 32 tests pass on 3.9, 3.10 and 3.14. Co-Authored-By: codeaf <[email protected]> * installer telemetry: the notice is read from docs/TELEMETRY.md The test read .telemetry-contract.md, a working note that is not in the repository and never will be, so it aborted with "telemetry contract is missing" in a clean checkout. It now extracts the fenced block under "## The notice" in docs/TELEMETRY.md — the same bytes as the Notice constant in internal/telemetry/notice.go — and holds the installer's TELEMETRY_NOTICE to it as before; a new assertion holds the README's telemetry fence to it too, byte for byte, so the invariant the suite existed to guard now has its third leg. Comments in scripts/install.sh, internal/telemetry/telemetry.go and this test now name docs/TELEMETRY.md as the source of the notice. No Go code changed and no Go tests were run. shellcheck is clean; the suite's 33 passing assertions include the two new notice checks, and the same two stat(1)-related assertions that already failed on this GNU machine before the change still fail with identical output. Co-Authored-By: codeaf <[email protected]> * release-downloads: the download-counts paragraph lives in docs/TELEMETRY.md docs/rules/promotion.md was the only home the paragraph had, and it came with a fallback pinning the branch to the state before the telemetry doc existed. The paragraph moves, byte for byte, into docs/TELEMETRY.md as a final "## Download counts" section beside the rest of the telemetry story, and promotion.md returns to its origin/dev content, so this branch now touches no release docs. test_no_telemetry_doc_on_this_branch asserted docs/TELEMETRY.md did not exist and is deleted. The two tests that read the paragraph read it where it now lives: both require a "## Download counts" section in docs/TELEMETRY.md naming .github/workflows/release-downloads.yml, scripts/release_downloads.py and the checksums.txt sidecar, and the promotion.md fallback is gone. No Go code changed and no Go tests were run; the property table in docs/TELEMETRY.md is untouched. The suite passes: 31 tests, OK. Co-Authored-By: codeaf <[email protected]> * installer telemetry: the mode check asks GNU stat first On Linux `stat -f` succeeds and reports the filesystem, so the BSD-first order never reached the GNU form and both permission checks failed. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01CqfhxLrrBNQxwPNvEgoBGR * telemetry: refuse to send to the production relay from a test binary Co-Authored-By: codeaf <[email protected]> * telemetry: testHome points the endpoint at a dead loopback Co-Authored-By: codeaf <[email protected]> * telemetry: the orphan-adoption test names the dead relay it now gets Co-Authored-By: codeaf <[email protected]> * telemetry: a test pins that no test binary reaches the production relay Co-Authored-By: codeaf <[email protected]> * telemetry: three tests pin the test-binary relay refusal Each asserts through real calls, not a re-implemented effect: the newRelay override lands after testHome and a flush reaches that recorder; a default-endpoint flush is refused like a dead relay (zero round trips, event still spooled); forcing the ladder on cannot reopen the path. Co-Authored-By: codeaf <[email protected]> * telemetry: allocation-free session counters Snapshot returns the SessionStats fields the SessionEnded constructor takes, so the wiring hands the struct straight over. Cost is stored as integer micro-dollars and divided back once, in Snapshot. Count* never ask whether telemetry is enabled and never allocate. Co-Authored-By: codeaf <[email protected]> * telemetry: gofmt the counters test One alignment fix in the many-goroutine test; no behaviour change. Co-Authored-By: codeaf <[email protected]> * config: telemetry setting row (CODEAF_TELEMETRY) Key "telemetry", on|off, default on, env pin CODEAF_TELEMETRY, in the interface group beside the history row. TelemetryAtIn walks the ladder — pin, project file, profile config, default — and TelemetryOffReason answers whether config turned the pipe off and names which rung did. The row sits in ProjectKeys so a repository may answer for itself. Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the telemetry command, and the setting it writes Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: session lifecycle events at execute (work in progress) Configure, notice, first_run and session_started at the start of every session command, session_ended and a one-second flush at its exit. The end event does not yet carry the session counters. * provider: count answered model calls and their spend at the log door * session: count sealed turns and executed tool calls * cmd/codeaf: the session-ended event carries the run's counters telemetryEnd builds the end event from telemetry.Snapshot so turns, model calls, tool calls and cost reach the spool; the stop-reason switch becomes a map over the exit ladder; and both lifecycle constructors are gated on the ladder being on so a disabled run touches no state. Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the counters test says the tally is process-wide The exact turn, call and cost assertions hold for one run of the package; a repeated run counts into the same atomic tally again. Co-Authored-By: codeaf <[email protected]> * guard: a fault hook Recover calls, for a reporter above this package Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: the fault event at both crash sites, off the current session Co-Authored-By: codeaf <[email protected]> * cmd/codeaf: a test that the crash site spools a fault and not the panic text Co-Authored-By: codeaf <[email protected]> * docs: finish the anonymous usage counts contract and add the 1095 changelog entry * guard: the fault hook sits behind an atomic pointer A goroutine recovering a panic read the hook while a test on another goroutine wrote it, which the race detector reported. SetOnFault stores it once; Recover loads it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: a task's interrupt word comes from the signal, not the cancelled context stopSignals cancels the signal context at the end of every errand, so the flag read from its Err said interrupted for a run that finished cleanly. Co-Authored-By: Claude Fable 5.1 <[email protected]> * tui3, installer: the telemetry row has a tab, and a custom install writes no marker TestEverySettingRowHasATab wants every registry row on the settings panel. TestCustomInstallOutsideTheStateRootDoesNotRunAdoption wants an install outside the state root to leave the login's state folders alone, so the install marker is written only when the root exists or the install is in it. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: the counters test zeroes the process tally first Other tests in the package drive model and tool calls through the doors that count, so the exact bands held only when the test ran alone. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: the version test drives the telemetry lifecycle, not execute execute starts writers under the state root that outlive the test, and the temp dir's cleanup raced them. The lifecycle alone is what the test pins. Co-Authored-By: Claude Fable 5.1 <[email protected]> * cmd/codeaf: gofmt the lifecycle test Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: codeaf <[email protected]> Co-authored-by: Claude Fable 5.1 <[email protected]> * session: the carry-ladder fixtures wait for the mark's drawing, not race the scheduler (#1104) * session: the carry-ladder fixtures wait for the mark's drawing, not race the scheduler TestASuccessfulHandoffJournalsTheRungThatSuppliedTheBrief failed once in a full make check at line 118: the worker's brief was the running model's draft, not the mastermind's written handoff. THE CAUSE IS NOT A STOLEN STEP. Reproduced under load (60 CPU burners, the fixture run 1500 times), every request the fixture makes is either the turn's own or one the aside answers: the queue carries only `round`, `draft` and `writer`, and the errands beside the turn (title, namer, the mark's drawing) never touch it. What moves is the ROUND COUNT — 41 to 54 across the runs — because the mark's drawing is a sidecar (sidecar.go) whose landing the scheduler places, and the turn is never held for it. On a busy machine the turn runs its rounds off faster than the drawing's goroutine is scheduled onto a core, the gap between the net firing and the drawing landing outruns the slack the script carries ([checkpointSlack] says so in its own words), the writer's ask lands PAST the end of the 53-step script, the scripted completer answers it with its past-the-end line `(unscripted)` — which is not prose — the handoff rung reads degenerate, and the carry ladder falls onto the draft. That is #392 exactly, one mechanism along. WHAT FIXES IT IS WAITING FOR THE READING. checkpointAgent now puts a watch on the turn's own context ([watchReadings]) and the handover fixtures submit through [watchedContext], so [answerWhenQuiet] holds each request that carries the belt until every reading beside it has landed — the order a real turn has, where a real model spends seconds on a step and the small readings land inside it. The gap is then one boundary whatever else the machine is doing, and a script long enough to cross the mark is long enough. THE REGRESSION TEST holds the drawing back by a fixed wall time — standing in for a scheduler that does not reach it in time — and insists on the behaviour: the turn does not spend its script racing the drawing, and the writer's rung still supplies the brief. It fails without the watch (the turn makes 54 rounds) and passes with it. Co-Authored-By: codeaf <[email protected]> * docs: the change entry for the carry-ladder drawing race Co-Authored-By: codeaf <[email protected]> --------- Co-authored-by: codeaf <[email protected]> * docs: the install proxy is live, and the manual says so (#1076) * docs: the install proxy is live, and the manual says so `https://agentfield.ai/get/codeaf` has served `scripts/install.sh` since 2026-09-16, the day the repository went public, so the compiled-in manual and docs/GUIDE.md stop saying it is "not serving yet" and stop calling the repository private. The proxied line goes first, as the README already has it; the raw GitHub line is described as the script under it. Both pages say to pipe into `bash`, not `sh`, because the script uses pipefail and `[[ ]]` and dash rejects them, and both state the channel path (`/dev`, `/staging`, `/rc`) beside the flags. The manual also says what the proxy refuses: a script without its channel line exactly once, or a body that is not a shell script, is a 502 rather than the wrong thing. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: entry for #1076 Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Fable 5.1 <[email protected]> * A launch says a newer codeaf is out, and /update installs it and restarts (#1060) * The release-tag grammar has one home, and it is internal/update Stable, rc, dev and staging tags are parsed, compared and bumped in exactly one place now. cmd/codeaf-release keeps its next, kind and prune behaviour and its tests unchanged; it reads the grammar from the new package rather than owning a second copy of it. A second parser is the thing that drifts: the updater about to land needs the same answer to "is this tag newer than that one" that the release tool gives, and one source of truth is the only way they cannot disagree. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * internal/update: look up a release, check it, and replace this binary with it The launch check answers only for a build that could sensibly be behind: a stable tag, or a release candidate whose stable line has since been published. A dev or staging build, a make build sha and an unstamped binary make no request at all, because a channel build is deliberately newer than stable and a source build belongs to whoever built it. The answer is cached for a day beside the profile, and every failure is silence. The install downloads into the target's own directory, matches the sha256 from checksums.txt, and only then renames over the running file, so a refused download, a wrong checksum or an unwritable directory all leave the original binary exactly where it was and no temp file behind. The API token never leaves the API host: the redirect policy strips it the moment a redirect changes host, which Go does not do on its own for another port or a subdomain. Windows has no atomic rename over a running image, so it goes through ReplaceFileW and puts the original back when Windows reports the one failure that moves it. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * tui3: a launch says a newer codeaf is out, and /update installs it The launch line is one dim transcript note carrying both versions, /update, and the curl line, because the command is what the owner asked to be given. It arrives as a command rather than an opening read, so the first frame is drawn before the network is asked anything and a slow or refused answer draws nothing. /update — /upgrade is the same road — names the tag, then the checksum and where it landed, then quits with a restart plan for the door to carry out. Already newest says so and stays. It refuses while a turn or a task is running, and once it has started, one shared guard keeps a second /update and a new turn from beginning underneath it: the restart must never quit on top of live work, and the home page's Enter roads go through the same guard chat's does. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * codeaf update, and the door that restarts the same conversation codeaf update --check prints one line and exits 0 when this release is newest, 3 when a newer one exists, 1 when it could not ask. Without --check it installs stable, or whatever --rc, --dev, --staging or --version names, choosing a channel build by its own publish time the way the installer does. A binary built from source refuses in place and names its path rather than replacing something a developer is holding. The restart happens here and not inside Bubble Tea: the surface fills a slot, the door reads it once the frame is down and the terminal is back, and then execs the new binary with this launch's own arguments plus --session pointing at this transcript, so the conversation reopens where it was. A linked-local engine is asked to retire only after the surface has detached, and it still refuses while another window or any live work is there. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * remote: a window that says it is leaving needs no torn-link grace The twenty-second grace exists for a terminal that was killed without saying so, and a deliberate detach is the opposite of that — the window has just announced its departure. Carrying the grace anyway left a cleanly closed window looking attached for two more call deadlines, which kept a freshly installed codeaf waiting behind the engine the old one had already left. It clears only once the last view is gone. Turns, tasks, unanswered questions, other views and live work all still refuse retirement, and a refusal still never stops the work it protects. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * The installer and the updater agree on what a channel tag looks like Two roads now install codeaf, and they were reading the same release list by different rules: the shell accepted any dev-* or staging-* tag and semver parts with leading zeroes, while internal/update accepts only the shapes the release workflow actually publishes. A malformed tag newer than a real one would send the two roads to different builds. One helper carries the grammar to both channel selection and the line that names the channel, and an unrecognised tag for the asked-for channel now says so instead of installing whatever came back. The publish-time ordering #1056 added is untouched: only which candidates are eligible changed. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * The manual stops saying that nothing self-updates That sentence was true until this change and is now the opposite of true, so it goes, and a section written in the words people bring takes its place: is there a newer version, how do I update codeaf, why does it say this every time I start. It states who gets the launch line and who never does, the opt-out variable, the day the cached answer lasts, the shell exit codes, and both refusals — a build made from source and an executable nobody may write. The command table names /update and /upgrade with the argument they take. Four probes reach the new section, and the remote page keeps its own asker's words in its heading so the new section cannot take that question from it. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * docs/changes: the update check and /update, for #1060 Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: --check answers for the channel it was asked about, and a newer build is never downgraded Two things a hand verification caught. --check parsed whatever tag it had selected with the stable grammar alone, so --dev, --staging and --rc could never be "newer" and every one of them answered "you are on the newest codeaf" and exited 0 while a newer build sat in the list. The check now keeps its channel all the way through the decision and always names the tag it is talking about: stable and rc compare as semver, a dev or staging tag is the same tag or a different one, and --version is equal or it is not. And a build AHEAD of the newest stable was quietly installed backwards — the check said newest, the install downgraded it. Both roads refuse now, naming both versions and the deliberate way past: this codeaf is v0.3.0, ahead of the newest stable v0.2.0 — pass --version v0.2.0 to install it anyway An explicit tag IS that deliberate road, so --version and /update <tag> still install exactly what was named. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: a failure names the asset and the status, and ends with the curl line A download that failed printed the whole fallback URL, which put the retired product name on somebody's screen. The line names the asset it asked for and the status it got back, and nothing about which repository answered — the compatibility attempts still happen, they are just not the person's business. Every terminal failure now ends with the curl line, which D9 asked for and the download path had been missing. The / list declared no argument for /update while the command page documented one, so the list gains the second row the way /cache has one. The manual section now says what the code does about a build ahead of the newest stable, where it had said such a build "gets nothing" — true of the launch line, and false of the install until this wave. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * update: the curl line is agentfield.ai/get/codeaf, the supported road The launch line, the refusals and the terminal door offered the raw GitHub address of scripts/install.sh. The repository is public and agentfield.ai/get/codeaf has served that script from main since 2026-09-16, so the constant now spells the supported line, the manual's update section names it in place of "the curl line below", and the change entry says so. Asked for on the pull request by Santosh. Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Opus 5 (1M context) <[email protected]> * a run's own scratch note no longer fails the delivery gate (#1096) * codeaf: a run's own note is offered outside the tree it is judged in leafOutputHint invited a job root to write its overflow document at a workspace-relative name (exec.SuggestPathFor's task-2-x1-toolchain-checks.md shape), so a run working in a person's directory left that note untracked at the root of their working tree. The delivery gate holds the run's record — the artifact registry, joined by jobArtifacts, plus the delivered tree — against the request, read the note as part of what was handed over, and refused the delivery; the run then ended incomplete over complete work. There is no separate mechanical clean-tree check to exempt, so the fix is the writer's: where the workspace belongs to the person, the root's file invitation points at the run's scratch beside its recorders and job logs, and the message keeps carrying the deliverable. * codeaf: a person's own file still joins the record the gate reads TestARootLeafInAPersonsDirectoryPutsItsNotesInScratch baselined the tree after the note was already written, so its landing read could never have observed anything: the record stayed empty because of the ordering, not because of the address. The baseline now happens first, in the executor's own order, and a hint that ever points into the tree again fails on the observation, not only on the spelling. And the other half of the law is pinned down: a file that turns up in the person's tree while the leaf runs — even at a name wearing the run's own task-<n>-<slug>.md shape — is still observed and still lands in the record the delivery gate is held to. Only the run's own note moved; nothing else about what the tree says was loosened. Co-Authored-By: codeaf <[email protected]> * docs: the change note for the root's overflow file moving out of the tree --------- Co-authored-by: codeaf <[email protected]> * chat: the wordmark is drawn CodeAF, four rows, capitals on the d's cap line (#1106) `┌─┐ / ├─┤ / └─┘` is not an `a`, it is two closed bowls of equal size stacked on each other, and two equal closed bowls are an `8`. That was the second-to-last glyph of the product's own name on the first screen of every fresh install. The owner's answer is to have the two letters at the end stand up out of the word as capitals: `A` is an apex, a crossbar and two legs; `F` is the one letter whose case is told by its arms alone, so its top arm is the longer one. A CAPITAL'S TOP IS THE ASCENDER'S TOP, and three rows of box-drawing could not give it that. A bar is drawn at the middle of its cell and a stem reaches the cell's edge, so the `d` stood half a row over any capital however it was formed — and a stem in the bottom row hung half a row under the bowls' baseline the same way. The fourth row is that half a row made whole: the x-height letters live in the lower three and leave the top row blank; the capitals and the `d`'s ascender start in the top row, the ascender as the half-stroke `╷` so its top is the capitals' bar; and every stem that ends on the baseline ends as `╵`, which stops where `└─┘` does. A `│` in the bottom row is a descender and nothing else. THE WORD IS SPELLED `codeaf` AND DRAWN `CodeAF`. `product` is unchanged, the glyph table is still keyed by its lowercase letters, and a terminal that cannot draw boxes still gets the word `codeaf`. The greeting's centring test read the unit's left edge off the wordmark's first row, which is now the cap line the x-height letters leave blank; it reads the second row. The one manual page that draws the wordmark had been drawing the retired `aforge` letterforms under the label "the codeaf wordmark" — the name law walks text and cannot see a name spelled in box-drawing — and draws the shipped forms now. Co-authored-by: Claude Fable 5.1 <[email protected]> * A real release download finishes: the 3-second check clock no longer covers the asset (#1110) * update: a release download gets its own clock, not the check's The release client was built as http.Client{Timeout: 3s} because three seconds is right for the launch check, and Go's Client.Timeout covers the WHOLE exchange including every body byte. The same client then downloaded the release asset, so a 57 MB download on a healthy link — about eight seconds here — was cut off three seconds in and a person read `read the release response: context deadline exceeded`. Client.Timeout is gone. The transport bounds only what has made no progress: a 5 s dial, a 5 s TLS handshake, a 10 s wait for response headers. Every call now takes its deadline from its context. The launch notice and `--check` share the one CheckTimeout constant, install metadata gets 10 s per request, and the asset gets a 30-second no-byte stall window plus a 15-minute ceiling — a dead link fails with a sentence, a slow one finishes. No Go deadline text reaches a person. A stall says `downloading codeaf-linux-amd64 stalled — no bytes for 30 s`, the ceiling says `took longer than 15 minutes`, and late headers say `did not start arriving within 10 s`. The stall window, the ceiling and the metadata window are injectable, so the tests prove the clocks with real slow, silent and trickling servers rather than waiting out the shipped ones. Co-Authored-By: Claude Fable 5.1 <[email protected]> * update: both doors read the one check constant `codeaf update` and the chat surface each spelled `3*time.Second` when they built the release client, which is how a figure meant for one small request came to govern an install. Both now pass update.CheckTimeout, and the three seconds live in exactly one place. The terminal's `--check` takes the client's Check, which is the whole-exchange check deadline; a plain `codeaf update` keeps Select, so the metadata requests an install makes get the ten seconds they need. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: the update download's own clock (#1110) Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Fable 5.1 <[email protected]> * docs/changes: v0.2.1 rolled up into CHANGELOG.md (#1113) The 33 entries that landed on dev since the v0.2.0 roll-up are the ## v0.2.1 section of CHANGELOG.md now; docs/changes/unreleased/ keeps only this pull request's own entry. Stable v0.2.1 takes its notes from that section. Co-Authored-By: Claude Fable 5.1 <[email protected]> * README: telemetry folds under Docs (#1115) The 23-line Telemetry section between Install and the product tour becomes one collapsed block at the end of Docs: a plain summary line, the notice verbatim, one sentence and the link to docs/TELEMETRY.md. The notice block is unchanged, so it still matches the binary, the installer and docs/TELEMETRY.md. Replaces #1112. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01NoeSVN8nqmwNVRXExgoUNN * santos/dev lands on dev: crew family, Pareto crewing, the Model Pool, custom connections (#1194) Lands the integration branch santos/dev (#1108, head 6db5125b2, 152 commits over dev) on dev as one squash, plus two fixes that kept its touched-packages job red: the pool start-up test empties the CI word it is not about (649bb4bfd), and a zombie leader no longer keeps a job's process group alive on Linux, which had delayed every finished background job by the two-second termination grace since #1155 (053e6a90f). #1108 carries the full account; the 94 change entries under docs/changes/unreleased/ are the per-change record. The commits folded in, oldest first: 5c413ef7b Keep archived evidence readable while tasks finish 3705a0cfe Document recovery evidence retrieval change 7834cc7d0 tui3, manual: a question's own answer box answers to the whole caret vocabulary 99d66e98c task: Add open/all source toggle to crew config c1e4cb57f docs: the change entry for the crew model family toggle e63b43927 crew: round-1 review fixes 304a5a223 crew: round-2 review fix 65477c0c5 task: Add open/all selector to /crew TUI picker 9189ef764 Merge branch 'task/add-open-all-selector-to-crew-tu-9e8cc0' into feat/crew-open-all-toggle ba9c12d61 crew: reconcile the picker onto config's source helpers ea05bc0f5 crew: a chosen family counts as an answered crew e1a037e0a Merge remote-tracking branch 'origin/dev' into feat/crew-open-all-toggle f6c2924a7 crew: the open table takes the picks we landed on eff44b542 docs: the change entry carries the open-table move and the family gate 71ce9bec0 crew: the family reaches the fallback, the settings skin, and the row's line b8ca5f513 crew: round-1 review fixes on the new table 8db5708ef crew: round-2 review fixes ce9759ef4 crew: round-3 review fixes 956e1f514 crew: round-4 review fixes 37ad27f4e crew: fixes from the first Opus round 6ffb95813 crew: the fixes from the Opus confirmation round 72b027436 crew: the comments the atomic write left behind 70d0e205b crew: round-four fixes from the Opus review 234300e37 Merge branch 'dev' into feat/crew-open-all-toggle 894292f28 crew: round-five fixes from the Opus review fba77f92d An answered landing carries its answer's fate when it is raised again, and an in-flight settle holds the question down (#1077) e69445058 Changelog entry for the landing-answer fate (#1086) 64ab7eae0 crew: round-six fixes from the Opus review 070016419 crew: round-seven fixes from the Opus review af09aa852 The review round's four findings: claim ownership, decider changes, recordable answers, plain words c24af0248 crew: round-eight fixes from the Opus review 289d372b9 The tests the landing's answer carries: fate, hold-down, redraw, claim doors 7e38346a9 The take-down comes before every mint, and the tui3 walk reads the newest card dbe1be03e The review rounds' last word: the release owns its claim, and the docs say what the code does 4cc4945a7 The Opus review: a release owns its generation, and every failed round still says its word a5cce0dde The done card says who is deciding, so the clause is a duplication, not the fact 6bd276e6c Merge fix/1077-landing-reraise into santos/dev 271016938 Merge feat/crew-open-all-toggle into santos/dev b459399d3 Merge fix/question-answer-box-caret-keys into santos/dev a38492026 Merge codex/recovery-evidence into santos/dev dc22e6461 cmd/codeaf: the role-door tests read the shipped mastermind instead of a copy of it 39f528f70 release: the pin-example check reads the guide the examples moved to 6439d3cb8 session, tui3: gofmt the three files santos/dev merged unformatted 854037967 tui3: a question's answer box kills to the end of the line on ctrl+k d93ec61ea session: the late-attach proposal test holds the work until the replay is read a81ead4de session: the landing-fate tests read today's clock instead of a fixed date 8fe4fff38 provider: the retry-avoid list rides the call's context into the knobs a266da3e2 provider: the retry-avoid veto narrows the wire through one field law 197d89334 exec: a node retry asks the router to avoid the lanes that failed it 2d02cc0b9 provider: the set-servable law's doc comment keeps its paragraph break e1d0e7524 exec: the retry test names its helper and spells its wall in time units e7d65a517 docs: the change note for the retry that avoids the failed upstream 11decbd92 Merge fix/provider-failover into santos/dev c099a51c2 Merge dev into santos/dev e47d92bde provider, exec: the lane a failure names is a wire fact provider reads (#1105) 396be97be Merge dev into santos/dev 3b97f50c3 Merge dev into santos/dev ea3b5c9a0 telemetry: the send path drops events that cannot name their version 573b288e4 cmd/codeaf: keep smoke runs from sending usage counts e8d627c6b docs: the change note for the usage-count audit fixes 11bec8d80 chat: a hosted session's usage counts are read off the events it receives b9d75b39e chat: the hosted-count tee is tested against the tally it must produce 39fa08d3d telemetry: stamp a version in the lifecycle tests 681e2adf1 chat: count the hosted boot agent once, and conversations opened beside 825ef2c41 connect, settings: named, multiple, switchable custom connections (#1092) f4d3f9a1a Merge branch 'dev' into fix/telemetry-audit 76fec37e3 ci: the Model Pool mirror workflow and the signature check it runs 2fb296bf1 crew: Pareto crewing, the learned picker and the Model Pool (#1093) a065ebb1b Merge remote-tracking branch 'origin/dev' into santos/dev 80ac250a0 Merge remote-tracking branch 'origin/feat/pool-mirror-workflow' into santos/dev 6092393ab Merge remote-tracking branch 'origin/fix/telemetry-audit' into santos/dev ef808dda4 session: a decision card under --yolo takes its default (#1117) 70a9e1f6e session: a task's thread stops its parts before it closes, and a machinery cut says so (#1118) c4df8f364 crew: an auto seat's rung names the pick that ran, once, for both doors (#1119) a9fcf9bc8 crew: a bare auto row under picked from = catalog reads the catalog's figures alone (#1120) 13be0dce4 relay: accept the exec and run doors, not only task and do (#1121) 90f29aec1 changes: name the #1120 note by its PR and stamp the pr field 0b77926ca do: seat from the settings the run will use (#1122) 32148d87d changes: stamp pr: 1120 on the #1120 note 113ed628e pool: a launch's start-up errands stop when the process closes (#1124) 6c98ddca3 verify, codeaf: a launch compiles fewer patterns and a surface holds fewer Ps (#1125) 50fe6a3e4 pool: the min_installs floor counts installs on the client (#1127) 076cde8ae relay: fold a resubmitted row once by its nonce (#1128) 8339ae71b relay: carry the acceptable metric beside role_quality (#1129) 0367c441b outbox: a refused row leaves the outbox and the rest of its batch is sent (#1130) 09aecd2f1 relay: the index holds only the cells that meet the min_installs floor (#1131) ae7c05dcd outbox: a Send compacts the file to the rows it still holds (#1133) 9e24f72d8 pool: the errand tracker's critical sections unlock from a defer (#1134) 358a569aa relay: fold a submit batch with one read and one write per distinct cell and day (#1136) a1d2eb1f7 pool: judge landed-but-unjudged runs on the next chat start (#1132) 3c6722859 verify: take the finished-tree check at a project cloned into a subdirectory (#1135) 263625b63 exec: a headless run leaves a pending judge record at its tail (#1137) 8be373d39 run: the headless task door leaves a pending judge record (#1138) a336b246e do: leave a pending landing for the Model Pool's judge (#1139) 8820a5659 cmd/codeaf: one readPendingRows test helper, not two (#1140) 49760fb45 pool: show and verify print both metrics the index carries (#1141) 6eaeb25c9 pool: status shows the pending judge records and the last sweep (#1142) 7c00b5f2c codeaf: a test's crash fixture stays in a profile the test owns (#1145) fc2eb357a manual: the status row's ledger is the tree's, not the conversation's (#1143) a06b53a91 test: pin the delivery gate over a grown job root (#1147) 7231347eb resident: the wall refuses a first replan round it cannot hold (#1144) 5e257557f cmd/codeaf: the package's test floor clears a profile the environment named 4dd1c562f Revert "cmd/codeaf: the package's test floor clears a profile the environment named" 9075f863c pool: the seed index is regenerated from the relay by seedgen (#1148) a5c44f3d2 pool: show --cells lists the held index's cells with installs and dims (#1149) 825996739 cmd/codeaf: the package test floor clears a profile the environment named (#1150) 81568aa82 session: report gitignored build products a run leaves, without gating on them (#1152) da4910593 internal/tui3: the opening hint test owns its profile and pins both doors 72de3bca7 docs/changes: #1151 — the opening hint test owns its profile 499083d1c Revert "docs/changes: #1151 — the opening hint test owns its profile" beb31ecf7 Revert "internal/tui3: the opening hint test owns its profile and pins both doors" 25b8b7dea version: an unstamped binary names the condition, not a Makefile target (#1151) c833b423a pool: a dropped row keeps the relay's reason and status shows it (#1154) cbb7d6d9e processgroup: verify a job group's identity before signalling it (#1155) efe498ed0 internal/tui3: the opening hint test owns its profile and pins both doors (#1153) 17b3b0e40 do: a continuation continues the plan it has instead of re-planning (#1156) 22cf3ce17 changes: correct the #1155 and #1156 notes' pr fields (#1157) 8e85b0fe7 cmd/codeaf: a surface runs under a soft memory limit drawn from the machine (#1158) ef1e92756 task: status row accounting (#1159) 44f572e26 pool: status's index line says what this run's fetch did to the cache (#1161) 3600fc9a5 pool: the pool's copy of a seat's id is the bare vendor/id (#1163) a2ebd1397 cmd/codeaf: no test can push pool rows to the default relay (#1164) 4fb7f0546 docs: the road a chat door bash push took under --yolo (#1160) 69219a1e1 cmd/codeaf: the soft memory limit is the smallest real bound, not the host s memory (#1162) 2b155b4b4 relay: bound judge severity, clamp adjusted scores, publish mean and sd on one scale (#1165) 2fb55324a relay: purge fixture vendors and refuse them at the door (#1167) 57504187a exec: a job's shell cannot reach the tmux server hosting codeaf (#1166) eb7b46559 internal/session: an unattended run may not move a branch it did not create (#1168) 80f770639 pool: status says the identity, the judged total and that nothing has landed (#1169) 1cb7333fa exec: an exec run's worker spend reaches the usage ledger (#1170) 96bb9402e changes: the #1169 note's title parses and reads as one line (#1172) 627236d95 config: the task door honors a stored crew word, and a pin survives the learned pick (#1171) 593092be6 internal/tui3: a test may not read a profile it did not create (#1173) ad8be1ed7 session: a landing card under --yolo takes its default instead of parking the run (#1174) 03d49a678 gitignore: the .furrow ids the engine writes into a workspace (#1175) 46edbdd32 internal/tui3: gofmt the profile-law helpers from #1173 (#1176) 3179e3611 relay: ALLOWED_VENDORS names the catalog's vendors and codeaf (#1177) 397a145a6 docs: why a settled /task leaves commits on task/<slug>, not the worktree branch (#1178) 915ff3a76 pool: regenerate the seed index from the relay's first clean publish (#1180) d9b42430f cmd/codeaf: the model warm stops when the process closes (#1179) bd2dcf334 session: the auditor's calls say they are the auditor's and which node they check (#1181) c6ed47c5a task: json unverified branch (#1182) 360352ad4 session: a settle turn runs under the checker's own bound, not the run's wall (#1183) 814e1af0e session: the tasks text and record line name a failed/unverified node's kept branch and verdict (#1184) 7c25a26d9 internal/session: the identical-effects test stops riding the turn guard (#1185) 6e7d0ed44 cmd/codeaf: exec and run envelopes name a non-verified node's kept branch and verdict (#1186) f4079d716 session: bound a worker's park on a promoted command (#1187) 994689fe7 docs: the model pool runbook names the Workers Paid plan and what it costs (#1188) c315be13e docs: the runbook's plan line says metered, not capped, and nothing more (#1189) 2e80c60d8 internal/tui3: the status word says working while a task subtree turns (#1191) 99363ca8f session: bound a job subtree's CPU and process count (#1190) 6db5125b2 docs: what the audit check seat can execute, and where that boundary is set (#1192) 649bb4bfd cmd/codeaf: the pool start-up test empties the CI word it is not about 053e6a90f processgroup: a zombie leader does not keep a job's group alive b4e66f515 docs/changes: the entry for #1194 Co-Authored-By: Santosh kumar <[email protected]> Co-Authored-By: Claude Fable 5.1 <[email protected]> * ci: Model Pool mirror workflow (signed index onto the model-pool branch) (#1116) * ci: the Model Pool mirror workflow and the signature check it runs Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs: the change entry for the Model Pool mirror workflow --------- Co-authored-by: Claude Fable 5.1 <[email protected]> Co-authored-by: santoshkumarradha <[email protected]> * chat: the unconnected custom row on /connect keeps its vendored name (#1195) modelConnectionStatus renamed every custom-id row to the connection's Written word, and the catalog template's Written is the bare id, so a person who had connected nothing yet saw `custom` where the manual, the README and #1107 say `Custom OpenAI-compatible API`. Only a connected instance is called what the person called it now; the contract test draws both rows and is red without the fix. Found verifying the staging build of #1194. Co-Authored-By: Claude Fable 5.1 <[email protected]> * docs/changes: v0.3.0 rolled up into CHANGELOG.md (#1196) 95 loose entries — the crew family and Pareto crewing, the Model Pool and its relay, custom connections, the telemetry audit, the update door's download clock, and the fixes that followed — become the `## v0.3.0` section the stable release reads its notes from. Co-Authored-By: Claude Fable 5.1 <[email protected]> * ci: a…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands the integration branch
santos/dev(#1108, head 6db5125, 152 commits overdev) ondevas one squash, at Abir's request, plus the two fixes that kept #1108'stouched packagesjob red on every run today.What lands
Everything #1108 lists: the crew model family (open or all), Pareto crewing from the live catalog, the
table|catalog|learnpick modes, the judged-run learner, the Model Pool client and relay with the mirror workflow, named/multiple/switchable custom connections, and the follow-up fixes #1117 through #1192. #1108 carries the full account and the independent end-to-end report; the 93 change entries underdocs/changes/unreleased/are the per-change record.Two fixes on top of 6db5125
poolcfg.Resolvereads a setCIas read-only when nothing else answers; GitHub Actions exportsCI=true, soTestWirePoolIndexStartsTheRefreshAndThePushcounted one errand instead of two on every runner while passing on every laptop. Test-only.reapShellsweeps before it reaps, so the shell is a zombie whileGroup.Aliveprobes the group withkill(-pgid, 0), which a zombie answers. Every finished background job read as still alive, was sent SIGTERM, and waited out the full 2 s termination grace before being reported done (TestTurnBoundaryReportsRunningAndOneTerminalTransitiontook 4.17 s on the branch against 2.15 s ondev; the runner tipped it over its 4 s bound).Alivenow reads the group's members from /proc and ignores zombies; off Linux nothing changes. Three contract tests, one proven red without the fix.Tested
cmd/codeaf,internal/execandinternal/processgroupin full with-count=1, and the light gate throughmake test-quick.make buildat the head) against a throwaway profile:version,pool status,pool show,pool verify(live relay, signature good); the chat on both roads (in-process and engine host) answering a tool-backed question,/crewdrawing the new chooser with its open/all switch,/statusnaming the build, clean exit;codeaf do … --jsonendingstop: donewith the file written.Notes for Santosh
santos/devis untouched. After this merges, reset it ontodev: its 152 commits are ondevas one squash. WIP: the worker harness — one loop under /task and do #1109, pareto crewing v2: one dial in dollars, a grade no model can move, and the paper behind it #1123 and docs: a footprint benchmark, its method, and one day's numbers #1126 stay based on it.devonce this lands.dosmoke: the--jsonchecklist row for "a file named hi.txt exists" readsnot reached · the run changed hi.txtalthough the gate passed and the file holdshello. Wording, not a blocker.🤖 Generated with Claude Code