๐Ÿฑ Medium-horizon agent planning MCP server
0
fork

Configure Feed

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

Commit design docs

karashiiro fb290d1f

+4891
+8
README.md
··· 1 + \# 9plan 2 + 3 + 4 + 5 + Design: `docs/design.md` 6 + 7 + 8 +
+373
docs/adrs.md
··· 1 + # 9plan Architecture Decision Records 2 + 3 + This document captures significant architectural decisions, the context that led to them, and the alternatives that were considered and rejected. 4 + 5 + --- 6 + 7 + ## ADR-001: Single Queue vs Priority Tiers 8 + 9 + **Status**: Accepted 10 + 11 + **Context**: 12 + Work queues commonly use priority systemsโ€”urgent/normal/low, or numeric priorities 1-10. This allows important work to surface ahead of less critical tasks. However, agent-based execution has different failure modes than human execution. 13 + 14 + **Decision**: 15 + Use a single ordered queue with front/back positioning semantics instead of priority tiers. 16 + 17 + **Rationale**: 18 + - **Failure mode prevention**: Priority systems create a failure mode where agents complete high-priority work and abandon low-priority items indefinitely. A single queue that must empty enforces completion. 19 + - **Cognitive simplicity**: Numeric priorities require agents to calibrate against existing items ("is this a 3 or a 4?"). Front/back maps directly to how work is discovered: "must do X before continuing" vs "should do Y eventually." 20 + - **Task completion guarantee**: Sessions represent bounded tasks. The queue emptying naturally signals task completion. 21 + 22 + **Alternatives Rejected**: 23 + - **Numeric priorities (1-10)**: Requires arbitrary calibration, doesn't prevent low-priority abandonment 24 + - **Priority tiers (high/medium/low)**: Same problems as numeric, just coarser 25 + - **Backlog + active queue**: Backlog items tend to be forgotten; defeats the "must eventually empty" property 26 + - **No queue (freeform notes)**: Loses ordering semantics and completion tracking 27 + 28 + **Consequences**: 29 + - โœ… All work eventually completes if agent follows protocol 30 + - โœ… Simple mental model for positioning decisions 31 + - โš ๏ธ Can't express "do this eventually but not before other eventual work" 32 + - โš ๏ธ Reordering requires manual queue manipulation 33 + 34 + --- 35 + 36 + ## ADR-002: Three-Word Session Names vs UUIDs 37 + 38 + **Status**: Accepted 39 + 40 + **Context**: 41 + Sessions need unique identifiers for resumption across conversation boundaries. Users must be able to communicate these identifiers, and agents must be able to remember them after context compaction. 42 + 43 + **Decision**: 44 + Use randomly-generated three-word identifiers (adjective-adjective-noun pattern, e.g., `copper-velvet-morning`) instead of UUIDs or sequential IDs. 45 + 46 + **Rationale**: 47 + - **Human memorability**: "Resume copper-velvet-morning" is natural speech; "resume a7f3c2e9-4d8b-11ef-8a2c-0242ac120003" is not 48 + - **Context survival**: Distinctive word combinations are more likely to survive context compaction than arbitrary character sequences 49 + - **Verbal communication**: Users can say session names aloud, write them in notes, share in chat 50 + - **Sufficient uniqueness**: With 1000+ word dictionaries, collision probability is extremely low for reasonable session counts 51 + 52 + **Alternatives Rejected**: 53 + - **UUIDs**: Guaranteed unique but completely unmemorable, hard to communicate 54 + - **Sequential IDs (session-1, session-2)**: Memorable but collide across different machines/users, don't convey identity 55 + - **User-provided names**: Requires user input, may conflict, doesn't guarantee uniqueness 56 + - **Hash of task description**: Unmemorable, changes if description edited 57 + 58 + **Consequences**: 59 + - โœ… Users can remember and communicate session names easily 60 + - โœ… More likely to survive in agent context 61 + - โš ๏ธ Theoretical collision risk with very large session counts 62 + - โš ๏ธ Requires collision checking on generation 63 + 64 + --- 65 + 66 + ## ADR-003: Hybrid Storage (SQLite + Flat Files) vs Pure Database 67 + 68 + **Status**: Accepted 69 + 70 + **Context**: 71 + Plan data needs to be stored persistently. Options range from pure flat files to pure database storage. Agents need to read and write plan content during execution. 72 + 73 + **Decision**: 74 + Use hybrid storage: SQLite for structural state (queue order, active plan, history), flat files for plan content that agents read/write. 75 + 76 + **Rationale**: 77 + - **Structural integrity**: SQLite protects queue order and active plan state from corruption. Agents can't accidentally break the queue by editing files. 78 + - **Agent accessibility**: Flat files can be read/written using standard filesystem tools that agents already have. No special database tooling needed. 79 + - **Checkpoint friendliness**: Agents update Notes sections during execution for progress checkpointing. File writes are simple, immediate, and don't require transaction semantics. 80 + - **Human debuggability**: Plan files can be inspected directly when debugging. Database requires query tools. 81 + - **Failure isolation**: File corruption doesn't break queue structure. Database corruption doesn't lose in-progress plan content. 82 + 83 + **Alternatives Rejected**: 84 + - **Pure SQLite**: Requires agents to use database tools for all plan access; less natural for checkpointing 85 + - **Pure flat files**: Queue order and state tracking in files is fragile; concurrent access (even across conversations) could corrupt 86 + - **JSON files**: More structure than plaintext, but adds parsing complexity for agents; plan format is simple enough for plaintext 87 + - **External database (Postgres, etc.)**: Requires additional infrastructure; overkill for single-agent, local execution 88 + 89 + **Consequences**: 90 + - โœ… Queue structure protected from accidental corruption 91 + - โœ… Agents use familiar file read/write operations 92 + - โœ… Easy debugging via file inspection 93 + - โš ๏ธ Potential for database and files to get out of sync (missing plan files) 94 + - โš ๏ธ Two storage systems to understand and maintain 95 + 96 + --- 97 + 98 + ## ADR-004: Semantic Dependency Resolution vs ID-Based References 99 + 100 + **Status**: Accepted 101 + 102 + **Context**: 103 + Plans depend on outputs from other plans. These dependencies could be tracked by plan ID ("plan k7f3m produces X") or by semantic description ("the Ghost API client module"). 104 + 105 + **Decision**: 106 + Use semantic dependency resolution. Plans declare inputs by description, and resolution happens via full-text search of completed plan outcomes. 107 + 108 + **Rationale**: 109 + - **Decomposition compatibility**: When a plan is decomposed, its children produce the actual outputs. ID-based references to the parent would find the aggregator, not the real outputs. 110 + - **Resilience to re-planning**: If a plan is discarded and re-planned, the new plan has a different ID. Semantic references still find the right output. 111 + - **Natural language**: Agents describe dependencies in terms of what they need ("authentication client"), not system artifacts ("output of k7f3m"). 112 + - **Search flexibility**: Descriptions can match partial terms, synonyms, related concepts. 113 + 114 + **Alternatives Rejected**: 115 + - **Plan ID references**: Brittle to decomposition and re-planning; requires knowing IDs at reference time 116 + - **Explicit dependency graph**: Adds complexity; must be maintained as plans change; doesn't handle dynamic discovery 117 + - **No dependency tracking**: Forces agents to re-derive what's available; risks duplicate work or missed dependencies 118 + 119 + **Consequences**: 120 + - โœ… Dependencies survive decomposition naturally 121 + - โœ… Natural language interface for agents 122 + - โš ๏ธ Search quality depends on outcome descriptions being specific and searchable 123 + - โš ๏ธ Ambiguous descriptions may return wrong results 124 + 125 + --- 126 + 127 + ## ADR-005: Deferred Parents vs Immediate Parent Completion on Decomposition 128 + 129 + **Status**: Accepted 130 + 131 + **Context**: 132 + When a plan is decomposed into subplans, what happens to the parent? It could be completed immediately (marking decomposition as "done"), discarded, or deferred for later aggregation. 133 + 134 + **Decision**: 135 + Defer parent plans to the back of the queue. Parents complete after children, performing aggregation and verification. 136 + 137 + **Rationale**: 138 + - **Hierarchy preservation**: Parents represent milestones and decision points. Keeping them allows "did Phase 1 succeed?" assessment. 139 + - **Failure handling**: If children fail, the parent aggregation step is the natural place to decide whether to re-plan. 140 + - **Combined documentation**: Parent completion documents the aggregate outcome of all children, making later search easier. 141 + - **Verification point**: Parent aggregation can verify children actually integrated correctly. 142 + 143 + **Alternatives Rejected**: 144 + - **Immediate completion**: Loses hierarchy; no aggregation point; can't verify children succeeded together 145 + - **Discard**: Loses all parent context; no milestone tracking; if children fail, no record of what was attempted 146 + - **Keep active (no defer)**: Blocks pulling new work; doesn't match the "child work happens next" semantics 147 + 148 + **Consequences**: 149 + - โœ… Hierarchy preserved for milestone tracking 150 + - โœ… Natural aggregation and verification point 151 + - โœ… Better documentation of combined outcomes 152 + - โš ๏ธ Parent completion is delayed; dependent plans can't wait for it (must use child outcomes) 153 + - โš ๏ธ Parents accumulate at back of queue 154 + 155 + --- 156 + 157 + ## ADR-006: Plan Files Deleted on Completion vs Archived 158 + 159 + **Status**: Accepted 160 + 161 + **Context**: 162 + When a plan completes, its content is indexed into the database. The file in `plans/` could be kept (archived), moved to a different directory, or deleted. 163 + 164 + **Decision**: 165 + Delete plan files on completion. Content lives only in the database after completion. 166 + 167 + **Rationale**: 168 + - **Single source of truth**: Completed plan content is in exactly one place (database). No risk of file and database diverging. 169 + - **Clean filesystem**: `plans/` directory shows only active and queued work. Easy to see what's pending. 170 + - **Search efficiency**: All completed content is indexed in FTS5. File search would be slower and less consistent. 171 + - **Space efficiency**: No accumulation of completed plan files over long sessions. 172 + 173 + **Alternatives Rejected**: 174 + - **Archive to separate directory**: Two sources of truth; filesystem accumulates; search must check both places 175 + - **Keep in place with status marker**: Clutters directory; requires parsing files to determine status 176 + - **Never store in database**: Loses FTS5 search capability; must search files directly 177 + 178 + **Consequences**: 179 + - โœ… Clean separation: files = pending, database = completed 180 + - โœ… Efficient search via FTS5 181 + - โš ๏ธ Can't inspect completed plan files directly (must query database) 182 + - โš ๏ธ If database corrupts, completed plan content is lost 183 + 184 + --- 185 + 186 + ## ADR-007: Front/Back Position Hints vs Insert-At-Index 187 + 188 + **Status**: Accepted 189 + 190 + **Context**: 191 + When adding plans to the queue, callers need to specify where to insert. Options range from simple (front/back) to precise (specific index or relative to another plan). 192 + 193 + **Decision**: 194 + Use simple front/back position hints. No index-based or relative positioning. 195 + 196 + **Rationale**: 197 + - **Matches discovery semantics**: Work is discovered as "must do before continuing" (front) or "should do eventually" (back). These are the natural categories. 198 + - **No index management**: Agents don't need to track or reason about queue indices, which change as plans are pulled and completed. 199 + - **Simpler mental model**: Two options instead of arbitrary positioning decisions. 200 + - **Reversible**: If initial positioning is wrong, defer can move a plan from active back to queue. 201 + 202 + **Alternatives Rejected**: 203 + - **Index-based insertion**: Requires knowing current queue state; indices shift as queue changes; error-prone 204 + - **Insert-after-plan-X**: Requires tracking plan IDs in queue; complex when referenced plan completes 205 + - **Priority numbers**: See ADR-001โ€”same problems with calibration and abandonment 206 + - **Multiple queues**: Complicates "must empty" semantics; where does work go? 207 + 208 + **Consequences**: 209 + - โœ… Simple, intuitive positioning 210 + - โœ… Matches how work is discovered 211 + - โš ๏ธ Front-insertion reversal problem: adding A, B, C at front gives [C, B, A] 212 + - โš ๏ธ Can't express "insert between these two specific plans" 213 + 214 + --- 215 + 216 + ## ADR-008: Session-Scoped vs Global History 217 + 218 + **Status**: Accepted 219 + 220 + **Context**: 221 + Completed plans are indexed for search. The search scope could be per-session, per-project (if projects existed), or global across all sessions. 222 + 223 + **Decision**: 224 + History is session-scoped. Each session has its own history that cannot be searched from other sessions. 225 + 226 + **Rationale**: 227 + - **Task isolation**: Sessions represent distinct tasks. Outputs from unrelated tasks shouldn't pollute search results. 228 + - **Naming collision avoidance**: "auth_client" in one session might be completely different from "auth_client" in another. 229 + - **Predictable scope**: Agents always know search results come from current task context. 230 + - **Storage locality**: Each session is self-contained in its directory; no shared state. 231 + 232 + **Alternatives Rejected**: 233 + - **Global history**: Cross-session pollution; naming collisions; harder to reason about sources 234 + - **Project-level history**: Requires project concept; sessions within project would share, but project boundaries unclear 235 + - **Explicit cross-session references**: Complex; requires knowing other session names; breaks isolation 236 + 237 + **Consequences**: 238 + - โœ… Clean task isolation 239 + - โœ… Predictable search scope 240 + - โš ๏ธ Can't reuse outputs across sessions (must recreate or manually copy) 241 + - โš ๏ธ Related sessions can't easily share context 242 + 243 + --- 244 + 245 + ## ADR-009: Single Agent Per Session vs Concurrent Access 246 + 247 + **Status**: Accepted 248 + 249 + **Context**: 250 + Could multiple agents (or agent instances) work on the same session simultaneously? This would enable parallelism but adds complexity. 251 + 252 + **Decision**: 253 + Single agent per session. No concurrent access support. 254 + 255 + **Rationale**: 256 + - **Simplicity**: No locking, race conditions, or conflict resolution needed. 257 + - **Active plan model**: "One plan active at a time" assumes single executor. Multiple agents would need multiple active plans or coordination. 258 + - **Queue consistency**: Front/back insertion is inherently sequential. Concurrent adds would require ordering decisions. 259 + - **Target use case**: Long-running tasks with single agent, possibly across multiple conversations, not parallel execution. 260 + 261 + **Alternatives Rejected**: 262 + - **Concurrent agents with locking**: Complex; must handle conflicts; unclear semantics for "active plan" 263 + - **Work stealing**: Interesting for parallelism but much more complex; changes execution model fundamentally 264 + - **Shared queue, separate execution contexts**: Still needs synchronization; partial solution 265 + 266 + **Consequences**: 267 + - โœ… Simple, predictable execution model 268 + - โœ… No synchronization complexity 269 + - โš ๏ธ Can't parallelize work within a session 270 + - โš ๏ธ Multiple conversations touching same session can corrupt state 271 + 272 + --- 273 + 274 + ## ADR-010: Alphanumeric Plan IDs vs Numeric Sequence 275 + 276 + **Status**: Accepted 277 + 278 + **Context**: 279 + Plans need identifiers for reference. Options include sequential numbers (1, 2, 3), UUIDs, or short alphanumeric hashes. 280 + 281 + **Decision**: 282 + Use short alphanumeric hashes (5+ characters, e.g., `k7f3m`) generated with collision checking. 283 + 284 + **Rationale**: 285 + - **Distinct from session names**: Three-word names for sessions, alphanumeric for plansโ€”no confusion about what you're referencing. 286 + - **Short but unique**: 5 characters with 36-character alphabet provides ~60 million combinations. Sufficient for session-scoped uniqueness. 287 + - **No semantic meaning**: Unlike sequential IDs, doesn't imply order or creation time. Plans can be reordered without ID confusion. 288 + - **Copy-paste friendly**: No spaces or special characters. 289 + 290 + **Alternatives Rejected**: 291 + - **Sequential numbers**: Implies ordering; confusing when plans complete out of order; reused IDs after discard? 292 + - **UUIDs**: Too long; hard to reference in Notes; overkill for session-scoped uniqueness 293 + - **Descriptive slugs**: Would need to be unique; long; change if goal changes 294 + - **Three-word names (like sessions)**: Confusingโ€”is "copper-velvet-morning" a session or plan? 295 + 296 + **Consequences**: 297 + - โœ… Clear distinction from session names 298 + - โœ… Short, easy to reference 299 + - โœ… No ordering implications 300 + - โš ๏ธ Not human-memorable (but that's what goal descriptions are for) 301 + - โš ๏ธ Requires collision checking 302 + 303 + --- 304 + 305 + ## ADR-011: Notes as Append-Only Scratchpad vs Structured Fields 306 + 307 + **Status**: Accepted 308 + 309 + **Context**: 310 + Plans need a place for accumulated context during executionโ€”progress checkpoints, deferral reasons, decomposition records. This could be a freeform scratchpad or structured fields. 311 + 312 + **Decision**: 313 + Notes is a freeform, append-only scratchpad. Content is accumulated, not replaced. 314 + 315 + **Rationale**: 316 + - **Flexibility**: Different plans need different kinds of notes. Structured fields would constrain or require many optional fields. 317 + - **History preservation**: Appending (especially deferral reasons) preserves the full history of what happened to a plan. 318 + - **Agent simplicity**: Agents append text; no need to parse and update specific fields. 319 + - **Recovery value**: Full history of notes provides breadcrumbs for recovery after context compaction. 320 + 321 + **Alternatives Rejected**: 322 + - **Structured status fields**: Rigid; doesn't capture freeform progress notes; harder to extend 323 + - **Replace-on-write**: Loses history; previous deferrals or checkpoints overwritten 324 + - **Separate notes file per update**: Filesystem clutter; harder to read full context 325 + 326 + **Consequences**: 327 + - โœ… Flexible, extensible 328 + - โœ… Full history preserved 329 + - โœ… Simple append operations 330 + - โš ๏ธ Notes can get long over many deferrals 331 + - โš ๏ธ No structured parsing (agent must read and interpret) 332 + 333 + --- 334 + 335 + ## ADR-012: Plan Validation vs Freeform Content 336 + 337 + **Status**: Accepted 338 + 339 + **Context**: 340 + Plan content could be completely freeform (any text), schema-validated (required fields), or something in between. 341 + 342 + **Decision**: 343 + Plans are validated against the format template. Required fields (Context, Goal, Approach, Success Criteria) must be present and non-empty. Optional fields (Inputs, Outputs) may be empty. 344 + 345 + **Rationale**: 346 + - **Quality enforcement**: Ensures plans are well-formed enough to be executable. Catches missing context or vague goals. 347 + - **Agent guidance**: Validation errors tell agents what's missing, prompting better plan creation. 348 + - **Consistency**: All plans follow the same structure, making them easier to read and search. 349 + - **Self-contained execution**: Required fields ensure plans have enough information for cold-start execution. 350 + 351 + **Alternatives Rejected**: 352 + - **No validation**: Agents could create incomplete plans; execution would fail later 353 + - **Strict schema (all fields required)**: Inputs/Outputs aren't always applicable; forces meaningless placeholders 354 + - **AI-based quality assessment**: Complex; subjective; slower 355 + 356 + **Consequences**: 357 + - โœ… Minimum quality guaranteed 358 + - โœ… Consistent plan structure 359 + - โœ… Clear error messages guide completion 360 + - โš ๏ธ Validation might reject valid-but-unusual plans 361 + - โš ๏ธ Template format is somewhat rigid 362 + 363 + --- 364 + 365 + ## Future Considerations 366 + 367 + These are not decisions but potential future changes noted during design: 368 + 369 + - **Batch add operation**: Would solve front-insertion reversal problem. Could accept ordered list of plans. 370 + - **HTTP transport**: Currently stdio only. HTTP would enable web-based clients and remote access. 371 + - **Plan templates**: Pre-defined patterns for common plan types (research, implementation, testing). 372 + - **Cross-session search**: Opt-in ability to search other sessions' history. Requires careful scoping. 373 + - **Plan versioning**: Track changes to plans over time, enable rollback. Currently Notes provides partial history but not full versioning.
+584
docs/api-examples.md
··· 1 + # 9plan API Examples 2 + 3 + Concrete request and response examples for every tool. 4 + 5 + --- 6 + 7 + ## Session Tools 8 + 9 + ### 9plan_session_create 10 + 11 + Creates a new session for a task. 12 + 13 + **Request:** 14 + ```json 15 + { 16 + "tool": "9plan_session_create", 17 + "arguments": { 18 + "task_description": "Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching" 19 + } 20 + } 21 + ``` 22 + 23 + **Response (Success):** 24 + ``` 25 + [Session: amber-quiet-river] 26 + 27 + Session created: amber-quiet-river 28 + Directory: /Users/dev/.9plan/sessions/amber-quiet-river 29 + 30 + The session is ready. Use 9plan_queue_add to add plans, then 9plan_queue_pull to begin work. 31 + ``` 32 + 33 + **Response (Error - rare, usually filesystem issues):** 34 + ``` 35 + Error: Failed to create session directory: Permission denied 36 + 37 + Check that 9plan has write access to /Users/dev/.9plan/sessions/ 38 + ``` 39 + 40 + --- 41 + 42 + ### 9plan_session_resume 43 + 44 + Loads an existing session. 45 + 46 + **Request:** 47 + ```json 48 + { 49 + "tool": "9plan_session_resume", 50 + "arguments": { 51 + "session_name": "amber-quiet-river" 52 + } 53 + } 54 + ``` 55 + 56 + **Response (Success - with active plan):** 57 + ``` 58 + [Session: amber-quiet-river] 59 + 60 + Session resumed: amber-quiet-river 61 + Directory: /Users/dev/.9plan/sessions/amber-quiet-river 62 + 63 + Task: Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching 64 + 65 + Active plan: k7f3m 66 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt 67 + Goal: Create authenticated Ghost API client module 68 + 69 + Queue (2 plans): 70 + 1. m2x9p - Implement response caching system 71 + 2. p4r2k - Build post display layer with pagination 72 + 73 + Completed: 0 plans 74 + 75 + An active plan exists. Read the plan file to continue, or use 9plan_plan_complete/defer/discard to close it out. 76 + ``` 77 + 78 + **Response (Success - no active plan):** 79 + ``` 80 + [Session: amber-quiet-river] 81 + 82 + Session resumed: amber-quiet-river 83 + Directory: /Users/dev/.9plan/sessions/amber-quiet-river 84 + 85 + Task: Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching 86 + 87 + Active plan: (none) 88 + 89 + Queue (3 plans): 90 + 1. k7f3m - Create authenticated Ghost API client module 91 + 2. m2x9p - Implement response caching system 92 + 3. p4r2k - Build post display layer with pagination 93 + 94 + Completed: 0 plans 95 + 96 + Use 9plan_queue_pull to get the next plan. 97 + ``` 98 + 99 + **Response (Success - empty queue):** 100 + ``` 101 + [Session: amber-quiet-river] 102 + 103 + Session resumed: amber-quiet-river 104 + Directory: /Users/dev/.9plan/sessions/amber-quiet-river 105 + 106 + Task: Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching 107 + 108 + Active plan: (none) 109 + 110 + Queue: (empty) 111 + 112 + Completed: 6 plans 113 + 114 + Task complete! Use 9plan_history_search to review completed work. 115 + ``` 116 + 117 + **Response (Error - session not found):** 118 + ``` 119 + Error: Session not found: amber-quiet-river 120 + 121 + Check the session name and try again. Session names are case-sensitive. 122 + Available sessions can be found in: /Users/dev/.9plan/sessions/ 123 + ``` 124 + 125 + --- 126 + 127 + ## Queue Tools 128 + 129 + ### 9plan_queue_add 130 + 131 + Adds a new plan to the queue. 132 + 133 + **Request (full plan):** 134 + ```json 135 + { 136 + "tool": "9plan_queue_add", 137 + "arguments": { 138 + "context": "Building Ghost blog app. This is the foundational component that all other parts depend on. Must complete first.", 139 + "goal": "Create authenticated Ghost API client module", 140 + "inputs": "- Ghost API credentials: provided by user or environment", 141 + "outputs": "- ghost_client module: authenticated client instance with methods for getPosts(), getPost(id), with proper error handling", 142 + "approach": "1. Set up Ghost Content API authentication using API key\n2. Create client wrapper with typed methods\n3. Implement error handling for auth failures and API errors\n4. Export configured client instance", 143 + "success_criteria": "- Client successfully authenticates with Ghost API\n- getPosts() returns post array\n- getPost(id) returns single post\n- Auth errors throw descriptive exceptions", 144 + "position": "back" 145 + } 146 + } 147 + ``` 148 + 149 + **Response (Success):** 150 + ``` 151 + [Session: amber-quiet-river] 152 + 153 + Plan added: k7f3m 154 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt 155 + Queue position: 1 156 + 157 + The plan file has been created. Use 9plan_queue_pull when ready to execute. 158 + ``` 159 + 160 + **Request (minimal - no inputs/outputs):** 161 + ```json 162 + { 163 + "tool": "9plan_queue_add", 164 + "arguments": { 165 + "context": "Building blog app. Need a basic UI component.", 166 + "goal": "Create PostCard component for displaying individual posts", 167 + "approach": "1. Create React component\n2. Accept post data as props\n3. Display title, excerpt, date\n4. Style with Tailwind", 168 + "success_criteria": "- Component renders post data\n- Styling looks good\n- No TypeScript errors", 169 + "position": "front" 170 + } 171 + } 172 + ``` 173 + 174 + **Response (Success - front position):** 175 + ``` 176 + [Session: amber-quiet-river] 177 + 178 + Plan added: r3t7w 179 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/r3t7w.txt 180 + Queue position: 1 (front) 181 + 182 + The plan file has been created. Use 9plan_queue_pull when ready to execute. 183 + ``` 184 + 185 + **Response (Error - missing required field):** 186 + ``` 187 + Error: Plan validation failed 188 + 189 + Missing required fields: 190 + - approach: Must describe how to accomplish the goal 191 + 192 + Please provide all required fields: context, goal, approach, success_criteria 193 + ``` 194 + 195 + **Response (Error - empty field):** 196 + ``` 197 + Error: Plan validation failed 198 + 199 + Empty required fields: 200 + - goal: Cannot be empty 201 + 202 + Please provide meaningful content for all required fields. 203 + ``` 204 + 205 + --- 206 + 207 + ### 9plan_queue_pull 208 + 209 + Gets the next plan to work on. 210 + 211 + **Request:** 212 + ```json 213 + { 214 + "tool": "9plan_queue_pull", 215 + "arguments": {} 216 + } 217 + ``` 218 + 219 + **Response (Success):** 220 + ``` 221 + [Session: amber-quiet-river] 222 + 223 + Active plan: k7f3m 224 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt 225 + 226 + Read the plan file for full context. Review for any ambiguities before starting execution. 227 + 228 + If the plan has inputs from other plans, use 9plan_history_search to find their outputs. 229 + If the plan's Notes indicate it was previously decomposed, use 9plan_history_get to retrieve child outcomes. 230 + ``` 231 + 232 + **Response (Error - plan already active):** 233 + ``` 234 + [Session: amber-quiet-river] 235 + 236 + Error: Cannot pull - a plan is already active 237 + 238 + Active plan: k7f3m 239 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt 240 + 241 + Complete, defer, or discard the active plan before pulling another. 242 + ``` 243 + 244 + **Response (Queue empty):** 245 + ``` 246 + [Session: amber-quiet-river] 247 + 248 + Queue is empty. Task complete! 249 + 250 + Completed plans: 6 251 + 252 + Use 9plan_history_search to review what was accomplished. 253 + ``` 254 + 255 + --- 256 + 257 + ## Plan Lifecycle Tools 258 + 259 + ### 9plan_plan_complete 260 + 261 + Marks the active plan as done. 262 + 263 + **Request:** 264 + ```json 265 + { 266 + "tool": "9plan_plan_complete", 267 + "arguments": { 268 + "outcome": "Created ghost_client module in src/lib/ghost-client.ts.\n\nExports:\n- GhostClient class with constructor(apiKey, apiUrl)\n- getPosts(options?: {limit, page}) โ†’ Post[]\n- getPost(id: string) โ†’ Post\n\nError handling:\n- GhostAuthError for 401/403 responses\n- GhostNotFoundError for 404 responses\n- GhostAPIError for other failures\n\nTested against live Ghost instance, all methods working." 269 + } 270 + } 271 + ``` 272 + 273 + **Response (Success):** 274 + ``` 275 + [Session: amber-quiet-river] 276 + 277 + Plan k7f3m completed and indexed. 278 + 279 + The plan's content and outcome are now searchable via 9plan_history_search. 280 + 281 + Queue status: 2 plans remaining 282 + Next: Use 9plan_queue_pull to continue work. 283 + ``` 284 + 285 + **Response (Error - no active plan):** 286 + ``` 287 + [Session: amber-quiet-river] 288 + 289 + Error: No active plan to complete 290 + 291 + Use 9plan_queue_pull to get a plan first. 292 + ``` 293 + 294 + --- 295 + 296 + ### 9plan_plan_defer 297 + 298 + Returns the active plan to the queue. 299 + 300 + **Request (blocked, retry soon):** 301 + ```json 302 + { 303 + "tool": "9plan_plan_defer", 304 + "arguments": { 305 + "reason": "Blocked: Need Ghost API credentials from user before proceeding", 306 + "position": "front" 307 + } 308 + } 309 + ``` 310 + 311 + **Response (Success - front):** 312 + ``` 313 + [Session: amber-quiet-river] 314 + 315 + Plan k7f3m deferred to front of queue. 316 + Reason recorded in Notes. 317 + 318 + Queue status: 3 plans 319 + Position: 1 (will be pulled next) 320 + 321 + Resolve the blocking issue, then use 9plan_queue_pull to retry. 322 + ``` 323 + 324 + **Request (decomposition, aggregate later):** 325 + ```json 326 + { 327 + "tool": "9plan_plan_defer", 328 + "arguments": { 329 + "reason": "Decomposed into subplans: x5k9m (PostList UI), r3t7w (pagination state), j8n2q (integration). Will aggregate child outcomes when re-pulled.", 330 + "position": "back" 331 + } 332 + } 333 + ``` 334 + 335 + **Response (Success - back):** 336 + ``` 337 + [Session: amber-quiet-river] 338 + 339 + Plan p4r2k deferred to back of queue. 340 + Reason recorded in Notes. 341 + 342 + Queue status: 4 plans 343 + Position: 4 (will execute after current queue) 344 + 345 + Use 9plan_queue_pull to continue with queued work. 346 + ``` 347 + 348 + **Response (Error - no active plan):** 349 + ``` 350 + [Session: amber-quiet-river] 351 + 352 + Error: No active plan to defer 353 + 354 + Use 9plan_queue_pull to get a plan first. 355 + ``` 356 + 357 + --- 358 + 359 + ### 9plan_plan_discard 360 + 361 + Abandons the active plan. 362 + 363 + **Request:** 364 + ```json 365 + { 366 + "tool": "9plan_plan_discard", 367 + "arguments": { 368 + "reason": "Plan superseded by external changes - the Ghost API was replaced with a different CMS. This plan is no longer relevant." 369 + } 370 + } 371 + ``` 372 + 373 + **Response (Success):** 374 + ``` 375 + [Session: amber-quiet-river] 376 + 377 + Plan k7f3m discarded. 378 + Reason: Plan superseded by external changes - the Ghost API was replaced with a different CMS. This plan is no longer relevant. 379 + 380 + The plan has been removed and will not appear in history. 381 + 382 + Queue status: 2 plans remaining 383 + Next: Use 9plan_queue_pull to continue work. 384 + ``` 385 + 386 + **Response (Error - no active plan):** 387 + ``` 388 + [Session: amber-quiet-river] 389 + 390 + Error: No active plan to discard 391 + 392 + Use 9plan_queue_pull to get a plan first. 393 + ``` 394 + 395 + --- 396 + 397 + ## History Tools 398 + 399 + ### 9plan_history_search 400 + 401 + Searches completed plans for outputs. 402 + 403 + **Request:** 404 + ```json 405 + { 406 + "tool": "9plan_history_search", 407 + "arguments": { 408 + "query": "ghost client API authentication" 409 + } 410 + } 411 + ``` 412 + 413 + **Response (Success - matches found):** 414 + ``` 415 + [Session: amber-quiet-river] 416 + 417 + Found 1 matching plan: 418 + 419 + 1. Plan k7f3m 420 + Goal: Create authenticated Ghost API client module 421 + Outcome: Created ghost_client module in src/lib/ghost-client.ts. 422 + Exports GhostClient class with getPosts(), getPost() methods... 423 + 424 + Use 9plan_history_get with the plan ID for full details. 425 + ``` 426 + 427 + **Response (Success - multiple matches):** 428 + ``` 429 + [Session: amber-quiet-river] 430 + 431 + Found 3 matching plans: 432 + 433 + 1. Plan k7f3m 434 + Goal: Create authenticated Ghost API client module 435 + Outcome: Created ghost_client module in src/lib/ghost-client.ts... 436 + 437 + 2. Plan m2x9p 438 + Goal: Implement response caching system 439 + Outcome: Created caching layer wrapping ghost_client... 440 + 441 + 3. Plan j8n2q 442 + Goal: Integrate data fetching with UI components 443 + Outcome: Connected cached ghost_client to PostList component... 444 + 445 + Use 9plan_history_get with the plan ID for full details. 446 + ``` 447 + 448 + **Response (No matches):** 449 + ``` 450 + [Session: amber-quiet-river] 451 + 452 + No matching plans found for: "payment processing stripe" 453 + 454 + Try different search terms, or check if the relevant work has been completed yet. 455 + ``` 456 + 457 + --- 458 + 459 + ### 9plan_history_get 460 + 461 + Retrieves a specific completed plan. 462 + 463 + **Request:** 464 + ```json 465 + { 466 + "tool": "9plan_history_get", 467 + "arguments": { 468 + "plan_id": "k7f3m" 469 + } 470 + } 471 + ``` 472 + 473 + **Response (Success):** 474 + ``` 475 + [Session: amber-quiet-river] 476 + 477 + Plan k7f3m (completed) 478 + 479 + # Context 480 + Building Ghost blog app. This is the foundational component that all other parts depend on. Must complete first. 481 + 482 + # Goal 483 + Create authenticated Ghost API client module 484 + 485 + # Inputs 486 + - Ghost API credentials: provided by user or environment 487 + 488 + # Outputs 489 + - ghost_client module: authenticated client instance with methods for getPosts(), getPost(id), with proper error handling 490 + 491 + # Approach 492 + 1. Set up Ghost Content API authentication using API key 493 + 2. Create client wrapper with typed methods 494 + 3. Implement error handling for auth failures and API errors 495 + 4. Export configured client instance 496 + 497 + # Success Criteria 498 + - Client successfully authenticates with Ghost API 499 + - getPosts() returns post array 500 + - getPost(id) returns single post 501 + - Auth errors throw descriptive exceptions 502 + 503 + # Notes 504 + [2024-01-15 14:30] Progress checkpoint: 505 + - Created ghost-client.ts with GhostClient class 506 + - Implemented constructor with API key validation 507 + - getPosts() method complete, tested against live API 508 + - Remaining: getPost(id), error handling improvements 509 + 510 + # Outcome 511 + Created ghost_client module in src/lib/ghost-client.ts. 512 + 513 + Exports: 514 + - GhostClient class with constructor(apiKey, apiUrl) 515 + - getPosts(options?: {limit, page}) โ†’ Post[] 516 + - getPost(id: string) โ†’ Post 517 + 518 + Error handling: 519 + - GhostAuthError for 401/403 responses 520 + - GhostNotFoundError for 404 responses 521 + - GhostAPIError for other failures 522 + 523 + Tested against live Ghost instance, all methods working. 524 + ``` 525 + 526 + **Response (Error - not found):** 527 + ``` 528 + [Session: amber-quiet-river] 529 + 530 + Error: Plan not found: xyz99 531 + 532 + The plan may not have been completed yet (check queue), or may have been discarded. 533 + Use 9plan_session_resume to see current queue, or 9plan_history_search to find related plans. 534 + ``` 535 + 536 + --- 537 + 538 + ## Common Patterns 539 + 540 + ### Resolving Dependencies 541 + 542 + When a plan has inputs, resolve them before execution: 543 + 544 + ``` 545 + 1. Read plan file, find inputs: 546 + "- cached_client: from Cache System work" 547 + 548 + 2. Search history: 549 + 9plan_history_search("cached_client cache system") 550 + 551 + 3. Get details if needed: 552 + 9plan_history_get("m2x9p") 553 + 554 + 4. Use the information from outcome to proceed 555 + ``` 556 + 557 + ### Aggregating After Decomposition 558 + 559 + When a parent plan is re-pulled: 560 + 561 + ``` 562 + 1. Read plan Notes, find child IDs: 563 + "Decomposed into subplans: x5k9m, r3t7w, j8n2q" 564 + 565 + 2. Get each child's outcome: 566 + 9plan_history_get("x5k9m") 567 + 9plan_history_get("r3t7w") 568 + 9plan_history_get("j8n2q") 569 + 570 + 3. Verify children succeeded, aggregate results 571 + 572 + 4. Complete parent: 573 + 9plan_plan_complete(outcome: "Aggregated from children...") 574 + ``` 575 + 576 + ### Adding Subplans in Correct Order 577 + 578 + To achieve execution order [A, B, C], add in reverse: 579 + 580 + ``` 581 + 9plan_queue_add(C, position: "front") โ†’ Queue: [C, ...] 582 + 9plan_queue_add(B, position: "front") โ†’ Queue: [B, C, ...] 583 + 9plan_queue_add(A, position: "front") โ†’ Queue: [A, B, C, ...] 584 + ```
+490
docs/data-flows.md
··· 1 + # 9plan Data Flow Diagrams 2 + 3 + Visual documentation of how data moves through the system during key operations. 4 + 5 + --- 6 + 7 + ## Storage Overview 8 + 9 + ``` 10 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 11 + โ”‚ Session Directory โ”‚ 12 + โ”‚ ~/.9plan/sessions/{session-name}/ โ”‚ 13 + โ”‚ โ”‚ 14 + โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 15 + โ”‚ โ”‚ session.db โ”‚ โ”‚ plans/ โ”‚ โ”‚ 16 + โ”‚ โ”‚ (SQLite) โ”‚ โ”‚ (Flat Files) โ”‚ โ”‚ 17 + โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 18 + โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ 19 + โ”‚ โ”‚ โ”‚ metadata โ”‚ โ”‚ โ”‚ โ”‚ {plan-id}.txt โ”‚ โ”‚ โ”‚ 20 + โ”‚ โ”‚ โ”‚ - task_description โ”‚ โ”‚ โ”‚ โ”‚ - Context โ”‚ โ”‚ โ”‚ 21 + โ”‚ โ”‚ โ”‚ - created_at โ”‚ โ”‚ โ”‚ โ”‚ - Goal โ”‚ โ”‚ โ”‚ 22 + โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ - Inputs โ”‚ โ”‚ โ”‚ 23 + โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ - Outputs โ”‚ โ”‚ โ”‚ 24 + โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ - Approach โ”‚ โ”‚ โ”‚ 25 + โ”‚ โ”‚ โ”‚ queue โ”‚ โ”‚ โ”‚ โ”‚ - Success Criteria โ”‚ โ”‚ โ”‚ 26 + โ”‚ โ”‚ โ”‚ - ordered plan IDs โ”‚ โ”‚ โ”‚ โ”‚ - Notes โ”‚ โ”‚ โ”‚ 27 + โ”‚ โ”‚ โ”‚ - active plan ref โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ 28 + โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ 29 + โ”‚ โ”‚ โ”‚ โ”‚ (Only queued + active โ”‚ โ”‚ 30 + โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ plans have files) โ”‚ โ”‚ 31 + โ”‚ โ”‚ โ”‚ history (FTS5) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 32 + โ”‚ โ”‚ โ”‚ - completed plans โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 33 + โ”‚ โ”‚ โ”‚ - full content โ”‚ โ”‚ โ”‚ 34 + โ”‚ โ”‚ โ”‚ - outcomes โ”‚ โ”‚ โ”‚ 35 + โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ 36 + โ”‚ โ”‚ โ”‚ โ”‚ 37 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 38 + โ”‚ โ”‚ 39 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 40 + ``` 41 + 42 + --- 43 + 44 + ## Session Creation Flow 45 + 46 + ``` 47 + 9plan_session_create(task_description) 48 + โ”‚ 49 + โ–ผ 50 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 51 + โ”‚ Generate Session โ”‚ 52 + โ”‚ Name (3 words) โ”‚ 53 + โ”‚ e.g. "amber-quiet- โ”‚ 54 + โ”‚ river" โ”‚ 55 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 56 + โ”‚ 57 + โ–ผ 58 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 59 + โ”‚ Check for โ”‚ 60 + โ”‚ Collision โ”‚โ—„โ”€โ”€โ”€ Regenerate if exists 61 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 62 + โ”‚ (unique) 63 + โ–ผ 64 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 65 + โ”‚ Create Directory โ”‚ 66 + โ”‚ sessions/amber- โ”‚ 67 + โ”‚ quiet-river/ โ”‚ 68 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 69 + โ”‚ 70 + โ–ผ 71 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 72 + โ”‚ Initialize SQLite โ”‚ 73 + โ”‚ session.db โ”‚ 74 + โ”‚ - Create tables โ”‚ 75 + โ”‚ - Insert metadata โ”‚ 76 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 77 + โ”‚ 78 + โ–ผ 79 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 80 + โ”‚ Create plans/ โ”‚ 81 + โ”‚ subdirectory โ”‚ 82 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 83 + โ”‚ 84 + โ–ผ 85 + Return: session_name, 86 + session_path 87 + ``` 88 + 89 + --- 90 + 91 + ## Plan Addition Flow 92 + 93 + ``` 94 + 9plan_queue_add(context, goal, approach, success_criteria, inputs?, outputs?, position?) 95 + โ”‚ 96 + โ–ผ 97 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 98 + โ”‚ Validate Plan โ”‚ 99 + โ”‚ (required fields) โ”‚โ”€โ”€โ”€โ”€ Error if invalid 100 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 101 + โ”‚ (valid) 102 + โ–ผ 103 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 104 + โ”‚ Generate Plan ID โ”‚ 105 + โ”‚ (alphanumeric, โ”‚ 106 + โ”‚ 5+ chars) โ”‚ 107 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 108 + โ”‚ 109 + โ–ผ 110 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 111 + โ”‚ Check for โ”‚ 112 + โ”‚ Collision โ”‚โ—„โ”€โ”€โ”€ Regenerate if exists 113 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 114 + โ”‚ (unique) 115 + โ–ผ 116 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 117 + โ”‚ PARALLEL โ”‚ 118 + โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 119 + โ”‚ โ”‚ Write Plan File โ”‚ โ”‚ Insert DB Record โ”‚ โ”‚ 120 + โ”‚ โ”‚ plans/{id}.txt โ”‚ โ”‚ - plan metadata โ”‚ โ”‚ 121 + โ”‚ โ”‚ - Full content โ”‚ โ”‚ - queue position โ”‚ โ”‚ 122 + โ”‚ โ”‚ - Empty Notes โ”‚ โ”‚ - status: queued โ”‚ โ”‚ 123 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 124 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 125 + โ”‚ 126 + โ–ผ 127 + Return: plan_id, 128 + plan_path, 129 + queue_position 130 + ``` 131 + 132 + **Position Logic:** 133 + 134 + ``` 135 + position = "front" position = "back" (default) 136 + 137 + Queue: [B, C, D] Queue: [B, C, D] 138 + Add A at front Add A at back 139 + Queue: [A, B, C, D] Queue: [B, C, D, A] 140 + ``` 141 + 142 + --- 143 + 144 + ## Plan Pull Flow 145 + 146 + ``` 147 + 9plan_queue_pull() 148 + โ”‚ 149 + โ–ผ 150 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 151 + โ”‚ Check Active Plan โ”‚ 152 + โ”‚ Exists? โ”‚ 153 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 154 + โ”‚ 155 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 156 + โ–ผ โ–ผ 157 + (yes) (no) 158 + โ”‚ โ”‚ 159 + โ–ผ โ–ผ 160 + ERROR: โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 161 + "Plan โ”‚ Check Queue โ”‚ 162 + already โ”‚ Empty? โ”‚ 163 + active" โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 164 + โ”‚ 165 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 166 + โ–ผ โ–ผ 167 + (yes) (no) 168 + โ”‚ โ”‚ 169 + โ–ผ โ–ผ 170 + "Queue empty, โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 171 + task โ”‚ Get Front Plan ID โ”‚ 172 + complete" โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 173 + โ”‚ 174 + โ–ผ 175 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 176 + โ”‚ Update DB โ”‚ 177 + โ”‚ - Remove from queueโ”‚ 178 + โ”‚ - Mark as active โ”‚ 179 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 180 + โ”‚ 181 + โ–ผ 182 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 183 + โ”‚ Verify Plan File โ”‚ 184 + โ”‚ Exists โ”‚ 185 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 186 + โ”‚ 187 + โ–ผ 188 + Return: plan_id, 189 + plan_path 190 + 191 + Agent then reads plans/{id}.txt via filesystem tools 192 + ``` 193 + 194 + --- 195 + 196 + ## Plan Completion Flow 197 + 198 + ``` 199 + 9plan_plan_complete(outcome) 200 + โ”‚ 201 + โ–ผ 202 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 203 + โ”‚ Get Active Plan โ”‚ 204 + โ”‚ from DB โ”‚โ”€โ”€โ”€โ”€ Error if none active 205 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 206 + โ”‚ 207 + โ–ผ 208 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 209 + โ”‚ Read Plan File โ”‚ 210 + โ”‚ plans/{id}.txt โ”‚ 211 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 212 + โ”‚ 213 + โ–ผ 214 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 215 + โ”‚ Index into โ”‚ 216 + โ”‚ History (FTS5) โ”‚ 217 + โ”‚ - Full plan contentโ”‚ 218 + โ”‚ - Outcome โ”‚ 219 + โ”‚ - Timestamp โ”‚ 220 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 221 + โ”‚ 222 + โ–ผ 223 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 224 + โ”‚ Delete Plan File โ”‚ 225 + โ”‚ plans/{id}.txt โ”‚ 226 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 227 + โ”‚ 228 + โ–ผ 229 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 230 + โ”‚ Clear Active Plan โ”‚ 231 + โ”‚ in DB โ”‚ 232 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 233 + โ”‚ 234 + โ–ผ 235 + Return: success, 236 + "pull next plan" 237 + ``` 238 + 239 + **Data Migration on Completion:** 240 + 241 + ``` 242 + BEFORE: AFTER: 243 + 244 + plans/k7f3m.txt โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” plans/k7f3m.txt 245 + # Context โ”‚ (DELETED) 246 + Building Ghost... โ”‚ 247 + # Goal โ”‚ session.db 248 + Create API client... โ”‚ history table: 249 + # Notes โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 250 + Progress: done auth โ”‚ โ”‚ id: k7f3m โ”‚ 251 + โ–ผ โ”‚ content: (full) โ”‚ 252 + completion โ”‚ outcome: Created โ”‚ 253 + process โ”‚ ghost_client... โ”‚ 254 + โ”‚ โ”‚ completed_at: ... โ”‚ 255 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 256 + (searchable via FTS5) 257 + ``` 258 + 259 + --- 260 + 261 + ## Plan Deferral Flow 262 + 263 + ``` 264 + 9plan_plan_defer(reason, position?) 265 + โ”‚ 266 + โ–ผ 267 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 268 + โ”‚ Get Active Plan โ”‚ 269 + โ”‚ from DB โ”‚โ”€โ”€โ”€โ”€ Error if none active 270 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 271 + โ”‚ 272 + โ–ผ 273 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 274 + โ”‚ Append to Notes โ”‚ 275 + โ”‚ in Plan File โ”‚ 276 + โ”‚ [timestamp] reason โ”‚ 277 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 278 + โ”‚ 279 + โ–ผ 280 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 281 + โ”‚ Add Back to Queue โ”‚ 282 + โ”‚ (front or back) โ”‚ 283 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 284 + โ”‚ 285 + โ–ผ 286 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 287 + โ”‚ Clear Active Plan โ”‚ 288 + โ”‚ in DB โ”‚ 289 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 290 + โ”‚ 291 + โ–ผ 292 + Return: success, 293 + new queue position 294 + ``` 295 + 296 + **State Change:** 297 + 298 + ``` 299 + BEFORE: AFTER: 300 + 301 + Active: k7f3m Active: (none) 302 + Queue: [m2x9p, p4r2k] Queue: [m2x9p, p4r2k, k7f3m] 303 + (if position=back) 304 + plans/k7f3m.txt 305 + # Notes plans/k7f3m.txt 306 + (empty) # Notes 307 + [2024-01-15 14:30] Deferred: 308 + Waiting for auth dependency 309 + ``` 310 + 311 + --- 312 + 313 + ## Decomposition Flow 314 + 315 + ``` 316 + Agent pulls plan, decides to decompose 317 + 318 + 1. Agent updates Notes in plan file: 319 + "Decomposing into: A.1, A.2, A.3" 320 + 321 + 2. Agent adds subplans (reverse order for correct execution): 322 + 323 + 9plan_queue_add(A.3) โ†’ front Queue: [A.3, B, C] 324 + 9plan_queue_add(A.2) โ†’ front Queue: [A.2, A.3, B, C] 325 + 9plan_queue_add(A.1) โ†’ front Queue: [A.1, A.2, A.3, B, C] 326 + 327 + 3. Agent defers parent: 328 + 329 + 9plan_plan_defer("Decomposed into A.1, A.2, A.3", back) 330 + 331 + Result: Queue: [A.1, A.2, A.3, B, C, A] 332 + โ”‚ 333 + โ””โ”€โ”€ Parent at back 334 + 335 + 4. Execution proceeds: 336 + - A.1 executes, completes 337 + - A.2 executes, completes 338 + - A.3 executes, completes 339 + - B executes, completes 340 + - C executes, completes 341 + - A (parent) pulled again for aggregation 342 + ``` 343 + 344 + --- 345 + 346 + ## History Search Flow 347 + 348 + ``` 349 + 9plan_history_search(query) 350 + โ”‚ 351 + โ–ผ 352 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 353 + โ”‚ FTS5 Query on โ”‚ 354 + โ”‚ history table โ”‚ 355 + โ”‚ โ”‚ 356 + โ”‚ Searches: โ”‚ 357 + โ”‚ - context โ”‚ 358 + โ”‚ - goal โ”‚ 359 + โ”‚ - inputs โ”‚ 360 + โ”‚ - outputs โ”‚ 361 + โ”‚ - approach โ”‚ 362 + โ”‚ - outcome โ”‚ 363 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 364 + โ”‚ 365 + โ–ผ 366 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 367 + โ”‚ Rank Results by โ”‚ 368 + โ”‚ Relevance โ”‚ 369 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 370 + โ”‚ 371 + โ–ผ 372 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 373 + โ”‚ Return Top N โ”‚ 374 + โ”‚ Matches โ”‚ 375 + โ”‚ โ”‚ 376 + โ”‚ For each: โ”‚ 377 + โ”‚ - plan_id โ”‚ 378 + โ”‚ - goal โ”‚ 379 + โ”‚ - outcome โ”‚ 380 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 381 + โ”‚ 382 + โ–ผ 383 + Agent uses results 384 + to resolve inputs 385 + ``` 386 + 387 + **Example Search:** 388 + 389 + ``` 390 + Query: "ghost client API authentication" 391 + 392 + History: 393 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 394 + โ”‚ k7f3m: "Create authenticated Ghost API client module" โ”‚ 395 + โ”‚ Outcome: "Created ghost_client module in src/lib/..." โ”‚ โ† MATCH 396 + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค 397 + โ”‚ m2x9p: "Implement response caching system" โ”‚ 398 + โ”‚ Outcome: "Created caching layer..." โ”‚ โ† no match 399 + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค 400 + โ”‚ x5k9m: "Create PostList UI component" โ”‚ 401 + โ”‚ Outcome: "Created PostList component..." โ”‚ โ† no match 402 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 403 + 404 + Returns: [{id: k7f3m, goal: "Create authenticated...", outcome: "..."}] 405 + ``` 406 + 407 + --- 408 + 409 + ## Session Resume Flow 410 + 411 + ``` 412 + 9plan_session_resume(session_name) 413 + โ”‚ 414 + โ–ผ 415 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 416 + โ”‚ Find Session โ”‚ 417 + โ”‚ Directory โ”‚โ”€โ”€โ”€โ”€ Error if not found 418 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 419 + โ”‚ 420 + โ–ผ 421 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 422 + โ”‚ Open session.db โ”‚ 423 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 424 + โ”‚ 425 + โ–ผ 426 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 427 + โ”‚ GATHER STATE โ”‚ 428 + โ”‚ โ”‚ 429 + โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 430 + โ”‚ โ”‚ Read metadata โ”‚ โ”‚ Read queue โ”‚ โ”‚ 431 + โ”‚ โ”‚ - task desc โ”‚ โ”‚ - ordered plan IDs โ”‚ โ”‚ 432 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ - goals for each โ”‚ โ”‚ 433 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 434 + โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 435 + โ”‚ โ”‚ Get active โ”‚ โ”‚ Count completed โ”‚ โ”‚ 436 + โ”‚ โ”‚ plan (if any) โ”‚ โ”‚ plans in history โ”‚ โ”‚ 437 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 438 + โ”‚ โ”‚ 439 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 440 + โ”‚ 441 + โ–ผ 442 + Return: session_name, 443 + session_path, 444 + task_description, 445 + queue (id + goal for each), 446 + active_plan (if any), 447 + completed_count 448 + ``` 449 + 450 + --- 451 + 452 + ## Tool Response Prefix Pattern 453 + 454 + All tool responses include the session name as a prefix: 455 + 456 + ``` 457 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 458 + โ”‚ Tool Response โ”‚ 459 + โ”‚ โ”‚ 460 + โ”‚ [Session: amber-quiet-river] โ—„โ”€โ”€ Always present โ”‚ 461 + โ”‚ โ”‚ 462 + โ”‚ Plan added: k7f3m โ”‚ 463 + โ”‚ Path: /Users/dev/.9plan/sessions/amber-quiet-river/... โ”‚ 464 + โ”‚ Queue position: 1 โ”‚ 465 + โ”‚ โ”‚ 466 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 467 + 468 + Purpose: Keep session name fresh in context for recovery 469 + after context compaction 470 + ``` 471 + 472 + --- 473 + 474 + ## File System States by Plan Status 475 + 476 + ``` 477 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 478 + โ”‚ Plan Status โ”‚ 479 + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค 480 + โ”‚ QUEUED โ”‚ ACTIVE โ”‚ COMPLETED โ”‚ DISCARDED โ”‚ 481 + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค 482 + โ”‚ DB: queue[] โ”‚ DB: active โ”‚ DB: history โ”‚ DB: (removed) โ”‚ 483 + โ”‚ File: YES โ”‚ File: YES โ”‚ File: NO โ”‚ File: NO โ”‚ 484 + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค 485 + โ”‚ plans/ โ”‚ plans/ โ”‚ (deleted) โ”‚ (deleted) โ”‚ 486 + โ”‚ {id}.txt โ”‚ {id}.txt โ”‚ โ”‚ โ”‚ 487 + โ”‚ โ”‚ โ”‚ Content in โ”‚ No record โ”‚ 488 + โ”‚ โ”‚ โ”‚ session.db โ”‚ preserved โ”‚ 489 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 490 + ```
+460
docs/design.md
··· 1 + # 9plan: Plan Queue MCP Server 2 + 3 + A session-scoped work queue for long time-horizon agent sequencing. 4 + 5 + --- 6 + 7 + ## Core Concepts 8 + 9 + ### Session 10 + 11 + A named container for a queue of plans. Sessions use randomly-generated three-word identifiers (e.g., `copper-velvet-morning`) for easy resumption across conversation boundaries. 12 + 13 + Sessions are: 14 + - **Persistent**: Survive conversation restarts 15 + - **Task-scoped**: One session = one overall task/goal 16 + - **Complete when empty**: The task is done when no plans remain 17 + 18 + ### Plan 19 + 20 + A discrete unit of work with enough detail for an agent to execute it without additional context. Each plan contains: 21 + 22 + - **Identifier**: Short alphanumeric hash (e.g., `k7f3m`), distinct from session names to avoid confusion 23 + - **Context**: High-level summary of the overall task, where this plan fits, and why it exists 24 + - **Goal**: What this plan accomplishes 25 + - **Inputs**: What this plan requires from other plans or external sources 26 + - **Outputs**: What this plan produces that other plans may consume 27 + - **Approach**: How to accomplish it (detailed, actionable) 28 + - **Success criteria**: Observable conditions that indicate completion 29 + - **Notes**: Working scratchpad for progress checkpoints, deferral reasons, etc. 30 + 31 + Plans are fully self-contained. A well-formed plan should allow an agent resuming from cold storage to execute without asking clarifying questionsโ€”even if the agent has no memory of prior plans or how this one was created. 32 + 33 + Plans may be pulled from the queue multiple times during their lifecycle: 34 + 1. **First pull**: Execute directly, or decompose into subplans and defer 35 + 2. **Subsequent pull** (if decomposed): Aggregate child outcomes from history, complete or re-plan 36 + 37 + When a plan is decomposed, its children produce the actual outputs. The parent's subsequent pull is for aggregation and verificationโ€”confirming children succeeded, documenting the combined result, and handling any failures. Other plans that depend on this branch's outputs can proceed once children complete; they don't need to wait for parent aggregation. 38 + 39 + Plans should be scoped small enough to complete within a focused session. If a plan is taking many tool calls or generating extensive output, it should be decomposed into smaller subplans. This reduces the risk of context compaction occurring mid-plan, and keeps the execution cycle frequent enough to maintain session awareness. 40 + 41 + ### Queue 42 + 43 + A single ordered list of plans. Position in the queue implies execution order. There are no separate priority tiers, backlogs, or deferred pilesโ€”just one queue that must eventually empty. 44 + 45 + Position semantics: 46 + - **Front**: Work that should happen next / before continuing current work 47 + - **Back**: Work that should happen eventually / after current work 48 + 49 + --- 50 + 51 + ## Tools 52 + 53 + All tool responses include the session name as a prefix (e.g., `[Session: amber-quiet-river]`). This keeps the session identifier fresh in recent context, improving the chance that agents retain session awareness after context compaction. 54 + 55 + The server instructions (provided when the MCP server connects) include the plan format template and session directory path. This ensures agents always have access to the expected structure and know where to find plan files. 56 + 57 + ### Session Management 58 + 59 + **`9plan_session_create`** 60 + Initializes a new session. Generates and returns the three-word identifier and session directory path. Optionally accepts an overall task description that frames the session's purpose. 61 + 62 + Returns: session name, path to session directory (containing `plans/` subdirectory). 63 + 64 + **`9plan_session_resume`** 65 + Loads an existing session by its three-word identifier. 66 + 67 + Returns: 68 + - Session name and directory path 69 + - Task description 70 + - Queue contents (plan IDs in order, with goals) 71 + - Currently-active plan (ID and file path, if any) 72 + - Count of completed plans 73 + 74 + Use this to understand session state after interruption or to review what work remains. 75 + 76 + ### Queue Management 77 + 78 + **`9plan_queue_add`** 79 + Adds a new plan to the queue. 80 + 81 + Parameters: 82 + - Context (required): high-level summary of overall task, where this plan fits, and why it exists 83 + - Goal (required) 84 + - Approach (required) 85 + - Success criteria (required) 86 + - Inputs (optional): what this plan requires from other plans or external sources 87 + - Outputs (optional): what this plan produces that other plans may consume 88 + - Position hint: `front` or `back` (default: `back`) 89 + 90 + Creates the plan file in `plans/` and adds to queue. Validates the plan against the format template before adding. If validation fails, returns an error describing what's missing or malformed. 91 + 92 + Use `front` when: discovered work that must complete before current work can proceed. 93 + Use `back` when: discovered work that should happen eventually but not immediately. 94 + 95 + Returns: plan ID and file path. 96 + 97 + **`9plan_queue_pull`** 98 + Removes the front plan from the queue and marks it as active. This is the primary way to get work. 99 + 100 + Returns: 101 + - Plan ID and file path (agent reads file for full content) 102 + - Guidance prompting the agent to review for ambiguities 103 + 104 + If the queue is empty, returns a message indicating the task is complete. 105 + 106 + Returns an error if a plan is already activeโ€”the agent must `9plan_plan_complete`, `9plan_plan_defer`, or `9plan_plan_discard` the current plan before pulling another. 107 + 108 + ### Plan Lifecycle 109 + 110 + **`9plan_plan_defer`** 111 + Returns the currently-active plan to the queue without completing it. 112 + 113 + Parameters: 114 + - Reason for deferral (required) 115 + - Position hint: `front` or `back` (default: `front`) 116 + 117 + The server appends the deferral reason to the plan's Notes section in the file, preserving a record of why the plan was deferred. 118 + 119 + Use cases: 120 + - **Blocked**: Can't proceed, will retry later (typically `front`) 121 + - **Decomposition**: Breaking into subplans, will aggregate results later (use `back`; see Decomposing a Plan workflow) 122 + - **Reordering**: New information suggests other work should happen first 123 + 124 + Returns an error if no plan is currently active. 125 + 126 + **`9plan_plan_complete`** 127 + Marks the currently-active plan as done. 128 + 129 + Parameters: 130 + - Outcome (required): summary of what was accomplished, including any outputs produced (artifacts, APIs, data structures) that other plans may consume 131 + 132 + The outcome should clearly describe outputs so that dependent plans can find them via `9plan_history_search`. Use specific names, file paths, and interface descriptions that match what was declared in the plan's Outputs field. 133 + 134 + On completion: 135 + 1. Plan content and outcome are indexed into the database 136 + 2. Plan file is deleted from `plans/` 137 + 3. Returns a message prompting the agent to pull the next plan 138 + 139 + Returns an error if no plan is currently active. 140 + 141 + **`9plan_plan_discard`** 142 + Abandons the currently-active plan without completing it. 143 + 144 + Parameters: 145 + - Reason (required): why this plan is being discarded 146 + 147 + The plan file is deleted and the plan is not recorded in history. Use when a plan becomes truly obsoleteโ€”superseded by external changes, discovered to be unnecessary, or fundamentally misconceived. Do not use for decomposition; use `9plan_plan_defer` instead so the parent can aggregate child outcomes. 148 + 149 + Returns an error if no plan is currently active. 150 + 151 + ### History 152 + 153 + **`9plan_history_search`** 154 + Searches completed plans for outputs matching a query. 155 + 156 + Parameters: 157 + - Query (required): description of the output being sought (e.g., "ghost_client module", "user authentication API") 158 + 159 + Returns matching completed plans with their IDs, goals, and outcomes, ordered by relevance. Use this to resolve inputsโ€”find what other plans produced that this plan needs. 160 + 161 + **`9plan_history_get`** 162 + Retrieves a specific completed plan by ID. 163 + 164 + Parameters: 165 + - Plan ID (required) 166 + 167 + Returns the full completed plan including context, goal, inputs, outputs, approach, success criteria, notes, and outcome. Use this when you know the specific plan ID (e.g., from Notes indicating which child plans were created during decomposition). 168 + 169 + --- 170 + 171 + ## Prompts 172 + 173 + ### `bootstrap` 174 + 175 + Guides initial session setup and planning decomposition. User-initiated. 176 + 177 + Workflow: 178 + 1. Clarify the overall task/goal through questions 179 + 2. Create a new session with the task description 180 + 3. Decompose into initial set of plans with clear success criteria 181 + 4. Identify cross-branch dependencies and capture in inputs/outputs fields 182 + 5. Enqueue all plans in logical order (respecting dependencies) 183 + 6. Return control to the agent with the session ready to pull from 184 + 185 + This prompt should emphasize creating plans that are detailed enough to execute without contextโ€”someone reading the plan in a fresh conversation should know exactly what to do. The inputs/outputs fields are especially important during bootstrap, when all branches are visible in the same context. 186 + 187 + --- 188 + 189 + ## File Structure 190 + 191 + Sessions use a hybrid storage model: SQLite for structural state, flat files for plan content. 192 + 193 + ``` 194 + 9plan/ 195 + โ””โ”€โ”€ sessions/ 196 + โ””โ”€โ”€ {session-name}/ 197 + โ”œโ”€โ”€ session.db # SQLite: queue order, active plan, metadata, history 198 + โ””โ”€โ”€ plans/ 199 + โ”œโ”€โ”€ {plan-id}.txt # Plan content files (queued and active only) 200 + โ””โ”€โ”€ ... 201 + ``` 202 + 203 + **SQLite database** (`session.db`) stores: 204 + - Session metadata (task description) 205 + - Queue order (list of plan IDs) 206 + - Active plan reference 207 + - Plan metadata (ID, status, timestamps) 208 + - Completed plan content (indexed on completion) 209 + 210 + **Plan files** (`plans/`) contain: 211 + - Full plan content for queued and active plans 212 + - Deleted when plan completes (content moves to DB) 213 + - Agent reads/writes these directly via filesystem tools 214 + 215 + This split protects structural integrity (agents can't accidentally corrupt queue order) while allowing efficient filesystem access for plan content and checkpointing. 216 + 217 + Each plan file is human-readable plaintext. The filename (without extension) is the plan ID: 218 + 219 + ``` 220 + # Context 221 + {high-level summary of overall task, where this plan fits, and why it exists} 222 + 223 + # Goal 224 + {what this accomplishes} 225 + 226 + # Inputs 227 + {what this plan requires from other plans or external sources, if any} 228 + {format: "- {description}: {source plan or external source}"} 229 + 230 + # Outputs 231 + {what this plan produces that other plans may consume, if any} 232 + {format: "- {description}: {details or specification}"} 233 + 234 + # Approach 235 + {how to accomplish it} 236 + 237 + # Success Criteria 238 + {observable completion conditions} 239 + 240 + # Notes 241 + {accumulated context, deferral reasons, progress checkpoints, etc.} 242 + ``` 243 + 244 + The Inputs and Outputs sections make cross-branch dependencies explicit. During decomposition, when sibling branches are visible in the same context, agents should capture what each branch needs (inputs) and provides (outputs). This information propagates to child plans, enabling agents to search history for required inputs even when the producing branch is no longer in context. 245 + 246 + **Dependency resolution is semantic**: Inputs reference plans by description (e.g., "ghost_client module from Ghost API Client"), not by plan ID. When resolving an input, agents use `9plan_history_search` to find completed plan outcomes that describe the required output. This allows dependencies to be satisfied by child plansโ€”if "Ghost API Client" was decomposed, its children's outcomes will contain the actual outputs. 247 + 248 + The Notes section is a working scratchpad. Agents update it directly via filesystem tools to record progress during executionโ€”this enables recovery if context compaction occurs mid-plan. 249 + 250 + **Plan lifecycle and storage:** 251 + - **Queued**: Metadata in DB, content in `plans/{id}.txt` 252 + - **Active**: Metadata in DB (marked active), content in `plans/{id}.txt` 253 + - **Complete**: Content moved to DB, file deleted from `plans/` 254 + 255 + Only queued and active plans exist as files. Completed plans are stored entirely in the database. 256 + 257 + --- 258 + 259 + ## Workflows 260 + 261 + ### Starting a New Task 262 + 263 + 1. User selects `bootstrap` prompt 264 + 2. Agent (guided by prompt) clarifies the task through questions 265 + 3. Agent creates session, decomposes into plans, adds them to queue 266 + 4. Agent calls `9plan_queue_pull` to begin work 267 + 268 + ### Normal Execution Loop 269 + 270 + 1. `9plan_queue_pull` returns plan ID and file path 271 + 2. Agent reads plan file via filesystem tools 272 + 3. Agent reviews plan, clarifies ambiguities if needed 273 + 4. Agent checks Notesโ€”if plan was previously decomposed, use `9plan_history_get` to retrieve child outcomes by ID 274 + 5. Agent checks Inputsโ€”for each dependency, use `9plan_history_search` to find matching outputs 275 + 6. Agent executes the work (or completes aggregation) 276 + 7. Agent updates Notes in plan file to checkpoint progress 277 + 8. Agent calls `9plan_plan_complete` (or `9plan_plan_defer` if blocked) 278 + 9. Loop until queue is empty 279 + 280 + **Input resolution**: When a plan declares an input like "ghost_client module from Ghost API Client", the agent calls `9plan_history_search` with that description. The producing plan may have been decomposedโ€”in that case, child plan outcomes will contain the actual output descriptions. 281 + 282 + **Child aggregation**: When a parent plan is re-pulled after decomposition, the Notes contain child plan IDs. The agent uses `9plan_history_get` with each ID to retrieve the full outcome for aggregation. 283 + 284 + ### Discovering New Work 285 + 286 + During execution, agent discovers something that needs to be done: 287 + 288 + - **Must happen first**: `9plan_queue_add` with `position: front` 289 + - **Should happen eventually**: `9plan_queue_add` with `position: back` 290 + - **Current plan is too large**: Decompose using the pattern below 291 + 292 + ### Decomposing a Plan 293 + 294 + When a plan is too large to complete in a focused session: 295 + 296 + 1. Identify subplans (A.1, A.2, A.3) 297 + 2. For each subplan, identify: 298 + - Inputs: what it needs from sibling subplans or other branches 299 + - Outputs: what it produces that siblings or other branches may need 300 + 3. Update parent's Notes: "Decomposing into: A.1 (goal), A.2 (goal), A.3 (goal)" 301 + 4. Add subplans at front in reverse order (A.3, A.2, A.1) to achieve correct execution order 302 + 5. Defer parent to back with reason: "Awaiting completion of subplans A.1, A.2, A.3" 303 + 304 + Result: `[A.1, A.2, A.3, ...other work..., A]` 305 + 306 + Capturing inputs/outputs during decomposition is criticalโ€”this is when sibling relationships are visible. Once subplans execute in separate sessions, cross-branch visibility is lost. 307 + 308 + When parent is eventually re-pulled: 309 + 1. Check Notes to see it was decomposed (includes child plan IDs) 310 + 2. Use `9plan_history_get` to retrieve each child's outcome by ID 311 + 3. Aggregate results, handle any failures 312 + 4. Complete the parent (or re-plan if children failed) 313 + 314 + ### Resuming After Interruption 315 + 316 + 1. Human provides session name (or agent recalls it) 317 + 2. Agent calls `9plan_session_resume`โ€”returns session state including active plan (if any) 318 + 3. If a plan was in progress, read its file and continue 319 + 4. Otherwise, `9plan_queue_pull` to get next work 320 + 321 + --- 322 + 323 + ## Design Notes 324 + 325 + **Why a single queue?** 326 + Separate buckets (urgent/normal/backlog) create a failure mode where agents complete high-priority work and abandon the rest. A single queue that must empty enforces completion. 327 + 328 + **Why position-based ordering?** 329 + Numeric priority requires agents to calibrate against existing items and make arbitrary judgments. `front`/`back` maps directly to how work is discovered: "I need to do X before I can continue" vs "I should do Y eventually." 330 + 331 + **Why hybrid storage (SQLite + flat files)?** 332 + SQLite protects structural integrityโ€”agents can't accidentally corrupt queue order or active plan state. Flat files allow efficient filesystem access for plan content, leveraging the agent harness's built-in file tools and safeguards. Agents read and checkpoint plans via familiar file operations; the MCP server manages queue structure via tools. Humans can still inspect plan files directly when debugging. 333 + 334 + **Why three-word names?** 335 + Easy for humans to remember and communicate. Avoids UUIDs while maintaining uniqueness. Example: "resume copper-velvet-morning" is more natural than "resume a7x2f9k". 336 + 337 + **Collision handling** 338 + Session names and plan IDs are randomly generated. On generation, the server must check for collisions and regenerate if a collision occurs. With sufficient word lists (1000+ words for session names) and hash length (5+ characters for plan IDs), collisions should be rare in practice. 339 + 340 + **Why session-scoped?** 341 + Sessions map to tasks, not projects. A project might involve many sessions. This keeps individual queues focused and achievableโ€”"done" is well-defined. 342 + 343 + **Why preserve parents during decomposition?** 344 + When a plan is decomposed into subplans, the parent is deferred (not discarded) so it can later aggregate child outcomes. This preserves the task hierarchy: parents represent milestones and decision points, children represent executable work. Without parent preservation, intermediate structure is lost and there's no natural point to assess "did Phase 1 succeed?" or "do I need to re-plan based on what children discovered?" 345 + 346 + **Parent completion timing** 347 + Deferred parents complete after all other queued work, including sibling branches. This means a plan like "Sync Engine" might execute and complete before "Ghost API Client" (its dependency) formally completesโ€”even though Ghost API Client's children have finished. This is correct: children produce the actual outputs, parent aggregation is for verification and milestone tracking. Dependent plans resolve inputs by searching history for children's outcomes, not by waiting for parent completion. 348 + 349 + **Handling cross-branch dependencies** 350 + When a task decomposes into parallel branches (e.g., "App" โ†’ "Auth" + "Feed"), dependencies between branches (e.g., Feed needs Auth's user data) are only visible at the moment of decompositionโ€”when both branches exist in the same context window. Once branches decompose further in separate sessions, agents lose visibility into sibling branches. 351 + 352 + This means dependencies must be: 353 + 1. **Identified during common ancestor decomposition**: When both branches are visible 354 + 2. **Encoded in plan inputs/outputs**: Explicit fields capture what each plan needs and provides 355 + 3. **Resolved semantically at execution time**: Search history for outcomes matching input descriptions 356 + 357 + The queue has no automatic dependency enforcement. Instead, dependencies are encoded as structured information: the Inputs and Outputs fields capture what each plan requires and produces, and completed plan outcomes document what was actually delivered. Resolution happens by searching history for matching outputsโ€”if a plan was decomposed, its children's outcomes contain the actual output descriptions. This keeps the model simple but requires discipline during decomposition to capture cross-branch relationships while they're visible, and clear output documentation at completion time. 358 + 359 + --- 360 + 361 + ## Assumptions 362 + 363 + This design makes the following assumptions about agents, tasks, and the operating environment: 364 + 365 + ### Agents 366 + 367 + **Single-threaded execution** 368 + One plan active at a time. Agents don't parallelize work. If agents could work on multiple plans concurrently, the "active plan" model breaks down. 369 + 370 + **Filesystem access to session directory** 371 + Agents must have read/write access to the session's `plans/` directory (provided in tool responses). This is used for reading plan content and checkpointing progress in Notes. The agent doesn't need access to the SQLite database or any other session files. 372 + 373 + **Protocol compliance** 374 + Agents will call `9plan_plan_complete`/`9plan_plan_defer`/`9plan_plan_discard` to close out work rather than simply stopping. An agent that abandons mid-plan leaves orphaned active state. 375 + 376 + **Benefits from explicit structure** 377 + Agents perform better with formal plans (goal, approach, criteria) than with freeform notes. This might not hold for all agent architectures. 378 + 379 + **Context window is the bottleneck** 380 + Compaction mitigations assume context limits are the primary failure mode. If agents fail for other reasons (tool errors, API limits, crashes), this design doesn't address those. 381 + 382 + ### Tasks 383 + 384 + **Decomposable into plans with optional hierarchy** 385 + Work can be represented as plans in a queue. Complex hierarchies can be preserved by deferring parent plans to aggregate child outcomes, but deeply nested structures (4+ levels) may become unwieldy. Tasks with complex cross-branch dependencies don't fit cleanly. 386 + 387 + **Plans are independently executable** 388 + Each plan is self-contained. Tasks where steps are deeply intertwined (requiring constant cross-reference) fight against this model. 389 + 390 + **Observable success criteria** 391 + Completion conditions can be clearly defined. Exploratory or creative tasks where "done" is fuzzy are harder to fit. 392 + 393 + **Finite work** 394 + A single queue that must empty assumes tasks eventually complete. Ongoing maintenance, monitoring, or indefinite tasks don't have a natural "done" state. 395 + 396 + ### Operating Environment 397 + 398 + **Single agent per session** 399 + No concurrent access. Multiple agents (or agent instances) touching the same session would cause race conditions on `queue.txt`, `meta.txt`, etc. 400 + 401 + **Reliable filesystem** 402 + Flat files assume the filesystem doesn't corrupt, lose writes, or have permission issues mid-operation. 403 + 404 + **Human available for recovery** 405 + Compaction recovery relies on a human noticing the agent is lost and providing the session name. Fully autonomous agents without human oversight can't recover this way. 406 + 407 + **Bounded session count** 408 + Three-word session names assume collision probability stays low because sessions don't accumulate indefinitely. Thousands of concurrent sessions would increase collision risk. 409 + 410 + ### Usage Patterns 411 + 412 + **Single bootstrap phase** 413 + The prompt assumes planning happens once upfront. Tasks requiring periodic re-planning or major pivots might need to essentially restart. 414 + 415 + **Static ordering is sufficient** 416 + Position-based ordering (front/back) is enough. Tasks where optimal ordering changes dynamically based on external factors would need constant reordering. 417 + 418 + **No plan versioning** 419 + Notes are overwritten, plans are replaced via decomposition. There's no history of how a plan evolved and no rollback capability. 420 + 421 + --- 422 + 423 + ## Known Limitations 424 + 425 + **Front-insertion order reversal** 426 + When adding multiple plans at the front (e.g., during decomposition), successive `9plan_queue_add` calls reverse the intended order: 427 + - add A at front โ†’ `[A, ...]` 428 + - add B at front โ†’ `[B, A, ...]` 429 + - add C at front โ†’ `[C, B, A, ...]` 430 + 431 + To achieve `[A, B, C, ...]`, agents must add in reverse order (C, B, A). This is workable but unintuitive. A future revision might introduce a batch add operation with explicit ordering, or a different insertion model. 432 + 433 + **Context compaction mid-plan** 434 + If context compaction occurs while an agent is executing a plan, the agent may lose awareness of the session. The session state is preserved (the plan remains active in the database), but the agent won't know to check it. 435 + 436 + Mitigations: 437 + - **Plan granularity**: Keep plans small enough to complete in a focused session, reducing compaction risk 438 + - **Session breadcrumbs**: Tool responses include the session name, keeping it fresh in recent context 439 + - **Progress checkpointing**: Agents should periodically update the plan's Notes section with progress; if a user needs to re-prompt after compaction, the agent can resume where it left off rather than restarting 440 + - **User re-prompt**: If an agent appears to have lost session awareness, user can prompt with the session name (e.g., "resume amber-quiet-river") 441 + 442 + **Partial decomposition** 443 + Decomposition is a multi-step operation: update notes, add subplans to queue, then defer the parent. If compaction occurs mid-decomposition, state may be inconsistentโ€”some subplans queued, parent still active, agent has lost context. 444 + 445 + Example inconsistent state: 446 + - Agent was decomposing plan A into sub1, sub2, sub3 447 + - Compaction after adding sub1, sub2 but before sub3 and defer 448 + - State: `active: A`, queue: `[sub1, sub2, B, C]` 449 + - Agent on resumption sees plan A is active, doesn't know sub1/sub2 are partial children 450 + 451 + Mitigations: 452 + - **Checkpoint before decomposing**: Before starting decomposition, update Notes with the full plan: "Decomposing into: sub1 (goal), sub2 (goal), sub3 (goal)" 453 + - **Checkpoint during decomposition**: After each add, update Notes: "Added sub1, sub2. Remaining: sub3" 454 + - **Recovery heuristic**: On resumption, if Notes indicate decomposition was in progress, agent can review queue for partial subplans and complete the decomposition 455 + - **Parent preserved**: Because decomposition uses `9plan_plan_defer` rather than `9plan_plan_discard`, the parent is never lostโ€”worst case, it remains active with Notes indicating the decomposition intent 456 + 457 + This doesn't guarantee atomic decomposition, but provides enough breadcrumbs for recovery. 458 + 459 + **Missing plan files** 460 + Since plan files live outside the database, an agent could delete a plan file that the database still references. The server handles this gracefullyโ€”reporting "plan file missing for ID x" rather than crashingโ€”but the plan content is lost. This is an acceptable tradeoff: agents don't delete files unprompted, and accidental deletion is recoverable via re-planning.
+375
docs/error-recovery.md
··· 1 + # 9plan Error Recovery Playbook 2 + 3 + When things go wrong, use this guide to diagnose and recover. Format: **Symptom โ†’ Likely Cause โ†’ Recovery Steps**. 4 + 5 + --- 6 + 7 + ## Session-Level Issues 8 + 9 + ### "I don't know what session I was working on" 10 + 11 + **Symptom**: Agent or user has lost track of the session name. 12 + 13 + **Likely Cause**: Context compaction removed the session identifier from working memory, or conversation restarted. 14 + 15 + **Recovery Steps**: 16 + 1. Check recent tool responsesโ€”session name appears as prefix (e.g., `[Session: amber-quiet-river]`) 17 + 2. Look for session name in conversation history 18 + 3. If using a file browser, check `~/.9plan/sessions/` for session directories 19 + 4. List sessions by modification time to find most recent: 20 + ```bash 21 + ls -lt ~/.9plan/sessions/ 22 + ``` 23 + 5. Resume with `9plan_session_resume` using the discovered name 24 + 25 + **Prevention**: Note session names somewhere persistent (project README, task tracker) at session creation. 26 + 27 + --- 28 + 29 + ### "Session resume shows unexpected state" 30 + 31 + **Symptom**: `9plan_session_resume` returns a queue or active plan that doesn't match expectations. 32 + 33 + **Likely Cause**: 34 + - Work was done in a different conversation that wasn't synced 35 + - Agent completed/deferred plans without user awareness 36 + - Different session with similar name 37 + 38 + **Recovery Steps**: 39 + 1. Verify session name matches exactly (three-word identifiers are case-sensitive) 40 + 2. Review the returned state carefully: 41 + - `active_plan`: What's currently in progress? 42 + - `queue`: What's waiting? 43 + - `completed_count`: How much was done? 44 + 3. If active plan exists, read its file and Notes section for context 45 + 4. If state is truly wrong, check if another session exists for this task 46 + 47 + **Prevention**: Always complete or defer plans before ending a conversation. Use progress checkpointing. 48 + 49 + --- 50 + 51 + ## Queue Issues 52 + 53 + ### "Plans are executing in wrong order" 54 + 55 + **Symptom**: A plan runs before its dependencies are ready. 56 + 57 + **Likely Cause**: 58 + - Plans were added in wrong order during decomposition 59 + - Front-insertion reversal: adding A, B, C at front gives `[C, B, A]` 60 + 61 + **Recovery Steps**: 62 + 1. If dependency isn't ready yet: 63 + - Defer current plan: `9plan_plan_defer` with reason "Blocked: waiting for {dependency}" 64 + - Position: `front` to retry soon, or `back` if other work can proceed 65 + 2. If dependency was completed but not found: 66 + - Use `9plan_history_search` to find the output 67 + - The producing plan may have been decomposedโ€”search for child outcomes 68 + 69 + **Prevention**: When decomposing, add subplans in **reverse** order so they execute correctly. Document this in approach notes. 70 + 71 + --- 72 + 73 + ### "Queue is stuck with an active plan" 74 + 75 + **Symptom**: `9plan_queue_pull` returns error "a plan is already active." 76 + 77 + **Likely Cause**: Previous conversation/execution ended without completing, deferring, or discarding the active plan. 78 + 79 + **Recovery Steps**: 80 + 1. Call `9plan_session_resume` to see active plan details 81 + 2. Read the active plan file to understand its state 82 + 3. Check the Notes section for progress checkpoints 83 + 4. Decide how to proceed: 84 + - **Plan was nearly complete**: Finish remaining work, then `9plan_plan_complete` 85 + - **Plan needs more work**: Continue execution, checkpoint progress 86 + - **Plan should wait**: `9plan_plan_defer` with reason 87 + - **Plan is obsolete**: `9plan_plan_discard` with reason 88 + 89 + **Prevention**: Always close out active plans before ending work sessions. 90 + 91 + --- 92 + 93 + ### "Queue is empty but task isn't done" 94 + 95 + **Symptom**: `9plan_queue_pull` says queue is empty, but the overall task has unfinished work. 96 + 97 + **Likely Cause**: 98 + - Not all necessary plans were created during bootstrap 99 + - A plan was discarded instead of deferred 100 + - Discovered work wasn't added to queue 101 + 102 + **Recovery Steps**: 103 + 1. Review task requirements against completed work: 104 + ``` 105 + 9plan_history_search("your task keywords") 106 + ``` 107 + 2. Identify gaps between requirements and completed plans 108 + 3. Add missing work: 109 + ``` 110 + 9plan_queue_add with appropriate context, goal, etc. 111 + ``` 112 + 4. Continue execution 113 + 114 + **Prevention**: Thorough upfront decomposition during bootstrap. Add discovered work immediately rather than "remembering for later." 115 + 116 + --- 117 + 118 + ## Plan Execution Issues 119 + 120 + ### "Plan inputs can't be found in history" 121 + 122 + **Symptom**: `9plan_history_search` returns no results for a declared input. 123 + 124 + **Likely Cause**: 125 + - The producing plan hasn't completed yet (dependency ordering issue) 126 + - The producing plan was decomposedโ€”outputs are in children, not parent 127 + - Search terms don't match how outputs were documented 128 + - The input was never actually produced (plan was discarded or failed) 129 + 130 + **Recovery Steps**: 131 + 1. Try broader search terms: 132 + ``` 133 + 9plan_history_search("ghost client") # instead of exact field name 134 + ``` 135 + 2. If the producing plan was decomposed, search for child plan outputs: 136 + ``` 137 + 9plan_history_search("getPosts API authentication") # functional description 138 + ``` 139 + 3. If still not found, check if the plan is still in queue (not yet executed) 140 + 4. If truly missing, the producing work needs to be re-done: 141 + - Add a new plan to create the required output 142 + - Defer current plan until dependency is ready 143 + 144 + **Prevention**: Document outputs with specific, searchable terms. Use multiple keywords that might match searches. 145 + 146 + --- 147 + 148 + ### "Plan file is missing" 149 + 150 + **Symptom**: Attempting to read a plan file returns "file not found" or similar error. 151 + 152 + **Likely Cause**: 153 + - Plan was completed (files are deleted on completion) 154 + - Plan was discarded (files are deleted on discard) 155 + - Filesystem issue or accidental deletion 156 + - Wrong path (session directory changed) 157 + 158 + **Recovery Steps**: 159 + 1. Check if plan is in history (it completed): 160 + ``` 161 + 9plan_history_get(plan_id) 162 + ``` 163 + 2. Verify the session path in `9plan_session_resume` output 164 + 3. If plan was truly lost: 165 + - Check Notes from other plans for context about what it contained 166 + - Re-create the plan with `9plan_queue_add` 167 + 168 + **Prevention**: Don't manually manipulate plan files outside of normal tool usage. 169 + 170 + --- 171 + 172 + ### "I lost context mid-plan" 173 + 174 + **Symptom**: Agent doesn't remember what it was doing, context window was compacted. 175 + 176 + **Likely Cause**: Long execution triggered context compaction, removing earlier conversation. 177 + 178 + **Recovery Steps**: 179 + 1. The session name should be in recent tool responses (breadcrumb design) 180 + 2. Call `9plan_session_resume` to get current state 181 + 3. If a plan is active, read its file: 182 + - Check Notes section for progress checkpoints 183 + - Resume from last checkpoint 184 + 4. If no progress was checkpointed, assess the plan and start execution fresh 185 + 186 + **Prevention**: 187 + - Keep plans small enough to complete in focused sessions 188 + - Checkpoint progress in Notes periodically (every few tool calls or significant milestones) 189 + - Note key context at start of execution 190 + 191 + --- 192 + 193 + ## Decomposition Issues 194 + 195 + ### "Partial decomposition - some subplans exist, parent still active" 196 + 197 + **Symptom**: Queue contains some subplans but parent plan is still marked active (not deferred). 198 + 199 + **Likely Cause**: Context compaction or interruption occurred mid-decomposition, after some `9plan_queue_add` calls but before `9plan_plan_defer`. 200 + 201 + **Recovery Steps**: 202 + 1. Read the parent plan's Notes sectionโ€”it should document decomposition intent: 203 + ``` 204 + "Decomposing into: sub1 (goal), sub2 (goal), sub3 (goal)" 205 + ``` 206 + 2. Compare Notes against queue to identify missing subplans 207 + 3. Add any remaining subplans 208 + 4. Complete the decomposition by deferring the parent: 209 + ``` 210 + 9plan_plan_defer 211 + reason: "Completing interrupted decomposition. Subplans: ..." 212 + position: back 213 + ``` 214 + 215 + **Example scenario**: 216 + - Notes say: "Decomposing into: A.1, A.2, A.3" 217 + - Queue shows: `[A.1, A.2, B, C]` (A.3 missing, parent still active) 218 + - Fix: Add A.3 at front, then defer parent 219 + 220 + **Prevention**: Always update Notes with full decomposition plan before starting to add subplans. 221 + 222 + --- 223 + 224 + ### "Parent plan can't find child outcomes" 225 + 226 + **Symptom**: Parent plan is re-pulled for aggregation, but `9plan_history_get` on child IDs fails. 227 + 228 + **Likely Cause**: 229 + - Child plan IDs weren't recorded in Notes during decomposition 230 + - Children haven't completed yet (still in queue) 231 + - Child was discarded instead of completed 232 + 233 + **Recovery Steps**: 234 + 1. Check queue for remaining children: 235 + ``` 236 + 9plan_session_resume โ†’ review queue contents 237 + ``` 238 + 2. If children are still queued, defer parent again: 239 + ``` 240 + 9plan_plan_defer 241 + reason: "Children not yet complete. Will retry after queue progress." 242 + position: back 243 + ``` 244 + 3. If children should have completed, search history by goal/output: 245 + ``` 246 + 9plan_history_search("the specific goal or output description") 247 + ``` 248 + 4. If children were lost, they may need to be re-planned 249 + 250 + **Prevention**: Record child plan IDs in parent's Notes during decomposition. Use specific, searchable output descriptions. 251 + 252 + --- 253 + 254 + ### "Cross-branch dependency wasn't captured" 255 + 256 + **Symptom**: A plan needs output from a sibling branch, but the input wasn't declared and the output can't be found. 257 + 258 + **Likely Cause**: During decomposition, the dependency between branches wasn't identified and recorded in inputs/outputs fields. 259 + 260 + **Recovery Steps**: 261 + 1. Search history broadly for the needed output: 262 + ``` 263 + 9plan_history_search("auth token user session") # functional description 264 + ``` 265 + 2. If found, proceedโ€”the input field was incomplete but the data exists 266 + 3. If not found, check if the producing work is still in queue 267 + 4. If the producing work was decomposed, search for its children's outputs 268 + 5. If truly missing, add a new plan to produce the required output 269 + 270 + **Prevention**: During decomposition, explicitly think through "what does each branch need from other branches?" Capture ALL cross-branch dependencies while they're visible. 271 + 272 + --- 273 + 274 + ## Data Issues 275 + 276 + ### "History search returns irrelevant results" 277 + 278 + **Symptom**: `9plan_history_search` returns plans that don't match what you're looking for. 279 + 280 + **Likely Cause**: Search terms are too broad or the relevant plan used different terminology. 281 + 282 + **Recovery Steps**: 283 + 1. Try more specific search terms 284 + 2. Try different phrasings of the same concept 285 + 3. If you know the approximate plan, try searching by goal keywords 286 + 4. Review recent history manually if session is small 287 + 288 + **Prevention**: Use specific, consistent terminology in outputs. Include multiple relevant keywords. 289 + 290 + --- 291 + 292 + ### "Database seems corrupted or inconsistent" 293 + 294 + **Symptom**: Tool calls return unexpected errors, state doesn't match filesystem, or data is missing. 295 + 296 + **Likely Cause**: 297 + - Interrupted operation left inconsistent state 298 + - Manual filesystem manipulation 299 + - Bug in 9plan server 300 + 301 + **Recovery Steps**: 302 + 1. Document current state: 303 + - What does `9plan_session_resume` return? 304 + - What files exist in `plans/` directory? 305 + - What operations were being performed? 306 + 2. Check for orphaned plan files (files in `plans/` not referenced by queue) 307 + 3. For serious corruption, consider: 308 + - Starting fresh session, manually recreating queue 309 + - Manually inspecting `session.db` if comfortable with SQLite 310 + 4. Report bug if this seems like a server issue 311 + 312 + **Prevention**: Let 9plan tools manage all state. Avoid manual file manipulation. 313 + 314 + --- 315 + 316 + ## Emergency Procedures 317 + 318 + ### "Everything is broken, I need to restart" 319 + 320 + **Steps for clean restart while preserving work**: 321 + 322 + 1. **Document current state**: 323 + - List all completed work (`9plan_history_search("")` or broad search) 324 + - Note any partially completed plans 325 + - Save any code/artifacts created outside 9plan 326 + 327 + 2. **Create new session**: 328 + ``` 329 + 9plan_session_create with task description 330 + ``` 331 + 332 + 3. **Recreate queue**: 333 + - Add only remaining work (not already-completed plans) 334 + - Reference completed work by description, not old plan IDs 335 + 336 + 4. **Resume execution**: 337 + - Old history won't be searchable from new session 338 + - May need to manually note where prior outputs are located 339 + 340 + ### "Agent is in a loop, keeps doing the same thing" 341 + 342 + **Symptom**: Agent repeatedly pulls the same plan or makes no progress. 343 + 344 + **Likely Cause**: 345 + - Plan is too vagueโ€”agent doesn't know what to do 346 + - Plan keeps getting deferred for the same reason 347 + - Agent is confused about success criteria 348 + 349 + **Recovery Steps**: 350 + 1. Discard the problematic plan: 351 + ``` 352 + 9plan_plan_discard 353 + reason: "Plan too vague / stuck in loop. Will re-plan with more detail." 354 + ``` 355 + 2. Create replacement with clearer specification: 356 + - More specific goal 357 + - More detailed approach 358 + - More concrete success criteria 359 + 3. If the work is fundamentally unclear, pause for human clarification 360 + 361 + --- 362 + 363 + ## Quick Reference: When to Use Each Action 364 + 365 + | Situation | Action | 366 + |-----------|--------| 367 + | Plan complete, work done | `9plan_plan_complete` | 368 + | Blocked, can't proceed now | `9plan_plan_defer` (front) | 369 + | Decomposing into subplans | `9plan_plan_defer` (back) | 370 + | Plan is obsolete/wrong | `9plan_plan_discard` | 371 + | Found new blocking work | `9plan_queue_add` (front) | 372 + | Found new eventual work | `9plan_queue_add` (back) | 373 + | Need outputs from prior work | `9plan_history_search` | 374 + | Need specific plan details | `9plan_history_get` | 375 + | Lost/need to recover | `9plan_session_resume` |
+378
docs/future-work.md
··· 1 + # 9plan Future Work and Non-Goals 2 + 3 + This document captures ideas that were explicitly considered and deferred, features that don't fit the current design, and potential enhancements for later versions. 4 + 5 + --- 6 + 7 + ## Explicit Non-Goals 8 + 9 + These are things 9plan intentionally does NOT do. They were considered and rejected, not overlooked. 10 + 11 + ### Multi-Agent Concurrency 12 + 13 + **What it would be**: Multiple agents working on the same session simultaneously. 14 + 15 + **Why not**: 16 + - The "one active plan" model assumes single execution 17 + - Queue ordering becomes complex with concurrent pulls 18 + - Locking and conflict resolution add significant complexity 19 + - Target use case is single-agent, long-horizon tasks 20 + 21 + **Workaround**: Create separate sessions for parallel workstreams; coordinate manually. 22 + 23 + --- 24 + 25 + ### Priority-Based Ordering 26 + 27 + **What it would be**: Numeric priorities (1-10) or priority tiers (high/medium/low). 28 + 29 + **Why not**: 30 + - Creates failure mode where low-priority work is abandoned 31 + - Requires agents to calibrate priorities against existing items 32 + - Front/back positioning is simpler and matches how work is discovered 33 + 34 + **Workaround**: Use front position for urgent work; reorder via defer if needed. 35 + 36 + --- 37 + 38 + ### Cross-Session Dependencies 39 + 40 + **What it would be**: Plans in one session depending on outputs from another session. 41 + 42 + **Why not**: 43 + - Breaks session isolation and "must empty" semantics 44 + - Session scoping keeps search results predictable 45 + - Different sessions may use same terms for different things 46 + 47 + **Workaround**: Complete dependent session first; manually reference its outputs. 48 + 49 + --- 50 + 51 + ### Plan Versioning / Rollback 52 + 53 + **What it would be**: Full history of plan changes with ability to revert. 54 + 55 + **Why not**: 56 + - Notes section provides partial history (deferral reasons, checkpoints) 57 + - Full versioning adds significant storage and complexity 58 + - Plans are disposableโ€”re-plan rather than rollback 59 + 60 + **Workaround**: Copy important plan state to Notes before major changes. 61 + 62 + --- 63 + 64 + ### Automatic Dependency Enforcement 65 + 66 + **What it would be**: System blocks plan execution until declared dependencies complete. 67 + 68 + **Why not**: 69 + - Dependencies are semantic, not ID-based; hard to verify automatically 70 + - Decomposition means children produce outputs, not parents 71 + - Would require dependency graph management 72 + 73 + **Workaround**: Defer plans that can't proceed; semantic search finds outputs. 74 + 75 + --- 76 + 77 + ### Project-Level Organization 78 + 79 + **What it would be**: Sessions grouped into projects with shared history. 80 + 81 + **Why not**: 82 + - Adds organizational layer that complicates core model 83 + - Session isolation is valuable for predictability 84 + - "Project" boundaries are often unclear 85 + 86 + **Workaround**: Use naming conventions (e.g., `myapp-auth`, `myapp-frontend`). 87 + 88 + --- 89 + 90 + ### Real-Time Collaboration 91 + 92 + **What it would be**: Multiple users viewing/editing session state simultaneously. 93 + 94 + **Why not**: 95 + - Single-agent execution model 96 + - Would require sync protocol, conflict resolution 97 + - Not the target use case 98 + 99 + **Workaround**: One user at a time; use session resume to hand off. 100 + 101 + --- 102 + 103 + ### Plan Templates 104 + 105 + **What it would be**: Pre-defined plan structures for common patterns (research, implementation, testing). 106 + 107 + **Why not (for now)**: 108 + - Current plan format is simple enough to create ad-hoc 109 + - Templates might constrain creative decomposition 110 + - Can be added later without breaking changes 111 + 112 + **Workaround**: Copy-paste from previous plans; agents can develop their own patterns. 113 + 114 + --- 115 + 116 + ## Potential Future Enhancements 117 + 118 + These are features that COULD be valuable but aren't prioritized for v1. 119 + 120 + ### Batch Add Operation 121 + 122 + **Problem**: Front-insertion reversal requires adding plans in reverse order. 123 + 124 + **Enhancement**: `9plan_queue_add_batch` accepting ordered list of plans, inserting in specified order. 125 + 126 + **Example**: 127 + ```json 128 + { 129 + "tool": "9plan_queue_add_batch", 130 + "arguments": { 131 + "plans": [ 132 + { "goal": "Plan A", ... }, 133 + { "goal": "Plan B", ... }, 134 + { "goal": "Plan C", ... } 135 + ], 136 + "position": "front" 137 + } 138 + } 139 + ``` 140 + Result: `[A, B, C, ...rest]` (correct order) 141 + 142 + **Complexity**: Lowโ€”queue manipulation is straightforward. 143 + 144 + **Priority**: Mediumโ€”solves real usability issue. 145 + 146 + --- 147 + 148 + ### HTTP Transport 149 + 150 + **Problem**: stdio transport requires server to be spawned by client. 151 + 152 + **Enhancement**: HTTP/SSE transport for remote access, web clients. 153 + 154 + **Benefits**: 155 + - Web-based interfaces could connect 156 + - Multiple clients could connect (though still single-agent) 157 + - Easier debugging/inspection 158 + 159 + **Complexity**: Mediumโ€”transport layer exists, need HTTP endpoints. 160 + 161 + **Priority**: Lowโ€”stdio works for current use cases. 162 + 163 + --- 164 + 165 + ### Session Export/Import 166 + 167 + **Problem**: Sessions are tied to local filesystem; can't share or move. 168 + 169 + **Enhancement**: Export session to portable format; import elsewhere. 170 + 171 + **Example**: 172 + ```bash 173 + 9plan export amber-quiet-river > session.json 174 + 9plan import session.json # creates new session 175 + ``` 176 + 177 + **Benefits**: 178 + - Share sessions between machines 179 + - Backup/restore beyond filesystem copy 180 + - Hand off sessions to other users 181 + 182 + **Complexity**: Mediumโ€”need serialization format, ID remapping. 183 + 184 + **Priority**: Lowโ€”filesystem copy works for most cases. 185 + 186 + --- 187 + 188 + ### Enhanced Search Operators 189 + 190 + **Problem**: FTS5 search is keyword-based; can't express complex queries. 191 + 192 + **Enhancement**: Support field-specific search, date ranges, status filters. 193 + 194 + **Example**: 195 + ```json 196 + { 197 + "tool": "9plan_history_search", 198 + "arguments": { 199 + "query": "goal:authentication outputs:client", 200 + "completed_after": "2024-01-01" 201 + } 202 + } 203 + ``` 204 + 205 + **Complexity**: Mediumโ€”need query parser, extended FTS5 usage. 206 + 207 + **Priority**: Lowโ€”current search works for dependency resolution. 208 + 209 + --- 210 + 211 + ### Progress Indicators 212 + 213 + **Problem**: No visibility into session progress without resuming. 214 + 215 + **Enhancement**: `9plan_session_status` providing quick overview without full resume. 216 + 217 + **Example response**: 218 + ``` 219 + Session: amber-quiet-river 220 + Status: In progress 221 + Progress: 4/7 plans complete (57%) 222 + Active: k7f3m (since 2 hours ago) 223 + ``` 224 + 225 + **Complexity**: Lowโ€”data already available. 226 + 227 + **Priority**: Lowโ€”resume provides this information. 228 + 229 + --- 230 + 231 + ### Plan Estimation 232 + 233 + **Problem**: No way to estimate remaining work or session duration. 234 + 235 + **Enhancement**: Optional time/effort estimates on plans; rollup to session level. 236 + 237 + **Example**: 238 + ```json 239 + { 240 + "tool": "9plan_queue_add", 241 + "arguments": { 242 + "goal": "Implement caching", 243 + "estimate": "2 hours", 244 + ... 245 + } 246 + } 247 + ``` 248 + 249 + **Complexity**: Mediumโ€”need estimation tracking, rollup logic. 250 + 251 + **Priority**: Lowโ€”estimation is notoriously unreliable for development tasks. 252 + 253 + --- 254 + 255 + ### Webhook Notifications 256 + 257 + **Problem**: No way to notify external systems of plan completions. 258 + 259 + **Enhancement**: Configure webhooks for session events. 260 + 261 + **Use cases**: 262 + - Slack notification when session completes 263 + - Trigger CI/CD on certain plan completions 264 + - Audit logging to external system 265 + 266 + **Complexity**: Highโ€”need configuration, retry logic, security. 267 + 268 + **Priority**: Lowโ€”manual notification works for current use cases. 269 + 270 + --- 271 + 272 + ### LLM-Assisted History Search 273 + 274 + **Problem**: Semantic search depends on exact keyword matches. 275 + 276 + **Enhancement**: Use embeddings or LLM reranking for better search relevance. 277 + 278 + **Benefits**: 279 + - Find outputs even with terminology mismatch 280 + - "Smart" understanding of what agent is looking for 281 + 282 + **Complexity**: Highโ€”need embedding infrastructure or LLM integration. 283 + 284 + **Priority**: Lowโ€”explicit output descriptions work well enough. 285 + 286 + --- 287 + 288 + ## Ideas Explicitly Rejected 289 + 290 + These were considered more deeply and rejected for specific reasons. 291 + 292 + ### Hierarchical Queue (Parent-Child Tracking) 293 + 294 + **Idea**: Maintain explicit parent-child relationships in queue structure. 295 + 296 + **Rejected because**: 297 + - Adds complexity to queue management 298 + - Semantic dependency resolution handles relationships 299 + - Notes capture decomposition for aggregation 300 + - Would complicate front/back positioning semantics 301 + 302 + --- 303 + 304 + ### Automatic Plan Splitting 305 + 306 + **Idea**: System detects large plans and suggests/performs decomposition. 307 + 308 + **Rejected because**: 309 + - "Large" is subjective and context-dependent 310 + - Agent is better positioned to know when decomposition helps 311 + - Would require understanding plan content semantically 312 + 313 + --- 314 + 315 + ### Undo/Redo Operations 316 + 317 + **Idea**: Undo last operation (plan add, complete, defer, discard). 318 + 319 + **Rejected because**: 320 + - Many operations have side effects (file deletion, DB changes) 321 + - Would require transaction log, complex state management 322 + - Defer provides "soft undo" for plan lifecycle 323 + - Re-planning is idiomatic for mistakes 324 + 325 + --- 326 + 327 + ### Session Archiving 328 + 329 + **Idea**: Completed sessions move to archive, freeing active space. 330 + 331 + **Rejected because**: 332 + - Filesystem storage is cheap 333 + - Archiving adds state (is it archived or deleted?) 334 + - Manual cleanup is simple and controllable 335 + 336 + --- 337 + 338 + ### Plan Dependencies via DAG 339 + 340 + **Idea**: Express dependencies as directed acyclic graph, with topological ordering. 341 + 342 + **Rejected because**: 343 + - Requires knowing all dependencies upfront 344 + - Doesn't handle dynamically discovered work 345 + - Cross-branch dependencies only visible at decomposition time 346 + - Semantic resolution is more flexible 347 + 348 + --- 349 + 350 + ## When to Reconsider 351 + 352 + These non-goals or deferrals should be reconsidered if: 353 + 354 + | Feature | Reconsider If... | 355 + |---------|------------------| 356 + | Multi-agent concurrency | Use cases emerge requiring parallel execution | 357 + | Priority ordering | Users consistently struggle with front/back | 358 + | Cross-session deps | Common pattern of related sessions emerges | 359 + | HTTP transport | Web-based interfaces become priority | 360 + | Batch add | Decomposition usability complaints increase | 361 + | Plan templates | Common patterns stabilize across users | 362 + 363 + --- 364 + 365 + ## Contributing Ideas 366 + 367 + If you have ideas for 9plan: 368 + 369 + 1. **Check this document first**โ€”it may already be addressed 370 + 2. **Consider the design principles**โ€”does it fit? 371 + - Self-contained plans 372 + - Semantic dependencies 373 + - Single queue discipline 374 + - Session isolation 375 + 3. **Describe the use case**โ€”what problem does it solve? 376 + 4. **Suggest an approach**โ€”how would it work? 377 + 378 + Ideas that conflict with core principles need strong justification. Ideas that extend capabilities without breaking principles are more likely to be adopted.
+169
docs/glossary.md
··· 1 + # 9plan Glossary 2 + 3 + A canonical reference for terminology used throughout the 9plan system. 4 + 5 + --- 6 + 7 + ## Core Concepts 8 + 9 + ### Session 10 + A named container for a queue of plans, representing a single overall task or goal. Sessions are identified by randomly-generated **three-word identifiers** (e.g., `copper-velvet-morning`) for easy human recall and communication. 11 + 12 + **Lifecycle**: Created โ†’ Active (while plans exist) โ†’ Complete (when queue empties) 13 + 14 + **Scope**: One session = one task. A project may involve multiple sessions. 15 + 16 + ### Plan 17 + A discrete, self-contained unit of work with enough detail for an agent to execute it without additional context or clarifying questions. Plans are identified by short **alphanumeric hashes** (e.g., `k7f3m`). 18 + 19 + **Key property**: A well-formed plan allows an agent resuming from "cold storage" (no memory of prior conversation) to execute successfully. 20 + 21 + ### Queue 22 + A single ordered list of plans within a session. There are no priority tiers, backlogs, or separate bucketsโ€”just one queue that must eventually empty for the session to complete. 23 + 24 + **Position semantics**: 25 + - **Front**: Work that must happen before continuing current work 26 + - **Back**: Work that should happen eventually, after current work 27 + 28 + --- 29 + 30 + ## Plan Structure 31 + 32 + ### Context (Plan Field) 33 + High-level summary explaining the overall task, where this plan fits within it, and why this plan exists. Provides the "big picture" framing. 34 + 35 + ### Goal (Plan Field) 36 + What this specific plan accomplishes. Should be concrete and achievable. 37 + 38 + ### Inputs (Plan Field) 39 + What this plan requires from other plans or external sources. References are **semantic** (by description), not by plan ID. Example: "ghost_client module from Ghost API Client work." 40 + 41 + ### Outputs (Plan Field) 42 + What this plan produces that other plans may consume. Should be specific enough for dependent plans to find via history search. Example: "Authenticated Ghost API client with methods for posts, pages, and media." 43 + 44 + ### Approach (Plan Field) 45 + Detailed, actionable steps for how to accomplish the goal. Should be specific enough that an agent doesn't need to make major decisions about methodology. 46 + 47 + ### Success Criteria (Plan Field) 48 + Observable conditions that indicate the plan is complete. Should be concrete and verifiable, not subjective. 49 + 50 + ### Notes (Plan Field) 51 + Working scratchpad for accumulating context during execution. Used for: 52 + - Progress checkpoints 53 + - Deferral reasons 54 + - Decomposition records (child plan IDs) 55 + - Recovery breadcrumbs 56 + 57 + --- 58 + 59 + ## Operations 60 + 61 + ### Decomposition 62 + The process of breaking a large plan into smaller, independently-executable subplans. The parent plan is **deferred** (not discarded) so it can later **aggregate** child outcomes. 63 + 64 + **Critical moment**: During decomposition, sibling plans are visible in the same context window. This is the only time cross-branch dependencies can be identified and captured in inputs/outputs fields. 65 + 66 + ### Aggregation 67 + When a parent plan is re-pulled after its children complete, the agent reviews child outcomes (via `9plan_history_get`), verifies success, documents combined results, and handles any failures. This is verification and milestone tracking, not primary work. 68 + 69 + ### Deferral 70 + Returning an active plan to the queue without completing it. Reasons include: 71 + - **Blocked**: Can't proceed, will retry later 72 + - **Decomposition**: Breaking into subplans, will aggregate later 73 + - **Reordering**: Other work should happen first 74 + 75 + The deferral reason is appended to the plan's Notes section. 76 + 77 + ### Completion 78 + Marking a plan as done. The outcome (summary of what was accomplished) is recorded, the plan content is indexed into the database for history search, and the plan file is deleted. 79 + 80 + ### Discarding 81 + Abandoning a plan without recording it in history. Used only when a plan becomes truly obsoleteโ€”superseded by external changes, discovered unnecessary, or fundamentally misconceived. **Never use for decomposition.** 82 + 83 + --- 84 + 85 + ## Dependency Concepts 86 + 87 + ### Semantic Dependency Resolution 88 + The mechanism by which plans find outputs from other plans. Instead of hard-coding plan IDs, plans describe what they need in natural language. At execution time, agents use `9plan_history_search` to find completed plan outcomes that match the description. 89 + 90 + **Why semantic**: If a plan was decomposed, its children's outcomes contain the actual outputs. ID-based references would point to the parent, which only aggregatedโ€”the children did the real work. 91 + 92 + ### Cross-Branch Dependency 93 + A dependency between plans that exist in different decomposition branches. Example: The "Feed" branch needs authentication outputs from the "Auth" branch. 94 + 95 + **Challenge**: Once branches decompose further in separate sessions, agents lose visibility into sibling branches. Cross-branch dependencies must be identified during the common ancestor's decomposition (when both branches are visible) and encoded in inputs/outputs fields. 96 + 97 + ### Input Resolution 98 + The process of finding outputs that satisfy a plan's declared inputs. Steps: 99 + 1. Read the plan's Inputs field 100 + 2. For each input, call `9plan_history_search` with the description 101 + 3. Review matching outcomes to find the required data/artifacts 102 + 103 + --- 104 + 105 + ## Context Management 106 + 107 + ### Context Window 108 + The agent's working memoryโ€”the portion of conversation history and system state visible to the agent at any given moment. Limited in size. 109 + 110 + ### Context Compaction 111 + When the context window fills, older content is summarized or removed to make room for new content. This can cause agents to lose awareness of the current session, active plan, or prior decisions. 112 + 113 + ### Session Breadcrumbs 114 + Tool responses include the session name (e.g., `[Session: amber-quiet-river]`) to keep it fresh in recent context. If compaction occurs, the session name is more likely to survive in the retained context. 115 + 116 + ### Progress Checkpointing 117 + Periodically updating a plan's Notes section with current progress. If context compaction occurs mid-plan, the agent (or a fresh agent after human re-prompt) can read the Notes and resume without restarting. 118 + 119 + --- 120 + 121 + ## Storage Concepts 122 + 123 + ### Hybrid Storage Model 124 + The architectural pattern where SQLite manages structural integrity (queue order, active plan state, history) while flat files provide agent-accessible plan content. Agents read/write plan files directly; the MCP server manages database operations. 125 + 126 + ### Plan File 127 + A human-readable plaintext file (`plans/{id}.txt`) containing a plan's full content. Exists only for queued and active plans. Deleted when plan completes (content moves to database). 128 + 129 + ### History Index 130 + The searchable record of completed plans stored in SQLite with FTS5 (full-text search). Enables semantic dependency resolution via `9plan_history_search`. 131 + 132 + --- 133 + 134 + ## Session States 135 + 136 + ### Active Plan 137 + The plan currently being executed. Only one plan can be active at a time. An agent must complete, defer, or discard the active plan before pulling another. 138 + 139 + ### Queued Plan 140 + A plan waiting in the queue to be pulled. Has a file in `plans/` and metadata in the database. 141 + 142 + ### Completed Plan 143 + A plan that was finished. Content is stored in the database (searchable via history). No file exists. 144 + 145 + ### Discarded Plan 146 + A plan that was abandoned. No record in history, no file. As if it never existed. 147 + 148 + --- 149 + 150 + ## Identifiers 151 + 152 + ### Three-Word Session Name 153 + Human-memorable identifier for sessions. Format: `{adjective}-{adjective}-{noun}` (e.g., `copper-velvet-morning`). Generated using word lists with collision checking. 154 + 155 + ### Plan ID 156 + Short alphanumeric hash identifying a plan (e.g., `k7f3m`). 5+ characters, generated with collision checking. Intentionally different format from session names to avoid confusion. 157 + 158 + --- 159 + 160 + ## Failure Modes 161 + 162 + ### Orphaned Active State 163 + When an agent stops mid-plan without calling complete/defer/discard. The plan remains marked active in the database, blocking new pulls. Requires human intervention or session resume to recover. 164 + 165 + ### Partial Decomposition 166 + When context compaction occurs during the multi-step decomposition process, leaving some subplans queued but the parent still active. Notes checkpointing provides recovery breadcrumbs. 167 + 168 + ### Missing Plan File 169 + When a plan file is deleted but the database still references it. The server handles this gracefully (reports the issue rather than crashing), but plan content is lost.
+1013
docs/implementation-plan.md
··· 1 + # 9plan MCP Server: Implementation Design Document 2 + 3 + **Version**: 1.2.0 4 + **Date**: January 2026 5 + **Status**: Draft 6 + 7 + --- 8 + 9 + ## Table of Contents 10 + 11 + 1. [Executive Summary](#1-executive-summary) 12 + 2. [Architecture Overview](#2-architecture-overview) 13 + 3. [Technology Stack](#3-technology-stack) 14 + 4. [Project Structure](#4-project-structure) 15 + 5. [Data Model](#5-data-model) 16 + 6. [Tool Specifications](#6-tool-specifications) 17 + 7. [Prompt Specifications](#7-prompt-specifications) 18 + 8. [File Formats](#8-file-formats) 19 + 9. [Logging](#9-logging) 20 + 10. [Error Handling](#10-error-handling) 21 + 11. [Configuration](#11-configuration) 22 + 12. [Dependency Injection & Testability](#12-dependency-injection--testability) 23 + 13. [Implementation Phases](#13-implementation-phases) 24 + 14. [Known Limitations](#14-known-limitations) 25 + 26 + --- 27 + 28 + ## 1. Executive Summary 29 + 30 + ### 1.1 Purpose 31 + 32 + 9plan is an MCP server that provides a **session-scoped work queue** for long time-horizon agent task sequencing. It enables AI agents to decompose complex tasks into discrete, self-contained plans that can be executed across conversation boundariesโ€”even surviving context compaction. 33 + 34 + ### 1.2 Core Value Proposition 35 + 36 + | Challenge | 9plan Solution | 37 + |-----------|----------------| 38 + | Agents lose context during long tasks | Persistent sessions with three-word identifiers for easy resumption | 39 + | Complex tasks require decomposition | Structured plans with explicit inputs/outputs for dependency tracking | 40 + | Work ordering is ambiguous | Single queue with front/back positioning semantics | 41 + | Progress is lost on interruption | Plan files serve as checkpoints; notes preserve state | 42 + 43 + ### 1.3 Design Principles 44 + 45 + 1. **Self-Contained Plans**: Each plan contains enough context for an agent to execute without asking clarifying questions 46 + 2. **Semantic Dependencies**: Cross-plan dependencies are resolved by searching history, not by ID references 47 + 3. **Single Queue Discipline**: No priority tiers or backlogsโ€”one queue that must eventually empty 48 + 4. **Hybrid Storage**: SQLite for structural integrity, flat files for agent-accessible content 49 + 5. **Session Breadcrumbs**: Tool responses include session names to aid context recovery 50 + 51 + --- 52 + 53 + ## 2. Architecture Overview 54 + 55 + ### 2.1 High-Level Architecture 56 + 57 + ``` 58 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 59 + โ”‚ MCP Client โ”‚ 60 + โ”‚ (Claude, Cursor, VS Code, etc.) โ”‚ 61 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 62 + โ”‚ stdio / HTTP 63 + โ–ผ 64 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 65 + โ”‚ 9plan MCP Server โ”‚ 66 + โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 67 + โ”‚ โ”‚ Tools โ”‚ โ”‚ Prompts โ”‚ โ”‚ Server Instructions โ”‚ โ”‚ 68 + โ”‚ โ”‚ (9 total) โ”‚ โ”‚ (bootstrap) โ”‚ โ”‚ (plan format spec) โ”‚ โ”‚ 69 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 70 + โ”‚ โ”‚ โ”‚ 71 + โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 72 + โ”‚ โ”‚ Core Services โ”‚ โ”‚ 73 + โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ 74 + โ”‚ โ”‚ โ”‚ Session โ”‚ โ”‚ Queue โ”‚ โ”‚ History โ”‚ โ”‚ โ”‚ 75 + โ”‚ โ”‚ โ”‚ Manager โ”‚ โ”‚ Manager โ”‚ โ”‚ Search (FTS5) โ”‚ โ”‚ โ”‚ 76 + โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ 77 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 78 + โ”‚ โ”‚ โ”‚ 79 + โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ 80 + โ”‚ โ”‚ Storage Layer โ”‚ โ”‚ 81 + โ”‚ โ”‚ SQLite (session.db) + Plan Files (plans/*.txt) โ”‚ โ”‚ 82 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 83 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 84 + ``` 85 + 86 + ### 2.2 Plan Lifecycle State Machine 87 + 88 + ``` 89 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 90 + โ”‚ Created โ”‚ 91 + โ”‚ (queued) โ”‚ 92 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 93 + โ”‚ 9plan_queue_pull 94 + โ–ผ 95 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 96 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ Active โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 97 + โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 98 + โ”‚ โ”‚ โ”‚ 99 + 9plan_plan_defer 9plan_plan_complete 9plan_plan_discard 100 + โ”‚ โ”‚ โ”‚ 101 + โ–ผ โ–ผ โ–ผ 102 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 103 + โ”‚ Queued โ”‚ โ”‚ Completed โ”‚ โ”‚ Discarded โ”‚ 104 + โ”‚ (deferred) โ”‚ โ”‚ (in history) โ”‚ โ”‚ (deleted) โ”‚ 105 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 106 + ``` 107 + 108 + ### 2.3 Key Architectural Decisions 109 + 110 + | Decision | Rationale | 111 + |----------|-----------| 112 + | **SQLite + flat files** | SQLite protects queue integrity; flat files allow agent filesystem access | 113 + | **stdio transport** | Most MCP clients spawn servers via stdio; HTTP can be added later | 114 + | **Single queue (no priorities)** | Prevents agents from abandoning low-priority work | 115 + | **Three-word session names** | Human-memorable, easy to communicate | 116 + | **Alphanumeric plan IDs** | Short (5 chars), collision-resistant | 117 + | **FTS5 for history search** | Built into SQLite, semantic search over completed plans | 118 + 119 + --- 120 + 121 + ## 3. Technology Stack 122 + 123 + ### 3.1 Core Dependencies 124 + 125 + | Component | Technology | Version | Rationale | 126 + |-----------|------------|---------|-----------| 127 + | **Runtime** | Node.js | โ‰ฅ22.5.0 | Required for built-in `node:sqlite` | 128 + | **MCP Framework** | @modelcontextprotocol/sdk | ^1.22.0 | Official SDK | 129 + | **MCP Transport** | @karashiiro/mcp | ^1.0.0 | Handles stdio + HTTP with sessions | 130 + | **Schema Validation** | Zod | ^3.25.0 | Runtime validation, env validation | 131 + | **Database** | node:sqlite | built-in | Zero deps, FTS5 support | 132 + | **Session Names** | unique-names-generator | ^4.7.1 | Pre-built dictionaries | 133 + | **Plan IDs** | nanoid | ^5.1.6 | Cryptographically secure, custom alphabets | 134 + | **App Directories** | env-paths | ^3.0.0 | Cross-platform standard directories | 135 + | **Logging** | pino | ^9.0.0 | Ultra-fast structured JSON logging | 136 + 137 + ### 3.2 Optional Dependencies (HTTP Transport) 138 + 139 + | Component | Technology | Version | 140 + |-----------|------------|---------| 141 + | **Web Framework** | hono | ^4.0.0 | 142 + | **Node Adapter** | @hono/node-server | ^1.0.0 | 143 + 144 + ### 3.3 Development Dependencies 145 + 146 + | Component | Technology | 147 + |-----------|------------| 148 + | **Build** | TypeScript ^5.8.0 | 149 + | **Dev Runner** | tsx | 150 + | **Testing** | Vitest | 151 + | **Log Formatting** | pino-pretty | 152 + 153 + ### 3.4 package.json 154 + 155 + ```json 156 + { 157 + "name": "9plan-mcp-server", 158 + "version": "1.0.0", 159 + "type": "module", 160 + "main": "dist/index.js", 161 + "scripts": { 162 + "build": "tsc", 163 + "dev": "tsx watch src/index.ts", 164 + "start": "node dist/index.js", 165 + "test": "vitest" 166 + }, 167 + "dependencies": { 168 + "@karashiiro/mcp": "^1.0.0", 169 + "@modelcontextprotocol/sdk": "^1.22.0", 170 + "env-paths": "^3.0.0", 171 + "nanoid": "^5.1.6", 172 + "pino": "^9.0.0", 173 + "unique-names-generator": "^4.7.1", 174 + "zod": "^3.25.0" 175 + }, 176 + "optionalDependencies": { 177 + "hono": "^4.0.0", 178 + "@hono/node-server": "^1.0.0" 179 + }, 180 + "devDependencies": { 181 + "@types/node": "^22.0.0", 182 + "pino-pretty": "^11.0.0", 183 + "tsx": "^4.0.0", 184 + "typescript": "^5.8.0", 185 + "vitest": "^3.0.0" 186 + }, 187 + "engines": { 188 + "node": ">=22.5.0" 189 + } 190 + } 191 + ``` 192 + 193 + --- 194 + 195 + ## 4. Project Structure 196 + 197 + ``` 198 + 9plan-mcp-server/ 199 + โ”œโ”€โ”€ src/ 200 + โ”‚ โ”œโ”€โ”€ index.ts # Entry point, transport setup 201 + โ”‚ โ”œโ”€โ”€ server.ts # McpServer factory, tool/prompt registration 202 + โ”‚ โ”œโ”€โ”€ types.ts # Core TypeScript interfaces 203 + โ”‚ โ”œโ”€โ”€ env.ts # Environment config with Zod 204 + โ”‚ โ”œโ”€โ”€ logger.ts # Pino logger configuration 205 + โ”‚ โ”‚ 206 + โ”‚ โ”œโ”€โ”€ tools/ 207 + โ”‚ โ”‚ โ”œโ”€โ”€ index.ts # Tool registration aggregator 208 + โ”‚ โ”‚ โ”œโ”€โ”€ session-create.ts 209 + โ”‚ โ”‚ โ”œโ”€โ”€ session-resume.ts 210 + โ”‚ โ”‚ โ”œโ”€โ”€ queue-add.ts 211 + โ”‚ โ”‚ โ”œโ”€โ”€ queue-pull.ts 212 + โ”‚ โ”‚ โ”œโ”€โ”€ plan-defer.ts 213 + โ”‚ โ”‚ โ”œโ”€โ”€ plan-complete.ts 214 + โ”‚ โ”‚ โ”œโ”€โ”€ plan-discard.ts 215 + โ”‚ โ”‚ โ”œโ”€โ”€ history-search.ts 216 + โ”‚ โ”‚ โ””โ”€โ”€ history-get.ts 217 + โ”‚ โ”‚ 218 + โ”‚ โ”œโ”€โ”€ prompts/ 219 + โ”‚ โ”‚ โ””โ”€โ”€ bootstrap.ts 220 + โ”‚ โ”‚ 221 + โ”‚ โ”œโ”€โ”€ db/ 222 + โ”‚ โ”‚ โ”œโ”€โ”€ schema.sql 223 + โ”‚ โ”‚ โ””โ”€โ”€ session-store.ts 224 + โ”‚ โ”‚ 225 + โ”‚ โ”œโ”€โ”€ generators/ 226 + โ”‚ โ”‚ โ”œโ”€โ”€ session-name.ts # Uses unique-names-generator 227 + โ”‚ โ”‚ โ””โ”€โ”€ plan-id.ts # Uses nanoid 228 + โ”‚ โ”‚ 229 + โ”‚ โ””โ”€โ”€ files/ 230 + โ”‚ โ””โ”€โ”€ plan-files.ts 231 + โ”‚ 232 + โ”œโ”€โ”€ tests/ 233 + โ”‚ โ”œโ”€โ”€ unit/ 234 + โ”‚ โ”‚ โ”œโ”€โ”€ session-store.test.ts 235 + โ”‚ โ”‚ โ”œโ”€โ”€ plan-files.test.ts 236 + โ”‚ โ”‚ โ””โ”€โ”€ generators.test.ts 237 + โ”‚ โ””โ”€โ”€ integration/ 238 + โ”‚ โ””โ”€โ”€ workflow.test.ts 239 + โ”‚ 240 + โ”œโ”€โ”€ package.json 241 + โ”œโ”€โ”€ tsconfig.json 242 + โ”œโ”€โ”€ vitest.config.ts 243 + โ””โ”€โ”€ README.md 244 + ``` 245 + 246 + --- 247 + 248 + ## 5. Data Model 249 + 250 + ### 5.1 SQLite Schema 251 + 252 + ```sql 253 + PRAGMA foreign_keys = ON; 254 + 255 + CREATE TABLE IF NOT EXISTS sessions ( 256 + name TEXT PRIMARY KEY, 257 + task_description TEXT, 258 + created_at TEXT DEFAULT (datetime('now')) 259 + ) STRICT; 260 + 261 + CREATE TABLE IF NOT EXISTS plans ( 262 + id TEXT PRIMARY KEY, 263 + session_name TEXT NOT NULL REFERENCES sessions(name) ON DELETE CASCADE, 264 + status TEXT NOT NULL CHECK (status IN ('queued', 'active', 'completed', 'discarded')), 265 + queue_position INTEGER, 266 + 267 + goal TEXT NOT NULL, 268 + context TEXT, 269 + inputs TEXT, 270 + outputs TEXT, 271 + approach TEXT, 272 + success_criteria TEXT, 273 + notes TEXT, 274 + outcome TEXT, 275 + 276 + created_at TEXT DEFAULT (datetime('now')), 277 + completed_at TEXT 278 + ) STRICT; 279 + 280 + CREATE INDEX IF NOT EXISTS idx_plans_queue 281 + ON plans(session_name, status, queue_position) 282 + WHERE status = 'queued'; 283 + 284 + CREATE INDEX IF NOT EXISTS idx_plans_active 285 + ON plans(session_name, status) 286 + WHERE status = 'active'; 287 + 288 + -- FTS5 for history search 289 + CREATE VIRTUAL TABLE IF NOT EXISTS plans_fts USING fts5( 290 + id, goal, context, inputs, outputs, outcome, 291 + content='plans', content_rowid='rowid' 292 + ); 293 + 294 + -- Triggers to keep FTS in sync 295 + CREATE TRIGGER IF NOT EXISTS plans_ai AFTER INSERT ON plans BEGIN 296 + INSERT INTO plans_fts(rowid, id, goal, context, inputs, outputs, outcome) 297 + VALUES (new.rowid, new.id, new.goal, new.context, new.inputs, new.outputs, new.outcome); 298 + END; 299 + 300 + CREATE TRIGGER IF NOT EXISTS plans_ad AFTER DELETE ON plans BEGIN 301 + INSERT INTO plans_fts(plans_fts, rowid, id, goal, context, inputs, outputs, outcome) 302 + VALUES ('delete', old.rowid, old.id, old.goal, old.context, old.inputs, old.outputs, old.outcome); 303 + END; 304 + 305 + CREATE TRIGGER IF NOT EXISTS plans_au AFTER UPDATE ON plans BEGIN 306 + INSERT INTO plans_fts(plans_fts, rowid, id, goal, context, inputs, outputs, outcome) 307 + VALUES ('delete', old.rowid, old.id, old.goal, old.context, old.inputs, old.outputs, old.outcome); 308 + INSERT INTO plans_fts(rowid, id, goal, context, inputs, outputs, outcome) 309 + VALUES (new.rowid, new.id, new.goal, new.context, new.inputs, new.outputs, new.outcome); 310 + END; 311 + ``` 312 + 313 + ### 5.2 TypeScript Interfaces 314 + 315 + ```typescript 316 + export type PlanStatus = 'queued' | 'active' | 'completed' | 'discarded'; 317 + 318 + export interface Session { 319 + name: string; 320 + taskDescription: string | null; 321 + createdAt: string; 322 + } 323 + 324 + export interface Plan { 325 + id: string; 326 + sessionName: string; 327 + status: PlanStatus; 328 + queuePosition: number | null; 329 + goal: string; 330 + context: string | null; 331 + inputs: string | null; 332 + outputs: string | null; 333 + approach: string | null; 334 + successCriteria: string | null; 335 + notes: string | null; 336 + outcome: string | null; 337 + createdAt: string; 338 + completedAt: string | null; 339 + } 340 + 341 + export interface QueuedPlan { 342 + id: string; 343 + goal: string; 344 + queuePosition: number; 345 + } 346 + 347 + export interface SessionState { 348 + sessionName: string; 349 + sessionPath: string; 350 + taskDescription: string | null; 351 + queue: QueuedPlan[]; 352 + activePlan: { id: string; filePath: string } | null; 353 + completedCount: number; 354 + } 355 + 356 + export interface HistoryMatch { 357 + id: string; 358 + goal: string; 359 + outcome: string | null; 360 + relevanceScore: number; 361 + } 362 + ``` 363 + 364 + --- 365 + 366 + ## 6. Tool Specifications 367 + 368 + All tools use the prefix `9plan_` with snake_case. 369 + 370 + ### 6.1 Session Tools 371 + 372 + #### `9plan_session_create` 373 + 374 + Creates a new session with a randomly-generated three-word identifier. 375 + 376 + **Input**: `{ task_description?: string }` 377 + 378 + **Output**: `{ session_name: string, session_path: string }` 379 + 380 + **Behavior**: 381 + 1. Generate unique three-word name 382 + 2. Create session directory with `session.db` and `plans/` 383 + 3. Initialize SQLite with schema 384 + 4. Return session name and path 385 + 386 + --- 387 + 388 + #### `9plan_session_resume` 389 + 390 + Loads an existing session by name. 391 + 392 + **Input**: `{ session_name: string }` (format: `word-word-word`) 393 + 394 + **Output**: Full session state including queue, active plan, and completed count. 395 + 396 + --- 397 + 398 + ### 6.2 Queue Tools 399 + 400 + #### `9plan_queue_add` 401 + 402 + Adds a new plan to the queue. 403 + 404 + **Input**: 405 + ```typescript 406 + { 407 + context: string, // Where this plan fits in overall task 408 + goal: string, // What this plan accomplishes 409 + approach: string, // How to accomplish the goal 410 + success_criteria: string, 411 + inputs?: string, // Dependencies from other plans 412 + outputs?: string, // What this produces 413 + position?: 'front' | 'back' // Default: 'back' 414 + } 415 + ``` 416 + 417 + **Output**: `{ plan_id: string, plan_path: string, queue_position: number }` 418 + 419 + **Position Semantics**: 420 + - `front`: Blocking work that must complete first 421 + - `back`: Work that should happen eventually 422 + 423 + --- 424 + 425 + #### `9plan_queue_pull` 426 + 427 + Removes the front plan from queue and marks it active. 428 + 429 + **Input**: None 430 + 431 + **Output**: `{ plan_id: string, plan_path: string, goal: string }` 432 + 433 + **Errors**: 434 + - `PLAN_ALREADY_ACTIVE`: Must complete/defer/discard current plan first 435 + - `QUEUE_EMPTY`: Task complete! ๐ŸŽ‰ 436 + 437 + --- 438 + 439 + ### 6.3 Plan Lifecycle Tools 440 + 441 + #### `9plan_plan_defer` 442 + 443 + Returns active plan to queue without completing it. 444 + 445 + **Input**: `{ reason: string, position?: 'front' | 'back' }` 446 + 447 + **Behavior**: Appends reason to Notes section with timestamp, returns to queue. 448 + 449 + **Use Cases**: 450 + - **Blocked**: Can't proceed โ†’ `position: 'front'` 451 + - **Decomposition**: Breaking into subplans โ†’ `position: 'back'` 452 + 453 + --- 454 + 455 + #### `9plan_plan_complete` 456 + 457 + Marks active plan as done. 458 + 459 + **Input**: `{ outcome: string }` โ€” Summary including outputs produced 460 + 461 + **Behavior**: 462 + 1. Store outcome in database 463 + 2. Index into FTS5 for history search 464 + 3. Delete plan file 465 + 4. Return queue status 466 + 467 + --- 468 + 469 + #### `9plan_plan_discard` 470 + 471 + Abandons active plan without completing. 472 + 473 + **Input**: `{ reason: string }` 474 + 475 + **Behavior**: Marks as discarded (NOT in searchable history), deletes file. 476 + 477 + **Use When**: Plan is obsolete, unnecessary, or misconceived. 478 + 479 + --- 480 + 481 + ### 6.4 History Tools 482 + 483 + #### `9plan_history_search` 484 + 485 + Searches completed plans for outputs matching a query. 486 + 487 + **Input**: `{ query: string, max_results?: number }` 488 + 489 + **Output**: Array of matches with `{ id, goal, outcome, relevance_rank }` 490 + 491 + Uses FTS5 MATCH with BM25 ranking across goal, context, inputs, outputs, outcome. 492 + 493 + --- 494 + 495 + #### `9plan_history_get` 496 + 497 + Retrieves a specific completed plan by ID. 498 + 499 + **Input**: `{ plan_id: string }` (5 alphanumeric chars) 500 + 501 + **Output**: Full plan content including outcome. 502 + 503 + --- 504 + 505 + ## 7. Prompt Specifications 506 + 507 + ### `bootstrap` 508 + 509 + Guides initial session setup and planning decomposition. 510 + 511 + **Arguments**: `{ task?: string }` 512 + 513 + **Content**: 514 + ``` 515 + You are helping the user set up a new 9plan session for a complex task. 516 + 517 + WORKFLOW: 518 + 1. CLARIFY: Ask questions to understand scope, components, dependencies 519 + 2. CREATE SESSION: Use 9plan_session_create with task description 520 + 3. DECOMPOSE: Break into discrete, self-contained plans 521 + 4. CAPTURE DEPENDENCIES: Specify inputs/outputs for each plan 522 + 5. ENQUEUE: Add plans with 9plan_queue_add (respect dependency order) 523 + 6. START WORK: Pull first plan with 9plan_queue_pull 524 + 525 + PLAN QUALITY CHECKLIST: 526 + โ–ก Goal is specific and measurable 527 + โ–ก Approach has concrete steps 528 + โ–ก Success criteria are observable 529 + โ–ก Inputs reference other plans by description 530 + โ–ก Outputs are specific enough to search for 531 + 532 + NOTE: Adding A, B, C at 'front' gives [C, B, A] โ€” add in reverse order if needed. 533 + ``` 534 + 535 + --- 536 + 537 + ## 8. File Formats 538 + 539 + ### 8.1 Plan File Format 540 + 541 + **Location**: `~/.9plan/sessions/{session-name}/plans/{plan-id}.txt` 542 + 543 + ``` 544 + # Context 545 + {Where this plan fits in the overall task} 546 + 547 + # Goal 548 + {What this plan accomplishes} 549 + 550 + # Inputs 551 + {Dependencies - format: "- description: source"} 552 + 553 + # Outputs 554 + {What this produces - format: "- description: details"} 555 + 556 + # Approach 557 + {Detailed, actionable steps} 558 + 559 + # Success Criteria 560 + {Observable completion conditions} 561 + 562 + # Notes 563 + {Working scratchpad - updated during execution} 564 + ``` 565 + 566 + --- 567 + 568 + ## 9. Logging 569 + 570 + ### 9.1 Logger Setup 571 + 572 + ```typescript 573 + // src/logger.ts 574 + import pino from 'pino'; 575 + import { env } from './env.js'; 576 + 577 + export const logger = process.env.NODE_ENV === 'development' 578 + ? pino({ 579 + level: env.NINEPLAN_LOG_LEVEL, 580 + transport: { 581 + target: 'pino-pretty', 582 + options: { colorize: true, translateTime: 'SYS:standard' }, 583 + }, 584 + }) 585 + : pino({ level: env.NINEPLAN_LOG_LEVEL }); 586 + 587 + export type Logger = pino.Logger; 588 + ``` 589 + 590 + ### 9.2 Session-Scoped Child Loggers 591 + 592 + ```typescript 593 + export class SessionStore { 594 + private log: Logger; 595 + 596 + constructor(sessionName: string) { 597 + this.log = logger.child({ session: sessionName }); 598 + } 599 + 600 + addPlan(plan: Plan): void { 601 + this.log.info({ planId: plan.id, goal: plan.goal }, 'Plan added'); 602 + } 603 + } 604 + ``` 605 + 606 + ### 9.3 Log Levels 607 + 608 + | Level | Usage | 609 + |-------|-------| 610 + | `debug` | SQL queries, queue state | 611 + | `info` | Session created, plan pulled/completed | 612 + | `warn` | Collision retry, missing optional field | 613 + | `error` | DB error, file write failure | 614 + 615 + --- 616 + 617 + ## 10. Error Handling 618 + 619 + ### 10.1 Error Categories 620 + 621 + | Category | Description | 622 + |----------|-------------| 623 + | `SESSION_NOT_FOUND` | Session does not exist | 624 + | `PLAN_NOT_FOUND` | Plan ID does not exist | 625 + | `NO_ACTIVE_PLAN` | Operation requires active plan | 626 + | `PLAN_ALREADY_ACTIVE` | Cannot pull; another plan is active | 627 + | `QUEUE_EMPTY` | Queue is empty (task complete) | 628 + | `VALIDATION_ERROR` | Input validation failed | 629 + | `FILE_SYSTEM_ERROR` | Filesystem operation failed | 630 + | `DATABASE_ERROR` | SQLite operation failed | 631 + 632 + ### 10.2 Error Response Format 633 + 634 + ``` 635 + [Session: {name}] Error: {category} 636 + 637 + {message} 638 + 639 + Suggested action: {suggestion} 640 + ``` 641 + 642 + --- 643 + 644 + ## 11. Configuration 645 + 646 + ### 11.1 Environment Variables 647 + 648 + ```typescript 649 + // src/env.ts 650 + import { z } from 'zod'; 651 + import envPaths from 'env-paths'; 652 + import { join } from 'node:path'; 653 + 654 + const paths = envPaths('9plan', { suffix: '' }); 655 + 656 + const envSchema = z.object({ 657 + NINEPLAN_SESSIONS_PATH: z.string().default(join(paths.data, 'sessions')), 658 + NINEPLAN_LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'), 659 + NINEPLAN_TRANSPORT: z.enum(['stdio', 'http']).default('stdio'), 660 + NINEPLAN_HTTP_PORT: z.coerce.number().default(8080), 661 + NINEPLAN_HTTP_HOST: z.string().default('127.0.0.1'), 662 + }); 663 + 664 + export const env = envSchema.parse(process.env); 665 + ``` 666 + 667 + ### 11.2 Entry Point 668 + 669 + ```typescript 670 + // src/index.ts 671 + import { serveStdio } from "@karashiiro/mcp/stdio"; 672 + import { serveHttp } from "@karashiiro/mcp/http"; 673 + import { createServer } from "./server.js"; 674 + import { logger } from "./logger.js"; 675 + import { env } from "./env.js"; 676 + 677 + if (env.NINEPLAN_TRANSPORT === "http") { 678 + const handle = await serveHttp(createServer, { 679 + port: env.NINEPLAN_HTTP_PORT, 680 + host: env.NINEPLAN_HTTP_HOST, 681 + endpoint: "/mcp", 682 + }); 683 + 684 + logger.info({ port: env.NINEPLAN_HTTP_PORT }, '9plan running (HTTP)'); 685 + 686 + process.on("SIGINT", async () => { 687 + await handle.close(); 688 + process.exit(0); 689 + }); 690 + } else { 691 + logger.info('9plan running (stdio)'); 692 + await serveStdio(createServer); 693 + } 694 + ``` 695 + 696 + ### 11.3 MCP Client Configuration 697 + 698 + **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`): 699 + ```json 700 + { 701 + "mcpServers": { 702 + "9plan": { 703 + "command": "node", 704 + "args": ["/path/to/9plan-mcp-server/dist/index.js"] 705 + } 706 + } 707 + } 708 + ``` 709 + 710 + --- 711 + 712 + ## 12. Dependency Injection & Testability 713 + 714 + We use **constructor injection** for testability without a DI framework. This keeps the codebase simple while allowing full mockability in tests. 715 + 716 + ### 12.1 The Pattern 717 + 718 + All classes accept their dependencies via an options object in the constructor: 719 + 720 + ```typescript 721 + // src/types.ts - Dependency interfaces 722 + export interface SessionStoreDeps { 723 + logger: Logger; 724 + generatePlanId: () => string; 725 + planFiles: PlanFileHandler; 726 + } 727 + 728 + export interface PlanFileHandlerDeps { 729 + logger: Logger; 730 + } 731 + ``` 732 + 733 + ### 12.2 Implementation Example 734 + 735 + ```typescript 736 + // src/db/session-store.ts 737 + import type { Logger } from '../logger.js'; 738 + import type { Plan, SessionStoreDeps } from '../types.js'; 739 + 740 + export class SessionStore { 741 + private log: Logger; 742 + private generatePlanId: () => string; 743 + private planFiles: PlanFileHandler; 744 + 745 + constructor( 746 + private sessionName: string, 747 + private sessionPath: string, 748 + deps: SessionStoreDeps, 749 + ) { 750 + this.log = deps.logger.child({ session: sessionName }); 751 + this.generatePlanId = deps.generatePlanId; 752 + this.planFiles = deps.planFiles; 753 + } 754 + 755 + addPlan(input: PlanInput): Plan { 756 + const id = this.generatePlanId(); 757 + this.log.info({ planId: id, goal: input.goal }, 'Adding plan'); 758 + // ... implementation 759 + } 760 + } 761 + ``` 762 + 763 + ### 12.3 Production Wiring 764 + 765 + ```typescript 766 + // src/container.ts - Simple factory functions, no framework needed 767 + import { logger } from './logger.js'; 768 + import { SessionStore } from './db/session-store.js'; 769 + import { PlanFileHandler } from './files/plan-files.js'; 770 + import { generatePlanId } from './generators/plan-id.js'; 771 + 772 + export function createPlanFileHandler(sessionPath: string): PlanFileHandler { 773 + return new PlanFileHandler(sessionPath, { logger }); 774 + } 775 + 776 + export function createSessionStore( 777 + sessionName: string, 778 + sessionPath: string, 779 + ): SessionStore { 780 + return new SessionStore(sessionName, sessionPath, { 781 + logger, 782 + generatePlanId, 783 + planFiles: createPlanFileHandler(sessionPath), 784 + }); 785 + } 786 + ``` 787 + 788 + ### 12.4 Testing with Mocks 789 + 790 + ```typescript 791 + // tests/unit/session-store.test.ts 792 + import { describe, it, expect, vi, beforeEach } from 'vitest'; 793 + import { SessionStore } from '../../src/db/session-store.js'; 794 + 795 + describe('SessionStore', () => { 796 + // Mock factory - creates fresh mocks for each test 797 + const createMocks = () => ({ 798 + logger: { 799 + info: vi.fn(), 800 + error: vi.fn(), 801 + warn: vi.fn(), 802 + debug: vi.fn(), 803 + child: vi.fn(() => ({ 804 + info: vi.fn(), 805 + error: vi.fn(), 806 + warn: vi.fn(), 807 + debug: vi.fn(), 808 + })), 809 + }, 810 + generatePlanId: vi.fn(() => 'test1'), 811 + planFiles: { 812 + write: vi.fn(), 813 + read: vi.fn(), 814 + delete: vi.fn(), 815 + exists: vi.fn(() => false), 816 + }, 817 + }); 818 + 819 + it('should generate ID and log when adding plan', () => { 820 + const mocks = createMocks(); 821 + const store = new SessionStore('test-session', '/tmp/test', mocks); 822 + 823 + store.addPlan({ 824 + goal: 'Test the thing', 825 + context: 'Testing context', 826 + approach: 'Test approach', 827 + successCriteria: 'Tests pass', 828 + }); 829 + 830 + expect(mocks.generatePlanId).toHaveBeenCalled(); 831 + expect(mocks.logger.child).toHaveBeenCalledWith({ session: 'test-session' }); 832 + }); 833 + 834 + it('should write plan file when adding plan', () => { 835 + const mocks = createMocks(); 836 + const store = new SessionStore('test-session', '/tmp/test', mocks); 837 + 838 + store.addPlan({ 839 + goal: 'Test the thing', 840 + context: 'Testing context', 841 + approach: 'Test approach', 842 + successCriteria: 'Tests pass', 843 + }); 844 + 845 + expect(mocks.planFiles.write).toHaveBeenCalled(); 846 + }); 847 + 848 + it('should throw when plan file write fails', () => { 849 + const mocks = createMocks(); 850 + mocks.planFiles.write.mockImplementation(() => { 851 + throw new Error('Disk full'); 852 + }); 853 + 854 + const store = new SessionStore('test-session', '/tmp/test', mocks); 855 + 856 + expect(() => store.addPlan({ 857 + goal: 'Test', 858 + context: 'Context', 859 + approach: 'Approach', 860 + successCriteria: 'Criteria', 861 + })).toThrow('Disk full'); 862 + }); 863 + }); 864 + ``` 865 + 866 + ### 12.5 Testing Tools 867 + 868 + Tools receive their dependencies through the session store: 869 + 870 + ```typescript 871 + // tests/unit/tools/queue-add.test.ts 872 + import { describe, it, expect, vi } from 'vitest'; 873 + import { handleQueueAdd } from '../../../src/tools/queue-add.js'; 874 + 875 + describe('9plan_queue_add', () => { 876 + it('should return plan ID and path on success', async () => { 877 + const mockStore = { 878 + addPlan: vi.fn(() => ({ 879 + id: 'abc12', 880 + queuePosition: 1, 881 + })), 882 + getSessionPath: vi.fn(() => '/tmp/sessions/test-session'), 883 + }; 884 + 885 + const result = await handleQueueAdd(mockStore, { 886 + goal: 'Do something', 887 + context: 'Context here', 888 + approach: 'Step by step', 889 + success_criteria: 'It works', 890 + }); 891 + 892 + expect(result.plan_id).toBe('abc12'); 893 + expect(result.plan_path).toContain('abc12.txt'); 894 + }); 895 + }); 896 + ``` 897 + 898 + ### 12.6 Why Not a DI Container? 899 + 900 + | Aspect | Manual Injection | DI Container (inversify/tsyringe) | 901 + |--------|------------------|-----------------------------------| 902 + | **Testability** | โœ… Full | โœ… Full | 903 + | **Lines of setup** | ~20 | ~80+ | 904 + | **Extra dependencies** | None | reflect-metadata, decorators | 905 + | **Decorator noise** | None | `@injectable()` on every class | 906 + | **Type safety** | โœ… Native TS | Requires Symbol tokens | 907 + | **Learning curve** | None | Moderate | 908 + 909 + For a project with <10 injectable services, manual wiring is simpler and equally testable. 910 + 911 + --- 912 + 913 + ## 13. Implementation Phases 914 + 915 + ### Phase 1: Foundation 916 + 917 + - [ ] Project scaffolding (package.json, tsconfig.json) 918 + - [ ] Install dependencies 919 + - [ ] Logger setup 920 + - [ ] TypeScript interfaces (including dep interfaces) 921 + - [ ] SQLite schema 922 + - [ ] Session name generator (unique-names-generator) 923 + - [ ] Plan ID generator (nanoid) 924 + - [ ] Entry point with transport 925 + 926 + ### Phase 2: Session Tools 927 + 928 + - [ ] `9plan_session_create` 929 + - [ ] `9plan_session_resume` 930 + - [ ] Session directory management 931 + - [ ] Database initialization 932 + - [ ] Container/factory functions 933 + 934 + ### Phase 3: Queue Tools 935 + 936 + - [ ] `9plan_queue_add` 937 + - [ ] `9plan_queue_pull` 938 + - [ ] Plan file creation/reading 939 + - [ ] Queue position management 940 + - [ ] Unit tests for queue operations 941 + 942 + ### Phase 4: Plan Lifecycle Tools 943 + 944 + - [ ] `9plan_plan_defer` 945 + - [ ] `9plan_plan_complete` 946 + - [ ] `9plan_plan_discard` 947 + - [ ] Notes accumulation 948 + - [ ] FTS5 indexing 949 + - [ ] Unit tests for lifecycle 950 + 951 + ### Phase 5: History Tools 952 + 953 + - [ ] `9plan_history_search` 954 + - [ ] `9plan_history_get` 955 + - [ ] FTS5 query implementation 956 + - [ ] Unit tests for search 957 + 958 + ### Phase 6: Polish 959 + 960 + - [ ] `bootstrap` prompt 961 + - [ ] Server instructions 962 + - [ ] Error messages 963 + - [ ] Integration tests 964 + - [ ] Documentation 965 + 966 + --- 967 + 968 + ## 14. Known Limitations 969 + 970 + ### Front-Insertion Order Reversal 971 + 972 + Adding A, B, C at front gives `[C, B, A, ...]`. Add subplans in reverse order to achieve correct sequence. 973 + 974 + ### Context Compaction Mid-Plan 975 + 976 + If compaction occurs while executing a plan, the agent loses session awareness. Session name is included in all tool responses to aid recovery. 977 + 978 + ### Single Agent Per Session 979 + 980 + No concurrent access supportโ€”designed for single-agent workflows. 981 + 982 + --- 983 + 984 + ## Appendix: Server Instructions 985 + 986 + The MCP server's `instructions` field: 987 + 988 + ``` 989 + 9plan manages session-scoped work queues for agent task sequencing. 990 + 991 + QUICK START: 992 + 1. Create session: 9plan_session_create 993 + 2. Add plans: 9plan_queue_add ('front' for blocking, 'back' for eventual) 994 + 3. Get work: 9plan_queue_pull 995 + 4. Execute the plan (read file for full context) 996 + 5. Complete: 9plan_plan_complete (or defer/discard) 997 + 6. Repeat until queue is empty 998 + 999 + PLAN FILE FORMAT: 1000 + # Context, # Goal, # Inputs, # Outputs, # Approach, # Success Criteria, # Notes 1001 + 1002 + DEPENDENCY RESOLUTION: 1003 + - Use 9plan_history_search to find outputs from completed plans 1004 + - Inputs reference plans by description, not ID 1005 + 1006 + SESSION RECOVERY: 1007 + - All responses include [Session: name] prefix 1008 + - Resume with: 9plan_session_resume 1009 + ``` 1010 + 1011 + --- 1012 + 1013 + *End of Implementation Design Document*
+440
docs/testing-philosophy.md
··· 1 + # 9plan Testing Philosophy 2 + 3 + This document explains what we test, why we test it, and how to maintain testing consistency as the codebase evolves. 4 + 5 + --- 6 + 7 + ## Testing Goals 8 + 9 + ### Primary Goals 10 + 11 + 1. **Protect invariants**: Queue ordering, active plan exclusivity, session isolation 12 + 2. **Verify state transitions**: Plan lifecycle (queued โ†’ active โ†’ completed/deferred/discarded) 13 + 3. **Ensure data integrity**: File/database consistency, FTS indexing 14 + 4. **Catch regressions**: Prevent working functionality from breaking 15 + 16 + ### Non-Goals 17 + 18 + - **100% coverage**: We don't chase coverage metrics; we test what matters 19 + - **Testing library internals**: We trust nanoid, unique-names-generator, SQLite 20 + - **Performance benchmarks**: Not a primary concern for this tool's use case 21 + 22 + --- 23 + 24 + ## What We Test 25 + 26 + ### Must Test: Invariants 27 + 28 + These are properties that must ALWAYS hold. Violations indicate serious bugs. 29 + 30 + | Invariant | Test Approach | 31 + |-----------|---------------| 32 + | Only one plan can be active at a time | Test that `queue_pull` with active plan returns error | 33 + | Queue order is preserved | Add plans, verify pull order matches expected | 34 + | Plan IDs are unique within session | Generate many IDs, check for collisions | 35 + | Session names are unique | Generate many names, check for collisions | 36 + | Completed plans are searchable | Complete a plan, search for its outputs | 37 + | Plan files exist for queued/active plans | After add, verify file exists; after complete, verify deleted | 38 + 39 + ### Must Test: State Transitions 40 + 41 + Each plan lifecycle transition should be tested. 42 + 43 + ``` 44 + Created (queued) 45 + โ”‚ 46 + โ”œโ”€ pull โ”€โ”€โ†’ Active 47 + โ”‚ โ”‚ 48 + โ”‚ โ”œโ”€ complete โ”€โ”€โ†’ Completed (in history) 49 + โ”‚ โ”‚ 50 + โ”‚ โ”œโ”€ defer โ”€โ”€โ†’ Queued (back in queue) 51 + โ”‚ โ”‚ 52 + โ”‚ โ””โ”€ discard โ”€โ”€โ†’ Discarded (gone) 53 + โ”‚ 54 + โ””โ”€ (invalid: can't complete/defer/discard from queued) 55 + ``` 56 + 57 + Test each valid transition AND test that invalid transitions return errors. 58 + 59 + ### Must Test: Error Conditions 60 + 61 + | Scenario | Expected Behavior | 62 + |----------|-------------------| 63 + | Pull with no plans in queue | Returns "queue empty" message | 64 + | Pull with active plan already | Returns error | 65 + | Complete with no active plan | Returns error | 66 + | Defer with no active plan | Returns error | 67 + | Discard with no active plan | Returns error | 68 + | Resume non-existent session | Returns error | 69 + | History search with no matches | Returns empty results | 70 + | History get with invalid ID | Returns error | 71 + 72 + ### Should Test: Integration Scenarios 73 + 74 + These verify components work together correctly. 75 + 76 + | Scenario | What It Tests | 77 + |----------|---------------| 78 + | Full plan lifecycle | Create โ†’ Pull โ†’ Complete โ†’ Search | 79 + | Decomposition workflow | Pull โ†’ Add children โ†’ Defer โ†’ Pull children โ†’ Complete children โ†’ Pull parent | 80 + | Dependency resolution | Complete plan A with outputs โ†’ Add plan B with inputs โ†’ Resolve via search | 81 + | Session resume | Create โ†’ Add plans โ†’ "Restart" โ†’ Resume โ†’ Verify state | 82 + | Progress checkpointing | Pull โ†’ Update notes โ†’ Verify notes persist | 83 + 84 + ### Should Test: Edge Cases 85 + 86 + | Edge Case | Why It Matters | 87 + |-----------|----------------| 88 + | Empty inputs/outputs fields | Optional fields shouldn't cause errors | 89 + | Very long plan content | No truncation or corruption | 90 + | Special characters in plans | Proper escaping, no injection | 91 + | Unicode in session names/plans | Correct handling throughout | 92 + | Plan with same goal added twice | Should work (different IDs) | 93 + | Defer then pull same plan | Should work, notes accumulate | 94 + 95 + ### Don't Test: External Libraries 96 + 97 + We trust these to work correctly: 98 + 99 + - **nanoid**: Cryptographic randomness 100 + - **unique-names-generator**: Word selection 101 + - **SQLite**: Database operations 102 + - **Zod**: Schema validation 103 + - **Pino**: Logging 104 + 105 + If we find bugs in these, we report upstream rather than work around with tests. 106 + 107 + --- 108 + 109 + ## Testing Layers 110 + 111 + ### Unit Tests 112 + 113 + **Scope**: Individual functions and classes in isolation. 114 + 115 + **Location**: `tests/unit/` 116 + 117 + **Dependencies**: Mocked via dependency injection. 118 + 119 + **What to test**: 120 + - `SessionStore` methods with mocked file handler and ID generator 121 + - `PlanFileHandler` with mocked filesystem (or temp directory) 122 + - ID/name generators (basic functionality, collision detection) 123 + 124 + **Example**: 125 + ```typescript 126 + describe('SessionStore.addPlan', () => { 127 + it('should generate unique ID', () => { 128 + const mocks = createMocks(); 129 + const store = new SessionStore('test', '/tmp', mocks); 130 + store.addPlan({ goal: 'Test', ... }); 131 + expect(mocks.generatePlanId).toHaveBeenCalled(); 132 + }); 133 + 134 + it('should write plan file', () => { 135 + const mocks = createMocks(); 136 + const store = new SessionStore('test', '/tmp', mocks); 137 + store.addPlan({ goal: 'Test', ... }); 138 + expect(mocks.planFiles.write).toHaveBeenCalled(); 139 + }); 140 + }); 141 + ``` 142 + 143 + ### Integration Tests 144 + 145 + **Scope**: Multiple components working together with real implementations. 146 + 147 + **Location**: `tests/integration/` 148 + 149 + **Dependencies**: Real SQLite (in-memory or temp file), real filesystem (temp directory). 150 + 151 + **What to test**: 152 + - Complete workflows (bootstrap โ†’ execute โ†’ complete) 153 + - File/database consistency 154 + - FTS5 search accuracy 155 + 156 + **Example**: 157 + ```typescript 158 + describe('Full plan lifecycle', () => { 159 + let sessionPath: string; 160 + let store: SessionStore; 161 + 162 + beforeEach(() => { 163 + sessionPath = fs.mkdtempSync('/tmp/9plan-test-'); 164 + store = createSessionStore('test-session', sessionPath); 165 + }); 166 + 167 + afterEach(() => { 168 + fs.rmSync(sessionPath, { recursive: true }); 169 + }); 170 + 171 + it('should complete full lifecycle', () => { 172 + // Add plan 173 + const { id } = store.addPlan({ goal: 'Test goal', ... }); 174 + 175 + // Pull (activates) 176 + const pulled = store.pull(); 177 + expect(pulled.id).toBe(id); 178 + 179 + // Complete 180 + store.complete({ outcome: 'Done' }); 181 + 182 + // Search should find it 183 + const results = store.searchHistory('Test goal'); 184 + expect(results).toHaveLength(1); 185 + expect(results[0].id).toBe(id); 186 + }); 187 + }); 188 + ``` 189 + 190 + ### Tool Tests 191 + 192 + **Scope**: MCP tool handlers with mocked store. 193 + 194 + **Location**: `tests/unit/tools/` 195 + 196 + **What to test**: 197 + - Input validation (Zod schemas) 198 + - Correct store method calls 199 + - Response formatting 200 + 201 + **Example**: 202 + ```typescript 203 + describe('9plan_queue_add', () => { 204 + it('should validate required fields', async () => { 205 + const mockStore = createMockStore(); 206 + 207 + await expect(handleQueueAdd(mockStore, { 208 + goal: 'Test', 209 + // missing: context, approach, success_criteria 210 + })).rejects.toThrow(/required/); 211 + }); 212 + 213 + it('should return plan ID and path', async () => { 214 + const mockStore = createMockStore(); 215 + mockStore.addPlan.mockReturnValue({ id: 'abc12', queuePosition: 1 }); 216 + 217 + const result = await handleQueueAdd(mockStore, validPlanInput); 218 + 219 + expect(result.plan_id).toBe('abc12'); 220 + expect(result.plan_path).toContain('abc12.txt'); 221 + }); 222 + }); 223 + ``` 224 + 225 + --- 226 + 227 + ## Test Utilities 228 + 229 + ### Mock Factory Pattern 230 + 231 + Create fresh mocks for each test to avoid state leakage: 232 + 233 + ```typescript 234 + // tests/helpers/mocks.ts 235 + export const createMocks = () => ({ 236 + logger: { 237 + info: vi.fn(), 238 + error: vi.fn(), 239 + warn: vi.fn(), 240 + debug: vi.fn(), 241 + child: vi.fn().mockReturnThis(), 242 + }, 243 + generatePlanId: vi.fn(() => `test${Math.random().toString(36).slice(2, 5)}`), 244 + planFiles: { 245 + write: vi.fn(), 246 + read: vi.fn(), 247 + delete: vi.fn(), 248 + exists: vi.fn(() => true), 249 + }, 250 + }); 251 + ``` 252 + 253 + ### Temp Directory Helper 254 + 255 + For integration tests that need real filesystem: 256 + 257 + ```typescript 258 + // tests/helpers/temp.ts 259 + import { mkdtempSync, rmSync } from 'fs'; 260 + import { tmpdir } from 'os'; 261 + import { join } from 'path'; 262 + 263 + export function withTempDir(fn: (dir: string) => void | Promise<void>) { 264 + const dir = mkdtempSync(join(tmpdir(), '9plan-test-')); 265 + try { 266 + return fn(dir); 267 + } finally { 268 + rmSync(dir, { recursive: true, force: true }); 269 + } 270 + } 271 + ``` 272 + 273 + ### Test Fixtures 274 + 275 + Standard plan data for consistent testing: 276 + 277 + ```typescript 278 + // tests/fixtures/plans.ts 279 + export const validPlan = { 280 + context: 'Test context for unit testing', 281 + goal: 'Verify the system works', 282 + approach: 'Run assertions against expected behavior', 283 + successCriteria: 'All assertions pass', 284 + inputs: '', 285 + outputs: 'Test results', 286 + }; 287 + 288 + export const planWithDependencies = { 289 + context: 'Building feature that depends on auth', 290 + goal: 'Implement feature X', 291 + approach: 'Use auth module', 292 + successCriteria: 'Feature works with auth', 293 + inputs: 'auth_client module from auth work', 294 + outputs: 'Feature X implementation', 295 + }; 296 + ``` 297 + 298 + --- 299 + 300 + ## Test Organization 301 + 302 + ### File Structure 303 + 304 + ``` 305 + tests/ 306 + โ”œโ”€โ”€ unit/ 307 + โ”‚ โ”œโ”€โ”€ db/ 308 + โ”‚ โ”‚ โ””โ”€โ”€ session-store.test.ts 309 + โ”‚ โ”œโ”€โ”€ files/ 310 + โ”‚ โ”‚ โ””โ”€โ”€ plan-files.test.ts 311 + โ”‚ โ”œโ”€โ”€ generators/ 312 + โ”‚ โ”‚ โ”œโ”€โ”€ session-name.test.ts 313 + โ”‚ โ”‚ โ””โ”€โ”€ plan-id.test.ts 314 + โ”‚ โ””โ”€โ”€ tools/ 315 + โ”‚ โ”œโ”€โ”€ session-create.test.ts 316 + โ”‚ โ”œโ”€โ”€ queue-add.test.ts 317 + โ”‚ โ””โ”€โ”€ ... 318 + โ”œโ”€โ”€ integration/ 319 + โ”‚ โ”œโ”€โ”€ lifecycle.test.ts 320 + โ”‚ โ”œโ”€โ”€ decomposition.test.ts 321 + โ”‚ โ””โ”€โ”€ history-search.test.ts 322 + โ”œโ”€โ”€ fixtures/ 323 + โ”‚ โ””โ”€โ”€ plans.ts 324 + โ””โ”€โ”€ helpers/ 325 + โ”œโ”€โ”€ mocks.ts 326 + โ””โ”€โ”€ temp.ts 327 + ``` 328 + 329 + ### Naming Conventions 330 + 331 + - Test files: `{module}.test.ts` 332 + - Describe blocks: Module or function name 333 + - Test names: `should {expected behavior} [when {condition}]` 334 + 335 + ```typescript 336 + describe('SessionStore', () => { 337 + describe('addPlan', () => { 338 + it('should generate unique plan ID', () => {}); 339 + it('should add plan to back of queue by default', () => {}); 340 + it('should add plan to front of queue when position is front', () => {}); 341 + it('should throw when required fields missing', () => {}); 342 + }); 343 + }); 344 + ``` 345 + 346 + --- 347 + 348 + ## Running Tests 349 + 350 + ### All Tests 351 + 352 + ```bash 353 + npm test 354 + ``` 355 + 356 + ### Watch Mode (Development) 357 + 358 + ```bash 359 + npm test -- --watch 360 + ``` 361 + 362 + ### Specific File 363 + 364 + ```bash 365 + npm test -- tests/unit/db/session-store.test.ts 366 + ``` 367 + 368 + ### Coverage Report 369 + 370 + ```bash 371 + npm test -- --coverage 372 + ``` 373 + 374 + ### CI Configuration 375 + 376 + ```yaml 377 + # .github/workflows/test.yml 378 + test: 379 + runs-on: ubuntu-latest 380 + steps: 381 + - uses: actions/checkout@v4 382 + - uses: actions/setup-node@v4 383 + with: 384 + node-version: '22' 385 + - run: npm ci 386 + - run: npm test 387 + ``` 388 + 389 + --- 390 + 391 + ## When to Add Tests 392 + 393 + ### Always Add Tests For 394 + 395 + - Bug fixes (test reproduces the bug, then verify fix) 396 + - New tools (full happy path + error cases) 397 + - Invariant changes (if we change what must always be true) 398 + - Complex logic (anything with multiple branches or state) 399 + 400 + ### Consider Skipping Tests For 401 + 402 + - Simple pass-through functions 403 + - Logging statements 404 + - Configuration parsing (Zod handles validation) 405 + - One-line utility functions 406 + 407 + ### Red Flags (Add Tests Immediately) 408 + 409 + - "I'm not sure if this edge case works" 410 + - "This broke once before" 411 + - "This interacts with multiple components" 412 + - "This has complex state transitions" 413 + 414 + --- 415 + 416 + ## Maintaining Tests 417 + 418 + ### When Code Changes 419 + 420 + 1. **Run tests first**: Verify baseline before changes 421 + 2. **Make changes**: Implement feature or fix 422 + 3. **Run tests again**: Catch regressions 423 + 4. **Update tests**: If behavior intentionally changed 424 + 5. **Add new tests**: For new functionality 425 + 426 + ### Test Hygiene 427 + 428 + - **Delete obsolete tests**: If feature removed, remove tests 429 + - **Keep tests fast**: Unit tests should complete in milliseconds 430 + - **Avoid test interdependence**: Each test should run in isolation 431 + - **Use descriptive names**: Future you should understand what's being tested 432 + 433 + ### Flaky Test Policy 434 + 435 + If a test fails intermittently: 436 + 437 + 1. Mark as `.skip` with comment explaining flakiness 438 + 2. File issue to investigate 439 + 3. Either fix the flakiness or delete the test 440 + 4. Don't leave flaky tests runningโ€”they erode trust
+601
docs/worked-example.md
··· 1 + # 9plan Worked Example: Ghost Blog Application 2 + 3 + A complete end-to-end walkthrough showing how 9plan manages a realistic multi-phase project from bootstrap to completion. 4 + 5 + --- 6 + 7 + ## The Task 8 + 9 + **Goal**: Build a simple blog application powered by the Ghost CMS API. The app should: 10 + - Authenticate with Ghost's Content API 11 + - Fetch and display blog posts 12 + - Support pagination 13 + - Cache responses for performance 14 + 15 + **Why this example**: It involves multiple components with dependencies, requires decomposition, and demonstrates cross-branch coordination. 16 + 17 + --- 18 + 19 + ## Phase 1: Bootstrap 20 + 21 + ### User Initiates Session 22 + 23 + ``` 24 + User: I want to build a Ghost-powered blog app. It needs auth, 25 + post fetching with pagination, and caching. 26 + ``` 27 + 28 + ### Agent Creates Session 29 + 30 + ``` 31 + Agent calls: 9plan_session_create 32 + task_description: "Build Ghost-powered blog application with authentication, 33 + post fetching, pagination, and response caching" 34 + 35 + Response: 36 + [Session: amber-quiet-river] 37 + Session created: amber-quiet-river 38 + Directory: /Users/dev/.9plan/sessions/amber-quiet-river 39 + ``` 40 + 41 + ### Agent Decomposes into Initial Plans 42 + 43 + The agent identifies three major components and their dependencies: 44 + 45 + ``` 46 + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 47 + โ”‚ Blog App โ”‚ 48 + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค 49 + โ”‚ Ghost API โ”‚ Cache โ”‚ Display Layer โ”‚ 50 + โ”‚ Client โ”‚ System โ”‚ (Posts + Paging) โ”‚ 51 + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค 52 + โ”‚ Outputs: โ”‚ Inputs: โ”‚ Inputs: โ”‚ 53 + โ”‚ - auth'd โ”‚ - API client โ”‚ - API client โ”‚ 54 + โ”‚ client โ”‚ Outputs: โ”‚ - Cache system โ”‚ 55 + โ”‚ โ”‚ - cache API โ”‚ Outputs: โ”‚ 56 + โ”‚ โ”‚ โ”‚ - working app โ”‚ 57 + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 58 + ``` 59 + 60 + **Critical moment**: Right now, all three branches are visible. The agent captures dependencies: 61 + - Cache System needs Ghost API Client's outputs 62 + - Display Layer needs both Ghost API Client AND Cache System outputs 63 + 64 + ### Adding Plans to Queue 65 + 66 + ``` 67 + Agent calls: 9plan_queue_add 68 + context: "Building Ghost blog app. This is the foundational component that 69 + all other parts depend on. Must complete first." 70 + goal: "Create authenticated Ghost API client module" 71 + inputs: "- Ghost API credentials: provided by user or environment" 72 + outputs: "- ghost_client module: authenticated client instance with methods 73 + for getPosts(), getPost(id), with proper error handling" 74 + approach: "1. Set up Ghost Content API authentication using API key 75 + 2. Create client wrapper with typed methods 76 + 3. Implement error handling for auth failures and API errors 77 + 4. Export configured client instance" 78 + success_criteria: "- Client successfully authenticates with Ghost API 79 + - getPosts() returns post array 80 + - getPost(id) returns single post 81 + - Auth errors throw descriptive exceptions" 82 + position: back 83 + 84 + Response: 85 + [Session: amber-quiet-river] 86 + Plan added: k7f3m 87 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt 88 + Queue position: 1 89 + ``` 90 + 91 + ``` 92 + Agent calls: 9plan_queue_add 93 + context: "Building Ghost blog app. This provides caching to reduce API calls 94 + and improve performance. Depends on Ghost API client." 95 + goal: "Implement response caching system" 96 + inputs: "- ghost_client module: from Ghost API Client work" 97 + outputs: "- cache module: wrap-able caching layer with get/set/invalidate 98 + - cached_client: ghost_client wrapped with caching" 99 + approach: "1. Design cache interface (get, set, invalidate, TTL) 100 + 2. Implement in-memory cache with configurable TTL 101 + 3. Create wrapper that checks cache before API calls 102 + 4. Add cache invalidation hooks" 103 + success_criteria: "- Second call to same endpoint returns cached data 104 + - Cache respects TTL settings 105 + - Manual invalidation works 106 + - Cache misses fall through to API" 107 + position: back 108 + 109 + Response: 110 + [Session: amber-quiet-river] 111 + Plan added: m2x9p 112 + Queue position: 2 113 + ``` 114 + 115 + ``` 116 + Agent calls: 9plan_queue_add 117 + context: "Building Ghost blog app. This is the user-facing component that 118 + displays posts with pagination. Depends on both API client and cache." 119 + goal: "Build post display layer with pagination" 120 + inputs: "- cached_client: from Cache System work 121 + - ghost_client module: from Ghost API Client (fallback)" 122 + outputs: "- Working blog display: renders posts with next/prev navigation" 123 + approach: "1. Create post list component with pagination controls 124 + 2. Implement page state management 125 + 3. Connect to cached client for data fetching 126 + 4. Add loading and error states" 127 + success_criteria: "- Posts display in paginated list 128 + - Next/prev navigation works 129 + - Loading states show during fetch 130 + - Errors display gracefully" 131 + position: back 132 + 133 + Response: 134 + [Session: amber-quiet-river] 135 + Plan added: p4r2k 136 + Queue position: 3 137 + ``` 138 + 139 + ### State After Bootstrap 140 + 141 + **Queue**: `[k7f3m, m2x9p, p4r2k]` 142 + 143 + **Files**: 144 + ``` 145 + amber-quiet-river/ 146 + โ”œโ”€โ”€ session.db 147 + โ””โ”€โ”€ plans/ 148 + โ”œโ”€โ”€ k7f3m.txt # Ghost API Client 149 + โ”œโ”€โ”€ m2x9p.txt # Cache System 150 + โ””โ”€โ”€ p4r2k.txt # Display Layer 151 + ``` 152 + 153 + **k7f3m.txt contents**: 154 + ``` 155 + # Context 156 + Building Ghost blog app. This is the foundational component that all other 157 + parts depend on. Must complete first. 158 + 159 + # Goal 160 + Create authenticated Ghost API client module 161 + 162 + # Inputs 163 + - Ghost API credentials: provided by user or environment 164 + 165 + # Outputs 166 + - ghost_client module: authenticated client instance with methods for 167 + getPosts(), getPost(id), with proper error handling 168 + 169 + # Approach 170 + 1. Set up Ghost Content API authentication using API key 171 + 2. Create client wrapper with typed methods 172 + 3. Implement error handling for auth failures and API errors 173 + 4. Export configured client instance 174 + 175 + # Success Criteria 176 + - Client successfully authenticates with Ghost API 177 + - getPosts() returns post array 178 + - getPost(id) returns single post 179 + - Auth errors throw descriptive exceptions 180 + 181 + # Notes 182 + ``` 183 + 184 + --- 185 + 186 + ## Phase 2: First Plan Execution 187 + 188 + ### Agent Pulls First Plan 189 + 190 + ``` 191 + Agent calls: 9plan_queue_pull 192 + 193 + Response: 194 + [Session: amber-quiet-river] 195 + Active plan: k7f3m 196 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt 197 + 198 + Review the plan file for full context. Check for ambiguities before 199 + starting execution. If inputs reference other plans, use 200 + 9plan_history_search to find their outputs. 201 + ``` 202 + 203 + ### Agent Reads Plan File 204 + 205 + Agent uses filesystem tools to read `k7f3m.txt`, reviews the content, and proceeds with execution. 206 + 207 + ### Mid-Execution Checkpoint 208 + 209 + After implementing the basic client structure, agent updates Notes: 210 + 211 + **k7f3m.txt (updated)**: 212 + ``` 213 + # Notes 214 + [2024-01-15 14:30] Progress checkpoint: 215 + - Created ghost-client.ts with GhostClient class 216 + - Implemented constructor with API key validation 217 + - getPosts() method complete, tested against live API 218 + - Remaining: getPost(id), error handling improvements 219 + ``` 220 + 221 + ### Discovering Subwork 222 + 223 + While implementing, agent realizes error handling is complex enough to warrant its own focus. But it's smallโ€”agent decides to complete it within this plan rather than decompose. 224 + 225 + ### Completing the Plan 226 + 227 + ``` 228 + Agent calls: 9plan_plan_complete 229 + outcome: "Created ghost_client module in src/lib/ghost-client.ts. 230 + 231 + Exports: 232 + - GhostClient class with constructor(apiKey, apiUrl) 233 + - getPosts(options?: {limit, page}) โ†’ Post[] 234 + - getPost(id: string) โ†’ Post 235 + 236 + Error handling: 237 + - GhostAuthError for 401/403 responses 238 + - GhostNotFoundError for 404 responses 239 + - GhostAPIError for other failures 240 + 241 + Tested against live Ghost instance, all methods working." 242 + 243 + Response: 244 + [Session: amber-quiet-river] 245 + Plan k7f3m completed and indexed. 246 + 247 + Queue status: 2 plans remaining 248 + Next: Use 9plan_queue_pull to continue 249 + ``` 250 + 251 + ### State After First Completion 252 + 253 + **Queue**: `[m2x9p, p4r2k]` 254 + 255 + **Database**: k7f3m now in history, searchable 256 + 257 + **Files**: 258 + ``` 259 + amber-quiet-river/ 260 + โ”œโ”€โ”€ session.db # k7f3m indexed in history 261 + โ””โ”€โ”€ plans/ 262 + โ”œโ”€โ”€ m2x9p.txt # Cache System 263 + โ””โ”€โ”€ p4r2k.txt # Display Layer 264 + ``` 265 + 266 + --- 267 + 268 + ## Phase 3: Dependency Resolution 269 + 270 + ### Agent Pulls Cache System Plan 271 + 272 + ``` 273 + Agent calls: 9plan_queue_pull 274 + 275 + Response: 276 + [Session: amber-quiet-river] 277 + Active plan: m2x9p 278 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/m2x9p.txt 279 + ``` 280 + 281 + ### Agent Resolves Input Dependency 282 + 283 + The plan's Inputs field says: `ghost_client module: from Ghost API Client work` 284 + 285 + ``` 286 + Agent calls: 9plan_history_search 287 + query: "ghost_client module Ghost API" 288 + 289 + Response: 290 + [Session: amber-quiet-river] 291 + Found 1 matching plan: 292 + 293 + Plan k7f3m: "Create authenticated Ghost API client module" 294 + Outcome: Created ghost_client module in src/lib/ghost-client.ts. 295 + Exports GhostClient class with getPosts(), getPost() methods... 296 + ``` 297 + 298 + Agent now knows where to import from and what interface to expect. 299 + 300 + ### Execution and Completion 301 + 302 + Agent implements the cache system, wrapping the Ghost client. 303 + 304 + ``` 305 + Agent calls: 9plan_plan_complete 306 + outcome: "Created caching layer in src/lib/cache.ts. 307 + 308 + Exports: 309 + - Cache<T> class with get(key), set(key, value, ttl?), invalidate(key) 310 + - createCachedClient(ghostClient) โ†’ returns proxied client 311 + 312 + The cached_client wraps GhostClient from src/lib/ghost-client.ts. 313 + Default TTL: 5 minutes. Configurable via Cache constructor. 314 + 315 + Cache hits logged to console for debugging." 316 + ``` 317 + 318 + --- 319 + 320 + ## Phase 4: Plan Decomposition 321 + 322 + ### Agent Pulls Display Layer Plan 323 + 324 + ``` 325 + Agent calls: 9plan_queue_pull 326 + 327 + Response: 328 + [Session: amber-quiet-river] 329 + Active plan: p4r2k 330 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/p4r2k.txt 331 + ``` 332 + 333 + ### Agent Decides to Decompose 334 + 335 + After reviewing the plan, agent realizes "Display Layer with pagination" is too large for a focused session. It involves: 336 + - UI component structure 337 + - State management 338 + - Data fetching integration 339 + - Loading/error states 340 + 341 + Agent decides to decompose. 342 + 343 + ### Checkpointing Before Decomposition 344 + 345 + ``` 346 + Agent updates p4r2k.txt Notes section: 347 + 348 + # Notes 349 + [2024-01-15 16:00] Decomposing into subplans: 350 + - p4r2k-1: Post list component (UI structure) 351 + - p4r2k-2: Pagination state management 352 + - p4r2k-3: Integration (connect data + UI) 353 + 354 + Will defer this plan after adding children. 355 + ``` 356 + 357 + ### Adding Subplans (Reverse Order for Correct Execution) 358 + 359 + ``` 360 + Agent calls: 9plan_queue_add 361 + context: "Building Ghost blog app display layer. This is the final integration 362 + step that connects the data layer to the UI components." 363 + goal: "Integrate data fetching with UI components" 364 + inputs: "- PostList component: from post list component work 365 + - usePagination hook: from pagination state work 366 + - cached_client: from Cache System work" 367 + outputs: "- Working integrated display: all pieces connected and functional" 368 + approach: "1. Import PostList and usePagination into main page 369 + 2. Connect usePagination to cached_client.getPosts() 370 + 3. Pass paginated data to PostList 371 + 4. Wire up navigation callbacks" 372 + success_criteria: "- Page loads and displays posts 373 + - Pagination controls navigate correctly 374 + - Loading states display during transitions" 375 + position: front 376 + 377 + Response: Plan added: j8n2q, position 1 378 + ``` 379 + 380 + ``` 381 + Agent calls: 9plan_queue_add 382 + context: "Building Ghost blog app display layer. This handles the pagination 383 + logic separately from UI rendering." 384 + goal: "Implement pagination state management" 385 + inputs: "(none - self-contained logic)" 386 + outputs: "- usePagination hook: manages page state, provides next/prev handlers" 387 + approach: "1. Create usePagination React hook 388 + 2. Track current page, total pages, loading state 389 + 3. Provide nextPage(), prevPage(), goToPage(n) handlers 390 + 4. Handle edge cases (first page, last page)" 391 + success_criteria: "- Hook tracks page state correctly 392 + - Navigation handlers update state 393 + - Edge cases don't cause invalid states" 394 + position: front 395 + 396 + Response: Plan added: r3t7w, position 1 397 + ``` 398 + 399 + ``` 400 + Agent calls: 9plan_queue_add 401 + context: "Building Ghost blog app display layer. This is the presentational 402 + component, separate from data fetching logic." 403 + goal: "Create PostList UI component" 404 + inputs: "(none - receives data via props)" 405 + outputs: "- PostList component: renders array of posts with pagination controls" 406 + approach: "1. Create PostList component accepting posts array prop 407 + 2. Add PostCard subcomponent for individual posts 408 + 3. Add pagination controls (prev/next buttons) 409 + 4. Style with basic CSS" 410 + success_criteria: "- Component renders post list from props 411 + - Pagination controls render and fire callbacks 412 + - Empty state handled gracefully" 413 + position: front 414 + 415 + Response: Plan added: x5k9m, position 1 416 + ``` 417 + 418 + ### Deferring Parent Plan 419 + 420 + ``` 421 + Agent calls: 9plan_plan_defer 422 + reason: "Decomposed into subplans: x5k9m (PostList UI), r3t7w (pagination state), 423 + j8n2q (integration). Will aggregate child outcomes when re-pulled." 424 + position: back 425 + 426 + Response: 427 + [Session: amber-quiet-river] 428 + Plan p4r2k deferred to back of queue. 429 + Reason recorded in Notes. 430 + ``` 431 + 432 + ### State After Decomposition 433 + 434 + **Queue**: `[x5k9m, r3t7w, j8n2q, p4r2k]` 435 + 436 + The subplans are at front in correct execution order. Parent `p4r2k` is at back, waiting to aggregate. 437 + 438 + **p4r2k.txt Notes (updated by server)**: 439 + ``` 440 + # Notes 441 + [2024-01-15 16:00] Decomposing into subplans: 442 + - p4r2k-1: Post list component (UI structure) 443 + - p4r2k-2: Pagination state management 444 + - p4r2k-3: Integration (connect data + UI) 445 + 446 + Will defer this plan after adding children. 447 + 448 + [2024-01-15 16:05] Deferred: Decomposed into subplans: x5k9m (PostList UI), 449 + r3t7w (pagination state), j8n2q (integration). Will aggregate child outcomes 450 + when re-pulled. 451 + ``` 452 + 453 + --- 454 + 455 + ## Phase 5: Executing Child Plans 456 + 457 + Agent executes `x5k9m`, `r3t7w`, and `j8n2q` in sequence. Each follows the normal cycle: 458 + 1. Pull 459 + 2. Resolve inputs via history search 460 + 3. Execute 461 + 4. Complete with descriptive outcome 462 + 463 + After all three complete: 464 + 465 + **Queue**: `[p4r2k]` 466 + 467 + **History**: k7f3m, m2x9p, x5k9m, r3t7w, j8n2q (all searchable) 468 + 469 + --- 470 + 471 + ## Phase 6: Parent Aggregation 472 + 473 + ### Agent Pulls Parent Plan Again 474 + 475 + ``` 476 + Agent calls: 9plan_queue_pull 477 + 478 + Response: 479 + [Session: amber-quiet-river] 480 + Active plan: p4r2k 481 + Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/p4r2k.txt 482 + ``` 483 + 484 + ### Agent Reads Notes, Sees Decomposition Record 485 + 486 + From the Notes section, agent sees this plan was decomposed and identifies child plan IDs. 487 + 488 + ### Agent Retrieves Child Outcomes 489 + 490 + ``` 491 + Agent calls: 9plan_history_get 492 + plan_id: x5k9m 493 + 494 + Response: [Full plan with outcome: "Created PostList component..."] 495 + ``` 496 + 497 + ``` 498 + Agent calls: 9plan_history_get 499 + plan_id: r3t7w 500 + 501 + Response: [Full plan with outcome: "Created usePagination hook..."] 502 + ``` 503 + 504 + ``` 505 + Agent calls: 9plan_history_get 506 + plan_id: j8n2q 507 + 508 + Response: [Full plan with outcome: "Integrated all components..."] 509 + ``` 510 + 511 + ### Agent Verifies and Aggregates 512 + 513 + All children succeeded. Agent tests the integrated result, confirms it works. 514 + 515 + ``` 516 + Agent calls: 9plan_plan_complete 517 + outcome: "Display layer complete. Aggregated from child plans: 518 + 519 + - x5k9m: PostList component in src/components/PostList.tsx 520 + - r3t7w: usePagination hook in src/hooks/usePagination.ts 521 + - j8n2q: Integration in src/pages/index.tsx 522 + 523 + All components working together. Posts display with functional 524 + pagination. Loading states and error handling in place. 525 + 526 + The blog application is now feature-complete." 527 + ``` 528 + 529 + --- 530 + 531 + ## Phase 7: Session Complete 532 + 533 + ``` 534 + Agent calls: 9plan_queue_pull 535 + 536 + Response: 537 + [Session: amber-quiet-river] 538 + Queue is empty. Task complete! 539 + 540 + Completed plans: 6 541 + Use 9plan_history_search to review outcomes. 542 + ``` 543 + 544 + ### Final State 545 + 546 + **Queue**: Empty 547 + 548 + **History**: All 6 plans indexed and searchable 549 + - k7f3m: Ghost API Client 550 + - m2x9p: Cache System 551 + - x5k9m: PostList UI 552 + - r3t7w: Pagination State 553 + - j8n2q: Integration 554 + - p4r2k: Display Layer (parent/aggregator) 555 + 556 + **Files**: 557 + ``` 558 + amber-quiet-river/ 559 + โ”œโ”€โ”€ session.db # All history indexed 560 + โ””โ”€โ”€ plans/ # Empty - all plans completed 561 + ``` 562 + 563 + --- 564 + 565 + ## Key Observations 566 + 567 + ### Dependency Flow 568 + ``` 569 + k7f3m (Ghost Client) 570 + โ†“ 571 + m2x9p (Cache) โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” 572 + โ†“ โ”‚ 573 + x5k9m (PostList UI) โ†โ”€โ”€ no deps โ”‚ 574 + โ†“ โ”‚ 575 + r3t7w (Pagination) โ†โ”€โ”€ no deps โ”‚ 576 + โ†“ โ”‚ 577 + j8n2q (Integration) โ†โ”€โ”€ needs all โ”€โ”˜ 578 + โ†“ 579 + p4r2k (Aggregation) โ†โ”€โ”€ verifies children 580 + ``` 581 + 582 + ### Cross-Branch Visibility 583 + When `p4r2k` was decomposed, all three children were created in the same context. This was the moment to capture that `j8n2q` (Integration) would need outputs from the Cache System (`m2x9p`), not just its sibling UI plans. 584 + 585 + ### Semantic Resolution in Action 586 + `j8n2q` declared an input: "cached_client: from Cache System work" 587 + 588 + At execution time, agent searched: 589 + ``` 590 + 9plan_history_search("cached_client Cache System") 591 + ``` 592 + 593 + And found `m2x9p`'s outcome describing exactly where to find it. 594 + 595 + ### Parent Aggregation Value 596 + `p4r2k` completing last allowed: 597 + 1. Verification that all UI pieces integrated correctly 598 + 2. A milestone record documenting the full Display Layer 599 + 3. A single point summarizing three child plans' work 600 + 601 + If someone later asks "what was the Display Layer work?", searching for `p4r2k` gives them the complete picture without finding all three children separately.