Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions SPEC_DRIVEN_PHASE2_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Spec-Driven Workflow — Phase 2 Plan (Remaining Features)

## Current State
- Constitution enforced with phase gates
- EARS notation + REQ-XXX.Y.Z IDs
- Orphan REQ hallucination detection
- Task dependency analysis (ParseTasks + AnalyzeTaskGroups)
- Parallel group identification

## Target: Complete All Remaining Features

### Phase A: Parallel Subagent Execution
**Goal:** Execute independent task groups in parallel using existing MultiAgentTool infrastructure.

**Changes:**
1. Create `internal/tool/spec_parallel.go` — SpecParallelTool
- Reads tasks.md, parses with spec.ParseTasks
- Groups with spec.AnalyzeTaskGroups
- Executes parallel groups via MultiAgentTool
- Collects results and reports

2. Leverage existing infrastructure:
- MultiAgentTool (already supports parallel execution)
- SpawnController.SpawnBackground (async spawning)
- taskruntime.Registry (result collection)

### Phase B: Reactive Test Execution on File Save
**Goal:** When a source file is saved, run related tests automatically.

**Changes:**
1. Enhance `internal/hooks/file_watcher.go`:
- On .go file save: run `go test ./<pkg>/...`
- On .ts/.js file save: run related test file
- On _test.go file save: run that specific test
2. Fire new `EventTestResult` hook with pass/fail status
3. Report results back to agent for fix iteration

### Phase C: Task Checklist Auto-Update
**Goal:** When code changes implement a task, auto-mark it complete.

**Changes:**
1. Create `internal/tool/spec_progress.go` — SpecProgressTool
- Scans code for REQ IDs
- Matches against tasks.md REQ references
- Marks tasks complete when implementation detected
- Reports progress stats

### Phase D: Multiple Plan Variations
**Goal:** Generate and compare multiple implementation approaches.

**Changes:**
1. Extend PlanTool with `variations` parameter
- Generate N different plan approaches
- Compare tradeoffs (performance, simplicity, risk)
- Let user choose preferred approach

### Phase E: Context Grounding Hooks
**Goal:** Before each spec stage, probe the repository for relevant context.

**Changes:**
1. Create `internal/tool/spec_ground.go` — SpecGroundTool
- Before Specify: gather codebase structure, related files
- Before Design: gather existing patterns, dependencies
- Before Plan: gather API contracts, test coverage
- Inject findings into system prompt

### Phase F: Spec Versioning in VCS
**Goal:** Ensure specs are committed alongside code.

**Changes:**
1. Create `internal/tool/spec_version.go` — SpecVersionTool
- Stage .hawk/specs/ changes
- Generate commit message referencing REQ IDs
- Link code commits to spec requirements

## Verification Strategy
1. After each phase: run `go test ./...` — must pass
2. After all phases: run `make lint` — must pass
3. Compare feature matrix against target
4. Iterate on any gaps found
124 changes: 124 additions & 0 deletions SPEC_DRIVEN_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Spec-Driven Workflow — Complete Implementation Plan

## Current State (After Initial Implementation)
- Proposal → Specify + Design (parallel) → Plan → Tasks → Implementing
- Constitution tool exists but is optional
- Cross-artifact validation exists (AnalyzeTool)
- Convergence tool exists but is manual
- File-watch hooks bridge exists
- Command hooks with patterns exist

## Target State (Perfect Spec-Driven Workflow)

### Phase 1: Constitution as Enforced Guardrails
**Goal:** Constitution is REQUIRED before spec work begins. Gates enforced at Plan transition.

**Changes:**
1. Constitution must exist before Specify/Design can run
2. Constitution injected into system prompt at ALL spec stages
3. Phase gates checked at Plan transition:
- Simplicity Gate: ≤3 projects/modules
- Anti-Abstraction Gate: framework used directly
- Integration-First Gate: contracts defined before implementation
- Test-First Gate: tests written before code
4. Gate failures require documented justification in "Complexity Tracking" section

