Skip to content

auxiliary inventory: every request says what it is for, a dead role goes, and the prefix gate weighs the real belt (review needed) - #996

Merged
santoshkumarradha merged 27 commits into
devfrom
fix/auxiliary-inventory
Sep 12, 2026
Merged

santoshkumarradha merged 27 commits into
devfrom
fix/auxiliary-inventory

Conversation

@santoshkumarradha

@santoshkumarradha santoshkumarradha commented Sep 12, 2026 •

Copy link
Copy Markdown
Member

Three findings from an audit of this build's auxiliary machinery. All three are
things the code reports about itself incorrectly; none changes what any model is
asked or answers.

1. RoleCompaction was a priced dial wired to nothing

Agent.compact takes _ context.Context and makes no model call — a compaction
is two mechanical rearrangements of text the session already has, and the
summariser was deleted long ago. RoleCompaction survived it: a constant, a
TierHigh assignment, a description, and a row on the Providers tab offering a
person a model for a call that never happens. TierHigh's own five-minute
patience was derived from "a compaction summary over a full window".

Deleted, with the settings and manual text that offered it, and the patience
re-derived from the auditor — the call on that tier that really does read a
great deal before it writes.

2. Nine of 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. 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 forget, and forgetting it is silent.
It is an argument now — callPurpose — so a call with no purpose does not
compile, and the door is the only thing in the package that calls
provider.WithCallTag (the turn, the ask tool and the errand ladder each used
to stamp their own).

auxiliary.go's claim that callRole is "the one door all auxiliary provider
calls pass through" was the false one, and now says what is true: it is the one
door every errand passes through, and it names what is legitimately not an
errand and why.

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

3. The prefix gate weighed a belt nobody runs

fixedPrefixBudget's doc comment has always said it bounds "the belt the
shipping conversation door assembles". The fixture under it had no memory store,
no accounts hub, no standing items and no saved programs: 18 tools, 39,073
bytes, green with eight kilobytes to spare. A configured machine carries 23 and
weighs 53,025 — 5,025 over the 48,000 cap the gate was enforcing. Invisibly,
every day. Same shape as #576.

The gate now weighs the belt shape named shippedBeltShape, the same "fully
wired" definition prompt_belt_test.go uses, through one shared helper, and
both arms — fixed and lean — weigh the shipped shape rather than a thin fixture. The figure becomes a
ratchet at the measurement with no headroom, 48,000 stays as the stated target,
and the test prints the shortfall on every green run.

The 5 KB is filed, not fixed: stand alone is 9,607 bytes, more than a
quarter of the tool block and nearly twice the next heaviest. Trimming a schema
changes what the model is told, not a byte count, so it belongs to whoever owns
the standing belt.

Seams reported, not worked around

  • Three roads build their own provider.NewClient — the standing sentinel,
    the memory tidy-up and the document reader — and never touch this door, so
    they are outside the errand ladder. They have no *Agent to reach it through.
    They are no longer outside the PURPOSE law: it now requires withPurpose on
    every own-client completion, per call, and the negative was planted on the
    Spark (each of the three stripped in turn, each failing by file:line).
  • Four of the nine are genuine errands — guardian, shaper, spell-out, intake
    — that could go through callRole and gain the ladder's fall-through, the
    tier's patience and a journal row. That asks a second model on failure, which
    is a behaviour change and does not belong in a fix PR.
  • subharness_intake builds its window on context.Background(), so a
    person's interrupt does not cut it. Fixing it is part of the callRole
    conversion above.

What this PR deliberately does not do — issue #1010

An altitude review found the shape is one level too shallow in three places, and
all three are cross-package. They are filed as #1010, with #928 as the
spine
, and they mostly DELETE what this PR adds:

  1. The purpose belongs on the wire, not in a package convention. This PR's
    law can only see internal/session; five other packages reach a model by
    other roads. provider.NewClient taking the purpose, and calllog.Record
    refusing an untagged row, deletes withPurpose, purposeInherited and the
    own-client second pass entirely, and makes the law one sentence module-wide.
  2. calls.jsonl records the call site's tag but not the lane.Role it was routed under, so a watched task step and an unwatched one are the same row #928: lane.Role rides on the record. cmd/aforge-replay's roleOf
    table is a hand-maintained join this PR had to grow by four rows and correct
    twice. provider.WithCallRole beside WithCallTag deletes the table — and
    with it the one rename this PR carries, purposeSentinel = "standing-check",
    which moves a word collision rather than fixing it.
  3. The prefix variance is removed, not measured around. widestBelt,
    bare.WidestGrepDescription and the two normalisations in
    prefixbudget_test.go exist only because grep and load_capability say
    different sentences on different machines (169 bytes). One description
    everywhere with the machine fact delivered at runtime deletes all of that.
    That one needs the belt's owner: bare's stated law says the opposite.

