feat: structured session handoff artifact for cross-session continuity (#940) (#1122)

Enhance /gsd:pause-work to write .planning/HANDOFF.json alongside
.continue-here.md. The JSON provides machine-readable state that
/gsd:resume-work can parse for precise resumption.

HANDOFF.json includes:
- Task position (phase, plan, task number, status)
- Completed and remaining tasks with commit hashes
- Blockers with type classification (technical/human_action/external)
- Human actions pending (API keys, approvals, manual testing)
- Uncommitted files list
- Context notes for mental model restoration

Resume-work changes:
- HANDOFF.json is primary resumption source (highest priority)
- Surfaces blockers and human actions immediately on session start
- Validates uncommitted files against git status
- Deletes HANDOFF.json after successful resumption
- Falls back to .continue-here.md if no JSON exists

Also checks for placeholder content in SUMMARY.md files to catch
false completions (frontmatter claims complete but body has TBD).

Fixes #940
This commit is contained in:
Tom Boucher
2026-03-18 12:01:25 -04:00
committed by GitHub
parent a97e4c2c6f
commit f54f3df776
5 changed files with 87 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
<purpose>
Create `.continue-here.md` handoff file to preserve complete work state across sessions. Enables seamless resumption with full context restoration.
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.
</purpose>
<required_reading>
@@ -27,10 +27,61 @@ If no active phase detected, ask user which phase they're pausing work on.
3. **Work remaining**: What's left in current plan/phase
4. **Decisions made**: Key decisions and rationale
5. **Blockers/issues**: Anything stuck
6. **Mental context**: The approach, next steps, "vibe"
7. **Files modified**: What's changed but not committed
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
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
```
Report any summaries with placeholder content as incomplete items.
</step>
<step name="write_structured">
**Write structured handoff to `.planning/HANDOFF.json`:**
```bash
timestamp=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" 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}"}
],
"human_actions_pending": [
{"action": "{what needs to be done}", "context": "{why}", "blocking": true}
],
"decisions": [
{"decision": "{what}", "rationale": "{why}", "phase": "{phase_number}"}
],
"uncommitted_files": [],
"next_action": "{specific first action when resuming}",
"context_notes": "{mental state, approach, what you were thinking}"
}
```
</step>
<step name="write">
@@ -92,19 +143,22 @@ timestamp=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" current-timesta
<step name="commit">
```bash
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "wip: [phase-name] paused at task [X]/[Y]" --files .planning/phases/*/.continue-here.md
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "wip: [phase-name] paused at task [X]/[Y]" --files .planning/phases/*/.continue-here.md .planning/HANDOFF.json
```
</step>
<step name="confirm">
```
✓ Handoff created: .planning/phases/[XX-name]/.continue-here.md
✓ Handoff created:
- .planning/HANDOFF.json (structured, machine-readable)
- .planning/phases/[XX-name]/.continue-here.md (human-readable)
Current state:
- Phase: [XX-name]
- 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

View File

@@ -63,6 +63,9 @@ cat .planning/PROJECT.md
Look for incomplete work that needs attention:
```bash
# Check for structured handoff (preferred — machine-readable)
cat .planning/HANDOFF.json 2>/dev/null
# Check for continue-here files (mid-plan resumption)
ls .planning/phases/*/.continue-here*.md 2>/dev/null
@@ -78,7 +81,18 @@ if [ "$has_interrupted_agent" = "true" ]; then
fi
```
**If .continue-here file exists:**
**If HANDOFF.json exists:**
- This is the primary resumption source — structured data from `/gsd:pause-work`
- Parse `status`, `phase`, `plan`, `task`, `total_tasks`, `next_action`
- Check `blockers` and `human_actions_pending` — surface these immediately
- Check `completed_tasks` for `in_progress` items — these need attention first
- Validate `uncommitted_files` against `git status` — flag divergence
- Use `context_notes` to restore mental model
- Flag: "Found structured handoff — resuming from task {task}/{total_tasks}"
- **After successful resumption, delete HANDOFF.json** (it's a one-shot artifact)
**If .continue-here file exists (fallback):**
- This is a mid-plan resumption point
- Read the file for specific resumption context
@@ -145,8 +159,12 @@ Based on project state, determine the most logical next action:
→ Primary: Resume interrupted agent (Task tool with resume parameter)
→ Option: Start fresh (abandon agent work)
**If HANDOFF.json exists:**
→ Primary: Resume from structured handoff (highest priority — specific task/blocker context)
→ Option: Discard handoff and reassess from files
**If .continue-here file exists:**
→ Primary: Resume from checkpoint
→ Fallback: Resume from checkpoint
→ Option: Start fresh on current plan
**If incomplete plan (PLAN without SUMMARY):**