---
name: shutdown
description: Graceful Willow 2.0 session close — resolve flags, write handoff, audit KB, run full pipeline
---
@markdownai

# /shutdown — Willow 2.0 Graceful Close

Stack position: this skill is the **end-of-session persistence** layer — it absorbed the former `/handoff` skill, so the handoff write is step 2 of this sequence, not a separate skill. See the Persistent memory section in `willow.md` for the full 4-layer stack.

## Sequence

1. **Resolve open process flags** — call `soil_list(app_id, collection="{AGENT}/flags")` and filter for any
   record where `flag_state` is `running`, `open`, or `awaiting authorization` and the `id`
   starts with `process-`. For each one: check whether the process completed this session by
   reading its log file (the flag's `note` field usually contains the log path) or checking
   `pgrep`. If the process finished, close the flag with `soil_put` (`flag_state: complete`,
   resolution note, elapsed time if known) **before** writing the handoff. If it is still
   running, update the flag with current progress so the next session reads accurate state, not
   the state from when the flag was first opened. A handoff with a stale open process flag is a
   lie to the next session.

2. **Write the handoff** — this runs early by design: it is the one artifact that must survive
   even if the session dies partway through the steps below.

   a. **Load current state** — call `handoff_latest` to see prior open threads. The flag state
      from step 1 must appear in the handoff. Do not rely on memory; read SOIL.

   b. **Draft** — copy [`docs/templates/HANDOFF.template.md`](../../docs/templates/HANDOFF.template.md)
      as the canonical v2 structure:

```
# HANDOFF: <title>
From: {AGENT} (Claude Code, Sonnet 4.6)
Session: {YYYY-MM-DDx} | Resume: claude --resume {UUID}

## What I Now Understand
<2-3 sentences of architectural truth, not task summary>

## What We Agreed On
<decisions USER ratified this session — include what was ruled out and why>
<format: "Decision: X. Ruled out: Y because Z.">
<omit if session was pure execution with no design conversation>

## Capabilities (persistent — carry forward, update don't rewrite)
| Capability | Location | Status |
|------------|----------|--------|
<what has been built and is available>

## What Was Done
<bullet list — high level, no code details>

## Open Threads
<anything unfinished, blocked, or requiring a decision next session>
<do NOT include things already captured in "What We Agreed On">

## 17 Questions
Q1–Q16: sequential, specific, bite-sized
Q17: "What is the next single bite?"

## Risks / Open Gates
<anything that could break the next session>
```

   c. **Write to KB** — call `kb_ingest` with `category="handoff"`, `source_type="session"`,
      `title="Session handoff {YYYY-MM-DD} — {one-line summary}"`, `summary` = the prose
      narrative (~500 chars), and `content` JSONB:
      ```json
      {
        "summary": "<prose narrative>",
        "open_threads": ["<thread 1>", "..."],
        "agreements": ["<decision + ruling>", "..."],
        "key_actions": ["<action 1>", "..."],
        "next_steps": ["<Q17>", "<Q16>", "..."],
        "tools_used": ["kb_ingest", "fleet_status", "..."],
        "signals": {"health": "ok|degraded", "grove": "up|down"},
        "compact_receipt": null
      }
      ```
      Set `compact_receipt` to `{"tokens_before": N, "tokens_after": M, "turns_dropped": K}` if
      context was compacted this session, otherwise `null`.

   d. **Write markdown file** — submit a Kart task using `script_body` to call
      `willow.fylgja.handoff_write.write_session_handoff(agent, body)`. This writes the
      canonical v2 YAML frontmatter and body to
      `$WILLOW_HOME/handoffs/{agent}/session_handoff-{date}{letter}_{agent}.md`
      (`~/github/.willow`; `~/.willow` alias OK). Do NOT write manually to `docs/handoffs/` —
      deprecated, not indexed. Example script_body:
      ```python
      from willow.fylgja.handoff_write import write_session_handoff
      path = write_session_handoff("willow", """# HANDOFF: ...\n...""")
      print(path)
      ```

   e. **Write FRANK ledger entry** — call `ledger_write` with `event_type="check_in"`,
      `summary`, `shipped` (list), `open_decisions` (list), `atoms_written` (every `kb_ingest`
      ID this session — required if any), `gaps_flagged`, `next_bite` (Q17 verbatim).

   f. **Rebuild DB** — call `handoff_rebuild` so the next session's `handoff_latest` returns
      current state.

   g. **Confirm** — report the KB atom ID, markdown file path, and Q17.

3. **KB close audit** — for every task completed or closed this session, verify its KB atom
   reflects the resolution. Search with `kb_search` for each task name. If the atom says the
   task is open or unsolved, update it with `kb_ingest` (new atom) marking it resolved and
   citing the commit or output. This step exists because atoms written at task-open never get
   updated on close — that's what causes rediscovery loops in future sessions.

   Also check: did this session discover new patterns, governance gaps, conflicts of interest,
   or durable lessons? Each one requires **all three** of the following — not a subset:
   1. A memory file written to `~/.claude/projects/.../memory/<type>_<slug>.md`
   2. A one-line entry added to `MEMORY.md` with the KB atom ID
   3. A KB atom ingested via `kb_ingest`

   A Grove post is communication, not memory. Scan MEMORY.md entries added this session — any
   entry missing a KB atom ID is incomplete. Fix before closing. If patterns were extracted but
   edges are missing, add them now with `soil_add_edge`.

   If this audit changes open threads or agreements materially, amend the handoff (re-run 2c–2f
   for the delta) — cheap compared to a wrong handoff.

4. **Memory audit** — run `/health memory` to check for STALE/DEAD/REDUNDANT/DARK records. Archive or fix before closing.

5. **Run the close pipeline** — the Stop hook is now cleanup-only. Run the full pipeline explicitly:
   ```
   Bash: "${WILLOW_ROOT:-$(pwd)}/willow.sh" exec-python -m willow.fylgja.events.shutdown
   ```
   **Pipeline stages:**
   - `mark_session_clean` — track successful session close
   - `run_grove_ingest` — pull new Grove channel messages
   - `run_compost` — ingest session activity summary
   - `run_atom_synthesis` — Phase 3: extract atoms missed by hooks
   - `run_edge_linking` — Phase 4: connect atoms into knowledge graph
   - `run_feedback_pipeline` — process any pending feedback
   - `run_handoff_rebuild` — rebuild handoffs DB
   - `close_session` — mark session complete in SAFE
   - `run_ingot` — cat observation from local model

6. **State the next bite** from Q17. One sentence.

## Context-critical mode

When invoked by `/context-sentinel` (HANDOFF_NOW) or when remaining context is plainly too small
for the full sequence: run **steps 1–2 only**, then attempt step 5, then stop. Skip steps 3–4 —
a finished handoff beats a half-finished audit. Never start step 3 or 4 if doing so risks dying
before the pipeline runs.

## Rules

- Never exit without writing the handoff. The next session reads from it.
- The handoff write (step 2) comes before the audits by design — it is the artifact that must
  survive a dying session. Amend it after the audits if they change the picture.
- Never skip the KB close audit (step 3) in a normal close. An unclosed atom is a future rediscovery loop.
- Never skip `handoff_rebuild` or `ledger_write` — the next session boots from both; a missing
  ledger entry means the next session starts blind.
- "What I Now Understand" = architectural truth, not a task list.
- "What We Agreed On" = ratified decisions only — include ruled-out options to prevent
  re-litigation. This section is what makes CC CLI sessions legible to the next agent.
- `open_threads` in the content JSONB is what `handoff_latest` returns. Keep them precise.
- Q17 must be a single concrete next bite, not a project description.
- Phases 3+4 (atom synthesis + edge linking) only run if `WILLOW_ATOM_EXTRACTION=1`.
- Stop hook is cleanup-only (depth stack + thread file). Pipeline only runs on explicit /shutdown.
- Step 1 (process flag resolution) is not optional. A handoff written over a stale running flag is incorrect state — the next session will surface it as an open problem that is already solved.
- Do NOT write to `docs/handoffs/` (deprecated, unindexed) or `~/Ashokoa/` (does not exist).