**Files:**
- `internal/tool/spec_constitution.go` — enforce constitution requirement
- `internal/engine/safety/permission_engine.go` — gate at Plan transition
- `internal/engine/permission_session_methods.go` — inject constitution into prompt

### Phase 2: EARS Notation + REQ-XXX.Y.Z Requirement IDs
**Goal:** Structured requirements with traceable IDs.

**Changes:**
1. EARS patterns enforced in spec.md:
- Ubiquitous: "The system shall..."
- Event-driven: "WHEN ... THEN ..."
- State-driven: "WHILE ... THEN ..."
- Unwanted: "The system shall not..."
- Optional: "IF ... THEN ..."
2. Each requirement gets REQ-XXX.Y.Z ID
3. Tasks reference REQ IDs
4. Code comments cite REQ IDs

**Files:**
- `internal/spec/validator.go` — EARS validation + REQ ID format
- `internal/tool/spec.go` — task format with REQ IDs
- `internal/tool/spec_analyze.go` — REQ coverage analysis

### Phase 3: NEEDS CLARIFICATION Enforcement
**Goal:** Explicit ambiguity markers prevent AI guessing.

**Changes:**
1. Max 3 unresolved NEEDS CLARIFICATION markers at a time
2. Cannot advance to Plan until all resolved
3. ClarifyTool auto-generates questions for detected ambiguities
4. System prompt instructs AI to use markers instead of guessing

**Files:**
- `internal/spec/validator.go` — enforcement logic
- `internal/tool/spec_clarify.go` — auto-question generation
- `internal/engine/safety/permission_engine.go` — gate at Plan transition

### Phase 4: Orphan REQ Hallucination Detection
**Goal:** Automated detection of code that doesn't trace to spec.

**Changes:**
1. Scan all source files for [REQ-XXX.Y.Z] citations
2. Compare against spec requirements
3. Orphan detection: code cites REQ not in spec → hallucination
4. Missing detection: REQ in spec not cited in code → gap

**Files:**
- `internal/spec/validator.go` — new ScanCodeForRequirements function
- `internal/tool/spec_analyze.go` — orphan/missing detection
- `internal/tool/spec_converge.go` — use detection results

### Phase 5: Subagent Parallelization
**Goal:** Independent tasks executed in parallel.

**Changes:**
1. Task dependency analysis (which tasks depend on which)
2. Parallel group identification
3. Subagent pool execution for independent tasks
4. Merge results after parallel phase completes

**Files:**
- `internal/tool/spec.go` — task dependency parsing
- `internal/engine/workflow/` — parallel execution support

### Phase 6: Auto Convergence Loop
**Goal:** Automatic spec-to-codebase gap detection and repair.

**Changes:**
1. Auto-trigger ConvergeTool after implementation phase
2. Detect drift between spec and codebase
3. Generate convergence tasks automatically
4. Loop until spec ↔ codebase match

**Files:**
- `internal/tool/spec_converge.go` — auto-trigger logic
- `internal/engine/safety/permission_engine.go` — post-implement hook

### Phase 7: Reactive File-Watch Validation
**Goal:** Continuous verification on file changes.

**Changes:**
1. On file save: run related tests
2. On file save: check REQ citations match spec
3. On file save: update task checklist automatically
4. On test failure: notify agent to fix

**Files:**
- `internal/hooks/file_watcher.go` — enhanced with validation
- `internal/hooks/hooks.go` — new hook events for test results

### Phase 8: Verification & Iteration
**Goal:** Compare against target, iterate until perfect.

**Process:**
1. Run full test suite after each phase
2. Compare feature matrix against target
3. Fix any gaps found
4. Iterate until all checks pass
4 changes: 2 additions & 2 deletions cmd/chat_subcommand_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func (s *specSubcommand) Handle(m *chatModel, args []string, text string) (tea.M
return handleSpecConfig(m, arg)
}

