Tandem: session-start (orientation)
Use at the start of a working session to re-orient. The blog's "start hooks load team-specific context dynamically" recommendation, implemented as a manual skill (less noisy than a hook that fires on every session, including 2-message ones).
Pre-flight — is the kit wired?
Before loading context, run the cheap wiring gate: node _00-Project-Management/93-Scripts/doctor.js --gate (npm: npm run pm:doctor -- --gate). It is silent on success; if it exits non-zero with kit not wired — run npm run pm:install, surface that first and recommend npm run pm:install before orienting — the kit isn't wired yet, so the folders/scripts this skill reads may be missing. (STORY-12.2.03)
What to load
Folder locations are resolved through the path map (pm-paths.js / pm-paths.json) rather than hardcoded, ensuring consistent references across all skills regardless of whether the repo uses the canonical or flattened layout. The script node _00-Project-Management/93-Scripts/lib/pm-paths.js resolve <role> prints the physical folder for any logical role (e.g., resolve stories, resolve decisions, resolve active, resolve monitor).
Active WIP index — resolve the
activefolder via the path map; it typically maps to12-Active(canonical) or00-Active(flattened). ReadACTIVE.mdfrom the resolved folder.- Falls back to: scan stories with
status: in-progressdirectly (slower but always works).
- Falls back to: scan stories with
Monitor / revision history — resolve the
monitorfolder via the path map; it typically maps to42-Monitor(canonical) or00-Monitor(flattened). ReadMONITOR.mdfrom the resolved folder.- If the file does not exist, note "no monitor file found".
Last 5 ADRs by filename, sorted descending by NNNN — resolve the
decisionsfolder via the path map; it maps to this repo's canonical decisions folder, or to06-ADRunder a flattened layout. GlobADR-*.mdunder that resolved folder.- If the folder does not exist, note "no ADR folder yet — first ADR will need to create it."
Stories folder — resolve the
storiesfolder via the path map; it maps to this repo's canonical stories folder, or to03-Storiesunder a flattened layout. GlobSTORY-*.mdrecursively under that resolved folder.- From that folder, surface:
status: in-progress— list paths + AC tick state.status: blocked— list paths + reason.status: in-reviewaged > 3 days — flag for close-out.
- From that folder, surface:
Unfinished autopilot run — ask the checkpoint, not the board, whether a previous unattended run is still open. Answered from the checkpoint alone (STORY-26.4.02 AC-6):
node _00-Project-Management/93-Scripts/autopilot-checkpoint.js --unfinishedIt prints
unfinished run present: <run_id> (paused|running|halted)orno unfinished run, and always exits 0 — orientation must never be blocked by a checkpoint probe, so read its stdout and never its exit code. Ahaltedrun is the one worth surfacing loudest: before ADR-0152 a run that reached its stop condition and a run whose session died at 3am produced indistinguishable checkpoints. If a run is reported open, name it in the orientation block and point at41-Reports/AUTOPILOT-CHECKPOINT-<run_id>.json; a checkpoint reported unreadable is not the same as "nothing to resume" and must be surfaced rather than skipped.Stale paused run — step 5 says whether a run is open; this says whether it has been open too long. Run it in the same breath (STORY-26.5.02, ADR-0159):
node _00-Project-Management/93-Scripts/autopilot-stale-runs.jsIt prints
stale paused run(s): <run_id> (<reason>, <age>)orno stale paused run, and always exits 0 — like the checkpoint probe, read its stdout and never its exit code. A run an operator has already judged is not listed by default; the line says how many are hidden, and--include-dismissedshows them with their reasons (STORY-29.1.04, ADR-0180).
For multi-file scans (step 4), delegate to an Explore agent (SOP §18) and ingest the summary — do not paste raw file contents into the main thread. Use the Explore scan only for the in-progress / blocked / stale-in-review list, NOT for project-wide totals — a broad fan-out scan can silently undercount or sample a subset of the stories folder. Take any shipped / total / blocked / in-progress counts from the resolved MONITOR (the maintained source of truth), per the tiebreaker rule in "Output rules" below. (Precedent: a session-start Explore scan once under-reported the story/epic totals and missed a blocked story vs the MONITOR's authoritative count — which is why project-wide counts come from the MONITOR and only the WIP list comes from the scan.)
Layout detection rule: check existence with Bash ls, Glob, or Read (which returns an error for missing files — treat that as "not present" rather than throwing). Do not assume any single layout. The orientation must work whether the repo uses the canonical SOP scaffold OR a project-specific flattened variant.
Stale paused runs — reported here, never acted on
A run that paused and never came back is invisible until somebody happens to open its
checkpoint. autopilot-stale-runs.js (step 6 above) makes it part of orientation. Two things
count as stale, and the notice names which:
overdue-scheduled-resume— the run recorded aresume_scheduled(or the olderpaused.resume_at) and that instant has passed. Staleness is measured from it.paused-without-schedule— the run was paused with no resume time at all and the pause is older than the threshold (default 24h,--max-paused-age-hours <n>). This is the arm the one real archived pause in this repository lands on, which is why it exists (ADR-0159).
A running, completed or halted run is never reported here. halted is surfaced by
step 5 instead, and loudly — the two probes answer different questions and neither substitutes
for the other.
A judged run stays quiet until the facts change (ADR-0180)
A notice that is always present is a notice nobody reads. When a pause is deliberate — this repository's one archived pause is held at an operator confirmation gate for a deferred release — record the judgement ONCE, with a reason and an actor:
node _00-Project-Management/93-Scripts/autopilot-stale-runs.js --dismiss <run_id> --reason "<why>" [--by "<who>"]
The reason is REQUIRED (a reason-less dismissal exits 2). So is the ACTOR, which defaults to
git config user.name — --by overrides it, and a dismissal that can name neither refuses
rather than recording a judgement nobody can be asked about (BUG-20260810-04). If an agent
records the judgement on an operator's behalf, say so IN --by: the store's job is to answer
who-dismissed-why, and "the Dev hat, on the operator's word" is a different fact from "the
operator". Both are written to
41-Reports/STALE-RUN-DISMISSALS.json — BESIDE the checkpoint, never on it, so nothing here
rewrites a run's own record. The dismissal is keyed to the EVIDENCE it was shown: the run, which
staleness arm fired, when the pause began and what resume was scheduled. A run that resumes
and pauses again, trips the other arm, or a DIFFERENT run going stale, all surface regardless —
that is the blanket mute BACKLOG-0147 named as the failure mode, and the evidence key is what
prevents it. Dismissing is a separate, deliberate command; session-start never writes one.
This step is read-only, and deliberately so. Session-start REPORTS a stale run and stops
there: it takes no action on the checkpoint, does not restart the work, and changes nothing
about the run's recorded state. Deciding what happens next is the operator's, in a session
opened for that purpose — a paused run may be paused on purpose, and orientation is not the
place to find out. Name the run_id, the reason, the age, and the checkpoint path; then move
on to the rest of the orientation block.
What to output
A short orientation block:
📌 Session-start orientation — <ISO date>
Active WIP:
- STORY-NN.M.PP-<slug> (in-progress, 3/5 ACs ticked, started <date>)
- STORY-NN.M.PP-<slug> (in-review, all ACs ticked, awaiting testplan run)
Blocked (1):
- STORY-NN.M.PP-<slug> — blocked on ADR-NNNN since <date>
Stale in-review (1):
- STORY-NN.M.PP-<slug> — in-review for 5 days, run /tandem:close-out-story
Recent ADRs:
- ADR-NNNN — <title> (<date>)
Last week (from MONITOR):
- <copy the most recent revision-history line>
Suggested hat: <Dev | PM | QA | Founder>
Suggested next step: <one specific action>
Output rules
- ≤ 25 lines total. This is orientation, not a status report.
- Do not modify any artefact during session-start — read-only.
- If the resolved ACTIVE / monitor file is empty (or none exists), say so and recommend the user pull a Ready story.
- If the resolved monitor file hasn't been updated in > 7 days, flag it.
- MONITOR is the tiebreaker over the fan-out story scan. If the step-4
Explorestory scan disagrees with the resolved monitor file (different shipped/blocked/in-progress counts, missing a story the monitor lists as blocked, or status values that don't match the project's enum) AND the monitor was updated within 7 days, treat the monitor as authoritative, surface the discrepancy in one line, and base the "Blocked / In-progress" lists on the monitor. A broad fan-out scan can silently undercount or sample a subset; the monitor is the maintained source of truth. - If the repo's folder layout differs from the canonical scaffold, name the resolved paths in a short note at the top of the output (one line, e.g. "Layout: 00-Monitor + 03-Stories + 06-ADR (flattened variant)") so subsequent skills + the user know what's actually being read.
Non-negotiable rules from CLAUDE.md
- Subagent delegation (SOP §18) for multi-file scans.
- Status enum — never invent values when summarising.
- Do not regenerate the dashboard at session start (that's the Stop hook's job).
Join this chat to the conversation Mode
This chat opts in to the project's global Mode as part of session start:
- Get the current state and join:
node _00-Project-Management/93-Scripts/mode.js join --session <session_id>node _00-Project-Management/93-Scripts/mode.js get --json - Lead the session announcement with the active mode, e.g.
"Tandem mode: DEV (set by you, 2026-06-03). I'll nudge on planning requests."
If mode is
neutral, say so and note the user can set one with/mode <plan|dev|dual|neutral>.
Use the session ID from the session context as <session_id>.
End-of-session-start
Always end with a single concrete suggested next action — not a menu. The user can override; the default should be obvious.