Both holding actions now name #1010 where a reader meets them
(clientdoor.go's purposeSentinel, prefixbudget_test.go's residue note).

Proof

  • go build ./... && go vet ./... clean.
  • Laws, manual, roles, session, tui3 and cmd suites on the Spark (see comment).
  • Real-model run on deepseek/deepseek-v4-flash checking calls.jsonl has a
    role on every auxiliary row and none untagged (see comment).

🤖 Generated with Claude Code

santoshkumarradha added a commit that referenced this pull request Sep 12, 2026
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@santoshkumarradha

Copy link
Copy Markdown
Member Author

Evidence

Laws gate (Spark, be3e2c866)

scripts/laws.sh green across the tree, including the two that this change is about:

ok  github.com/Agent-Field/aforge-v2/cmd/aforge-replay   0.493s
ok  github.com/Agent-Field/aforge-v2/internal/session    6.920s
ok  github.com/Agent-Field/aforge-v2/internal/tui3       8.787s
...
QUICKPKGS
ok  github.com/Agent-Field/aforge-v2/internal/roles      0.003s
ok  github.com/Agent-Field/aforge-v2/internal/manual     1.699s
ok  github.com/Agent-Field/aforge-v2/internal/e2e        0.332s
ok  github.com/Agent-Field/aforge-v2/internal/iconlaw    0.191s

The laws run found a real sibling before I did: TestEveryTagTheBuildWritesResolvesToARoleSomebodyDeclared walks the tree for provider.WithCallTag, and internal/session has exactly one of those now (the relay line inside the door). It is taught to read the door's own call sites instead — and it immediately named the two calls that had just been given names for the first time, handoff-draft and subharness, neither of which anything priced. Both now have a row in callSiteRoles.

The new law bites

Deliberately blanking one purpose:

