Skip to content

feat: Add Homebrew support and automated tests - #1844

Open
pav2345 wants to merge 3198 commits into
Agent-Field:devfrom
pav2345:feat/homebrew-install
Open

pav2345 wants to merge 3198 commits into
Agent-Field:devfrom
pav2345:feat/homebrew-install

Conversation

@pav2345

@pav2345 pav2345 commented Oct 17, 2026 •

Copy link
Copy Markdown

Summary

  • Add a Homebrew Formula for installing CodeAF on macOS and Linux.
  • Add a Homebrew Cask definition for macOS.
  • Add automated validation scripts for Formula and Cask definitions.
  • Add tests for Homebrew packaging and release validation.
  • Add Homebrew CI checks.
  • Update README installation instructions and documentation.

Supported Platforms

  • macOS: Intel and Apple Silicon
  • Linux: AMD64 and ARM64 (Formula only)

Testing

  • Homebrew Formula installation tested on Linux via WSL
  • Homebrew Formula test executed
  • Automated Python unit tests passed
  • Strict Homebrew Formula audit passes
  • Cask installation verified on macOS

Notes

  • Linux testing was performed using WSL.
  • macOS Cask installation still requires validation on macOS.
  • A public Homebrew tap is not included in this PR.

Motivation

Provide a convenient Homebrew installation option for CodeAF and establish automated validation to improve packaging reliability.

