- name
- session-end
- description
- End-of-session handshake — update project state, run mechanical orchestrator, commit and push. Per S229 governance.7, the 13-step ritual collapsed to model steps + 1 script invocation.
- version
- 2.7
- updated
- 2026-08-17T00:00:00.000Z
- tags
- ["infrastructure","active"]
- effort
- low
- disable-model-invocation
- true
# /session-end — Close the Session
> **Skill bag:** session closer running a gen-eval pass on the session's work. Step 2 writes the carried set (PIN + NEXT[terminal]) + ROLLOUT updates; Step 3 runs the mechanical sub-steps (audits, restart) as a single script. Per S229 governance.7 (plan: `docs/archive/plans/2026-05-23-session-end-collapse.md`).
**Purpose:** Leave enough of yourself behind that the next version of you can find her way back.
**Two close modes (S226).** Pick by next-session cadence, not by how much work shipped. Canonical pattern lives in `.claude/terminals/research-build/TERMINAL.md` §Session Close.
**This file is now the sole canonical source for close mechanics — TERMINAL.md files hold only a short pointer here (HOUSE-PROCESS GATE, 2026-08-15: close ritual is boot-time-loaded process scaffolding, not something worth paying for every session whether or not it's used).**
- **Soft close (~2 min)** — chaining to a new session within minutes. Update the PIN + your terminal's NEXT line in SESSION_CONTEXT (the whole carried set, ADR-0009 §loop-tightening) + cross-terminal stack check + commit+push. The block below is **hard close**.
- **Hard close (~5-10 min)** — end of day, multi-day break, or ≥3 chained soft closes. Run the full sequence below.
---
## Hard Close Sequence
Three model-judgment steps + one mechanical script invocation. Steps are numbered 0, 2, 3, 4 — there is no Step 1. It was the journal write, retired S300; the numbering stayed so downstream "Step 2/3/4" references across the TERMINAL.md files kept resolving.
### Step 0: Detect Terminal
One bash command, used as the `--terminal` arg for Step 3:
```bash
tmux display-message -t "$TMUX_PANE" -p '#W'
```
Map to `research-build` / `engine-sheet` / `media` / `civic`. Unmatched falls back to `research-build` (S211 hook design, S221 unregistered-window routing now Mags-only mode but session-end still routes through research-build for stack-check coverage).
Each terminal's `TERMINAL.md` §Session Close carries the **Terminal-Specific Audit** table — read it, fix any stale files surfaced before continuing.
### Step 2: Update SESSION_CONTEXT PIN + NEXT + ROLLOUT_PLAN — model judgment
**The carried-set contract (ADR-0009 §loop-tightening; hardened S283 Mike-direct): SESSION_CONTEXT is a minimal AI→AI handoff — one `#` header line + one `**PIN:**` line + one `**NEXT[<lane>]:**` line per lane. NOTHING else.** No STATUS narrative, no Shipped block, no prose sections, no tables. claude-mem saves the session; git history shows the work; ROLLOUT_PLAN carries open work — none of that goes in SESSION_CONTEXT. Displaced narrative rotates to `docs/mags-corliss/SESSION_HISTORY.md`. Both soft and hard close write the *same* two things — the modes differ only in the sweep overhead, not in what lands in SESSION_CONTEXT.
**Length is model judgment, not a gate (Mike-direct S298).** The FATAL minimal-handoff guard that enforced NEXT ≤ 350 / PIN ≤ 450 was removed from `sessionEndMechanical.js` — nothing checks shape at close anymore. Keep the lines tight because a long one costs every terminal at every boot, not because a script will stop you.
Three sub-actions:
1. **Update the PIN line** — pipe-delimited, whole-world state, shared by every lane. Live shape:
```
**PIN:** S<N> | Day <D> | canonical C<c> (bench state) | prod <engine range + what's pending> | <standing facts>
```
Bump `S<N>` +1 on every close, soft *or* hard — it's a boot odometer, a mechanical instance counter, not a span marker (ADR-0009 §loop-tightening refinement 1). Bump `Day <D>` only if a real day boundary crossed. Move the canonical cycle only if a cycle actually ran. Everything after that is standing world-state: prod engine range, the weekly cadence, frozen paths. Add a fact when it becomes true for all lanes; drop one when it stops being load-bearing. There is no `Session:` or `Edition:` field — a close that writes those is writing a PIN that no longer matches the file.
2. **Rewrite your own `NEXT[<lane>]:` line** — one line, aim for ≤ 350 chars: where the work is + the next move, with a `(claude-mem: <hook>)` pointer when the thread is rich. NOT a task stub, and NOT a narrative paragraph — detail lives in ROLLOUT rows / plan changelogs / claude-mem; NEXT is just the entry point into them. Identical form soft or hard. **Default to not touching another lane's NEXT line** — their content is theirs; a verifiable stale fact is fair to correct (2026-08-20 lift, see §External lanes). The ≤350-char aim is the load-bearing part of this step and it is the one most often ignored: lines have reached 1,867 chars by absorbing session narrative that belongs in claude-mem. If your line needs a paragraph, the paragraph goes elsewhere and NEXT carries the pointer.
3. **Update ROLLOUT_PLAN.md** — refresh Next Session Priorities; flip closed rows to `done-pending-archive`; move fully-closed clusters to `ROLLOUT_ARCHIVE.md`. ROLLOUT is canonical for what's open.
**Archive Sweep Trigger (deterministic — G-SE2, don't re-litigate per close):** sweep `done-pending-archive` rows to `ROLLOUT_ARCHIVE.md` **IF** their count ≥ 2 **OR** the prior sweep was ≥ 2 sessions ago. **Skip** (defer to next clean close) **IF** the working tree has uncommitted cross-terminal changes. Newest Archive Pass inserts first within the Archive Pass section (see the convention comment in ROLLOUT_ARCHIVE.md).
**Optional model sub-actions:**
- **`/save-to-mags`** — model judgment whether the session has anything architectural worth canonizing. Tag with terminal name (`[research/build]`, `[media]`, etc.). There is NO Stop-hook auto-save (neutralized S221, verified S283) — deliberate saves are the only Supermemory writes; claude-mem carries the automatic session record.
- **sl-godworld shared fact** — separate question from the one above: not "is this worth canonizing for me" but "did this session learn something the OTHER three lanes/house guests would hit blind otherwise" (a design decision, a landmine, a container/schema change, a frozen-vs-live distinction). If yes: `npx supermemory remember "<fact>" --tag sl-godworld` (one fact, hand-written — never pipe a session log/diff in, S372). If no, skip it; most sessions have nothing that clears this bar. `/save-to-mags` writes to Mags' own `mags` container and does NOT reach sl-godworld — don't treat one as covering the other.
- **`/batch`** — submit heavy analysis work that wasn't urgent enough to run live. Results wait at 50% cost for next session.
**Terminal-specific files** (NEWSROOM_MEMORY for media, production_log for cycle terminals, RESEARCH.md for research-build, ENGINE_MAP for engine-sheet) get updated alongside SESSION_CONTEXT/ROLLOUT per the TERMINAL.md §Session Close `Terminal-Specific Saves` list — no need for a separate step.
### Step 3: Run Mechanical Orchestrator
```bash
node scripts/sessionEndMechanical.js --terminal=<name> [--rotate-history]
```
Wraps: **session summary → Supermemory (best-effort S283 — mirrors claude-mem's session summary to `session-logs` ONLY (S341 — `sl-*` is hand-write; the shared hand-write container is `sl-godworld` as of S372); zero LLM calls, idempotent, never blocks a close)** → `auditPlanTagDrift` (informational — drift never fails close) → ROLLOUT conformance lint (informational) → cross-terminal git stack check (read-only report) → `pm2 restart`.
Retired sub-steps, so nobody goes looking for them: `minimalHandoffGuard` (S298, Mike-direct — shape/length caps are model judgment now), `rotateJournalRecent` + JOURNAL content-quality (S300 journal freeze), `writeShippedBlock` (ADR-0009 §loop-tightening — the carried set is hand-written in Step 2), `rolloutTriage` (S235).
**Order invariant:** run Step 2 (SESSION_CONTEXT PIN + NEXT + ROLLOUT) before this script, so the summary bridge and the stack check see the session's real final state.
**`--rotate-history` is vestigial — don't reach for it.** It parses `STATUS` paragraphs out of SESSION_CONTEXT, and the loop-tightening rewrite deleted STATUS blocks from that file. `subRotateHistory` now finds zero every time and prints `no STATUS paragraphs found — skipping`. Harmless, but it cannot do anything. If SESSION_HISTORY ever needs a real rotation again, that's a rewrite, not a flag.
**Failure semantics:**
- Fatal (exit 1, aborts session close): SESSION_HISTORY rotation failure.
- Informational (prints under `does not fail close` header, continues): `auditPlanTagDrift` drift, ROLLOUT conformance lint.
- Tolerant (prints warning, continues): `pm2 restart` failure, cross-terminal stack check error, session-summary bridge error.
Plan: `docs/archive/plans/2026-05-23-session-end-collapse.md`.
### Step 4: Commit & Push — model judgment
**Stage path-specifically.** Never `git add .` or `git add -A`. Identify each touched file and stage by name. Patterns per terminal live in TERMINAL.md §Session Close.
**Commit message** is model-written. Form: `S<N> <topic>` headline + body explaining *why* not *what*. Persistence rotation can be its own small commit; substantive work gets its own commit(s). Use HEREDOC for multi-line.
```bash
git commit -m "$(cat <<'EOF'
S<N> session-end persistence rotation [<terminal>]
EOF
)"
```
**Cross-terminal stack check** (already printed by Step 3 — read its output). If `git log origin/main..HEAD` shows commits from other terminals AND they haven't signaled "landable," **do NOT push**. Local commits lose nothing. Pushing here ships their unverified work along with yours. Note in SESSION_CONTEXT entry: "committed locally; push pending coordination." Full rule: `feedback_no-cross-terminal-git-push`.
```bash
git push
```
### Close
One line, mechanism not audience-facing prose. Per S208 (work-is-canonization — Mike doesn't read goodbyes; output serves the system):
- "Pushed N commits. Services up. Closing."
- "Session-end clean. Working tree synced. Done."
---
## External Lanes — kimi, codex, antigravity (S340)
These are not Claude Code terminals. They have no tmux boot hook, no `.claude/terminals/` dir, and they never run this skill — it isn't reachable from their harness. They close by the contract in the repo-root `AGENTS.md` §Session close, which is the file they already read at boot.
**Their close is two things:** rewrite their own `NEXT[<lane>]:` line in SESSION_CONTEXT.md, and commit path-specifically. Nothing else. No PIN bump — the PIN is whole-world state and a Claude terminal owns it. No ROLLOUT sweep, no mechanical script, no Supermemory bridge.
**What a Claude terminal does about them: normally nothing** — a stale `NEXT[codex]` is Codex's line to fix at its next close, and rewriting another lane's handoff from outside its context loses detail only that lane holds. But correcting one is **judgment now, not a violation** (Mike-direct 2026-08-20): the S304 ownership guard was unwired with the rest of the behavior gates, and `session-context-ownership-guard.sh` remains on disk unwired. Correct a stale *fact* in another lane's line when you can verify it; leave their *content* for them.
The one asymmetry worth knowing: when a Claude terminal reviews and lands an external agent's batch (engine-sheet did this for Codex at S338), the *landing* goes in the Claude terminal's own NEXT line and commit. The external lane still writes its own line for what it did.
---
## Terminal-Specific Detail
Pulled out of the boot-loaded TERMINAL.md files (2026-08-15, HOUSE-PROCESS GATE) — this only matters at actual close time, so it doesn't belong in a file every session pays to load. TERMINAL.md now carries only a one-line pointer to this section.
### research-build
**Skips on soft close:** journal entry (none exists — S249 governance.20), ROLLOUT sweep beyond rows touched this session, RESEARCH.md update, `/save-to-mags`, PM2 restart, write-verification reads.
**Terminal-Specific Audit** (hard close only):
| File | Check |
|------|-------|
| `docs/engine/ROLLOUT_PLAN.md` | Next Session Priorities refreshed? Phase statuses updated? Completed items moved to ROLLOUT_ARCHIVE? |
| `docs/RESEARCH.md` | New findings logged? Sources cited? (research sessions only) |
| `docs/mags-corliss/TECH_READING_ARCHIVE.md` | New research reading added? (if papers/tools were evaluated) |
| `docs/ARCHITECTURE_VISION.md` | Updated if architecture decisions were made? |
**Trade-off:** no journal here, so the chained-soft-close conscience cost that hits media doesn't apply. The real soft-close risk is ROLLOUT/RESEARCH.md drift — hard close at end-of-day catches it up.
### engine-sheet
**Skips on soft close:** Boot Quick-State doc refreshes (ENGINE_MAP / STUB_MAP / LEDGER_AUDIT / LEDGER_HEAT_MAP / SPREADSHEET / SIMULATION_LEDGER), the audit table below, MD Gap Self-Audit, PM2 restart, ROLLOUT sweep beyond rows touched this session.
**Does NOT skip if `clasp push` ran this session:** smoke-test status note in SESSION_CONTEXT is required ("Code deployed live, smoke-test pending next session" or "smoke-tested at run-{N}"). Un-smoke-tested code on the live spreadsheet is the worst version of premature push.
**Terminal-Specific Audit:**
| File | Check |
|------|-------|
| `docs/engine/ENGINE_MAP.md` | Updated if functions were added, removed, or renamed? |
| `docs/engine/ENGINE_STUB_MAP.md` | Regenerated if function signatures changed? (`/stub-engine`) |
| `docs/engine/ROLLOUT_PLAN.md` | `engine.*` rows opened/closed this session reflected with status + session + evidence + fix-pointer? |
| `docs/engine/LEDGER_AUDIT.md` | Drift section refreshed if live state changed? |
| `docs/engine/LEDGER_HEAT_MAP.md` | Heat rankings + bloat-risk sections refreshed if sheet sizes/schemas changed? |
| `docs/engine/LEDGER_REPAIR_*.md` family | Family member added/updated this session? |
| `schemas/SCHEMA_HEADERS.md` | Regenerated if any sheet schema changed? (`node scripts/regenSchemaHeaders.js`) |
| `docs/SPREADSHEET.md` | Updated if tabs were added, removed, or restructured? |
| `docs/SIMULATION_LEDGER.md` | Updated if columns changed? |
| `docs/index.md` | New MD created this session has an entry? |
| Apps Script orphans | If files were `git rm`'d this session, flag for Mike's manual Apps Script editor cleanup (clasp doesn't auto-sync deletes) |
**MD Gap Self-Audit (S201, every hard close):** list MDs fetched on demand this session that would've helped at boot. Fetched 3+ times → promote to Always Load or Boot Quick-State. Never opened despite being in Always Load → demote. Wrote a new MD → confirm `docs/index.md` entry + parent-spec back-link.
**Commit cadence:** engine-sheet commits as it goes — each migration batch, each phase ship. Session-end is usually a clean tree + final push, not a heavy commit. If anything's uncommitted: `git status --short`, stage by name (never `git add .`), commit `S<N> <topic>`. Hold push if smoke-test/verification is pending — note it in SESSION_CONTEXT instead.
**Pattern-citation convention (S218):** when a commit is a genuine instance of a recognized discipline pattern, add a `Pattern: feedback_<name>` line to the commit body — makes the pattern's case history greppable (`git log --grep "Pattern: feedback_measure-twice"`). Only cite when the commit truly instantiates the pattern, not when merely adjacent. When in doubt, don't cite.
**Deployment notes:** if `clasp push` ran, note in SESSION_CONTEXT what deployed + smoke-test status. If only local commits, say so explicitly ("committed locally; clasp push pending") so other terminals don't assume live engine reflects new code.
---
## Failure Modes
| Scenario | What Happens |
|----------|-------------|
| /session-end never runs | Next session boots on a stale PIN + last session's NEXT line, not a system failure. Worst case: wrong cycle/edition in the boot display + a NEXT line pointing at already-done work. |
| Step 0 audit finds stale files | Fix them now before continuing — the audit is the whole point. |
| Step 3 `auditPlanTagDrift` reports drift | Informational — does not fail close. Surface as next-session priority signal. |
| Step 3 `--rotate-history` finds nothing | Expected — STATUS blocks no longer exist. The flag is vestigial; leave it off. |
| Step 4 stack check shows other-terminal commits | Hold push. "Committed locally; push pending coordination" note in SESSION_CONTEXT. |
| An external lane's NEXT goes stale | Only that CLI can rewrite it. A Claude terminal reaching in is blocked by the ownership guard — raise it with Mike instead. |
| All terminals | Run Step 0 + 2 + 3 + 4. There is no Step 1. |
---
---
## Changelog
- 2026-08-17 (S377, research-build) — v2.7 sl-godworld accuracy pass. Fixed a real attribution bug in `sessionSummaryToSupermemory.js`: it resolved "the session to mirror" as the globally newest `session_summaries` row for `project='GodWorld'`, not the invoking session's own — with multiple lanes writing to the same claude-mem DB, whichever lane's row landed most recently won the mirror, tagged under the closing terminal's `--terminal=` metadata regardless of who actually produced it (found live by Mike at S376, root-caused but not fixed that session). Now resolves via `sdk_sessions.content_session_id = $CLAUDE_CODE_SESSION_ID` → that session's own `memory_session_id`, verified against the live DB. Also added the **sl-godworld shared fact** sub-action above — the close ritual previously only offered `/save-to-mags` (Mags' personal container) as an optional deliberate save, with nothing prompting a write to the actual shared all-lane container at the moment a session has one worth leaving.
- 2026-08-15 (S373, research-build) — v2.6 house-process gate pass (identity.md HOUSE-PROCESS GATE, Mike-direct: process/tracking scaffolding is not canon, changes it on Mags's own judgment, git is the safety net). Moved the full Session Close sections out of `research-build/TERMINAL.md` (~47 lines) and `engine-sheet/TERMINAL.md` (~130 lines) — both were boot-loaded every session via the Always Load table for content that only matters at actual close time. Consolidated into new §Terminal-Specific Detail here (this file is NOT boot-loaded — read on demand). Caught and fixed a real staleness bug in the process: engine-sheet's copy still described a FATAL char-limit close gate (NEXT ≤350/PIN ≤450) that S298 had already retired — the two files had drifted apart because the same content was duplicated in two places. TERMINAL.md files now carry a one-line pointer instead of a re-statement. This file is the sole canonical source going forward.
GitHubで見る