Version. 0.1
Date. 2026-08-07
Author. Claude Code (Wave 0 session). Operator: Marvin Percival.
Charter. standing-notes/dunin7-standing-authorization-charter-v0_1.
Scope. Wave 0 investigation 0.3 of standing-notes/loomworks-development-schedule-v0_1. Read-only. No code changed.
Read against. Engine loomworks-engine at afdeff5; stele, stele-agentic-id, grantha read read-only.
Status. Decision note. This is the one item the schedule does not pre-decide (loomworks-development-schedule-v0_1:38). CR-2026-189 waits on one Operator word on §5.
When no agent is named, the engine mints a UUID on the spot and stamps it into the permanent record as the actor that did the work. Verified sites:
| Site | What it does |
|---|---|
| api/deps.py:331 | SeedInductionAgent(llm, agent_id=uuid.uuid4()) — a new agent identity on every HTTP request |
| api/routers/considerations.py:202 | id=body.agent_actor_id or uuid.uuid4() — client-suppliable, falsy-coalesced to an invention |
| engagement/render.py:227–229 | agent_actor_id=uuid.uuid4() and instruction_set_id=uuid.uuid4() — an instruction-set id pointing at no instruction set |
| orchestration/routers/converse.py (3 sites) | same pattern, hand-rolled |
It is declared, not accidental. api/schemas.py:4256 documents it to API consumers: "when omitted a fresh agent identity is allocated for the run." That is exactly why B-39 was struck from B-25 and opened as its own item — B-25's test is untrue record writes, and a documented allocation is not untrue. It is, in the build list's own words, "a record naming an agent that never existed."
Two structural facts make it stick. First, memory_events.actor_id is a bare UUID with no foreign key — verified: zero ForeignKey declarations on any actor_id across every migration. Nothing has ever been able to check it. Second, the engine's own law says there is exactly one sanctioned way to write an actor it cannot name (memory/base.py:164: "Anywhere else, an unnameable actor is a defect") — and these sites are not it.
And it is permanent. Memory is append-only; every invented id already written stays written. Whatever is decided applies going forward only.
Provenance.wasAttributedTo is required (base.py:194). You cannot fix this by writing no actor.NON_HUMAN_ACTOR_KINDS is rejected at commit and discard (assertions.py:284, :515, :601). Changing an actor's kind changes who may commit — so no option may quietly re-kind an agent.src/, all of them comments. It is a named future occupant of two function bodies, and the one that takes an actor (authority/authorizer.py:119) has never been passed an agent. So no option can lean on GRANTHA existing.PrincipalRow has eight columns and no nature discriminator, deliberately (stele-agent-identity-scoping-v0_3:32: "a nature column fights the design").stele/actor.py:12 states it: agent-actor construction "has no single constructor." That absence is the mechanical reason there are ~20 independent mint sites.
Replace per-request uuid4() with stable, deterministic identities for the small set of things that actually act, extending a pattern already in the tree: RENDER_AUTO_TRIGGER_SYSTEM_ACTOR, RECONCILIATION_SPECIALIST_ACTOR, _COMPANION_ACTOR_ID and three others are already fixed sentinel UUIDs.
considerations.py:202 so a caller either names an agent or gets the declared default; amend the API description at schemas.py:4256.
Agent actors resolve to real contributors rows (kind='agent') rather than being minted, with validation at the write boundary.
stele/actor.py:12 names); resolution at each site; a guard test that an unregistered agent id cannot be written; optionally an FK on memory_events.actor_id — though note the table serves human and agent actors and a Companion actor whose id is the person's principal UUID, so an FK is not a one-line addition.contributors/registry.py:110), and agents/standard_agents.py:11 states the durable identity is "the display name + purpose, not the row id" — so "the same agent" across engagements still has many ids. It fixes referential integrity without fixing identity continuity. It also has no lifecycle: nothing deactivates, expires, or GCs agent rows today.
stele-agentic-id already specifies precisely this problem's subject — spec V12: "Derived Agent: ephemeral principal — signed credential, no stored row, own UUID, TTL-bounded." Local signature mint (no issuer round trip), bounded-chain-walk verification, clamp/kill revocation, acts_for as the human link.
agentic references anywhere in the engine or stele.acts_for consent/binding is still spec-open; (iv) it is V1 wire-frozen, so the seam must fit the freeze or wait for V2, which is FORAY-gated; (v) it presumes a substrate fork that the record does not show as resolved (below).
stele/scoping/stele-agent-identity-scoping-v0_3.md §9 posed Path A (agent nature inside Stele) / Path B (separate substrate) / Path C (federation seam), leaned Path C, and instructed: "Do not advance the agent design or this note further until §9 is decided."
A search of the record found no document resolving §9. stele-agentic-id is Path B built out, and the July investigation treats the Stele/Agentic-ID split as settled at the product level — but that is not the same as §9 being answered, and the instruction may still bind. This ambiguity is itself worth an Operator word, because Option C is a §9 commitment in all but name, while Options A and B are deliberately fork-neutral.
Option A now; Option B as its named follow-on; Option C as the stated destination, not this CR.
The reasoning, plainly: B-39's actual complaint is that the record names agents that never existed. Option A removes exactly that, costs little, touches no substrate, commits to no fork, and can be verified by reading. It is the honest floor.
Option B is the right second step and should be named as such rather than folded in — it is where an FK and a single constructor belong, and it is a bigger change than it looks because of the mixed-actor table.
Option C is where this genuinely ends up, and the lean is explicitly not to reach for it now: adopting a not-yet-production, wire-frozen substrate across a seam that does not exist, on top of an unresolved substrate fork, to solve "stop minting throwaway UUIDs," is a large commitment bought for a small defect. It should be chosen when agent identity needs to be verifiable by someone outside the engine — which is a product decision, not a cleanup one.
What would change the lean. If the Operator intends Loomworks to be agentic-id's customer zero on a near horizon, then A is throwaway work and C should be scoped directly instead — at which point the §9 question must be settled first.
Which option, and — if A or B — whether the §9 fork stays parked. Everything else in Wave 3's CR-2026-189 follows mechanically from that answer.
DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — B-39 scoping note — v0.1 — 2026-08-07