DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path change-requests/cr-2026-235-retire-the-rendering-roster-role-in-place-v0_2.md

CR-2026-235 — Retire the 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.


Plain-language summary

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.


1. What is true today

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.

2. Why this is worth doing at all

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.

3. The change

agents/native_agent_roster.pykeep 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:

Nothing else changes. No migration. No schema. No test. No behaviour.

4. What this does not do

5. Acceptance

  1. The comment is in place, carrying all six points at §3 — especially the retired-id point, which is the one most easily lost.
  2. NATIVE_AGENT_ROSTER still has four entries; test_native_agent_roster_matches_migration_seed passes unchanged.
  3. A source-contract test asserts nothing in 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.
  4. Suite green with the delta accounted for; ruff clean; mypy against the unchanged baseline; no migration.

6. Correction from v0.1 — and the error trail

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.

7. Two corrections owed elsewhere

Filed beside, never over.

8. What pre-flight established, folded in

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.

9. Pre-flight

  1. Re-derive §1 at current main. No consumer should have appeared since 7136e34.
  2. Confirm §5.3's test can be written against the roster structure rather than a string, per the literal-pinning note.

DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-235 — Retire the rendering Roster Role In Place — v0.2 — 2026-08-24