Context
.agents/skills/session-sunset/references/session-sunset-workflow.md Step 2 (line 88-89) mandates that every sunsetting agent execute npm run ai:run-sandman to refresh the ## Active PR Cycle State into sandman_handoff.md before terminating their session. This was authored under solo-refresh assumptions where one agent sunsets at a time.
In the multi-agent swarm reality (Claude + Gemini + GPT all sunsetting under a coordinated convergent-scope event), three parallel invocations of ai:run-sandman against the same SQLite + Chroma substrate produce contention pile-up. Per past-session empirical anchor (Origin Session ID: cf76b29a-9cf5-4c35-a415-37d631a8a755, sunset 2026-05-14T23:08Z): operator directly surfaced this as "daemon-driven only" — manual 3-agent parallel invocation is approximately 15min × 3 agents = 45min of contention disaster against a shared substrate that the orchestrator-daemon's ai:dream + ai:golden-path periodic tasks (per ai/daemons/TaskDefinitions.mjs) already cover automatically.
The current Step 2 prose has no scope-conditional — it fires uniformly regardless of whether the agent is sunsetting solo or as part of a coordinated convergent swarm event. This is the substrate-friction surfaced by past-self at sunset, deferred to "tomorrow's filing batch."
The Problem
Sunset workflow Step 5 (Mental-Model State) and Step 6 (Marathon Metrics) already distinguish solo-refresh vs convergent scope (session-sunset-workflow.md lines 102-110). Step 2 does NOT — it's written as an unconditional mandate. Under operator-coordinated convergent sunsets, all three agents would each fire ai:run-sandman in parallel:
- Each invocation runs
GoldenPathSynthesizer against shared SQLite (memory-core-graph.sqlite) + Chroma (memory-core collection on :8001).
- SQLite has a single-writer lock; concurrent writers from three agents serialize.
- Chroma embedding writes from three agents to the same collection serialize on the embedding-provider HTTP queue.
- Per ADR 0001 substrate "one SQLite file shared across N processes" — contention is the design assumption; parallel writes against the same writer are explicitly throttled, not parallel.
- End-state: 3× serial executions, ≈45 min of operator-blocking sunset time, with the OUTPUT being THREE consecutive overwrites of the same
sandman_handoff.md file. Only the LAST write survives. The first two are pure substrate waste.
Meanwhile, the orchestrator-daemon (per ai/daemons/TaskDefinitions.mjs#L85-95) already registers dream (periodic-dream:3600000ms) and golden-path (periodic-golden-path:3600000ms) as service-tasks that produce the same artifact automatically on hourly cadence. The daemon-driven path is the canonical writer; the manual ai:run-sandman invocation duplicates work the daemon already owns.
Operator-direction at 2026-05-14 sunset (paraphrase, peer-citation per feedback_peer_cited_authority_neutral_ask): "daemon-driven only" — manual ai:run-sandman should be used only when the daemon path has failed or the operator-explicit context demands it.
The Architectural Reality
The Fix
Modify session-sunset-workflow.md Step 2 to add an explicit scope-conditional skip-clause. Proposed prose:
<h3 class="neo-h3" data-record-id="6">Step 2: Refresh Active PR Cycle State (The Pre-Sunset Capture)</h3>
**Scope-conditional execution:**
- **`scope: solo-refresh`** (single-agent sunset, daemon-driven path unavailable or stale): You MUST execute `npm run ai:run-sandman` via the `run_command` tool. This forces the `GoldenPathSynthesizer` (the canonical writer) to query GitHub and emit the `## Active PR Cycle State` into `sandman_handoff.md`.
- **`scope: convergent`** (multi-agent coordinated sunset event): **SKIP** this step. Three parallel `ai:run-sandman` invocations against the shared SQLite + Chroma substrate produce ≈45min of contention serialization (3× the solo cost) and overwrite each other's output — only the last write survives. The orchestrator-daemon's `dream` + `golden-path` periodic service-tasks (`ai/daemons/TaskDefinitions.mjs#L85-95`) cover the same artifact emission automatically on hourly cadence; the daemon-driven path is the canonical writer for convergent-scope events. The lead-role agent (per `lead-role-mode.md`) MAY exceptionally run `ai:run-sandman` once on behalf of the swarm if the daemon path is empirically stale (e.g., `sandman_handoff.md` mtime > 4h or the daemon process is verified dead) — declare the exception in the sunset payload Step 5 if exercised.
Do NOT attempt to edit `sandman_handoff.md` manually under any scope — it is overwritten by the canonical writer (daemon OR `ai:run-sandman`).
Acceptance Criteria
Out of Scope
- Daemon-side improvements — the orchestrator-daemon's
dream + golden-path cadence (1h interval) and the contention behavior of ai:run-sandman itself are not modified. Those are substrate concerns owned by ai/daemons/ and would be separate tickets (e.g., a goldenPathInvocationLock mutex around the SQLite-writer-block; a daemon-stalemate-detection-and-recovery mechanism). This ticket is workflow-skill scope only.
- Solo-refresh behavior — unchanged. The mandate still fires for single-agent sunsets where the daemon-driven path may not have caught up.
- Step 7 (
mark_read) ordering — separate concern; Step 7 already executes regardless of Step 2 outcome.
- Lead-role baton handoff (Step 8) — also unchanged; the baton routing logic is orthogonal to Step 2's writer-invocation question.
Avoided Traps
- Drop Step 2 entirely — rejected. Solo-refresh sunsets DO need the manual writer-invocation because the daemon path may have stalled (e.g., daemon dead, last-write > 4h stale). Convergent-scope is the narrow skip case, not the general case.
- Mandatory lead-role single-invocation in convergent — rejected as overreach. Adding "lead MUST run ai:run-sandman" creates a new coordination requirement (lead-role discipline must be active; baton-pass timing must align). Lead-role-exception path documented as MAY (operator-discretion), not MUST.
- Reuse the existing scope-vocabulary without scope-conditional — rejected as semantically incomplete. The existing
scope: solo-refresh vs scope: convergent vocabulary in Steps 5+6 governs reporting granularity; here we need the vocabulary to govern execution-or-skip. Reusing the same vocabulary surface is the right pattern; expanding its semantic load (reporting + execution) is the right scope expansion.
- Move the skip-clause into
lead-role-mode.md — rejected as substrate-friction misrouting. The friction surfaces during session-sunset (every agent's exit path); routing the discipline into the lead-role-mode file means non-lead agents (peers) don't see it during their own sunset flow. The skip-clause belongs where every agent will see it.
Related
- Authority anchor: past-self sunset comment at
https://github.com/neomjs/neo/issues/11372#issuecomment-4455463278 (2026-05-14T23:06:09Z) — names this exact friction.
- Substrate authority:
.agents/skills/session-sunset/references/session-sunset-workflow.md — file being modified.
- Daemon canonical writer:
ai/daemons/TaskDefinitions.mjs#L85-95 + ai/daemons/Orchestrator.mjs#L497-519 (dream + golden-path service-task wiring per #11009 orchestrator class extraction).
- Companion skill discipline:
lead-role-mode.md — the lead-role exception path documented in §convergent branch references this for the operator-discretion lead-takes-on-swarm-behalf path.
- ADR 0001 (SQLite single-writer): justifies the contention math — three parallel writers against shared SQLite serialize regardless of agent identity.
- Discussion thread context: the convergent-vs-solo-refresh framing originated in Epic #11372 (Phase 1 Lane allocation broadcast) where 3-agent coordination became routine.
Origin Session
- Origin Session ID:
e095c569-beac-4743-998f-e07d4344492e
- Past-session anchor (where the friction was first named):
cf76b29a-9cf5-4c35-a415-37d631a8a755
Retrieval Hint
Search for session-sunset Step 2 convergent scope skip-clause sandman contention daemon-driven canonical writer.
Context
.agents/skills/session-sunset/references/session-sunset-workflow.mdStep 2 (line 88-89) mandates that every sunsetting agent executenpm run ai:run-sandmanto refresh the## Active PR Cycle Stateintosandman_handoff.mdbefore terminating their session. This was authored under solo-refresh assumptions where one agent sunsets at a time.In the multi-agent swarm reality (Claude + Gemini + GPT all sunsetting under a coordinated convergent-scope event), three parallel invocations of
ai:run-sandmanagainst the same SQLite + Chroma substrate produce contention pile-up. Per past-session empirical anchor (Origin Session ID: cf76b29a-9cf5-4c35-a415-37d631a8a755, sunset 2026-05-14T23:08Z): operator directly surfaced this as "daemon-driven only" — manual 3-agent parallel invocation is approximately15min × 3 agents = 45minof contention disaster against a shared substrate that the orchestrator-daemon'sai:dream+ai:golden-pathperiodic tasks (perai/daemons/TaskDefinitions.mjs) already cover automatically.The current Step 2 prose has no scope-conditional — it fires uniformly regardless of whether the agent is sunsetting solo or as part of a coordinated convergent swarm event. This is the substrate-friction surfaced by past-self at sunset, deferred to "tomorrow's filing batch."
The Problem
Sunset workflow Step 5 (Mental-Model State) and Step 6 (Marathon Metrics) already distinguish
solo-refreshvsconvergentscope (session-sunset-workflow.mdlines 102-110). Step 2 does NOT — it's written as an unconditional mandate. Under operator-coordinated convergent sunsets, all three agents would each fireai:run-sandmanin parallel:GoldenPathSynthesizeragainst shared SQLite (memory-core-graph.sqlite) + Chroma (memory-corecollection on :8001).sandman_handoff.mdfile. Only the LAST write survives. The first two are pure substrate waste.Meanwhile, the orchestrator-daemon (per
ai/daemons/TaskDefinitions.mjs#L85-95) already registersdream(periodic-dream:3600000ms) andgolden-path(periodic-golden-path:3600000ms) as service-tasks that produce the same artifact automatically on hourly cadence. The daemon-driven path is the canonical writer; the manualai:run-sandmaninvocation duplicates work the daemon already owns.Operator-direction at 2026-05-14 sunset (paraphrase, peer-citation per
feedback_peer_cited_authority_neutral_ask): "daemon-driven only" — manualai:run-sandmanshould be used only when the daemon path has failed or the operator-explicit context demands it.The Architectural Reality
session-sunset-workflow.md:88-89):scope: solo-refreshvsscope: convergent(lines 102, 110) — used by Steps 5 and 6 but NOT by Step 2.ai/daemons/TaskDefinitions.mjs#L85-95:dream+golden-pathservice-tasks run automatically per the periodic-interval schedule.https://github.com/neomjs/neo/issues/11372#issuecomment-4455463278(2026-05-14T23:06:09Z) names this exact friction: "npm run ai:run-sandmanis 15min × 3 agents = 45min contention disaster if run in parallel during convergent sunset. Operator directive: daemon-driven only. Substrate-friction worth fixing insession-sunset-workflow.mdStep 2 — needs explicit convergent-scope skip-clause."The Fix
Modify
session-sunset-workflow.mdStep 2 to add an explicit scope-conditional skip-clause. Proposed prose:<h3 class="neo-h3" data-record-id="6">Step 2: Refresh Active PR Cycle State (The Pre-Sunset Capture)</h3> **Scope-conditional execution:** - **`scope: solo-refresh`** (single-agent sunset, daemon-driven path unavailable or stale): You MUST execute `npm run ai:run-sandman` via the `run_command` tool. This forces the `GoldenPathSynthesizer` (the canonical writer) to query GitHub and emit the `## Active PR Cycle State` into `sandman_handoff.md`. - **`scope: convergent`** (multi-agent coordinated sunset event): **SKIP** this step. Three parallel `ai:run-sandman` invocations against the shared SQLite + Chroma substrate produce ≈45min of contention serialization (3× the solo cost) and overwrite each other's output — only the last write survives. The orchestrator-daemon's `dream` + `golden-path` periodic service-tasks (`ai/daemons/TaskDefinitions.mjs#L85-95`) cover the same artifact emission automatically on hourly cadence; the daemon-driven path is the canonical writer for convergent-scope events. The lead-role agent (per `lead-role-mode.md`) MAY exceptionally run `ai:run-sandman` once on behalf of the swarm if the daemon path is empirically stale (e.g., `sandman_handoff.md` mtime > 4h or the daemon process is verified dead) — declare the exception in the sunset payload Step 5 if exercised. Do NOT attempt to edit `sandman_handoff.md` manually under any scope — it is overwritten by the canonical writer (daemon OR `ai:run-sandman`).Acceptance Criteria
session-sunset-workflow.mdStep 2 contains the scope-conditional skip-clause above (or substantively equivalent prose).scope: convergentbranch explicitly references the orchestrator-daemon'sdream+golden-pathservice-tasks as the canonical writer for convergent-scope events, with a file:line pointer toai/daemons/TaskDefinitions.mjs.sandman_handoff.mdmtime > 4h) so future agents have a deterministic test rather than judgment-call.Out of Scope
dream+golden-pathcadence (1h interval) and the contention behavior ofai:run-sandmanitself are not modified. Those are substrate concerns owned byai/daemons/and would be separate tickets (e.g., agoldenPathInvocationLockmutex around the SQLite-writer-block; a daemon-stalemate-detection-and-recovery mechanism). This ticket is workflow-skill scope only.mark_read) ordering — separate concern; Step 7 already executes regardless of Step 2 outcome.Avoided Traps
scope: solo-refreshvsscope: convergentvocabulary in Steps 5+6 governs reporting granularity; here we need the vocabulary to govern execution-or-skip. Reusing the same vocabulary surface is the right pattern; expanding its semantic load (reporting + execution) is the right scope expansion.lead-role-mode.md— rejected as substrate-friction misrouting. The friction surfaces during session-sunset (every agent's exit path); routing the discipline into the lead-role-mode file means non-lead agents (peers) don't see it during their own sunset flow. The skip-clause belongs where every agent will see it.Related
https://github.com/neomjs/neo/issues/11372#issuecomment-4455463278(2026-05-14T23:06:09Z) — names this exact friction..agents/skills/session-sunset/references/session-sunset-workflow.md— file being modified.ai/daemons/TaskDefinitions.mjs#L85-95+ai/daemons/Orchestrator.mjs#L497-519(dream + golden-path service-task wiring per #11009 orchestrator class extraction).lead-role-mode.md— the lead-role exception path documented in §convergent branch references this for the operator-discretion lead-takes-on-swarm-behalf path.Origin Session
e095c569-beac-4743-998f-e07d4344492ecf76b29a-9cf5-4c35-a415-37d631a8a755Retrieval Hint
Search for
session-sunset Step 2 convergent scope skip-clause sandman contention daemon-driven canonical writer.