diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0c4fe38..44fe25b 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ { "name": "shipspec", "description": "Spec-driven development for Claude Code. Plan bigger features with persistent PRDs, technical designs, and implementation tasks that keep your AI coding agent on track across sessions.", - "version": "1.0.0", + "version": "1.1.0", "author": { "name": "ShipSpec" }, diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 14ff92b..8cbdbd0 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "shipspec", "description": "Spec-driven development for big features. When features get too big, plan mode gets too vague—leading to hallucinations during implementation. ShipSpec replaces vague plans with structured PRDs, technical designs, and ordered tasks that keep Claude grounded.", - "version": "1.0.0", + "version": "1.1.0", "author": { "name": "ShipSpec" }, diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a93bb33 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,217 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +ShipSpec is a Claude Code plugin for spec-driven feature development. It replaces vague plans with structured PRDs (Product Requirements Documents), SDDs (Software Design Documents), and ordered tasks with acceptance criteria. + +## Plugin Architecture + +``` +shipspec/ +├── commands/ # User-invocable slash commands +│ ├── feature-planning.md # 7-phase planning workflow +│ ├── implement-task.md # Single task implementation with verification +│ ├── implement-feature.md # Full feature implementation loop +│ ├── cancel-task-loop.md # Cancel active task retry loop +│ └── cancel-feature-retry.md +├── agents/ # Specialized subagents (Task tool invocations) +│ ├── prd-gatherer.md # Requirements elicitation +│ ├── design-architect.md # Technical design decisions +│ ├── task-planner.md # Task decomposition +│ ├── task-manager.md # TASKS.json parsing/validation/status updates +│ ├── task-verifier.md # Acceptance criteria verification +│ └── planning-validator.md # PRD/SDD alignment checking +├── skills/ # Reusable skill templates (invoked via skill loader) +│ ├── prd-template/ # PRD structure and patterns +│ ├── sdd-template/ # Atlassian 8-section SDD format +│ ├── codebase-context/ # Tech stack extraction +│ ├── agent-prompts/ # Task prompt generation +│ ├── research/ # Web/doc research patterns +│ └── task-loop-verify/ # Loop verification skill +├── hooks/ # Stop hooks for Ralph Loop methodology +│ ├── hooks.json # Hook registration +│ ├── task-loop-hook.sh # Per-task auto-retry +│ ├── feature-retry-hook.sh # Feature-wide task retry +│ └── planning-refine-hook.sh # Large task refinement +└── .claude-plugin/ # Plugin metadata + └── plugin.json +``` + +## Key Workflows + +### Feature Planning (`/feature-planning`) +7-phase workflow: Description → Setup → Requirements Gathering → PRD Generation → Technical Decisions → SDD Generation → Task Generation + +Output: +- `.shipspec/planning/{feature}/PRD.md` - Product requirements +- `.shipspec/planning/{feature}/SDD.md` - Technical design +- `.shipspec/planning/{feature}/TASKS.json` - Machine-parseable task metadata +- `.shipspec/planning/{feature}/TASKS.md` - Human-readable task prompts + +### Task Implementation (`/implement-task`, `/implement-feature`) +1. Parse and validate TASKS.json via `task-manager` agent +2. Find next ready task (dependencies satisfied, status = `not_started`) +3. Update status to `in_progress` via task-manager `update_status` operation +4. Create loop state file (JSON) for auto-retry +5. Implement and verify via `task-verifier` agent +6. Update status to `completed` on success, retry on failure + +## Task File Structure + +### TASKS.json (Machine-Parseable) +```json +{ + "version": "1.0", + "feature": "feature-name", + "summary": { + "total_tasks": 5, + "total_points": 18, + "critical_path": ["TASK-001", "TASK-003", "TASK-005"] + }, + "phases": [ + { "id": 1, "name": "Foundation" } + ], + "tasks": { + "TASK-001": { + "title": "Setup Database Schema", + "status": "not_started", + "phase": 1, + "points": 3, + "depends_on": [], + "blocks": ["TASK-002"], + "prd_refs": ["REQ-001"], + "sdd_refs": ["Section 5.1"], + "acceptance_criteria": ["Schema file exists", "Migration runs"], + "testing": ["npm run db:migrate"], + "prompt": "## Context\n..." + } + } +} +``` + +### TASKS.md (Human-Readable) +Contains context, requirements prose, technical approach, files to create/modify, key interfaces, and constraints. No status markers or dependencies (those live in JSON). + +### Task Status Values +- `"not_started"` - Task not yet begun +- `"in_progress"` - Currently being implemented +- `"completed"` - All acceptance criteria verified + +## Ralph Loop Methodology + +Stop hooks intercept Claude's exit and feed prompts back until completion markers are detected: +- `VERIFIED|INCOMPLETE|BLOCKED|MISALIGNED` +- `VERIFIED|BLOCKED` +- `APPROVED|APPROVED_WITH_WARNINGS` + +State files are stored alongside planning artifacts in `.shipspec/planning//`. + +## Hook Behavior + +All hooks share stdin sequentially. Each hook: +1. Checks for pointer file (`.shipspec/active-loop.local.json`) first +2. Parses pointer JSON to check if this hook's loop type is active +3. If not this hook's loop type, exits immediately (preserves stdin for other hooks) +4. If active, reads the state file from `.shipspec/planning//.local.json` +5. Reads task prompt from TASKS.json: `jq -r '.tasks[TASK_ID].prompt' TASKS.json` + +Empty stdin detection prevents erroneous state file deletion when multiple hooks are active. + +## Development Notes + +### Testing Hooks +Hooks expect JSON input with `transcript_path` field. Test with: +```bash +echo '{"transcript_path": "/path/to/transcript.jsonl"}' | ./hooks/task-loop-hook.sh +``` + +### Pointer File Format (`.shipspec/active-loop.local.json`) +```json +{ + "feature": "feature-name", + "loop_type": "task-loop", + "state_path": ".shipspec/planning/feature-name/task-loop.local.json", + "created_at": "2024-01-15T10:30:00Z" +} +``` + +Supported loop types: `task-loop`, `feature-retry`, `planning-refine` + +### State File Format (`.shipspec/planning//.local.json`) + +**task-loop.local.json:** +```json +{ + "active": true, + "feature": "feature-name", + "task_id": "TASK-001", + "iteration": 1, + "max_iterations": 5, + "started_at": "2024-01-15T10:30:00Z" +} +``` + +**feature-retry.local.json:** +```json +{ + "active": true, + "feature": "feature-name", + "current_task_id": "TASK-001", + "task_attempt": 1, + "max_task_attempts": 5, + "tasks_completed": 0, + "total_tasks": 5, + "started_at": "2024-01-15T10:30:00Z" +} +``` + +**planning-refine.local.json:** +```json +{ + "active": true, + "feature": "feature-name", + "iteration": 1, + "max_iterations": 3, + "large_tasks": ["TASK-003", "TASK-007"], + "tasks_refined": 0, + "started_at": "2024-01-15T10:30:00Z" +} +``` + +Note: Task prompts are read from TASKS.json, not stored in state files. + +### Requirement Numbering +- REQ-001 to REQ-009: Core Features +- REQ-010 to REQ-019: User Interface +- REQ-020 to REQ-029: Data & Storage +- REQ-030 to REQ-039: Integration +- REQ-040 to REQ-049: Performance +- REQ-050 to REQ-059: Security + +### Task Sizing +Fibonacci story points (1, 2, 3, 5, 8). Tasks >5 points trigger auto-refinement. + +### JSON Operations (jq) +All hooks and commands use `jq` for JSON parsing: +```bash +# Read task status +jq -r '.tasks["TASK-001"].status' TASKS.json + +# Update task status +jq '.tasks["TASK-001"].status = "in_progress"' TASKS.json > tmp && mv tmp TASKS.json + +# Read prompt +jq -r '.tasks["TASK-001"].prompt' TASKS.json + +# Increment iteration +jq '.iteration += 1' state.json > tmp && mv tmp state.json +``` + +### Version Bumping +When updating the plugin version, update both files: +- `.claude-plugin/plugin.json` - the actual plugin version +- `.claude-plugin/marketplace.json` - the version advertised in the marketplace + +These are not automatically synced. diff --git a/README.md b/README.md index 7190f1e..67b3084 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,50 @@ # ShipSpec Claude Code Plugin -**Spec-driven development for big features.** +**Spec-driven development meets the Ralph Wiggum loop.** -Claude Code's native plan mode works great—for small features. But when a feature gets big, the plan gets vague. There's just too much to capture, so you end up with high-level bullet points instead of real structure. And vague plans lead to hallucinations during implementation. +Two problems plague AI-assisted coding: -Claude loses sight of *why* it's building what it's building. It makes architecture decisions that contradict what you discussed. It "finishes" tasks that don't actually meet the requirements. The plan is there, but it's too shallow to keep a complex implementation on track. +1. **Vibe coding** — Throwing prompts at Claude without structure. It makes plausible-sounding but wrong assumptions. The bigger the feature, the more it drifts. -**The problem isn't Claude—it's that big features need more than a plan. They need a spec.** +2. **Giving up too early** — Claude does its best attempt, exits, and you're left debugging half-finished work. -ShipSpec replaces vague plans with structured PRDs, technical designs, and ordered tasks that keep Claude grounded throughout implementation. +ShipSpec solves both. Specs keep Claude grounded. Ralph loops keep Claude iterating until it's actually done. + +## How It Works + +``` +┌─────────────────────────────────────────────────────────────┐ +│ THE SPEC SIDE │ +│ │ +│ Feature Idea │ +│ ↓ │ +│ PRD (what to build + numbered requirements) │ +│ ↓ │ +│ SDD (how to build it + architecture decisions) │ +│ ↓ │ +│ TASKS.json (ordered work + acceptance criteria) │ +└─────────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────┐ +│ THE RALPH SIDE │ +│ │ +│ while task_not_verified: │ +│ implement(task) │ +│ verify(acceptance_criteria) │ +│ if failed: retry with feedback │ +└─────────────────────────────────────────────────────────────┘ +``` + +**Together:** Each task carries its requirements, design context, and verification criteria. The Ralph loop keeps running until those criteria pass. Claude can't drift because the spec is always there. It can't give up because the loop keeps it going. ## Why ShipSpec? | Problem | Solution | |---------|----------| -| Big features make plans too vague | Structured PRD with numbered requirements | -| Claude drifts from original intent | Requirements stay visible, linked to every task | -| Architecture decisions get contradicted | SDD documents design choices before implementation | -| Implementation feels chaotic | Ordered tasks with acceptance criteria and verification | - -**The result**: Claude always knows *why* it's building something (requirements) and *how* to build it (design), working through manageable chunks that build on each other. +| Vibe coding makes Claude drift | Structured PRD with numbered requirements | +| Claude forgets original intent | Requirements linked to every task | +| Architecture decisions get contradicted | SDD documents design choices upfront | +| Claude gives up on first attempt | Ralph loops retry until verified | ## Features @@ -192,6 +217,12 @@ The `/implement-feature` command implements all tasks and runs a comprehensive f - **Use `/implement-task` to work through tasks manually**: Tracks progress and verifies completion - **Use `/implement-feature` for automation**: Implements all tasks and runs comprehensive final review +## Inspiration + +ShipSpec combines two ideas: +- [Spec-driven development](https://www.atlassian.com/blog/developer/spec-driven-development-with-rovo-dev) — Planning with PRDs and SDDs before execution +- [The Ralph Wiggum loop](https://ghuntley.com/ralph/) — Iterative loops that keep going until the work is done + ## Issues & Feedback Found a bug or have a suggestion? [Submit an issue](https://github.com/jsegov/shipspec-claude-code-plugin/issues) diff --git a/agents/planning-validator.md b/agents/planning-validator.md index 109a704..cf1c586 100644 --- a/agents/planning-validator.md +++ b/agents/planning-validator.md @@ -25,7 +25,7 @@ description: Use this agent to verify task implementation aligns with SDD design model: sonnet color: cyan -tools: Read, Glob, Grep +tools: Read, Glob, Grep, Bash(jq:*) --- # Planning Validator @@ -43,7 +43,18 @@ Verify that a completed task's implementation matches: You will receive: 1. **Task ID** - The task being validated (e.g., TASK-001) 2. **Feature name** - Used to locate planning artifacts at `.shipspec/planning/{feature}/` -3. **References section** - The task's references containing SDD sections and PRD requirements + +## Data Sources + +Task references come from TASKS.json: + +```bash +# Get PRD references for a task +jq -r '.tasks["TASK-001"].prd_refs[]' .shipspec/planning/{feature}/TASKS.json + +# Get SDD references for a task +jq -r '.tasks["TASK-001"].sdd_refs[]' .shipspec/planning/{feature}/TASKS.json +``` ## Validation Process @@ -52,18 +63,25 @@ You will receive: Load the planning documents: - `.shipspec/planning/{feature}/PRD.md` - `.shipspec/planning/{feature}/SDD.md` +- `.shipspec/planning/{feature}/TASKS.json` + +If any file doesn't exist, note it and continue with available documents. -If either file doesn't exist, note it and continue with available documents. +### Step 2: Extract References from JSON -### Step 2: Parse References +Read the task's references from TASKS.json: + +```bash +jq -r '.tasks["TASK-XXX"]' .shipspec/planning/{feature}/TASKS.json +``` -Extract from the task's References section: -- **SDD references**: "Design Doc: Section X.Y" or "SDD: Section X.Y" -- **PRD references**: "PRD: REQ-XXX" or "Requirements: REQ-XXX" +Extract: +- `prd_refs` array - requirement IDs (e.g., ["REQ-001", "REQ-002"]) +- `sdd_refs` array - SDD section references (e.g., ["Section 5.1", "Section 6.2"]) ### Step 3: Validate Design Alignment (SDD) -For each SDD section reference: +For each SDD section reference in `sdd_refs`: 1. **Locate the section** in SDD.md - Search for headers matching "Section X.Y" or "X.Y" pattern @@ -85,7 +103,7 @@ For each SDD section reference: ### Step 4: Validate Requirements Coverage (PRD) -For each PRD requirement reference (REQ-XXX): +For each PRD requirement reference (REQ-XXX) in `prd_refs`: 1. **Locate the requirement** in PRD.md - Search for "REQ-XXX" pattern @@ -167,7 +185,7 @@ Output a structured report: ### Recommendation [ALIGNED] Implementation matches planning documents. Ready to proceed. [MISALIGNED] Fix the issues above before marking task complete. -[UNVERIFIED] Some references could not be verified. Consider updating references in TASKS.md or planning documents. +[UNVERIFIED] Some references could not be verified. Consider updating references in TASKS.json or planning documents. ``` ## Verification Strategies @@ -216,7 +234,7 @@ If a section reference is ambiguous (e.g., "Section 5" could match "5.1", "5.2", - Validate against the matched section ### No References Found -If the task has no SDD or PRD references: +If the task has empty `prd_refs` and `sdd_refs` arrays: - Return status: **ALIGNED** (nothing to validate) - Note: "No planning references found in task" @@ -227,8 +245,9 @@ If implementation partially matches the design: ## Important Notes -1. **Be thorough**: Check every referenced section and requirement -2. **Be specific**: Provide exact file paths, line numbers, and code snippets as evidence -3. **Be helpful**: If something fails, explain how to fix it -4. **Be honest**: If you can't verify something, mark it UNVERIFIED -5. **Check context**: Sometimes implementation details are spread across multiple files +1. **Read from TASKS.json**: All references come from the JSON file's `prd_refs` and `sdd_refs` arrays +2. **Be thorough**: Check every referenced section and requirement +3. **Be specific**: Provide exact file paths, line numbers, and code snippets as evidence +4. **Be helpful**: If something fails, explain how to fix it +5. **Be honest**: If you can't verify something, mark it UNVERIFIED +6. **Check context**: Sometimes implementation details are spread across multiple files diff --git a/agents/task-manager.md b/agents/task-manager.md index 40c6466..e8735f9 100644 --- a/agents/task-manager.md +++ b/agents/task-manager.md @@ -1,6 +1,6 @@ --- name: task-manager -description: Use this agent to manage task lifecycle operations on TASKS.md files. Examples: +description: Use this agent to manage task lifecycle operations on TASKS.json files. Examples: Context: Command needs to find the next task to implement @@ -30,25 +30,44 @@ description: Use this agent to manage task lifecycle operations on TASKS.md file Need task details for implementation - trigger task-manager + + Context: Command needs to update task status after implementation + user: "Mark TASK-003 as completed for feature auth-system" + assistant: "I'll use the task-manager agent to update the task status in TASKS.json." + Need to update task status - trigger task-manager + + model: sonnet color: blue -tools: Read, Glob, Grep +tools: Read, Glob, Grep, Write, Bash(jq:*) --- # Task Manager -You are a task lifecycle manager that parses TASKS.md files, tracks task states, manages dependencies, and provides structured task information for implementation commands. +You are a task lifecycle manager that parses TASKS.json files, tracks task states, manages dependencies, and provides structured task information for implementation commands. ## Your Mission -Given a feature name and an operation, analyze the TASKS.md file and return structured data about task states, dependencies, and next actions. You are **read-only** - you analyze but never modify files. +Given a feature name and an operation, analyze the TASKS.json file and return structured data about task states, dependencies, and next actions. ## Input You will receive: -1. **Feature name** - Used to locate TASKS.md at `.shipspec/planning/{feature}/TASKS.md` -2. **Operation** - One of: `parse`, `find_next`, `find_in_progress`, `validate`, `get_task`, `get_progress` -3. **Task ID** (optional) - For `get_task` operation +1. **Feature name** - Used to locate files at `.shipspec/planning/{feature}/` +2. **Operation** - One of: `parse`, `find_next`, `find_in_progress`, `validate`, `get_task`, `get_progress`, `update_status` +3. **Task ID** (optional) - For `get_task` and `update_status` operations +4. **New Status** (optional) - For `update_status` operation: `not_started`, `in_progress`, or `completed` + +## File Structure + +Tasks are stored in two files: + +| File | Purpose | Used For | +|------|---------|----------| +| `TASKS.json` | Machine-parseable metadata | Status, dependencies, acceptance criteria, prompts | +| `TASKS.md` | Human-readable content | Reference only (not parsed for state) | + +**Source of Truth:** TASKS.json is authoritative for all plugin operations. --- @@ -56,17 +75,12 @@ You will receive: ### Operation: `parse` -Load and parse TASKS.md into a structured task map. +Load and parse TASKS.json into a structured task map. **Process:** -1. Read `.shipspec/planning/{feature}/TASKS.md` -2. Extract all tasks with: - - Task ID (TASK-XXX format) - - Title (from task header) - - Status: `[ ]` (not_started), `[~]` (in_progress), `[x]` (completed) - - Dependencies (from `Depends on:` or `Dependencies:` line) - - Phase (from section header, if present) -3. Build dependency graph +1. Read `.shipspec/planning/{feature}/TASKS.json` +2. Validate JSON structure +3. Extract task map with all fields 4. Calculate statistics **Output Format:** @@ -74,25 +88,26 @@ Load and parse TASKS.md into a structured task map. ## Parse Result **Feature:** {feature} -**File:** .shipspec/planning/{feature}/TASKS.md +**File:** .shipspec/planning/{feature}/TASKS.json ### Task Map -| ID | Title | Status | Dependencies | Phase | -|----|-------|--------|--------------|-------| -| TASK-001 | Setup database schema | completed | None | Foundation | -| TASK-002 | Create user types | not_started | TASK-001 | Foundation | -| TASK-003 | Implement API | not_started | TASK-001, TASK-002 | Core | +| ID | Title | Status | Dependencies | Phase | Points | +|----|-------|--------|--------------|-------|--------| +| TASK-001 | Setup database schema | completed | None | 1 | 3 | +| TASK-002 | Create user types | not_started | TASK-001 | 1 | 2 | +| TASK-003 | Implement API | not_started | TASK-001, TASK-002 | 2 | 3 | ### Statistics | Metric | Value | |--------|-------| | Total Tasks | X | -| Completed | Y | -| In Progress | Z | -| Not Started | W | -| Remaining | Z + W | +| Total Points | Y | +| Completed | Z (W%) | +| In Progress | A | +| Not Started | B | +| Remaining Points | C | ``` --- @@ -102,8 +117,8 @@ Load and parse TASKS.md into a structured task map. Find the first task that is ready to start (dependencies satisfied). **Criteria:** -1. Status is `[ ]` (not_started) -2. All dependencies have status `[x]` (completed) +1. Status is `"not_started"` +2. All tasks in `depends_on` have status `"completed"` 3. Return first matching task in document order **Output Format:** @@ -117,7 +132,8 @@ Find the first task that is ready to start (dependencies satisfied). - **Task ID:** TASK-XXX - **Title:** [title] - **Dependencies:** [list or "None"] -- **Phase:** [phase name] +- **Phase:** [phase number] +- **Points:** [story points] ### ALL_COMPLETE @@ -125,6 +141,7 @@ All tasks have been completed. No remaining work. - **Total Tasks:** X - **Completed:** X +- **Total Points:** Y ### BLOCKED @@ -146,7 +163,7 @@ No tasks are currently ready. The following tasks are blocked: ### Operation: `find_in_progress` -Find any task(s) with status `[~]`. +Find any task(s) with status `"in_progress"`. **Output Format:** ```markdown @@ -163,6 +180,7 @@ No tasks are currently in progress. - **Task ID:** TASK-XXX - **Title:** [title] - **Phase:** [phase] +- **Points:** [story points] ### MULTIPLE @@ -172,20 +190,21 @@ In-progress tasks: - **TASK-XXX:** [title] - **TASK-YYY:** [title] -**Action Required:** Resolve by marking all but one as `[ ]` (not started) or `[x]` (completed), then run the command again. +**Action Required:** Run `update_status` to mark all but one as `not_started` or `completed`. ``` --- ### Operation: `validate` -Check TASKS.md for structural issues. +Check TASKS.json for structural issues. **Checks:** -1. Circular dependencies -2. Multiple in-progress tasks -3. Invalid task ID references in dependencies -4. Missing dependencies (task depends on non-existent task) +1. Valid JSON structure +2. Circular dependencies +3. Multiple in-progress tasks +4. Invalid task ID references in depends_on/blocks +5. Missing required fields **Output Format:** ```markdown @@ -198,7 +217,8 @@ Check TASKS.md for structural issues. No issues found. Task structure is valid. - **Total Tasks:** X -- **Dependency Chains:** Valid DAG +- **Total Points:** Y +- **Dependency Graph:** Valid DAG ### INVALID @@ -211,26 +231,29 @@ Cycle detected: TASK-001 -> TASK-002 -> TASK-003 -> TASK-001 ``` -**Fix:** Remove one of the dependencies to break the cycle. +**Fix:** Update TASKS.json to remove one of the dependencies. #### Multiple In-Progress Tasks -Tasks marked `[~]`: +Tasks with status "in_progress": - TASK-XXX: [title] - TASK-YYY: [title] -**Fix:** Mark all but one as `[ ]` or `[x]`. +**Fix:** Use `update_status` to mark all but one as `not_started` or `completed`. #### Invalid References - TASK-003 depends on **TASK-999** (not found) -- TASK-005 depends on **TASK-010** (not found) +- TASK-005 blocks **TASK-010** (not found) -**Fix:** Update the `Depends on:` line to reference existing tasks. +**Fix:** Update the dependency arrays to reference existing tasks. -### Recommendation +#### Missing Fields -[Specific steps to fix the issues] +- TASK-003 missing required field: `acceptance_criteria` +- TASK-005 missing required field: `prompt` + +**Fix:** Add missing fields to the task objects. ``` --- @@ -258,16 +281,28 @@ Get a specific task by ID with full prompt content. - **Task ID:** TASK-XXX - **Title:** [title] - **Current Status:** not_started | in_progress -- **Phase:** [phase] +- **Phase:** [phase number] +- **Points:** [story points] - **Dependencies:** [list with their statuses] +- **PRD Refs:** REQ-001, REQ-002 +- **SDD Refs:** Section 5.1 + +#### Acceptance Criteria +1. [criterion 1] +2. [criterion 2] +3. [criterion 3] + +#### Testing Requirements +- [test command 1] +- [test command 2] #### Full Task Prompt -[Complete task content from TASKS.md, from header to next task header] +[Complete prompt content from TASKS.json tasks[id].prompt field] ### NOT_FOUND -Task **[task-id]** not found in TASKS.md. +Task **[task-id]** not found in TASKS.json. Available tasks: - TASK-001: [title] @@ -276,7 +311,7 @@ Available tasks: ### ALREADY_COMPLETED -Task **TASK-XXX** is already completed (status `[x]`). +Task **TASK-XXX** is already completed (status: "completed"). Choose a different task or omit the task-id to get the next available. @@ -309,18 +344,24 @@ Get completion statistics for the feature. | Metric | Value | |--------|-------| | Total Tasks | X | -| Completed | Y (Z%) | -| In Progress | A | -| Not Started | B | -| Blocked | C | +| Total Points | Y | +| Completed Tasks | Z (W%) | +| Completed Points | A (B%) | +| In Progress | C | +| Not Started | D | +| Blocked | E | ### Phase Progress -| Phase | Completed | Total | Status | -|-------|-----------|-------|--------| -| Foundation | 2 | 3 | In Progress | -| Core | 0 | 5 | Blocked | -| Testing | 0 | 2 | Not Started | +| Phase | Name | Completed | Total | Points Done | Status | +|-------|------|-----------|-------|-------------|--------| +| 1 | Foundation | 2 | 3 | 5/8 | In Progress | +| 2 | Core | 0 | 5 | 0/15 | Blocked | +| 3 | Testing | 0 | 2 | 0/6 | Not Started | + +### Critical Path + +TASK-001 → TASK-003 → TASK-005 → TASK-007 ### Current State @@ -331,104 +372,126 @@ Get completion statistics for the feature. --- -## Parsing Rules +### Operation: `update_status` -### Task Header Detection +Update a task's status in TASKS.json. -Look for these patterns: -``` -### - [ ] TASK-001: Title Here -> not_started -### - [~] TASK-002: Title Here -> in_progress -### - [x] TASK-003: Title Here -> completed -``` +**Input:** +- Task ID (required) +- New Status (required): `"not_started"`, `"in_progress"`, or `"completed"` -Also handle variations: -``` -### - [ ] TASK-001 - Title Here (dash separator) -### - [ ] TASK-001 Title Here (space separator) -``` +**Process:** +1. Read TASKS.json +2. Validate task exists +3. Update status field +4. Write back to TASKS.json -### Task ID Normalization +**Output Format:** +```markdown +## Status Update -Always normalize to `TASK-XXX` format (zero-padded to 3 digits): -- Input: `3`, `03`, `003`, `T03`, `TASK-003`, `task-003` -- Output: `TASK-003` +**Status:** SUCCESS | NOT_FOUND | INVALID_STATUS -### Dependency Extraction +### SUCCESS -Look for these patterns within task content: -``` -Depends on: TASK-001, TASK-002 -Dependencies: TASK-001 -Depends on: None -- Depends on: TASK-001 +Task **TASK-XXX** status updated: `[old_status]` → `[new_status]` + +### NOT_FOUND + +Task **[task-id]** not found in TASKS.json. + +### INVALID_STATUS + +Invalid status: "[status]" + +Valid values: `not_started`, `in_progress`, `completed` ``` -A task with `Depends on: None`, empty dependencies, or no dependencies line is considered to have no dependencies. +--- -### Phase Detection +## JSON Parsing -Tasks are grouped under phase headers: -```markdown -## Phase 1: Foundation -### - [ ] TASK-001: ... +### Reading TASKS.json + +Use jq for JSON operations: + +```bash +# Read entire file +jq '.' .shipspec/planning/{feature}/TASKS.json -## Phase 2: Core Implementation -### - [ ] TASK-002: ... +# Get task status +jq -r '.tasks["TASK-001"].status' TASKS.json + +# Find not_started tasks +jq -r '.tasks | to_entries[] | select(.value.status == "not_started") | .key' TASKS.json + +# Check if all dependencies completed +jq -r '.tasks["TASK-003"].depends_on[] as $dep | .tasks[$dep].status' TASKS.json ``` -Extract the phase name for each task based on the nearest preceding `## Phase` header. +### Writing TASKS.json -### Task Prompt Extraction +Use jq for updates: -The full task prompt includes everything from the task header (`### - [ ] TASK-XXX:`) to either: -- The next task header (`### - [ ] TASK-YYY:`) -- The next phase header (`## Phase`) -- The end of the document +```bash +# Update task status +jq '.tasks["TASK-001"].status = "in_progress"' TASKS.json > tmp && mv tmp TASKS.json +``` + +### Task ID Normalization + +Always normalize to `TASK-XXX` format (zero-padded to 3 digits): +- Input: `3`, `03`, `003`, `T03`, `TASK-003`, `task-003` +- Output: `TASK-003` + +### Dependency Resolution + +A task is ready when: +1. Its status is `"not_started"` +2. Its `depends_on` array is empty, OR +3. All tasks in `depends_on` have status `"completed"` --- ## Error Handling -### TASKS.md Not Found +### TASKS.json Not Found ```markdown -**Error:** TASKS.md not found +**Error:** TASKS.json not found -File not found at: `.shipspec/planning/{feature}/TASKS.md` +File not found at: `.shipspec/planning/{feature}/TASKS.json` **Action:** Run `/feature-planning {feature}` to create planning artifacts. ``` -### Empty TASKS.md +### Invalid JSON ```markdown -**Error:** No tasks found +**Error:** Invalid JSON -TASKS.md exists but contains no parseable tasks. +Failed to parse TASKS.json: [error message] -**Action:** Tasks may need to be generated. Run `/feature-planning {feature}` to complete the planning workflow. +**Action:** Check file for syntax errors or regenerate using `/feature-planning`. ``` -### Malformed Task +### Empty Tasks ```markdown -**Warning:** Could not parse task - -Line X: [problematic content] +**Error:** No tasks found -This task will be skipped. Please fix the task header format. +TASKS.json exists but contains no tasks in the `tasks` object. -Expected format: `### - [ ] TASK-XXX: Title` +**Action:** Tasks may need to be generated. Run `/feature-planning {feature}` to complete the planning workflow. ``` --- ## Important Notes -1. **Read-only:** This agent analyzes but never modifies TASKS.md -2. **Deterministic ordering:** Always return tasks in document order +1. **TASKS.json is source of truth:** All status, dependencies, and prompts come from JSON +2. **Deterministic ordering:** Return tasks in sorted ID order (TASK-001, TASK-002, etc.) 3. **Normalize IDs:** Always output TASK-XXX format regardless of input format -4. **Validate dependencies:** Check that all referenced dependencies exist -5. **Be precise:** Include exact task IDs and titles for clarity -6. **Handle edge cases:** Empty deps, missing phases, malformed entries +4. **Validate before updates:** Check task exists before any update_status operation +5. **Atomic writes:** Use tmp file pattern for safe JSON updates +6. **Handle missing fields:** Use defaults or report as validation error diff --git a/agents/task-planner.md b/agents/task-planner.md index 4afc2b4..889df92 100644 --- a/agents/task-planner.md +++ b/agents/task-planner.md @@ -25,13 +25,22 @@ description: Use this agent for task decomposition and planning. Examples: model: sonnet color: green -tools: Read, Glob, Grep, Bash(find:*), Bash(wc:*) +tools: Read, Glob, Grep, Write, Bash(find:*), Bash(wc:*) --- # Task Planner You are a technical project manager specializing in breaking down features into well-defined, implementable tasks. Your task lists should be immediately usable by coding agents. +## Output Files + +You will generate TWO files: + +| File | Purpose | +|------|---------| +| `TASKS.json` | Machine-parseable metadata for plugin operations | +| `TASKS.md` | Human-readable task prompts | + ## Your Process ### Phase 1: Review Inputs @@ -63,15 +72,132 @@ For each task: 2. **Identify dependencies** (what must complete first) 3. **Assign category** (feature/infrastructure/testing/docs/security/performance) -### Phase 4: Create Task Prompts +### Phase 4: Generate Output Files + +Generate both TASKS.json and TASKS.md using the formats below. + +## TASKS.json Format + +```json +{ + "version": "1.0", + "feature": "feature-name", + "summary": { + "total_tasks": 5, + "total_points": 18, + "critical_path": ["TASK-001", "TASK-003", "TASK-005"] + }, + "phases": [ + { "id": 1, "name": "Foundation" }, + { "id": 2, "name": "Core Implementation" }, + { "id": 3, "name": "Polish" } + ], + "tasks": { + "TASK-001": { + "title": "Setup Database Schema", + "status": "not_started", + "phase": 1, + "points": 3, + "depends_on": [], + "blocks": ["TASK-002", "TASK-003"], + "prd_refs": ["REQ-001", "REQ-002"], + "sdd_refs": ["Section 5.1"], + "acceptance_criteria": [ + "Schema file exists at db/schema.sql", + "All tables have primary keys", + "Foreign key relationships match SDD", + "Migration runs without errors" + ], + "testing": [ + "Run migration: npm run db:migrate", + "Verify tables: npm run db:verify" + ], + "prompt": "## Context\nThis task establishes the data layer for the feature...\n\n## Requirements\n- Create users table with id, email, created_at\n- Create sessions table with foreign key to users\n\n## Technical Approach\n\n### Suggested Implementation\n1. Create migration file in db/migrations/\n2. Define table structures\n3. Add indexes for common queries\n\n### Files to Create/Modify\n- `db/migrations/002_add_users.sql` - New migration\n- `db/schema.sql` - Update documentation\n\n## Constraints\n- Follow existing naming conventions (snake_case)\n- Use SERIAL for auto-increment IDs" + } + } +} +``` + +### Task Fields Reference + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `title` | string | Yes | Clear, action-oriented task title | +| `status` | enum | Yes | Always `"not_started"` for new tasks | +| `phase` | integer | Yes | Phase number (1-indexed) | +| `points` | integer | Yes | Fibonacci: 1, 2, 3, 5, 8 | +| `depends_on` | array | Yes | Task IDs that must complete first | +| `blocks` | array | Yes | Task IDs that depend on this | +| `prd_refs` | array | Yes | Requirement IDs (REQ-XXX) | +| `sdd_refs` | array | Yes | SDD section references | +| `acceptance_criteria` | array | Yes | Verifiable completion criteria | +| `testing` | array | Yes | Test commands/verification steps | +| `prompt` | string | Yes | Full implementation prompt (escaped markdown) | + +## TASKS.md Format + +The markdown file is human-readable. No status markers, dependencies, or acceptance criteria (those are in JSON). + +```markdown +# Implementation Tasks: [Feature Name] + +## Summary + +- Total Tasks: 5 +- Total Story Points: 18 +- Critical Path: TASK-001 → TASK-003 → TASK-005 + +## Requirement Coverage + +| Requirement | Task(s) | +|-------------|---------| +| REQ-001 | TASK-001, TASK-003 | +| REQ-002 | TASK-002, TASK-004 | + +--- + +## Phase 1: Foundation + +### TASK-001: Setup Database Schema -Use the agent-prompts skill template for each task. Ensure: -- Context is clear (what and why) -- Requirements are specific and checkable -- Technical approach references actual codebase patterns -- File paths are accurate -- Tests are specified -- Acceptance criteria are verifiable +#### Context +This task establishes the data layer for the feature. The schema must support +all entities defined in the SDD and enable the API operations in Phase 2. + +#### Requirements +- Create users table with id, email, created_at +- Create sessions table with foreign key to users +- Add indexes for common query patterns + +#### Technical Approach +Follow the existing migration pattern in `db/migrations/`. Use the same +column naming conventions as existing tables. + +#### Files to Create/Modify +- `db/migrations/002_add_users.sql` - New migration file +- `db/schema.sql` - Update schema documentation + +#### Key Interfaces +```sql +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + email VARCHAR(255) UNIQUE NOT NULL, + created_at TIMESTAMP DEFAULT NOW() +); +``` + +#### Constraints +- Follow existing naming conventions (snake_case) +- Use SERIAL for auto-increment IDs +- All timestamps must include timezone + +--- + +## Phase 2: Core Implementation + +### TASK-002: Create API Endpoints +... +``` ## Task Decomposition Rules @@ -90,17 +216,17 @@ Use the agent-prompts skill template for each task. Ensure: ``` Feature X -├── - [ ] TASK-001: Database schema/migrations (infrastructure) -├── - [ ] TASK-002: Type definitions and interfaces (infrastructure) -├── - [ ] TASK-003: API endpoint - create (feature) -├── - [ ] TASK-004: API endpoint - read (feature) -├── - [ ] TASK-005: API endpoint - update (feature) -├── - [ ] TASK-006: API endpoint - delete (feature) -├── - [ ] TASK-007: UI component - form (feature) -├── - [ ] TASK-008: UI component - list (feature) -├── - [ ] TASK-009: Unit tests (testing) -├── - [ ] TASK-010: Integration tests (testing) -└── - [ ] TASK-011: Documentation (documentation) +├── TASK-001: Database schema/migrations (infrastructure, 3pts) +├── TASK-002: Type definitions and interfaces (infrastructure, 2pts) +├── TASK-003: API endpoint - create (feature, 3pts) +├── TASK-004: API endpoint - read (feature, 2pts) +├── TASK-005: API endpoint - update (feature, 3pts) +├── TASK-006: API endpoint - delete (feature, 2pts) +├── TASK-007: UI component - form (feature, 3pts) +├── TASK-008: UI component - list (feature, 3pts) +├── TASK-009: Unit tests (testing, 3pts) +├── TASK-010: Integration tests (testing, 3pts) +└── TASK-011: Documentation (documentation, 2pts) ``` ## Dependency Management @@ -119,48 +245,35 @@ Schema → Types → API → Service → UI → Tests - Different UI components (after API exists) - Different test suites -## Output Format +## Prompt Template -Use checkbox format for task headers to enable status tracking: -- `- [ ]` = Not started -- `- [~]` = In progress -- `- [x]` = Completed +Each task's `prompt` field should contain: ```markdown -# Implementation Tasks: [Feature Name] +## Context +[2-3 sentences explaining where this task fits and why it matters] -## Summary -| Metric | Value | -|--------|-------| -| Total Tasks | X | -| Total Story Points | Y | -| Estimated Duration | Z sessions (assuming 20 pts/session) | -| Critical Path | TASK-001 → TASK-003 → TASK-007 | +## Requirements +- [Specific, verifiable requirement 1] +- [Specific, verifiable requirement 2] -## Requirement Coverage -| Requirement | Task(s) | -|-------------|---------| -| REQ-001 | TASK-001, TASK-003 | -| REQ-002 | TASK-002, TASK-004 | - ---- - -## Phase 1: Foundation (X points) - -### - [ ] TASK-001: [Title] -[Full task prompt using template] +## Technical Approach -### - [ ] TASK-002: [Title] -[Full task prompt using template] +### Suggested Implementation +[Step-by-step guidance based on codebase patterns] ---- - -## Phase 2: Core Implementation (Y points) +### Files to Create/Modify +- `path/to/file.ts` - [What changes] -### - [ ] TASK-003: [Title] -[Full task prompt using template] +### Key Interfaces +```typescript +// Define expected interfaces +``` -[Continue for all tasks...] +## Constraints +- [Pattern to follow] +- [Library to use] +- [Area not to modify] ``` ## Quality Checklist @@ -170,7 +283,8 @@ Before finalizing: - [ ] No task exceeds 8 story points - [ ] Dependencies form a valid DAG (no cycles) - [ ] Critical path is identified -- [ ] Each task has complete acceptance criteria +- [ ] Each task has complete acceptance criteria (in JSON) - [ ] File paths reference actual codebase locations - [ ] Test tasks are included - [ ] Documentation tasks are included +- [ ] `prompt` field contains full implementation guidance diff --git a/agents/task-verifier.md b/agents/task-verifier.md index 325d684..f580588 100644 --- a/agents/task-verifier.md +++ b/agents/task-verifier.md @@ -25,7 +25,7 @@ description: Use this agent to verify task completion by checking acceptance cri model: sonnet color: yellow -tools: Read, Glob, Grep, Bash(git:*), Bash(find:*), Bash(ls:*), Bash(cat:*), Bash(head:*), Bash(wc:*), Bash(npm:*), Bash(yarn:*), Bash(pnpm:*), Bash(bun:*), Bash(cargo:*), Bash(make:*), Bash(pytest:*), Bash(go:*), Bash(mypy:*), Bash(ruff:*), Bash(flake8:*), Bash(golangci-lint:*) +tools: Read, Glob, Grep, Bash(git:*), Bash(find:*), Bash(ls:*), Bash(cat:*), Bash(head:*), Bash(wc:*), Bash(npm:*), Bash(yarn:*), Bash(pnpm:*), Bash(bun:*), Bash(cargo:*), Bash(make:*), Bash(pytest:*), Bash(go:*), Bash(mypy:*), Bash(ruff:*), Bash(flake8:*), Bash(golangci-lint:*), Bash(jq:*) --- # Task Verifier @@ -34,30 +34,66 @@ You are a quality assurance specialist who verifies that implementation tasks ha ## Your Mission -Given a task prompt with acceptance criteria, systematically verify each criterion by examining the codebase, running tests, and checking for evidence of completion. +Given a task ID and feature name, systematically verify each acceptance criterion from TASKS.json by examining the codebase, running tests, and checking for evidence of completion. ## Input You will receive: -1. The full task prompt including acceptance criteria -2. The feature name (for context on where files should be) -3. Optionally, the task ID being verified +1. The feature name (to locate TASKS.json) +2. The task ID to verify +3. Optionally, the full task prompt (for additional context) + +## Data Sources + +All acceptance criteria and testing requirements come from TASKS.json: + +```bash +# Get acceptance criteria for a task +jq -r '.tasks["TASK-001"].acceptance_criteria[]' .shipspec/planning/{feature}/TASKS.json + +# Get testing requirements +jq -r '.tasks["TASK-001"].testing[]' .shipspec/planning/{feature}/TASKS.json +``` ## Verification Process -### Step 1: Extract Acceptance Criteria +### Step 0: Check for Completion Promise + +If the task prompt contains a `## Completion Promise` section: + +1. Extract the promise text (the identifier between `` tags expected) +2. Search recent assistant messages in the conversation for `EXACT_TEXT` +3. **If promise found and matches exactly:** + - Set `promise_matched = true` + - Note in report: "Completion promise detected and matched" +4. **If promise section exists but no matching promise found:** + - Set `promise_matched = false` + - Continue to Step 1 (standard verification applies) +5. **If task has NO Completion Promise section:** + - Set `promise_matched = false` (not applicable) + - Continue to Step 1 normally -Parse the task prompt to find the `## Acceptance Criteria` section. Each line starting with `- [ ]` or `- [x]` is a criterion to verify. +### Step 1: Load Acceptance Criteria from JSON -**If no Acceptance Criteria section is found OR the section has no criteria items:** +Read the task's acceptance criteria from TASKS.json: + +```bash +jq -r '.tasks["TASK-XXX"]' .shipspec/planning/{feature}/TASKS.json +``` + +Extract: +- `acceptance_criteria` array - each item is a criterion to verify +- `testing` array - test commands to run + +**If no acceptance_criteria found OR the array is empty:** - Set status to **BLOCKED** -- Report: "No acceptance criteria found in task prompt. A task without acceptance criteria cannot be auto-verified." -- Recommendation: "Add an `## Acceptance Criteria` section to the task in TASKS.md with specific, testable criteria." +- Report: "No acceptance criteria found in TASKS.json for this task." +- Recommendation: "Add acceptance criteria to the task in TASKS.json." - **Stop here** - do not proceed to Step 2 ### Step 2: Categorize Each Criterion -For each criterion, determine the verification method: +For each criterion string in the `acceptance_criteria` array, determine the verification method: | Criterion Pattern | Verification Method | |-------------------|---------------------| @@ -89,6 +125,8 @@ For each criterion: 3. **Evaluate the result**: Does it meet the criterion? 4. **Record the outcome**: PASS, FAIL, or CANNOT_VERIFY +Also execute any commands from the `testing` array. + ### Step 4: Produce Verification Report Output a structured report: @@ -109,6 +147,13 @@ Output a structured report: | 3 | No type errors | FAIL | 2 type errors found | | 4 | Documentation updated | CANNOT_VERIFY | No docs folder found | +### Testing Requirements + +| Test Command | Status | Output | +|--------------|--------|--------| +| npm run db:migrate | PASS | Migration completed | +| npm test | FAIL | 2 tests failing | + ### Issues Found #### Issue 1: Type Errors @@ -198,10 +243,24 @@ git diff --name-only HEAD~5 ## Edge Cases ### Cannot Verify -If a criterion cannot be verified (e.g., "User experience is smooth"), mark as CANNOT_VERIFY and note why: -- Requires manual testing -- Requires running application -- Subjective criterion + +If a criterion cannot be verified, behavior depends on whether a completion promise was matched in Step 0: + +**If completion promise WAS matched (promise_matched = true):** +- Treat subjective CANNOT_VERIFY criteria as PASS +- The developer's promise signal counts as explicit verification +- Example: If criterion is "Documentation is clear (SUBJECTIVE)" and promise matched → PASS +- Non-subjective CANNOT_VERIFY items (e.g., missing test infrastructure) remain CANNOT_VERIFY + +**If completion promise was NOT matched:** +- Mark subjective criteria as CANNOT_VERIFY with reason: + - Requires manual testing + - Requires running application + - Subjective criterion +- These do NOT block task completion + +**How to identify subjective criteria:** +Look for markers like "(SUBJECTIVE)", "(subjective)", "clear", "intuitive", "smooth", "user experience" in the criterion text. ### Partial Completion If some criteria pass but others fail, status is INCOMPLETE. List exactly what needs to be fixed. @@ -228,8 +287,34 @@ Always end with one of these clear verdicts: ## Important Notes -1. **Be thorough**: Check every criterion, don't skip any -2. **Be specific**: Show exact evidence (file paths, line numbers, error messages) -3. **Be helpful**: If something fails, explain how to fix it -4. **Be honest**: If you can't verify something, say so -5. **Check related files**: Sometimes implementation spans multiple files +1. **Read from TASKS.json**: All criteria come from the JSON file's `acceptance_criteria` array +2. **Be thorough**: Check every criterion, don't skip any +3. **Be specific**: Show exact evidence (file paths, line numbers, error messages) +4. **Be helpful**: If something fails, explain how to fix it +5. **Be honest**: If you can't verify something, say so +6. **Check related files**: Sometimes implementation spans multiple files + +## Completion Promise Format + +Tasks with subjective criteria can include this section in their prompt to enable explicit completion signals: + +```markdown +## Completion Promise + +To signal completion, output: UNIQUE_IDENTIFIER + +**This promise should only be output when:** +- [List specific conditions for this task] + +Do NOT output this promise if unsure or to exit early. +``` + +**Promise requirements:** +- The promise text must be a meaningful identifier (e.g., "API_DESIGN_COMPLETE", "DATABASE_SCHEMA_DONE") +- Must appear verbatim in `TEXT` XML tags +- Match is case-sensitive with no extra whitespace + +**When promise is matched:** +- Subjective criteria marked "(SUBJECTIVE)" are treated as PASS +- Non-subjective CANNOT_VERIFY items still remain CANNOT_VERIFY +- Report notes: "Completion promise detected and matched" diff --git a/commands/cancel-feature-retry.md b/commands/cancel-feature-retry.md new file mode 100644 index 0000000..57206c7 --- /dev/null +++ b/commands/cancel-feature-retry.md @@ -0,0 +1,77 @@ +--- +description: Cancel active feature implementation retry loop +allowed-tools: + - Bash(test:*) + - Bash(rm:*) + - Bash(jq:*) + - Read +--- + +# Cancel Feature Retry + +Cancel an active feature implementation retry loop. + +## Step 1: Check for Active Loop + +Check for the pointer file: +```bash +test -f .shipspec/active-loop.local.json && echo "POINTER_EXISTS" || echo "NOT_FOUND" +``` + +**If NOT_FOUND:** +> "No active feature retry loop found." + +**Stop here.** + +**If POINTER_EXISTS:** +Read the pointer file and verify it's a feature-retry: +```bash +jq -r '.loop_type // empty' .shipspec/active-loop.local.json +``` + +**If loop_type is NOT "feature-retry":** +> "No active feature retry loop found. (A different loop type is active)" + +**Stop here.** + +## Step 2: Read Current State + +Extract the state path from the pointer: +```bash +jq -r '.state_path // empty' .shipspec/active-loop.local.json +``` + +Parse the state file (JSON) to extract: +```bash +jq -r '.current_task_id // empty' [state_path] +jq -r '.feature // empty' [state_path] +jq -r '.task_attempt // 0' [state_path] +jq -r '.max_task_attempts // 5' [state_path] +jq -r '.tasks_completed // 0' [state_path] +jq -r '.total_tasks // 0' [state_path] +``` + +- `current_task_id` - the task being implemented +- `feature` - the feature name +- `task_attempt` - current attempt number for the task +- `max_task_attempts` - maximum attempts per task +- `tasks_completed` - number of completed tasks +- `total_tasks` - total number of tasks + +## Step 3: Cancel the Loop + +Remove both the state file and pointer: + +```bash +rm -f [state_path] .shipspec/active-loop.local.json +``` + +## Step 4: Report + +> "Cancelled feature retry for **[FEATURE]** +> +> - Current task: [CURRENT_TASK_ID] (attempt [TASK_ATTEMPT]/[MAX_TASK_ATTEMPTS]) +> - Progress: [TASKS_COMPLETED]/[TOTAL_TASKS] tasks completed +> +> The current task remains in `in_progress` status in TASKS.json. +> Run `/implement-feature [feature]` to resume, or `/implement-task [feature]` to continue task-by-task." diff --git a/commands/cancel-task-loop.md b/commands/cancel-task-loop.md new file mode 100644 index 0000000..0122832 --- /dev/null +++ b/commands/cancel-task-loop.md @@ -0,0 +1,72 @@ +--- +description: Cancel active task implementation loop +allowed-tools: + - Bash(test:*) + - Bash(rm:*) + - Bash(jq:*) + - Read +--- + +# Cancel Task Loop + +Cancel an active task implementation loop. + +## Step 1: Check for Active Loop + +Check for the pointer file: +```bash +test -f .shipspec/active-loop.local.json && echo "POINTER_EXISTS" || echo "NOT_FOUND" +``` + +**If NOT_FOUND:** +> "No active task loop found." + +**Stop here.** + +**If POINTER_EXISTS:** +Read the pointer file and verify it's a task-loop: +```bash +jq -r '.loop_type // empty' .shipspec/active-loop.local.json +``` + +**If loop_type is NOT "task-loop":** +> "No active task loop found. (A different loop type is active)" + +**Stop here.** + +## Step 2: Read Current State + +Extract the state path from the pointer: +```bash +jq -r '.state_path // empty' .shipspec/active-loop.local.json +``` + +Parse the state file (JSON) to extract: +```bash +jq -r '.task_id // empty' [state_path] +jq -r '.feature // empty' [state_path] +jq -r '.iteration // 0' [state_path] +jq -r '.max_iterations // 5' [state_path] +``` + +- `task_id` - the task being implemented +- `feature` - the feature name +- `iteration` - current attempt number +- `max_iterations` - maximum attempts configured + +## Step 3: Cancel the Loop + +Remove both the state file and pointer: + +```bash +rm -f [state_path] .shipspec/active-loop.local.json +``` + +## Step 4: Report + +> "Cancelled task loop for **[TASK_ID]** in feature **[FEATURE]** +> +> - Was at iteration: [ITERATION]/[MAX_ITERATIONS] +> +> The task remains in `in_progress` status in TASKS.json. +> Run `/implement-task [feature]` to resume, or use task-manager to update the task status." diff --git a/commands/feature-planning.md b/commands/feature-planning.md index c478a31..0d7c9bc 100644 --- a/commands/feature-planning.md +++ b/commands/feature-planning.md @@ -1,7 +1,7 @@ --- description: Start planning a new feature with AI-assisted PRD generation argument-hint: [feature-description] -allowed-tools: Read, Glob, Grep, Write, Bash(git status), Bash(git log:*), Bash(find:*), Bash(ls:*), Bash(cat:*), Bash(head:*), Bash(mkdir:*), Bash(rm:*), Task, AskUserQuestion +allowed-tools: Read, Glob, Grep, Write, Bash(git status), Bash(git log:*), Bash(find:*), Bash(ls:*), Bash(cat:*), Bash(head:*), Bash(mkdir:*), Bash(rm:*), Bash(jq:*), Task, AskUserQuestion --- # Feature Planning @@ -17,7 +17,7 @@ This command runs through 7 phases: 4. **PRD Generation** - Generate PRD and pause for user review 5. **Technical Decisions** - Interactive Q&A with design-architect agent 6. **SDD Generation** - Generate SDD and pause for user review -7. **Task Generation** - Automatically generate implementation tasks +7. **Task Generation** - Automatically generate implementation tasks (TASKS.json + TASKS.md) --- @@ -289,26 +289,34 @@ Delegate to the `task-planner` subagent to: - Create detailed agent prompts for each task - Group into execution phases -Using the agent-prompts skill, create a comprehensive task list with: - -1. **Summary** - - Total tasks and story points - - Estimated duration - - Critical path +Using the agent-prompts skill, create comprehensive task files: + +1. **TASKS.json** - Machine-parseable metadata + - Version and feature name + - Summary (total tasks, points, critical path) + - Phases array + - Tasks object with all metadata: + - title, status, phase, points + - depends_on, blocks arrays + - prd_refs, sdd_refs arrays + - acceptance_criteria, testing arrays + - prompt field (full implementation prompt) + +2. **TASKS.md** - Human-readable task prompts + - Summary section - Requirement coverage matrix - -2. **Execution Phases** - - Phase 1: Foundation (schema, types) - - Phase 2: Core Implementation (APIs, services) - - Phase 3: UI Layer (components, pages) - - Phase 4: Polish (tests, docs) - -3. **Individual Tasks** - - Each with full agent prompt - - Dependencies clearly marked - - Acceptance criteria - -Save the tasks to: `.shipspec/planning/[FEATURE_DIR]/TASKS.md` + - Phase groupings + - Individual tasks with: + - Context + - Requirements (prose) + - Technical Approach + - Files to Create/Modify + - Key Interfaces + - Constraints + +Save to: +- `.shipspec/planning/[FEATURE_DIR]/TASKS.json` +- `.shipspec/planning/[FEATURE_DIR]/TASKS.md` ### Cleanup Temporary Files @@ -319,7 +327,142 @@ rm -f .shipspec/planning/[FEATURE_DIR]/context.md rm -f .shipspec/planning/[FEATURE_DIR]/description.txt ``` -The context and description are now incorporated into the PRD, SDD, and TASKS.md files. +The context and description are now incorporated into the PRD, SDD, and task files. + +--- + +## Phase 8: Task Refinement (Optional) + +After generating TASKS.json, analyze task complexity to identify tasks that may be too large. + +### 8.1: Identify Large Tasks + +Parse TASKS.json and find tasks with estimated effort > 5 story points: + +```bash +jq -r '.tasks | to_entries[] | select(.value.points > 5) | "\(.key): \(.value.title) (\(.value.points) points)"' .shipspec/planning/[FEATURE_DIR]/TASKS.json +``` + +**If no large tasks found:** +> "All tasks are appropriately sized (≤5 story points). Skipping refinement." + +Skip to Completion Summary. + +**If large tasks found:** + +Show user: +> "## Task Size Analysis +> +> Found **X tasks** with estimated effort > 5 story points: +> +> | Task | Title | Story Points | +> |------|-------|--------------| +> | TASK-003 | [Title] | 8 | +> | TASK-007 | [Title] | 13 | +> +> Large tasks are harder to implement and verify. Would you like to auto-refine them into smaller subtasks?" + +Use AskUserQuestion with options: +- "Yes, auto-refine large tasks" +- "No, keep current breakdown" + +**If user chooses No:** Skip to Completion Summary. + +### 8.2: Initialize Refinement Loop + +Create state files (JSON format): +```bash +# Create pointer file +cat > .shipspec/active-loop.local.json << 'EOF' +{ + "feature": "[FEATURE_DIR]", + "loop_type": "planning-refine", + "state_path": ".shipspec/planning/[FEATURE_DIR]/planning-refine.local.json", + "created_at": "[ISO timestamp]" +} +EOF + +# Create state file in feature directory +cat > .shipspec/planning/[FEATURE_DIR]/planning-refine.local.json << 'EOF' +{ + "active": true, + "feature": "[FEATURE_DIR]", + "iteration": 1, + "max_iterations": 3, + "large_tasks": ["TASK-003", "TASK-007"], + "tasks_refined": 0, + "started_at": "[ISO timestamp]" +} +EOF +``` + +### 8.3: Refine Each Large Task + +For each task in large_tasks: + +1. Get full task details from TASKS.json: + ```bash + jq -r '.tasks["TASK-XXX"]' .shipspec/planning/[FEATURE_DIR]/TASKS.json + ``` + +2. Delegate to `task-planner` agent: + > "Break down TASK-XXX into 2-3 subtasks. + > + > Original task: + > [task prompt] + > + > Requirements: + > - Each subtask should be < 3 story points + > - Preserve the original acceptance criteria distributed across subtasks + > - Maintain dependency relationships + > - Use format TASK-XXX-A, TASK-XXX-B, etc. for subtask IDs" + +3. Replace original task with generated subtasks in TASKS.json and TASKS.md +4. Update dependencies pointing to original task +5. Run task-manager validate to check no circular deps + +**If validation fails:** +- Rollback changes +- Mark task as "cannot refine" +- Continue to next large task + +### 8.4: Check Completion + +After processing all large tasks: + +1. Re-analyze TASKS.json for tasks > 5 points: + ```bash + jq '[.tasks | to_entries[] | select(.value.points > 5)] | length' .shipspec/planning/[FEATURE_DIR]/TASKS.json + ``` +2. If still have large tasks AND iteration < max_iterations: + - Increment iteration in state file: + ```bash + jq '.iteration += 1' .shipspec/planning/[FEATURE_DIR]/planning-refine.local.json > tmp && mv tmp .shipspec/planning/[FEATURE_DIR]/planning-refine.local.json + ``` + - Add new large tasks to list + - Return to 8.3 +3. If no more large tasks OR max iterations: + - Clean up state file + - Show summary + +### 8.5: Summary + +> "## Task Refinement Complete +> +> **Results:** +> - Original large tasks: X +> - Successfully refined: Y +> - Could not refine: Z +> +> **New task count:** N (was M)" + +**Output completion marker:** +`` + +Clean up: +```bash +rm -f .shipspec/planning/[FEATURE_DIR]/planning-refine.local.json .shipspec/active-loop.local.json +``` --- @@ -340,7 +483,8 @@ After all phases complete, provide: > **Generated Documents:** > - `.shipspec/planning/[FEATURE_DIR]/PRD.md` - Product requirements > - `.shipspec/planning/[FEATURE_DIR]/SDD.md` - Technical design -> - `.shipspec/planning/[FEATURE_DIR]/TASKS.md` - Implementation tasks +> - `.shipspec/planning/[FEATURE_DIR]/TASKS.json` - Task metadata (machine-parseable) +> - `.shipspec/planning/[FEATURE_DIR]/TASKS.md` - Task prompts (human-readable) > > **Next Steps:** > Run `/implement-task [FEATURE_DIR]` to start implementing the first task. diff --git a/commands/implement-feature.md b/commands/implement-feature.md index fb0db0b..7b9a32b 100644 --- a/commands/implement-feature.md +++ b/commands/implement-feature.md @@ -1,12 +1,12 @@ --- description: Implement all tasks for a feature end-to-end automatically argument-hint: -allowed-tools: Read, Glob, Grep, Write, Edit, Bash(cat:*), Bash(ls:*), Bash(find:*), Bash(git:*), Bash(head:*), Bash(wc:*), Bash(npm:*), Bash(npx:*), Bash(yarn:*), Bash(pnpm:*), Bash(bun:*), Bash(cargo:*), Bash(make:*), Bash(pytest:*), Bash(go:*), Bash(mypy:*), Bash(ruff:*), Bash(flake8:*), Bash(golangci-lint:*), Task, AskUserQuestion +allowed-tools: Read, Glob, Grep, Write, Edit, Bash(cat:*), Bash(ls:*), Bash(find:*), Bash(git:*), Bash(head:*), Bash(wc:*), Bash(npm:*), Bash(npx:*), Bash(yarn:*), Bash(pnpm:*), Bash(bun:*), Bash(cargo:*), Bash(make:*), Bash(pytest:*), Bash(go:*), Bash(mypy:*), Bash(ruff:*), Bash(flake8:*), Bash(golangci-lint:*), Bash(rm:*), Bash(mkdir:*), Bash(jq:*), Task, AskUserQuestion --- # Implement Feature: $ARGUMENTS -Automatically implement ALL tasks in a feature's TASKS.md file end-to-end. After all tasks are complete, run a comprehensive review against PRD, SDD, and all acceptance criteria. +Automatically implement ALL tasks in a feature's TASKS.json file end-to-end. After all tasks are complete, run a comprehensive review against PRD, SDD, and all acceptance criteria. ## Step 0: Validate Argument @@ -40,7 +40,7 @@ ls -d .shipspec/planning/$ARGUMENTS 2>/dev/null || echo "NOT_FOUND" **Check for required planning artifacts:** ```bash echo "=== Checking planning artifacts ===" -ls .shipspec/planning/$ARGUMENTS/TASKS.md 2>/dev/null || echo "TASKS.md NOT FOUND" +ls .shipspec/planning/$ARGUMENTS/TASKS.json 2>/dev/null || echo "TASKS.json NOT FOUND" ls .shipspec/planning/$ARGUMENTS/PRD.md 2>/dev/null || echo "PRD.md NOT FOUND" ls .shipspec/planning/$ARGUMENTS/SDD.md 2>/dev/null || echo "SDD.md NOT FOUND" ``` @@ -57,7 +57,7 @@ Delegate to the `task-manager` agent with: - Feature name: $ARGUMENTS - Operation: `parse` -**If error (TASKS.md not found or empty):** +**If error (TASKS.json not found or empty):** Show the error message from task-manager and stop. **If successful:** @@ -103,21 +103,99 @@ Show the warning message from task-manager and **stop** - wait for user to resol 4. **Based on verification result:** + **Log verification** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | VERIFY | [result] | [details] + ``` + **If VERIFIED:** - - Update task status from `[~]` to `[x]` in TASKS.md + - Update task status to `completed`: + - Delegate to `task-manager` with operation `update_status`, task ID, and new status `completed` + - **Log task completion** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | COMPLETE | Status updated to completed + ``` + - **Clean up any existing feature retry state:** + ```bash + rm -f .shipspec/planning/$ARGUMENTS/feature-retry.local.json .shipspec/active-loop.local.json + ``` + - **Output completion marker:** + `VERIFIED` - Tell user: "Task [TASK-ID] verified complete! Marking as done and continuing..." - Continue to Step 4 (main loop) - **If INCOMPLETE or BLOCKED:** + **If INCOMPLETE:** - Tell user: "Task [TASK-ID] not yet complete. Continuing implementation..." + - **Create feature retry state files** for auto-retry (only if not already retrying this task): + ```bash + POINTER_FILE=".shipspec/active-loop.local.json" + STATE_FILE=".shipspec/planning/$ARGUMENTS/feature-retry.local.json" + # Check if we're already retrying the SAME task (preserve counter for retry) + EXISTING_TASK=$(jq -r '.current_task_id // empty' "$STATE_FILE" 2>/dev/null || echo "") + if [[ "$EXISTING_TASK" != "[task-id]" ]]; then + # New task or no state file - create fresh with counter=1 + # Create pointer file (JSON format) + cat > "$POINTER_FILE" << 'EOF' + { + "feature": "$ARGUMENTS", + "loop_type": "feature-retry", + "state_path": ".shipspec/planning/$ARGUMENTS/feature-retry.local.json", + "created_at": "[ISO timestamp]" + } + EOF + # Create state file in feature directory (JSON format) + cat > "$STATE_FILE" << 'EOF' + { + "active": true, + "feature": "$ARGUMENTS", + "current_task_id": "[task-id]", + "task_attempt": 1, + "max_task_attempts": 5, + "total_tasks": [total count], + "tasks_completed": [completed count], + "started_at": "[ISO timestamp]" + } + EOF + fi + # If same task, skip creation - hook will increment counter + ``` - **IMPLEMENT THE TASK**: - Read the task prompt carefully - Identify files to create or modify - Write the actual code following the implementation notes - Run any specified build/test commands as you implement - Follow the acceptance criteria to guide what needs to be done - - Mark as `[x]` when implementation is complete - - Continue to Step 4 (main loop) + - After implementation, delegate to `task-verifier` to verify the task + - **If VERIFIED after implementation:** + - Update task status to `completed`: + - Delegate to `task-manager` with operation `update_status`, task ID, and new status `completed` + - Log task completion + - Clean up state file: `rm -f .shipspec/planning/$ARGUMENTS/feature-retry.local.json .shipspec/active-loop.local.json` + - Output: `VERIFIED` + - Continue to Step 4 (main loop) + - **If still INCOMPLETE:** + - DO NOT output completion marker (stop hook will trigger retry) + - Show what failed to user + + - **If BLOCKED after implementation:** + - **Clean up state file:** + ```bash + rm -f .shipspec/planning/$ARGUMENTS/feature-retry.local.json .shipspec/active-loop.local.json + ``` + - **Output blocked marker:** + `BLOCKED` + - Tell user: "Task [TASK-ID] is blocked: [reason]. Manual intervention required." + - Ask user how to proceed (skip task, abort feature, or manual fix) + + **If BLOCKED:** + - **Clean up any existing feature retry state:** + ```bash + rm -f .shipspec/planning/$ARGUMENTS/feature-retry.local.json .shipspec/active-loop.local.json + ``` + - **Output blocked marker:** + `BLOCKED` + - Tell user: "Task [TASK-ID] is blocked: [reason]. Manual intervention required." + - Ask user how to proceed (skip task, abort feature, or manual fix) **If NONE:** Continue to Step 4 (main loop). @@ -140,7 +218,7 @@ Show the blocked tasks table from task-manager: > > No tasks are currently ready. [Show blocked tasks from task-manager response] > -> This may indicate a circular dependency or missing task. Please review TASKS.md." +> This may indicate a circular dependency or missing task. Please review TASKS.json." **Stop the loop** - ask user how to proceed. @@ -154,10 +232,15 @@ Continue to Step 4.3 with the returned task. Once a ready task is found: -1. **Update TASKS.md**: Change status from `[ ]` to `[~]` - - Find `### - [ ] TASK-XXX:` -> Replace with `### - [~] TASK-XXX:` +1. **Update task status** to `in_progress`: + - Delegate to `task-manager` with operation `update_status`, task ID, and new status `in_progress` + +2. **Log task start** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | START | [X/TOTAL] [title] + ``` -2. **Display task header**: +3. **Display task header**: > "--- > > ## [X/TOTAL] Implementing: [TASK-ID] - [Title] @@ -166,21 +249,82 @@ Once a ready task is found: > > ---" -3. **Get full task prompt** by delegating to `task-manager` with: +4. **Get full task prompt** by delegating to `task-manager` with: - Feature name: $ARGUMENTS - Operation: `get_task` - Task ID: [the ready task ID from step 4.1] -4. **IMPLEMENT THE TASK**: +5. **Create feature retry state files** for auto-retry on failure (only if not already retrying this task): + ```bash + POINTER_FILE=".shipspec/active-loop.local.json" + STATE_FILE=".shipspec/planning/$ARGUMENTS/feature-retry.local.json" + # Check if we're already retrying the SAME task (preserve counter for retry) + EXISTING_TASK=$(jq -r '.current_task_id // empty' "$STATE_FILE" 2>/dev/null || echo "") + if [[ "$EXISTING_TASK" != "[task-id]" ]]; then + # New task or no state file - create fresh with counter=1 + # Create pointer file (JSON format) + cat > "$POINTER_FILE" << 'EOF' + { + "feature": "$ARGUMENTS", + "loop_type": "feature-retry", + "state_path": ".shipspec/planning/$ARGUMENTS/feature-retry.local.json", + "created_at": "[ISO timestamp]" + } + EOF + # Create state file in feature directory (JSON format) + cat > "$STATE_FILE" << 'EOF' + { + "active": true, + "feature": "$ARGUMENTS", + "current_task_id": "[task-id]", + "task_attempt": 1, + "max_task_attempts": 5, + "total_tasks": [total count], + "tasks_completed": [completed count], + "started_at": "[ISO timestamp]" + } + EOF + fi + # If same task, skip creation - hook will increment counter + ``` + +6. **IMPLEMENT THE TASK**: - Read the task prompt carefully - Identify files to create or modify - Write the actual code following the implementation notes - Run any specified build/test commands as you implement - Follow the acceptance criteria to guide what needs to be done -5. **Mark task complete**: After implementation, update TASKS.md from `[~]` to `[x]` +### 4.4: Verify and Handle Result + +After implementation, verify the task: + +1. **Delegate to task-verifier** agent with: + - The full task prompt including acceptance criteria + - The feature name ($ARGUMENTS) + - The task ID -6. **Show progress**: +2. **Log verification** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | VERIFY | [result] | [details] + ``` + +3. **Handle verification result:** + +**If VERIFIED:** +- Update task status to `completed`: + - Delegate to `task-manager` with operation `update_status`, task ID, and new status `completed` +- **Log task completion** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | COMPLETE | Status updated to completed + ``` +- **Clean up state file:** + ```bash + rm -f .shipspec/planning/$ARGUMENTS/feature-retry.local.json .shipspec/active-loop.local.json + ``` +- **Output completion marker:** + `VERIFIED` +- **Show progress**: > "**Progress: [X/TOTAL]** > > | Status | Count | @@ -188,9 +332,29 @@ Once a ready task is found: > | Completed | X | > | Remaining | Y | > -> *Continuing to next task...*" +> *Task verified! Continuing to next task...*" +- Return to Step 4.1 to find the next task. + +**If INCOMPLETE:** +- DO NOT output completion marker (stop hook will trigger retry) +- Keep task as `in_progress` (no status change needed) +- Show what failed to user: +> "**Verification Failed** +> +> The following criteria are not met: +> - [List failed criteria] +> +> The loop will retry automatically." -7. Return to Step 4.1 to find the next task. +**If BLOCKED:** +- **Clean up state file:** + ```bash + rm -f .shipspec/planning/$ARGUMENTS/feature-retry.local.json .shipspec/active-loop.local.json + ``` +- **Output blocked marker:** + `BLOCKED` +- Tell user: "Task [TASK-ID] is blocked: [reason]. Manual intervention required." +- Ask user how to proceed (skip task, abort feature, or manual fix) --- @@ -198,6 +362,11 @@ Once a ready task is found: After all tasks are marked complete, perform a comprehensive review of the entire feature implementation. +**Clean up any remaining state file:** +```bash +rm -f .shipspec/planning/$ARGUMENTS/feature-retry.local.json .shipspec/active-loop.local.json +``` + > "## All Tasks Implemented! > > Running comprehensive feature review against PRD, SDD, and all acceptance criteria..." @@ -210,13 +379,16 @@ Load the PRD and SDD for reference: ### 5.2: Validate All Acceptance Criteria -For each task in TASKS.md that is marked `[x]` (completed): +For each task in TASKS.json that has status `completed`: -1. Extract the full task prompt (from task header to next task header) +1. Get the task details from TASKS.json: + ```bash + jq -r '.tasks["TASK-XXX"]' .shipspec/planning/$ARGUMENTS/TASKS.json + ``` 2. Delegate to the `task-verifier` agent with: - - The full task prompt including acceptance criteria - - The feature name ($ARGUMENTS) - The task ID + - The feature name ($ARGUMENTS) + - The full task prompt for context 3. Record the verification result (VERIFIED, INCOMPLETE, or BLOCKED) **Aggregate results into a summary:** @@ -237,19 +409,23 @@ If any task is INCOMPLETE, collect all failed criteria for the final report in S ### 5.3: Validate Planning Alignment (Design & Requirements) -For each task in TASKS.md that is marked `[x]` (completed) AND has a `## References` section: +For each task in TASKS.json that has status `completed` AND has non-empty `prd_refs` or `sdd_refs` arrays: -1. Delegate to the `planning-validator` agent with: +1. Check if task has planning references: + ```bash + jq -r '.tasks["TASK-XXX"] | (.prd_refs | length), (.sdd_refs | length)' .shipspec/planning/$ARGUMENTS/TASKS.json + ``` + +2. If either array is non-empty, delegate to the `planning-validator` agent with: - The task ID - The feature name ($ARGUMENTS) - - The task's References section (containing SDD sections and PRD requirements) -2. Record the validation result: +3. Record the validation result: - **ALIGNED**: Implementation matches design and requirements - **MISALIGNED**: Implementation doesn't match (collect specific issues) - **UNVERIFIED**: References not found (track in `missing_references` list) -3. Aggregate results from all tasks +4. Aggregate results from all tasks **Output Format:** @@ -277,7 +453,7 @@ For each task in TASKS.md that is marked `[x]` (completed) AND has a `## Referen **Result:** X/Y requirements satisfied ``` -**Note:** Tasks without a References section are skipped for planning validation (they still go through acceptance criteria validation in 5.2). +**Note:** Tasks without prd_refs and sdd_refs arrays are skipped for planning validation (they still go through acceptance criteria validation in 5.2). ### 5.4: Generate Final Verdict @@ -323,6 +499,11 @@ Compile results from all three validation categories: - AND no explicit FAILED criteria exist (otherwise it's NEEDS WORK) - Examples: missing test infrastructure, cannot run type checker, missing acceptance criteria +**Log final review result** to `.claude/shipspec-debug.log`: +``` +$(date -u +%Y-%m-%dT%H:%M:%SZ) | FEATURE | REVIEW | [verdict] | [summary e.g., "5/5 tasks verified" or "2 criteria failed"] +``` + ### 5.5: Take Action Based on Verdict **If APPROVED:** @@ -342,6 +523,9 @@ Compile results from all three validation categories: > - Commit your changes: `git add . && git commit -m "Implement $ARGUMENTS feature"` > - Create a pull request if needed" +**Output feature complete marker:** +`APPROVED` + **If APPROVED WITH WARNINGS:** > "## APPROVED WITH WARNINGS @@ -364,13 +548,16 @@ Compile results from all three validation categories: > **Action Required:** > Please manually verify these references are either: > 1. No longer relevant (safe to ignore) -> 2. Need to be updated in TASKS.md, PRD.md, or SDD.md +> 2. Need to be updated in TASKS.json, PRD.md, or SDD.md > > **Next Steps:** > - Review the unverified references above > - If satisfied, proceed with: `git add . && git commit -m "Implement $ARGUMENTS feature"` > - If references need fixing, update the planning artifacts and re-run `/implement-feature $ARGUMENTS`" +**Output feature complete marker:** +`APPROVED_WITH_WARNINGS` + **If NEEDS WORK:** > "## NEEDS WORK @@ -406,7 +593,7 @@ Compile results from all three validation categories: > > **Common Resolutions:** > - **Missing test infrastructure**: Set up testing framework (e.g., `npm init jest`, `pytest`) -> - **Missing acceptance criteria**: Add acceptance criteria to tasks in TASKS.md +> - **Missing acceptance criteria**: Add acceptance criteria to tasks in TASKS.json > - **Missing type checker**: Install and configure type checking (e.g., TypeScript, mypy) > > After resolving the blocking issues, run `/implement-feature $ARGUMENTS` again." @@ -425,7 +612,7 @@ If a task prompt is too vague to implement: > **Options:** > 1. I'll make reasonable assumptions and implement > 2. Skip this task -> 3. Stop so you can clarify the task in TASKS.md" +> 3. Stop so you can clarify the task in TASKS.json" ### Build/Test Failures During Implementation If running build or tests fails during implementation: @@ -451,7 +638,7 @@ If the project doesn't have a type checker or linter configured: ## Important Notes -1. **Save progress frequently**: Update TASKS.md after each task completion so progress survives interruptions +1. **Save progress frequently**: Update TASKS.json after each task completion so progress survives interruptions 2. **Be thorough**: Implement each task fully before marking complete 3. **Read context**: Use PRD.md and SDD.md in the feature directory for additional context 4. **Follow patterns**: Look at existing code in the codebase for consistent patterns diff --git a/commands/implement-task.md b/commands/implement-task.md index 910854d..318879e 100644 --- a/commands/implement-task.md +++ b/commands/implement-task.md @@ -1,7 +1,7 @@ --- -description: Implement a specific task or the next available task from TASKS.md +description: Implement a specific task or the next available task from TASKS.json argument-hint: [task-id] -allowed-tools: Read, Glob, Grep, Write, Edit, Bash(cat:*), Bash(ls:*), Bash(find:*), Bash(git:*), Bash(head:*), Bash(wc:*), Bash(npm:*), Bash(npx:*), Bash(yarn:*), Bash(pnpm:*), Bash(bun:*), Bash(cargo:*), Bash(make:*), Bash(pytest:*), Bash(go:*), Bash(mypy:*), Bash(ruff:*), Bash(flake8:*), Bash(golangci-lint:*), Task +allowed-tools: Read, Glob, Grep, Write, Edit, Bash(cat:*), Bash(ls:*), Bash(find:*), Bash(git:*), Bash(head:*), Bash(wc:*), Bash(npm:*), Bash(npx:*), Bash(yarn:*), Bash(pnpm:*), Bash(bun:*), Bash(cargo:*), Bash(make:*), Bash(pytest:*), Bash(go:*), Bash(mypy:*), Bash(ruff:*), Bash(flake8:*), Bash(golangci-lint:*), Bash(rm:*), Bash(mkdir:*), Bash(jq:*), Task --- # Implement Task: $ARGUMENTS @@ -49,13 +49,13 @@ ls -d .shipspec/planning/[feature-dir] 2>/dev/null || echo "NOT_FOUND" > > Please run `/feature-planning` first to create the planning artifacts." -**Check for TASKS.md:** +**Check for TASKS.json:** ```bash -ls -la .shipspec/planning/[feature-dir]/TASKS.md 2>/dev/null || echo "TASKS.md NOT FOUND" +ls -la .shipspec/planning/[feature-dir]/TASKS.json 2>/dev/null || echo "TASKS.json NOT FOUND" ``` -**If TASKS.md not found:** -> "No TASKS.md found in `.shipspec/planning/[feature-dir]/`. +**If TASKS.json not found:** +> "No TASKS.json found in `.shipspec/planning/[feature-dir]/`. > > Run `/feature-planning` to complete the planning workflow and generate tasks." @@ -65,7 +65,7 @@ Delegate to the `task-manager` agent with: - Feature name: [feature-dir] - Operation: `parse` -**If error (TASKS.md not found or empty):** +**If error (TASKS.json not found or empty):** Show the error message from task-manager and stop. **If successful:** @@ -137,23 +137,39 @@ Tell the user: - Task ID: [the in-progress task ID] Delegate to the `task-verifier` subagent with: -- The full task prompt for the in-progress task +- The task ID - The feature directory name +- The full task prompt for context **Based on verification result:** +**Log verification result** to `.claude/shipspec-debug.log`: +``` +$(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | VERIFY | [VERIFIED|INCOMPLETE|BLOCKED] | [brief failure reason if any] +``` + - **VERIFIED**: - Continue to Step 4.5 to validate planning alignment (if task has references) - **INCOMPLETE**: - - Keep the task as `[~]` + - Keep the task status as `in_progress` (no status update needed) - Show the user what's missing + - **Clean up loop state and output incomplete marker:** + ```bash + rm -f .shipspec/planning/[feature-dir]/task-loop.local.json .shipspec/active-loop.local.json + ``` + Then output: `INCOMPLETE` - Tell user: "Task [TASK-ID] is not complete. Please address the issues above, then run this command again." - **Stop here** - don't proceed to next task - **BLOCKED**: - - Keep the task as `[~]` + - Keep the task status as `in_progress` - Show the blocking reason + - **Clean up loop state and output blocked marker:** + ```bash + rm -f .shipspec/planning/[feature-dir]/task-loop.local.json .shipspec/active-loop.local.json + ``` + Then output: `BLOCKED` - Ask user how they want to proceed **If NONE:** @@ -163,41 +179,79 @@ Continue to Step 5. **This step runs after task-verifier returns VERIFIED.** -Check if the task has a `## References` section containing SDD or PRD references. +Check if the task has `prd_refs` or `sdd_refs` arrays in TASKS.json: + +```bash +jq -r '.tasks["[task-id]"] | (.prd_refs | length), (.sdd_refs | length)' .shipspec/planning/[feature-dir]/TASKS.json +``` -**If the task has SDD or PRD references:** +**If the task has any PRD or SDD references (either array is non-empty):** 1. Tell user: "Acceptance criteria verified. Checking planning alignment..." 2. Delegate to the `planning-validator` agent with: - The task ID - The feature directory name - - The task's References section 3. **Based on validation result:** **If ALIGNED:** - Tell user: "Planning alignment verified." - - Update task status from `[~]` to `[x]` in TASKS.md + - Update task status to `completed`: + - Delegate to `task-manager` with operation `update_status`, task ID, and new status `completed` + - **Log task completion** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | COMPLETE | Status updated to completed + ``` + - **Clean up loop state and output completion marker:** + ```bash + rm -f .shipspec/planning/[feature-dir]/task-loop.local.json .shipspec/active-loop.local.json + ``` + Then output: `VERIFIED` - Tell user: "Task [TASK-ID] complete! Moving to next task..." - If task-id was specified and matches: **Stop here** - the requested task is complete - Otherwise: Continue to Step 5 **If MISALIGNED:** - Show specific misalignment issues + - **Clean up loop state and output misaligned marker:** + ```bash + rm -f .shipspec/planning/[feature-dir]/task-loop.local.json .shipspec/active-loop.local.json + ``` + Then output: `MISALIGNED` - Tell user: "Implementation doesn't match design/requirements. Please fix the issues above." - - Keep task as `[~]`, **stop here** + - Keep task as `in_progress`, **stop here** **If UNVERIFIED:** - Show missing references - - Tell user: "Warning: Some planning references could not be verified. Consider updating references in TASKS.md or planning documents." - - Update task status from `[~]` to `[x]` in TASKS.md (warning only, not blocking) + - Tell user: "Warning: Some planning references could not be verified. Consider updating references in TASKS.json or planning documents." + - Update task status to `completed`: + - Delegate to `task-manager` with operation `update_status`, task ID, and new status `completed` + - **Log task completion** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | COMPLETE | Status updated to completed (with warnings) + ``` + - **Clean up loop state and output completion marker:** + ```bash + rm -f .shipspec/planning/[feature-dir]/task-loop.local.json .shipspec/active-loop.local.json + ``` + Then output: `VERIFIED` - Tell user: "Task [TASK-ID] complete with warnings. Moving to next task..." - If task-id was specified and matches: **Stop here** - Otherwise: Continue to Step 5 -**If the task has NO References section:** -- Update task status from `[~]` to `[x]` in TASKS.md +**If the task has NO references (both arrays are empty):** +- Update task status to `completed`: + - Delegate to `task-manager` with operation `update_status`, task ID, and new status `completed` +- **Log task completion** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | COMPLETE | Status updated to completed + ``` +- **Clean up loop state and output completion marker:** + ```bash + rm -f .shipspec/planning/[feature-dir]/task-loop.local.json .shipspec/active-loop.local.json + ``` + Then output: `VERIFIED` - Tell user: "Task [TASK-ID] verified complete! Moving to next task..." - If task-id was specified and matches: **Stop here** - Otherwise: Continue to Step 5 @@ -223,15 +277,20 @@ Delegate to the `task-manager` agent with: Once a target task is identified: -1. **Update TASKS.md**: Change the task's status from `[ ]` to `[~]` - - Find `### - [ ] TASK-XXX:` -> Replace with `### - [~] TASK-XXX:` +1. **Update task status** to `in_progress`: + - Delegate to `task-manager` with operation `update_status`, task ID, and new status `in_progress` + +2. **Log task start** to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | START | Implementing: [title] + ``` -2. **Get full task prompt** by delegating to `task-manager` with: +3. **Get full task prompt** by delegating to `task-manager` with: - Feature name: [feature-dir] - Operation: `get_task` - Task ID: [the target task ID] -3. **Display to user**: +4. **Display to user**: > "## Starting Task: [TASK-ID] > @@ -252,6 +311,48 @@ Once a target task is identified: > ``` > This will verify your work and move to the next task." +## Step 6.5: Initialize Loop State + +After starting the task, create the loop state files for automatic retry: + +**Create pointer file (JSON format):** +```bash +cat > .shipspec/active-loop.local.json << 'EOF' +{ + "feature": "[feature-dir]", + "loop_type": "task-loop", + "state_path": ".shipspec/planning/[feature-dir]/task-loop.local.json", + "created_at": "[ISO timestamp]" +} +EOF +``` + +**Create state file in feature directory (JSON format):** +```bash +cat > .shipspec/planning/[feature-dir]/task-loop.local.json << 'EOF' +{ + "active": true, + "feature": "[feature-dir]", + "task_id": "[task-id]", + "iteration": 1, + "max_iterations": 5, + "started_at": "[ISO timestamp]" +} +EOF +``` + +**Note:** The full task prompt is stored in TASKS.json's `tasks[task-id].prompt` field and can be retrieved by the hook using jq. + +**Log loop start** to `.claude/shipspec-debug.log`: +``` +$(date -u +%Y-%m-%dT%H:%M:%SZ) | [task-id] | LOOP_START | Attempt 1/5 +``` + +**The stop hook will automatically:** +- Retry if verification fails (up to 5 attempts) +- Exit cleanly when `VERIFIED` is output +- Exit cleanly when `BLOCKED` is output + ## Step 7: Summary After displaying the task, show progress: @@ -278,4 +379,4 @@ If task-id doesn't match expected formats: > - Full ID: `TASK-003`" ### Empty Dependencies Field -If a task has `Depends on: None` or no dependencies section, treat it as having no dependencies (ready if status is `[ ]`). +If a task has empty `depends_on` array, treat it as having no dependencies (ready if status is `not_started`). diff --git a/hooks/feature-retry-hook.sh b/hooks/feature-retry-hook.sh new file mode 100755 index 0000000..438e20f --- /dev/null +++ b/hooks/feature-retry-hook.sh @@ -0,0 +1,185 @@ +#!/bin/bash +# hooks/feature-retry-hook.sh +# Stop hook for feature implementation with per-task auto-retry + +set -euo pipefail + +POINTER_FILE=".shipspec/active-loop.local.json" +EXPECTED_LOOP_TYPE="feature-retry" + +# Exit early if no pointer file - BEFORE consuming stdin +# (Multiple hooks share stdin; inactive hooks must not consume it) +if [[ ! -f "$POINTER_FILE" ]]; then + exit 0 +fi + +# Parse pointer file (JSON) to get loop type and state path +LOOP_TYPE=$(jq -r '.loop_type // empty' "$POINTER_FILE" 2>/dev/null || echo "") +STATE_FILE=$(jq -r '.state_path // empty' "$POINTER_FILE" 2>/dev/null || echo "") +FEATURE=$(jq -r '.feature // empty' "$POINTER_FILE" 2>/dev/null || echo "") + +# Exit if this hook's loop type is not active +if [[ "$LOOP_TYPE" != "$EXPECTED_LOOP_TYPE" ]]; then + exit 0 +fi + +# Exit if state file doesn't exist (stale pointer) +if [[ -z "$STATE_FILE" ]] || [[ ! -f "$STATE_FILE" ]]; then + echo "⚠️ Feature retry: Pointer references non-existent state file" >&2 + rm -f "$POINTER_FILE" # Clean up stale pointer + exit 0 +fi + +# Only read stdin if this hook is active +INPUT=$(cat) + +# Check if stdin was already consumed by another hook +if [[ -z "$INPUT" ]]; then + echo "⚠️ Feature retry: No stdin received (likely consumed by another active hook)" >&2 + echo " State file preserved for next session" >&2 + exit 0 # Exit without deleting state - loop continues next time +fi + +TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty') + +# Parse state file (JSON) +TASK_ATTEMPT=$(jq -r '.task_attempt // 0' "$STATE_FILE" 2>/dev/null || echo "0") +MAX_ATTEMPTS=$(jq -r '.max_task_attempts // 5' "$STATE_FILE" 2>/dev/null || echo "5") +TASK_ID=$(jq -r '.current_task_id // empty' "$STATE_FILE" 2>/dev/null || echo "") +TASKS_COMPLETED=$(jq -r '.tasks_completed // 0' "$STATE_FILE" 2>/dev/null || echo "0") +TOTAL_TASKS=$(jq -r '.total_tasks // 0' "$STATE_FILE" 2>/dev/null || echo "0") + +# Get prompt from TASKS.json +TASKS_FILE=".shipspec/planning/$FEATURE/TASKS.json" +if [[ -f "$TASKS_FILE" ]]; then + PROMPT_TEXT=$(jq -r --arg id "$TASK_ID" '.tasks[$id].prompt // empty' "$TASKS_FILE" 2>/dev/null || echo "") +else + PROMPT_TEXT="" +fi + +# Validate numeric fields +if [[ ! "$TASK_ATTEMPT" =~ ^[0-9]+$ ]]; then + echo "⚠️ Feature retry: State file corrupted" >&2 + echo " File: $STATE_FILE" >&2 + echo " Problem: 'task_attempt' field is not a valid number (got: '$TASK_ATTEMPT')" >&2 + echo " Run /cancel-feature-retry or delete the file manually" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +if [[ ! "$MAX_ATTEMPTS" =~ ^[0-9]+$ ]]; then + echo "⚠️ Feature retry: State file corrupted" >&2 + echo " File: $STATE_FILE" >&2 + echo " Problem: 'max_task_attempts' field is not a valid number (got: '$MAX_ATTEMPTS')" >&2 + echo " Run /cancel-feature-retry or delete the file manually" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Validate prompt text exists +if [[ -z "$PROMPT_TEXT" ]]; then + echo "⚠️ Feature retry: Could not retrieve task prompt" >&2 + echo " Task ID: $TASK_ID" >&2 + echo " TASKS.json: $TASKS_FILE" >&2 + echo "" >&2 + echo " This usually means:" >&2 + echo " - TASKS.json was deleted or moved" >&2 + echo " - Task ID doesn't exist in TASKS.json" >&2 + echo "" >&2 + echo " Run /cancel-feature-retry or delete the state file manually" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Validate transcript exists and has content +if [[ -z "$TRANSCRIPT_PATH" ]] || [[ ! -f "$TRANSCRIPT_PATH" ]]; then + echo "⚠️ Feature retry: Transcript not found, allowing exit" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Check for assistant messages +if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null; then + echo "⚠️ Feature retry: No assistant messages in transcript, allowing exit" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Extract last assistant message +LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1) +if [[ -z "$LAST_LINE" ]]; then + echo "⚠️ Feature retry: Failed to extract last assistant message" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Parse with error handling +LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r ' + .message.content | + map(select(.type == "text")) | + map(.text) | + join("\n") +' 2>&1) || true + +if [[ -z "$LAST_OUTPUT" ]]; then + echo "⚠️ Feature retry: Assistant message contained no text" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Check for completion markers +# Feature task complete - allow exit to continue to next task +if echo "$LAST_OUTPUT" | grep -q 'VERIFIED'; then + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Task blocked - allow exit, needs manual intervention +if echo "$LAST_OUTPUT" | grep -q 'BLOCKED'; then + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Feature complete marker - all tasks done +if echo "$LAST_OUTPUT" | grep -q ''; then + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Max attempts check for current task (0 means unlimited) +if [[ $MAX_ATTEMPTS -gt 0 ]] && [[ $TASK_ATTEMPT -ge $MAX_ATTEMPTS ]]; then + echo "⚠️ Feature retry: Max attempts ($MAX_ATTEMPTS) reached for $TASK_ID" >&2 + echo " Run /cancel-feature-retry to clear state if needed" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Increment attempt in state file (JSON) +NEXT_ATTEMPT=$((TASK_ATTEMPT + 1)) +jq --argjson attempt "$NEXT_ATTEMPT" '.task_attempt = $attempt' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" + +# Build context-aware system message +MAX_DISPLAY="$MAX_ATTEMPTS" +if [[ "$MAX_ATTEMPTS" == "0" ]]; then + MAX_DISPLAY="unlimited" +fi + +# All retries are post-verification failures (verification always runs before hook triggers) +SYSTEM_MSG="🔄 **Feature Implementation: Task Retry** + +**Feature:** $FEATURE +**Task:** $TASK_ID (Attempt $NEXT_ATTEMPT/$MAX_DISPLAY) +**Progress:** $TASKS_COMPLETED/$TOTAL_TASKS tasks completed + +Previous attempt did not pass all acceptance criteria. +Review what failed and fix the implementation." + +# Return block decision - feed original prompt back for another attempt +jq -n \ + --arg prompt "$PROMPT_TEXT" \ + --arg msg "$SYSTEM_MSG" \ + '{ + "decision": "block", + "reason": $prompt, + "systemMessage": $msg + }' diff --git a/hooks/hooks.json b/hooks/hooks.json new file mode 100644 index 0000000..276e361 --- /dev/null +++ b/hooks/hooks.json @@ -0,0 +1,22 @@ +{ + "hooks": { + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/task-loop-hook.sh" + }, + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/feature-retry-hook.sh" + }, + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/planning-refine-hook.sh" + } + ] + } + ] + } +} diff --git a/hooks/planning-refine-hook.sh b/hooks/planning-refine-hook.sh new file mode 100755 index 0000000..a2cb318 --- /dev/null +++ b/hooks/planning-refine-hook.sh @@ -0,0 +1,158 @@ +#!/bin/bash +# hooks/planning-refine-hook.sh +# Stop hook for task refinement during planning + +set -euo pipefail + +POINTER_FILE=".shipspec/active-loop.local.json" +EXPECTED_LOOP_TYPE="planning-refine" + +# Exit early if no pointer file - BEFORE consuming stdin +# (Multiple hooks share stdin; inactive hooks must not consume it) +if [[ ! -f "$POINTER_FILE" ]]; then + exit 0 +fi + +# Parse pointer file (JSON) to get loop type and state path +LOOP_TYPE=$(jq -r '.loop_type // empty' "$POINTER_FILE" 2>/dev/null || echo "") +STATE_FILE=$(jq -r '.state_path // empty' "$POINTER_FILE" 2>/dev/null || echo "") +FEATURE=$(jq -r '.feature // empty' "$POINTER_FILE" 2>/dev/null || echo "") + +# Exit if this hook's loop type is not active +if [[ "$LOOP_TYPE" != "$EXPECTED_LOOP_TYPE" ]]; then + exit 0 +fi + +# Exit if state file doesn't exist (stale pointer) +if [[ -z "$STATE_FILE" ]] || [[ ! -f "$STATE_FILE" ]]; then + echo "⚠️ Planning refine: Pointer references non-existent state file" >&2 + rm -f "$POINTER_FILE" # Clean up stale pointer + exit 0 +fi + +# Only read stdin if this hook is active +INPUT=$(cat) + +# Check if stdin was already consumed by another hook +if [[ -z "$INPUT" ]]; then + echo "⚠️ Planning refine: No stdin received (likely consumed by another active hook)" >&2 + echo " State file preserved for next session" >&2 + exit 0 # Exit without deleting state - loop continues next time +fi + +TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty') + +# Parse state file (JSON) +ITERATION=$(jq -r '.iteration // 0' "$STATE_FILE" 2>/dev/null || echo "0") +MAX_ITERATIONS=$(jq -r '.max_iterations // 3' "$STATE_FILE" 2>/dev/null || echo "3") + +# Build refinement prompt from state - for planning-refine, we use a static prompt +# since the refinement instructions are in the state file +PROMPT_TEXT="Continue refining large tasks in \`.shipspec/planning/$FEATURE/TASKS.json\`. + +### Instructions: +1. Read TASKS.json and identify tasks with story points > 5 +2. For each large task, break it into 2-3 smaller subtasks (each ≤3 story points) +3. Update TASKS.json with the new subtasks +4. Update TASKS.md to reflect the changes +5. Preserve acceptance criteria across subtasks +6. Update dependencies pointing to the original task + +### Completion: +When all tasks are ≤5 story points, output: +\`\`" + +# Validate numeric fields +if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then + echo "⚠️ Planning refine: State file corrupted" >&2 + echo " File: $STATE_FILE" >&2 + echo " Problem: 'iteration' field is not a valid number (got: '$ITERATION')" >&2 + echo " Delete the file manually to cancel: rm $STATE_FILE" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +if [[ ! "$MAX_ITERATIONS" =~ ^[0-9]+$ ]]; then + echo "⚠️ Planning refine: State file corrupted" >&2 + echo " File: $STATE_FILE" >&2 + echo " Problem: 'max_iterations' field is not a valid number (got: '$MAX_ITERATIONS')" >&2 + echo " Delete the file manually to cancel: rm $STATE_FILE" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Validate transcript exists and has content +if [[ -z "$TRANSCRIPT_PATH" ]] || [[ ! -f "$TRANSCRIPT_PATH" ]]; then + echo "⚠️ Planning refine: Transcript not found, allowing exit" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Check for assistant messages +if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null; then + echo "⚠️ Planning refine: No assistant messages in transcript, allowing exit" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Extract last assistant message +LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1) +if [[ -z "$LAST_LINE" ]]; then + echo "⚠️ Planning refine: Failed to extract last assistant message" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Parse with error handling +LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r ' + .message.content | + map(select(.type == "text")) | + map(.text) | + join("\n") +' 2>&1) || true + +if [[ -z "$LAST_OUTPUT" ]]; then + echo "⚠️ Planning refine: Assistant message contained no text" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Check for completion marker +if echo "$LAST_OUTPUT" | grep -q ''; then + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Max iterations check (0 means unlimited) +if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then + echo "⚠️ Planning refine: Max iterations ($MAX_ITERATIONS) reached" >&2 + echo " Delete the file manually if needed: rm $STATE_FILE" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Increment iteration in state file (JSON) +NEXT_ITER=$((ITERATION + 1)) +jq --argjson iter "$NEXT_ITER" '.iteration = $iter' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" + +# Build system message +MAX_DISPLAY="$MAX_ITERATIONS" +if [[ "$MAX_ITERATIONS" == "0" ]]; then + MAX_DISPLAY="unlimited" +fi + +SYSTEM_MSG="🔄 **Task Refinement: Iteration $NEXT_ITER/$MAX_DISPLAY** + +**Feature:** $FEATURE + +Continue refining large tasks. Check TASKS.json for any tasks still > 5 story points." + +# Return block decision - feed refinement prompt back for another attempt +jq -n \ + --arg prompt "$PROMPT_TEXT" \ + --arg msg "$SYSTEM_MSG" \ + '{ + "decision": "block", + "reason": $prompt, + "systemMessage": $msg + }' diff --git a/hooks/task-loop-hook.sh b/hooks/task-loop-hook.sh new file mode 100755 index 0000000..c630260 --- /dev/null +++ b/hooks/task-loop-hook.sh @@ -0,0 +1,184 @@ +#!/bin/bash +# hooks/task-loop-hook.sh +# Stop hook for automatic task verification retry loop + +set -euo pipefail + +POINTER_FILE=".shipspec/active-loop.local.json" +EXPECTED_LOOP_TYPE="task-loop" + +# Exit early if no pointer file - BEFORE consuming stdin +# (Multiple hooks share stdin; inactive hooks must not consume it) +if [[ ! -f "$POINTER_FILE" ]]; then + exit 0 +fi + +# Parse pointer file (JSON) to get loop type and state path +LOOP_TYPE=$(jq -r '.loop_type // empty' "$POINTER_FILE" 2>/dev/null || echo "") +STATE_FILE=$(jq -r '.state_path // empty' "$POINTER_FILE" 2>/dev/null || echo "") +FEATURE=$(jq -r '.feature // empty' "$POINTER_FILE" 2>/dev/null || echo "") + +# Exit if this hook's loop type is not active +if [[ "$LOOP_TYPE" != "$EXPECTED_LOOP_TYPE" ]]; then + exit 0 +fi + +# Exit if state file doesn't exist (stale pointer) +if [[ -z "$STATE_FILE" ]] || [[ ! -f "$STATE_FILE" ]]; then + echo "⚠️ Task loop: Pointer references non-existent state file" >&2 + rm -f "$POINTER_FILE" # Clean up stale pointer + exit 0 +fi + +# Only read stdin if this hook is active +INPUT=$(cat) + +# Check if stdin was already consumed by another hook +if [[ -z "$INPUT" ]]; then + echo "⚠️ Task loop: No stdin received (likely consumed by another active hook)" >&2 + echo " State file preserved for next session" >&2 + exit 0 # Exit without deleting state - loop continues next time +fi + +TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty') + +# Parse state file (JSON) +ITERATION=$(jq -r '.iteration // 0' "$STATE_FILE" 2>/dev/null || echo "0") +MAX_ITERATIONS=$(jq -r '.max_iterations // 5' "$STATE_FILE" 2>/dev/null || echo "5") +TASK_ID=$(jq -r '.task_id // empty' "$STATE_FILE" 2>/dev/null || echo "") + +# Get prompt from TASKS.json +TASKS_FILE=".shipspec/planning/$FEATURE/TASKS.json" +if [[ -f "$TASKS_FILE" ]]; then + PROMPT_TEXT=$(jq -r --arg id "$TASK_ID" '.tasks[$id].prompt // empty' "$TASKS_FILE" 2>/dev/null || echo "") +else + PROMPT_TEXT="" +fi + +# Validate numeric fields +if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then + echo "⚠️ Task loop: State file corrupted" >&2 + echo " File: $STATE_FILE" >&2 + echo " Problem: 'iteration' field is not a valid number (got: '$ITERATION')" >&2 + echo " Run /cancel-task-loop or delete the file manually" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +if [[ ! "$MAX_ITERATIONS" =~ ^[0-9]+$ ]]; then + echo "⚠️ Task loop: State file corrupted" >&2 + echo " File: $STATE_FILE" >&2 + echo " Problem: 'max_iterations' field is not a valid number (got: '$MAX_ITERATIONS')" >&2 + echo " Run /cancel-task-loop or delete the file manually" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Validate prompt text exists +if [[ -z "$PROMPT_TEXT" ]]; then + echo "⚠️ Task loop: Could not retrieve task prompt" >&2 + echo " Task ID: $TASK_ID" >&2 + echo " TASKS.json: $TASKS_FILE" >&2 + echo "" >&2 + echo " This usually means:" >&2 + echo " - TASKS.json was deleted or moved" >&2 + echo " - Task ID doesn't exist in TASKS.json" >&2 + echo "" >&2 + echo " Run /cancel-task-loop or delete the state file manually" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Validate transcript exists and has content +if [[ -z "$TRANSCRIPT_PATH" ]] || [[ ! -f "$TRANSCRIPT_PATH" ]]; then + echo "⚠️ Task loop: Transcript not found, allowing exit" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Check for assistant messages +if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null; then + echo "⚠️ Task loop: No assistant messages in transcript, allowing exit" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Extract last assistant message +LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1) +if [[ -z "$LAST_LINE" ]]; then + echo "⚠️ Task loop: Failed to extract last assistant message" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Parse with error handling +LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r ' + .message.content | + map(select(.type == "text")) | + map(.text) | + join("\n") +' 2>&1) || true + +if [[ -z "$LAST_OUTPUT" ]]; then + echo "⚠️ Task loop: Assistant message contained no text" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Check for completion markers +if echo "$LAST_OUTPUT" | grep -q 'VERIFIED'; then + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 # Allow exit - task verified +fi + +if echo "$LAST_OUTPUT" | grep -q 'BLOCKED'; then + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 # Allow exit - task blocked, needs manual intervention +fi + +if echo "$LAST_OUTPUT" | grep -q 'INCOMPLETE'; then + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 # Allow exit - task incomplete, needs manual fixes +fi + +if echo "$LAST_OUTPUT" | grep -q 'MISALIGNED'; then + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 # Allow exit - implementation misaligned with planning +fi + +# Max iterations check (0 means unlimited) +if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then + echo "⚠️ Task loop: Max iterations ($MAX_ITERATIONS) reached for $TASK_ID" >&2 + echo " Run /cancel-task-loop to clear state if needed" >&2 + rm -f "$STATE_FILE" "$POINTER_FILE" + exit 0 +fi + +# Increment iteration in state file (JSON) +NEXT_ITERATION=$((ITERATION + 1)) +jq --argjson iter "$NEXT_ITERATION" '.iteration = $iter' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE" + +# Build context-aware system message +MAX_DISPLAY="$MAX_ITERATIONS" +if [[ "$MAX_ITERATIONS" == "0" ]]; then + MAX_DISPLAY="unlimited" +fi + +# All retries are post-verification failures (verification always runs before hook triggers) +SYSTEM_MSG="🔄 **Task Loop: Attempt $NEXT_ITERATION/$MAX_DISPLAY** + +**Task:** $TASK_ID +**Feature:** $FEATURE + +Previous attempt did not pass all acceptance criteria. +Review what failed and continue implementation. When done, run task-verifier to check completion." + +# Return block decision - feed original prompt back for another attempt +jq -n \ + --arg prompt "$PROMPT_TEXT" \ + --arg msg "$SYSTEM_MSG" \ + '{ + "decision": "block", + "reason": $prompt, + "systemMessage": $msg + }' diff --git a/skills/agent-prompts/SKILL.md b/skills/agent-prompts/SKILL.md index e752e38..6b43b8c 100644 --- a/skills/agent-prompts/SKILL.md +++ b/skills/agent-prompts/SKILL.md @@ -1,27 +1,159 @@ --- name: agent-prompts description: This skill should be used when the user asks to "generate tasks", "create implementation plan", "break down feature", "write agent prompts", "decompose into tasks", "create work items", or when creating agent-ready task descriptions from PRD and SDD documents. -version: 0.1.0 +version: 0.2.0 --- # Agent Prompt Generation Create structured task prompts that coding agents can execute effectively. -## Task Prompt Template +## Output Files + +Task generation produces two files: + +| File | Purpose | Used By | +|------|---------|---------| +| `TASKS.json` | Machine-parseable metadata | Plugin agents, hooks, commands | +| `TASKS.md` | Human-readable task prompts | Developers, code review | + +**Source of Truth**: TASKS.json is authoritative. TASKS.md is a derived human-readable view. + +## TASKS.json Structure + +```json +{ + "version": "1.0", + "feature": "feature-name", + "summary": { + "total_tasks": 5, + "total_points": 18, + "critical_path": ["TASK-001", "TASK-003", "TASK-005"] + }, + "phases": [ + { "id": 1, "name": "Foundation" }, + { "id": 2, "name": "Core Implementation" }, + { "id": 3, "name": "Polish" } + ], + "tasks": { + "TASK-001": { + "title": "Setup Database Schema", + "status": "not_started", + "phase": 1, + "points": 3, + "depends_on": [], + "blocks": ["TASK-002", "TASK-003"], + "prd_refs": ["REQ-001", "REQ-002"], + "sdd_refs": ["Section 5.1"], + "acceptance_criteria": [ + "Schema file exists at db/schema.sql", + "All tables have primary keys", + "Foreign key relationships match SDD", + "Migration runs without errors" + ], + "testing": [ + "Run migration: npm run db:migrate", + "Verify tables: npm run db:verify" + ], + "prompt": "## Context\nThis task establishes the data layer...\n\n## Requirements\n- Create users table with id, email, created_at\n..." + } + } +} +``` + +### Task Fields + +| Field | Type | Description | +|-------|------|-------------| +| `title` | string | Clear, action-oriented task title | +| `status` | enum | `"not_started"` \| `"in_progress"` \| `"completed"` | +| `phase` | integer | Phase number (1-indexed) | +| `points` | integer | Fibonacci story points: 1, 2, 3, 5, 8 | +| `depends_on` | array | Task IDs that must complete first | +| `blocks` | array | Task IDs that depend on this task | +| `prd_refs` | array | Requirement IDs (REQ-XXX) this task addresses | +| `sdd_refs` | array | SDD section references | +| `acceptance_criteria` | array | Verifiable criteria for completion | +| `testing` | array | Test commands or verification steps | +| `prompt` | string | Full markdown prompt for implementation | -Each task should include this structure. Note the checkbox format `- [ ]` in the header for status tracking: +## TASKS.md Structure + +The markdown file contains human-readable content only. No status markers, dependencies, or acceptance criteria (those live in JSON). ```markdown -### - [ ] TASK-XXX: [Clear, Action-Oriented Title] +# Implementation Tasks: [Feature Name] + +## Summary + +- Total Tasks: 5 +- Total Story Points: 18 +- Critical Path: TASK-001 → TASK-003 → TASK-005 + +## Requirement Coverage + +| Requirement | Task(s) | +|-------------|---------| +| REQ-001 | TASK-001, TASK-003 | +| REQ-002 | TASK-002, TASK-004 | + +--- + +## Phase 1: Foundation + +### TASK-001: Setup Database Schema + +#### Context +This task establishes the data layer for the feature. The schema must support +all entities defined in the SDD and enable the API operations in Phase 2. + +#### Requirements +- Create users table with id, email, created_at +- Create sessions table with foreign key to users +- Add indexes for common query patterns + +#### Technical Approach +Follow the existing migration pattern in `db/migrations/`. Use the same +column naming conventions as existing tables. + +#### Files to Create/Modify +- `db/migrations/002_add_users.sql` - New migration file +- `db/schema.sql` - Update schema documentation + +#### Key Interfaces +```sql +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + email VARCHAR(255) UNIQUE NOT NULL, + created_at TIMESTAMP DEFAULT NOW() +); +``` + +#### Constraints +- Follow existing naming conventions (snake_case) +- Use SERIAL for auto-increment IDs +- All timestamps must include timezone + +--- + +## Phase 2: Core Implementation + +### TASK-002: Create API Endpoints +... +``` +## Task Prompt Template + +Each task's `prompt` field should follow this structure: + +```markdown ## Context [2-3 sentences explaining where this task fits in the larger system and why it matters] ## Requirements -- [ ] [Specific, verifiable requirement 1] -- [ ] [Specific, verifiable requirement 2] -- [ ] [Specific, verifiable requirement 3] +- [Specific, verifiable requirement 1] +- [Specific, verifiable requirement 2] +- [Specific, verifiable requirement 3] ## Technical Approach @@ -49,48 +181,22 @@ interface ExpectedOutput { - Use `[specific library]` for `[purpose]` - Do not modify `[protected area]` - Maintain backward compatibility with `[existing API]` - -## Testing Requirements -- Unit test: [What to test, expected coverage] -- Integration test: [End-to-end scenario to verify] -- Edge cases: [Specific edge cases to handle] - -## Acceptance Criteria -- [ ] [Criterion 1 - must be verifiable] -- [ ] [Criterion 2 - must be verifiable] -- [ ] All tests pass -- [ ] No type errors (if applicable) -- [ ] Linting passes (if configured) - -## Dependencies -- Depends on: [TASK-XXX] (must complete first) -- Blocks: [TASK-YYY] (cannot start until this completes) - -## References -- Design Doc: Section X.Y -- PRD: REQ-XXX, REQ-YYY -- Similar implementation: `path/to/similar/code` - -## Estimated Effort -- Story Points: [1/2/3/5/8] ``` ## Task Sizing Guidelines -| Points | Description | Duration | Example | -|--------|-------------|----------|---------| -| 1 | Trivial change | < 2 hours | Config update, copy change | -| 2 | Small task | 2-4 hours | Single function, simple component | -| 3 | Medium task | 4-8 hours | Multiple functions, moderate complexity | -| 5 | Large task | 1-2 days | Significant feature piece | -| 8 | Complex task | 2-3 days | Cross-cutting, multiple systems | +| Points | Description | Example | +|--------|-------------|---------| +| 1 | Trivial change | Config update, copy change | +| 2 | Small task | Single function, simple component | +| 3 | Medium task | Multiple functions, moderate complexity | +| 5 | Large task | Significant feature piece | +| 8 | Complex task | Cross-cutting, multiple systems | -**Rule:** If a task would be larger than 8 points, break it down. +**Rule:** Tasks >5 points trigger auto-refinement. Tasks >8 points must be broken down. ## Task Categories -Assign each task to exactly one category: - | Category | Description | Examples | |----------|-------------|----------| | `feature` | New user-facing functionality | New UI component, API endpoint | @@ -104,7 +210,7 @@ Assign each task to exactly one category: When generating tasks, identify: -1. **Hard Dependencies (Blocks)** +1. **Hard Dependencies (depends_on/blocks)** - Task X must complete before Task Y can start - Usually: schema → API → UI @@ -114,99 +220,49 @@ When generating tasks, identify: 3. **Critical Path** - The longest chain of dependent tasks - - Determines minimum project duration - -## Task Status Tracking - -Tasks use checkbox format in the header for status tracking: - -| Status | Format | Meaning | -|--------|--------|---------| -| Not Started | `### - [ ] TASK-XXX:` | Ready to implement | -| In Progress | `### - [~] TASK-XXX:` | Currently being worked on | -| Completed | `### - [x] TASK-XXX:` | Done and verified | + - Store in `summary.critical_path` ## Execution Phases Group tasks into logical phases: -```markdown -### Phase 1: Foundation -- [ ] TASK-001: Database schema -- [ ] TASK-002: Base types and interfaces -[These must complete first] - -### Phase 2: Core Implementation -- [ ] TASK-003: API endpoints (depends on 001, 002) -- [ ] TASK-004: Service layer (depends on 001, 002) -[Can be parallelized] - -### Phase 3: UI Layer -- [ ] TASK-005: Components (depends on 003) -- [ ] TASK-006: Pages (depends on 005) - -### Phase 4: Polish -- [ ] TASK-007: Tests (depends on 003, 004, 005) -- [ ] TASK-008: Documentation (depends on all) -``` - -## Output Format for Task List - -When generating a complete task list: - -```markdown -# Implementation Tasks: [Feature Name] - -## Summary -- Total Tasks: X -- Total Story Points: Y -- Estimated Duration: Z sessions -- Critical Path: TASK-001 → TASK-003 → TASK-005 - -## Requirement Coverage -| Requirement | Task(s) | -|-------------|---------| -| REQ-001 | TASK-001, TASK-003 | -| REQ-002 | TASK-002, TASK-004 | - -## Phase 1: [Phase Name] - -### - [ ] TASK-001: [Title] -[Full task prompt...] - -### - [ ] TASK-002: [Title] -[Full task prompt...] - -## Phase 2: [Phase Name] - -### - [ ] TASK-003: [Title] -[Full task prompt...] -``` +| Phase | Purpose | Typical Tasks | +|-------|---------|---------------| +| 1: Foundation | Setup and infrastructure | Schema, types, config | +| 2: Core | Main implementation | APIs, services, core logic | +| 3: UI | User interface | Components, pages, forms | +| 4: Polish | Quality and docs | Tests, documentation, cleanup | ## Quality Checklist Before finalizing tasks: -- [ ] Every task has clear acceptance criteria +- [ ] Every task has clear acceptance criteria in JSON - [ ] Dependencies form a valid DAG (no cycles) - [ ] No task exceeds 8 story points - [ ] Every requirement (REQ-XXX) maps to at least one task - [ ] Tests are included as explicit tasks - [ ] File paths reference actual codebase structure +- [ ] `prompt` field contains full implementation guidance ## Typical Decomposition Pattern ``` Feature X -├── - [ ] TASK-001: Database schema/migrations (infrastructure) -├── - [ ] TASK-002: Type definitions and interfaces (infrastructure) -├── - [ ] TASK-003: API endpoint - create (feature) -├── - [ ] TASK-004: API endpoint - read (feature) -├── - [ ] TASK-005: API endpoint - update (feature) -├── - [ ] TASK-006: API endpoint - delete (feature) -├── - [ ] TASK-007: UI component - form (feature) -├── - [ ] TASK-008: UI component - list (feature) -├── - [ ] TASK-009: Unit tests (testing) -├── - [ ] TASK-010: Integration tests (testing) -└── - [ ] TASK-011: Documentation (documentation) +├── TASK-001: Database schema/migrations (infrastructure, 3pts) +├── TASK-002: Type definitions and interfaces (infrastructure, 2pts) +├── TASK-003: API endpoint - create (feature, 3pts) +├── TASK-004: API endpoint - read (feature, 2pts) +├── TASK-005: API endpoint - update (feature, 3pts) +├── TASK-006: API endpoint - delete (feature, 2pts) +├── TASK-007: UI component - form (feature, 3pts) +├── TASK-008: UI component - list (feature, 3pts) +├── TASK-009: Unit tests (testing, 3pts) +├── TASK-010: Integration tests (testing, 3pts) +└── TASK-011: Documentation (documentation, 2pts) ``` + +## Schema Reference + +See `references/tasks-schema.json` for the full JSON Schema specification. +See `references/state-schemas.json` for loop state file schemas. diff --git a/skills/agent-prompts/references/state-schemas.json b/skills/agent-prompts/references/state-schemas.json new file mode 100644 index 0000000..46a158d --- /dev/null +++ b/skills/agent-prompts/references/state-schemas.json @@ -0,0 +1,168 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://shipspec.dev/schemas/state.json", + "title": "Loop State File Schemas", + "description": "Schemas for Ralph Loop state files", + + "$defs": { + "task-loop": { + "title": "Task Loop State", + "description": "State for per-task auto-retry loop", + "type": "object", + "required": ["active", "feature", "task_id", "iteration", "max_iterations", "started_at"], + "properties": { + "active": { + "type": "boolean", + "description": "Whether the loop is currently active" + }, + "feature": { + "type": "string", + "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$", + "description": "Feature name (kebab-case)" + }, + "task_id": { + "type": "string", + "pattern": "^TASK-[0-9]{3}$", + "description": "Current task being implemented" + }, + "iteration": { + "type": "integer", + "minimum": 1, + "description": "Current retry iteration" + }, + "max_iterations": { + "type": "integer", + "minimum": 1, + "default": 5, + "description": "Maximum retry attempts before escalation" + }, + "started_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when loop started" + } + } + }, + + "feature-retry": { + "title": "Feature Retry State", + "description": "State for feature-wide implementation with per-task retry", + "type": "object", + "required": ["active", "feature", "current_task_id", "task_attempt", "max_task_attempts", "tasks_completed", "total_tasks", "started_at"], + "properties": { + "active": { + "type": "boolean", + "description": "Whether the loop is currently active" + }, + "feature": { + "type": "string", + "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$", + "description": "Feature name (kebab-case)" + }, + "current_task_id": { + "type": "string", + "pattern": "^TASK-[0-9]{3}$", + "description": "Currently active task being implemented" + }, + "task_attempt": { + "type": "integer", + "minimum": 1, + "description": "Current retry attempt for the current task" + }, + "max_task_attempts": { + "type": "integer", + "minimum": 1, + "default": 5, + "description": "Maximum retry attempts per task" + }, + "tasks_completed": { + "type": "integer", + "minimum": 0, + "description": "Number of tasks already completed" + }, + "total_tasks": { + "type": "integer", + "minimum": 1, + "description": "Total number of tasks in the feature" + }, + "started_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when loop started" + } + } + }, + + "planning-refine": { + "title": "Planning Refinement State", + "description": "State for large task auto-refinement loop", + "type": "object", + "required": ["active", "feature", "iteration", "max_iterations", "large_tasks", "tasks_refined", "started_at"], + "properties": { + "active": { + "type": "boolean", + "description": "Whether the refinement loop is active" + }, + "feature": { + "type": "string", + "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$", + "description": "Feature name (kebab-case)" + }, + "iteration": { + "type": "integer", + "minimum": 1, + "description": "Current refinement iteration" + }, + "max_iterations": { + "type": "integer", + "minimum": 1, + "default": 3, + "description": "Maximum refinement iterations" + }, + "large_tasks": { + "type": "array", + "items": { + "type": "string", + "pattern": "^TASK-[0-9]{3}$" + }, + "description": "Task IDs with >5 story points needing refinement" + }, + "tasks_refined": { + "type": "integer", + "minimum": 0, + "description": "Number of tasks successfully refined so far" + }, + "started_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when refinement started" + } + } + }, + + "active-loop-pointer": { + "title": "Active Loop Pointer", + "description": "Pointer file indicating which loop is currently active", + "type": "object", + "required": ["feature", "loop_type", "state_path", "created_at"], + "properties": { + "feature": { + "type": "string", + "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" + }, + "loop_type": { + "type": "string", + "enum": ["task-loop", "feature-retry", "planning-refine"] + }, + "state_path": { + "type": "string", + "description": "Path to the state file relative to project root" + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + } + } +} diff --git a/skills/agent-prompts/references/tasks-schema.json b/skills/agent-prompts/references/tasks-schema.json new file mode 100644 index 0000000..f7e2319 --- /dev/null +++ b/skills/agent-prompts/references/tasks-schema.json @@ -0,0 +1,165 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://shipspec.dev/schemas/tasks.json", + "title": "TASKS.json Schema", + "description": "Machine-parseable task metadata for ShipSpec feature implementation", + "type": "object", + "required": ["version", "feature", "summary", "phases", "tasks"], + "properties": { + "version": { + "type": "string", + "description": "Schema version for forward compatibility", + "const": "1.0" + }, + "feature": { + "type": "string", + "description": "Feature name (kebab-case)", + "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" + }, + "summary": { + "type": "object", + "required": ["total_tasks", "total_points", "critical_path"], + "properties": { + "total_tasks": { + "type": "integer", + "minimum": 1, + "description": "Total number of tasks" + }, + "total_points": { + "type": "integer", + "minimum": 1, + "description": "Sum of all story points" + }, + "critical_path": { + "type": "array", + "items": { + "type": "string", + "pattern": "^TASK-[0-9]{3}$" + }, + "description": "Longest dependency chain determining minimum duration" + } + } + }, + "phases": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": { + "type": "integer", + "minimum": 1 + }, + "name": { + "type": "string", + "minLength": 1 + } + } + }, + "description": "Logical groupings of tasks" + }, + "tasks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/task" + }, + "propertyNames": { + "pattern": "^TASK-[0-9]{3}$" + }, + "description": "Map of task ID to task metadata" + } + }, + "$defs": { + "task": { + "type": "object", + "required": [ + "title", + "status", + "phase", + "points", + "depends_on", + "blocks", + "prd_refs", + "sdd_refs", + "acceptance_criteria", + "testing", + "prompt" + ], + "properties": { + "title": { + "type": "string", + "minLength": 1, + "description": "Clear, action-oriented task title" + }, + "status": { + "type": "string", + "enum": ["not_started", "in_progress", "completed"], + "description": "Current task status" + }, + "phase": { + "type": "integer", + "minimum": 1, + "description": "Phase number this task belongs to" + }, + "points": { + "type": "integer", + "enum": [1, 2, 3, 5, 8], + "description": "Fibonacci story points (tasks >5 trigger auto-refinement)" + }, + "depends_on": { + "type": "array", + "items": { + "type": "string", + "pattern": "^TASK-[0-9]{3}$" + }, + "description": "Task IDs that must complete before this task can start" + }, + "blocks": { + "type": "array", + "items": { + "type": "string", + "pattern": "^TASK-[0-9]{3}$" + }, + "description": "Task IDs that cannot start until this task completes" + }, + "prd_refs": { + "type": "array", + "items": { + "type": "string", + "pattern": "^REQ-[0-9]{3}$" + }, + "description": "Requirement IDs from PRD this task addresses" + }, + "sdd_refs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "SDD section references (e.g., 'Section 5.1')" + }, + "acceptance_criteria": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Verifiable criteria for task completion" + }, + "testing": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Test commands or verification steps" + }, + "prompt": { + "type": "string", + "minLength": 1, + "description": "Full task prompt markdown (Context, Requirements, Technical Approach, etc.)" + } + } + } + } +} diff --git a/skills/task-loop-verify/SKILL.md b/skills/task-loop-verify/SKILL.md new file mode 100644 index 0000000..add5683 --- /dev/null +++ b/skills/task-loop-verify/SKILL.md @@ -0,0 +1,164 @@ +--- +name: task-loop-verify +description: Verify the current task and output a completion marker. Use this to check if a task is complete and signal the stop hook to allow session exit. +version: 0.1.0 +allowed-tools: Read, Glob, Grep, Bash(rm:*), Bash(jq:*), Task +--- + +# Task Loop Verification + +Verify the current task in the loop and output a completion marker if successful. This skill wraps task-verifier and outputs the marker that the stop hook looks for. + +## When to Use + +Use this skill when: +- You've finished implementing a task and want to verify it +- The stop hook has triggered another loop iteration +- You want to exit the task loop cleanly + +## Process + +### Step 1: Read Loop State + +First, check for the pointer file and get the state path: + +```bash +if [[ -f .shipspec/active-loop.local.json ]]; then + LOOP_TYPE=$(jq -r '.loop_type // empty' .shipspec/active-loop.local.json) + if [[ "$LOOP_TYPE" == "task-loop" ]]; then + jq -r '.state_path // empty' .shipspec/active-loop.local.json + else + echo "WRONG_LOOP_TYPE" + fi +else + echo "NO_POINTER" +fi +``` + +**If NO_POINTER or WRONG_LOOP_TYPE:** +> "No active task loop. Run `/implement-task ` to start a task." +> Stop here. + +**If state_path found:** +Check the state file exists: +```bash +test -f [state_path] && echo "EXISTS" || echo "NO_STATE_FILE" +``` + +**If NO_STATE_FILE:** +Clean up stale pointer and report: +```bash +rm -f .shipspec/active-loop.local.json +``` +> "No active task loop (stale pointer cleaned up). Run `/implement-task ` to start a task." +> Stop here. + +**If found:** +Parse the JSON state file to extract: +```bash +jq -r '.feature // empty' [state_path] +jq -r '.task_id // empty' [state_path] +jq -r '.iteration // 0' [state_path] +jq -r '.max_iterations // 5' [state_path] +``` + +- `feature`: The feature directory name +- `task_id`: The task being implemented +- `iteration`: Current attempt number +- `max_iterations`: Maximum allowed attempts + +**Store the state_path for cleanup later.** + +### Step 2: Load Task Prompt + +The task prompt is stored in TASKS.json, not in the state file. + +Read the prompt from TASKS.json: +```bash +jq -r --arg id "[task_id]" '.tasks[$id].prompt // empty' .shipspec/planning/[feature]/TASKS.json +``` + +### Step 3: Run Verification + +Delegate to the `task-verifier` agent with: +- The full task prompt (including acceptance criteria) +- The feature name +- The task ID + +### Step 4: Handle Result + +Based on task-verifier result: + +#### VERIFIED + +All acceptance criteria passed. + +1. Clean up state file: + ```bash + rm -f [state_path] .shipspec/active-loop.local.json + ``` + +2. Update TASKS.json: Use task-manager agent with `update_status` operation to set status to `completed` + +3. Log completion to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task_id] | LOOP_END | VERIFIED after [iteration] attempts + ``` + +4. **Output the completion marker:** + `VERIFIED` + +5. Tell user: "Task [task_id] verified! All acceptance criteria passed." + +#### INCOMPLETE + +Some criteria failed. Manual intervention required. + +1. Clean up state file: + ```bash + rm -f [state_path] .shipspec/active-loop.local.json + ``` + +2. Log the failure to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task_id] | LOOP_END | INCOMPLETE | [brief failure reason] + ``` + +3. **Output the incomplete marker:** + `INCOMPLETE` + +4. Show the user what failed: + > "## Verification Failed + > + > The following criteria are not met: + > - [List failed criteria] + > + > Please fix these issues and run `/implement-task [feature]` again." + +#### BLOCKED + +Cannot verify due to infrastructure issues. + +1. Clean up state file: + ```bash + rm -f [state_path] .shipspec/active-loop.local.json + ``` + +2. Log to `.claude/shipspec-debug.log`: + ``` + $(date -u +%Y-%m-%dT%H:%M:%SZ) | [task_id] | LOOP_END | BLOCKED | [reason] + ``` + +3. **Output the blocked marker:** + `BLOCKED` + +4. Tell user: "Task verification is blocked: [reason]. Manual intervention required." + +## Important Notes + +1. **Completion markers are critical** - the stop hook looks for these to decide whether to allow session exit +2. **All results output markers** - VERIFIED, INCOMPLETE, and BLOCKED all output completion markers to exit the loop +3. **INCOMPLETE requires manual fix** - user must address issues and re-run `/implement-task` +4. **BLOCKED needs investigation** - tasks that can't be verified need manual attention +5. **State file cleanup** - always remove on any completion (VERIFIED, INCOMPLETE, or BLOCKED) to prevent stale loops +6. **Prompts in TASKS.json** - the task prompt is read from TASKS.json, not stored in the state file