m.session.PermSvc().SetSpecStage(engine.SpecStageSpecify)
m.messages = append(m.messages, displayMsg{role: "system", content: "Spec workflow started — Write/Edit/Bash are gated until spec.md, plan.md, and tasks.md are written and ApproveImplementation is approved."})
m.session.PermSvc().SetSpecStage(engine.SpecStageProposal)
m.messages = append(m.messages, displayMsg{role: "system", content: "Spec workflow started — Write/Edit/Bash are gated. Start with Proposal, then Specify + Design (parallel), then Plan, Tasks, and ApproveImplementation."})
if arg == "" {
return m, nil
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/chat_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,8 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if chosen != nil && m.session != nil {
switch chosen.Action {
case specActionStart:
m.session.PermSvc().SetSpecStage(engine.SpecStageSpecify)
m.messages = append(m.messages, displayMsg{role: "system", content: "Spec workflow started — Write/Edit/Bash are gated until spec.md, plan.md, and tasks.md are written and ApproveImplementation is approved."})
m.session.PermSvc().SetSpecStage(engine.SpecStageProposal)
m.messages = append(m.messages, displayMsg{role: "system", content: "Spec workflow started — Write/Edit/Bash are gated. Start with Proposal, then Specify + Design (parallel), then Plan, Tasks, and ApproveImplementation."})
case specActionStatus:
m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Spec stage: %s", specStageLabel(m.session))})
case specActionEdit:
Expand Down
8 changes: 6 additions & 2 deletions cmd/spec_picker.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ type specPickerEntry struct {
}

var specPickerEntries = []specPickerEntry{
{specActionStart, "Start", "Begin the workflow — writes spec.md, then plan.md, then tasks.md"},
{specActionStart, "Start", "Begin the workflow — writes proposal.md, then spec.md + design.md, then plan.md, then tasks.md"},
{specActionStatus, "Status", "Show the current stage"},
{specActionEdit, "Edit", "Edit the active spec's artifacts (spec.md, plan.md, tasks.md)"},
{specActionEdit, "Edit", "Edit the active spec's artifacts"},
{specActionResume, "Resume", "Resume from the current stage — continue where you left off"},
{specActionArchive, "Archive", "Archive a completed spec when implementation is done"},
{specActionConfigure, "Configure", "Set language, framework, methodology, architecture preferences"},
Expand Down Expand Up @@ -207,8 +207,12 @@ func (sp *SpecPicker) Render(viewWidth int) string {
// rather than a *engine.Session.
func specStageDisplayName(stage engine.SpecStage) string {
switch stage {
case engine.SpecStageProposal:
return "Proposal"
case engine.SpecStageSpecify:
return "Specify"
case engine.SpecStageDesign:
return "Design"
case engine.SpecStagePlan:
return "Plan"
case engine.SpecStageTasks:
Expand Down
4 changes: 2 additions & 2 deletions cmd/spec_picker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func TestChatSpecSubcommand_WithDescriptionStartsDirectly(t *testing.T) {
if cm.specPicker != nil && cm.specPicker.IsOpen() {
t.Error("expected /spec with a description to start directly, not open the picker")
}
if currentSpecStage(cm.session) != engine.SpecStageSpecify {
t.Fatalf("expected stage SpecStageSpecify, got %v", currentSpecStage(cm.session))
if currentSpecStage(cm.session) != engine.SpecStageProposal {
t.Fatalf("expected stage SpecStageProposal, got %v", currentSpecStage(cm.session))
}
}
50 changes: 39 additions & 11 deletions internal/engine/permission_session_methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package engine

import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/GrayCodeAI/hawk/internal/spec"
Expand All @@ -17,9 +19,7 @@ func specConfigForPrompt() string {

// specStageSystemPrompt is appended to the system prompt (ephemerally) while
// a spec workflow is active and not yet approved for implementation. It
// steers the model through discovery → Specify → Plan → Tasks → approval,
// mirroring the old Plan Mode's research-then-approve shape but with real,
// persisted documents at each stage.
// steers the model through the full spec-driven workflow.
const specStageSystemPrompt = "\n\n## Spec Stage (workflow gate)\n" +
"You are working through a spec-driven workflow. Research is unrestricted, but write/execute tools are blocked until you complete the workflow. " +
"\n\n### Workflow\n" +
Expand All @@ -31,19 +31,47 @@ const specStageSystemPrompt = "\n\n## Spec Stage (workflow gate)\n" +
"Use the `AskUser` tool for questions — you can ask one at a time or batch them. " +
"There is no limit on questions — ask what you need. " +
"If the user says 'you decide' or gives you freedom, make reasonable choices based on the codebase context.\n" +
"2. **Specify**: Call `Specify` with your full understanding to write spec.md. " +
"Use `[NEEDS CLARIFICATION: ...]` markers in the spec for any remaining unknowns (max 3 unresolved at a time).\n" +
"3. **Plan**: Call `Plan` with your technical approach to write plan.md.\n" +
"4. **Tasks**: Call `Tasks` with a breakdown to write tasks.md.\n" +
"5. **Approve**: Call `ApproveImplementation` to ask the user to approve moving to implementation. " +
"2. **Proposal**: Call `Proposal` to write proposal.md — establish WHY this change is needed (problem, goals, scope, success criteria).\n" +
"3. **Constitution**: Call `Constitution` with action='init' to create the project constitution if none exists. " +
"The constitution defines non-negotiable rules that guide all subsequent decisions.\n" +
"4. **Specify** + **Design** (parallel): After Proposal and Constitution, call both `Specify` (requirements — WHAT the system does) and `Design` (technical approach — HOW). These can be done in either order or concurrently.\n" +
"5. **Plan**: Call `Plan` with your implementation plan to write plan.md. Requires both Specify and Design to be complete. " +
"Plan must document phase gate compliance (Simplicity, Anti-Abstraction, Integration-First).\n" +
"6. **Tasks**: Call `Tasks` with a breakdown to write tasks.md.\n" +
"7. **Approve**: Call `ApproveImplementation` to ask the user to approve moving to implementation. " +
"Only after they approve will Write/Edit/Bash be permitted.\n" +
"\n### Quality checks\n" +
"- Spec should focus on WHAT and WHY, not HOW (no implementation details).\n" +
"- Proposal should be concise (1-2 pages) focusing on WHY.\n" +
"- Spec should focus on WHAT (requirements, scenarios), not HOW.\n" +
"- Design should focus on HOW (architecture, decisions, trade-offs).\n" +
"- Requirements should use EARS notation (The system shall... / WHEN...THEN...).\n" +
"- Each requirement gets a REQ-XXX.Y.Z identifier for traceability.\n" +
"- Requirements should be testable, unambiguous, with measurable success criteria.\n" +
"- Edge cases, scope boundaries, and assumptions should be documented.\n" +
"- Tasks must use `- [ ]` checkbox format.\n" +
"- Tasks must use `- [ ]` checkbox format and reference REQ IDs.\n" +
"- Use [NEEDS CLARIFICATION: question] markers instead of guessing (max 3 at a time).\n" +
"\nUse `SpecConfig` tool to check user's language/framework/methodology/architecture preferences. " +
"Use `SpecList` to see existing specs. Use `SpecEdit` to refine artifacts mid-workflow."
"Use `SpecList` to see existing specs. Use `SpecEdit` to refine artifacts mid-workflow. " +
"Use `Constitution` tool to create/update project governing principles."

func constitutionForPrompt(slug string) string {
if slug == "" {
return ""
}
cwd, err := os.Getwd()
if err != nil {
return ""
}
path := filepath.Join(cwd, ".hawk", "specs", slug, "constitution.md")
data, err := os.ReadFile(path)
if err != nil {
return ""
}
return "\n\n## Project Constitution (active)\n" +
"The following constitution governs all decisions in this spec workflow. " +
"Every artifact you create must comply with these principles.\n\n" +
string(data)
}

func (s *Session) SetMaxTurns(turns int) error {
if turns < 0 {
Expand Down
Loading
Loading