rendering Roster Role In Place — v0.2
Version: v0.2
Date: 2026-08-24
Status: Drafted, not pre-flighted. Markdown primary — the consumer is Claude Code.
Changes from v0.1: v0.1 was a removal and could not be executed. Pre-flight found the binding test asserts set equality against migration 0113's literal, not generic uniqueness — so the module entry and the migration row are tied and neither moves alone (§6). And the database refuses the row delete outright: two lifecycle rows reference it under ON DELETE NO ACTION (§7). Ruled 2026-08-24: leave the row. Which means the entry stays too, and the CR becomes a retirement in place — one comment, no migration, no schema change. v0.1 stands as sibling; its wrong premise is preserved at §6.
Occasion: standing-notes/loomworks-scoping-the-roster-exception-v0_2 §3b.
Runtime direction screen: No conflict — comment only. Nothing executes differently. It corrects what a reader believes, not what the system does.
Independent of: the roster-exception ruling (5a / 5b / 5c). Correct under all three.
Cites: standing-notes/loomworks-standing-note-migrations-and-the-code-that-runs-at-another-time-v0_2; standing-notes/loomworks-finding-the-render-path-never-asks-v0_1; standing-notes/loomworks-standing-note-h0-tightening-guarantee-and-its-scope-v0_1; O5, O10.
The identity roster carries four roles. Three are used. The fourth — rendering — is resolved by nothing, anywhere. Freezing it reports success and stops no render, because the render path has never asked it anything.
The first draft proposed deleting it. It cannot be deleted, for two good reasons that only became visible by trying.
The roster is written twice on purpose — once in code, once in a migration — with a test asserting the two match exactly. That test exists to stop them drifting apart, and removing the entry from one side is exactly the drift it refuses. The two halves move together or not at all.
And the database row cannot go either: two lifecycle rows point at it, recording the freezes of 23 and 24 August. Those rows are evidence, and evidence cannot be deleted. Postgres refuses the delete outright.
So the entry stays and gains a comment. The comment says what is true: nothing resolves this role, it governs nothing, freezing it does nothing, and the id is retired and must never be reused.
That achieves what the removal was for. The problem was never the row's existence — it was that a reader meets a freezable identity named rendering and assumes it governs rendering. The next reader now meets the truth at the exact point of confusion, at no schema cost and with no evidence disturbed.
Nothing resolves the rendering role. Three role literals reach the roster's entry points across src/: seed_induction, render_dispatch, shaping. Every other reference is a pass-through variable, and since shaping.py is the only caller of mint_session, session.role can only ever be "shaping". No route, config or request supplies a role string. Confirmed by AST at 7136e34.
Every other "rendering" literal in the codebase is a different concept sharing the word.
It is a control that appears to reach something it has never touched. Test 8 froze …022702 and rendered anyway. The finding: the freeze's success report is not a lie — it set the status and ended the zero sessions it had, and never claimed anything about rendering. The claim was supplied by a shared noun.
An unused row is cheap. A misleading one is not. The tightening-guarantee note records the general form: a control that reports success without acting is worse than no control, because it is the one nobody checks twice. This is the purest instance — it does not even have the excuse of a wiring gap, because there is no wiring to gap.
A comment fixes what a deletion would have fixed, because the harm is in what a reader infers, not in the row's existence.
agents/native_agent_roster.py — keep the NativeAgentIdentity(role="rendering", …) entry and add a comment directly above it, in the register of the existing render-specialist comment. It must carry, in the module's own voice:
src/, verified by AST at 7136e34.check_grantability, mint_session, require_live_session and resolve_agent_identity have zero references across the 28 files of that path. Render specialists hold per-binding identity in render_specialist_binding and are deliberately off this roster.loomworks-finding-the-render-path-never-asks-v0_1.agent_lifecycle_events rows that are evidence and cannot be deleted, and the module entry is bound to migration 0113's literal by test_native_agent_roster_matches_migration_seed. Neither half moves alone.…022702 must never be reused. A seeded id is a citation; reissuing it would make two things cite the same row across time.Nothing else changes. No migration. No schema. No test. No behaviour.
render_dispatch, which is resolved and correct.NATIVE_AGENT_ROSTER still has four entries; test_native_agent_roster_matches_migration_seed passes unchanged.src/ resolves "rendering" as a role, pinned against the roster structure rather than a string literal. This is the durable half of the CR — the comment states a fact; the test keeps it true. If someone later wires rendering, the test fails and the comment gets deleted in the same change, which is exactly the intended coupling.Preserved rather than smoothed.
v0.1 §7.3 said the binding test iterates generically and asserts uniqueness rather than count, so it should pass unchanged.
It asserts set equality. test_native_agent_roster_matches_migration_seed parses _SEED_ROSTER out of migration 0113 with ast.literal_eval and asserts from_module == _migration_seed_roster(). Removing the module entry fails it immediately, demonstrated rather than reasoned:
Extra items in the right set:
('rendering', 'Claude (rendering)', '00000000-0000-0000-0000-000000022702')
The trail, since it ran through three documents. A trace report described the roster's uniqueness test — test_roster_entries_are_unique_on_role_and_id — and took it for the binding test, which sits directly above it. That sentence went into loomworks-scoping-the-roster-exception-v0_2 §3b as zero test changes, and from there into this CR's §7.3.
Two documents inherited it and both need correcting beside (§7).
The lesson, and it is this arc's fifth of the same kind: two tests in one file, one named for what it checks and one named for what it protects. The uniqueness test says what it does. The binding test's name — matches_migration_seed — says what it does too, and it was the neighbour that got read. Reading the file is not the same as reading the test, which is the same shape as reading construction sites and not what they refer to.
The test is working exactly as designed. Its own docstring: the roster is written twice deliberately and this test is what makes the duplication safe rather than a drift hazard. It refused the drift v0.1 would have introduced. That is the halt being caused by a control doing its job, which is the best kind.
Filed beside, never over.
loomworks-scoping-the-roster-exception-v0_2 §3b says removal costs two code sites, one new migration, zero test changes. All three are wrong. It is one code site plus a comment, no migration, and the entry cannot be removed at all — because the row cannot be, and the binding ties them.cr-2026-235-…-v0_1 §7.3 carries the same claim.
8a. The row cannot be deleted, and the database says so first. Two lifecycle rows reference …022702, both identity_frozen — the unexplained 2026-08-23 freeze by an actor resolving to nobody, and Test 8's freeze by Marvin. Zero agent_sessions rows reference it.
Both foreign keys into agent_identities are ON DELETE NO ACTION. Probed in a rolled-back transaction:
ForeignKeyViolationError: update or delete on table "agent_identities" violates
foreign key constraint "fk_agent_lifecycle_events_identity"
Nothing cascades, nothing silently succeeds. v0.1 §5's instruction to halt rather than cascade could never be reached — Postgres halts first.
8b. Of v0.1 §6's three shapes, only one requires no affirmative act. Loosening the foreign key or deleting the lifecycle rows each require doing something; leaving the row requires nothing. Ruled: leave the row. Loosening a foreign key to remove an unused row would be the discard collision's shape arriving in a second place, for no gain.
8c. Every reference to …022702: two in the engine — native_agent_roster.py:79 and 0113_….py:70 — zero in tests or fixtures, and seven across five documents in loomworks-record, all describing it and none depending on it.
7136e34.
DUNIN7 — Done In Seven LLC — Miami, Florida
CR-2026-235 — Retire the rendering Roster Role In Place — v0.2 — 2026-08-24