Version: v0.1
Date: 2026-08-21
Status: Scoping note. Not a change request. Names a design question and its live instance; rules nothing. Markdown primary — the consumer is Claude Code and future change-request drafting.
Occasion: CC's post-merge trace of agent-ActorRef uses, 2026-08-21, following CR-2026-232's merge at fbe5ce2.
Reads with: change-requests/cr-2026-232-unsessioned-agent-actions-sentinel-and-baseline-v0_2 §2a and §10; change-requests/cr-2026-231-w6-writing-the-grant-into-the-record-v0_3 §2, §3, §11; agents/unsessioned_baseline.py, the "THE LIMIT OF THIS BASELINE" block.
Does not touch: CR-2026-231 §6, unruled.
CR-2026-232 established that an agent reference is used for one of two reasons — to say this agent did this thing, or to say this is the agent. Only the first can be missing a grant. The sentinel marks the first; the second carries nothing.
That distinction is correct. What the change request assumed, without saying so, is that it is a property of the object — that a given agent reference is an attributing one or a naming one. It is not. It is a property of the use. And there is now one place in the engine where a single object is used both ways in the same function call, which means it carries a marker that is true of one use and false of the other.
Nothing is broken today. The sentinel says this action had no grant, and it is riding into a field that describes no action, so the record contains a statement about an act where no act occurred. It is a false statement in a field that means something — small, contained, and the same kind of falsehood this arc has spent a week eliminating elsewhere.
This note names the shape, records the instance, and lists what closing it would involve. It does not choose. Three of the four options change a domain type, and that is not something a scoping note decides.
CR-2026-232 §2a states the category and its test:
> An agent-kind ActorRef is built for one of two reasons. Attributing — this agent did this thing. Naming — this is the agent. Only an attributing construction can be missing a grant. The discriminating question: if this reference were wrong, would the record say the wrong agent did something, or would the system use the wrong agent?
The category is right. Its subject is wrong.
The question §2a asks — would the record say the wrong agent did something — is a question about a field, not about an object. wasAttributedTo attributes. specialist_ref names. The same ActorRef handed to both is attributing in one and naming in the other, simultaneously.
So attribution is a property of the use site. The sentinel, however, lives on the object. A per-use property is being stored on a shared object, and it travels wherever the object travels.
This is the third instance of one error in this arc.
| Where | Per-X property | Stored on | Caught by |
|---|---|---|---|
| CR-2026-231 v0.1 §3.2 | Per-action session id | Per-process ActorRef | Gate 1 pre-flight |
| CR-2026-231 v0.2 §5 | Per-identity grant event | Per-engagement Memory log | Gate 2 pre-flight |
| Here | Per-use attribution marker | Shared ActorRef object | Post-merge trace |
The two questions this arc has now earned, and they are siblings:
The first catches a value that outlives its truth. The second catches a value that is true here and false one argument over.
agents/render_dispatch.py, in _record_external_production_dispatched.
The same specialist_actor object is passed three times in one call:
wasAttributedTo= — attributingactor= — attributingspecialist_ref= — naming
Its construction is in render_specialist_bootstrap.py, an attributing site in the render_specialists group, so the object carries session_id=UNSESSIONED_ACTION_ID.
The consequence: the sentinel — whose documented meaning is an agent attributed an act on a path where no session exists — is written into a field that describes no act. The record makes a claim about an action in a place where there is no action.
Why CR-2026-232's tests cannot see it. Both directions of §5.2 are asserted and both pass. The scan finds Call nodes; nothing is constructed at the naming use, so there is no node to classify. The test guarantees every construction is classified. This is not a construction. The baseline module says exactly this in its own limit block — it was written knowing the gap, which is why the gap was found and not stumbled into.
From the same trace, recorded so a future change request has the set rather than re-deriving it:
| Site | Forwards | Status |
|---|---|---|
| credit/proposal_applier.py | RECONCILIATION_SPECIALIST_ACTOR into shaping_agent_ref | Safe — the constant was never attributing, carries None throughout |
| engagement/composition_orchestrator.py | step.specialist_ref onward | Safe — naming to naming |
| engagement/shape_confirmation.py | prior.shaping_agent_ref into the next version | Safe — naming to naming |
| engagement/shaping_skill.py | triggered_by into shaping_agent_ref | Unconfirmed — triggered_by may be a person or an agent depending on caller |
All four are safe by construction, not by design. They forward a value that happens never to have been attributing. The one that broke is the one where an attributing object reached a naming field, and nothing structural prevents another.
shaping_skill.py is the one to look at first in any closure work: it is not confirmed agent-kind at all, so its behaviour depends on the caller.
4a. Rule the instance benign and document it. The sentinel in specialist_ref is noise a reader can be told to ignore.
Cheapest. Costs the thing this arc has been buying all week — the record saying only true things. It also leaves the general shape open, so the next instance is discovered the same way.
4b. Strip the marker at naming use sites. Pass specialist_actor.model_copy(update={"session_id": None}) into naming fields.
Correct at each site, invisible in the type, and requires every future author to know the rule. It is discipline where structure is wanted.
4c. Give naming its own type. A NamedAgent — kind, id, display name — with no session, no capability_ref, no approval_mode. Attribution keeps ActorRef.
Makes the category structural: a naming field cannot hold an attributing reference because it will not typecheck. Anticipated at CR-2026-232 §2a as an observation. The largest option — it touches persisted payload shapes and their read paths.
4d. Make the marker per-use at the boundary. The session travels as an argument to the write rather than on the actor; the writer composes what is recorded.
Considered at CR-2026-231 §3 and rejected there because widening append_event touches 55 files. That reasoning still holds for append_event. It may not hold for a narrower boundary, and this note does not re-litigate it.
The honest ranking, offered not ruled: 4c is right and 4b is affordable. 4a is what happens by default if nothing is decided, and it should be chosen deliberately if it is chosen at all.
specialist_ref naming in every use, or attributing in some? §2a's question has only been asked of it once, at one site.session_id under 4c? The field is already serialized into memory_events.provenance on live rows. A type change has a read-side.shaping_skill.py's triggered_by carry an agent in practice? Its type permits a person.capability_ref and approval_mode? Both are per-use delegation facts living on the same shared object. This note has not checked, and the question should be asked before any type change, not after.Point 4 is the one most likely to change the size of the work.
It does not rule which option. It does not open a change request. It does not touch CR-2026-232's baseline, which is correct as merged and already documents this limit. It does not claim the instance is a defect — it is a false statement in a field, which may be judged acceptable. And it does not extend the claim to capability_ref or approval_mode without checking, which is §5.4.
DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — Scoping Note: Attribution Is a Property of the Use, Not of the Actor — v0.1 — 2026-08-21