--- FAIL: TestEveryRequestThroughTheDoorSaysWhatItIsFor
    nohiddenwork_test.go:388: guardian.go:177 reaches the wire with no purpose.
    Every request this package makes says what it is FOR, because a tag that can
    be forgotten is a tag that will be — and the call log cannot say what a build
    spent its night on (clientdoor.go's [callPurpose])

Real-model runs (Spark, deepseek/deepseek-v4-flash, make build binary at be3e2c866)

Three isolated AFORGE_HOMEs, aforge chat --once, reading the resulting logs/calls.jsonl:

home prompt rows untagged tags
/tmp/afh996 read go.mod; a wide "review every package" ask 62 0 turn 50, reflex 8, title 4
/tmp/afh997 propose a task + run bash 30 0 turn 21, markreader 5, title 2, caption 2
/tmp/afh998 describe an image at a path 41 0 turn 17, tool:view_image 10, title 6, tool:read 6, caption 2

133 rows, 0 untagged. tool:view_image and tool:read are the ask-tool path whose tag moved from its own WithCallTag to the door, and it still writes the same word; markreader and caption are errands through callRole, whose role still becomes the tag.

Honest limit on the live evidence: these runs exercise the turn, the ask tool, the errand ladder, the namer and the captioner. They do not fire the nine paths this PR renamed — guardian, vision, shaper, spell-out, intake, planner, designer, handoff draft, subharness step — each of which needs a configuration or a turn shape a headless --once does not reach (an approval gate with a guardian model, a model that cannot see images, a turn long enough to hit the ceiling, the TUI's ctrl+r). What covers those is the structural law, which walks all eleven call sites and fails the build on any of them going blank; that is a stronger guarantee than any one live run, but it is a different kind of evidence and worth saying so.

Prefix gate

the fixed prefix is 53025 bytes (~13256 tokens): prompt 19114 + tools 33911 over 23 tools
it is 5025 bytes over the 48000 target — see this file's head for the bill and who owes it
--- PASS: TestTheFixedPrefixStaysUnderItsBudget
--- PASS: TestTheLeanPrefixStaysUnderItsBudget   (the lean prefix is 31341 bytes over 12 tools)

Every shape weighed, for the record:

shape tools tool block total
a conversation that remembers (shipped) 23 33,911 53,025
a conversation with memory off 18 19,993 39,107
a task node that may fan out 19 26,232 45,346
a worker with inherited reads 16 17,822 36,936
a lean conversation on a small window 12 14,243 33,357
(the old gate's fixture, v3ShapedAgent) 18 19,959 39,073

One red, and it is not this branch's

The full internal/session suite under load reported:

--- FAIL: TestC1AProposalCannotPutRepositoryWorkInTheCheckout (0.33s)
    testing.go:1464: TempDir RemoveAll cleanup: unlinkat
    /tmp/TestC1AProposal.../002: directory not empty

It is not in .github/known-red.txt. It passes -count=3 in isolation on this branch and on origin/dev (82624af79), and it is a t.TempDir cleanup race — something is still writing into the directory when the test ends — which is the load-sensitive shape CLAUDE.md says to treat as a bug report rather than rerun and move on. Nothing in this PR changes any goroutine's lifetime, so it is not from here; I am reporting it rather than absorbing it. A dev-under-load run to confirm it reproduces on the base is in flight.

@santoshkumarradha

Copy link
Copy Markdown
Member Author

The session red is dev's, and it is a load-sensitive family rather than one test

Follow-up on the red I reported above. I ran the full internal/session suite on origin/dev (82624af79) on the same box under the same load, and it is red too — with a different test:

branch full-suite internal/session isolated -count=3
fix/auxiliary-inventory (be3e2c866) --- FAIL: TestC1AProposalCannotPutRepositoryWorkInTheCheckout — TempDir RemoveAll cleanup: unlinkat …/002: directory not empty pass
origin/dev (82624af79) --- FAIL: TestHandingTheSameDecisionOverTwiceSaysItIsAlreadyHandedOver pass

Two different tests, neither in .github/known-red.txt, both green in isolation, both red only when the whole package runs. That is the shape CLAUDE.md names as a bug report rather than a known red — but it is pre-existing on the base, it is not the same test twice, and nothing in this PR changes any goroutine's lifetime or any temp-directory handling.

So: not this branch's, reported rather than absorbed, and no line added to known-red.txt. Worth its own issue — the varying identity of the failing test is the interesting part, and it points at shared state or a cleanup race rather than at either named test.

Everything this PR is actually about is green: the laws gate, internal/roles, internal/manual, internal/e2e, internal/iconlaw, cmd/aforge-replay, and internal/session + internal/tui3 inside the laws run.

Live tag evidence, final tally

The two longer runs finished and one of them started a real task, so the node's own turns are in the log:

home rows untagged tags
/tmp/afh996 62 0 turn 50, reflex 8, title 4
/tmp/afh997 47 0 turn 22, task 9, markreader 6, reflex 6, title 2, caption 2
/tmp/afh998 54 0 turn 28, tool:view_image 10, tool:read 6, title 6, caption 2, reflex 2

163 rows, 0 untagged, covering turn, task, title, caption, markreader, reflex, tool:read and tool:view_image — i.e. the turn, a node's turn, the errand ladder and the ask tool, which are the three callers whose own WithCallTag this PR deleted in favour of the door. All three still write exactly the word they wrote before.

santoshkumarradha and others added 8 commits September 12, 2026 10:07
…ed 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) <[email protected]>
…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) <[email protected]>
…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 #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) <[email protected]>
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
…s 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) <[email protected]>
…isters

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) <[email protected]>
…ritten 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) <[email protected]>
santoshkumarradha and others added 14 commits September 12, 2026 10:17
…in 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) <[email protected]>
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) <[email protected]>
`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) <[email protected]>
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) <[email protected]>
… 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) <[email protected]>
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
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) <[email protected]>
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) <[email protected]>
…g 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) <[email protected]>
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) <[email protected]>
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) <[email protected]>
… 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) <[email protected]>
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
…ent 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) <[email protected]>
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 #1010 (spine: #928) as the place it is deleted.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
santoshkumarradha and others added 3 commits September 12, 2026 11:35
…n'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) <[email protected]>
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.

1 participant