@~/.claude/gsd-core/references/response-language-directive.md Create structured `.planning/HANDOFF.json` and `.continue-here.md` handoff files to preserve complete work state across sessions. The JSON provides machine-readable state for `/gsd:resume-work`; the markdown provides human-readable context. Read all files referenced by the invoking prompt's execution_context before starting. ## Context Detection Determine what kind of work is being paused and set the handoff destination accordingly: ```bash # Check for active phase phase=$(ls -t .planning/phases/*/PLAN.md 2>/dev/null | head -1 || true) phase=${phase:+$(basename "$(dirname "$phase")")} # Check for active spike spike=$(ls -t .planning/spikes/*/SPIKE.md .planning/spikes/*/DESIGN.md .planning/spikes/*/README.md 2>/dev/null | head -1 || true) spike=${spike:+$(basename "$(dirname "$spike")")} # Check for active sketch sketch=$(ls -t .planning/sketches/*/README.md .planning/sketches/*/index.html 2>/dev/null | head -1 || true) sketch=${sketch:+$(basename "$(dirname "$sketch")")} # Check for active deliberation deliberation=$(ls .planning/deliberations/*.md 2>/dev/null | head -1 || true) ``` - **Phase work**: active phase directory → handoff to `.planning/phases/XX-name/.continue-here.md` - **Spike work**: active spike directory or spike-related files (no active phase) → handoff to `.planning/spikes/SPIKE-NNN/.continue-here.md` (create directory if needed) - **Sketch work**: active sketch directory (no active phase/spike) → handoff to `.planning/sketches/.continue-here.md` - **Deliberation work**: active deliberation file (no phase/spike/sketch) → handoff to `.planning/deliberations/.continue-here.md` - **Research work**: research notes exist but no phase/spike/sketch/deliberation → handoff to `.planning/.continue-here.md` - **Default**: no detectable context → handoff to `.planning/.continue-here.md`, note the ambiguity in `` If phase is detected, proceed with phase handoff path. Otherwise use the first matching non-phase path above. **Collect complete state for handoff:** 1. **Current position**: Which phase, which plan, which task 2. **Work completed**: What got done this session 3. **Work remaining**: What's left in current plan/phase 4. **Decisions made**: Key decisions and rationale 5. **Blockers/issues**: Anything stuck 6. **Human actions pending**: Things that need manual intervention (MCP setup, API keys, approvals, manual testing) 7. **Background processes**: Any running servers/watchers that were part of the workflow 8. **Files modified**: What's changed but not committed 9. **Outstanding async external jobs**: any `.planning/async-jobs/*.json` manifests for non-terminal jobs — record job id, backend, status, expected artifacts, verification + resume commands, and any watcher/daemon state. Do NOT cancel the external job; it keeps running across the pause. 10. **Blocking constraints**: Anti-patterns or methodological failures encountered during this session that a resuming agent MUST be aware of before proceeding. Only include items discovered through actual failure — not warnings or predictions. Assign each constraint a `severity`: - `blocking` — The resuming agent MUST demonstrate understanding before proceeding. The discuss-phase and execute-phase workflows will enforce a mandatory understanding check. - `advisory` — Important context but does not gate resumption. Ask user for clarifications if needed via conversational questions. **Also inspect SUMMARY.md files for false completions:** ```bash # Check for placeholder content in existing summaries grep -l "To be filled\|placeholder\|TBD" .planning/phases/*/*.md 2>/dev/null || true ``` Report any summaries with placeholder content as incomplete items. **Write structured handoff to `.planning/HANDOFF.json`:** ```bash _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif _gsd_at "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd_run is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; case "$(gsd_run runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') GSD_IDENTITY_STATUS=ok;; esac; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi timestamp=$(gsd_run query current-timestamp full --raw) ``` ```json { "version": "1.0", "timestamp": "{timestamp}", "phase": "{phase_number}", "phase_name": "{phase_name}", "phase_dir": "{phase_dir}", "plan": {current_plan_number}, "task": {current_task_number}, "total_tasks": {total_task_count}, "status": "paused", "completed_tasks": [ {"id": 1, "name": "{task_name}", "status": "done", "commit": "{short_hash}"}, {"id": 2, "name": "{task_name}", "status": "done", "commit": "{short_hash}"}, {"id": 3, "name": "{task_name}", "status": "in_progress", "progress": "{what_done}"} ], "remaining_tasks": [ {"id": 4, "name": "{task_name}", "status": "not_started"}, {"id": 5, "name": "{task_name}", "status": "not_started"} ], "blockers": [ {"description": "{blocker}", "type": "technical|human_action|external", "workaround": "{if any}"} ], "async_jobs": [ {"manifest": ".planning/async-jobs/{job}.json", "job_id": "{id}", "backend": "{backend}", "status": "running", "submit_command": "{cmd}", "submitted_at": "{iso8601}", "expected_artifacts": ["..."], "verification_command": "{cmd}", "resume_command": "{cmd}"} ], "human_actions_pending": [ {"action": "{what needs to be done}", "context": "{why}", "blocking": true} ], "decisions": [ {"decision": "{what}", "rationale": "{why}", "phase": "{phase_number}"} ], "uncommitted_files": ["XY path", "..."], # #3968: MEASURED — see below "next_action": "{specific first action when resuming}", "context_notes": "{mental state, approach, what you were thinking}" } ``` Any recorded `async_jobs` entries are the primary resume context on the next session — check them first before treating a PLAN-without-SUMMARY as incomplete work. **`uncommitted_files` is measured, never asserted (#3968).** Populate it from an actual call, not from memory — a narrated `[]` over a dirty tree is how 14 plans' worth of uncommitted code went invisible in the wild: ```bash UNCOMMITTED=$(git status --porcelain) # One array entry per line ("XY path"); truncate the list at 50 entries and note the # elided count, but NEVER round it to empty — a non-empty porcelain output is the single # most load-bearing fact a resume session needs. ``` **Write handoff to the path determined in the detect step** (e.g. `.planning/phases/XX-name/.continue-here.md`, `.planning/spikes/SPIKE-NNN/.continue-here.md`, or `.planning/.continue-here.md`): ```markdown --- context: [phase|spike|sketch|deliberation|research|default] phase: XX-name task: 3 total_tasks: 7 status: in_progress last_updated: [timestamp from current-timestamp] --- # BLOCKING CONSTRAINTS — Read Before Anything Else > These are not suggestions. Each constraint below was discovered through failure. > Acknowledge each one explicitly before proceeding. - [ ] CONSTRAINT: [name] — [what it is] — [structural mitigation required] **Do not proceed until all boxes are checked.** _If no constraints have been identified yet, remove this section._ ## Critical Anti-Patterns | Pattern | Description | Severity | Prevention Mechanism | |---------|-------------|----------|---------------------| | [pattern name] | [what it is and how it manifested] | blocking | [structural step that prevents recurrence — not acknowledgment] | | [pattern name] | [what it is and how it manifested] | advisory | [guidance for avoiding it] | **Severity values:** `blocking` — resuming agent must pass understanding check before proceeding. `advisory` — important context, does not gate resumption. _Remove rows that do not apply. The discuss-phase and execute-phase workflows parse this table and enforce a mandatory understanding check for any `blocking` rows._ [Where exactly are we? Immediate context] Completed Tasks: - Task 1: [name] - Done - Task 2: [name] - Done - Task 3: [name] - In progress, [what's done] - Task 3: [what's left] - Task 4: Not started - Task 5: Not started - Decided to use [X] because [reason] - Chose [approach] over [alternative] because [reason] - [Blocker 1]: [status/workaround] ## Required Reading (in order) 1. [document] — [why it matters] 1. `.planning/METHODOLOGY.md` (if it exists) — project analytical lenses; apply before any assumption analysis ## Critical Anti-Patterns (do NOT repeat these) - [ANTI-PATTERN]: [what it is] → [structural mitigation] ## Infrastructure State - [service/env]: [current state] ## Pre-Execution Critique Required - Design artifact: [path] - Critique focus: [key questions the critic should probe] - Gate: Do NOT begin execution until critique is complete and design is revised [Mental state, what were you thinking, the plan] Start with: [specific first action when resuming] ``` Be specific enough for a fresh Claude to understand immediately. Use `current-timestamp` for last_updated field. You can use init todos (which provides timestamps) or call directly: ```bash timestamp=$(gsd_run query current-timestamp full --raw) ``` ```bash gsd_run query commit "wip: [context-name] paused at [X]/[Y]" --files [handoff-path] .planning/HANDOFF.json ``` ``` ✓ Handoff created: - .planning/HANDOFF.json (structured, machine-readable) - [handoff-path] (human-readable) Current state: - Context: [phase|spike|deliberation|research] - Location: [XX-name or SPIKE-NNN] - Task: [X] of [Y] - Status: [in_progress/blocked] - Blockers: [count] ({human_actions_pending count} need human action) - Committed as WIP To resume: /gsd:resume-work ``` - [ ] Context detected (phase/spike/deliberation/research/default) - [ ] .continue-here.md created at correct path for detected context - [ ] Required Reading, Anti-Patterns, and Infrastructure State sections filled - [ ] Pre-Execution Critique section filled if pausing between design and execution - [ ] Committed as WIP - [ ] User knows location and how to resume