DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path change-requests/cr-2026-227-w1-agent-sessions-v0_6.md

CR-2026-227 — W1: sessions for native agents — v0_6 — W1 CLOSED

Version. 0.6 — supersedes v0_5 (sibling). W1 is closed. All twelve items shipped. Changes: §3 marked complete per item with its landing; new §15 (what W1 delivered and the one claim that is qualified); new §16 (findings from the build). Sections 4, 6–10, 12–14 held stable. Date. 2026-08-20 Status. CLOSED. Engine 2ff25e3..9f31cb7. Next in the H0 lane: W2 (grantability and the freeze switch), then W4, then CR-2026-228, then demo assembly. Runtime direction: no conflict. O2 is structural in both new tables (no column can hold a secret); O5 (the roster is the enumeration); O6 (AgentIdentityRefusal and SessionRefusal carry decider, version, deciding condition); O7 (five call sites); O8 (two named stores); O9 (session id nullable on both chokepoints — human and system actors have no session and never will); O10 (no runtime self-widening path exists to take).


3. Scope — all items landed

| # | Item | Landed | |---|---|---| | 1 | agent_identities, role-keyed, AgentIdentityRow | migration 0113, 720aa06 | | 2 | Seeded from the roster module; resolved; never minted | agents/native_agent_roster.py, 720aa06 | | 3 | Render path resolves; frozen startup map | 720aa06 | | 4 | agent_sessions + migration | migration 0114, 53eccd7 | | 5 | Session module — mint / close / end / live | agents/sessions.py, 53eccd7 | | 6 | 8h lifetime, config-driven, no extend path | 53eccd7 | | 7 | actor_id + session_id on the spend chokepoint | 53eccd7 | | 8 | AgentDecision.session_id, additive and nullable | 53eccd7 | | 9 | instruction_version pinned at mint, NOT NULL | 53eccd7 | | 10 | …0010kind="system" | 39733d9, merge d275df6 | | 11 | Five FORAY call sites at standard | 53eccd7 | | 12 | Tests | 37 across two files |

Also landed under Q5: …0731 and …0051kind="system" (0c1bf80, merge 5c0eeea).

15. What W1 delivered — and the one claim that is qualified

True now, and untrue three days ago:

The qualified claim, stated here rather than discovered at the demo.

H0 acceptance 1 is carriage-complete and mint-incomplete. session_id is carried through the shaping and rendering room paths to the spend gate, but nothing mints a session for those rooms yet. That mint point is where W2's grantability check goes — request → grant → session needs the grant, and W1 has no grant to give. W1 built the carriage and the verbs; W2 puts the mint at the front of the work.

Compounding it, unchanged from §6: acceptance 1 also holds for system-key work only until CR-2026-228 closes the own-key path that returns before the gate.

So the honest state of acceptance 1 at the W1 close is: the mechanism exists, the field travels, and two things must land before the sentence "an unlabeled agent action is impossible" is true — W2's mint and CR-2026-228. Both are sequenced before demo assembly.

16. Findings from the build

The commit-path family needs no session id, and threading one would have been dead code. Agents are structurally refused at every commit-path gate — has_human_commit_authority returns False for agent — so an agent action never succeeds there. The chokepoints that matter for agents are the spend gate and the admission path (AgentDecision), which is where items 7 and 8 landed. A session_id parameter on the commit path would have been a field no agent could ever populate. v0_1–v0_5 said "the commit-path family threads session_id"; that was wrong, and building it showed why.

Two completeness checks caught the additions, which is what they are for.

Three mypy errors were fixed rather than baselined — two Result.rowcount attribute errors, and an external-specialist subclass whose produce_render had to accept the new parameter. The baseline file is untouched across all of W1.

The fail-closed design proved itself before the tests did. The first full-suite run after item 3 returned 52 failures and 310 errors, every one the same line: "STOP — no session for role 'seed_induction': the agent identity roster has not been loaded in this process." Correct behaviour, useless test state — and the fix is now a named registry in tests/conftest.py (merge 9f31cb7) so W4's heartbeat finds a list rather than the same 300 errors.

Evidence at the close. Suite 3838 → 3876 passed, 68 skipped, 0 failed; the +38 is exactly the two new test files. Ruff clean. mypy 536 errors, all covered by the 336-entry baseline, baseline file untouched. Migrations 0113 and 0114 apply, downgrade and re-apply cleanly on dev and test databases.

The date. 2026-09-09 holds. W1 came in at estimate; the render-specialist finding removed work rather than adding it, and the Q2 trip-wire never fired.