DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path change-requests/cr-2026-235-remove-the-unconsumed-rendering-roster-role-v0_1.md

CR-2026-235 — Remove the Unconsumed rendering Roster Role — v0.1

Version: v0.1 Date: 2026-08-24 Status: Drafted, not pre-flighted. Markdown primary — the consumer is Claude Code. Occasion: standing-notes/loomworks-scoping-the-roster-exception-v0_2 §3b — nothing resolves the rendering role, and Test 8 froze a row that governs nothing by construction. Runtime direction screen: No conflict — this removes a seeded identity with no consumer. No act becomes possible or impossible; no authority changes hands. It deletes an implication the system cannot honour. Independent of: the roster-exception ruling (5a / 5b / 5c). This is correct under all three, and mandatory under 5c. 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; R1.


Plain-language summary

The identity roster carries four roles. Three are used. The fourth — rendering — is resolved by nothing, anywhere.

That would be harmless if it were invisible. It is not: it is a freezable row named for the thing everyone assumes it governs. Freezing it reports success, changes a status, and stops no render — because the render path has never asked it anything. The misleading part is not the ungoverned render path. It is a control that looks like it reaches one.

This removes the role. Two code sites, one new migration, no test changes.

One thing this does not do: it does not make rendering governed. It removes a false suggestion that it already is. After this, the render path is visibly ungoverned rather than apparently governed — which is a better state to be in while the real question is still open.


1. What is true today

Nothing resolves the rendering role. Three concrete role literals reach the roster's entry points across src/:

| Role | Resolved by | |---|---| | seed_induction | identity_roles.pyapi/deps.py, converse.py ×2 | | render_dispatch | render_specialist_bootstrap.py | | shaping | shaping.py, via mint_session(role=SHAPING_ROLE) | | rendering | nothing |

Every other reference is a pass-through variable — resolve_agent_identity(role), check_grantability(session.role) — 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.

Every other "rendering" literal in the codebase is a different concept sharing the word: a room name, a spend-pause room, a prompt. None touches agent_identities.

2. Why it is worth removing rather than leaving

It is a control that appears to reach something it has never touched. Test 8 froze …022702 and rendered anyway. The finding that came out of it is the important one: 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 already records the general form: in an audit system, a control that reports success without acting is worse than no control, because it is the one nobody checks twice. The rendering row is the purest instance — it does not even have the excuse of a wiring gap, because there is no wiring to gap.

And it is a seeded row, which makes it a claim. The roster's own words: a seeded row's UUID is a citation. A citation to an identity nothing consults cites nothing.

3. What this does not do

4. Step 1 — remove the entry

agents/native_agent_roster.py: delete the NativeAgentIdentity(role="rendering", …) block.

Leave a comment in its place, in the same register as the existing comment about render specialists. It should say: the role was seeded by CR-2026-227 in anticipation of wiring the render path to the runtime; that wiring never happened; nothing ever resolved it; it was removed by this CR; and the id …022702 is retired and must not be reused, because a seeded id is a citation and reissuing one would make two things cite the same row across time.

NATIVE_AGENT_ROLES derives from the tuple and needs no separate edit.

5. Step 2 — delete the row, forward

Migration 0116. Delete the agent_identities row with id 00000000-0000-0000-0000-000000022702.

Do not edit migration 0113. Its data literal is history and must stay valid against the code of its own era — the migrations standing note, third instance. The row is deleted forward, never edited backward.

Downgrade re-inserts it, with the same deterministic id and display name, so 0116 round-trips cleanly. Take the values from 0113's own literal, not from the module — the same reason 0113 does not import the roster.

Guard the delete. If the row does not exist, the migration succeeds silently rather than failing — a database seeded after this CR will not have it. If the row exists and is referenced by an agent_sessions or agent_lifecycle_events foreign key, halt rather than cascading. In dev, …022702 carries two identity_frozen lifecycle rows from the 2026-08-23 freeze and Test 8 — see §6, which is the one genuinely open question in this CR.

6. The one open question — two lifecycle rows point at it

agent_lifecycle_events currently holds exactly two rows, both identity_frozen, both naming …022702.

One is the unexplained 2026-08-23 freeze by an actor that resolves to nobody. One is Test 8's freeze by Marvin, whose unfreeze left no record.

agent_lifecycle_events.agent_identity_id is a foreign key to agent_identities. So deleting the row either fails, cascades, or requires those rows to go.

Both of those rows are evidence — one is the arc's most-cited live instance of an unresolvable actor, the other of the widening leaving no record. Evidence cannot be deleted is already ruled.

So this CR halts on the question rather than answering it. Three shapes, none chosen here:

The first shape is likely right, and it means this CR may be code-only. Do not decide it during execution.

7. Acceptance

  1. NATIVE_AGENT_ROSTER has three entries; NATIVE_AGENT_ROLES follows.
  2. The retirement comment is in place, naming the retired id and why it must not be reused.
  3. test_native_agent_roster_matches_migration_seed passes — it iterates generically and asserts uniqueness rather than count, so it should pass unchanged. If it fails, halt: it means the binding is tighter than believed and the migration must move in step.
  4. Nothing in src/ resolves "rendering" as a role — asserted by a source-contract test, pinned against the roster structure rather than a string literal.
  5. §6 is reported at the gate boundary and not decided during execution.
  6. Suite green with the delta accounted for; ruff clean; mypy against the unchanged baseline.

8. Gates

Explicit path staging; no git add -A; halt before push.

9. Pre-flight

  1. Re-derive §1's table at current main. It comes from a trace at 7136e34; confirm no consumer has appeared.
  2. Confirm the binding test's assertion shape — generic iteration and uniqueness, not a count. If it pins a count, §7.3's halt applies.
  3. Report every reference to …022702 anywhere — code, migrations, tests, fixtures — before touching anything.

DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-235 — Remove the Unconsumed rendering Roster Role — v0.1 — 2026-08-24