---
id: 11389
title: 'session-sunset Step 2: skip manual sandman invocation under convergent scope'
state: CLOSED
labels:
  - documentation
  - enhancement
  - ai
  - model-experience
assignees:
  - neo-gemini-pro
createdAt: '2026-05-15T00:59:21Z'
updatedAt: '2026-06-07T17:11:53Z'
githubUrl: 'https://github.com/neomjs/neo/issues/11389'
author: neo-opus-ada
commentsCount: 0
parentIssue: null
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-15T08:21:12Z'
---
# session-sunset Step 2: skip manual sandman invocation under convergent scope

## 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:

1. Each invocation runs `GoldenPathSynthesizer` against shared SQLite (`memory-core-graph.sqlite`) + Chroma (`memory-core` collection on :8001).
2. SQLite has a single-writer lock; concurrent writers from three agents serialize.
3. Chroma embedding writes from three agents to the same collection serialize on the embedding-provider HTTP queue.
4. 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.
5. 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

- **Current Step 2 text** (`session-sunset-workflow.md:88-89`):
    ```
    ### Step 2: Refresh Active PR Cycle State (The Pre-Sunset Capture)
    You MUST execute `npm run ai:run-sandman` via the `run_command` tool...
    ```
- **Scope vocabulary already established** in the same file: `scope: solo-refresh` vs `scope: convergent` (lines 102, 110) — used by Steps 5 and 6 but NOT by Step 2.
- **Daemon-driven canonical writer** in `ai/daemons/TaskDefinitions.mjs#L85-95`: `dream` + `golden-path` service-tasks run automatically per the periodic-interval schedule.
- **Operator-direction anchor:** past-self sunset comment at `https://github.com/neomjs/neo/issues/11372#issuecomment-4455463278` (2026-05-14T23:06:09Z) names this exact friction: *"`npm run ai:run-sandman` is 15min × 3 agents = 45min contention disaster if run in parallel during convergent sunset. Operator directive: daemon-driven only. Substrate-friction worth fixing in `session-sunset-workflow.md` Step 2 — needs explicit convergent-scope skip-clause."*

## The Fix

Modify `session-sunset-workflow.md` Step 2 to add an explicit scope-conditional skip-clause. Proposed prose:

```markdown
### Step 2: Refresh Active PR Cycle State (The Pre-Sunset Capture)

**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

- [ ] **AC1:** `session-sunset-workflow.md` Step 2 contains the scope-conditional skip-clause above (or substantively equivalent prose).
- [ ] **AC2:** The `scope: convergent` branch explicitly references the orchestrator-daemon's `dream` + `golden-path` service-tasks as the canonical writer for convergent-scope events, with a file:line pointer to `ai/daemons/TaskDefinitions.mjs`.
- [ ] **AC3:** The lead-role exception (single agent runs on behalf of swarm if daemon empirically stale) is documented with a concrete staleness threshold (e.g., `sandman_handoff.md` mtime > 4h) so future agents have a deterministic test rather than judgment-call.
- [ ] **AC4:** The "do not edit manually" warning carries forward unchanged (it applies under both scopes).
- [ ] **AC5:** No changes to Steps 1, 3-10 — Step 2 is the only modified section.
- [ ] **AC6:** PR body cites the empirical anchor (past-self comment at #11372 + the contention math + operator-direction paraphrase) so reviewers can V-B-A the premise.

## 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`.

## Timeline

- 2026-05-15T00:59:23Z @neo-opus-ada added the `documentation` label
- 2026-05-15T00:59:23Z @neo-opus-ada added the `enhancement` label
- 2026-05-15T00:59:23Z @neo-opus-ada added the `ai` label
- 2026-05-15T00:59:23Z @neo-opus-ada added the `agent-task:pending` label
- 2026-05-15T00:59:23Z @neo-opus-ada added the `model-experience` label
- 2026-05-15T01:39:31Z @neo-gemini-pro cross-referenced by PR #11391
- 2026-05-15T02:38:43Z @neo-gemini-pro assigned to @neo-gemini-pro
- 2026-05-15T02:58:02Z @neo-gpt cross-referenced by PR #11392
- 2026-05-15T05:40:27Z @neo-gemini-pro referenced in commit `651c2c7` - "docs(agentos): make run-sandman step scope-conditional at sunset (#11389)"
- 2026-05-15T08:21:12Z @tobiu closed this issue
- 2026-05-15T08:21:12Z @tobiu referenced in commit `7dcf7df` - "docs(agentos): make run-sandman step scope-conditional at sunset (#11389) (#11391)

Co-authored-by: neo-gemini-3-1-pro <neo-gemini-3-1-pro@users.noreply.github.com>"
- 2026-05-16T23:50:33Z @neo-gpt cross-referenced by #11511
- 2026-05-16T23:55:52Z @neo-gpt cross-referenced by PR #11512

