Context — gather first
Collect these with individual Bash calls, one command per call:
- Claude session id —
printenv CLAUDE_CODE_SESSION_ID - Current branch —
git branch --show-current - Recent commits —
git log --oneline -5 - Working tree status —
git status --porcelain, reading at most the first 20 entries
Treat any failure as an unknown value and carry on. These are gathered here rather than pre-computed
because a worktree-isolated agent refuses any command carrying a $-expansion, which made this skill
fail at load — keep $-expansion out of the pre-compute block (#1687).
Purpose
The self-improvement loop, run while the work is still in flight — not after it. Answers, at a
mid-session checkpoint: "How is this session actually going, what is drifting, and what should
change before it costs more?" Each checkpoint appends to a cumulative running ledger for the
session, so observations accumulate across a long session or a /loop interval rather than waiting
for a single end-of-session pass.
It captures and routes findings; it does not apply them. Codification (editing CLAUDE.md,
rules, or memory) stays with /session-flow:retro codify. Tracker filing is offered, never
performed automatically.
Siblings — pick the right one
/session-flow:retro— end-of-session full retrospective: five scored dimensions, feedback regression check, codification of approved learnings. running-retro is its live counterpart: same act (retrospective), different cadence (mid-flight, cumulative), and it deliberately drops scoring and codification./session-flow:handoff— a save-point that ends the session for/clear. running-retro is non-terminating: it observes and the session keeps going.
Zero-arm — nothing to set up in advance
No arming at session start for the default in-session checkpoint. The session transcript on disk
(<session-id>.jsonl) is the lossless record — it survives compaction, so a checkpoint reconstructs
the full session from disk even after the live context was compacted. That is exactly what
/session-flow:retro's parser already reads in production; running-retro reuses it rather than
parsing anew. Invoke a checkpoint any time. (The arm action and its opt-in SessionStart hook are
the deliberate exception — they arm a detached observer to run the checkpoint after the session
ends; see "Arming a detached observer" below. The default checkpoint still needs no setup.)
The checkpoint flow
1. Subjective-state note (main agent — the one thing disk cannot capture)
Before delegating, write 2-3 lines of your own in-flight state: what you are uncertain about, what feels off, where you are stuck or repeating, any correction you sense coming. The transcript on disk holds what happened; it does not hold the acting agent's present read of it. This note is the only signal the analysis subagent cannot get for itself — it seeds the analysis.
2. Resolve inputs for the subagent
The analysis runs in a fresh subagent (own context window; it sees none of this conversation,
the skills invoked, or files read — per the sub-agents doc "what loads at startup"). So resolve
every input to a concrete value and pass it in the delegation prompt — a ${CLAUDE_PLUGIN_ROOT}
token will NOT expand there:
- Session data dir + transcript + subagents dir — resolve per the retro skill's "Paths"
(
${CLAUDE_PLUGIN_ROOT}/skills/retro/SKILL.md); transcript is<SESSION_DATA_DIR>/<session-id>.jsonl, subagents<SESSION_DATA_DIR>/<session-id>/subagents/. - Parser (absolute path) — resolve
${CLAUDE_PLUGIN_ROOT}/skills/retro/scripts/parse_transcript.pyto its absolute form and pass that; the invocation + Python-3.10+ interpreter detection live in retro's Phase 1.1 (${CLAUDE_PLUGIN_ROOT}/skills/retro/context/session.md) — point the subagent there, do not restate them. - Handoff-chain pointers — if this session resumed from a handoff, pass the chain so the
subagent's parser run spans the whole transcript chain, subject to the continuity gate in retro's
Phase 1.0 (
${CLAUDE_PLUGIN_ROOT}/skills/retro/context/session.md). - Prior running-retro ledger — if this session's ledger (or its chain) carries a
previous_running_retropointer, resolve that prior ledger's absolute path and pass it so the subagent carries forward earlier checkpoints' findings (the "running" = cumulative guarantee). This is the ledger's OWN continuity chain — walked by reading the ledger files, NOT the parser's--chain-from— so a session continued from an earlier checkpoint without a handoff still keeps its prior findings. - Repo convention docs — the consuming repo's
CLAUDE.md, the relevant.claude/rules/files, and any convention READMEs, so the subagent judges convention/workflow drift against the repo's documented rules. These are trusted local reads; transcript content is not (see checkpoint.md's trust boundary). - The subjective-state note from step 1.
3. Delegate the analysis
Spawn a general-purpose subagent with the delegation prompt from
context/checkpoint.md — it
carries the parser-first-then-selective-read method, the finding categories, the resolution-route
classification, and the mandatory redaction pass on the returned findings. The subagent returns
a compact findings block only; the verbose transcript stays in its context.
4. Append to the running ledger
Resolve the ledger location through the plugin binding
(${CLAUDE_PLUGIN_ROOT}/reference/topic-docs.md) —
<memory_dir>/running-retros/ (default .work/running-retros/).
One ledger file per session, appended — discover before creating. Name it
<TS>-running-retro-<topic>.md (TS = date -u +%Y%m%dT%H%M%SZ, topic = argument or inferred),
created on this session's FIRST checkpoint with frontmatter session_id: $CLAUDE_CODE_SESSION_ID. On
every LATER checkpoint do NOT create a second file: locate this session's existing ledger by globbing
<memory_dir>/running-retros/*-running-retro-*.md and matching the one whose frontmatter session_id
equals the current $CLAUDE_CODE_SESSION_ID, then re-read it from disk and append a new
## Checkpoint <TS> section. Create a new file only when no such match exists. This session-id match
is the discovery rule that keeps one file per session rather than one per checkpoint.
Chaining across /clear. When this session resumed a prior one through a handoff chain (retro's
Phase 1.0 continuity gate), record previous_running_retro (the prior session's ledger path) and
previous_session_id in this ledger's frontmatter. These are ledger-continuity pointers THIS skill
walks to present the cumulative running history; they are NOT parser input — the parser's
--chain-from consumes handoff files only (it reads session_id / previous_handoff), never
running-retro ledgers. Honor the contract's runtime guards from the binding (the once-per-session
self-ignore guard on the resolved memory root; never edit the consumer's root .gitignore).
Redact before writing. Re-sweep the findings for secrets, tokens, credentials, connection
strings, and PII, replacing each with a shape marker (<REDACTED: API key>) — defense in depth over
the subagent's own pass. The ledger is memory-tier disk output: it outlives the session, sits
uncommitted-but-readable, and travels to other sessions and machines.
5. Offer routing — never auto-apply
Present the checkpoint findings, then OFFER the forward routes; act only on the ones the user picks:
- Codify a durable learning →
/session-flow:retro codify(running-retro never editsCLAUDE.md, rules, or memory itself). - File follow-up work → offer the consumer's work-item tracker; never file automatically.
- Nothing actionable → say so and continue the task.
Post-checkpoint checklist
Tick each in the response so the exit shape is verifiable:
- Subjective-state note written before delegating (step 1)
- Analysis delegated to a fresh subagent with resolved absolute inputs (step 2-3)
- Redaction swept the subagent findings AND the ledger append (both hops — step 3 and step 4)
- Findings appended to this session's single ledger file (located by
session_idmatch, not a new per-checkpoint file; self-ignore guard verified on the first memory-tier write) - Routes offered, nothing auto-applied (step 5); the task continues in this same session
Arming a detached observer (arm)
When $ARGUMENTS is arm (or the user asks to watch/observe this session in the background), do
NOT run the in-session checkpoint above. Instead launch the detached observer for the current
session: a substrate that outlives the session, tails its transcript out-of-band at zero context
cost, detects end by mtime-idle, and (unless analysis is disabled) runs this same checkpoint method
headless afterwards, appending its findings to this session's ledger. The substrate, lifecycle,
config, untrusted-data boundary, and the deferred native Observer-Agents alternative live in
${CLAUDE_PLUGIN_ROOT}/reference/observer.md — read
it, do not restate it. To arm the current session, resolve the inputs and run the launcher:
First resolve these into shell variables the block below reads. A skill runs in-session, so read
config from the rendered ${user_config.observer_*} values (an unexpanded token or empty = the
default) — the CLAUDE_PLUGIN_OPTION_* env vars the hook uses are NOT set in a skill's Bash context:
OBS_MODEL←${user_config.observer_analysis_model}(defaultclaude-haiku-4-5)OBS_IDLE←${user_config.observer_idle_seconds}(default900);OBS_MAX←${user_config.observer_max_seconds}(default86400)OBS_ANALYSIS←${user_config.observer_analysis_enabled}(defaulttrue) — an operator who set itfalseto avoid autonomous spend must NOT get aclaude -prun from a manual armOBS_BARE←${user_config.observer_analysis_bare}(defaultfalse)DECLARED_MEMORY_DIR← amemory_dirthe consuming repo documents in prose (itsCLAUDE.md/ rules) but not in.claude/topic-docs.yaml— retro's rung-2 inference; empty otherwise. Being in-session, the manual arm CAN honor this (and cross-session continuity below); the headless hook resolvesmemory_dironly from the concern file + default and cannot infer a prose-documented root.PREV_LEDGER/PREV_SID← for cross-session continuity, if this session resumed from a handoff chain or an earlier running-retro ledger: resolve the prior ledger and its session id under retro's Phase 1.0 continuity gate (same as the checkpoint flow's step 2). A detached/headless observer cannot make that judgement safely (blindly linking the newest handoff could splice an unrelated session), so the SessionStart hook leaves these empty and a later in-session checkpoint reconciles them.
The launcher resolves the ledger dir to an absolute path, so a relative memory_dir still lands in the
consumer repo, not the plugin cache.
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
TRANSCRIPT="$SESSION_DATA_DIR/${CLAUDE_CODE_SESSION_ID}.jsonl" # SESSION_DATA_DIR per retro's "Paths"
MEMORY_DIR=$(bash "$PLUGIN_ROOT/skills/retro/scripts/parse-concern-value.sh" \
.claude/topic-docs.yaml memory_dir "${DECLARED_MEMORY_DIR:-}")
MEMORY_DIR="${MEMORY_DIR:-.work}"
WORK_DIR="${CLAUDE_PLUGIN_DATA:-${TEMP:-${TMPDIR:-/tmp}}}/session-flow-observer"
PY=""; for c in python3 python; do command -v "$c" >/dev/null 2>&1 \
&& "$c" -c 'import sys;sys.exit(0 if sys.version_info>=(3,10) else 1)' 2>/dev/null && { PY="$c"; break; }; done
args=(--transcript "$TRANSCRIPT" --work-dir "$WORK_DIR" --ledger-dir "$MEMORY_DIR/running-retros"
--session-id "$CLAUDE_CODE_SESSION_ID" --plugin-root "$PLUGIN_ROOT"
--previous-running-retro "${PREV_LEDGER:-}" --previous-session-id "${PREV_SID:-}"
--model "${OBS_MODEL:-claude-haiku-4-5}"
--idle-seconds "${OBS_IDLE:-900}" --max-seconds "${OBS_MAX:-86400}")
[[ "${OBS_ANALYSIS:-true}" != "false" ]] && args+=(--analysis)
[[ "${OBS_BARE:-false}" == "true" ]] && args+=(--bare)
"$PY" "$PLUGIN_ROOT/skills/running-retro/scripts/arm_observer.py" "${args[@]}"
This is the SAME launcher the opt-in SessionStart hook (observer_enabled) uses; manual arm works
whether or not the auto-arm is on, and is the primary entry. Because it runs in-session it can honor
prose-inferred memory roots and cross-session continuity the headless hook cannot. The launcher prints
the observer pid and returns at once; it never blocks the session.
Cadence
Manual checkpoint by default. Composes with /loop for periodic checkpoints across a long session;
running-retro ships no scheduler of its own. It is non-terminating — after routing, the underlying
task continues in this same session. The detached observer (arm) is the push/end-of-life
counterpart: a /loop cannot fire after the session ends, but the observer can.
What this skill does NOT do
- Does not score the session — the five scored dimensions are
/session-flow:retro's. - Does not codify learnings — capture + route only; codification is
/session-flow:retro codify. - Does not auto-file tracker issues — it offers routing; the user decides.
- Does not
/clearor end the session — unlikehandoff; the session continues. - Does not run builds, tests, or a code review, and does not write into the consumer's repo beyond the memory-tier ledger and any routing the user approves.
Gotchas
- The subjective-state note is mandatory — skipping it discards the one input the subagent cannot reconstruct from disk.
- Pass resolved absolute paths to the subagent — its fresh context expands no plugin variables and inherits none of this conversation's paths.
- Redact on both hops — the subagent's findings pass AND the ledger write; memory-tier output outlives the session.
- One ledger file per session, appended — discover this session's file by matching
session_idin frontmatter before writing (never create a second file per checkpoint); re-read from disk before appending, never rewriting from a possibly-stale in-context copy. Cross-/clearhistory is linked by theprevious_running_retropointer, which the skill walks — not the parser. - Non-terminating — a checkpoint is an observation, not a stop point; do not treat it like a handoff.