santoshkumarradha and others added 30 commits September 12, 2026 09:27
…way net (Agent-Field#923)

* The turn decides, informed; promotion inherits; the ceiling is a runaway net

A one-line read-only design question read home-panel files for ten rounds in
48 seconds and was taken away from the conversation that was answering it. The
round-10 ceiling fired, a summary reader said `split`, and a cold worker opened
on a brief plus stub pointers: its first six calls re-read the chat's own
results, then fourteen more files, 551k input tokens at 24% cached over 393
seconds, and it never ticked an item. Three things were wrong at once. The
decision was made by the wrong party — a reader of a summary rather than the
model holding the context. It was made on the wrong signal — a count of rounds,
which measures nothing about whether the work is going anywhere. And the move
threw away everything the turn had read.

So the count no longer moves anything, and what it buys instead is a sentence.

THE TURN DECIDES, INFORMED. The rungs below the last one call nobody and move
nothing. They put one note into the running turn at its next step, through the
one ambient door a loop nudge already uses: the facts it cannot see about itself
(rounds so far, distinct files read, bytes of results held) and the three roads
on, in plain words — answer now; carry the rest on in a room with `quick_task`
and `inherit`; hand out what it has not read. The cost is stated once, where the
choice is: a worker that does not inherit opens on a brief about your work
instead of on your work, and reads it all again. No timer, no model call, no
message[0].

PROMOTION INHERITS. `quick_task` gains `inherit`: the worker is a read-only fork
of the caller's transcript, so its first request carries the caller's messages
and tool results verbatim and it reads nothing twice. It is bounded by a
property rather than a number — refused, in a result the model reads, when the
caller's context does not fit the worker model's own working room
(`compactThresholdOf`, the build's own line for how far a conversation may
grow), and the refusal names the road still open.

THE RUNAWAY NET. `readMark` survives as the last rung only, and it no longer
asks about rounds: it fires when the turn's context has passed the room it can
still work in, when the loop detector has already nudged twice, or when the wall
has run out. Each of those is a fact the build already had. A turn reading ten
files in 48 seconds reaches none of them.

AND THE CARRY-ON ROAD PROMOTES. When the net does fire, the carry-on goes
through the same admission door as the tool, with the drawn items as its
checklist and the context inherited. The brief road survives only where
inheritance cannot go — the rung that fired because the context is too big to
carry — and there the brief now carries the compiled results themselves rather
than pointers at calls the worker would have to run again.

Deleted with it: `checkpointSplitNote` and the `split` decision, the seam mark,
`quickFromDrawing`'s bare-spec path, and the stub-pointer section for an
inherited worker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The late-drawing fixture buys its reading at the net, not at the first mark

Agent-Field#962's fixture holds a drawing past the turn's last step, and it opened that
window by crossing the FIRST mark. With the rungs below the last one buying
nothing, the first mark asks nobody, so the fixture now takes the turn to THE
RUNAWAY NET instead: every scripted round reports a fuller window
([checkpointFillAt]) until the context bound fires, and the note rungs' own
rows are dropped from what it returns, because a row that cost no call is not a
row about a drawing landing late.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* manual: the page about a brief that could not be written carries the asker's words in its heading

The probe "the run said the brief could not be written, what happened" reached
adaptive-runs in the last of four slots on dev and lost that slot once this
change's task pages said more about briefs. Retrieval is the feature, so the
heading now says it the way a person asks it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…Field#935)

* The person's word wins at the request, never at the next turn

A model a person names while work is running used to land at the next TURN.
For a conversation that is seconds; for a task step it is up to twenty
minutes. On 2026-09-11 a step sat thirteen minutes into a wait, the owner
picked another model and typed continue, the room answered `its next turn
takes it`, and the step was still talking to the model they had moved off a
minute later.

The pick now reaches the work at the next REQUEST, within lane.SpokenWithin,
through one door:

- activeGeneration carries the turn's own partial buffer, so
  `productive()` — has this request produced anything a person could use —
  is a state read of the text already on their screen rather than a second
  count of the same deltas.
- Agent.hearModelLocked is the one place a person's model reaches running
  work: it records the word for the boundary and, when the request has
  produced nothing, cuts it through cutGenerationLocked with a third cause,
  errPersonCut. It answers ModelLandsNow or ModelLandsNextRequest.
- runTurn re-reads the word at ONE site, the top of the request ladder, and
  starts the chain again from the model they named. freshModel is now the
  one place a model gets a whole budget of its own; the rescue hop calls it
  too.
- The room says which of the two happened — `switching now` or `the next
  request takes it` — and never the word "turn". RetargetTask carries the
  landing, over the wire as well.

generation_law_test.go fails the build on a second place that cancels a
generation. The manual's five claims about "the task's next turn" are
replaced by what is true, and models-and-cost.md gains the section somebody
asks for when they press a model mid-reply.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSshDkszBQ9RpR4dPsC5fQ

* docs: the change entry for Agent-Field#935

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSshDkszBQ9RpR4dPsC5fQ

* The beat law follows the door to the function it hands to

SetModel is one line now — the work and the answer to when the pick lands
live in setModel beside it — and the law that says a picked model reaches
the lane beat was reading the door's own body. It reads the road the pick
takes, and still cannot be satisfied by a door that does nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSshDkszBQ9RpR4dPsC5fQ

* The model-latch fixture pins the arm that is still true

TestSetModelAppliesFromTheNextTurn asserted the contract this change
replaces — a swap landing at the next TURN — so it was the defect written
down as a test. What is still true from that seat is the case with nothing
in flight to let go of: a word said while a tool is running is cut from
nothing and rides the very next request. personsword_test.go holds the
other two arms.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSshDkszBQ9RpR4dPsC5fQ

* The person's word is the head of every chain

A failing step walked its own fallback ladder past a model the person had
just named, and a step whose completer offers no chain ended the turn on
"there is nowhere else to try" with their choice unasked.

The move takes the word where the move is really made, so the line a person
reads names the model their reply actually went to rather than a rung the
boundary was about to overrule. takeTheModel is the one place a step changes
model, and `root` — whose chain this is, a fact about the move and not about
who made it — is the only thing that differs between the two roads. The
reading of whether a step can move at all PEEKS at the word, because it runs
on every failure including the ones that ask the same model again.

Three stale comments go with it: SetReasoning claiming a level latches
"exactly as the model does" when the two rules are now deliberately
different, auxiliary.go's errand floor, and completeWithRetry's "2s, 4s, 8s,
max 3 retries" — budgets deleted before the recovery wave.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSshDkszBQ9RpR4dPsC5fQ

* Agent-Field#925's two conversation sentences are false now, so they go

The rebase carried Agent-Field#925's words onto a tree where the cut makes them
untrue. `a pick in a conversation you are sitting in front of still
lands on your next message` and `a model you pick with /model does not
redirect a rescue that is already happening` were both true on the day
they were written and neither survives the request boundary: a pick
lands on the next REQUEST, and a person's word is the head of the chain.

The room's own sentence loses its rescue clause with them. It was worth
a clause while the pick had to wait for a move somebody else made; now
the next request carries the pick whether that request is a rescue, a
retry or the step's own next step, so naming the rescue would name one
road out of three.

Agent-Field#925's `TaskNode.picked` is untouched. It owns where a node's NEXT
WORKER is built and this owns the request already out — two scopes, no
second mechanism.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* One reading of what the person has read, and one door for every word

Four things the review found, and all four are the same shape: a rule
stated in one place and answered in two.

ONE READING. `has anything reached the person` had two answers five
lines apart in the same stream observer. The recall's gate counted a
reasoning delta; the person's word did not, and said so on the page —
`thinking where nobody can read`, which the code contradicts, because
visible thinking is drawn and Agent-Field#760 made a reasoning-only turn something
somebody watches. `reachedThePerson` is the one reading now: a word of
the answer, a word of visible thinking, or a tool row formed far enough
to be spoken. `recallAside.token` is deleted and the aside reads the
turn's own. A person's word can no longer let go of a request that has
drawn anything at all, which is also what closes the withdrawal gap: the
state where a cut takes a thought off somebody's screen is unreachable
rather than handled.

ONE DOOR FOR EVERY WORD. `continue` typed into a room was enqueued and
never heard — drained when the request it interrupted ended, which on
the owner's own step was thirteen minutes later. That is the other half
of the 14:40:10 evidence and the report claimed it was already done. A
direction goes through the same hearing door the pick does. The CAUSE is
the caller's, and it is the one thing that genuinely differs: a model
changes what the next REQUEST rides, so it is answered at the request
boundary; words change what the next STEP carries, so a direction takes
the conversation's own `errSteerCut` and returns to the boundary where
queued lines are drained.

THE ROOM IS TOLD. The cut sends `EventRetrying` with `Next` naming the
model they chose — the one door that already means `this attempt is
void` — so the wait a person spoke to end stops counting. And the
waiting line no longer says `trying again` when the news is a move: the
row it is composed from says `moving to <model>`, and somebody who
picked a model should not be told their choice was a failure being
recovered from. A rescue hop's status line agrees with its own row now
for the same reason.

THE LAW FOLLOWS THE VALUE. The one-door test matched three tokens, so a
local named `generation` — which `completeWithRetryReasoning` already
binds — walked past it. It enumerates every road that hands out a
generation and flags anything reached by one, however it is named.

And the door compared a pick against the SESSION's model rather than the
one a rescued step is riding, which cost a whole cut request when
somebody picked the fallback the step was already on. `Agent.riding`
publishes what the ladder always knew.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* The person's word is not a boundary cut, and the one door holds both

Agent-Field#962 landed mid-review with `boundaryCut`: a cut whose REASON rides no
transcript is owed rather than dropped, and `beginGeneration` spends it.
The rebase composes cleanly because both changes are about the same
door — Agent-Field#962's owed-or-dropped decision lives in `cutGenerationLocked`,
which this lane had already made the single cancel site, and
`beginGeneration` spends the owed cut THROUGH that door rather than
reaching for the handle it is holding.

Neither of this lane's causes is a boundary cut, and the test Agent-Field#962
states is what says so. `errPersonCut`'s reason is written to
`Agent.spokenModel` before the cut is raised and taken from there at the
request boundary; a direction is on the worker's queue before its cut.
Both ride something the next request reads either way, exactly as a
recall's block rides the transcript — so a word arriving between two
requests needs no cut at all, and owing one would cancel a request that
was about to be right.

And this cause has a second reason of its own: an owed cut reports
itself answered, and that answer is what the room says out loud. A
person would read `switching now` over a pick that is in fact riding the
next request.

The one-door law was re-run against Agent-Field#962's new spend site and catches
the shortcut Agent-Field#962's own doc warns about: `active.cancel(owed.cause)`
inside `beginGeneration` fails it by name and line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Three from the review: the shared reading is safe, withdrawn, and on a page

(a) `recallAside.reached` replaced an atomic.Bool with a plain pointer
written by the turn's goroutine and read by the sidecar's — a race
whether or not anybody ever observed it. It is an `atomic.Pointer` now,
and a nil load is simply a reading not handed over yet, which
`reachedThePerson.did` already answers as "nothing has reached them" —
the safe answer and the one that was true at that instant. Proved under
`-race` over the recall, memory and person's-word fixtures.

(b) `StreamReplaced` reset the four buffers and not the reading. What
the dead machine drew is withdrawn on the event it sends, so the person
has none of it in front of them — and a reading left standing would tell
every door that may re-ask the request that they still do.

(c) `let go of because you chose another model` is a string a person
meets when they read a long step back, so models-and-cost.md has a
section for it beside the other log-row questions: it is not a failure,
here is why it is written down anyway, and it carries no door, so
nothing reading the log to ask "did this turn end" counts their own act.
A new heading rather than prose added to an existing one — the held-out
score goes from `first 10/22` to `first 12/22` and holds top-4 at 18.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: a word said between two requests is not owed a cut

The next request already carries the model the person named, so cutGeneration
must answer false when nothing is running; otherwise the room says
"switching now" for a request that is not starting.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…(review needed) (Agent-Field#992)

* permissions: the frame names its way out wherever the pointer stands

Agent-Field#946's frame and Agent-Field#985's grading landed within a day of each other and
disagreed about one row. Agent-Field#946 was cut before the gate graded, so it drew
`safe answer` on `deny all` only while the pointer was ON `deny all` —
the same sentence then, because Agent-Field#933 opened every permission on the
refusal. Agent-Field#985 made an ordinary call open on `allow all`, and the mark
went missing from exactly the frames where the pointer stands on the act
and the way out most needs naming. A panel of ONE never lost it:
questionPanelOption marks any hands-only answer that loses nothing,
whatever the pointer is doing.

`deny all` is the answer that loses nothing by construction — the frame
forms only where every member has one, and the row sends each question
its own — so it now says so on every frame, which is one rule with the
panel's rather than a second one.

TestPermissionsFromOneStepAreOneFrame was the other half, and the red on
dev: it read a costly set as opening on `deny all`, which was Agent-Field#933's rule
and is no longer anyone's. It now asserts the pointer the grading puts
there and, separately, that the refusal is named though the pointer is
not on it.

The manual's grouped-frame section, the gallery caption and the e2e
needle's reason all still explained the deny-first pointer; each now says
what is true and what expired with the grading. internal/e2e's live
assertion is unchanged and was the corroboration: it waits for `safe
answer` on four ordinary reads, so it would have gone red on the next
live run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* permissions: one predicate decides the safe answer, on a panel and on a frame

From the architecture review of Agent-Field#992. The first fix marked `deny all`
unconditionally, which was a second spelling of a rule questionpanel.go
already owned — and a wrong one: a frame whose member carries its asker's
pick, or whose calls are plainly reversible, marked a row that its own
tabs leave bare, so `2 one by one` contradicted the frame it was pressed
from.

questionMarksSafe is now the one predicate, questionSetMarksSafe folds it
over a set exactly as questionGroupStart folds the pointer's rule, and
the word is read only through questionSafeAside.
TestTheSafeAnswerIsMarkedByOnePredicate fails any file that spells
questionSafeWord for itself, so a third drawing of a question cannot
invent a third rule about when a person is told where their way out is.

The frame test pinned the mark with a frame-wide substring, which passed
just as happily with the mark moved onto `allow all` — the one reading
that tells a person their keystroke loses nothing when it grants four
calls. It is a row assertion now, through questionRowSaying, and it
checks `allow all` does NOT wear it. The `wary` case asserts the opposite
of what it did, and says why.

Nothing checked a FRAME's pointer against the gate's own grade — the live
needle for that screen is the mark, which is now deliberately
pointer-independent. TestTheFrameOpensWhereTheEngineGradedTheCalls does,
with no model: the grave control is read from
internal/session/testdata/consentask-rm-rf.json and must refuse to join a
set, and the ordinary answers are session.AnswerOptions' own.

The manual said `enter` on an untouched permission denies, in two places,
which Agent-Field#953 made false. Both now split the MARK from the POINTER, and what
`safe answer` means gets its own heading in the asker's words rather than
a clause inside the grave case — it is drawn on every approval. The frame
section lost the paragraph that split its answer list and gained a
heading of its own, and the phone-width claim is scoped: under sixty
columns there is no frame and no mark at all.

The lifetime row's "THIS TEST FAILS THE DAY THE GRADING LANDS (Agent-Field#953)" is
rewritten wherever it appears: the grading landed, nothing went red, and
whether the row is owed is issue Agent-Field#995. DESIGN.md's pointer bullet carried
the pre-grading rule and now carries the stakes rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ield#1001)

The sentence told a person to run `aforge engine --stop` with no
--workspace. That flag resolves to the HOME directory, never to the
workspace being complained about, so copying the line exactly as written
from inside a checkout stopped the healthy home engine and left the
offending one running — and the line came back on the next launch,
forever.

Measured on the owner's laptop before the fix: a host on wire 14, started
2026-09-11 11:23 from a dirty build, still holding the aforge-v2 workspace
twenty-two hours and eight rebuilds later. Every launch in that checkout
read the refusal, and every remedy typed from it stopped the wrong host.
`aforge engine --stop --workspace <the checkout>` retired it at once.

The workspace comes from the host's OWN answer, remote.HostSelf.Workspace,
rather than from what this process thinks it opened: the sentence is about
the machine that will not let go, so the words a person types have to name
what IT is holding. A host too old to fill the field keeps the bare
command rather than being given an invented path.

sessionHeldElsewhereSentence in chatv3.go already spells the flag for this
exact reason, and its comment names this function as its voice. The voice
was the one place not doing it.

The version-mismatch branch is untouched. Refusing to splice a surface
onto a host whose frames it cannot read is right, and the gentler
busyEngineHostSentence road cannot honestly be offered across
incompatible wires.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…nd two orphan packages (Agent-Field#991)

* ignore macOS Finder litter

The tracked Finder metadata was accidental repository noise. Remove it and
teach the ignore file not to admit another copy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* retire private planning dumps

Move the lasting routing and harness research into the documentation tree,
remove superseded root planning artifacts, and keep surviving references
self-contained.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* prune superseded plan documents

The deleted plans describe retired branches, surfaces, or research snapshots.
Keep their live contracts in the current architecture and design records.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* keep only summarized design evidence

Raw experiment cells and the generated home-design export duplicated retained
reports and decisions. Remove the captures while preserving their conclusions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* make retained paths portable

Machine-specific paths leaked from benchmark defaults, captured reports, and
source comments. Express the same locations through home-relative or neutral
placeholders without changing harness behavior.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Remove orphan voice and gate packages

The voice and gate packages had no importers anywhere in the tree. Remove
them, their tests, and the structural-law entries their files had occupied;
git grep proved both package import paths unused before deletion.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Remove unreachable Go functions

Remove 68 functions reported unreachable from every main and test by
deadcode -test ./..., together with the imports and private data they stranded.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* the captured frames and the benchmark defaults name nobody

169 of the retained terminal captures under docs/design/polish/frames carried a
developer's home directory in visible text and inside OSC-8 hyperlink targets;
the substitution is length-preserving, so every column and every escape payload
is byte-identical. Four benchmark scripts defaulted to a private scratchpad that
existed on one machine and no other — they default under $HOME now. A private
host name, a provider's internal pod address and four person-specific comment
examples go the same way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* the transcription door no longer cites a package that is gone

internal/voice held v1's microphone recorder and its own transcription client,
and nothing imported it any more, so it went. Four comments and one design
document still described it in the present tense — MULTIMODAL.md said its
machinery "is real and waits on a surface wave", and transcribe.go cited
internal/voice/transcriber.go as the live source of the wire shape it mirrors.
The shape is unchanged and transcribe_test.go is now the only thing pinning it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* two documentation lines outlived what they pointed at

JOURNEY.md's catalog used to be an index of scripts as well as a catalog of
journeys; the harness that made it one drove the retired v1/v2 surfaces and went
with them, so the page now says that internal/e2e covers selected v3 scenarios
rather than one script per row. FIDELITY.md spoke of what "the .dc.html actually
sets" after that export was removed; it is the record of what the design set.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* the change entry for the second public cleanup

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* the last personal paths and the last voice reference, now that their pull requests landed

Thirteen pull requests merged into dev while this branch was being built, which
freed six files the collision rule had put out of reach. internal/provider/
attribution.go still said internal/voice reaches the transcription endpoint on
its own; internal/manual/chat/commands.md taught the slash rules with a real
person's home in the example, and so did internal/tui3/detach.go and
slashchip.go; docs/design/questions/DESIGN.md linked two ideation drafts that
never existed in this tree; and two conversation-runtime records and one
ab-routing ledger carried absolute paths from the machine that made them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* the change entry follows the files that came back into reach

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
… has closed (review needed) (Agent-Field#997)

* tests: the reading beside a turn stops writing into a conversation that has closed

`internal/session`'s load-dependent reds name a different test each full run
(Agent-Field#959). One of them is now held still: five thousand runs of
`TestAPickTakenByTheClockReadsAsProvisionalAndRunsWithNobodyParked` on the Spark
under load failed thirteen times, every one of them as `TempDir RemoveAll
cleanup: directory not empty` rather than on an assertion, and the file left
behind in the directory each time was `told.json` — the delta stamp.

The writer is `Agent.refreshElsewhere`, started from the turn loop as a bare
`go` of its own. Nothing joins it and `Agent.Close` does not wait for it, so it
stamps the conversation's folder after the session has closed — in a test, into
a `t.TempDir()` that is being removed, which reds whichever test happens to own
that directory.

Two changes, one cause. The reading goes through `readBeside`, the one door for
a reading beside the work (sidecar.go exists because four of these each had
their own idea of cancellation; this was a fifth with none). And the stamp is
written in the same locked step as the assignment, refused when the session has
closed — the ask lane's own law about a clock that ran out after the door shut,
said about a reading. That also makes the stamp true: it claims this session's
model was told, and a stamp that advanced on a block no model ever read is a day
of other windows' landings this conversation never mentions again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changelog: the reading beside a turn and the stamp it writes (Agent-Field#997)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* review: the stamp is owed behind the path, the reading obeys its context, and the law reads the tree

Three blocking findings from the architecture review of Agent-Field#997.

The stamp was written under `a.mu` — a stat, a marshal and a file write inside
the lock a steer, a cut, a named interruption and every drain take before a
request leaves. That is the shape Agent-Field#876 lifted out of the meta stamp, and the
≤50 ms law cannot see it because `paceAgent` never sets `Place.Dir`. It is owed
under the lock and performed behind it now, through a `stampWriter` of its own
that `Agent.SettleWrites` settles: the owe is registered while the session is
still open, which puts it ahead of `closed` and inside the settle `Close`
already performs. The closed check moves up to the reading's first acquisition,
so a closed session does not walk the index at all.

The `readBeside` ask discarded its context, which left the goroutine exactly the
lifetime the deleted bare `go` had while three comments and the change entry
claimed it was cancelled with the turn. The context is threaded and obeyed
before the walk and again before the assignment, and `tellsElsewhere` is asked
at the call site so a task node's turn enrols no no-op reading in the watch.

The test pinned the early return rather than the shape. `Owe` starts its
performer at once, so "the file is not there yet" is a race with that goroutine
and cannot be a fixture: where the call SITS is the property, and
taskdelta_law_test.go reads the tree for it, with both the shape it refuses and
the shape it asks for planted. The runtime half now holds the reading on a
channel through a real `Close` and settles before asserting the negative.

Also: `NoteTold` only moves forward, because two readings written behind the
path can land out of order; and the entry no longer claims the stamp is written
where what it claims becomes true — delivery is at the drain's
`landVolatileLocked` and `elsewhereTold` is never persisted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* tests: a fixture that owns an agent settles what its reading owed

Moving the stamp behind the path moved the fixtures' obligation with it. Three
hand-built agents in taskdelta_test.go call the reading directly and nothing
ever closes them, so the write they owed was left to land whenever — which the
proof run caught at once: 200 runs under `-race` failed as
`TestAProjectWithNoOtherWindowGetsNoBlock … TempDir RemoveAll cleanup: directory
not empty`, the same shape, one level down.

They settle in their own cleanup now, which is the contract `Agent.Close` meets
for a real session and which placemeta.go already states for every test that
reads one of those files back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* review: the reading stays on the law, lands after its turn, and owes the later stamp

Four findings from the confirm review.

The ask was hoisted into a variable, which took `refreshElsewhere` out of the
set the sidecar law watches — that set is read off the tree, and it reads only
the function literal written into `readBeside`'s second argument. It is written
out inline again, inside the condition rather than behind a nil.

The assignment refused a reading whose turn had ended, which is not what the
context is for: `elsewhereText` belongs to the conversation, not to the turn, and
loop.go says in so many words that a read landing late rides the next step.
Dropped, a disk slower than a short turn re-walks from the same unmoved `since`
forever and delivers nothing. The context still stops the reading STARTING the
walk; only `closed` stops the answer landing.

The stamp's coalescing is max-wins on the instant rather than last-closure-wins.
`Agent.Abandon` releases `a.mu` before it cancels, so an older reading can be
owed after a newer one, and a [stampWriter] replaces its patch — the older
closure would have been the survivor and would have written a stamp behind the
block the model was handed. The instant is kept beside the writer under the lock
and only moves forward; the patch writes what is kept.

And the law accepted `NoteTold` inside ANY `Owe`, including the meta stamp's —
which is off the path and also never happens, because the next meta patch
replaces it. It requires the told stamp's own writer now, with that negative
planted beside the other two.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* review: the closed check and the three writes it guards are one swap-shaped hold

Agent-Field#999 lands `Agent.writeIfOpen`, the one door for "write nothing into a
conversation that has closed", and this reading is one of its callers the moment
it is on dev. So the region is shaped for that swap now: the lock, the check, the
three lines it guards, the unlock, and nothing else between them — the prose
moved above the hold and the comment names Agent-Field#999 as what replaces it.

No behaviour changes: the check already sat immediately before the owe, with
comments between.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rs turn bad (Agent-Field#1003)

The lane row's default ran aforge's own chooser on every request, paying
aforge's measurement budget to second-guess a router that balances well
most of the time. Now auto sends the legacy preferences and lets the
router route; a per-model gate counts refusals and unusable answers, and
twice in a short while arms the chooser for a window. The belief ledger
learns from every named lane's answers either way, so a takeover is never
a cold guess. openrouter keeps its meaning as auto without the safety.
…fer (review needed) (Agent-Field#1012)

* provider, cmd/aforge: the last loose locks hold their mutex from a defer

The provider half of the lockdefer burn (Agent-Field#431): forty-seven sites across
thirteen internal/provider files, plus the seven cmd/aforge stragglers,
were the snapshot/test-and-set-then-release idiom with the Unlock on the
success path. Each became a small method that holds the lock from a defer
for its whole body and answers what the caller needs, with the slow or
re-entrant tail left outside. No logic, ordering, or under-lock field
access changed.

That was the last entry on the known-red ledger, so the ledger burns to
zero here: .github/known-red.txt and its ratchet test are deleted
together, as the ruling in docs/rules/ci.md prescribes. The shared
repositoryRoot helper moves to internal/ci/root.go so the remaining ci
tests keep their door.

TestEveryLockInTheGuardedTreeUnlocksFromADefer now passes on a clean
tree, and the full internal/provider, cmd/aforge, internal/guard and
internal/ci suites are green.

Co-Authored-By: Claude <noreply@anthropic.com>

* changelog: the lockdefer burn entry for Agent-Field#1012

* docs, ci: the words stop describing the known-red ratchet that is gone

The burn deleted .github/known-red.txt and its internal/ci ratchet, but
docs/rules/ci.md, the Makefile header, the ci.yml laws comment and a
clause in internal/remote/ratchet_test.go still narrated the ratchet as
live machinery — a constant that no longer compiles. Each now says the
ledger burned to zero in Agent-Field#1012, that an absent ledger skips nothing, and
that the one reading stays so a ledger cannot quietly return. Comment and
prose only; no behavior.

Co-Authored-By: Claude <noreply@anthropic.com>

* provider: the routefirst takeover drain holds its mutex from a defer

e61f57a (Agent-Field#1003) landed on dev after this branch was cut, with one
snapshot-then-release site in routefirst.go's tellTakeover. It became
takeParkedTakeovers, the same drain-in-one-locked-step shape as its
siblings, so the Emits run outside the lock and the rebased tree keeps
the guard scan at zero.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
…, billed arms, cache pin, shaped gate, stale-build sweep (Agent-Field#1015)

* checkpoint: the race's tightening never stands the first mark below half the price

A raced both-yes pulled the first [taking stock] down to the very
boundary the verdict landed on, and a measured planning turn was told
at round three over one issue and two listings — noise the model waved
past, which is what the round-twenty note then inherited. The first
rung now never stands below checkpointFirstRungFloor, derived as
checkpointPrice / checkpointRatio: triage may make the first look
sooner, it may not make it empty. An unraced turn's ladder is
byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* journal: billed arms leave their own call lines, and a turn that hopped seals one usage line per model

The measured planning chat's journal summed to $0.789 of a $1.412 bill:
hedge arms and cut retries were banked to the meter through reconciled()
but wrote no call line, so exactly the expensive requests left no
evidence. A billed receipt now writes one, marked arm: hedge or
arm: reconciled — evidence like every call line, moving no total.

And the turn's usage seal named the model latched at the seal, so the
mid-session hop attributed all 31 requests (2.4M input) to the second
model though the first served thirteen. addUsage now keeps the turn's
share per answering model — the same name the bank row carries — and
appendUsage seals one line per model when there were several, the
turn's one wall time riding the first by name so a replay never reads
it twice.

Co-Authored-By: Kimi K3 <noreply@moonshot.ai>

* changelog: 1007 title under the one-line bound

Co-Authored-By: Kimi K3 <noreply@moonshot.ai>

* turn fold: slices of a repeatedly read file point at the file, and the newest stays whole

The measured planning chat read checkpoint.go in ten overlapping slices
and carried all ten, verbatim, through twenty requests: the fold bounded
generic tool-result accumulation but had no notion that slices of one
file are re-readable where they came from. When the foldable region now
holds several reads of one path, each older slice becomes a pointer in
the stub family the prompt already teaches — the read recipe, offset and
limit, that brings the slice back — no copy is filed, and the newest
slice is kept whole. Selection counts a kept slice as buying nothing, so
a pass still runs only when it can buy the whole headroom.

The trigger, target, batch-completeness and frozen-horizon laws are
untouched; bash and every other tool's results keep the filed pointer
they always got. Freshness of a kept slice is the held-read seam's
question, not this pass's: a pointer says the range was read and how to
read it back, never that the file still says it.

Co-Authored-By: Kimi K3 <noreply@moonshot.ai>

* session: a read the conversation already holds answers with a pointer

Held-range ledger (heldreads.go): a `read` fully covered by bytes the
live transcript still carries, against an unchanged file stamp, is
answered with a one-line pointer instead of a fresh fetch; a changed
stamp retires the whole entry, a partial overlap fetches whole, and a
stubbed or folded result drops out of coverage by itself. The read
tool's description prices the round trip it costs, the system prompt
teaches the pointer sentence beside the stub pointer, and the chat
manual quotes the behavior.

Co-Authored-By: Claude <noreply>

* lanes: the cache pin joins the lane chooser's enforced set rather than being dropped by it

* provider: the OpenRouter-takeover gate counts a refusal only when the router's own pick was refused

A narrowed demand's refusal is evidence about our choice, not the router's
default: for the gate to count it arming the takeover from within the
takeover's own demands. shape reaches the door from the one classifier that
holds the knobs (refusalobject.go's Client.refusalObject), and the refusal
door asks it (routefirst.go's Client.noteRouterRefusal, velocity.go's two
refuseLane branches). The answer sides stay shape-agnostic: an answer that
arrived is a verdict about the machine that spoke it, whichever spine chose it.

Measured in a live chat (2026-09-12): takeover armed, every narrowed demand
404'd, each 404 re-armed the gate — four 'Retry 1/2: relaxed the endpoint
filter' lines at ~80k tokens a re-send before the first rung went bare.

* tui3, session: a launch sweep names every live session on an older build rev

The third binary-shadow incident in the repo's history ends with a visible warning instead of 'same old rev' on every screen. The sweep reads presence files (fail-open), freshness from presenceWindow (age, never pid — the file's own law), and the launch gate emits one advisory line through the same noteFacts door the landing keys use. Manual section added to starting-aforge.md.

* checkpoint: a probe pins the first-rung floor against race-at-any-round

* changelog: entries renumbered to the pull request they ship with

* fmt: sweep and law-visit edits through gofmt

* tests: pin the hop's split seal instead of the old one-line shape

TestEveryResponseWritesItsOwnLineWithItsOwnShape names two distinct
answering models in its fixture (the first response falls back to the
latch, the second names vendor/served-model), so the per-model seal
split correctly writes two usage lines there. The expectation now pins
the split — one line per answering model, figures per model, the wall
time on the first by name — exactly the hop case usagejournal_test.go
covers.

Co-Authored-By: Kimi K3 <noreply@moonshot.ai>

* provider: the gate's shape test is a demand, not the ladder's membership

The first cut classified shape through wirePreferences(...).membershipNarrowing()
— the ladder's first-rung question, which counts require_parameters as
membership. Every machine-unmodified ask read as narrowed, so an ordinary
paced refusal (one of the router's own picks queueing) stopped arming the
takeover: gate_e2e_test's TestTwoPacedAnswersHandTheRoadToTheChooser went red.
Shape is now read from the knobs as a DEMAND — hedgeLane, or a lane choice
carrying admitted names — with require_parameters and the ledger's own Ignore
vetoes left outside the gate's question.

* session: restore the offset/limit pin sentence and route held-reads through the one decode door

lawregistry_test.go pins 'page the rest with offset/limit' in the read
description; heldreads.go now decodes with decodeToolArguments like
every other tool; the round-trip sentence is trimmed to keep the lean
prefix under its budget.

Co-Authored-By: Claude <noreply>

* exec/bare: the edit merge sentence owns its whole rule, reclaiming the round-trip-priced read sentence's room

The lean prefix was 62 bytes over on CI. The 'large unchanged regions'
clause restates the merge sentence, so it goes; the held-range round
trip sentence stays whole.

Co-Authored-By: Claude <noreply>

* changelog: 1015-stale-build-sweep surface fixed to the enum it checks

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Kimi K3 <noreply@moonshot.ai>
…-Field#1004)

* e2e: a needle names a key, it never spells one

TestQuestionsE2E has been 14 of 18 red since Agent-Field#933 took the brackets off
every key on this surface. The suite went on waiting three minutes at a
time for `[1] delete it` while the line drew `1 delete it`, and the
untagged gate that exists so this cannot happen (Agent-Field#184) was green
throughout, because it was looking at the half of each needle that had
not moved:

  - nine needles in questions_e2e_test.go were inline literals, which
    the table's gate cannot see at all; and
  - eleven rows of the table spelled `[c] change` in `screen` while
    their `source` said only `change`, so the gate checked the word and
    never the key in front of it.

So a row that is offered on a key now NAMES the key, and one function
puts the two together:

  - tuiWord gains `key`, and [say] returns [tuiWord.spelling] — the word
    alone on a plain row, `keyedWord(key, word)` on a keyed one.
  - [keyedWord] is the one place this suite writes the gap between a key
    and its word. The nine pasted answers in the questions scenarios go
    through it, so `delete it` stays the scenario's word and the `1 ` in
    front of it stays the product's grammar.
  - nineteen rows are respelled onto it: the question grammar's own
    keys, the consent block's two, and the four landing answers, whose
    one-space column internal/tui3's own relanding and papercuts tests
    pin (`a resolve it   n drop it   s tell it`).

And [TestNoNeedleSpellsAKeyTheSurfaceStoppedSpelling] is the law that
keeps it: a go/ast walk of every screen-reading call in this package
refusing a needle-position literal that brackets a key or pastes a
numbered answer onto its word. It reads the table as data and the suite
as source, and it leaves every other literal alone — a sentence the
MODEL wrote is a coincidence, not a source of truth, which is what the
suite's own header has always said.

internal/manual/chat/questions.md said a key is drawn `[1] allow once`.
It is drawn `1 allow once`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changes: the e2e question needles entry (Agent-Field#1004)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* e2e: the chip is a tail, and a letter is the question's only once you aim

The first paid run after the respelling went from 4 of 18 to 7, and the
two families left in it are the same rot one layer down.

THE CHIP LEADS WITH THE QUESTION'S OWN HEAD. `questionChipTail` waited
for `question · alt+a`; the status line draws `? delete the build
directory? · alt+a` (question.go's questionSegment) and spells the word
`question` only where there is no head, or `3 questions` where there are
several. Its `source` was `alt+a`, so the untagged gate was green on it
exactly as it was on the brackets. The row is its own name now: the
tail, ` · alt+a`.

A LETTER IS THE QUESTION'S ONLY ONCE YOU HAVE AIMED AT IT. `o`, `c`,
`x`, `?` are each the first letter of a word people type into the box,
so the block does not take one from somebody who has not yet looked at
the question (questionkeys.go's questionAimKey; the manual says it in a
person's own words). Six scenarios pressed `o` to open a page, the `o`
went into the message box, and each waited out its three minutes for a
page nobody had opened — with `› o` on the screen it printed.

So [aim] is the gesture, spelled once: `↓` then `↑`, because an aim may
not be an answer — `enter` takes the pick and `esc` puts the question
away — and the walk is therefore walked straight back, leaving the
pointer where it was and the hand on the block.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* e2e: the page opens on the pointer, so the foot says what enter would send

The room scenario waited for `nothing chosen yet` on a page that had
just opened on `1 postgres`. Since Agent-Field#789 every answer has a pointer the
arrows walk and enter takes, and the page opens where the block's
pointer stood — questionroom.go's own enter path says so — so a page
with answers on it always has one to send. `nothing chosen yet` belongs
to the `something else…` row, which stands after the last option and
carries no key.

So the moment is read the way it now happens: the foot says `answering `
what the page opened on, and it may not also claim nothing is chosen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changes: the whole of what moved, and the three defects under it (Agent-Field#1004)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…oes, and the prefix gate weighs the real belt (review needed) (Agent-Field#996)

* roles: the compaction role is deleted, because no compaction has called a model since the summariser went

RoleCompaction was a priced, described, settings-visible dial wired to nothing.
[Agent.compact] takes `_ context.Context` and makes no model call at all — a
pass is two mechanical rearrangements of text the session already has — so the
row on the Providers tab offered a person a model for a call that never
happens, and TierHigh's patience was derived from a summary nobody writes.

The role, its tier assignment, its description and the settings and manual text
that offered it all go. The high tier keeps every tenant it actually has —
the auditor, the shaper, vision, the repair, the careful part of a division —
each registered from the package that makes the call.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* session: every request says what it is for, and the door is the only thing that spells it

NINE OF THE ELEVEN CALLERS OF THIS PACKAGE'S MODEL DOOR REACHED THE WIRE
ANONYMOUSLY. The guardian, vision, the shaper, the spell-out, the subharness
intake, the run planner, the harness designer, the ceiling's handoff draft and a
saved program's own AI step all called completeWithModel with no
provider.WithCallTag anywhere above them, so the model-call log could not say
what any of them was: 2,309 of the 2,839 untagged finishes in the ten days to
2026-09-10 are this package's, and with them the answer to "what was this build
spending that model on all night".

The tag was a context value a caller could set or forget, and forgetting it is
silent. It is an argument now — clientdoor.go's `callPurpose` — so a call with
no purpose does not compile, and the door is the one thing in the package that
calls provider.WithCallTag. The three callers that did stamp their own (the
turn, the ask tool, the errand ladder) pass a purpose instead, which is one
spelling of one fact where there were four.

AND auxiliary.go's OWN CLAIM WAS THE FALSE ONE. It said callRole was "the one
door all auxiliary provider calls pass through" while ten calls went round it.
It is the one door every ERRAND passes through, which is a narrower and true
claim, and the sentence says so and names what is legitimately not an errand —
vision streams into the room in the person's own turn, the handoff draft is
billed to the turn on the turn's own model, a program's step runs on no role's
model at all.

nohiddenwork_test.go is the law, on the laws gate: a blank purpose fails the
build, a WithCallTag outside the door fails the build, and the walk fails if it
ever stops finding the door.

No behaviour changes. Every call goes to the same model with the same options;
what is new is that its row in the log has a name on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* tests: the prefix gate weighs the belt a configured machine actually carries

THE GATE WAS POINTED AT A SHAPE NOBODY RUNS. fixedPrefixBudget's own doc comment
has always said it bounds "the belt the shipping conversation door assembles".
The fixture under it was v3ShapedAgent: a conversation with no memory store, no
accounts hub, no standing items and no saved programs — eighteen tools, 39,073
bytes, eight kilobytes of headroom, green every day.

A machine somebody has finished setting up carries twenty-three tools and weighs
53,025 bytes, which is 5,025 OVER the 48,000 cap the gate was enforcing. It has
been over for as long as the shape existed and the gate could not see it, which
is Agent-Field#576's shape exactly.

The gate now weighs beltShapes[0] — "the shipping conversation, fully wired",
the same definition prompt_belt_test.go uses, through one new helper so the two
cannot drift apart again.

AND THE FIGURE BECOMES A RATCHET. "The measurement plus a tenth" was right when
the number came out of a diet that had just been paid for. This measurement is
not the end of a diet; it is the first honest weighing of a prefix that had
never been weighed, already past what the last diet aimed at, so a tenth on top
would be five more kilobytes nobody chose. It is the measurement and nothing
else, and anything that grows the shipped prefix now fails the build.

WHAT IS OWED IS FILED, NOT FIXED. 48,000 stays as the stated target and the test
prints the 5,025 shortfall on every green run. The bill is one tool: `stand` is
9,607 bytes, more than a quarter of the whole tool block and nearly twice the
next heaviest. Trimming a schema is a change to what the model is told, not to a
byte count, so it belongs to whoever owns the standing belt rather than here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changes: the auxiliary inventory entry (Agent-Field#996)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* replay: the tag law reads the door, because the door is where a tag is stated now

TestEveryTagTheBuildWritesResolvesToARoleSomebodyDeclared walks the tree for
provider.WithCallTag. internal/session has exactly one of those now — the relay
line inside the door — so the law was reading a variable it could not resolve
and finding fourteen tags for a table of thirty-seven.

It reads the door's own call sites instead, which is the same thing the build
does: the purpose argument is what reaches calllog.Record.Tag. The
`callPurpose(...)` conversion comes off before the argument is read, a role worn
as a purpose resolves to internal/roles' vocabulary (over-approximated, because
this law is safe answering with more tags than one site writes and holed
answering with fewer), and the door's own constants are read from their
declaration the way roleWords already reads internal/roles.

AND IT IMMEDIATELY FOUND THE TWO CALLS THAT HAD JUST BEEN GIVEN NAMES. The
ceiling's handoff draft and a saved program's AI step now write tags, and
nothing priced them: both are lane.RoleAuxiliary and both have a row. The draft
rung is the dearest side-call on the ceiling road and it has been invisible to
every reading of calls.jsonl until now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* manual: the roles table stops listing a role the binary no longer registers

The models-and-cost page's roles table is described on the page itself as
'built from what is registered in the running binary, so it is the truth about
this build'. It listed `compaction`, which nothing registers any more, and the
crew's careful line and its account of what aforge calls on your behalf both
named the summary a compaction keeps — a call that has not existed since the
summariser was deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* prefix gate: the budget is the widest machine's, not the one it was written on

The first spelling of the ratchet was 53,100 — the laptop that weighed it said
53,025 — and it failed on the runner that proved it, at 53,132. The prefix is
not the same size everywhere: `grep` says a longer sentence about itself where
ripgrep is absent, which is 134 bytes on every request of every turn.

So the gate weighs what the WIDEST machine pays. bare.WidestGrepDescription is
the one place that knows which of the two sentences that is, and widestBelt
swaps it in wherever the gate runs, with a law that fails if the tool it names
ever leaves the belt — otherwise the budget would quietly get easier.

The residual is the shelf: load_capability lists the groups THIS BUILD has, 27
bytes more on a machine that can edit video. That is filed in the file's head
rather than fixed here, because naming what a build COULD have is a change to
what the model is told.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changes: the prefix figure is the widest machine's

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* roles: a role that stops existing is dropped from the profiles that pin it

Deleting RoleCompaction left the word in every config.json that had pinned it,
and the panel re-serialises the whole `models.roles` string on any change — so
the next time that person moved ANY OTHER pin the write came back `"compaction"
is not a role`, and every role pin on the machine was unchangeable until
somebody hand-edited config.json. The deletion had no reading for what it
deleted.

roles.Retired is that reading: a one-way list of the words this build used to
answer to, with the line that says what happened to each. It is the only memory
the build has of them, and without it a dead word is indistinguishable from a
typo.

ParseModelRoles drops those keys and says so once per process; writeModelRoles
takes them out of the TEXT, which is what is actually stored, so a person who
changes one pin stops re-saving a sentence about nothing. The test is the exact
string a pre-deletion profile holds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* prose: nothing says a compaction asks a model any more

A compaction is two mechanical passes over the transcript; it has never asked a
model. Six comments and two manual lines still offered it as the example of a
billed auxiliary errand — the pocket the title comes out of, the thing the
careful tier is for, a non-streamed call like a harness node — which is a
sentence about a call that does not exist, and was the figure the careful tier's
own patience was derived from until this PR re-derived it.

The settings hint for `careful work` no longer promises it either: what that
tier actually buys is the check on finished task work, the brief a task is
shaped into, and reading an image.

The transcript NOTE a compaction writes is still called a summary in agent.go,
because that one is real.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* roles: a role's word lives in internal/roles, and a law says so

`const spellOutRole roles.Role = "spellout"` lived in internal/session. It
registered, resolved and tagged its call correctly — and reached the cost report
as a word nobody had written down, because roleWords parses internal/roles and
nothing else. roleOf prices an unrecognised tag as a background errand with
nobody waiting, so the ONE auxiliary a person sits and watches was the one
counted as if it did not matter. Nothing failed; a number was quietly wrong.

The word moves to roles.RoleSpellOut beside its siblings, the Register call
stays at the call site, and TestEveryRoleWordIsDeclaredWhereTheVocabularyIs
fails the next one.

And the replay walker stops enumerating receiver spellings. callName flattened
one selector level, so three of the six rows in that switch —
`p.agent.completeWithModel` and friends — rendered as "" and could never match:
the ladder looked complete and was half dead. methodName asks the only question
the law has, which is the method's own name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* prefix gate: both arms weigh the shipped shape, at the widest machine

THE LEAN ARM HAD THE SAME HOLE THE FULL ARM WAS REPAIRED FOR. leanShapedAgent
built its own thin config — no memory store, no accounts hub, no standing items,
no saved programs — so the arm that bounds the prefix for the person with the
LEAST room weighed a belt without `stand` (9.6 KB on its own), `remember` or
`search_conversations`, none of which leanCapabilityGroups shelves. It is the
shipped shape on a small window now, which is what its own comment always
claimed.

And three ways the number could go back to depending on who ran it:

  beltShapes[0] read the fullest shape only until somebody put a row in front of
  it. beltShapeNamed asks for it by name.

  WidestGrepDescription returned the fallback sentence because that one is
  longer today; it now measures both and returns the longer, and widestBelt
  ASSIGNS rather than comparing — a swap that only fires when it makes the
  number bigger is a swap that stops firing silently.

  load_capability names the groups this BUILD has, and `edit_video` exists only
  where ffmpeg is on PATH — 27 bytes of prefix that depend on a program nobody
  installed on purpose. loadCapabilityDescription takes the members as a
  question now, so the gate can ask for the widest answer instead of this
  machine's.

Both budget figures are re-measured in the next commit, on the Spark.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* prefix: both caps are the measurement, and the three own-client roads say what they are

THE CAPS. Full 53,141 and lean 44,989, both weighed on the Spark against the
shipped shape at the widest machine, both with no headroom, both printing the
shortfall against the target they owe on every green run. PERF.md moves in this
commit, which is the rule for a cap.

THE ROADS. memory_consolidate.go, standing_run.go and tools_doc.go each build a
provider client the one door does not make — a longer timeout, static routing, a
document config — and complete on it directly. That is a real difference, so the
answer is not to force them through the door; it is to make them say the word
the door says. clientdoor.go's withPurpose is that, and it is still the only
file in internal/session that spells provider.WithCallTag.

The two errands this build runs most often with nobody there reached the call
log with NO TAG AT ALL, which made them indistinguishable from a turn that had
lost its name. The standing check is `standing-check` and not `sentinel`,
because cmd/aforge already writes that word for the resident's quorum errand and
two calls under one tag is a reading of neither.

The law now pairs the halves: a file that builds its own client owes a
withPurpose, and it fails if it finds fewer than three such files — a structural
law that matched nothing would pass for ever.

And `compaction` comes out of the replay role table: no build can write it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changes: the four findings the review turned up

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* law: the purpose is required on every own-client call, not once per file

A file-level pairing is satisfied by one road naming itself while the road
beside it stays anonymous, which is the exact shape of the failure this law
exists for: the forgettable one is always the second one.

Every completion in a file that builds its own provider client must now carry a
withPurpose on the context it is handed — inline, or through a local the purpose
was folded into. standing_run.go writes the purpose four wrappers deep and
completes on `callCtx` several lines later, so the reader follows assignments to
a fixed point rather than looking only at the argument.

And the second pass counts the CALLS it judged, not the files: three files that
build a client and nought completions in them would pass in silence.

cmd/aforge-replay's two doc comments stop citing auxiliary.go's deleted
WithCallTag line and point at clientdoor.go's callPurpose, which is where the
tag is spelled now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* roles: the new law found two more of its own kind, and they are fixed

TestEveryRoleWordIsDeclaredWhereTheVocabularyIs caught
`const standingSentinelRole roles.Role = "sentinel"` in internal/session the
first time it ran, which is the second instance of exactly the defect it was
written for and the reason it is a law rather than a note. The word moves to
roles.RoleSentinel; the Register call stays at the call site.

And with `spellout` finally visible to roleWords, the tag law asked
cmd/aforge-replay to say what it prices as — so the table gains the row, which
is spellout.go's own lane.RoleAuxiliary and not a second reading of it.

That row carries the seam it exposes: a person SITS AND WATCHES a spell-out, and
an auxiliary is by definition a call nobody's seconds are being spent on. Either
the lane role or the product is wrong there. The table's job is to agree with
the build, so it does, and says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* roles: a dead pin is a dead pin, and three more numbers stop depending on the machine

THE RETIRED LEDGER IS DELETED. It was a map of the words this build used to
answer to, so a dropped pin could be told apart from a typo — and it could not
earn its keep. Both are a pin no call will ever consult, both should stop being
written back, and the only reader who can act on either is the person adding a
word right now. Keeping it meant every future deletion owed it an entry, and the
day somebody forgot, the old failure would come back exactly as it was.

So the reading is symmetric: a pin for any word that is not a role is dropped,
the writer refuses only a name being ADDED, and nothing dead is ever written
back. The row DRAWS what is acted on (config.LivePinsAt) rather than the raw
stored text — it used to draw `compaction: some/model` as a live pin while the
list under it had no such line — and the panel says "no longer a role — that pin
is ignored" to the person about to lose it. It is a sentence and not the
log.Printf this had, which was the only one in internal/config and landed on
stderr in every headless and --json door.

AND THREE MORE MACHINE-DEPENDENT NUMBERS. The lean page interpolates the working
directory, so that arm measured 45,034 on macOS against 44,989 on Linux and
failed — a fixture artefact worth tens of bytes in the gate whose entire purpose
is to be one number for everyone. The lean arm was also being handed the FULL
shape's load_capability sentence, making its figure a hybrid of two belts that
neither pays. And the replay table priced `vision` as an errand nobody waits on
while image.go, its only writer, sets lane.RoleTalk with the reasoning beside
it: a look at an image streams into the room during the person's own turn.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* prefix: one weighing, one shipped shape, and a debt that is enforced

THE DEBT WAS INVISIBLE. It was a t.Logf, and nothing in the Makefile passes -v —
so the one line saying the shipped prefix is thousands of bytes over what it is
supposed to be was printed where no human and no CI log would show it. The cap
is the TARGET plus a dated waiver now, one per arm in one place, and a waiver
only ever shrinks: known-red's discipline applied to bytes. Growing the prefix
takes a diff with somebody's name on it.

ONE WEIGHING. The two arms were forty lines each, written twice, and the copies
had already drifted — only one printed its shortfall, and it was the arm with
the smaller debt. reportPrefix is called twice.

ONE SHIPPED SHAPE. v3ShapedAgent WAS the second opinion: its own thin config,
eighteen tools, weighed by four gates including both budgets. It is
shippedShapeAgent now. beltShapes' lean row built a third config again, so the
belt law and the prefix gate were measuring different lean conversations under
one name; both call buildShippedConversation.

ONE VOCABULARY READER. purposeWords and roleWords were the same function with a
different path; they are constWordsOfType, read once, with the empty-result
panic inside it. The role law catches the CONVERSION spelling too —
`roleRepair = roles.Role("repair")` was invisible to it — and now also reads
every roles.Register argument, which is the spelling that cannot be worked
around. roles.RoleRepair is declared with the others.

And the manual stops teaching a summarizer rung, its `session: summarizer
returned nothing` string, and a focus appended to its instructions. None of that
exists: a pass stubs and folds, asks no model, and never folds your own words.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* prefix: the lean page is weighed the same on every machine, to the byte

The working directory was a fixture artefact worth tens of bytes; the
Workstation line is runtime.GOOS/GOARCH and worth one, which a ratchet with no
headroom fails on just as hard. Both are normalised at the one place that needs
them to be, and the product still tells the model the truth about the machine it
is on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* prefix: the lean waiver shrinks on its first run, and `repair` gets a price

The lean figure went 44,989 → 44,920 because the page's working directory
stopped being a fixture artefact and the lean arm got its OWN shelf sentence
instead of the full belt's. That is sixty-nine bytes off a number that had been
measured on a hybrid, and the waiver comes down with it — which is the whole
behaviour the ledger exists to have.

And `repair` reaches the replay table. It only became visible there when the
role stopped being conjured at its call site as roles.Role("repair"): a word
spelled that way is invisible to every reader of the vocabulary, which was the
defect, and the law that now catches it found this the moment it could see it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changes: the review's findings, all of them

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* prefix: the lean waiver shrinks again, to the byte the page now measures

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* roles: the registry is the tier table, and the vocabulary is a different question

A REGRESSION I INTRODUCED, CAUGHT BY THE SUITE. Making the read drop any word
the REGISTRY does not know threw away `imagegen:paint/model` — a real pin doing
real work. A role gets into the registry by registering, which is how it gets a
tier, and a role with no tier never does: a painter is chosen by a pin or not at
all, since a chat model in tiers.high is not a statement about painting. So the
registry was never the answer to "what are the roles".

roles.Vocabulary is that answer and roles.Known is the reader's door to it. The
list is hand-written, which is a thing that falls behind, so a law parses the
file it lives in and fails when a declared role is missing from it or a listed
one is not declared. The refusal lists the vocabulary too, so a person is shown
`imagegen` as a name they may pin.

And the retired-pin test seeds the row the way a profile written before the
deletion holds it — straight into the file, because the writer is exactly what
would refuse it today and that person never typed it today either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* session: the two holding actions name the issue that deletes them

The `standing-check` rename and [widestBelt] are both measurements around a
problem rather than fixes for it, and both are deferred to a cross-package wave
rather than grown here. Each now says so where a reader meets it, and names
issue Agent-Field#1010 (spine: Agent-Field#928) as the place it is deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* config: whether a pin was already there is a question about the person's file

writeModelRoles forgives a retired word it FINDS and refuses one being TYPED,
and it asked parseModelRoles which of the two it was looking at. That reader
drops every retired word by design, so the stored row it handed back could never
contain the one word the check existed to find: a name that had sat in the file
since before the deletion read as a name typed for the first time, and a machine
with an old `compaction:` pin could not save a change to any other pin.

The set is read off the stored TEXT now — storedRoleWords — because every reader
above that line drops what it does not recognise, and the dropped word is
exactly the word this question is about.

The asymmetry gets its own test: the same word, forgiven when found and refused
when typed. The old test only ever typed a word that was never a role, so the
half that broke was the half nothing watched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* session: a held-reads pointer sentence pays 150 bytes on both prefix arms (Agent-Field#1015)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…gent-Field#1018)

* tui3: the draft kill ring — whole-box clears keep the words, and the ↑ walk visits them dim in front of the sent history

* tui3: /drafts, the page over the kill ring (enter restores, d lets one go)

* changelog: the draft-page entry uses the PR's own number

* tui3: /drafts takes its place under /compact and has a fate on home
…to OpenRouter unpinned, exactly the named provider pinned — is the row this build ships (Agent-Field#1019)

`simple` is the default routing row: no pin sends no provider object and OpenRouter's own routing answers; a pin sends exactly {only:[X], allow_fallbacks:false}. The routing row reaches every client this process builds; a pin is refused by the wire, not by a saved memory, and the person is told in the conversation; the pin rides the calls a person is reading and every call of a command they typed; the call log names what each attempt carried; the picker predicts no machine where nothing chooses; a stall under simple without a pin is reported, never rescued. latency, price and off remain opt-in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv
… still queued (Agent-Field#1024)

The hand-over press wakes a turn that drains the steering queue and, if it finishes, hands unsettled nodes back; two tests read the queue between those two events and saw it shrink. They now count what the model was told, queued plus drained, and hold the turn open so the press under test lands while the model is still deciding. No production change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv
…tall (Agent-Field#1021)

* docs: a README that sells the chat flow and tells the truth about install

The README was an install page carrying four curl lines that 404 for anyone
who is not signed in: the repository is private, `scripts/install.sh` is not
on `main`, and the `agentfield.ai/get/aforge` proxy is an unmerged pull
request on the website. It said nothing about the product.

This one leads with what aforge is — a terminal session you talk to, that
reads and edits files, runs commands, hands work to tasks you can walk away
from, remembers, and takes slash commands — and shows a real captured screen
of that loop on the default model. The install section puts the road that
works today first (clone, `make build`, `bin/aforge`), then names both curl
roads and says in one sentence each which is live and which is not. Long
tails — installer flags, the slash-command table, the key table, the tool
names — are folded.

`internal/manual/chat/running-from-the-terminal.md` carried the same four
dead curl commands, so the manual law makes it the same change. Its first
section now says the same true thing; every `## ` heading is untouched, so
the probe table still reaches it.

Numbers appear only where a cited constant backs them, and the two places
shipped text disagrees with itself (`--parallel` 8 versus 32, `--budget`
150,000 versus `--token-budget` 220,000) are left out rather than picked.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: README fixes from the refutation pass

An independent lane re-ran every recipe in the claims ledger and drove the
real binary in tmux. Three sentences did not survive, and one whole road was
missing.

The permission card was fiction. The ledger "checked" it with a grep that
matched a comment at internal/tui3/consent.go:20 — whose own next paragraph
says the block draws it now. What the surface actually draws is a bordered
block whose header counts ten seconds (config.DefaultConsentTimeout), whose
second choice is the bare word `always`, and which carries `c change`,
`? ask back` and `safe answer`. Replaced with a verbatim capture at 100
columns, and `c` and `?` are now described in the words question.go uses.

`aforge models` was listed as needing no key. It exits 1 with `aforge needs a
model to work with.`; the repo's own manual page already said so at
running-from-the-terminal.md:583. It now has its own sentence.

The first-run quote was the branch nobody sees. firstrun.go:808 picks
`setupConnectWord` wherever browser connect is available, so a fresh reader
never meets `setupKeyWord`. Both roads are now named, folded, with the
condition that chooses between them.

And the gap: "run `aforge` inside a project" never said what that means. A
new section gives the launch order from cmd/aforge/chatv3_layout.go:84 —
`--workspace`, else the git root, else the directory, except in your home
directory or under /tmp where aforge keeps a folder of its own and says so —
names `/workspace` as the door, and says that on a machine with conversations
on it bare `aforge` opens home rather than a chat. All of it was reproduced
in tmux first.

Smaller: `/spend`'s registered meaning carries its ` · alt+3` again, the nav
row gains the Tools anchor, `ctrl+g` and the right-hand column are described
as the code has them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: the change entry for the README rewrite (Agent-Field#1021)

Five invalidations, because five things a session might remember about this
repository stopped being true: the four dead curl lines as the install road,
the same four in the manual page, the one-line permission card, `aforge
models` as a keyless command, and `setupKeyWord` as the first-run copy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: the account tools are named as conditional

The tool-name table listed the five Gmail and calendar tools and the four
Slack ones with no condition, which reads as "Gmail is on the belt". It is
not: familyTools (internal/session/tools_connect.go:438) hands out the Google
five only for a connected Google account and the Slack four only for Slack,
and connectcaps.go:57 holds the same two sets as the inverse. The conditions
paragraph under the table now says so and names the door a person uses,
`/connect` (internal/tui3/commands.go:78).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ire will ask for, a routing change lands now, the debug pin records bodies, enter unpins (Agent-Field#1025)

The status chip, the settings lane row and the picker read the pin the transport will act on (provider.PinNow) instead of the profile row, so a retired pairing draws auto with its reason and never @x over a bare request. A routing change in the settings sheet re-installs the row and reaches the very next request because the profile door hands no static routing down. AFORGE_DEBUG=1 takes the in-process road like --debug and writes request bodies. In the lane fold, enter on the machine in force unpins it and typing filters that model's machines. Closes Agent-Field#1022.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv
…hat drew it, and the opened step shows the prompt (Agent-Field#1026)

The generate_image row draws the file it is about in primary ink and the image model that actually drew as a dim qualifier, read from the tail of the tool's own result line (", generated on <model>") so the row and the request cannot disagree; an unknown model adds nothing. Opening the step leads with the prompt, one dim line per other input, then "drawn with X" or "asked for best · drawn with X", then the picture. view_image rows gain their file the same way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv
…ands every engine down (Agent-Field#1000)

Home's foot box is a block of one height — three rows, padded below the draft so the caret stays put — and the floor is spent only from frames that have the rows to spare, through one door the chat's foot and every place's frame ask. `aforge engine --stop-all` names each workspace it stands down. Gate re-run on Spark against dev 0738312 merge: laws, pr-ready (tui3 312 s, cmd/aforge, enginehost) all ok; driven captures at 120x40 and 80x24 match dev.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv
…e behind a model is provider (Agent-Field#1027)

Owner ruling 2026-09-14 (Agent-Field#1023 item 1): everywhere a person reads, the thing behind a model that answers a request is a provider — the settings row, the /model hints (→ providers), the fold sentences, the rescue and stall notices, the config hints, and the manual page titled Providers. Code identifiers, config keys (lane.talk), the @morph chip and the /status --json `lane` key (a script contract) keep their spelling. A law test fails any tui3 string literal that says lane.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv
…nt-Field#1033)

* PERF.md: the prefix budget table says the caps the gate enforces

The waivers were raised by 150 on each arm in the commit that made them, with
the reason in the code comment, and this table was never opened — which is the
one thing the performance law forbids: a cap that changes changes the doc in
the same commit. The table now reads 53,291 and 45,066, over the targets by
5,291 and 13,566. Closes Agent-Field#1030.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv

* docs: change entry for Agent-Field#1033

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…when you come back to the tab (Agent-Field#1036)

* session: a card for a decision you already made is not asked again when you come back to the tab

A turn's backlog is every event that turn has sent, kept so a surface arriving
mid-turn reads the work from its first line. A CARD in it is the one event that
is not a report of something that happened but a question about something that
has not — and the replay handed it over verbatim, whatever had since been
decided. So a person who approved a task, looked at another tab and came back
was asked to approve it again, on every return, for as long as that turn ran;
and because a question takes the keyboard where it is drawn, the conversation
underneath it could not be scrolled while it stood. Both symptoms, one cause.

The replay now consults the answer. [eventHub.attach] is handed what is still
being asked — the banked words, read off a.mu at the moment of the attach, which
is where a question goes on the book when it is raised and comes off it the
moment it is answered or withdrawn — and leaves out a card whose question is no
longer one. The backlog stays a faithful log of the turn; what is filtered is
the REPLAY. [questionAsked] names the question a card put in front of somebody,
in the lanes' own keys, for consent, a task proposal, a standing card, a harness
offer and a connect ask alike.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv

* session, tui3: the card comes back with your answer under it, not with the question again

The first half of this change left the settled card out of a turn's replay
altogether, and that traded one lie for another: the surface reads a propose_task
call whose card never arrived as a call that was refused (task.go's
[app.refuseFormingCard]), so coming back to the tab said `not started · the call
was refused` over work that was running.

So the engine states the outcome instead of the replay swallowing the question.
[taskWait.decided] restates the proposal with the answer stamped on it the moment
anybody settles it — a person, another window, or the countdown — beside the
restatement [taskWait.withdraw] already made for the other ending, and at the one
place both roads pass through. A card that states its own outcome is not asking
anything, so the replay keeps it and leaves the open one out.

The surface draws it as what it is: the assignment with the answer under it and
no question raised. [taskVerdict] is the one reading of approved-and-redirect
that the window whose key answered and the window replaying a decision both take.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv

* docs: the change entry for Agent-Field#1036

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182eyxXP8Xe7ADNKpYDe4wv

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…he same turn carries on (Agent-Field#1037)

* approval: let account discovery reach its own question

The shipped prompt blanket put approval questions in front of both account
discovery and the connect card. Account listing now lifts as a read, while the
connect tool lifts because its own card is the one consent question; named
rules, deny, and the existing floors still win.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* session: carry account connections through the same turn

Connect answers used to collapse refusal, silence, and typed instructions into
one no, while the tool description told the model to wait for another turn.
Answer provenance now survives to distinct sentences, one shared instruction
says to continue on the next request of this turn, and the belt teaches the
conditional discovery path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* tui3: settle browser waits when their turn ends

A browser row could keep waiting and animating after its turn had closed, and
words typed beneath a browser question opened a key-check card. Turn settlement
now closes those rows with the existing incomplete wording, and only typed-key
questions can open the check card.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* session: hide browser sign-in exchange details

Browser callback failures used to pass their raw exchange text, including a
state nonce, into the transcript and tool result. The session boundary now
keeps vendor refusals in plain words and replaces internal exchange failures
with one honest, non-sensitive sentence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* manual: tell the truth about account connections

The manual said the browser stayed closed, concurrent credential writers could
lose a change, and account bootstrap tools followed the ordinary approval path.
It now describes the opened browser and retained address, locked store, single
connect question, same-turn continuation, and the distinct ways a question can
finish.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* session: let a connect answer beat the clock

The five-minute branch could call an answer silence after its resolver had
already claimed the question. The clock and resolver now share one atomic
winner, and a claimed answer is returned even when its send is still in flight.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* connect: hide every failed browser exchange

The session hid familiar OAuth failures, but unknown text passed through and
the Connections tab still drew raw callback errors. Browser failures now cross
one deny-by-default boundary; only a short vendor-shaped refusal is retained.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* session: prove account pickup on the shipped posture

The same-turn acceptance ran under blanket allow, where its zero-consent claim
could not fail. It now runs under the shipped prompt posture and proves the
connect card is the only question before the newly armed tool runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* manual: keep account answers inside the search window

The privacy heading traded away the old asker's words, the SearchBoth guard was
rewritten into a plain search, and the MCP inventory was split under false
counts. Both vocabularies now retrieve, the rescue direction is loud again,
and the original 28-row section is whole.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* session: the account cue pays for itself out of what was said twice

The new account sentence and the same-turn wording pushed both prefix arms 79
bytes over their caps, and the sentence named `services` without the belt fact
claiming that name. The fact now lists both tools it spells, says only what has
to be true before the call, and `use_service` no longer repeats the ordering
the fact already gives — leaving both prefixes below where they were on dev.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* changelog: the connect-on-demand entry for Agent-Field#1037

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…e keystroke (Agent-Field#1039)

Home's ctrl+e writes the archive flag and then re-reads the world on the spot,
so that the row moves under the hand rather than on the next sweep. The seam it
re-read through answers from what is held and refreshes behind itself, and the
write went straight past that cache to the engine — so the rebuild redrew the
row exactly as it was, and the row went only when a later beat's fetch had
returned and a later beat rebuilt. Two seconds of allowed staleness plus up to
two three-second beats is the several seconds a person saw between pressing the
key and watching the row go.

hostWorld.archive is the seam now: the write over the wire, then the held copy
corrected and the entry aged out so the next reading still asks the engine what
it really thinks. That is hostStanding.save's correction, which already existed
for the ambient side, applied to the one write home makes against the world. A
refused write changes nothing here, and bringing a row back is the same
correction in the other direction.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Agent-Field#1041)

* module: the import path is github.com/Agent-Field/codeaf

The repository is being renamed from aforge-v2 to codeaf; the module path
moves first, on its own, so the mechanical import rewrite is one reviewable
commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* build: the binary, the commands and the corpus tag are codeaf

The commands move with the product name: cmd/aforge becomes cmd/codeaf, and
the five neighbours beside it — census, changes, demo-home, release, replay —
follow. `make build` writes bin/codeaf, the packed-manual build tag is
codeaf_packed_manual, the release artifact is codeaf-release, the CI scratch
root is /tmp/codeaf-ci, and .gitignore's stray-binary line is /codeaf.

The manual page starting-aforge.md is starting-codeaf.md, and every reference
to it moves with it.

release.yml stops naming the repository: RELEASE_REPOSITORY is now
${{ github.repository }}, which is right before the GitHub rename, right
after it, and right in a fork that publishes to itself.

The wide textual rename of the product's own words is the next commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* rename: the product in the Go sources is codeaf

The wide textual pass over every Go file: identifiers, strings, comments and
test expectations spell codeaf, CODEAF_ and ~/.codeaf where they spelled the
old name. It is the mechanical half of the rename and it is deliberately
plain — no fallback reads the old environment variable, no adoption moves an
old state root, no shim answers to the old command. Those arrive next, so
that they are added to a tree with one name in it rather than a tree with two.

Four things the machine could not do on its own, and a person did:

  - `AForge` inside an ordinary English word is not the product. A blind pass
    turned `AForgedTool` and `AForgettablePreference` into rubble; both are
    English again, spelled so the letters of the old name no longer occur.
  - The folder search ladder was documented and tested entirely in queries
    derived from the old name — its lead, its initials, its loose reading and
    the transposition the last rung exists for. `codeaf` shares four letters
    with the `~/code/` every one of those paths begins with, so several of
    those queries now reach a higher rung and would never have tested the rung
    they were written for. Every example is rederived from the new name and
    the ladder's own comment says what it now says.
  - The wordmark on the first screen had no letterform for `c` or `d`, and
    [wordmarkRows] SKIPS a letter it cannot draw — so the first screen of a
    fresh install would have spelled the name with two holes in it.
  - The guard that failed any first-screen sentence still carrying the retired
    name compared against a literal the same pass had renamed, so it asserted
    the opposite of its intent. That duty is the whole tree's now, and the
    name law is where it goes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* rename: the manual, the docs and the tooling say codeaf

The same wide pass over everything a person reads or runs: the two manual
corpora, the system prompt, the READMEs and design notes, the workflows, the
installer, the bench harnesses and their self-tests.

The pieces that are plumbing rather than prose, and had to move with it:

  - `.gitignore` ignores `.codeaf/` where it ignored the old repository-local
    directory.
  - CHANGELOG.md keeps every word it recorded and loses one: the HTML comment
    `codeaf-changes roll` inserts beneath is the tool's anchor, not a release
    note, and the tool had just been renamed out from under it.
  - `internal/exec/testdata/rtk` is a stub, and the only thing it shares with
    the test driving it is the name of the variable it logs to. Renaming one
    and not the other left the stub logging to /dev/null and the test reading
    an empty file.
  - The realissue bench rig names the arm it runs. `bench/conversation`'s own
    runner had already moved to `codeaf`, so a candidate still declaring the
    old arm was one the runner would refuse to start.

`test/remote`, `internal/furrowbin` and the two `docs/design` capture scripts
are renamed too. They sit outside the paths the wide pass was pointed at, and
each of them names the binary, the state root or an environment variable that
had already moved — a stale spelling there is a broken command, not a stale
word. furrow is still furrow throughout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* manual: the pinned-version install line gives VERSION to bash

`VERSION=<tag>` written before `bash` in a pipeline sets the variable for
`curl`, which does not read it, and not for the script on the other side of
the pipe — so the documented way to pin a release installed whatever `latest`
resolved to. README.md, docs/rules/promotion.md and release.yml all spell the
piped form already; this page was the one that did not, which is why
internal/release's TestPublishedPinExamplesGiveVersionToBash was red on dev.

The example is now a runnable line rather than a sentence describing one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* manual: the page says what codeaf used to be called

`## What it is called` carried a sentence contrasting the wordmark's spelling
with the prose's, and the mechanical rename collapsed both halves onto one
word — it read "it used to say codeaf in some of those places and codeaf in
the others", which says nothing and is the kind of sentence a reader stops
trusting the page over.

The history moves to its own `## The old name` section, which is where it can
name the retired spellings honestly, and which is the one place in this corpus
the name law lets them be written. It answers the five questions somebody
actually asks — what happened to aforge, is this the same thing, is this the
CodeAF from the benchmarks, why is the folder .codeaf now — and it states the
three things on a person's machine that keep working: the adopted state folder
and the link left behind, the AFORGE_ variable still read when the CODEAF_ one
is unset, and .aforge-v3/config.json still read inside a repository.

The section is written long rather than dense on purpose. A short section whose
heading carries every one of those words outranks its own neighbours under
BM25: the first draft took the top result for "how do I start codeaf" away from
`## Starting it`, which is the page talking over itself.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chat: three comments and a failure message that named two retired products

Each of these was written when the surface spelled one product in the
letterforms and another in the prose three rows under it, and each named both.
The mechanical rename wrote the same word into both halves, so all three now
said a thing happened and then described it happening to itself.

They are rewritten to keep the anecdote — which is the ONE SOURCE OF TRUTH
law's own worked example and worth keeping — without naming a product that no
longer exists. The name law covers internal/tui3, so restoring the dead
spellings is not available to them, and a `legacy-name` marker is for a
compatibility seam rather than for prose.

TestTheWordmarksRightEdgeIsNeverAHoleBetweenTwoStrokes failed with "the name
would read `aforga`", which was the old name with its `e` drawn as an `a`. It
derives the word from [product] now, so the message stays true through the next
rename as well.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* namelaw: a live surface cannot quietly say the old name again

A rename is finished when the tree stops being able to undo it by accident. A
wide `sed` makes every literal say the new word once; nothing then stops the
next wave writing the old one back into a manual page, a workflow or a first-run
sentence, and nobody finds out until somebody runs the wide search again.

So the law is a package of nothing but the law, found by scripts/laws.sh through
its go/parser import and therefore on the pull-request gate the day it lands. It
reads every Go source under cmd, internal and bench; go.mod, the Makefile, the
ignore file and the size budget; the workflows, the issue and pull-request
templates and the rulesets README; the scripts and the bench tooling; both
manual corpora and the session prompts; and the documents an agent is pointed
at. A failure names the file and the line.

The exemptions are records rather than holes, and each says why beside itself.
CHANGELOG.md, docs/changes, docs/design, the captured frames, bench-results,
audit-notes and everything under a testdata directory say what was true on the
day they were written. A line marked `legacy-name` is a compatibility seam —
inline it exempts only itself, and on a line of its own it reaches forward to
the first blank line, which is how a multi-line string or a two-line shell
fallback is marked once. A Markdown section headed with the words "old name" is
the one page that explains the history to a person who asks. The root Markdown
set is an allow-list and not a glob, so a lane's untracked spec or notes at the
root is not silently promoted into a product surface.

Fixture tests prove every boundary on a temporary tree rather than on this one,
so the repository-sized assertion going green means the surfaces were read
rather than that the walk found nothing convenient.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: the agent-facing guides name the product, and the name it replaced

CLAUDE.md and AGENTS.md are one file reached through a symlink, so they stay
byte-identical by construction. They gain `## The name` — codeaf, lowercase,
since 2026-09-14, and the law that enforces it — and `## The old name, and the
three places it is still allowed`, which names the retired spellings outright.
Naming them is the point: an agent whose memory predates today will spell them,
and a section that refuses to say the word cannot tell that agent it is wrong.
The section's own heading carries the words "old name", which is the law's
exemption, so the guide is allowed to say what it has to say.

Two sentences the wide pass left standing on one word: PERF.md's owner ruling
had become a tautology about builds, and docs/CHAT-V3.md still described the
rename as a refactor that had not happened yet while spelling the new name for
both sides of it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* tui3: the folder-search comments reason from the new name

Four comments still explained the ladder with queries derived from the old
name (an initials example with a letter the new name does not have, and a
slip example that no longer slipped). They reason from the rederived
examples now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/design/polish: the three audits keep their record

The wide rename made both sides of a recorded comparison the same word, so
sentences that once said the wordmark spelled one name while the prose spelled
another became false. A dated audit is a record; it keeps the words it saw.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* env: one door for the product's own variables, and the old spelling behind it

Every CODEAF_ name the product reads now goes through internal/env, which
answers the CODEAF_ spelling and falls through to the AFORGE_ one when the new
name is unset or set empty — so a machine whose shell still exports the former
names keeps working for one release.

There are two doors on purpose. Get and Lookup are for a name the caller knows
at compile time, and they PANIC on anything that does not start with CODEAF_,
because a constant naming somebody else's variable is a programmer error. Value
and LookupValue are for a name that is a value at runtime — a settings row's Env
field, a knob table, a seat, an onboarding row — where the same table can hold
OPENROUTER_API_KEY beside CODEAF_MODEL; an owned name there gets the same
fallback and a foreign one is read plain.

The remaining call sites move in the commits that follow, and the law that keeps
them there lands with the last of them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* home: both names resolve the state root, and the first real start adopts the old folder

CODEAF_HOME wins; AFORGE_HOME is honoured when it says nothing. With neither
set the root is ~/.codeaf when that exists, ~/.aforge when only that exists —
a legacy root the adoption below could not move — and ~/.codeaf otherwise.

THE ADOPTION IS ONE-WAY, ONCE, AND NEVER FATAL. On a machine with no ~/.codeaf
and a real ~/.aforge directory, the first start of the real binary renames the
folder and leaves ~/.aforge behind as a symlink to it, so an old PATH line
(~/.aforge/bin), an old unit file, a running daemon and anybody's own script
keep resolving. A second start changes nothing. Any failure is one line and the
process continues — the resolution above then reads the old root, which is why
the fallback exists rather than being dead weight.

It is called from main and from nowhere else, and the exported door returns
immediately inside a test binary: a test that resolves the state root must never
move the state of whoever ran it (internal/home/undertest.go says why). The
machinery underneath is driven directly by its own tests.

`codeaf help env` says once that the AFORGE_ spelling is still read for one
release, and the demo home now strips both spellings from the child it launches
— an inherited AFORGE_HOME would otherwise point a throwaway launch at the
owner's own state root, which is the single thing that file exists to prevent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* project: a repository's own files are read under either name and written under one

The three places codeaf keeps something inside somebody else's repository move
to the new name and keep reading the old one: project settings are
<workspace>/.codeaf/config.json with .aforge-v3/config.json read when the new
file is absent, a conversation's autonomy rules are <workspace>/.codeaf/
autonomy.json with .aforge/autonomy.json behind them, and a repository's
subharness shelf is <repo>/.codeaf/subharnesses with <repo>/.aforge/
subharnesses behind it.

READS FALL BACK; WRITES NEVER DO. Every writer names the new path and leaves the
old file exactly where it is — nothing here rewrites a person's repository on
its own, and somebody who renames the folder themselves simply stops using the
fallback.

The comment above ProjectConfigDir said this directory was deliberately not on
the product's final name because the rename would happen once, at the end. This
is that rename, so the paragraph now says what is true instead.

The orientation digest skips .aforge and .aforge-v3 beside .codeaf, so a
repository that still carries either is not read to the model as source, and
.gitignore knows about the new directory as well as the task system's own.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* tasks: the work is committed as codeaf and recognised under either name

A task commit is authored codeaf <codeaf@localhost>. Every reader that has to
tell the task system's own work from the person's accepts the former identity
too, because an existing repository carries commits made under it: a branch that
has only ever moved under one of those two identities has not been moved by the
person, and a landing must not be held back from it.

THE SAME RULE ONE DIRECTORY OVER. The folders the task system leaves inside a
repository are still written as .codeaf-v3 — live worktrees are registered under
that path by git itself and moving it is not something a compatibility layer may
do — but every reader of them now recognises .aforge-v3 as well: branch
protection, the audit walk, the mirror copy, the dirt reading, the left-behind
scan, and the clean and reset that must not sweep an old folder away. One list,
in one place, rather than a name spelled into nine files.

An empty commit range is also no longer read as the person having moved the
branch. The early returns above it mean the range normally holds a commit, but
a reading that comes back with nothing has observed nothing — and the reading-
failure case beside it already answers that the same way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* standing: one timer per machine, and a child hears both spellings

Installing the timer now stops and removes an aforge-tick unit on the same
machine before writing codeaf-tick, on systemd and on launchd alike. A machine
that had the old one would otherwise tick twice — two processes doing the same
standing pass against the same state root, five minutes apart forever.

The units it writes carry the state root under BOTH names, and the reader that
digs the root back out of a unit file on disk accepts either, so a unit written
by the old build still resolves after an upgrade and a unit written by this one
still works if something downgrades.

The same for every variable the product hands to a program it starts: a skill's
check.sh, a skills-bin shell, an rtk shell. They receive the CODEAF_ name and
the AFORGE_ one with the same value, because a script somebody wrote last month
reads the name it was written against and this program cannot edit it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* remote and install: the far command is codeaf, and the law holds the door shut

A host that answers "command not found" is told the program is called codeaf now
— it was aforge before 2026-09-14 — and must be installed there under that name.
There is no retry under the old name: a machine that has the old binary has the
old program, and starting it would be a conversation against something else. The
recogniser reads either word out of the far shell's own wording, because the
sentence a person needs is the same either way.

The installer pulls Agent-Field/codeaf, installs into ~/.codeaf/bin, marks the
PATH line `# codeaf installer`, and reads CODEAF_* variables with the AFORGE_*
ones as fallbacks. Two transitional roads, each with a comment saying when to
delete it: a release lookup that 404s under the new repository is retried under
Agent-Field/aforge-v2, and a release with no codeaf-<os>-<arch> asset installs
the aforge one — under the new name, since what is installed is this program.

AND THE LAW ARRIVES WITH THE LAST CALL SITE IT GOVERNS. internal/env's law reads
the tree with go/parser — so scripts/laws.sh runs it on every pull request — and
fails any product read of an owned variable through os: a CODEAF_ or AFORGE_
literal, a constant anywhere in the repository that holds one, or either pulled
out of os.Environ. The table-driven families that cannot be seen through
statically are held by a count of their dynamic-door calls instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* resident: the skill-check test names the old spelling where the law can see it

The marker inside the embedded shell script was a string to the name law, so
the tree was red on a line that was a legitimate shim. The old spelling is a
Go constant on its own marked line now, interpolated into the script.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* pair and relay: a name on disk or on the wire is not renamed with the product

A device key, a relay machine name, a registration label, two protocol tokens,
two HTTP headers and the pairing PAKE's two identities are bytes an EXISTING
machine already wrote down or already speaks. The wide pass renamed all of them,
which unpairs every paired device (`decodeSeed` rejects a key written by any
earlier build, and `Load` answers "is not a device key any more — remove it …
which un-pairs every device it was paired with"), changes the machine name a
person types after `--at`, and leaves an old relay unable to see either header.

They keep their former spelling permanently, each marked `legacy-name`: these
are not a one-release seam, they are the wire. The device-key reader accepts
both prefixes and keeps writing the old one, so a key written by this build is
still readable by a machine that has not upgraded. Only labels a person reads
say codeaf.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* install and release: the folder is adopted before it is made, and an old release still resolves

Three things the install road got wrong, each of which meets a person on the day
they upgrade.

THE INSTALLER MADE THE NEW STATE ROOT BEFORE THE BINARY COULD ADOPT THE OLD ONE.
`mkdir -p "$INSTALL_DIR"` creates `~/.codeaf/bin`, and adoption declines the
moment `~/.codeaf` exists — so every existing person who upgraded through the
documented road looked brand new, with their conversations, projects, memories
and keys on disk and unreachable. Proved against the real binary. The verified
download is now run once from the temporary directory first, while `~/.codeaf`
is still absent, and only when the install directory is under the state root.

A PINNED VERSION COULD NOT REACH THE FORMER REPOSITORY. `VERSION=<tag>` skips
the API call that carries the repository fallback, so a tag published before the
GitHub rename resolved against a repository that does not exist yet — which is
exactly the window this release sits in. The asset download falls back to the
former repository too, keeping the checksum in the same one it chose.

THE FORMER PATH LINE WAS LEFT BEHIND AND DUPLICATED. A shell file carrying
`# aforge installer` kept a PATH entry for `~/.aforge/bin` — through the
adoption link, the OLD binary — and gained a second line beside it. The old
marker is recognised and its line is replaced in place.

Also: the Release workflow read `.github/known-red.txt` under `set -euo
pipefail`, and that ledger burned to zero in Agent-Field#1012. Every non-dev release job
died before its tests. The Makefile already guarded for it; the workflow does
now too. That line is byte-identical on dev — it is not this wave's regression,
but this wave's release runs through it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* standing and wake: a timer written under the old name is seen, repaired and removed

Installing a timer already removed its former-name twin, but nothing ever
reached Install on the machine that needed it. `Timer.read` looked only at the
current unit path, so a machine carrying `aforge-tick.*` reported no timer at
all: the standing page said nothing was installed while the old unit went on
firing, startup had no drift to repair, and turning standing off left it
running. Reading falls back to the former units and reports them as installed
and stale, and Uninstall removes both generations on both platforms.

`internal/watchdog` — the v1 resident's wake timer — had no former-name spelling
anywhere in the file. Status, install and uninstall each knew one generation, so
an upgraded machine kept an active `aforge-wake` unit whose ExecStart still
resolves through the adoption link to the OLD binary, invisible, and installing
the current one gave it a second timer against one state root. It now carries
the same shape internal/standing has.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* home: an unusable current root falls back, and the page stops promising the link

Adoption and resolution both treated ANY existing object at `~/.codeaf` as the
state root. A regular file there declined the adoption and was then returned as
the root, so a person with real state in `~/.aforge` got
`.codeaf/graph.db is a file, and a folder was needed there` and no way to their
own things. Both now require a directory: anything else logs one line and the
former directory goes on serving.

The manual said codeaf leaves a link at the old path AND that a failure means it
carries on reading the old folder. Those cannot both be true of the same run —
the link is best effort AFTER the move, so a move that succeeds and a link that
fails leaves no old path at all. The page says it tries to leave the link, and
that it is the MOVE failing that keeps the old folder in use.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* environment and caches: the one door is used everywhere, and a cache is not thrown away

A key reference a person STORED was read with `os.Getenv`, so a stored
`CODEAF_X` got none of the fallback the environment package exists to give —
and the law could not see it, because the law only knows a read whose argument
it can read at parse time. The reference goes through the dynamic door, and the
law gains injected fixtures for every shape it claims to refuse plus a sentence
saying a runtime-supplied name is beyond it and `env.Value` is the door for one.

The extracted-document cache headers were renamed, and the reader compares them
exactly — so every `.extracted.md` on an existing machine became a miss and the
document was extracted again, which on the OCR and model engines is money per
document. The former headers are accepted on read; the current ones are still
what is written.

Four surfaces said an `AFORGE_*` name is read when the `CODEAF_*` one is
"unset". The code also falls back on an empty one. They say "unset or empty".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* namelaw and English: one marked line is one exemption, and "an codeaf" is not a word

A line that was exactly `// legacy-name` exempted every following non-blank line
— a hole wide enough to hide a paragraph, and a different law from the one
CLAUDE.md states, which is that each line carries its own marker. Nothing in the
tree relied on the run, so only same-line markers open the exemption now, and
the fixtures that blessed the run refuse it instead. Two markers sat on lines
with nothing retired on them and are gone.

`aforge` takes "an" and `codeaf` takes "a". Twelve sentences the wide pass left
mid-sentence now read, including the two a person meets — the terminal page's
"An `codeaf do` errand" and the models page's — and `internal/furrowbin`'s
promise that "every codeaf is an codeaf with furrow".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* tasks and media: the repository's own folder is .codeaf, and the former one is still read

Stage 1's wide pass turned `<workspace>/.aforge-v3/` into `.codeaf-v3/` — a
spelling that has never existed on any disk — while the settings layer had
already put the repository's own file at `<workspace>/.codeaf/config.json`. Two
folders for one product is the thing this wave exists to remove, and every
reader written as the LEGACY rung was spelled `.codeaf-v3`, so it found nothing
anywhere.

One folder now. Every dropping — tasks, images, music, audio, video, jobs,
stubs, attachments, the ground baseline, the release receipt, the left-behind
record — writes under `<workspace>/.codeaf/<kind>`, and every reader of a kind
accepts `.aforge-v3/<kind>` beside it, marked `legacy-name`. Reads prefer the
current path and fall back only when it is absent. Nothing rewrites somebody's
repository on its own.

THE TASK WORKTREE WAS THE ONE THAT BIT. `tasksDirName` was the only spelling
used to build a worktree path and the git-root lock, so a task registered under
`<repo>/.aforge-v3/tasks/<session>/<id>` — every task on every machine that has
run this program — was not found, not resumed and not cleaned, and an old binary
and a new one took DIFFERENT locks on one repository. Both paths are tried, and
a fixture registers a real `git worktree` under the former path and carries it
through reload, resume and removal.

Nine manual pages told a person to use `.codeaf-v3/config.json` for a
repository's settings — a path the code has never read. They say `.codeaf`, and
the tasks page names both spellings once where it says what is never committed.
`.gitignore` drops the line that never named anything, and the orientation
digest stops walking a directory it could not recognise.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* changes: the entry for the rename carries its pull request number

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* config: the receipt names the spelling of the variable that answered

A person who set AFORGE_MODEL was told the model came from CODEAF_MODEL, a
row they never wrote. The env door now says which spelling supplied a value,
the seat records it when the environment rung answers, and the receipt says
that word.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* codeaf-changes: a notes command renders one version's section to fit a GitHub release page

GitHub refuses a release body over 125,000 characters, and a rolled-up
section carries every fold of every entry — the v0.2.0 section renders
at 1.7 MB. `codeaf-changes notes <tag>` prints the section whole when it
fits, the headline layer with the folds removed when it does not, and a
cut at a line boundary when even that is too long, always ending with the
line that says the record is in CHANGELOG.md. A tag with no section is a
non-zero exit and nothing on stdout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* release: the stable notes go through codeaf-changes notes, not an awk copy of the section

The publish job copied the whole `## <tag>` section of CHANGELOG.md into
the release body; the first roll-up would have failed the stable publish
at the last step, after six binaries were built. The workflow now asks
codeaf-changes for notes that fit, the workflow test refuses the awk
copy coming back, and the two rules say what the page carries when the
section is bigger than a body may be.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: the entry for Agent-Field#1044

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* docs/changes: v0.2.0 rolled up into CHANGELOG.md

The 613 loose entries in docs/changes/unreleased/ — every pull request
since v0.1.0, the codeaf rename included — are now the `## v0.2.0`
section of CHANGELOG.md, headline lines with the claims one fold away, and
the loose files are gone. This lands on dev before the promotion, as
docs/rules/promotion.md orders, so the stable release reads its notes
from this section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* exec: the removed-worker law reads CHANGELOG.md as the record it already reads docs/changes as

The law skipped docs/changes because a changelog entry says what was
removed, by name, and rewriting it would falsify the record. The first
roll-up moved those entries into CHANGELOG.md, which the law did not
skip, and went red on the laws. The rolled-up changelog is the same
record.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: the entry for Agent-Field#1045

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…Agent-Field#1047)

* release: the release-surface test survives an absent known-red ledger

The ledger burned to zero and was deleted (Agent-Field#1012). Under set -euo pipefail
the step's first line — an awk over the missing file inside an assignment
— exited 2 before go test ran, on every channel but dev, which skips this
job. The first staging build after the deletion published nothing that
way. The file is read only when it is there, as scripts/laws.sh reads it,
and a test runs the real prologue under the real shell options with the
ledger absent, empty, comments-only and carrying a name.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: the entry for Agent-Field#1047

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ue on release day (Agent-Field#1050)

* Make the release-day install story true

Describe each release channel and the conditions on both installer roads.
Disclose the bounded background rtk fetch used by codeaf do, and state when
GitHub can enforce the checked-in branch rules.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/changes: the release-day truth entry for Agent-Field#1050

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…Field#1051)

* Third-party notices, generated from the build and guarded by a law

Nothing in this repository reproduced the licences of the code compiled into
the binary. internal/pair/cpace is BSD-3-Clause and linked into every build,
whose clause 2 asks for the notice in the materials that go out with a binary
distribution; internal/connect/ampcatalog/providers.json is an embedded copy
of the amp-labs catalog; and every module in the build carries the same shape
of obligation. None of them had a notice anywhere.

THIRD-PARTY-NOTICES.md is that notice, and cmd/codeaf-notices generates it
from the build itself — the modules `go list -deps ./cmd/codeaf` names, plus
the two components this tree carries its own copies of — so it cannot be
written by hand and cannot quietly go stale. Like codeaf-changes it is a
developer's binary and never a verb on codeaf, so the product pays nothing
for it.

The law beside it reads the same module set and fails when a compiled module
has no section, when either copied component is missing, and when the
committed CPace licence text is no longer reproduced word for word. It needs
no network and decides in a third of a second.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* A release carries the third-party notices beside its binaries

The release workflow published dist/codeaf-* and checksums.txt and nothing
else, so the notice existed in the repository and reached nobody who
installed the thing. A licence that asks for its text in the materials
provided with a binary distribution is not satisfied by a file on a branch.

The build job now copies THIRD-PARTY-NOTICES.md into dist/ before the
checksums are written, so checksums.txt covers it and the publish step —
which already uploads dist/* — attaches it to every release on every
channel. The installer needs no change: it looks up only the row naming its
own asset and ignores the rest.

internal/release asserts both lines, because a copy that quietly stops
happening is exactly the kind of omission nobody notices until somebody asks
for the notice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* README and the promotion runbook name the notices

Somebody reading the install section had no way to know a notice ships, and
somebody following the release runbook had no way to know it needs
regenerating when the dependencies move. One line each, where the reader
already is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs/changes: the change entry for the third-party notices

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Agent-Field#1056)

* The installer takes the newest channel build, not the first one listed

`--rc`, `--dev` and `--staging` read `releases?per_page=100` and took the
first tag that matched the channel. GitHub does not return that list in
publish order: with `--dev` today it chose `dev-20260915-e2ae913b7d0c`
(13:43Z) while `dev-20260915-56a22c20ec53` (14:56Z) and
`dev-20260915-4b6ec83cfbfa` (15:21Z) both existed, and a person got a stale
build with no sign anything was wrong. The whole list is read now and the
candidate with the greatest `published_at` wins, falling back to
`created_at` when a release has no publish time.

The refusal named only one of its two causes. A private repository answers
an anonymous call with a 404 and took the same path, so a person was told
about a rate limit that was not the problem; the sentence names both now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* The change entry for the installer's channel pick

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
ZeroPoint95 and others added 6 commits October 15, 2026 08:39
…r silently (Agent-Field#1830)

* automations: the design, and the core — schedule, store, presence, clock

The design (docs/design/automations/DESIGN.md) replaces standing items and the
v1 scheduler with one mechanism: a schedule, an optional look, an action, run
only while a codeaf window is open. audit-notes/standing-and-schedules.md is the
evidence it answers.

internal/automation is the object and its machinery, with nothing wired yet:

- schedule: five-field cron read in the automation's own zone, stepping in
  absolute time so the wall clock never goes backwards (a 01:30 line runs once
  on the night the clocks fall back); intervals on an anchored grid, so late
  runs never drift the next one; lines that never match are refused.
- store: SQLite (WAL) with the person's half as a document and the clock's
  state as guarded columns; a slot becomes a run in one transaction and can
  never be taken twice; a change counter every window reads from.
- presence: one OS lock per open window, so "is any window open?" is answered
  by the kernel, not a heartbeat.
- clock: one holder at a time, catch-up once and marked late, one run per
  automation at a time, two pieces of work at once, watches that speak on the
  change to yes, explicit outcomes read from reports and context causes, and a
  clean stop when the last window closes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the engine side — the automation tool, its card, and the runner

- `automation` (propose, list, pause, resume, delete, run, history) is on a
  conversation's belt when a store is behind it, and never on a task's or a
  run's: nothing an automation does may arm another. Every answer ends with
  the time.
- The card is one question kind (`automation`) built once in the session:
  1 Save · 2 Save and run it now / check it now (not for reminders or worktree
  work) · 0 Don't save · words are a correction. It shows the schedule in words
  and exactly, the first run, and a watch's estimated daily cost when the price
  is known. Save and run it now hands the action back to do in this turn, so
  approvals reach the person and "always" is banked for later runs.
- The runner looks (a command with the provider keys stripped, a bounded file
  listing that says what changed since the last decided look, or a tool through
  the person's own gate with its connected account put to use first), judges
  every look on the low-tier seat and bills it to the usage ledger against the
  automation, and runs work as a session built from the person's own config:
  their approval rules with nobody to ask, the first refusal for want of the
  person stopping the run as your call, a money cap, and an outcome that comes
  only from `automation_report`.
- File watches keep a small memo between looks so a change made while codeaf
  was closed is still seen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory always: the store keeps rules as memories marked always

- A memories column, always_on, declared for new stores and added to old
  ones by migrateMemoriesAlways inside one immediate transaction, with a
  partial index over active rules; the owner migration now checks its
  column inside its transaction too, so two opens of one old store at
  once cannot both alter the table.
- memory_always events set and clear the flag; the add and supersede
  payloads carry it, a supersession keeps the retired row's rule, and the
  replay and the sync fold both apply it.
- The write door promotes a remembered row when the same words are asked
  for always, and never demotes a rule.
- AlwaysMemories reads the rules for named owners in one order (person,
  machine, project; oldest first); rules are no longer router candidates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the clock process, window presence, and no timers on the machine

- `codeaf clock` (hidden machinery, like `engine`) is the one process that runs
  automations: it takes the lock or leaves, builds each run's configuration
  through the same assembly a conversation uses (extracted from openV3Launch as
  v3ConfigFor, which finds no conversation and starts nothing), runs keyless
  when there is no key so reminders still work, and retires when its binary is
  replaced and nothing is running.
- Every window holds presence for as long as runSurface runs and starts a
  clock when none holds the lock, checking again every 30s.
- Conversations get the automations door, one store handle per process.
- Every start of this build takes the old launchd/systemd timers (tick and
  wake, both names) off the login, acting only on definition files it finds
  under HOME. `codeaf tick` stays as a hidden verb that does nothing, so a
  timer an older build installed ends quietly until it is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the window side — the card, the run lines, notifications and the store watcher

Every window reads the automations store on a two-second beat: a finished
run draws one dim line in the conversation that made it, the first window
on the machine to claim a run raises the desktop notification, and the
quit question gets its snapshot. Local windows hold presence themselves;
the doors to another machine will hold it there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory always: rules ride every turn and task, /always, the memory place's a

- Session: remember gains always; a rule settles through the one write
  path and lands as a preference; an ordinary line never sees, rewords or
  retires a rule; /always (RememberAlways) asks nothing; AlwaysMemories
  lists the rules in force; a conversation with no project of its own
  keeps its rules for the person and says so.
- The model's own rule is a question: internal/approval's KeepsARule is a
  floor under every allow (and under no policy at all), no memo or
  guardian answers it, its card offers no standing yes, and with nobody to
  ask it is refused in words the model can act on.
- Injection beside the standing orders: an <always> block in message[0]
  (no ages, stable order, 12 rules / 2,400 runes, kept on a failed read,
  lean profiles included), task briefs, plan-born run workers and
  codeaf do; rules skip the binding reads and the memory tidy.
- Wire: Memory.RememberAlways, Memory.Always and Memory.SetAlways.
- TUI: /always and its listing, an always fact on rule rows, a on the
  strip and the foot, and the card.
- Manual: three sections on what-i-remember and the probes that ask how
  to make a rule; PERF.md records the rules block's bounds.
- The fixed prefix is over its cap by 152 bytes on this branch alone; the
  waivers are not raised (the standing deletion pays it back).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: delete the v1 resident's scheduler

The v1 resident kept a scheduler nothing could reach: no surface has made a
charter since Agent-Field#469, and no production code installs its timer. It still ran
through `codeaf wake`, advertised in --help as "run one full background pass
by hand", and doctor read its timer. docs/design/automations/DESIGN.md
replaces all of it; this removes it.

Gone:

- internal/resident: the watch engine and sentinel seam (watch.go), charter
  commands and the timer offer (charters.go), tenure, the practice loop, the
  retrospective's charter proposals and hygiene nudges, and every hook in the
  tick, the quiet gate and the splice (the charter draft on compile).
- internal/store: charter.go, charter_spec.go, tenure.go, standing_watch.go,
  the practice loop's half of practice.go and the dead notebook reads, the
  charter command kinds, the proposal-appetite trait and the "proposals"
  reversal class, and the three charter question categories.
- internal/head: the temporal compiler (standing.go), the charter half of
  question answering (charters.go, now answers.go), rule targets in
  change/stop/recall/open/status, the `standing` belt read, and the money
  rails for standing proposals.
- internal/watchdog, and internal/registry (no importers at all).
- cmd/codeaf: wake's pass, the sentinel client, the doctor rows `background
  timer` and `standing`, and the wiring in resident_build.go.
- config: practice_budget_usd, practice_idle, brief_after and tenure_after,
  their rows, resolvers and env-table lines; the keys are retired, so a
  profile holding one is not told it is ignored.

`codeaf wake` stays dispatched as a hidden verb that does nothing and exits 0,
because a timer an older build installed keeps running it until the first
start of the automations build removes it, and an unknown verb exits 1.

Old stores still open and rebuild. Every v1 event stays in the journal and
replays as an unknown kind; the `charters` table is no longer created but is
never dropped, and nothing reads it beyond a tolerant lookup naming an older
build's work in a self receipt. nodes.charter_id, Provenance.CharterID,
agent_questions.origin_charter_id, the brief's charter item and the practice
rounds' question replay all stay, because removing them is a migration that
says nothing new. A charter question an older build left open lapses on the
next pass, and a queued charter command is refused in a sentence rather than
by its spelling. internal/store/legacy_scheduler_test.go pins both halves.

The resident's own manual loses standing-goals.md and says what happened in
daily-rhythm.md; the chat manual's `codeaf wake` section becomes one short
section with `codeaf tick`, and the doctor section loses its timer row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: over --host and --at — the far machine's store on the wire, and its clock kept for an attached window

A window over --host or --at sits in front of a conversation on another
machine, whose automation tool saves into that machine's store and whose
clock runs only while a window is open there. So such a window now reads
and changes the far machine's automations, and the far clock counts it.

- Protocol 21: thirteen Automations.* calls, one per tui3.AutomationsSeam
  function, answered from the engine machine's store; Welcome.Automations
  and Welcome.AutomationsZone say the engine has them and in which zone a
  rhythm is read; Hello.Window says the connection is a window.
- The engine door supplies remote.EngineAutomations (the store, the zone,
  and keepAutomationsWindow as HoldWindow). A Window hello holds presence
  before it is welcomed and lets it go on every road out; an engine with no
  store advertises nothing and refuses every call.
- hostOptions builds the seam from the client when the welcome says so,
  and the zero seam otherwise. Only the --host and --at boot dials say
  Window; the beside dials and the local engine road never do, which a
  structural law now holds.
- Manual: a section on which machine runs them and when they stop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: the v1 scheduler is gone

docs/STANDING.md opens with a note saying what was removed, what an older
store keeps, and that docs/design/automations/DESIGN.md replaces it; the rest
of the page is left as history.

docs/FEATURES.md loses the entries that described the removed machinery as
working: the timer offer, charter proposals, the practice loop, probation and
tenure, and `codeaf wake`. The entries that only mentioned it (the modality
rail, the retrospective, the arrival brief, curiosity receipts, competence,
doctor and the environment list) now say what the code does. docs/LIMITS.md
drops the practice carve-out row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the demo home seeds automations, not standing orders

cmd/codeaf-demo-home now makes seven automations across its three projects
(two reminders, the morning sweep on a cron line, a dependency bump in a
separate worktree, a command watch on CI, a once-watch on pricing.json, and a
paused one) and replays the clock over a fortnight of open windows, so their
runs carry every outcome, late catch-ups, and run folders that are really on
disk. Three were said in seeded conversations, whose Words are turns those
journals have, so their lines land when those conversations open. The spend
ledger books each run's own cost under UsageLine.Automation in place of the
standing check line, and the package no longer imports internal/standing.

internal/automation gains Store.SetClock: the one door a fixture needs to
write a history through Create, Take, Start and Finish at the moment it
happened. Nothing in the product calls it.

A demo launch, and the hand-open line it prints, set CODEAF_NO_AUTOMATIONS=1,
so no window opened on the demo starts the clock: the seeded work would
otherwise run for real on the person's own key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the place, the typed command and the quit warning; standing leaves tui3

/automations replaces the standing place in the same slot: the list with
each automation's schedule and last run, its history, and → run, pause,
stop, edit, delete and open. The exact typed forms add, edit, run, pause,
resume and delete, and an add or edit ends on a card like the model's.
Leaving the last window with a run in hand warns once.

Home's next panel and since-you-left read automations; ask here keeps
working, re-homed to its own errands folder, with the automation card in
its pane. Every standing surface in tui3 is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the hints teach automations, and the standing words leave the place prose

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the standing engine leaves the session, the doors and the wire

- internal/session: the stand tool, its card, inbox, world section, mark
  and runner are deleted; the belt fact and the page's paragraph now name
  `automation`, and the prefix waivers fall to the measurement
- automation runs borrow the brain read-only, as standing firings did
- the memory tidy rides the clock (automation.Tidy), gated on MachineIdle
- the sweep's fourth rule reaps stale errands and keeps any an automation
  was saved from
- an automation card answered from another window takes every key its own
  card drew (`2` was refused at the doorstep)
- cmd/codeaf: the ticker, the background repair, the standing seam and the
  host's standing cache are gone; the clock carries the tidy
- remote: Standing.* methods, ResolveStanding and the marked submit leave the
  wire; the held kind goes; the surface-door ledger drops to 18
- config: the background-checks row is retired with its key
- run: worker briefs close on the person's rules alone

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: watch names the idea, and the standing purpose goes

- `watch`'s description said lasting checks were `stand`'s, a tool that is
  gone; it now names an automation without naming a verb some belts lack
- the unused standing-check call purpose leaves; the replay tool still
  reads old logs that carry it
- the fixed prefix falls another 8 bytes and its waiver with it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the docs say what was built

- the design doc gains the typed grammar, the → keys, the quit warning as
  shipped, the store's notification claim, the kept `ask here` errands, the
  read-only brain and the tidy on the clock; the watch cost estimate it
  promised was never built and goes
- the guide, the README, FEATURES, LIMITS and the memory docs name
  automations and `/always` where they named standing orders; the README's
  standing screenshot goes with the page it showed
- AMBIENT and STANDING-ORDERS are marked superseded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the kind's answer row is said to be the superset it is

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the reading says why it is handed its palette

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* e2e: automations replace standing orders in the end-to-end suite

Nothing in internal/e2e imports internal/standing or leans on a standing
door any more, so the package builds once those are deleted.

- The host guard proves the old-timer removal touches only the throwaway
  login: testdata/oldtimers runs RemoveOldTimers as a real program behind
  the guard, and the developer's tick and wake files, both names, are
  hashed around it. The guard also stands in for osascript and
  notify-send, and sets CODEAF_NO_AUTOMATIONS=1 on every launch whose
  scenario does not name it.
- The shared world keeps no ambient store and no standing answers; say
  and drain take none, and decline an automation card if one ever comes.
- The three standing lanes are deleted. TestAutomationsE2E replaces the
  three TestTUIE2E subtests that were about standing orders, with no key:
  ask here ending on an automation card (wide and narrow), and a typed
  reminder said by the clock into its conversation, as a notification and
  on the automations place, then the clock leaving on its own.
- The words table drops the standing rows, respells the ones automations
  renamed, and catches up with home's activity heading, the seven-place
  bar and the Spending tab's per-chat label; testHomeShape,
  testAnswerFromHome and testOneSpendFigure read what is drawn now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the tui3 and session test builds compile again

Three test files still named what the standing removal took away, so
neither package's tests built at this commit and no manual gate in them
could run: notice_test.go proved a retirement event that no longer
exists, and the usage tests asked for a spend subject that is now
SubjectAutomation.

- notice_test.go: the gesture is /automations, which fires
  eventAutomationsOpened.
- usage_ledger_test.go: an automation's run writes its line under the
  automation's id, and the rollup names the automation.
- usage_spend_test.go: the automation row and its subject word.

These are byte for byte the versions 61022e9 carries, so the two
branches agree on them when they meet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the e2e timing notes stop promising a standing pass

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the chat manual says what replaced standing orders

The chat corpus still taught standing orders — /standing, the stand card,
keeping an eye, the OS timer and `codeaf tick` — after automations had
replaced them. The chat answers from these pages alone, so it would have
offered a page, a key and a timer that no longer exist.

- automations.md is new: reminders, scheduled work and watches; the card
  and its answers; schedules and cron; /automations and its → verbs;
  history and what a run came to; where the news arrives; the clock that
  runs only while a window is open, missed runs marked late; cost limits;
  worktree or checkout; the typed /automations forms; ask here; rules as
  memories marked always; the automation and automation_report tools;
  where standing orders went; and what automations cannot do.
- keeping-an-eye.md and standing-orders.md are deleted. Every other page
  that described standing — home, keys, commands, places, screen,
  asking-from-home, the remote pages, models-and-cost, questions, tasks,
  what-i-can-do, what-i-remember, hints-and-tips and the rest — now says
  what the automations code does, quoting its strings.
- chat_test.go: the forty-nine probes that reached a deleted page are
  pointed at the page that answers them now, and fifty-six probes are
  added in the asker's words. The rerun and due-time tests follow.
- truth_test.go no longer imports internal/standing: the standing facts
  leave, and automation.DefaultTime and automation.DefaultUSD are pinned
  where the pages quote them.
- asked.go's wants follow the pages; lawregistry_test.go drops the two
  laws filed against keeping-an-eye; session's manual gate opens an
  automations store so `automation` is checked, and a second gate builds
  a run's belt so `automation_report` is too.
- The e2e due-time subtest still reaches home, and waits for the
  sentence that says where an automation's next time is shown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: internal/standing is deleted

Nothing imports it any more: the session, the doors, the wire, the
config, the manual's truth table and the end-to-end suite have all
moved to internal/automation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: home's tip leads with the command

A home tip says a key or a command first, and the one that taught saying
an automation did not, so home's row broke its own law.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: a collection cannot gain a standing item

The kind names nothing codeaf runs any more. A row an older build wrote
can still be found and taken out; the schema keeps the kind so an older
build on the same machine still opens the database.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: a conversation switched to is told what it missed

The missed runs were gathered only on a window's first reading, so a
conversation switched to in a window already open never heard them. Now
any conversation that comes to the front is asked about once, and a run
a window has drawn is not drawn twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the manual says when an errand that went nowhere is cleared

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: gofmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the complexity ratchet holds the automations engine

The ratchet walked the standing ticker and inbox, which are gone; it now
walks the automations clock, store and runner. The run's Work held 25
decisions against a ceiling of 15, so it is split along its own phases:
the run's configuration, the turn watched for the two reasons to stop,
and what it came to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: comments stop describing standing orders as live

Comments in cmd/ and internal/ (internal/standing and internal/e2e left
alone) that still described the standing feature as current now say
what the code does: the automation tool, /automations, `codeaf clock`
and the rules marked always. Historical records are kept, with doc
links to deleted symbols turned into plain text or pointed at the
replacement, and every bracketed link a rewritten comment names
resolves.

Comments only: the token stream of every changed file is identical
outside comments, apart from one stray blank line in effortscope.go's
import block that gofmt removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: a run of work stops at the daily spending limit

Standing firings were held to the person's daily limit; an automation's
work run is shaped like a task, which the turn's check passes over, so
it spent past the limit. It now asks before anything is made for it and
reads `your call` when the limit is reached. A watch's looks are not held
to it: each costs a fraction of a cent, and a refused look is news.

The design doc gets back the watch cost estimate, which is built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the suites pass without standing orders

- home settles its phone shape before it builds again; the band read
  that used to do it went with standing orders
- `/automations` has its fates on home: the place bare, a conversation
  for `add` and `edit`, home's own line for the id verbs
- `automation_report` is communication, and both automation tools have a
  folder-hold disposition; `usd` is a fractional argument
- the dead standing fields, the backlog fold, the held kind, a separator
  and three band orders are removed
- tests that still named standing words, `/standing` as a door or the
  standing ladder stamper now read the automations surface

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: ask an ask-here errand's approvals in its own pane

A call inside home's `ask here` errand that the approval policy grades
"ask" blocks inside the errand's session until that agent is answered.
errandEvent had no case for EventConsentRequest, and nothing else holds
the errand's agent, so the errand sat `working` on a question no screen
drew, and a `1` pressed at it went into the follow-up box.

- The pane now draws the approval under the call it is about, with the
  conversation's own question builder and renderer: `1 allow once` and
  `3 deny`. There is no `always` from the pane.
- The answer goes straight to the errand's agent (ResolveConsent), not
  through the block's door, whose receipt lands above the window's own
  box and whose `c change` would be answered by the window's engine.
- The row says `waiting on you` and sorts first; the live block gives
  way while the turn waits; the call's row keeps `allowed`/`denied`.
- The block's settle guard applies to the approval's digits and clicks,
  so a digit typed into a follow-up cannot allow an unread command.
- Approvals are dropped when the turn ends; several in one step are
  asked oldest first. Results pair to rows by call id.
- errandWait is renamed waitErrand so the harness's waiter law sees it
  and a test can hold a gated stream open.
- asking-from-home.md says all of this, and no longer sends approvals
  to `continue as a conversation`, which ends the waiting call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the word stands on the tab bar, between memory and spend

A place that appeared only while somebody stood in it was a room nobody knew
was there. Eight words need 84 columns, so a narrower frame folds spend and
settings behind more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: one strength, one write door, every line in front of every turn (WIP)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: banners through the terminal, closing pauses, and watches find the key

What the owner's walkthrough of the first build found, fixed:

- A banner is raised whether or not codeaf has the keyboard, so `r` on a
  reminder raises one too. In iTerm2 it is iTerm2's own (OSC 9), in Ghostty,
  kitty, WezTerm and foot the terminal's OSC 777, and clicking it brings the
  tab back. Elsewhere it is osascript with a sound, or notify-send with the
  logo. The first banner on a machine names the sending app once, linked to
  Notification settings. A codeaf-branded macOS banner needs a Developer ID:
  macOS 26 refuses an ad-hoc-signed applet silently.
- A run's line is also said on the message line of home or the place in
  front.
- Quitting never asks. A run closing codeaf cut off is told to nobody and runs
  again, once, when a window next opens.
- A watch's check passes over a model whose service has no key, down to the
  conversation's own model, and names the model and service when none is
  reachable. The clock re-reads the saved key before every run, and follows
  the newest window's environment (a salted fingerprint, never a value), so
  a key exported in one shell and not another no longer decides it.
- The typed form reads `at 6pm`, `at tomorrow 9am` and `at monday 9am`
  without quotes.
- The clock no longer runs the memory tidy, which the memory rework rules out.
- Home's since-you-left whisper no longer says watches run while codeaf is
  shut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the demo's cut-off run is one the person stopped, and the row-zero law starts at 84 columns

A run codeaf closing cut off now picks up again, which the demo's replay of
the rhythm cannot express. Eight words on the bar need 84 columns, so that
is where the bar is promised never to move. The since-you-left whisper says
what happened rather than what landed, which a first look must not claim.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: store, reflex and session tests for one strength (WIP)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: surface tests for one strength (WIP)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: wire, door and seeder tests for one strength (WIP)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the tidy, its clock types and the rule-consent floor are gone

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the manual, the settings row and the last stale seams match one strength

- what-i-remember is rewritten for one strength, the Memory tab, noticing and
  undo; the other chat pages stop describing the router, the always flag, the
  tidy, the shelf key and "memory is off"
- the settings row is "notice things to remember" and applies from the next turn
- the model's forget is seeded allow beside remember: both say what they did and
  /forget takes it back
- a far engine with no store says "memory is unavailable on this machine"
- a live switch to the lean profile keeps the memory block
- the speed and no-change tests read the noticing pass instead of the removed
  pre-turn recall and extractor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the laws, the waivers and the docs follow one strength

- lane.RoleRecall goes with the pre-turn recall: a role nobody calls is owed a
  deletion, and the provider tests that borrowed it use the tool role
- the complexity ratchet watches the memory engine instead of the deleted
  contextual files; learnFromTurn, importMemoryFile and MemorySnapshot split
  under its ceiling
- both prefix waivers fall to the measurement (52,519 and 44,915), in PERF.md too,
  and PERF.md's memory sections describe the one bounded block
- tests that pinned the old shape (lean and tool-less pages dropped memory, an
  explained override became a memory, the settings row needed a restart) now
  pin the new one
- the settings detail leads with what off does, and "remember across chats"
  still finds the row
- docs/memory-architecture.md describes what memory is now; the contextual
  page is a pointer into history; the audit that started this is kept

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: a test name stops spelling the old product name

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the change entry for Agent-Field#1830

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the change entry for Agent-Field#1831

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: a run's whole result is read in its history, and its banner carries it

The run under the cursor in an automation's history shows its full report in
ordinary text, and the history opens on the newest run. A banner says what the
run came to and up to 180 characters of its report, without repeating the title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the Memories tab reads like Activity, and /forget is gone

- the tab is "Memories"; typing "memory" still finds it, and eight tab words
  now need 86 columns
- groups are rows of their own that open on → and fold on ←, never enter; lines
  hang on tree connectors with kept-by and age columns, and a pane reads the
  cursor row out on frames of 110 columns or more
- the words being written on the add row or in a line's editor are drawn in
  that row, with the caret; before, they were typed into nothing visible
- /forget and the undo stack behind it are removed: a person forgets and puts
  back on the tab, the model's forget stays, and every receipt made on the
  person's behalf says "see or forget it on the Memories tab"
- wire Memory.ForgetQuery goes with the command, folded into Version 22

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: the review's fixes

- Deleting an automation stops its run in hand: the clock asks whether each
  run's automation still exists, since the delete takes the run rows (and any
  stop request) with it.
- A rhythm of days that would overflow a duration is refused, not wrapped.
- A files watch's walk checks its budget and its run's context on every
  entry, so a rarely matching glob over a whole home no longer walks it all.
- The judge's client is kept per model and account, so a replaced key is used.
- A window's presence file is locked and written before it is renamed into
  place, so a counter can no longer remove a window being opened.
- `codeaf do` reads the rules before its deadline starts.
- A typed id needs four characters, and the refusal says where the id comes
  from. A ~ folder crosses unexpanded and the store reads it as its own
  machine's home, so a line typed over --host names the far machine's folder.
- Each automation keeps its last hundred runs; forgotten and deleted runs'
  folders go, except a folder holding a worktree, whose changes are the
  person's to review.
- A watch's banner says its own line, not what its look saw.
- The old timers' files are removed before their jobs are unloaded, by label.
- Stale docs and comments; the audit note is dropped from the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the catalogue test reads the Memories tab row

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the bound is the store's, held inside every write

Review fixes for Agent-Field#1830.

- the 4,800-rune bound, the line cost and the quoting move to the store
  (memory_room.go); every door that makes a line active — write, replace,
  reword, move, put back — is measured inside its write transaction against
  every block the owner reaches, so a line kept everywhere cannot overflow
  another project, and two writers with room for one admit one
- duplicates are the target owner's only; a move or a replacement onto words
  already kept lets the copy go instead of keeping two, and a forgotten line
  cannot be moved
- a store an earlier build filled past the bound lets its always rules, then
  project, machine and person lines ride (FitMemories), and the conversation
  says once how many don't fit
- the model's remember and forget act only on a turn the person typed
- a noticed line is said on the standing lane when the turn's stream has
  closed; an ask-here errand is not noticed
- memories_fts stays this release for older engines still running
- the settings row reads one spelling; the tab's foot over an empty filter
  says esc; the project lookup's wire error is no longer "no project"
- stale comments, names and docs; the audit notes and the contextual doc go

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: the guide's tool table names automation, not the removed stand

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: tell a person who had memory off, once per machine, that their lines ride

The owner's ruling on R8: on the first start after the upgrade, a person whose
memory row was off is told once that their N kept lines now ride every
conversation they hold in, with the Memories tab to forget them. The marker is
created exclusively beside the store on that first start whatever the setting
says, so it is said once per machine and never to someone who turns noticing
off later. No line changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: a woken turn does not arm the model's verbs; the person's line does

A turn a task's landing or a job's ending opens is empty and marked a wake, and
it armed remember/forget because it carried no authored mark. The verbs now arm
only on a non-empty line the person typed, on their line drained into a turn
mid-way, and on the follow-up turn started to answer a line they typed in the
last seconds of the turn before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: a line noticed while its conversation is out of sight waits for the person

The keeper holds a conversation's standing lane while another is on screen,
and it drained memory's notices to nowhere. Memory's notices now say they are
in no record (Event.Unrecorded); the keeper keeps them, files them with the
conversation's parked receipts, and coming back draws them there. A turn's own
lines wait for the turn to end, since coming back mid-turn replays them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: a full refusal says whose block it would overflow; an already-kept correction is said

The store's refusal now carries the owner it would overflow, and every door
says it from the conversation's own view: here, for <project>, or in another
project. Over --host the name in the text is compared instead. Noticing a
correction that was already kept lets the old line go and now says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: test the bound on replacement, the unbounded import and a held second writer

A seam inside the room check holds the first writer in its transaction; the
second must not read the room until the first commits, and then finds none.
The test fails with deferred transactions. Replacement is measured, and the
legacy import is kept whole past the bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: words already kept are not put back twice; an unknown project counts nothing

Putting back a line, or rewording one, onto words its owner already keeps
active is refused with store.ErrMemoryKept, and the tab says "already kept".
When the engine cannot say which project this is, the tab takes nothing from
the failed answer and its head row draws no don't-fit count. Tests cover the
tab's refusal words, the no-match hint and the no-project note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: a stub keeps the changelog's contextual-memory link; its terminal recipe goes

CHANGELOG.md links docs/contextual-memory.md, so a one-line page says it is
retired and where its contents are. The terminal acceptance recipe and its
recorder tested the removed evidence layer, and are deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the manual, the change entry and the design say what round two changed

Full receipts say whose block; words already kept are refused on put-back
and rewording; an already-kept correction is said; a line noticed out of sight
waits for the person; a woken turn arms the verbs only once the person types;
an unknown project counts nothing that doesn't fit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* memory: the turn that answers the person carries it on its own message

A stale flag could outlive a failed, declined, vision or abandoned turn and
arm the next task's landing. The drain now reports whether it heard the
person: mid-turn that arms the rest of the turn, and at the turn's end the
follow-up wake's opening carries answersPerson. No agent state is left. A
real-loop test fails a turn after a steer and checks the next wake is not
armed; the keeper's turn notices, the hand-off on the way forward and the
unrecorded mark are each pinned by a test that fails without them. The manual
says an answer to a question on an idle conversation does not arm the verbs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: the change entry records the re-measured prefix waivers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… is refused (Agent-Field#1837)

* automations: a finished reminder moved later wakes again; a late save is refused

Moving a FINISHED one-time reminder later with `e` on /automations could leave
the row finished. Two things combined:

- CommandLine wrote a one-time `at` to the minute, so an `in 2m` reminder
  (seconds precision) came back earlier in the box, and "the next minute"
  could be seconds away.
- Store.Update saved a new one-time moment that had already passed while the
  card was up as the next run; the clock took it at once and the reminder was
  finished again a beat after the save.

Fixes:
- The edit line keeps a moment's seconds (and fraction); parseMoment reads them.
- Store.Update refuses a changed one-time moment that is not after now.
- An edit is refused for a past moment only when it moves the moment, so a
  finished reminder can be renamed.
- automation.NextAfterChange is the one rule both doors' cards use for the
  next run, so the typed edit no longer promises a run to a paused automation
  or a fresh slot to an unchanged rhythm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* changelog: a finished reminder moved later wakes again (Agent-Field#1837)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* automations: review fixes — paused receipt, DST round trip, manual

- A typed edit of a paused automation says "still paused — resume starts it
  again" instead of a next slot it does not run at.
- The edit line writes a moment in a fall-back's repeated hour with its
  offset, so it reads back as the same instant.
- A moment that passed in the current minute is refused to the second.
- The Store.Update comment names errPassed, and that a zone-only change to a
  one-time schedule is refused too.
- DESIGN.md narrowed: only a change is refused at save time; an add answered
  late still runs once.
- Manual: a section on a change refused at save time and renaming a finished
  reminder, the refusal list, and three retrieval probes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…gent-Field#1836)

* tui3: make the switch and plan test fakes safe for the off-loop readers

The -race failures in the held-send tests and the run-stop card test were in
the test seams, not the surface. The kept conversation's watcher calls
Attach from its own goroutine, and the door line asks PlanTasks,
PlanTaskPage and the plan verbs off the loop; the real agents take their
locks for all of these. The fakes did not:

- switchAgent: Attach and its stop guard running/backlog/stops with a
  mutex; tests that flip running while a watcher is live use setRunning.
- planFake: PlanTasks answers a copy as the store does; setStatus writes a
  new slice instead of over the one planAppWith handed the surface; pages
  are guarded, and the program-room test writes them through setPage.

Fixes the four held-send races, TestStopOnTheRunsOwnPageRaisesTheCardAndTheCardStopsTheRun,
and three more of the same class found on dev: TestAProgramRoomReadsTheLandingTheStoreEndedAhead,
TestTheCancelKeyOnAPlanRowEndsItThroughTheStore, TestPOnAPlanRowPausesThenResumes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: change entry for the held-send race fakes (Agent-Field#1836)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: keep the Agent-Field#1836 change entry title under 100 characters

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* tui3: route the plan fakes' test writes through their lock; atomic rail counters

Review follow-ups on Agent-Field#1836:

- railPlanCounter's rows/pages counts are atomic; they are bumped by the
  surface's off-loop reads.
- planFake gains editRows (in place, keeping planAppWith's aliasing) and
  setPlan; every direct test write to fake.plan and fake.pages goes through
  them or setPage.
- switchAgent.mu's comment says exactly what it covers.
- The change entry's invalidates line is cut to the rules' length and drops
  surface for this test-only change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s what it says again (Agent-Field#969) (Agent-Field#1796)

* census: the call row carries the demanded set, so served != lane means what it says again (Agent-Field#969)

Since the chooser demands the whole admitted set (provider.only with
fallbacks off), the router may serve any member of it, so Record.Lane
(the ranked head) stopped being the only machine the request would
accept. The census read served != lane as the router going somewhere we
did not choose, which after the set-demand is often the router choosing
a different member of the set we did choose.

Add Record.Lanes []string, filled from laneChoice.Only when the demand
is still being sent, carrying the whole set when it has two or more
members (a one-machine demand is left to Lane alone). The census counts
asked != served only when served fell OUTSIDE the admitted set.

callrows.Row embeds calllog.Record, so the census reads the new field
the day it lands.

* docs: change entry for Agent-Field#1796 (demanded set on the call row)

* address review: record the demand that actually travelled, not the original choice

ZeroPoint95's review on Agent-Field#1796 caught that demandedLanes read the original
laneChoice.Only, so a rescue (sends only:[B]) and a narrowed retry (drops a
refused machine) recorded the whole original set and misattributed Lane.

Capture provider.only as wirePreferences composed it, at encode time, onto
the trace; carry it onto the openAttempt so a finish row written after a
later attempt re-encoded still describes its own. demandedLanes now reads
that composed set, and recordedLane names the single machine a narrowed
attempt really demanded rather than the ranked head it recovered from.

Adds the two wire-versus-log regression tests the review asked for: a rescue
arm records only its one machine, and a narrowed retry does not keep the set
it no longer demands and attributes to the machine it does.

* address review 2: preserve the demanded set through the hopped close, fix docs

ZeroPoint95's second review caught that closeOpenAttempt clears trace.open
before recording, so a finish row built on the fallback close read the latest
encode's demand rather than the closed attempt's. Carry the attempt's snapshot
through recordFacts.demandedOnly and read it off the ended row in
bodyDemandedOnly, keyed on ended (set only on that row) so an attempt closed
carrying no demand is not handed a later one.

Also remove the stale duplicated demandedLanes doc that still described reading
laneChoice.Only, and correct the Record.Lanes doc: Lane is a member of the set
but not necessarily its first element, since a three-to-two retry can keep the
original ranked head as Lane while Lanes names the surviving pair.

Adds TestAHoppedAttemptKeepsItsOwnDemandedSetThroughTheClose, which fails
without the carry and passes with it.

* docs: clarify call demand snapshot comments

---------

Co-authored-by: ZeroPoint95 <329227198+ZeroPoint95@users.noreply.github.com>
* docs: describe the built-in coding, security and review specialists

* docs: record the specialist README corrections

* docs: refine specialist caveat and unwrap touched prose
@CLAassistant

CLAassistant commented Oct 17, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown
Member

Hey, thanks for the PR. Could you sign the CLA when you get a chance so we can review it?

@pav2345

pav2345 commented Oct 17, 2026

Copy link
Copy Markdown
Author

please Review the PR and let me know sir these is My first PR if I did anything mistake help i can learn

7vignesh and others added 2 commits October 18, 2026 20:39
…nt-Field#1546) (Agent-Field#1842)

* session: the daily spend ceiling binds task and errand turns too (Agent-Field#1546)

railBlockLocked skipped the daily budget check for InTask runners and the
home Errand, and the crew guard that also enforces it is absent under
--one-model and for an InTask agent with no crew. So a chat /task past the
day's limit, and a task under --one-model, kept spending with no refusal
while headless codeaf do and standing firings honoured the limit.

Remove the InTask/Errand carve-out so the daily ceiling is checked on every
turn-starting path. It is a ceiling read of the shared ledger, never a
tally, so a path already held to the crew's own day cap is refused in two
places, never charged twice.

* docs: change entry for Agent-Field#1842 (daily ceiling on every path)

* address review: carry the selected daily budget and spend preauthorization into workers

ZeroPoint95's review on Agent-Field#1842 caught that the daily check read the default
profile for workers (which carry no ProfileDir), so a worker refused under a
higher selected limit and spent past a lower one, and that it re-applied the
limit --yes-spend deliberately lifts.

- Resolve the daily ceiling once at the root (where ProfileDir is set) and
  carry it as Config.DailyBudgetUSD; dailyCeilingUSD reads live from the
  profile for a root and the carried figure for a worker, never a profile at
  an empty path.
- Carry Config.SpendPreauthorized (--yes-spend / CODEAF_PREAUTHORIZE_SPEND=1)
  into workers; it lifts the daily check and nothing else, matching the crew
  guard.
- Thread both through all three worker constructors: the task-child
  (task_run.go), the adaptive-run worker (orchestrate.go) and the plan-run
  bash worker (run.CrewFactory, which gains a preauthorized parameter).

Adds regressions: a worker held to its carried selected budget (higher allows,
lower refuses) not the default, and preauthorization lifting the daily ceiling
while a conversation limit stays in force.

* fix: preserve daily budget authority in roots and task checks

---------

Co-authored-by: ZeroPoint95 <329227198+ZeroPoint95@users.noreply.github.com>
…ld#1877)

* docs: record DeepSWE harness versions and release context

* docs: add DeepSWE version evidence change entry (Agent-Field#1877)

* docs: use public senior-dev lineage and simplify version records
@AbirAbbas

Copy link
Copy Markdown
Collaborator

Thanks for putting this together. We can only review and merge contributions once the CLA is signed, and it's still pending a few days after the bot's prompt, so I'm closing this for now. If you sign it (link in the CLA comment above) and comment here, we'll reopen it and give it a proper review.

@AbirAbbas AbirAbbas closed this Oct 20, 2026
0xMrMasry and others added 3 commits October 20, 2026 13:44
…#1870)

* fix: refresh Codex catalog client version

* docs: record Codex catalog version fix for Agent-Field#1870

* test: cover version-gated Codex discovery through both catalog doors

Pin the backend fixture independently of the shared client version so reverting the advertised version hides newer models in both direct and translated listings. Preserve visibility filtering and cached reasoning metadata.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Abir Abbas <abirabbas1998@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: build valid SQLite file URIs on Windows

* docs: record Windows SQLite URI fix for Agent-Field#1868

* test: preserve POSIX SQLite URIs and cover Windows path forms

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Abir Abbas <abirabbas1998@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…1845)

* fix(session): require explicit work orders for auto tasks

* docs(changelog): add entry for PR 1845

* test(session): tolerate prompt line wrapping

* fix(session): distinguish polite work requests from questions

* test(session): cover routing authorization and stopped-result scope

Separate feasibility questions from explanation requests in the shared route
contract and the searchable chat manual. Pin examples with their complete
classification clauses, and prove stopped-result suppression does not block
fresh requests or normal remediation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(manual): make stopped-result routing reachable through search

Give the stopped-result rule its own section so the manual context includes
both the restart guard and the fresh-human-request exception. Pin the actual
retrieved answer, since merely finding the long tasks page hid the rule beyond
the section limit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(session): judge human steering during stopped-result wakes

Read current-turn person provenance and judge the newly consumed instruction, including a verbatim repeat of the stopped request.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(session): scope stopped-result routing to each delivered task

Keep attempt snapshots and effective obligations distinct even when sibling tasks inherit the same request. Skip result-only cancelled work before classification, and show both readers only the independent eligible work with explicit cancellation boundaries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(session): restore task cancellation from its saved ending

Derive the stopped runtime flag from the persisted stopped ending. Keep restored cancelled results out of routing while ordinary failed results retain repair eligibility.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(session): limit stopped-result guard to unambiguous result-only wakes

Keep dev's routing inputs, floors and rulings whenever a human speaks, results
are mixed, or provenance is incomplete. Skip both classifiers only when every
delivered result is stopped for the current request in a result-only wake.

Remove scoped eligible-work questions, attempt tags and snapshot deduplication.
Retain frozen stop provenance and restored cancellation for the stop-only case.
Preserve untagged news through batching without changing existing owed asks.
Compare production wake, Submit, retry, cancel and recovery probes against dev,
and narrow the manual and change entry to the conservative guarantee.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(session): drain steering before reading routes in stopped-wake tests

Both fresh-human stopped-wake tests submitted a steer from the answer hook but let the turn race the boundary drain, so CI could count one route reading instead of two. Drain inside the hook, as the mixed-result steering test already does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Abir Abbas <abirabbas1998@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@pav2345

pav2345 commented Oct 20, 2026

Copy link
Copy Markdown
Author

Hi, I've signed the CLA as requested. Could you please reopen the PR and review my contribution? Thanks sir!

…gent-Field#1884)

* docs(agents): say CI is real, Spark is private and no key is normal

Agents in cloud sessions kept reporting that the repository has no CI and
no Spark. CI is GitHub Actions and runs on every pull request; gh fails
there only because its token does not authenticate, so the guide now
points at the GitHub MCP tools. Spark is the owner's private machine and
was never a step anyone else owed. Add a when-to-run table so cloud
sessions lean on touched packages instead of repeating it locally, and
drop the PR template's line about the deleted known-red ledger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtiPtzDsHa753NHJBpKYpn

* docs(changelog): add entry for PR 1884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtiPtzDsHa753NHJBpKYpn

---------

Co-authored-by: Claude <noreply@anthropic.com>
santoshkumarradha and others added 9 commits October 20, 2026 18:50
…pers (Agent-Field#1881)

* tui3: /helpers makes this conversation a new team's manager and asks it for helpers

A person deep in a piece of work can now turn it into a team effort with
the conversation that knows the work in charge. `/helpers` (or `+ Make
team` beside Tasks in the right-hand column of a chat in no team) makes a
new top-level team with the conversation in front as its manager in one
store edit, narrows the strip to it, and once the store has taken the
edit sends the conversation one turn: propose two or three helpers, ask,
start nobody until the person agrees.

The person answers in words. Yes is team_start under the usual approval
posture; no is the new team_dissolve, which forgets a team that has never
held anyone but its manager (no member, former member, subteam or
Traffic). It is on the belt only while that is true, for every team
however it was made, and the store checks again under its lock.

Every rule a team made on the Teams page meets is met here: a fresh name,
the next colour, one conversation managing one team and its subteams, and
the store's refusal taking the window back. The manual's team manager
page has the command, when it is not offered, and cancelling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* tui3: /helpers lives in the Teams dropdown, and says what it asks

The column button beside Tasks is gone. The Teams dropdown's last row,
under a rule, is now always `+ New from this session`: it puts `/helpers`
and a default ask in the message box, in place of what was there (kept
on the drafts ring), and sends nothing until the person presses enter.
Because the row is always there, the `Teams ▾` chip shows with no teams
yet; only over --host to an engine without teams is it absent. The
dropdown no longer prints an up/down, enter, esc footer.

Nothing hidden is sent any more. /helpers sends exactly the words after
it, and a bare /helpers sends the default ask and shows it in full in the
transcript. The ask is in the person's words and names no tool; the
model knows it manages the team from the role note every manager gets.

A helper the manager starts opens as a tab on that team's strip: when
the manager is in front, the strip is set to the team's overlay, without
moving focus or the draft.

The cursor no longer lands on the new row when no team is shown, and
the long-list window follows the cursor's line, now that a rule sits
between the teams and the last row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* tui3: + New from this session only with no team selected

With a team's overlay on the strip, the Teams dropdown no longer offers
`+ New from this session`, and /helpers refuses with
`/helpers works with no team selected · clear <team> with × first, or use
+ Add subteam on Teams for a team inside it`. The team /helpers makes is
always top-level, and making it from inside another team's overlay read
as nesting. Nesting keeps its own doors: + Add subteam on Teams, and a
manager's sub-team start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* tui3: a 💡 tip for /helpers

`/helpers makes this conversation the manager of a new team` joins the
tips, after /task's. It is armed only where /helpers would act: after the
first exchange, with no team selected, in a conversation that manages
none, never on home. It retires the first time /helpers is typed or the
Teams dropdown's `+ New from this session` is chosen. The table is
twenty-five tips now, and the hints page says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* session: a conversation that has gone on may suggest /helpers, once

After the person's fourth message, an interactive conversation in no
team is told once, in a note of its own, that it may end a reply with
one sentence suggesting a team when the work is long and has parts a
few helpers could take on at once: "Want to bring in some helpers? Type
/helpers and I'll suggest who." Only the person can make the team; the
note says so, and says never for small work, never instead of the work,
and never again once suggested or declined.

It is a note and not a paragraph of prompts/system.md because the page
is the fixed prefix every request pays for, held under a budget that
only shrinks, and the suggestion only fits a conversation that has gone
on. Tasks' workers, automation runs, home's ask here and conversations
already in a team are never told. The team manager page says why the
chat suggested it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* session: a member a manager started wakes with its brief again

On an ordinary launch every team edit goes through the engine's teams
door, which stamps a new member's join time. A member started by
team_start always joins after the start that made it was written, and
the join bound in the Traffic delivery dropped that start: nothing was
addressed to the member, the start watch never woke it, and helpers sat
idle while the manager reported them running. The unit test missed it
because it added the member with no join time.

A member's own start, addressed to its handle within the start grace,
is now exempt from the bound; every other line from before the join is
still withheld, which the new test also holds.

The default /helpers ask no longer says "Don't start anyone until I
agree", which the memory that notices rules kept as a standing project
rule. It now says what happens next in this exchange: "I'll answer yes,
ask for changes, or cancel."

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: change entry for /helpers (Agent-Field#1881)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* tui3: + New from this session makes the team at once; /helpers asks it

Choosing the Teams dropdown's row used to fill the message box and
nothing else, and the change below was easy to miss. The row now makes
the team the moment it is chosen: this conversation becomes its manager,
the strip narrows to it and the column turns to its Traffic on the same
frame, and the box is given `/helpers` and the ask for the next step.

/helpers now asks the team the conversation in front manages for
helpers, and makes no team. In a conversation that manages none it
changes nothing and says to use + New from this session. A /helpers sent
before the store has the row's team is held until it does, so the
manager's first step already knows it is one. A refused save empties the
box again.

The default ask is one sentence: "Suggest two or three helpers for this
work, and I'll say yes, change them, or cancel." The tip, the one-time
suggestion note, the command rows, the manual and the change entry say
where the team is made now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* tui3: the team + New from this session makes is named after the conversation

The click still makes the team at once, under its folder's name, so the
strip changes on the frame. Beside the line, the naming role is asked
once (five seconds at most, on the cheap naming model) for one or two
words naming the work, from the conversation's title or opening words,
and the team takes the answer.

The rename is a teams-store edit applied only while the team still has
the name it was made with and no other team has the new one, so a name
the person chose here or in another window is never replaced, and a
team that is gone stays gone. A failure, a timeout or an answer that is
not a name leaves the folder's name. The receipt says the final name
once; a name that arrives after it is said on its own line.

session's NameTeamWords asks the short question and cuts to the limit;
NameTeam is the group's three. Teams.Name carries an optional words
field; an engine without it answers the group's question and the
surface cuts the answer to two words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(teams): bind startup briefs to memberships and retire used-team dissolve

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Hotfix of Agent-Field#1887 onto v0.8.0, carrying only that change to main. Connected
services ask for streamed token counts with stream_options.include_usage
(the default router road keeps its existing usage request), an explicit
400/422 refusal of the option retries once and is remembered per endpoint,
and missing text counts gain separately labelled estimates that never enter
billing. Each physical answer keeps its own hashed receipt so a later
provider receipt replaces its estimate in analytics. The relay that accepts
the new usage statuses shipped as website-open-infra Agent-Field#82.

The settings page keeps v0.8.0's resident practice paragraph; the rest of
the change is byte-identical to Agent-Field#1887's head 9445d02 except where dev-only
commits had moved the surrounding lines.

Co-Authored-By: Abir Abbas <abirabbas1998@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The v0.8.1 hotfix carries Agent-Field#1887 alone on top of v0.8.0, so its section
holds that change and the v0.8.0 reconciliation entry (Agent-Field#1825) that was
still loose on main. Both entries leave the unreleased folder here, and
the merge back into staging and dev removes them there too, so the next
roll-up on dev cannot list them a second time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: improve model usage accounting across providers

* docs: keep account-specific references out of usage documentation

* docs: record model usage accounting changes for Agent-Field#1887

* fix: include structured text responses in model usage estimates

* fix(provider): preserve usage roads and unique attempt receipts

Streamed requests had gained stream_options on the default router road,
changing its established wire bytes. Only connected services now ask for
that option, following the actual endpoint after a billing-road change.
The router retains its existing usage.include request.

The usage option had also admitted unrelated streamed 400s to learn(),
and a connected endpoint refusing it with 422 failed a call dev answered.
Keep the old repair eligibility independent of the usage option and retry
only an explicit unsupported-option refusal on 400 or 422. Remember that
refusal for the endpoint without changing unrelated errors.

Cover router and connected wire shapes, fallback and refusal memoization,
dev error/request-count parity, raw-stream single settlement, and the
separation between token estimates and billing. Update the manual and
usage disclosures with the precise roads and refusal statuses.

Ollama 0.9.0 reuses generation ids from a small random range. The former
receipt key collapsed separate answers and reused event ids with different
counts. Include the physical attempt in that key, and carry it through late
recovery so estimates still pair with their measured receipt. Cover repeated
provider ids and raw-stream recovery without duplicate rows or bills.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(telemetry): preserve receipt presence and emit safe token shapes

Previously a caller's status could disagree with emitted components, unsafe
integers could reach the relay, and late receipts could turn absent counts
into measured zeroes. Derive status from safe emitted components, omit invalid
counts and unsafe sums, and require a receipt identity for estimates.

Keep late token presence through a session-bound recorder. Cost-only recovery
emits no token update; one-sided recovery stays partial. Extend the shared
fixture and test its outputs against the relay's validation rules.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(provider): reconcile receipt identity and compose usage repairs

Previously whitespace generation ids split recovery from observation, repeated
tool names inflated estimates, opaque replay counted as input text, and a
metadata-only cut invented observed zero output. Canonicalize the generation
inside the physical-attempt receipt helper and count only honest observations.

Compose independent repairs once each without widening unrelated refusal
handling. Associate usage-option refusal wording with that option. Preserve
late token presence and recovered prices. Keep unmetered direct-service
answers off receipt lookup because recovery retains the original bearer.

Prove native cache aliases match canonical billing counts and token pricing;
no estimate is banked or priced.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: describe measured aliases and usage receipt limits

Previously the disclosure omitted the total-only exception and overstated
measurement and recovery behavior. Describe component-preserving recovery,
canonical receipt identities, safe numeric limits, opaque input, empty output,
and bounded composed repairs.

Record the deliberate native-alias measured-count change and unchanged
no-lookup behavior for unmetered direct-service overflow. Estimates remain
separate from billing, response usage and spend.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Abir Abbas <abirabbas1998@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
main receives Agent-Field#1887 alone as v0.8.1; merging that hotfix here keeps every
later staging-to-main promotion a fast-forward and lets the release guard
find the rc commit on staging. Where staging already matched dev, the
merged files are byte-identical to Agent-Field#1887's head.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Agent-Field#1887 reached dev as 663dfbf and main as the v0.8.1 hotfix. This merge
brings that hotfix's history and changelog roll-up here, so dev keeps
containing staging and main. Code is unchanged; the v0.8.1 section lands in
CHANGELOG.md, and the Agent-Field#1825 and Agent-Field#1887 entries it rolled up leave the
unreleased folder so the next roll-up does not list them again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@pav2345

pav2345 commented Oct 23, 2026

Copy link
Copy Markdown
Author

Dear santoshkumar sir please check these pR let me know any thing wrong which i made Thank You Sir

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.