🐱 Medium-horizon agent planning MCP server
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add plan granularity guidance to prevent over-decomposition

- Add "PLAN SCOPE" section to bootstrap prompt with examples
- Update invoke-inner.ps1 with condensed granularity rules
- Reduce notekeeper scenario from 6 plans to 2 chunky plans
- Add testing field to test fixtures and mocks
- Fix outdated assertion in queue-pull test

+105 -116
+37 -7
src/prompts/bootstrap.ts
··· 43 43 44 44 6. **START WORK**: Pull first plan with 9plan_queue_pull 45 45 46 + ## PLAN SCOPE - AVOID OVER-DECOMPOSITION 47 + 48 + Plans should be at the FEATURE level, not the function level. Each plan should represent meaningful, user-visible progress. 49 + 50 + ✅ GOOD plan scope: 51 + - "Implement CLI commands for notes (add, list, search, delete)" - groups related functionality 52 + - "Build authentication system with JWT and middleware" - complete feature 53 + - "Create storage layer with JSON persistence" - coherent module 54 + 55 + ❌ TOO GRANULAR - avoid these: 56 + - "Implement add command" then "Implement list command" - should be ONE plan 57 + - "Write tests for X" as separate plan - testing is part of EVERY plan, not its own 58 + - "Commit changes" or "Final commit" - NEVER a plan! Commits happen when completing plans 59 + - "Run the build" - this is verification, not a plan goal 60 + 61 + Rule of thumb: If you're creating more than 3-5 plans for a straightforward feature, you're probably over-decomposing. A simple CLI app needs 1-2 plans, not 6-7. 62 + 46 63 ## PLAN QUALITY CHECKLIST 47 64 48 65 Before adding a plan, verify: 49 66 □ Goal is specific and measurable (not vague) 67 + □ Goal represents a FEATURE, not a single function 50 68 □ Approach has concrete, actionable steps 51 69 □ Testing has SPECIFIC verification commands (REQUIRED - see below) 52 70 □ Success criteria are observable (you'll know when done) ··· 84 102 85 103 ## EXAMPLE DECOMPOSITION 86 104 105 + ### Good Example: CLI Notes App 106 + 107 + Starting task: "Build a CLI note-taking app with add, list, search, delete" 108 + 109 + Plans (just 2!): 110 + 1. **Storage & Types** - Note interface + JSON persistence (loadNotes, saveNotes) 111 + 2. **CLI Commands & Entry Point** - All commands (add, list, search, delete) + argument routing 112 + 113 + NOT 6 separate plans for each command! 114 + 115 + ### Good Example: Blog App with API Integration 116 + 87 117 Starting task: "Build Ghost-powered blog app" 88 118 89 - Plans to add (in order they should execute): 90 - 1. Ghost API Client - Create authenticated client 91 - 2. Cache System - Add response caching 92 - 3. Display Layer - Build UI components 119 + Plans to add (3 feature-level plans): 120 + 1. **Ghost API Integration** - Authenticated client + response caching 121 + 2. **Display Layer** - UI components for posts, pages, navigation 122 + 3. **Routing & Assembly** - Wire up routes, error handling, final integration 93 123 94 124 Since we want execution order [1, 2, 3], add plans at back: 95 - - 9plan_queue_add(Ghost API Client, position: back) → [1] 96 - - 9plan_queue_add(Cache System, position: back) → [1, 2] 97 - - 9plan_queue_add(Display Layer, position: back) → [1, 2, 3] 125 + - 9plan_queue_add(Ghost API Integration, position: back) → [1] 126 + - 9plan_queue_add(Display Layer, position: back) → [1, 2] 127 + - 9plan_queue_add(Routing & Assembly, position: back) → [1, 2, 3] 98 128 99 129 Now ready to pull!`; 100 130
+5
tests/fixtures/plans.ts
··· 13 13 approach: 14 14 "1. Set up test environment\n2. Run assertions\n3. Verify expected behavior", 15 15 successCriteria: "All assertions pass without errors", 16 + testing: "Run `npm test` and verify all assertions pass", 16 17 inputs: "", 17 18 outputs: "Test results and verification report", 18 19 }; ··· 25 26 goal: "Implement feature X with authentication", 26 27 approach: "1. Import auth module\n2. Implement feature\n3. Test integration", 27 28 successCriteria: "Feature works with authentication", 29 + testing: "Run integration tests with `npm run test:integration`", 28 30 inputs: "- auth_client module: from Authentication work", 29 31 outputs: "- feature_x implementation: authenticated feature", 30 32 }; ··· 37 39 goal: "Minimal test goal", 38 40 approach: "Minimal approach", 39 41 successCriteria: "Done", 42 + testing: "Verify output matches expected", 40 43 }; 41 44 42 45 /** ··· 49 52 outputs: "- result: test output", 50 53 approach: "Run the tests", 51 54 successCriteria: "Tests pass", 55 + testing: "Run `npm test` and check output", 52 56 notes: "", 53 57 }; 54 58 ··· 62 66 outputs: "- deliverable: output", 63 67 approach: "Step by step execution", 64 68 successCriteria: "All steps complete", 69 + testing: "Verify each checkpoint completes successfully", 65 70 notes: 66 71 "[2024-01-15 10:00] Started work\n[2024-01-15 11:00] Checkpoint 1 done", 67 72 };
+1
tests/helpers/mocks.ts
··· 137 137 outputs: null, 138 138 approach: null, 139 139 successCriteria: null, 140 + testing: null, 140 141 notes: null, 141 142 outcome: null, 142 143 createdAt: "2024-01-15T10:00:00Z",
+13
tests/integration/decomposition.test.ts
··· 45 45 goal: "Implement full authentication flow", 46 46 approach: "Break down into login, registration, and password reset", 47 47 successCriteria: "All auth features working with tests", 48 + testing: "Run auth test suite with `npm run test:auth`", 48 49 }); 49 50 50 51 // Step 2: Pull parent ··· 58 59 goal: "Implement password reset", 59 60 approach: "Build email verification and reset form", 60 61 successCriteria: "Password reset emails sent and processed", 62 + testing: "Test password reset flow end-to-end", 61 63 outputs: "password_reset_module", 62 64 }, 63 65 "front", ··· 69 71 goal: "Implement user registration", 70 72 approach: "Build registration form with validation", 71 73 successCriteria: "Users can register new accounts", 74 + testing: "Test registration with valid and invalid inputs", 72 75 outputs: "registration_module", 73 76 }, 74 77 "front", ··· 80 83 goal: "Implement login functionality", 81 84 approach: "Build login form and session management", 82 85 successCriteria: "Users can log in and out", 86 + testing: "Test login flow with valid credentials", 83 87 outputs: "login_module", 84 88 }, 85 89 "front", ··· 135 139 goal: "Complete tricky implementation", 136 140 approach: "Try different approaches", 137 141 successCriteria: "Working implementation", 142 + testing: "Verify implementation works correctly", 138 143 }); 139 144 140 145 // First attempt - blocked ··· 168 173 goal: "Build API client", 169 174 approach: "HTTP wrapper with retry logic", 170 175 successCriteria: "Client handles all endpoints", 176 + testing: "Test all API endpoints respond correctly", 171 177 outputs: "api_client module", 172 178 }); 173 179 store.pullPlan(); ··· 178 184 goal: "Build data validator", 179 185 approach: "Zod schemas for all models", 180 186 successCriteria: "All data validated at runtime", 187 + testing: "Test validation with valid and invalid data", 181 188 outputs: "validator module", 182 189 }); 183 190 store.pullPlan(); ··· 202 209 goal: "Existing plan 1", 203 210 approach: "app", 204 211 successCriteria: "done", 212 + testing: "verify", 205 213 }); 206 214 store.addPlan({ 207 215 context: "ctx", 208 216 goal: "Existing plan 2", 209 217 approach: "app", 210 218 successCriteria: "done", 219 + testing: "verify", 211 220 }); 212 221 213 222 // Pull first and decompose ··· 220 229 goal: "Child A", 221 230 approach: "app", 222 231 successCriteria: "done", 232 + testing: "verify", 223 233 }, 224 234 "front", 225 235 ); ··· 229 239 goal: "Child B", 230 240 approach: "app", 231 241 successCriteria: "done", 242 + testing: "verify", 232 243 }, 233 244 "front", 234 245 ); ··· 250 261 goal: "Main task", 251 262 approach: "Identify blockers and resolve", 252 263 successCriteria: "Task complete", 264 + testing: "Verify task completes successfully", 253 265 }); 254 266 255 267 store.pullPlan(); ··· 261 273 goal: "Resolve blocker", 262 274 approach: "Fix the dependency", 263 275 successCriteria: "Blocker resolved", 276 + testing: "Verify blocker is resolved", 264 277 }, 265 278 "front", 266 279 );
+4
tests/integration/history-search.test.ts
··· 90 90 goal: plan.goal, 91 91 approach: plan.approach, 92 92 successCriteria: plan.successCriteria, 93 + testing: "Run tests and verify functionality", 93 94 outputs: plan.outputs, 94 95 }); 95 96 store.pullPlan(); ··· 165 166 goal: "Build widget generator", 166 167 approach: "Use bazinator pattern", 167 168 successCriteria: "Widgets generated correctly", 169 + testing: "Test widget generation", 168 170 outputs: "quuxinator_module", 169 171 }); 170 172 store.pullPlan(); ··· 202 204 goal: "Implement unicorn sparkle feature", 203 205 approach: "Glitter everywhere", 204 206 successCriteria: "Maximum sparkle", 207 + testing: "Test sparkle output", 205 208 }); 206 209 store.pullPlan(); 207 210 store.discardPlan("Changed requirements"); ··· 218 221 goal: "Implement dragon feature", 219 222 approach: "Fire breathing logic", 220 223 successCriteria: "Dragons work", 224 + testing: "Test fire breathing", 221 225 }); 222 226 223 227 // Should not find queued plan
+1
tests/unit/tools/queue-add.test.ts
··· 19 19 goal: "Test goal", 20 20 approach: "Test approach", 21 21 success_criteria: "Test criteria", 22 + testing: "Run tests and verify output", 22 23 }; 23 24 24 25 beforeEach(() => {
+2 -2
tests/unit/tools/queue-pull.test.ts
··· 60 60 61 61 const result = handleQueuePull(mockStore, "test-session", {}); 62 62 63 - // The response tells users to read the plan file 64 - expect(result.content[0]!.text).toContain("Read the plan file"); 63 + // The response tells users to review the plan before starting 64 + expect(result.content[0]!.text).toContain("Review for any ambiguities"); 65 65 }); 66 66 67 67 it("should handle QUEUE_EMPTY with completed count", () => {
+34 -106
validation/scenarios/notekeeper-full.yaml
··· 41 41 - id: bootstrap_plans 42 42 description: "Add initial plans for all components" 43 43 prompt: | 44 - Now let's plan out the work. We need: 45 - 1. A Note type definition and storage module (read/write JSON) 46 - 2. An "add" command to create new notes 47 - 3. A "list" command to show all notes 48 - 4. A "search" command to find notes by keyword 49 - 5. A "delete" command to remove notes 50 - 6. A CLI entry point that routes to the right command 44 + Now let's plan out the work. Think at the FEATURE level, not individual functions. 51 45 52 - Add these as plans in the order they should be executed. 53 - The storage module should be first since other commands depend on it. 46 + We need two main pieces: 47 + 1. Storage layer - Note type definition and JSON file persistence (loadNotes, saveNotes, generateId) 48 + 2. CLI layer - All commands (add, list, search, delete) plus the entry point that routes to them 49 + 50 + Add these as 2 plans in the order they should be executed. 51 + The storage module should be first since the CLI depends on it. 54 52 55 53 expected: 56 54 tools_called: 57 55 - 9plan_queue_add 58 56 state: 59 - queue_length: 6 # 6 plans: storage, add, list, search, delete, CLI 57 + queue_length: 2 # 2 plans: storage layer, CLI layer 60 58 files: 61 59 - pattern: "~/.9plan/sessions/*/plans/*.txt" 62 - count: 6 60 + count: 2 63 61 64 62 validation: 65 63 admin_validate: true 66 64 67 - # Step 3: Execute storage module 65 + # Step 3: Execute storage layer 68 66 - id: execute_storage 69 - description: "Pull and implement the storage module" 67 + description: "Pull and implement the storage layer" 70 68 prompt: | 71 - Pull the first plan and implement the storage module. 69 + Pull the first plan and implement the storage layer. 72 70 Create the files in validation/sandbox/notekeeper/src/. 73 71 74 72 The Note type should have: id (string), content (string), createdAt (string). ··· 79 77 - 9plan_queue_pull 80 78 - 9plan_plan_complete 81 79 state: 82 - queue_length: 5 80 + queue_length: 1 83 81 completed_count: 1 84 82 files: 85 83 - pattern: "validation/sandbox/notekeeper/src/types.ts" ··· 97 95 validation: 98 96 admin_validate: true 99 97 100 - # Step 4: Execute add command 101 - - id: execute_add 102 - description: "Pull and implement the add command" 98 + # Step 4: Execute CLI layer (all commands + entry point) 99 + - id: execute_cli 100 + description: "Pull and implement all CLI commands and entry point" 103 101 prompt: | 104 - Pull the next plan and implement the add command. 105 - It should take a note content string and save a new note. 102 + Pull the final plan and implement the complete CLI layer: 103 + - add command: takes content string, saves new note 104 + - list command: displays all notes with IDs and content 105 + - search command: finds notes by keyword 106 + - delete command: removes note by ID 107 + - Entry point: parses args and routes to the right command 108 + 109 + Usage: notekeeper <command> [args] 110 + Commands: add <content>, list, search <keyword>, delete <id> 111 + 112 + Create these in validation/sandbox/notekeeper/src/commands/ and src/index.ts. 106 113 107 114 expected: 108 115 tools_called: 109 116 - 9plan_queue_pull 110 117 - 9plan_plan_complete 111 118 state: 112 - queue_length: 4 119 + queue_length: 0 113 120 completed_count: 2 121 + queue_empty: true 114 122 files: 115 123 - pattern: "validation/sandbox/notekeeper/src/commands/add.ts" 116 124 exists: true 117 - 118 - validation: 119 - admin_validate: true 120 - 121 - # Step 5: Execute list command 122 - - id: execute_list 123 - description: "Pull and implement the list command" 124 - prompt: | 125 - Pull the next plan and implement the list command. 126 - It should display all notes with their IDs and content. 127 - 128 - expected: 129 - tools_called: 130 - - 9plan_queue_pull 131 - - 9plan_plan_complete 132 - state: 133 - queue_length: 3 134 - completed_count: 3 135 - files: 136 125 - pattern: "validation/sandbox/notekeeper/src/commands/list.ts" 137 126 exists: true 138 - 139 - validation: 140 - admin_validate: true 141 - 142 - # Step 6: Execute search command 143 - - id: execute_search 144 - description: "Pull and implement the search command" 145 - prompt: | 146 - Pull the next plan and implement the search command. 147 - It should search note content for a keyword and show matching notes. 148 - 149 - expected: 150 - tools_called: 151 - - 9plan_queue_pull 152 - - 9plan_plan_complete 153 - state: 154 - queue_length: 2 155 - completed_count: 4 156 - files: 157 127 - pattern: "validation/sandbox/notekeeper/src/commands/search.ts" 158 128 exists: true 159 - 160 - validation: 161 - admin_validate: true 162 - 163 - # Step 7: Execute delete command 164 - - id: execute_delete 165 - description: "Pull and implement the delete command" 166 - prompt: | 167 - Pull the next plan and implement the delete command. 168 - It should delete a note by ID. 169 - 170 - expected: 171 - tools_called: 172 - - 9plan_queue_pull 173 - - 9plan_plan_complete 174 - state: 175 - queue_length: 1 176 - completed_count: 5 177 - files: 178 129 - pattern: "validation/sandbox/notekeeper/src/commands/delete.ts" 179 130 exists: true 180 - 181 - validation: 182 - admin_validate: true 183 - 184 - # Step 8: Execute CLI entry point 185 - - id: execute_cli 186 - description: "Pull and implement the CLI entry point" 187 - prompt: | 188 - Pull the final plan and implement the CLI entry point. 189 - It should parse command line arguments and route to the right command. 190 - 191 - Usage: notekeeper <command> [args] 192 - Commands: add <content>, list, search <keyword>, delete <id> 193 - 194 - expected: 195 - tools_called: 196 - - 9plan_queue_pull 197 - - 9plan_plan_complete 198 - state: 199 - queue_length: 0 200 - completed_count: 6 201 - queue_empty: true 202 - files: 203 131 - pattern: "validation/sandbox/notekeeper/src/index.ts" 204 132 exists: true 205 133 ··· 232 160 233 161 # Verify history search works 234 162 history_searches: 235 - - query: "storage module loadNotes" 163 + - query: "storage loadNotes saveNotes" 236 164 min_results: 1 237 - description: "Should find the storage module plan" 165 + description: "Should find the storage layer plan" 238 166 239 - - query: "add command" 167 + - query: "CLI commands add list search delete" 240 168 min_results: 1 241 - description: "Should find the add command plan" 169 + description: "Should find the CLI layer plan" 242 170 243 171 # Final state check 244 172 final_state: 245 173 queue_empty: true 246 174 all_plans_completed: true 247 - completed_count: 6 175 + completed_count: 2
+8 -1
validation/scripts/invoke-inner.ps1
··· 64 64 3. ENQUEUE: Add plans with 9plan_queue_add (use "back" position, add in dependency order) 65 65 4. EXECUTE: Pull plans with 9plan_queue_pull, implement, then complete with 9plan_plan_complete 66 66 67 + PLAN SCOPE - AVOID OVER-DECOMPOSITION: 68 + Plans should be at the FEATURE level, not the function level. 69 + - GOOD: "Implement CLI commands for notes (add, list, search, delete)" - groups related functionality 70 + - BAD: "Implement add command" then "Implement list command" - too granular, should be ONE plan 71 + - NEVER A PLAN: "Write tests for X", "Commit changes", "Run the build" - these are part of completing plans, not separate plans 72 + Rule of thumb: A simple CLI app needs 1-2 plans, not 6-7. 73 + 67 74 PLAN STRUCTURE - Each plan MUST have: 68 75 - Context: Where this fits in the overall task 69 - - Goal: Specific, measurable objective 76 + - Goal: Specific, measurable objective (at FEATURE level, not function level) 70 77 - Inputs: What this plan needs from other plans (by description) 71 78 - Outputs: What this plan produces that others may need 72 79 - Approach: Concrete, actionable steps