···11+# 9plan Architecture Decision Records
22+33+This document captures significant architectural decisions, the context that led to them, and the alternatives that were considered and rejected.
44+55+---
66+77+## ADR-001: Single Queue vs Priority Tiers
88+99+**Status**: Accepted
1010+1111+**Context**:
1212+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.
1313+1414+**Decision**:
1515+Use a single ordered queue with front/back positioning semantics instead of priority tiers.
1616+1717+**Rationale**:
1818+- **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.
1919+- **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."
2020+- **Task completion guarantee**: Sessions represent bounded tasks. The queue emptying naturally signals task completion.
2121+2222+**Alternatives Rejected**:
2323+- **Numeric priorities (1-10)**: Requires arbitrary calibration, doesn't prevent low-priority abandonment
2424+- **Priority tiers (high/medium/low)**: Same problems as numeric, just coarser
2525+- **Backlog + active queue**: Backlog items tend to be forgotten; defeats the "must eventually empty" property
2626+- **No queue (freeform notes)**: Loses ordering semantics and completion tracking
2727+2828+**Consequences**:
2929+- โ All work eventually completes if agent follows protocol
3030+- โ Simple mental model for positioning decisions
3131+- โ ๏ธ Can't express "do this eventually but not before other eventual work"
3232+- โ ๏ธ Reordering requires manual queue manipulation
3333+3434+---
3535+3636+## ADR-002: Three-Word Session Names vs UUIDs
3737+3838+**Status**: Accepted
3939+4040+**Context**:
4141+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.
4242+4343+**Decision**:
4444+Use randomly-generated three-word identifiers (adjective-adjective-noun pattern, e.g., `copper-velvet-morning`) instead of UUIDs or sequential IDs.
4545+4646+**Rationale**:
4747+- **Human memorability**: "Resume copper-velvet-morning" is natural speech; "resume a7f3c2e9-4d8b-11ef-8a2c-0242ac120003" is not
4848+- **Context survival**: Distinctive word combinations are more likely to survive context compaction than arbitrary character sequences
4949+- **Verbal communication**: Users can say session names aloud, write them in notes, share in chat
5050+- **Sufficient uniqueness**: With 1000+ word dictionaries, collision probability is extremely low for reasonable session counts
5151+5252+**Alternatives Rejected**:
5353+- **UUIDs**: Guaranteed unique but completely unmemorable, hard to communicate
5454+- **Sequential IDs (session-1, session-2)**: Memorable but collide across different machines/users, don't convey identity
5555+- **User-provided names**: Requires user input, may conflict, doesn't guarantee uniqueness
5656+- **Hash of task description**: Unmemorable, changes if description edited
5757+5858+**Consequences**:
5959+- โ Users can remember and communicate session names easily
6060+- โ More likely to survive in agent context
6161+- โ ๏ธ Theoretical collision risk with very large session counts
6262+- โ ๏ธ Requires collision checking on generation
6363+6464+---
6565+6666+## ADR-003: Hybrid Storage (SQLite + Flat Files) vs Pure Database
6767+6868+**Status**: Accepted
6969+7070+**Context**:
7171+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.
7272+7373+**Decision**:
7474+Use hybrid storage: SQLite for structural state (queue order, active plan, history), flat files for plan content that agents read/write.
7575+7676+**Rationale**:
7777+- **Structural integrity**: SQLite protects queue order and active plan state from corruption. Agents can't accidentally break the queue by editing files.
7878+- **Agent accessibility**: Flat files can be read/written using standard filesystem tools that agents already have. No special database tooling needed.
7979+- **Checkpoint friendliness**: Agents update Notes sections during execution for progress checkpointing. File writes are simple, immediate, and don't require transaction semantics.
8080+- **Human debuggability**: Plan files can be inspected directly when debugging. Database requires query tools.
8181+- **Failure isolation**: File corruption doesn't break queue structure. Database corruption doesn't lose in-progress plan content.
8282+8383+**Alternatives Rejected**:
8484+- **Pure SQLite**: Requires agents to use database tools for all plan access; less natural for checkpointing
8585+- **Pure flat files**: Queue order and state tracking in files is fragile; concurrent access (even across conversations) could corrupt
8686+- **JSON files**: More structure than plaintext, but adds parsing complexity for agents; plan format is simple enough for plaintext
8787+- **External database (Postgres, etc.)**: Requires additional infrastructure; overkill for single-agent, local execution
8888+8989+**Consequences**:
9090+- โ Queue structure protected from accidental corruption
9191+- โ Agents use familiar file read/write operations
9292+- โ Easy debugging via file inspection
9393+- โ ๏ธ Potential for database and files to get out of sync (missing plan files)
9494+- โ ๏ธ Two storage systems to understand and maintain
9595+9696+---
9797+9898+## ADR-004: Semantic Dependency Resolution vs ID-Based References
9999+100100+**Status**: Accepted
101101+102102+**Context**:
103103+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").
104104+105105+**Decision**:
106106+Use semantic dependency resolution. Plans declare inputs by description, and resolution happens via full-text search of completed plan outcomes.
107107+108108+**Rationale**:
109109+- **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.
110110+- **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.
111111+- **Natural language**: Agents describe dependencies in terms of what they need ("authentication client"), not system artifacts ("output of k7f3m").
112112+- **Search flexibility**: Descriptions can match partial terms, synonyms, related concepts.
113113+114114+**Alternatives Rejected**:
115115+- **Plan ID references**: Brittle to decomposition and re-planning; requires knowing IDs at reference time
116116+- **Explicit dependency graph**: Adds complexity; must be maintained as plans change; doesn't handle dynamic discovery
117117+- **No dependency tracking**: Forces agents to re-derive what's available; risks duplicate work or missed dependencies
118118+119119+**Consequences**:
120120+- โ Dependencies survive decomposition naturally
121121+- โ Natural language interface for agents
122122+- โ ๏ธ Search quality depends on outcome descriptions being specific and searchable
123123+- โ ๏ธ Ambiguous descriptions may return wrong results
124124+125125+---
126126+127127+## ADR-005: Deferred Parents vs Immediate Parent Completion on Decomposition
128128+129129+**Status**: Accepted
130130+131131+**Context**:
132132+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.
133133+134134+**Decision**:
135135+Defer parent plans to the back of the queue. Parents complete after children, performing aggregation and verification.
136136+137137+**Rationale**:
138138+- **Hierarchy preservation**: Parents represent milestones and decision points. Keeping them allows "did Phase 1 succeed?" assessment.
139139+- **Failure handling**: If children fail, the parent aggregation step is the natural place to decide whether to re-plan.
140140+- **Combined documentation**: Parent completion documents the aggregate outcome of all children, making later search easier.
141141+- **Verification point**: Parent aggregation can verify children actually integrated correctly.
142142+143143+**Alternatives Rejected**:
144144+- **Immediate completion**: Loses hierarchy; no aggregation point; can't verify children succeeded together
145145+- **Discard**: Loses all parent context; no milestone tracking; if children fail, no record of what was attempted
146146+- **Keep active (no defer)**: Blocks pulling new work; doesn't match the "child work happens next" semantics
147147+148148+**Consequences**:
149149+- โ Hierarchy preserved for milestone tracking
150150+- โ Natural aggregation and verification point
151151+- โ Better documentation of combined outcomes
152152+- โ ๏ธ Parent completion is delayed; dependent plans can't wait for it (must use child outcomes)
153153+- โ ๏ธ Parents accumulate at back of queue
154154+155155+---
156156+157157+## ADR-006: Plan Files Deleted on Completion vs Archived
158158+159159+**Status**: Accepted
160160+161161+**Context**:
162162+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.
163163+164164+**Decision**:
165165+Delete plan files on completion. Content lives only in the database after completion.
166166+167167+**Rationale**:
168168+- **Single source of truth**: Completed plan content is in exactly one place (database). No risk of file and database diverging.
169169+- **Clean filesystem**: `plans/` directory shows only active and queued work. Easy to see what's pending.
170170+- **Search efficiency**: All completed content is indexed in FTS5. File search would be slower and less consistent.
171171+- **Space efficiency**: No accumulation of completed plan files over long sessions.
172172+173173+**Alternatives Rejected**:
174174+- **Archive to separate directory**: Two sources of truth; filesystem accumulates; search must check both places
175175+- **Keep in place with status marker**: Clutters directory; requires parsing files to determine status
176176+- **Never store in database**: Loses FTS5 search capability; must search files directly
177177+178178+**Consequences**:
179179+- โ Clean separation: files = pending, database = completed
180180+- โ Efficient search via FTS5
181181+- โ ๏ธ Can't inspect completed plan files directly (must query database)
182182+- โ ๏ธ If database corrupts, completed plan content is lost
183183+184184+---
185185+186186+## ADR-007: Front/Back Position Hints vs Insert-At-Index
187187+188188+**Status**: Accepted
189189+190190+**Context**:
191191+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).
192192+193193+**Decision**:
194194+Use simple front/back position hints. No index-based or relative positioning.
195195+196196+**Rationale**:
197197+- **Matches discovery semantics**: Work is discovered as "must do before continuing" (front) or "should do eventually" (back). These are the natural categories.
198198+- **No index management**: Agents don't need to track or reason about queue indices, which change as plans are pulled and completed.
199199+- **Simpler mental model**: Two options instead of arbitrary positioning decisions.
200200+- **Reversible**: If initial positioning is wrong, defer can move a plan from active back to queue.
201201+202202+**Alternatives Rejected**:
203203+- **Index-based insertion**: Requires knowing current queue state; indices shift as queue changes; error-prone
204204+- **Insert-after-plan-X**: Requires tracking plan IDs in queue; complex when referenced plan completes
205205+- **Priority numbers**: See ADR-001โsame problems with calibration and abandonment
206206+- **Multiple queues**: Complicates "must empty" semantics; where does work go?
207207+208208+**Consequences**:
209209+- โ Simple, intuitive positioning
210210+- โ Matches how work is discovered
211211+- โ ๏ธ Front-insertion reversal problem: adding A, B, C at front gives [C, B, A]
212212+- โ ๏ธ Can't express "insert between these two specific plans"
213213+214214+---
215215+216216+## ADR-008: Session-Scoped vs Global History
217217+218218+**Status**: Accepted
219219+220220+**Context**:
221221+Completed plans are indexed for search. The search scope could be per-session, per-project (if projects existed), or global across all sessions.
222222+223223+**Decision**:
224224+History is session-scoped. Each session has its own history that cannot be searched from other sessions.
225225+226226+**Rationale**:
227227+- **Task isolation**: Sessions represent distinct tasks. Outputs from unrelated tasks shouldn't pollute search results.
228228+- **Naming collision avoidance**: "auth_client" in one session might be completely different from "auth_client" in another.
229229+- **Predictable scope**: Agents always know search results come from current task context.
230230+- **Storage locality**: Each session is self-contained in its directory; no shared state.
231231+232232+**Alternatives Rejected**:
233233+- **Global history**: Cross-session pollution; naming collisions; harder to reason about sources
234234+- **Project-level history**: Requires project concept; sessions within project would share, but project boundaries unclear
235235+- **Explicit cross-session references**: Complex; requires knowing other session names; breaks isolation
236236+237237+**Consequences**:
238238+- โ Clean task isolation
239239+- โ Predictable search scope
240240+- โ ๏ธ Can't reuse outputs across sessions (must recreate or manually copy)
241241+- โ ๏ธ Related sessions can't easily share context
242242+243243+---
244244+245245+## ADR-009: Single Agent Per Session vs Concurrent Access
246246+247247+**Status**: Accepted
248248+249249+**Context**:
250250+Could multiple agents (or agent instances) work on the same session simultaneously? This would enable parallelism but adds complexity.
251251+252252+**Decision**:
253253+Single agent per session. No concurrent access support.
254254+255255+**Rationale**:
256256+- **Simplicity**: No locking, race conditions, or conflict resolution needed.
257257+- **Active plan model**: "One plan active at a time" assumes single executor. Multiple agents would need multiple active plans or coordination.
258258+- **Queue consistency**: Front/back insertion is inherently sequential. Concurrent adds would require ordering decisions.
259259+- **Target use case**: Long-running tasks with single agent, possibly across multiple conversations, not parallel execution.
260260+261261+**Alternatives Rejected**:
262262+- **Concurrent agents with locking**: Complex; must handle conflicts; unclear semantics for "active plan"
263263+- **Work stealing**: Interesting for parallelism but much more complex; changes execution model fundamentally
264264+- **Shared queue, separate execution contexts**: Still needs synchronization; partial solution
265265+266266+**Consequences**:
267267+- โ Simple, predictable execution model
268268+- โ No synchronization complexity
269269+- โ ๏ธ Can't parallelize work within a session
270270+- โ ๏ธ Multiple conversations touching same session can corrupt state
271271+272272+---
273273+274274+## ADR-010: Alphanumeric Plan IDs vs Numeric Sequence
275275+276276+**Status**: Accepted
277277+278278+**Context**:
279279+Plans need identifiers for reference. Options include sequential numbers (1, 2, 3), UUIDs, or short alphanumeric hashes.
280280+281281+**Decision**:
282282+Use short alphanumeric hashes (5+ characters, e.g., `k7f3m`) generated with collision checking.
283283+284284+**Rationale**:
285285+- **Distinct from session names**: Three-word names for sessions, alphanumeric for plansโno confusion about what you're referencing.
286286+- **Short but unique**: 5 characters with 36-character alphabet provides ~60 million combinations. Sufficient for session-scoped uniqueness.
287287+- **No semantic meaning**: Unlike sequential IDs, doesn't imply order or creation time. Plans can be reordered without ID confusion.
288288+- **Copy-paste friendly**: No spaces or special characters.
289289+290290+**Alternatives Rejected**:
291291+- **Sequential numbers**: Implies ordering; confusing when plans complete out of order; reused IDs after discard?
292292+- **UUIDs**: Too long; hard to reference in Notes; overkill for session-scoped uniqueness
293293+- **Descriptive slugs**: Would need to be unique; long; change if goal changes
294294+- **Three-word names (like sessions)**: Confusingโis "copper-velvet-morning" a session or plan?
295295+296296+**Consequences**:
297297+- โ Clear distinction from session names
298298+- โ Short, easy to reference
299299+- โ No ordering implications
300300+- โ ๏ธ Not human-memorable (but that's what goal descriptions are for)
301301+- โ ๏ธ Requires collision checking
302302+303303+---
304304+305305+## ADR-011: Notes as Append-Only Scratchpad vs Structured Fields
306306+307307+**Status**: Accepted
308308+309309+**Context**:
310310+Plans need a place for accumulated context during executionโprogress checkpoints, deferral reasons, decomposition records. This could be a freeform scratchpad or structured fields.
311311+312312+**Decision**:
313313+Notes is a freeform, append-only scratchpad. Content is accumulated, not replaced.
314314+315315+**Rationale**:
316316+- **Flexibility**: Different plans need different kinds of notes. Structured fields would constrain or require many optional fields.
317317+- **History preservation**: Appending (especially deferral reasons) preserves the full history of what happened to a plan.
318318+- **Agent simplicity**: Agents append text; no need to parse and update specific fields.
319319+- **Recovery value**: Full history of notes provides breadcrumbs for recovery after context compaction.
320320+321321+**Alternatives Rejected**:
322322+- **Structured status fields**: Rigid; doesn't capture freeform progress notes; harder to extend
323323+- **Replace-on-write**: Loses history; previous deferrals or checkpoints overwritten
324324+- **Separate notes file per update**: Filesystem clutter; harder to read full context
325325+326326+**Consequences**:
327327+- โ Flexible, extensible
328328+- โ Full history preserved
329329+- โ Simple append operations
330330+- โ ๏ธ Notes can get long over many deferrals
331331+- โ ๏ธ No structured parsing (agent must read and interpret)
332332+333333+---
334334+335335+## ADR-012: Plan Validation vs Freeform Content
336336+337337+**Status**: Accepted
338338+339339+**Context**:
340340+Plan content could be completely freeform (any text), schema-validated (required fields), or something in between.
341341+342342+**Decision**:
343343+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.
344344+345345+**Rationale**:
346346+- **Quality enforcement**: Ensures plans are well-formed enough to be executable. Catches missing context or vague goals.
347347+- **Agent guidance**: Validation errors tell agents what's missing, prompting better plan creation.
348348+- **Consistency**: All plans follow the same structure, making them easier to read and search.
349349+- **Self-contained execution**: Required fields ensure plans have enough information for cold-start execution.
350350+351351+**Alternatives Rejected**:
352352+- **No validation**: Agents could create incomplete plans; execution would fail later
353353+- **Strict schema (all fields required)**: Inputs/Outputs aren't always applicable; forces meaningless placeholders
354354+- **AI-based quality assessment**: Complex; subjective; slower
355355+356356+**Consequences**:
357357+- โ Minimum quality guaranteed
358358+- โ Consistent plan structure
359359+- โ Clear error messages guide completion
360360+- โ ๏ธ Validation might reject valid-but-unusual plans
361361+- โ ๏ธ Template format is somewhat rigid
362362+363363+---
364364+365365+## Future Considerations
366366+367367+These are not decisions but potential future changes noted during design:
368368+369369+- **Batch add operation**: Would solve front-insertion reversal problem. Could accept ordered list of plans.
370370+- **HTTP transport**: Currently stdio only. HTTP would enable web-based clients and remote access.
371371+- **Plan templates**: Pre-defined patterns for common plan types (research, implementation, testing).
372372+- **Cross-session search**: Opt-in ability to search other sessions' history. Requires careful scoping.
373373+- **Plan versioning**: Track changes to plans over time, enable rollback. Currently Notes provides partial history but not full versioning.
+584
docs/api-examples.md
···11+# 9plan API Examples
22+33+Concrete request and response examples for every tool.
44+55+---
66+77+## Session Tools
88+99+### 9plan_session_create
1010+1111+Creates a new session for a task.
1212+1313+**Request:**
1414+```json
1515+{
1616+ "tool": "9plan_session_create",
1717+ "arguments": {
1818+ "task_description": "Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching"
1919+ }
2020+}
2121+```
2222+2323+**Response (Success):**
2424+```
2525+[Session: amber-quiet-river]
2626+2727+Session created: amber-quiet-river
2828+Directory: /Users/dev/.9plan/sessions/amber-quiet-river
2929+3030+The session is ready. Use 9plan_queue_add to add plans, then 9plan_queue_pull to begin work.
3131+```
3232+3333+**Response (Error - rare, usually filesystem issues):**
3434+```
3535+Error: Failed to create session directory: Permission denied
3636+3737+Check that 9plan has write access to /Users/dev/.9plan/sessions/
3838+```
3939+4040+---
4141+4242+### 9plan_session_resume
4343+4444+Loads an existing session.
4545+4646+**Request:**
4747+```json
4848+{
4949+ "tool": "9plan_session_resume",
5050+ "arguments": {
5151+ "session_name": "amber-quiet-river"
5252+ }
5353+}
5454+```
5555+5656+**Response (Success - with active plan):**
5757+```
5858+[Session: amber-quiet-river]
5959+6060+Session resumed: amber-quiet-river
6161+Directory: /Users/dev/.9plan/sessions/amber-quiet-river
6262+6363+Task: Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching
6464+6565+Active plan: k7f3m
6666+ Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt
6767+ Goal: Create authenticated Ghost API client module
6868+6969+Queue (2 plans):
7070+ 1. m2x9p - Implement response caching system
7171+ 2. p4r2k - Build post display layer with pagination
7272+7373+Completed: 0 plans
7474+7575+An active plan exists. Read the plan file to continue, or use 9plan_plan_complete/defer/discard to close it out.
7676+```
7777+7878+**Response (Success - no active plan):**
7979+```
8080+[Session: amber-quiet-river]
8181+8282+Session resumed: amber-quiet-river
8383+Directory: /Users/dev/.9plan/sessions/amber-quiet-river
8484+8585+Task: Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching
8686+8787+Active plan: (none)
8888+8989+Queue (3 plans):
9090+ 1. k7f3m - Create authenticated Ghost API client module
9191+ 2. m2x9p - Implement response caching system
9292+ 3. p4r2k - Build post display layer with pagination
9393+9494+Completed: 0 plans
9595+9696+Use 9plan_queue_pull to get the next plan.
9797+```
9898+9999+**Response (Success - empty queue):**
100100+```
101101+[Session: amber-quiet-river]
102102+103103+Session resumed: amber-quiet-river
104104+Directory: /Users/dev/.9plan/sessions/amber-quiet-river
105105+106106+Task: Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching
107107+108108+Active plan: (none)
109109+110110+Queue: (empty)
111111+112112+Completed: 6 plans
113113+114114+Task complete! Use 9plan_history_search to review completed work.
115115+```
116116+117117+**Response (Error - session not found):**
118118+```
119119+Error: Session not found: amber-quiet-river
120120+121121+Check the session name and try again. Session names are case-sensitive.
122122+Available sessions can be found in: /Users/dev/.9plan/sessions/
123123+```
124124+125125+---
126126+127127+## Queue Tools
128128+129129+### 9plan_queue_add
130130+131131+Adds a new plan to the queue.
132132+133133+**Request (full plan):**
134134+```json
135135+{
136136+ "tool": "9plan_queue_add",
137137+ "arguments": {
138138+ "context": "Building Ghost blog app. This is the foundational component that all other parts depend on. Must complete first.",
139139+ "goal": "Create authenticated Ghost API client module",
140140+ "inputs": "- Ghost API credentials: provided by user or environment",
141141+ "outputs": "- ghost_client module: authenticated client instance with methods for getPosts(), getPost(id), with proper error handling",
142142+ "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",
143143+ "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",
144144+ "position": "back"
145145+ }
146146+}
147147+```
148148+149149+**Response (Success):**
150150+```
151151+[Session: amber-quiet-river]
152152+153153+Plan added: k7f3m
154154+Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt
155155+Queue position: 1
156156+157157+The plan file has been created. Use 9plan_queue_pull when ready to execute.
158158+```
159159+160160+**Request (minimal - no inputs/outputs):**
161161+```json
162162+{
163163+ "tool": "9plan_queue_add",
164164+ "arguments": {
165165+ "context": "Building blog app. Need a basic UI component.",
166166+ "goal": "Create PostCard component for displaying individual posts",
167167+ "approach": "1. Create React component\n2. Accept post data as props\n3. Display title, excerpt, date\n4. Style with Tailwind",
168168+ "success_criteria": "- Component renders post data\n- Styling looks good\n- No TypeScript errors",
169169+ "position": "front"
170170+ }
171171+}
172172+```
173173+174174+**Response (Success - front position):**
175175+```
176176+[Session: amber-quiet-river]
177177+178178+Plan added: r3t7w
179179+Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/r3t7w.txt
180180+Queue position: 1 (front)
181181+182182+The plan file has been created. Use 9plan_queue_pull when ready to execute.
183183+```
184184+185185+**Response (Error - missing required field):**
186186+```
187187+Error: Plan validation failed
188188+189189+Missing required fields:
190190+- approach: Must describe how to accomplish the goal
191191+192192+Please provide all required fields: context, goal, approach, success_criteria
193193+```
194194+195195+**Response (Error - empty field):**
196196+```
197197+Error: Plan validation failed
198198+199199+Empty required fields:
200200+- goal: Cannot be empty
201201+202202+Please provide meaningful content for all required fields.
203203+```
204204+205205+---
206206+207207+### 9plan_queue_pull
208208+209209+Gets the next plan to work on.
210210+211211+**Request:**
212212+```json
213213+{
214214+ "tool": "9plan_queue_pull",
215215+ "arguments": {}
216216+}
217217+```
218218+219219+**Response (Success):**
220220+```
221221+[Session: amber-quiet-river]
222222+223223+Active plan: k7f3m
224224+Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt
225225+226226+Read the plan file for full context. Review for any ambiguities before starting execution.
227227+228228+If the plan has inputs from other plans, use 9plan_history_search to find their outputs.
229229+If the plan's Notes indicate it was previously decomposed, use 9plan_history_get to retrieve child outcomes.
230230+```
231231+232232+**Response (Error - plan already active):**
233233+```
234234+[Session: amber-quiet-river]
235235+236236+Error: Cannot pull - a plan is already active
237237+238238+Active plan: k7f3m
239239+Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt
240240+241241+Complete, defer, or discard the active plan before pulling another.
242242+```
243243+244244+**Response (Queue empty):**
245245+```
246246+[Session: amber-quiet-river]
247247+248248+Queue is empty. Task complete!
249249+250250+Completed plans: 6
251251+252252+Use 9plan_history_search to review what was accomplished.
253253+```
254254+255255+---
256256+257257+## Plan Lifecycle Tools
258258+259259+### 9plan_plan_complete
260260+261261+Marks the active plan as done.
262262+263263+**Request:**
264264+```json
265265+{
266266+ "tool": "9plan_plan_complete",
267267+ "arguments": {
268268+ "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."
269269+ }
270270+}
271271+```
272272+273273+**Response (Success):**
274274+```
275275+[Session: amber-quiet-river]
276276+277277+Plan k7f3m completed and indexed.
278278+279279+The plan's content and outcome are now searchable via 9plan_history_search.
280280+281281+Queue status: 2 plans remaining
282282+Next: Use 9plan_queue_pull to continue work.
283283+```
284284+285285+**Response (Error - no active plan):**
286286+```
287287+[Session: amber-quiet-river]
288288+289289+Error: No active plan to complete
290290+291291+Use 9plan_queue_pull to get a plan first.
292292+```
293293+294294+---
295295+296296+### 9plan_plan_defer
297297+298298+Returns the active plan to the queue.
299299+300300+**Request (blocked, retry soon):**
301301+```json
302302+{
303303+ "tool": "9plan_plan_defer",
304304+ "arguments": {
305305+ "reason": "Blocked: Need Ghost API credentials from user before proceeding",
306306+ "position": "front"
307307+ }
308308+}
309309+```
310310+311311+**Response (Success - front):**
312312+```
313313+[Session: amber-quiet-river]
314314+315315+Plan k7f3m deferred to front of queue.
316316+Reason recorded in Notes.
317317+318318+Queue status: 3 plans
319319+Position: 1 (will be pulled next)
320320+321321+Resolve the blocking issue, then use 9plan_queue_pull to retry.
322322+```
323323+324324+**Request (decomposition, aggregate later):**
325325+```json
326326+{
327327+ "tool": "9plan_plan_defer",
328328+ "arguments": {
329329+ "reason": "Decomposed into subplans: x5k9m (PostList UI), r3t7w (pagination state), j8n2q (integration). Will aggregate child outcomes when re-pulled.",
330330+ "position": "back"
331331+ }
332332+}
333333+```
334334+335335+**Response (Success - back):**
336336+```
337337+[Session: amber-quiet-river]
338338+339339+Plan p4r2k deferred to back of queue.
340340+Reason recorded in Notes.
341341+342342+Queue status: 4 plans
343343+Position: 4 (will execute after current queue)
344344+345345+Use 9plan_queue_pull to continue with queued work.
346346+```
347347+348348+**Response (Error - no active plan):**
349349+```
350350+[Session: amber-quiet-river]
351351+352352+Error: No active plan to defer
353353+354354+Use 9plan_queue_pull to get a plan first.
355355+```
356356+357357+---
358358+359359+### 9plan_plan_discard
360360+361361+Abandons the active plan.
362362+363363+**Request:**
364364+```json
365365+{
366366+ "tool": "9plan_plan_discard",
367367+ "arguments": {
368368+ "reason": "Plan superseded by external changes - the Ghost API was replaced with a different CMS. This plan is no longer relevant."
369369+ }
370370+}
371371+```
372372+373373+**Response (Success):**
374374+```
375375+[Session: amber-quiet-river]
376376+377377+Plan k7f3m discarded.
378378+Reason: Plan superseded by external changes - the Ghost API was replaced with a different CMS. This plan is no longer relevant.
379379+380380+The plan has been removed and will not appear in history.
381381+382382+Queue status: 2 plans remaining
383383+Next: Use 9plan_queue_pull to continue work.
384384+```
385385+386386+**Response (Error - no active plan):**
387387+```
388388+[Session: amber-quiet-river]
389389+390390+Error: No active plan to discard
391391+392392+Use 9plan_queue_pull to get a plan first.
393393+```
394394+395395+---
396396+397397+## History Tools
398398+399399+### 9plan_history_search
400400+401401+Searches completed plans for outputs.
402402+403403+**Request:**
404404+```json
405405+{
406406+ "tool": "9plan_history_search",
407407+ "arguments": {
408408+ "query": "ghost client API authentication"
409409+ }
410410+}
411411+```
412412+413413+**Response (Success - matches found):**
414414+```
415415+[Session: amber-quiet-river]
416416+417417+Found 1 matching plan:
418418+419419+1. Plan k7f3m
420420+ Goal: Create authenticated Ghost API client module
421421+ Outcome: Created ghost_client module in src/lib/ghost-client.ts.
422422+ Exports GhostClient class with getPosts(), getPost() methods...
423423+424424+Use 9plan_history_get with the plan ID for full details.
425425+```
426426+427427+**Response (Success - multiple matches):**
428428+```
429429+[Session: amber-quiet-river]
430430+431431+Found 3 matching plans:
432432+433433+1. Plan k7f3m
434434+ Goal: Create authenticated Ghost API client module
435435+ Outcome: Created ghost_client module in src/lib/ghost-client.ts...
436436+437437+2. Plan m2x9p
438438+ Goal: Implement response caching system
439439+ Outcome: Created caching layer wrapping ghost_client...
440440+441441+3. Plan j8n2q
442442+ Goal: Integrate data fetching with UI components
443443+ Outcome: Connected cached ghost_client to PostList component...
444444+445445+Use 9plan_history_get with the plan ID for full details.
446446+```
447447+448448+**Response (No matches):**
449449+```
450450+[Session: amber-quiet-river]
451451+452452+No matching plans found for: "payment processing stripe"
453453+454454+Try different search terms, or check if the relevant work has been completed yet.
455455+```
456456+457457+---
458458+459459+### 9plan_history_get
460460+461461+Retrieves a specific completed plan.
462462+463463+**Request:**
464464+```json
465465+{
466466+ "tool": "9plan_history_get",
467467+ "arguments": {
468468+ "plan_id": "k7f3m"
469469+ }
470470+}
471471+```
472472+473473+**Response (Success):**
474474+```
475475+[Session: amber-quiet-river]
476476+477477+Plan k7f3m (completed)
478478+479479+# Context
480480+Building Ghost blog app. This is the foundational component that all other parts depend on. Must complete first.
481481+482482+# Goal
483483+Create authenticated Ghost API client module
484484+485485+# Inputs
486486+- Ghost API credentials: provided by user or environment
487487+488488+# Outputs
489489+- ghost_client module: authenticated client instance with methods for getPosts(), getPost(id), with proper error handling
490490+491491+# Approach
492492+1. Set up Ghost Content API authentication using API key
493493+2. Create client wrapper with typed methods
494494+3. Implement error handling for auth failures and API errors
495495+4. Export configured client instance
496496+497497+# Success Criteria
498498+- Client successfully authenticates with Ghost API
499499+- getPosts() returns post array
500500+- getPost(id) returns single post
501501+- Auth errors throw descriptive exceptions
502502+503503+# Notes
504504+[2024-01-15 14:30] Progress checkpoint:
505505+- Created ghost-client.ts with GhostClient class
506506+- Implemented constructor with API key validation
507507+- getPosts() method complete, tested against live API
508508+- Remaining: getPost(id), error handling improvements
509509+510510+# Outcome
511511+Created ghost_client module in src/lib/ghost-client.ts.
512512+513513+Exports:
514514+- GhostClient class with constructor(apiKey, apiUrl)
515515+- getPosts(options?: {limit, page}) โ Post[]
516516+- getPost(id: string) โ Post
517517+518518+Error handling:
519519+- GhostAuthError for 401/403 responses
520520+- GhostNotFoundError for 404 responses
521521+- GhostAPIError for other failures
522522+523523+Tested against live Ghost instance, all methods working.
524524+```
525525+526526+**Response (Error - not found):**
527527+```
528528+[Session: amber-quiet-river]
529529+530530+Error: Plan not found: xyz99
531531+532532+The plan may not have been completed yet (check queue), or may have been discarded.
533533+Use 9plan_session_resume to see current queue, or 9plan_history_search to find related plans.
534534+```
535535+536536+---
537537+538538+## Common Patterns
539539+540540+### Resolving Dependencies
541541+542542+When a plan has inputs, resolve them before execution:
543543+544544+```
545545+1. Read plan file, find inputs:
546546+ "- cached_client: from Cache System work"
547547+548548+2. Search history:
549549+ 9plan_history_search("cached_client cache system")
550550+551551+3. Get details if needed:
552552+ 9plan_history_get("m2x9p")
553553+554554+4. Use the information from outcome to proceed
555555+```
556556+557557+### Aggregating After Decomposition
558558+559559+When a parent plan is re-pulled:
560560+561561+```
562562+1. Read plan Notes, find child IDs:
563563+ "Decomposed into subplans: x5k9m, r3t7w, j8n2q"
564564+565565+2. Get each child's outcome:
566566+ 9plan_history_get("x5k9m")
567567+ 9plan_history_get("r3t7w")
568568+ 9plan_history_get("j8n2q")
569569+570570+3. Verify children succeeded, aggregate results
571571+572572+4. Complete parent:
573573+ 9plan_plan_complete(outcome: "Aggregated from children...")
574574+```
575575+576576+### Adding Subplans in Correct Order
577577+578578+To achieve execution order [A, B, C], add in reverse:
579579+580580+```
581581+9plan_queue_add(C, position: "front") โ Queue: [C, ...]
582582+9plan_queue_add(B, position: "front") โ Queue: [B, C, ...]
583583+9plan_queue_add(A, position: "front") โ Queue: [A, B, C, ...]
584584+```
···11+# 9plan: Plan Queue MCP Server
22+33+A session-scoped work queue for long time-horizon agent sequencing.
44+55+---
66+77+## Core Concepts
88+99+### Session
1010+1111+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.
1212+1313+Sessions are:
1414+- **Persistent**: Survive conversation restarts
1515+- **Task-scoped**: One session = one overall task/goal
1616+- **Complete when empty**: The task is done when no plans remain
1717+1818+### Plan
1919+2020+A discrete unit of work with enough detail for an agent to execute it without additional context. Each plan contains:
2121+2222+- **Identifier**: Short alphanumeric hash (e.g., `k7f3m`), distinct from session names to avoid confusion
2323+- **Context**: High-level summary of the overall task, where this plan fits, and why it exists
2424+- **Goal**: What this plan accomplishes
2525+- **Inputs**: What this plan requires from other plans or external sources
2626+- **Outputs**: What this plan produces that other plans may consume
2727+- **Approach**: How to accomplish it (detailed, actionable)
2828+- **Success criteria**: Observable conditions that indicate completion
2929+- **Notes**: Working scratchpad for progress checkpoints, deferral reasons, etc.
3030+3131+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.
3232+3333+Plans may be pulled from the queue multiple times during their lifecycle:
3434+1. **First pull**: Execute directly, or decompose into subplans and defer
3535+2. **Subsequent pull** (if decomposed): Aggregate child outcomes from history, complete or re-plan
3636+3737+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.
3838+3939+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.
4040+4141+### Queue
4242+4343+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.
4444+4545+Position semantics:
4646+- **Front**: Work that should happen next / before continuing current work
4747+- **Back**: Work that should happen eventually / after current work
4848+4949+---
5050+5151+## Tools
5252+5353+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.
5454+5555+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.
5656+5757+### Session Management
5858+5959+**`9plan_session_create`**
6060+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.
6161+6262+Returns: session name, path to session directory (containing `plans/` subdirectory).
6363+6464+**`9plan_session_resume`**
6565+Loads an existing session by its three-word identifier.
6666+6767+Returns:
6868+- Session name and directory path
6969+- Task description
7070+- Queue contents (plan IDs in order, with goals)
7171+- Currently-active plan (ID and file path, if any)
7272+- Count of completed plans
7373+7474+Use this to understand session state after interruption or to review what work remains.
7575+7676+### Queue Management
7777+7878+**`9plan_queue_add`**
7979+Adds a new plan to the queue.
8080+8181+Parameters:
8282+- Context (required): high-level summary of overall task, where this plan fits, and why it exists
8383+- Goal (required)
8484+- Approach (required)
8585+- Success criteria (required)
8686+- Inputs (optional): what this plan requires from other plans or external sources
8787+- Outputs (optional): what this plan produces that other plans may consume
8888+- Position hint: `front` or `back` (default: `back`)
8989+9090+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.
9191+9292+Use `front` when: discovered work that must complete before current work can proceed.
9393+Use `back` when: discovered work that should happen eventually but not immediately.
9494+9595+Returns: plan ID and file path.
9696+9797+**`9plan_queue_pull`**
9898+Removes the front plan from the queue and marks it as active. This is the primary way to get work.
9999+100100+Returns:
101101+- Plan ID and file path (agent reads file for full content)
102102+- Guidance prompting the agent to review for ambiguities
103103+104104+If the queue is empty, returns a message indicating the task is complete.
105105+106106+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.
107107+108108+### Plan Lifecycle
109109+110110+**`9plan_plan_defer`**
111111+Returns the currently-active plan to the queue without completing it.
112112+113113+Parameters:
114114+- Reason for deferral (required)
115115+- Position hint: `front` or `back` (default: `front`)
116116+117117+The server appends the deferral reason to the plan's Notes section in the file, preserving a record of why the plan was deferred.
118118+119119+Use cases:
120120+- **Blocked**: Can't proceed, will retry later (typically `front`)
121121+- **Decomposition**: Breaking into subplans, will aggregate results later (use `back`; see Decomposing a Plan workflow)
122122+- **Reordering**: New information suggests other work should happen first
123123+124124+Returns an error if no plan is currently active.
125125+126126+**`9plan_plan_complete`**
127127+Marks the currently-active plan as done.
128128+129129+Parameters:
130130+- Outcome (required): summary of what was accomplished, including any outputs produced (artifacts, APIs, data structures) that other plans may consume
131131+132132+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.
133133+134134+On completion:
135135+1. Plan content and outcome are indexed into the database
136136+2. Plan file is deleted from `plans/`
137137+3. Returns a message prompting the agent to pull the next plan
138138+139139+Returns an error if no plan is currently active.
140140+141141+**`9plan_plan_discard`**
142142+Abandons the currently-active plan without completing it.
143143+144144+Parameters:
145145+- Reason (required): why this plan is being discarded
146146+147147+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.
148148+149149+Returns an error if no plan is currently active.
150150+151151+### History
152152+153153+**`9plan_history_search`**
154154+Searches completed plans for outputs matching a query.
155155+156156+Parameters:
157157+- Query (required): description of the output being sought (e.g., "ghost_client module", "user authentication API")
158158+159159+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.
160160+161161+**`9plan_history_get`**
162162+Retrieves a specific completed plan by ID.
163163+164164+Parameters:
165165+- Plan ID (required)
166166+167167+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).
168168+169169+---
170170+171171+## Prompts
172172+173173+### `bootstrap`
174174+175175+Guides initial session setup and planning decomposition. User-initiated.
176176+177177+Workflow:
178178+1. Clarify the overall task/goal through questions
179179+2. Create a new session with the task description
180180+3. Decompose into initial set of plans with clear success criteria
181181+4. Identify cross-branch dependencies and capture in inputs/outputs fields
182182+5. Enqueue all plans in logical order (respecting dependencies)
183183+6. Return control to the agent with the session ready to pull from
184184+185185+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.
186186+187187+---
188188+189189+## File Structure
190190+191191+Sessions use a hybrid storage model: SQLite for structural state, flat files for plan content.
192192+193193+```
194194+9plan/
195195+โโโ sessions/
196196+ โโโ {session-name}/
197197+ โโโ session.db # SQLite: queue order, active plan, metadata, history
198198+ โโโ plans/
199199+ โโโ {plan-id}.txt # Plan content files (queued and active only)
200200+ โโโ ...
201201+```
202202+203203+**SQLite database** (`session.db`) stores:
204204+- Session metadata (task description)
205205+- Queue order (list of plan IDs)
206206+- Active plan reference
207207+- Plan metadata (ID, status, timestamps)
208208+- Completed plan content (indexed on completion)
209209+210210+**Plan files** (`plans/`) contain:
211211+- Full plan content for queued and active plans
212212+- Deleted when plan completes (content moves to DB)
213213+- Agent reads/writes these directly via filesystem tools
214214+215215+This split protects structural integrity (agents can't accidentally corrupt queue order) while allowing efficient filesystem access for plan content and checkpointing.
216216+217217+Each plan file is human-readable plaintext. The filename (without extension) is the plan ID:
218218+219219+```
220220+# Context
221221+{high-level summary of overall task, where this plan fits, and why it exists}
222222+223223+# Goal
224224+{what this accomplishes}
225225+226226+# Inputs
227227+{what this plan requires from other plans or external sources, if any}
228228+{format: "- {description}: {source plan or external source}"}
229229+230230+# Outputs
231231+{what this plan produces that other plans may consume, if any}
232232+{format: "- {description}: {details or specification}"}
233233+234234+# Approach
235235+{how to accomplish it}
236236+237237+# Success Criteria
238238+{observable completion conditions}
239239+240240+# Notes
241241+{accumulated context, deferral reasons, progress checkpoints, etc.}
242242+```
243243+244244+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.
245245+246246+**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.
247247+248248+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.
249249+250250+**Plan lifecycle and storage:**
251251+- **Queued**: Metadata in DB, content in `plans/{id}.txt`
252252+- **Active**: Metadata in DB (marked active), content in `plans/{id}.txt`
253253+- **Complete**: Content moved to DB, file deleted from `plans/`
254254+255255+Only queued and active plans exist as files. Completed plans are stored entirely in the database.
256256+257257+---
258258+259259+## Workflows
260260+261261+### Starting a New Task
262262+263263+1. User selects `bootstrap` prompt
264264+2. Agent (guided by prompt) clarifies the task through questions
265265+3. Agent creates session, decomposes into plans, adds them to queue
266266+4. Agent calls `9plan_queue_pull` to begin work
267267+268268+### Normal Execution Loop
269269+270270+1. `9plan_queue_pull` returns plan ID and file path
271271+2. Agent reads plan file via filesystem tools
272272+3. Agent reviews plan, clarifies ambiguities if needed
273273+4. Agent checks Notesโif plan was previously decomposed, use `9plan_history_get` to retrieve child outcomes by ID
274274+5. Agent checks Inputsโfor each dependency, use `9plan_history_search` to find matching outputs
275275+6. Agent executes the work (or completes aggregation)
276276+7. Agent updates Notes in plan file to checkpoint progress
277277+8. Agent calls `9plan_plan_complete` (or `9plan_plan_defer` if blocked)
278278+9. Loop until queue is empty
279279+280280+**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.
281281+282282+**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.
283283+284284+### Discovering New Work
285285+286286+During execution, agent discovers something that needs to be done:
287287+288288+- **Must happen first**: `9plan_queue_add` with `position: front`
289289+- **Should happen eventually**: `9plan_queue_add` with `position: back`
290290+- **Current plan is too large**: Decompose using the pattern below
291291+292292+### Decomposing a Plan
293293+294294+When a plan is too large to complete in a focused session:
295295+296296+1. Identify subplans (A.1, A.2, A.3)
297297+2. For each subplan, identify:
298298+ - Inputs: what it needs from sibling subplans or other branches
299299+ - Outputs: what it produces that siblings or other branches may need
300300+3. Update parent's Notes: "Decomposing into: A.1 (goal), A.2 (goal), A.3 (goal)"
301301+4. Add subplans at front in reverse order (A.3, A.2, A.1) to achieve correct execution order
302302+5. Defer parent to back with reason: "Awaiting completion of subplans A.1, A.2, A.3"
303303+304304+Result: `[A.1, A.2, A.3, ...other work..., A]`
305305+306306+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.
307307+308308+When parent is eventually re-pulled:
309309+1. Check Notes to see it was decomposed (includes child plan IDs)
310310+2. Use `9plan_history_get` to retrieve each child's outcome by ID
311311+3. Aggregate results, handle any failures
312312+4. Complete the parent (or re-plan if children failed)
313313+314314+### Resuming After Interruption
315315+316316+1. Human provides session name (or agent recalls it)
317317+2. Agent calls `9plan_session_resume`โreturns session state including active plan (if any)
318318+3. If a plan was in progress, read its file and continue
319319+4. Otherwise, `9plan_queue_pull` to get next work
320320+321321+---
322322+323323+## Design Notes
324324+325325+**Why a single queue?**
326326+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.
327327+328328+**Why position-based ordering?**
329329+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."
330330+331331+**Why hybrid storage (SQLite + flat files)?**
332332+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.
333333+334334+**Why three-word names?**
335335+Easy for humans to remember and communicate. Avoids UUIDs while maintaining uniqueness. Example: "resume copper-velvet-morning" is more natural than "resume a7x2f9k".
336336+337337+**Collision handling**
338338+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.
339339+340340+**Why session-scoped?**
341341+Sessions map to tasks, not projects. A project might involve many sessions. This keeps individual queues focused and achievableโ"done" is well-defined.
342342+343343+**Why preserve parents during decomposition?**
344344+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?"
345345+346346+**Parent completion timing**
347347+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.
348348+349349+**Handling cross-branch dependencies**
350350+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.
351351+352352+This means dependencies must be:
353353+1. **Identified during common ancestor decomposition**: When both branches are visible
354354+2. **Encoded in plan inputs/outputs**: Explicit fields capture what each plan needs and provides
355355+3. **Resolved semantically at execution time**: Search history for outcomes matching input descriptions
356356+357357+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.
358358+359359+---
360360+361361+## Assumptions
362362+363363+This design makes the following assumptions about agents, tasks, and the operating environment:
364364+365365+### Agents
366366+367367+**Single-threaded execution**
368368+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.
369369+370370+**Filesystem access to session directory**
371371+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.
372372+373373+**Protocol compliance**
374374+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.
375375+376376+**Benefits from explicit structure**
377377+Agents perform better with formal plans (goal, approach, criteria) than with freeform notes. This might not hold for all agent architectures.
378378+379379+**Context window is the bottleneck**
380380+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.
381381+382382+### Tasks
383383+384384+**Decomposable into plans with optional hierarchy**
385385+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.
386386+387387+**Plans are independently executable**
388388+Each plan is self-contained. Tasks where steps are deeply intertwined (requiring constant cross-reference) fight against this model.
389389+390390+**Observable success criteria**
391391+Completion conditions can be clearly defined. Exploratory or creative tasks where "done" is fuzzy are harder to fit.
392392+393393+**Finite work**
394394+A single queue that must empty assumes tasks eventually complete. Ongoing maintenance, monitoring, or indefinite tasks don't have a natural "done" state.
395395+396396+### Operating Environment
397397+398398+**Single agent per session**
399399+No concurrent access. Multiple agents (or agent instances) touching the same session would cause race conditions on `queue.txt`, `meta.txt`, etc.
400400+401401+**Reliable filesystem**
402402+Flat files assume the filesystem doesn't corrupt, lose writes, or have permission issues mid-operation.
403403+404404+**Human available for recovery**
405405+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.
406406+407407+**Bounded session count**
408408+Three-word session names assume collision probability stays low because sessions don't accumulate indefinitely. Thousands of concurrent sessions would increase collision risk.
409409+410410+### Usage Patterns
411411+412412+**Single bootstrap phase**
413413+The prompt assumes planning happens once upfront. Tasks requiring periodic re-planning or major pivots might need to essentially restart.
414414+415415+**Static ordering is sufficient**
416416+Position-based ordering (front/back) is enough. Tasks where optimal ordering changes dynamically based on external factors would need constant reordering.
417417+418418+**No plan versioning**
419419+Notes are overwritten, plans are replaced via decomposition. There's no history of how a plan evolved and no rollback capability.
420420+421421+---
422422+423423+## Known Limitations
424424+425425+**Front-insertion order reversal**
426426+When adding multiple plans at the front (e.g., during decomposition), successive `9plan_queue_add` calls reverse the intended order:
427427+- add A at front โ `[A, ...]`
428428+- add B at front โ `[B, A, ...]`
429429+- add C at front โ `[C, B, A, ...]`
430430+431431+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.
432432+433433+**Context compaction mid-plan**
434434+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.
435435+436436+Mitigations:
437437+- **Plan granularity**: Keep plans small enough to complete in a focused session, reducing compaction risk
438438+- **Session breadcrumbs**: Tool responses include the session name, keeping it fresh in recent context
439439+- **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
440440+- **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")
441441+442442+**Partial decomposition**
443443+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.
444444+445445+Example inconsistent state:
446446+- Agent was decomposing plan A into sub1, sub2, sub3
447447+- Compaction after adding sub1, sub2 but before sub3 and defer
448448+- State: `active: A`, queue: `[sub1, sub2, B, C]`
449449+- Agent on resumption sees plan A is active, doesn't know sub1/sub2 are partial children
450450+451451+Mitigations:
452452+- **Checkpoint before decomposing**: Before starting decomposition, update Notes with the full plan: "Decomposing into: sub1 (goal), sub2 (goal), sub3 (goal)"
453453+- **Checkpoint during decomposition**: After each add, update Notes: "Added sub1, sub2. Remaining: sub3"
454454+- **Recovery heuristic**: On resumption, if Notes indicate decomposition was in progress, agent can review queue for partial subplans and complete the decomposition
455455+- **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
456456+457457+This doesn't guarantee atomic decomposition, but provides enough breadcrumbs for recovery.
458458+459459+**Missing plan files**
460460+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
···11+# 9plan Error Recovery Playbook
22+33+When things go wrong, use this guide to diagnose and recover. Format: **Symptom โ Likely Cause โ Recovery Steps**.
44+55+---
66+77+## Session-Level Issues
88+99+### "I don't know what session I was working on"
1010+1111+**Symptom**: Agent or user has lost track of the session name.
1212+1313+**Likely Cause**: Context compaction removed the session identifier from working memory, or conversation restarted.
1414+1515+**Recovery Steps**:
1616+1. Check recent tool responsesโsession name appears as prefix (e.g., `[Session: amber-quiet-river]`)
1717+2. Look for session name in conversation history
1818+3. If using a file browser, check `~/.9plan/sessions/` for session directories
1919+4. List sessions by modification time to find most recent:
2020+ ```bash
2121+ ls -lt ~/.9plan/sessions/
2222+ ```
2323+5. Resume with `9plan_session_resume` using the discovered name
2424+2525+**Prevention**: Note session names somewhere persistent (project README, task tracker) at session creation.
2626+2727+---
2828+2929+### "Session resume shows unexpected state"
3030+3131+**Symptom**: `9plan_session_resume` returns a queue or active plan that doesn't match expectations.
3232+3333+**Likely Cause**:
3434+- Work was done in a different conversation that wasn't synced
3535+- Agent completed/deferred plans without user awareness
3636+- Different session with similar name
3737+3838+**Recovery Steps**:
3939+1. Verify session name matches exactly (three-word identifiers are case-sensitive)
4040+2. Review the returned state carefully:
4141+ - `active_plan`: What's currently in progress?
4242+ - `queue`: What's waiting?
4343+ - `completed_count`: How much was done?
4444+3. If active plan exists, read its file and Notes section for context
4545+4. If state is truly wrong, check if another session exists for this task
4646+4747+**Prevention**: Always complete or defer plans before ending a conversation. Use progress checkpointing.
4848+4949+---
5050+5151+## Queue Issues
5252+5353+### "Plans are executing in wrong order"
5454+5555+**Symptom**: A plan runs before its dependencies are ready.
5656+5757+**Likely Cause**:
5858+- Plans were added in wrong order during decomposition
5959+- Front-insertion reversal: adding A, B, C at front gives `[C, B, A]`
6060+6161+**Recovery Steps**:
6262+1. If dependency isn't ready yet:
6363+ - Defer current plan: `9plan_plan_defer` with reason "Blocked: waiting for {dependency}"
6464+ - Position: `front` to retry soon, or `back` if other work can proceed
6565+2. If dependency was completed but not found:
6666+ - Use `9plan_history_search` to find the output
6767+ - The producing plan may have been decomposedโsearch for child outcomes
6868+6969+**Prevention**: When decomposing, add subplans in **reverse** order so they execute correctly. Document this in approach notes.
7070+7171+---
7272+7373+### "Queue is stuck with an active plan"
7474+7575+**Symptom**: `9plan_queue_pull` returns error "a plan is already active."
7676+7777+**Likely Cause**: Previous conversation/execution ended without completing, deferring, or discarding the active plan.
7878+7979+**Recovery Steps**:
8080+1. Call `9plan_session_resume` to see active plan details
8181+2. Read the active plan file to understand its state
8282+3. Check the Notes section for progress checkpoints
8383+4. Decide how to proceed:
8484+ - **Plan was nearly complete**: Finish remaining work, then `9plan_plan_complete`
8585+ - **Plan needs more work**: Continue execution, checkpoint progress
8686+ - **Plan should wait**: `9plan_plan_defer` with reason
8787+ - **Plan is obsolete**: `9plan_plan_discard` with reason
8888+8989+**Prevention**: Always close out active plans before ending work sessions.
9090+9191+---
9292+9393+### "Queue is empty but task isn't done"
9494+9595+**Symptom**: `9plan_queue_pull` says queue is empty, but the overall task has unfinished work.
9696+9797+**Likely Cause**:
9898+- Not all necessary plans were created during bootstrap
9999+- A plan was discarded instead of deferred
100100+- Discovered work wasn't added to queue
101101+102102+**Recovery Steps**:
103103+1. Review task requirements against completed work:
104104+ ```
105105+ 9plan_history_search("your task keywords")
106106+ ```
107107+2. Identify gaps between requirements and completed plans
108108+3. Add missing work:
109109+ ```
110110+ 9plan_queue_add with appropriate context, goal, etc.
111111+ ```
112112+4. Continue execution
113113+114114+**Prevention**: Thorough upfront decomposition during bootstrap. Add discovered work immediately rather than "remembering for later."
115115+116116+---
117117+118118+## Plan Execution Issues
119119+120120+### "Plan inputs can't be found in history"
121121+122122+**Symptom**: `9plan_history_search` returns no results for a declared input.
123123+124124+**Likely Cause**:
125125+- The producing plan hasn't completed yet (dependency ordering issue)
126126+- The producing plan was decomposedโoutputs are in children, not parent
127127+- Search terms don't match how outputs were documented
128128+- The input was never actually produced (plan was discarded or failed)
129129+130130+**Recovery Steps**:
131131+1. Try broader search terms:
132132+ ```
133133+ 9plan_history_search("ghost client") # instead of exact field name
134134+ ```
135135+2. If the producing plan was decomposed, search for child plan outputs:
136136+ ```
137137+ 9plan_history_search("getPosts API authentication") # functional description
138138+ ```
139139+3. If still not found, check if the plan is still in queue (not yet executed)
140140+4. If truly missing, the producing work needs to be re-done:
141141+ - Add a new plan to create the required output
142142+ - Defer current plan until dependency is ready
143143+144144+**Prevention**: Document outputs with specific, searchable terms. Use multiple keywords that might match searches.
145145+146146+---
147147+148148+### "Plan file is missing"
149149+150150+**Symptom**: Attempting to read a plan file returns "file not found" or similar error.
151151+152152+**Likely Cause**:
153153+- Plan was completed (files are deleted on completion)
154154+- Plan was discarded (files are deleted on discard)
155155+- Filesystem issue or accidental deletion
156156+- Wrong path (session directory changed)
157157+158158+**Recovery Steps**:
159159+1. Check if plan is in history (it completed):
160160+ ```
161161+ 9plan_history_get(plan_id)
162162+ ```
163163+2. Verify the session path in `9plan_session_resume` output
164164+3. If plan was truly lost:
165165+ - Check Notes from other plans for context about what it contained
166166+ - Re-create the plan with `9plan_queue_add`
167167+168168+**Prevention**: Don't manually manipulate plan files outside of normal tool usage.
169169+170170+---
171171+172172+### "I lost context mid-plan"
173173+174174+**Symptom**: Agent doesn't remember what it was doing, context window was compacted.
175175+176176+**Likely Cause**: Long execution triggered context compaction, removing earlier conversation.
177177+178178+**Recovery Steps**:
179179+1. The session name should be in recent tool responses (breadcrumb design)
180180+2. Call `9plan_session_resume` to get current state
181181+3. If a plan is active, read its file:
182182+ - Check Notes section for progress checkpoints
183183+ - Resume from last checkpoint
184184+4. If no progress was checkpointed, assess the plan and start execution fresh
185185+186186+**Prevention**:
187187+- Keep plans small enough to complete in focused sessions
188188+- Checkpoint progress in Notes periodically (every few tool calls or significant milestones)
189189+- Note key context at start of execution
190190+191191+---
192192+193193+## Decomposition Issues
194194+195195+### "Partial decomposition - some subplans exist, parent still active"
196196+197197+**Symptom**: Queue contains some subplans but parent plan is still marked active (not deferred).
198198+199199+**Likely Cause**: Context compaction or interruption occurred mid-decomposition, after some `9plan_queue_add` calls but before `9plan_plan_defer`.
200200+201201+**Recovery Steps**:
202202+1. Read the parent plan's Notes sectionโit should document decomposition intent:
203203+ ```
204204+ "Decomposing into: sub1 (goal), sub2 (goal), sub3 (goal)"
205205+ ```
206206+2. Compare Notes against queue to identify missing subplans
207207+3. Add any remaining subplans
208208+4. Complete the decomposition by deferring the parent:
209209+ ```
210210+ 9plan_plan_defer
211211+ reason: "Completing interrupted decomposition. Subplans: ..."
212212+ position: back
213213+ ```
214214+215215+**Example scenario**:
216216+- Notes say: "Decomposing into: A.1, A.2, A.3"
217217+- Queue shows: `[A.1, A.2, B, C]` (A.3 missing, parent still active)
218218+- Fix: Add A.3 at front, then defer parent
219219+220220+**Prevention**: Always update Notes with full decomposition plan before starting to add subplans.
221221+222222+---
223223+224224+### "Parent plan can't find child outcomes"
225225+226226+**Symptom**: Parent plan is re-pulled for aggregation, but `9plan_history_get` on child IDs fails.
227227+228228+**Likely Cause**:
229229+- Child plan IDs weren't recorded in Notes during decomposition
230230+- Children haven't completed yet (still in queue)
231231+- Child was discarded instead of completed
232232+233233+**Recovery Steps**:
234234+1. Check queue for remaining children:
235235+ ```
236236+ 9plan_session_resume โ review queue contents
237237+ ```
238238+2. If children are still queued, defer parent again:
239239+ ```
240240+ 9plan_plan_defer
241241+ reason: "Children not yet complete. Will retry after queue progress."
242242+ position: back
243243+ ```
244244+3. If children should have completed, search history by goal/output:
245245+ ```
246246+ 9plan_history_search("the specific goal or output description")
247247+ ```
248248+4. If children were lost, they may need to be re-planned
249249+250250+**Prevention**: Record child plan IDs in parent's Notes during decomposition. Use specific, searchable output descriptions.
251251+252252+---
253253+254254+### "Cross-branch dependency wasn't captured"
255255+256256+**Symptom**: A plan needs output from a sibling branch, but the input wasn't declared and the output can't be found.
257257+258258+**Likely Cause**: During decomposition, the dependency between branches wasn't identified and recorded in inputs/outputs fields.
259259+260260+**Recovery Steps**:
261261+1. Search history broadly for the needed output:
262262+ ```
263263+ 9plan_history_search("auth token user session") # functional description
264264+ ```
265265+2. If found, proceedโthe input field was incomplete but the data exists
266266+3. If not found, check if the producing work is still in queue
267267+4. If the producing work was decomposed, search for its children's outputs
268268+5. If truly missing, add a new plan to produce the required output
269269+270270+**Prevention**: During decomposition, explicitly think through "what does each branch need from other branches?" Capture ALL cross-branch dependencies while they're visible.
271271+272272+---
273273+274274+## Data Issues
275275+276276+### "History search returns irrelevant results"
277277+278278+**Symptom**: `9plan_history_search` returns plans that don't match what you're looking for.
279279+280280+**Likely Cause**: Search terms are too broad or the relevant plan used different terminology.
281281+282282+**Recovery Steps**:
283283+1. Try more specific search terms
284284+2. Try different phrasings of the same concept
285285+3. If you know the approximate plan, try searching by goal keywords
286286+4. Review recent history manually if session is small
287287+288288+**Prevention**: Use specific, consistent terminology in outputs. Include multiple relevant keywords.
289289+290290+---
291291+292292+### "Database seems corrupted or inconsistent"
293293+294294+**Symptom**: Tool calls return unexpected errors, state doesn't match filesystem, or data is missing.
295295+296296+**Likely Cause**:
297297+- Interrupted operation left inconsistent state
298298+- Manual filesystem manipulation
299299+- Bug in 9plan server
300300+301301+**Recovery Steps**:
302302+1. Document current state:
303303+ - What does `9plan_session_resume` return?
304304+ - What files exist in `plans/` directory?
305305+ - What operations were being performed?
306306+2. Check for orphaned plan files (files in `plans/` not referenced by queue)
307307+3. For serious corruption, consider:
308308+ - Starting fresh session, manually recreating queue
309309+ - Manually inspecting `session.db` if comfortable with SQLite
310310+4. Report bug if this seems like a server issue
311311+312312+**Prevention**: Let 9plan tools manage all state. Avoid manual file manipulation.
313313+314314+---
315315+316316+## Emergency Procedures
317317+318318+### "Everything is broken, I need to restart"
319319+320320+**Steps for clean restart while preserving work**:
321321+322322+1. **Document current state**:
323323+ - List all completed work (`9plan_history_search("")` or broad search)
324324+ - Note any partially completed plans
325325+ - Save any code/artifacts created outside 9plan
326326+327327+2. **Create new session**:
328328+ ```
329329+ 9plan_session_create with task description
330330+ ```
331331+332332+3. **Recreate queue**:
333333+ - Add only remaining work (not already-completed plans)
334334+ - Reference completed work by description, not old plan IDs
335335+336336+4. **Resume execution**:
337337+ - Old history won't be searchable from new session
338338+ - May need to manually note where prior outputs are located
339339+340340+### "Agent is in a loop, keeps doing the same thing"
341341+342342+**Symptom**: Agent repeatedly pulls the same plan or makes no progress.
343343+344344+**Likely Cause**:
345345+- Plan is too vagueโagent doesn't know what to do
346346+- Plan keeps getting deferred for the same reason
347347+- Agent is confused about success criteria
348348+349349+**Recovery Steps**:
350350+1. Discard the problematic plan:
351351+ ```
352352+ 9plan_plan_discard
353353+ reason: "Plan too vague / stuck in loop. Will re-plan with more detail."
354354+ ```
355355+2. Create replacement with clearer specification:
356356+ - More specific goal
357357+ - More detailed approach
358358+ - More concrete success criteria
359359+3. If the work is fundamentally unclear, pause for human clarification
360360+361361+---
362362+363363+## Quick Reference: When to Use Each Action
364364+365365+| Situation | Action |
366366+|-----------|--------|
367367+| Plan complete, work done | `9plan_plan_complete` |
368368+| Blocked, can't proceed now | `9plan_plan_defer` (front) |
369369+| Decomposing into subplans | `9plan_plan_defer` (back) |
370370+| Plan is obsolete/wrong | `9plan_plan_discard` |
371371+| Found new blocking work | `9plan_queue_add` (front) |
372372+| Found new eventual work | `9plan_queue_add` (back) |
373373+| Need outputs from prior work | `9plan_history_search` |
374374+| Need specific plan details | `9plan_history_get` |
375375+| Lost/need to recover | `9plan_session_resume` |
+378
docs/future-work.md
···11+# 9plan Future Work and Non-Goals
22+33+This document captures ideas that were explicitly considered and deferred, features that don't fit the current design, and potential enhancements for later versions.
44+55+---
66+77+## Explicit Non-Goals
88+99+These are things 9plan intentionally does NOT do. They were considered and rejected, not overlooked.
1010+1111+### Multi-Agent Concurrency
1212+1313+**What it would be**: Multiple agents working on the same session simultaneously.
1414+1515+**Why not**:
1616+- The "one active plan" model assumes single execution
1717+- Queue ordering becomes complex with concurrent pulls
1818+- Locking and conflict resolution add significant complexity
1919+- Target use case is single-agent, long-horizon tasks
2020+2121+**Workaround**: Create separate sessions for parallel workstreams; coordinate manually.
2222+2323+---
2424+2525+### Priority-Based Ordering
2626+2727+**What it would be**: Numeric priorities (1-10) or priority tiers (high/medium/low).
2828+2929+**Why not**:
3030+- Creates failure mode where low-priority work is abandoned
3131+- Requires agents to calibrate priorities against existing items
3232+- Front/back positioning is simpler and matches how work is discovered
3333+3434+**Workaround**: Use front position for urgent work; reorder via defer if needed.
3535+3636+---
3737+3838+### Cross-Session Dependencies
3939+4040+**What it would be**: Plans in one session depending on outputs from another session.
4141+4242+**Why not**:
4343+- Breaks session isolation and "must empty" semantics
4444+- Session scoping keeps search results predictable
4545+- Different sessions may use same terms for different things
4646+4747+**Workaround**: Complete dependent session first; manually reference its outputs.
4848+4949+---
5050+5151+### Plan Versioning / Rollback
5252+5353+**What it would be**: Full history of plan changes with ability to revert.
5454+5555+**Why not**:
5656+- Notes section provides partial history (deferral reasons, checkpoints)
5757+- Full versioning adds significant storage and complexity
5858+- Plans are disposableโre-plan rather than rollback
5959+6060+**Workaround**: Copy important plan state to Notes before major changes.
6161+6262+---
6363+6464+### Automatic Dependency Enforcement
6565+6666+**What it would be**: System blocks plan execution until declared dependencies complete.
6767+6868+**Why not**:
6969+- Dependencies are semantic, not ID-based; hard to verify automatically
7070+- Decomposition means children produce outputs, not parents
7171+- Would require dependency graph management
7272+7373+**Workaround**: Defer plans that can't proceed; semantic search finds outputs.
7474+7575+---
7676+7777+### Project-Level Organization
7878+7979+**What it would be**: Sessions grouped into projects with shared history.
8080+8181+**Why not**:
8282+- Adds organizational layer that complicates core model
8383+- Session isolation is valuable for predictability
8484+- "Project" boundaries are often unclear
8585+8686+**Workaround**: Use naming conventions (e.g., `myapp-auth`, `myapp-frontend`).
8787+8888+---
8989+9090+### Real-Time Collaboration
9191+9292+**What it would be**: Multiple users viewing/editing session state simultaneously.
9393+9494+**Why not**:
9595+- Single-agent execution model
9696+- Would require sync protocol, conflict resolution
9797+- Not the target use case
9898+9999+**Workaround**: One user at a time; use session resume to hand off.
100100+101101+---
102102+103103+### Plan Templates
104104+105105+**What it would be**: Pre-defined plan structures for common patterns (research, implementation, testing).
106106+107107+**Why not (for now)**:
108108+- Current plan format is simple enough to create ad-hoc
109109+- Templates might constrain creative decomposition
110110+- Can be added later without breaking changes
111111+112112+**Workaround**: Copy-paste from previous plans; agents can develop their own patterns.
113113+114114+---
115115+116116+## Potential Future Enhancements
117117+118118+These are features that COULD be valuable but aren't prioritized for v1.
119119+120120+### Batch Add Operation
121121+122122+**Problem**: Front-insertion reversal requires adding plans in reverse order.
123123+124124+**Enhancement**: `9plan_queue_add_batch` accepting ordered list of plans, inserting in specified order.
125125+126126+**Example**:
127127+```json
128128+{
129129+ "tool": "9plan_queue_add_batch",
130130+ "arguments": {
131131+ "plans": [
132132+ { "goal": "Plan A", ... },
133133+ { "goal": "Plan B", ... },
134134+ { "goal": "Plan C", ... }
135135+ ],
136136+ "position": "front"
137137+ }
138138+}
139139+```
140140+Result: `[A, B, C, ...rest]` (correct order)
141141+142142+**Complexity**: Lowโqueue manipulation is straightforward.
143143+144144+**Priority**: Mediumโsolves real usability issue.
145145+146146+---
147147+148148+### HTTP Transport
149149+150150+**Problem**: stdio transport requires server to be spawned by client.
151151+152152+**Enhancement**: HTTP/SSE transport for remote access, web clients.
153153+154154+**Benefits**:
155155+- Web-based interfaces could connect
156156+- Multiple clients could connect (though still single-agent)
157157+- Easier debugging/inspection
158158+159159+**Complexity**: Mediumโtransport layer exists, need HTTP endpoints.
160160+161161+**Priority**: Lowโstdio works for current use cases.
162162+163163+---
164164+165165+### Session Export/Import
166166+167167+**Problem**: Sessions are tied to local filesystem; can't share or move.
168168+169169+**Enhancement**: Export session to portable format; import elsewhere.
170170+171171+**Example**:
172172+```bash
173173+9plan export amber-quiet-river > session.json
174174+9plan import session.json # creates new session
175175+```
176176+177177+**Benefits**:
178178+- Share sessions between machines
179179+- Backup/restore beyond filesystem copy
180180+- Hand off sessions to other users
181181+182182+**Complexity**: Mediumโneed serialization format, ID remapping.
183183+184184+**Priority**: Lowโfilesystem copy works for most cases.
185185+186186+---
187187+188188+### Enhanced Search Operators
189189+190190+**Problem**: FTS5 search is keyword-based; can't express complex queries.
191191+192192+**Enhancement**: Support field-specific search, date ranges, status filters.
193193+194194+**Example**:
195195+```json
196196+{
197197+ "tool": "9plan_history_search",
198198+ "arguments": {
199199+ "query": "goal:authentication outputs:client",
200200+ "completed_after": "2024-01-01"
201201+ }
202202+}
203203+```
204204+205205+**Complexity**: Mediumโneed query parser, extended FTS5 usage.
206206+207207+**Priority**: Lowโcurrent search works for dependency resolution.
208208+209209+---
210210+211211+### Progress Indicators
212212+213213+**Problem**: No visibility into session progress without resuming.
214214+215215+**Enhancement**: `9plan_session_status` providing quick overview without full resume.
216216+217217+**Example response**:
218218+```
219219+Session: amber-quiet-river
220220+Status: In progress
221221+Progress: 4/7 plans complete (57%)
222222+Active: k7f3m (since 2 hours ago)
223223+```
224224+225225+**Complexity**: Lowโdata already available.
226226+227227+**Priority**: Lowโresume provides this information.
228228+229229+---
230230+231231+### Plan Estimation
232232+233233+**Problem**: No way to estimate remaining work or session duration.
234234+235235+**Enhancement**: Optional time/effort estimates on plans; rollup to session level.
236236+237237+**Example**:
238238+```json
239239+{
240240+ "tool": "9plan_queue_add",
241241+ "arguments": {
242242+ "goal": "Implement caching",
243243+ "estimate": "2 hours",
244244+ ...
245245+ }
246246+}
247247+```
248248+249249+**Complexity**: Mediumโneed estimation tracking, rollup logic.
250250+251251+**Priority**: Lowโestimation is notoriously unreliable for development tasks.
252252+253253+---
254254+255255+### Webhook Notifications
256256+257257+**Problem**: No way to notify external systems of plan completions.
258258+259259+**Enhancement**: Configure webhooks for session events.
260260+261261+**Use cases**:
262262+- Slack notification when session completes
263263+- Trigger CI/CD on certain plan completions
264264+- Audit logging to external system
265265+266266+**Complexity**: Highโneed configuration, retry logic, security.
267267+268268+**Priority**: Lowโmanual notification works for current use cases.
269269+270270+---
271271+272272+### LLM-Assisted History Search
273273+274274+**Problem**: Semantic search depends on exact keyword matches.
275275+276276+**Enhancement**: Use embeddings or LLM reranking for better search relevance.
277277+278278+**Benefits**:
279279+- Find outputs even with terminology mismatch
280280+- "Smart" understanding of what agent is looking for
281281+282282+**Complexity**: Highโneed embedding infrastructure or LLM integration.
283283+284284+**Priority**: Lowโexplicit output descriptions work well enough.
285285+286286+---
287287+288288+## Ideas Explicitly Rejected
289289+290290+These were considered more deeply and rejected for specific reasons.
291291+292292+### Hierarchical Queue (Parent-Child Tracking)
293293+294294+**Idea**: Maintain explicit parent-child relationships in queue structure.
295295+296296+**Rejected because**:
297297+- Adds complexity to queue management
298298+- Semantic dependency resolution handles relationships
299299+- Notes capture decomposition for aggregation
300300+- Would complicate front/back positioning semantics
301301+302302+---
303303+304304+### Automatic Plan Splitting
305305+306306+**Idea**: System detects large plans and suggests/performs decomposition.
307307+308308+**Rejected because**:
309309+- "Large" is subjective and context-dependent
310310+- Agent is better positioned to know when decomposition helps
311311+- Would require understanding plan content semantically
312312+313313+---
314314+315315+### Undo/Redo Operations
316316+317317+**Idea**: Undo last operation (plan add, complete, defer, discard).
318318+319319+**Rejected because**:
320320+- Many operations have side effects (file deletion, DB changes)
321321+- Would require transaction log, complex state management
322322+- Defer provides "soft undo" for plan lifecycle
323323+- Re-planning is idiomatic for mistakes
324324+325325+---
326326+327327+### Session Archiving
328328+329329+**Idea**: Completed sessions move to archive, freeing active space.
330330+331331+**Rejected because**:
332332+- Filesystem storage is cheap
333333+- Archiving adds state (is it archived or deleted?)
334334+- Manual cleanup is simple and controllable
335335+336336+---
337337+338338+### Plan Dependencies via DAG
339339+340340+**Idea**: Express dependencies as directed acyclic graph, with topological ordering.
341341+342342+**Rejected because**:
343343+- Requires knowing all dependencies upfront
344344+- Doesn't handle dynamically discovered work
345345+- Cross-branch dependencies only visible at decomposition time
346346+- Semantic resolution is more flexible
347347+348348+---
349349+350350+## When to Reconsider
351351+352352+These non-goals or deferrals should be reconsidered if:
353353+354354+| Feature | Reconsider If... |
355355+|---------|------------------|
356356+| Multi-agent concurrency | Use cases emerge requiring parallel execution |
357357+| Priority ordering | Users consistently struggle with front/back |
358358+| Cross-session deps | Common pattern of related sessions emerges |
359359+| HTTP transport | Web-based interfaces become priority |
360360+| Batch add | Decomposition usability complaints increase |
361361+| Plan templates | Common patterns stabilize across users |
362362+363363+---
364364+365365+## Contributing Ideas
366366+367367+If you have ideas for 9plan:
368368+369369+1. **Check this document first**โit may already be addressed
370370+2. **Consider the design principles**โdoes it fit?
371371+ - Self-contained plans
372372+ - Semantic dependencies
373373+ - Single queue discipline
374374+ - Session isolation
375375+3. **Describe the use case**โwhat problem does it solve?
376376+4. **Suggest an approach**โhow would it work?
377377+378378+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
···11+# 9plan Glossary
22+33+A canonical reference for terminology used throughout the 9plan system.
44+55+---
66+77+## Core Concepts
88+99+### Session
1010+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.
1111+1212+**Lifecycle**: Created โ Active (while plans exist) โ Complete (when queue empties)
1313+1414+**Scope**: One session = one task. A project may involve multiple sessions.
1515+1616+### Plan
1717+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`).
1818+1919+**Key property**: A well-formed plan allows an agent resuming from "cold storage" (no memory of prior conversation) to execute successfully.
2020+2121+### Queue
2222+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.
2323+2424+**Position semantics**:
2525+- **Front**: Work that must happen before continuing current work
2626+- **Back**: Work that should happen eventually, after current work
2727+2828+---
2929+3030+## Plan Structure
3131+3232+### Context (Plan Field)
3333+High-level summary explaining the overall task, where this plan fits within it, and why this plan exists. Provides the "big picture" framing.
3434+3535+### Goal (Plan Field)
3636+What this specific plan accomplishes. Should be concrete and achievable.
3737+3838+### Inputs (Plan Field)
3939+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."
4040+4141+### Outputs (Plan Field)
4242+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."
4343+4444+### Approach (Plan Field)
4545+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.
4646+4747+### Success Criteria (Plan Field)
4848+Observable conditions that indicate the plan is complete. Should be concrete and verifiable, not subjective.
4949+5050+### Notes (Plan Field)
5151+Working scratchpad for accumulating context during execution. Used for:
5252+- Progress checkpoints
5353+- Deferral reasons
5454+- Decomposition records (child plan IDs)
5555+- Recovery breadcrumbs
5656+5757+---
5858+5959+## Operations
6060+6161+### Decomposition
6262+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.
6363+6464+**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.
6565+6666+### Aggregation
6767+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.
6868+6969+### Deferral
7070+Returning an active plan to the queue without completing it. Reasons include:
7171+- **Blocked**: Can't proceed, will retry later
7272+- **Decomposition**: Breaking into subplans, will aggregate later
7373+- **Reordering**: Other work should happen first
7474+7575+The deferral reason is appended to the plan's Notes section.
7676+7777+### Completion
7878+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.
7979+8080+### Discarding
8181+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.**
8282+8383+---
8484+8585+## Dependency Concepts
8686+8787+### Semantic Dependency Resolution
8888+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.
8989+9090+**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.
9191+9292+### Cross-Branch Dependency
9393+A dependency between plans that exist in different decomposition branches. Example: The "Feed" branch needs authentication outputs from the "Auth" branch.
9494+9595+**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.
9696+9797+### Input Resolution
9898+The process of finding outputs that satisfy a plan's declared inputs. Steps:
9999+1. Read the plan's Inputs field
100100+2. For each input, call `9plan_history_search` with the description
101101+3. Review matching outcomes to find the required data/artifacts
102102+103103+---
104104+105105+## Context Management
106106+107107+### Context Window
108108+The agent's working memoryโthe portion of conversation history and system state visible to the agent at any given moment. Limited in size.
109109+110110+### Context Compaction
111111+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.
112112+113113+### Session Breadcrumbs
114114+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.
115115+116116+### Progress Checkpointing
117117+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.
118118+119119+---
120120+121121+## Storage Concepts
122122+123123+### Hybrid Storage Model
124124+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.
125125+126126+### Plan File
127127+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).
128128+129129+### History Index
130130+The searchable record of completed plans stored in SQLite with FTS5 (full-text search). Enables semantic dependency resolution via `9plan_history_search`.
131131+132132+---
133133+134134+## Session States
135135+136136+### Active Plan
137137+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.
138138+139139+### Queued Plan
140140+A plan waiting in the queue to be pulled. Has a file in `plans/` and metadata in the database.
141141+142142+### Completed Plan
143143+A plan that was finished. Content is stored in the database (searchable via history). No file exists.
144144+145145+### Discarded Plan
146146+A plan that was abandoned. No record in history, no file. As if it never existed.
147147+148148+---
149149+150150+## Identifiers
151151+152152+### Three-Word Session Name
153153+Human-memorable identifier for sessions. Format: `{adjective}-{adjective}-{noun}` (e.g., `copper-velvet-morning`). Generated using word lists with collision checking.
154154+155155+### Plan ID
156156+Short alphanumeric hash identifying a plan (e.g., `k7f3m`). 5+ characters, generated with collision checking. Intentionally different format from session names to avoid confusion.
157157+158158+---
159159+160160+## Failure Modes
161161+162162+### Orphaned Active State
163163+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.
164164+165165+### Partial Decomposition
166166+When context compaction occurs during the multi-step decomposition process, leaving some subplans queued but the parent still active. Notes checkpointing provides recovery breadcrumbs.
167167+168168+### Missing Plan File
169169+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
···11+# 9plan MCP Server: Implementation Design Document
22+33+**Version**: 1.2.0
44+**Date**: January 2026
55+**Status**: Draft
66+77+---
88+99+## Table of Contents
1010+1111+1. [Executive Summary](#1-executive-summary)
1212+2. [Architecture Overview](#2-architecture-overview)
1313+3. [Technology Stack](#3-technology-stack)
1414+4. [Project Structure](#4-project-structure)
1515+5. [Data Model](#5-data-model)
1616+6. [Tool Specifications](#6-tool-specifications)
1717+7. [Prompt Specifications](#7-prompt-specifications)
1818+8. [File Formats](#8-file-formats)
1919+9. [Logging](#9-logging)
2020+10. [Error Handling](#10-error-handling)
2121+11. [Configuration](#11-configuration)
2222+12. [Dependency Injection & Testability](#12-dependency-injection--testability)
2323+13. [Implementation Phases](#13-implementation-phases)
2424+14. [Known Limitations](#14-known-limitations)
2525+2626+---
2727+2828+## 1. Executive Summary
2929+3030+### 1.1 Purpose
3131+3232+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.
3333+3434+### 1.2 Core Value Proposition
3535+3636+| Challenge | 9plan Solution |
3737+|-----------|----------------|
3838+| Agents lose context during long tasks | Persistent sessions with three-word identifiers for easy resumption |
3939+| Complex tasks require decomposition | Structured plans with explicit inputs/outputs for dependency tracking |
4040+| Work ordering is ambiguous | Single queue with front/back positioning semantics |
4141+| Progress is lost on interruption | Plan files serve as checkpoints; notes preserve state |
4242+4343+### 1.3 Design Principles
4444+4545+1. **Self-Contained Plans**: Each plan contains enough context for an agent to execute without asking clarifying questions
4646+2. **Semantic Dependencies**: Cross-plan dependencies are resolved by searching history, not by ID references
4747+3. **Single Queue Discipline**: No priority tiers or backlogsโone queue that must eventually empty
4848+4. **Hybrid Storage**: SQLite for structural integrity, flat files for agent-accessible content
4949+5. **Session Breadcrumbs**: Tool responses include session names to aid context recovery
5050+5151+---
5252+5353+## 2. Architecture Overview
5454+5555+### 2.1 High-Level Architecture
5656+5757+```
5858+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
5959+โ MCP Client โ
6060+โ (Claude, Cursor, VS Code, etc.) โ
6161+โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
6262+ โ stdio / HTTP
6363+ โผ
6464+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
6565+โ 9plan MCP Server โ
6666+โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
6767+โ โ Tools โ โ Prompts โ โ Server Instructions โ โ
6868+โ โ (9 total) โ โ (bootstrap) โ โ (plan format spec) โ โ
6969+โ โโโโโโโโฌโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
7070+โ โ โ
7171+โ โโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
7272+โ โ Core Services โ โ
7373+โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ โ
7474+โ โ โ Session โ โ Queue โ โ History โ โ โ
7575+โ โ โ Manager โ โ Manager โ โ Search (FTS5) โ โ โ
7676+โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ โ
7777+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
7878+โ โ โ
7979+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
8080+โ โ Storage Layer โ โ
8181+โ โ SQLite (session.db) + Plan Files (plans/*.txt) โ โ
8282+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
8383+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
8484+```
8585+8686+### 2.2 Plan Lifecycle State Machine
8787+8888+```
8989+ โโโโโโโโโโโโโโโโโโโโ
9090+ โ Created โ
9191+ โ (queued) โ
9292+ โโโโโโโโโโฌโโโโโโโโโโ
9393+ โ 9plan_queue_pull
9494+ โผ
9595+ โโโโโโโโโโโโโโโโโโโโ
9696+ โโโโโโโโโโโโ Active โโโโโโโโโโโโ
9797+ โ โโโโโโโโโโโโโโโโโโโโ โ
9898+ โ โ โ
9999+ 9plan_plan_defer 9plan_plan_complete 9plan_plan_discard
100100+ โ โ โ
101101+ โผ โผ โผ
102102+ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
103103+ โ Queued โ โ Completed โ โ Discarded โ
104104+ โ (deferred) โ โ (in history) โ โ (deleted) โ
105105+ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
106106+```
107107+108108+### 2.3 Key Architectural Decisions
109109+110110+| Decision | Rationale |
111111+|----------|-----------|
112112+| **SQLite + flat files** | SQLite protects queue integrity; flat files allow agent filesystem access |
113113+| **stdio transport** | Most MCP clients spawn servers via stdio; HTTP can be added later |
114114+| **Single queue (no priorities)** | Prevents agents from abandoning low-priority work |
115115+| **Three-word session names** | Human-memorable, easy to communicate |
116116+| **Alphanumeric plan IDs** | Short (5 chars), collision-resistant |
117117+| **FTS5 for history search** | Built into SQLite, semantic search over completed plans |
118118+119119+---
120120+121121+## 3. Technology Stack
122122+123123+### 3.1 Core Dependencies
124124+125125+| Component | Technology | Version | Rationale |
126126+|-----------|------------|---------|-----------|
127127+| **Runtime** | Node.js | โฅ22.5.0 | Required for built-in `node:sqlite` |
128128+| **MCP Framework** | @modelcontextprotocol/sdk | ^1.22.0 | Official SDK |
129129+| **MCP Transport** | @karashiiro/mcp | ^1.0.0 | Handles stdio + HTTP with sessions |
130130+| **Schema Validation** | Zod | ^3.25.0 | Runtime validation, env validation |
131131+| **Database** | node:sqlite | built-in | Zero deps, FTS5 support |
132132+| **Session Names** | unique-names-generator | ^4.7.1 | Pre-built dictionaries |
133133+| **Plan IDs** | nanoid | ^5.1.6 | Cryptographically secure, custom alphabets |
134134+| **App Directories** | env-paths | ^3.0.0 | Cross-platform standard directories |
135135+| **Logging** | pino | ^9.0.0 | Ultra-fast structured JSON logging |
136136+137137+### 3.2 Optional Dependencies (HTTP Transport)
138138+139139+| Component | Technology | Version |
140140+|-----------|------------|---------|
141141+| **Web Framework** | hono | ^4.0.0 |
142142+| **Node Adapter** | @hono/node-server | ^1.0.0 |
143143+144144+### 3.3 Development Dependencies
145145+146146+| Component | Technology |
147147+|-----------|------------|
148148+| **Build** | TypeScript ^5.8.0 |
149149+| **Dev Runner** | tsx |
150150+| **Testing** | Vitest |
151151+| **Log Formatting** | pino-pretty |
152152+153153+### 3.4 package.json
154154+155155+```json
156156+{
157157+ "name": "9plan-mcp-server",
158158+ "version": "1.0.0",
159159+ "type": "module",
160160+ "main": "dist/index.js",
161161+ "scripts": {
162162+ "build": "tsc",
163163+ "dev": "tsx watch src/index.ts",
164164+ "start": "node dist/index.js",
165165+ "test": "vitest"
166166+ },
167167+ "dependencies": {
168168+ "@karashiiro/mcp": "^1.0.0",
169169+ "@modelcontextprotocol/sdk": "^1.22.0",
170170+ "env-paths": "^3.0.0",
171171+ "nanoid": "^5.1.6",
172172+ "pino": "^9.0.0",
173173+ "unique-names-generator": "^4.7.1",
174174+ "zod": "^3.25.0"
175175+ },
176176+ "optionalDependencies": {
177177+ "hono": "^4.0.0",
178178+ "@hono/node-server": "^1.0.0"
179179+ },
180180+ "devDependencies": {
181181+ "@types/node": "^22.0.0",
182182+ "pino-pretty": "^11.0.0",
183183+ "tsx": "^4.0.0",
184184+ "typescript": "^5.8.0",
185185+ "vitest": "^3.0.0"
186186+ },
187187+ "engines": {
188188+ "node": ">=22.5.0"
189189+ }
190190+}
191191+```
192192+193193+---
194194+195195+## 4. Project Structure
196196+197197+```
198198+9plan-mcp-server/
199199+โโโ src/
200200+โ โโโ index.ts # Entry point, transport setup
201201+โ โโโ server.ts # McpServer factory, tool/prompt registration
202202+โ โโโ types.ts # Core TypeScript interfaces
203203+โ โโโ env.ts # Environment config with Zod
204204+โ โโโ logger.ts # Pino logger configuration
205205+โ โ
206206+โ โโโ tools/
207207+โ โ โโโ index.ts # Tool registration aggregator
208208+โ โ โโโ session-create.ts
209209+โ โ โโโ session-resume.ts
210210+โ โ โโโ queue-add.ts
211211+โ โ โโโ queue-pull.ts
212212+โ โ โโโ plan-defer.ts
213213+โ โ โโโ plan-complete.ts
214214+โ โ โโโ plan-discard.ts
215215+โ โ โโโ history-search.ts
216216+โ โ โโโ history-get.ts
217217+โ โ
218218+โ โโโ prompts/
219219+โ โ โโโ bootstrap.ts
220220+โ โ
221221+โ โโโ db/
222222+โ โ โโโ schema.sql
223223+โ โ โโโ session-store.ts
224224+โ โ
225225+โ โโโ generators/
226226+โ โ โโโ session-name.ts # Uses unique-names-generator
227227+โ โ โโโ plan-id.ts # Uses nanoid
228228+โ โ
229229+โ โโโ files/
230230+โ โโโ plan-files.ts
231231+โ
232232+โโโ tests/
233233+โ โโโ unit/
234234+โ โ โโโ session-store.test.ts
235235+โ โ โโโ plan-files.test.ts
236236+โ โ โโโ generators.test.ts
237237+โ โโโ integration/
238238+โ โโโ workflow.test.ts
239239+โ
240240+โโโ package.json
241241+โโโ tsconfig.json
242242+โโโ vitest.config.ts
243243+โโโ README.md
244244+```
245245+246246+---
247247+248248+## 5. Data Model
249249+250250+### 5.1 SQLite Schema
251251+252252+```sql
253253+PRAGMA foreign_keys = ON;
254254+255255+CREATE TABLE IF NOT EXISTS sessions (
256256+ name TEXT PRIMARY KEY,
257257+ task_description TEXT,
258258+ created_at TEXT DEFAULT (datetime('now'))
259259+) STRICT;
260260+261261+CREATE TABLE IF NOT EXISTS plans (
262262+ id TEXT PRIMARY KEY,
263263+ session_name TEXT NOT NULL REFERENCES sessions(name) ON DELETE CASCADE,
264264+ status TEXT NOT NULL CHECK (status IN ('queued', 'active', 'completed', 'discarded')),
265265+ queue_position INTEGER,
266266+267267+ goal TEXT NOT NULL,
268268+ context TEXT,
269269+ inputs TEXT,
270270+ outputs TEXT,
271271+ approach TEXT,
272272+ success_criteria TEXT,
273273+ notes TEXT,
274274+ outcome TEXT,
275275+276276+ created_at TEXT DEFAULT (datetime('now')),
277277+ completed_at TEXT
278278+) STRICT;
279279+280280+CREATE INDEX IF NOT EXISTS idx_plans_queue
281281+ ON plans(session_name, status, queue_position)
282282+ WHERE status = 'queued';
283283+284284+CREATE INDEX IF NOT EXISTS idx_plans_active
285285+ ON plans(session_name, status)
286286+ WHERE status = 'active';
287287+288288+-- FTS5 for history search
289289+CREATE VIRTUAL TABLE IF NOT EXISTS plans_fts USING fts5(
290290+ id, goal, context, inputs, outputs, outcome,
291291+ content='plans', content_rowid='rowid'
292292+);
293293+294294+-- Triggers to keep FTS in sync
295295+CREATE TRIGGER IF NOT EXISTS plans_ai AFTER INSERT ON plans BEGIN
296296+ INSERT INTO plans_fts(rowid, id, goal, context, inputs, outputs, outcome)
297297+ VALUES (new.rowid, new.id, new.goal, new.context, new.inputs, new.outputs, new.outcome);
298298+END;
299299+300300+CREATE TRIGGER IF NOT EXISTS plans_ad AFTER DELETE ON plans BEGIN
301301+ INSERT INTO plans_fts(plans_fts, rowid, id, goal, context, inputs, outputs, outcome)
302302+ VALUES ('delete', old.rowid, old.id, old.goal, old.context, old.inputs, old.outputs, old.outcome);
303303+END;
304304+305305+CREATE TRIGGER IF NOT EXISTS plans_au AFTER UPDATE ON plans BEGIN
306306+ INSERT INTO plans_fts(plans_fts, rowid, id, goal, context, inputs, outputs, outcome)
307307+ VALUES ('delete', old.rowid, old.id, old.goal, old.context, old.inputs, old.outputs, old.outcome);
308308+ INSERT INTO plans_fts(rowid, id, goal, context, inputs, outputs, outcome)
309309+ VALUES (new.rowid, new.id, new.goal, new.context, new.inputs, new.outputs, new.outcome);
310310+END;
311311+```
312312+313313+### 5.2 TypeScript Interfaces
314314+315315+```typescript
316316+export type PlanStatus = 'queued' | 'active' | 'completed' | 'discarded';
317317+318318+export interface Session {
319319+ name: string;
320320+ taskDescription: string | null;
321321+ createdAt: string;
322322+}
323323+324324+export interface Plan {
325325+ id: string;
326326+ sessionName: string;
327327+ status: PlanStatus;
328328+ queuePosition: number | null;
329329+ goal: string;
330330+ context: string | null;
331331+ inputs: string | null;
332332+ outputs: string | null;
333333+ approach: string | null;
334334+ successCriteria: string | null;
335335+ notes: string | null;
336336+ outcome: string | null;
337337+ createdAt: string;
338338+ completedAt: string | null;
339339+}
340340+341341+export interface QueuedPlan {
342342+ id: string;
343343+ goal: string;
344344+ queuePosition: number;
345345+}
346346+347347+export interface SessionState {
348348+ sessionName: string;
349349+ sessionPath: string;
350350+ taskDescription: string | null;
351351+ queue: QueuedPlan[];
352352+ activePlan: { id: string; filePath: string } | null;
353353+ completedCount: number;
354354+}
355355+356356+export interface HistoryMatch {
357357+ id: string;
358358+ goal: string;
359359+ outcome: string | null;
360360+ relevanceScore: number;
361361+}
362362+```
363363+364364+---
365365+366366+## 6. Tool Specifications
367367+368368+All tools use the prefix `9plan_` with snake_case.
369369+370370+### 6.1 Session Tools
371371+372372+#### `9plan_session_create`
373373+374374+Creates a new session with a randomly-generated three-word identifier.
375375+376376+**Input**: `{ task_description?: string }`
377377+378378+**Output**: `{ session_name: string, session_path: string }`
379379+380380+**Behavior**:
381381+1. Generate unique three-word name
382382+2. Create session directory with `session.db` and `plans/`
383383+3. Initialize SQLite with schema
384384+4. Return session name and path
385385+386386+---
387387+388388+#### `9plan_session_resume`
389389+390390+Loads an existing session by name.
391391+392392+**Input**: `{ session_name: string }` (format: `word-word-word`)
393393+394394+**Output**: Full session state including queue, active plan, and completed count.
395395+396396+---
397397+398398+### 6.2 Queue Tools
399399+400400+#### `9plan_queue_add`
401401+402402+Adds a new plan to the queue.
403403+404404+**Input**:
405405+```typescript
406406+{
407407+ context: string, // Where this plan fits in overall task
408408+ goal: string, // What this plan accomplishes
409409+ approach: string, // How to accomplish the goal
410410+ success_criteria: string,
411411+ inputs?: string, // Dependencies from other plans
412412+ outputs?: string, // What this produces
413413+ position?: 'front' | 'back' // Default: 'back'
414414+}
415415+```
416416+417417+**Output**: `{ plan_id: string, plan_path: string, queue_position: number }`
418418+419419+**Position Semantics**:
420420+- `front`: Blocking work that must complete first
421421+- `back`: Work that should happen eventually
422422+423423+---
424424+425425+#### `9plan_queue_pull`
426426+427427+Removes the front plan from queue and marks it active.
428428+429429+**Input**: None
430430+431431+**Output**: `{ plan_id: string, plan_path: string, goal: string }`
432432+433433+**Errors**:
434434+- `PLAN_ALREADY_ACTIVE`: Must complete/defer/discard current plan first
435435+- `QUEUE_EMPTY`: Task complete! ๐
436436+437437+---
438438+439439+### 6.3 Plan Lifecycle Tools
440440+441441+#### `9plan_plan_defer`
442442+443443+Returns active plan to queue without completing it.
444444+445445+**Input**: `{ reason: string, position?: 'front' | 'back' }`
446446+447447+**Behavior**: Appends reason to Notes section with timestamp, returns to queue.
448448+449449+**Use Cases**:
450450+- **Blocked**: Can't proceed โ `position: 'front'`
451451+- **Decomposition**: Breaking into subplans โ `position: 'back'`
452452+453453+---
454454+455455+#### `9plan_plan_complete`
456456+457457+Marks active plan as done.
458458+459459+**Input**: `{ outcome: string }` โ Summary including outputs produced
460460+461461+**Behavior**:
462462+1. Store outcome in database
463463+2. Index into FTS5 for history search
464464+3. Delete plan file
465465+4. Return queue status
466466+467467+---
468468+469469+#### `9plan_plan_discard`
470470+471471+Abandons active plan without completing.
472472+473473+**Input**: `{ reason: string }`
474474+475475+**Behavior**: Marks as discarded (NOT in searchable history), deletes file.
476476+477477+**Use When**: Plan is obsolete, unnecessary, or misconceived.
478478+479479+---
480480+481481+### 6.4 History Tools
482482+483483+#### `9plan_history_search`
484484+485485+Searches completed plans for outputs matching a query.
486486+487487+**Input**: `{ query: string, max_results?: number }`
488488+489489+**Output**: Array of matches with `{ id, goal, outcome, relevance_rank }`
490490+491491+Uses FTS5 MATCH with BM25 ranking across goal, context, inputs, outputs, outcome.
492492+493493+---
494494+495495+#### `9plan_history_get`
496496+497497+Retrieves a specific completed plan by ID.
498498+499499+**Input**: `{ plan_id: string }` (5 alphanumeric chars)
500500+501501+**Output**: Full plan content including outcome.
502502+503503+---
504504+505505+## 7. Prompt Specifications
506506+507507+### `bootstrap`
508508+509509+Guides initial session setup and planning decomposition.
510510+511511+**Arguments**: `{ task?: string }`
512512+513513+**Content**:
514514+```
515515+You are helping the user set up a new 9plan session for a complex task.
516516+517517+WORKFLOW:
518518+1. CLARIFY: Ask questions to understand scope, components, dependencies
519519+2. CREATE SESSION: Use 9plan_session_create with task description
520520+3. DECOMPOSE: Break into discrete, self-contained plans
521521+4. CAPTURE DEPENDENCIES: Specify inputs/outputs for each plan
522522+5. ENQUEUE: Add plans with 9plan_queue_add (respect dependency order)
523523+6. START WORK: Pull first plan with 9plan_queue_pull
524524+525525+PLAN QUALITY CHECKLIST:
526526+โก Goal is specific and measurable
527527+โก Approach has concrete steps
528528+โก Success criteria are observable
529529+โก Inputs reference other plans by description
530530+โก Outputs are specific enough to search for
531531+532532+NOTE: Adding A, B, C at 'front' gives [C, B, A] โ add in reverse order if needed.
533533+```
534534+535535+---
536536+537537+## 8. File Formats
538538+539539+### 8.1 Plan File Format
540540+541541+**Location**: `~/.9plan/sessions/{session-name}/plans/{plan-id}.txt`
542542+543543+```
544544+# Context
545545+{Where this plan fits in the overall task}
546546+547547+# Goal
548548+{What this plan accomplishes}
549549+550550+# Inputs
551551+{Dependencies - format: "- description: source"}
552552+553553+# Outputs
554554+{What this produces - format: "- description: details"}
555555+556556+# Approach
557557+{Detailed, actionable steps}
558558+559559+# Success Criteria
560560+{Observable completion conditions}
561561+562562+# Notes
563563+{Working scratchpad - updated during execution}
564564+```
565565+566566+---
567567+568568+## 9. Logging
569569+570570+### 9.1 Logger Setup
571571+572572+```typescript
573573+// src/logger.ts
574574+import pino from 'pino';
575575+import { env } from './env.js';
576576+577577+export const logger = process.env.NODE_ENV === 'development'
578578+ ? pino({
579579+ level: env.NINEPLAN_LOG_LEVEL,
580580+ transport: {
581581+ target: 'pino-pretty',
582582+ options: { colorize: true, translateTime: 'SYS:standard' },
583583+ },
584584+ })
585585+ : pino({ level: env.NINEPLAN_LOG_LEVEL });
586586+587587+export type Logger = pino.Logger;
588588+```
589589+590590+### 9.2 Session-Scoped Child Loggers
591591+592592+```typescript
593593+export class SessionStore {
594594+ private log: Logger;
595595+596596+ constructor(sessionName: string) {
597597+ this.log = logger.child({ session: sessionName });
598598+ }
599599+600600+ addPlan(plan: Plan): void {
601601+ this.log.info({ planId: plan.id, goal: plan.goal }, 'Plan added');
602602+ }
603603+}
604604+```
605605+606606+### 9.3 Log Levels
607607+608608+| Level | Usage |
609609+|-------|-------|
610610+| `debug` | SQL queries, queue state |
611611+| `info` | Session created, plan pulled/completed |
612612+| `warn` | Collision retry, missing optional field |
613613+| `error` | DB error, file write failure |
614614+615615+---
616616+617617+## 10. Error Handling
618618+619619+### 10.1 Error Categories
620620+621621+| Category | Description |
622622+|----------|-------------|
623623+| `SESSION_NOT_FOUND` | Session does not exist |
624624+| `PLAN_NOT_FOUND` | Plan ID does not exist |
625625+| `NO_ACTIVE_PLAN` | Operation requires active plan |
626626+| `PLAN_ALREADY_ACTIVE` | Cannot pull; another plan is active |
627627+| `QUEUE_EMPTY` | Queue is empty (task complete) |
628628+| `VALIDATION_ERROR` | Input validation failed |
629629+| `FILE_SYSTEM_ERROR` | Filesystem operation failed |
630630+| `DATABASE_ERROR` | SQLite operation failed |
631631+632632+### 10.2 Error Response Format
633633+634634+```
635635+[Session: {name}] Error: {category}
636636+637637+{message}
638638+639639+Suggested action: {suggestion}
640640+```
641641+642642+---
643643+644644+## 11. Configuration
645645+646646+### 11.1 Environment Variables
647647+648648+```typescript
649649+// src/env.ts
650650+import { z } from 'zod';
651651+import envPaths from 'env-paths';
652652+import { join } from 'node:path';
653653+654654+const paths = envPaths('9plan', { suffix: '' });
655655+656656+const envSchema = z.object({
657657+ NINEPLAN_SESSIONS_PATH: z.string().default(join(paths.data, 'sessions')),
658658+ NINEPLAN_LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
659659+ NINEPLAN_TRANSPORT: z.enum(['stdio', 'http']).default('stdio'),
660660+ NINEPLAN_HTTP_PORT: z.coerce.number().default(8080),
661661+ NINEPLAN_HTTP_HOST: z.string().default('127.0.0.1'),
662662+});
663663+664664+export const env = envSchema.parse(process.env);
665665+```
666666+667667+### 11.2 Entry Point
668668+669669+```typescript
670670+// src/index.ts
671671+import { serveStdio } from "@karashiiro/mcp/stdio";
672672+import { serveHttp } from "@karashiiro/mcp/http";
673673+import { createServer } from "./server.js";
674674+import { logger } from "./logger.js";
675675+import { env } from "./env.js";
676676+677677+if (env.NINEPLAN_TRANSPORT === "http") {
678678+ const handle = await serveHttp(createServer, {
679679+ port: env.NINEPLAN_HTTP_PORT,
680680+ host: env.NINEPLAN_HTTP_HOST,
681681+ endpoint: "/mcp",
682682+ });
683683+684684+ logger.info({ port: env.NINEPLAN_HTTP_PORT }, '9plan running (HTTP)');
685685+686686+ process.on("SIGINT", async () => {
687687+ await handle.close();
688688+ process.exit(0);
689689+ });
690690+} else {
691691+ logger.info('9plan running (stdio)');
692692+ await serveStdio(createServer);
693693+}
694694+```
695695+696696+### 11.3 MCP Client Configuration
697697+698698+**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
699699+```json
700700+{
701701+ "mcpServers": {
702702+ "9plan": {
703703+ "command": "node",
704704+ "args": ["/path/to/9plan-mcp-server/dist/index.js"]
705705+ }
706706+ }
707707+}
708708+```
709709+710710+---
711711+712712+## 12. Dependency Injection & Testability
713713+714714+We use **constructor injection** for testability without a DI framework. This keeps the codebase simple while allowing full mockability in tests.
715715+716716+### 12.1 The Pattern
717717+718718+All classes accept their dependencies via an options object in the constructor:
719719+720720+```typescript
721721+// src/types.ts - Dependency interfaces
722722+export interface SessionStoreDeps {
723723+ logger: Logger;
724724+ generatePlanId: () => string;
725725+ planFiles: PlanFileHandler;
726726+}
727727+728728+export interface PlanFileHandlerDeps {
729729+ logger: Logger;
730730+}
731731+```
732732+733733+### 12.2 Implementation Example
734734+735735+```typescript
736736+// src/db/session-store.ts
737737+import type { Logger } from '../logger.js';
738738+import type { Plan, SessionStoreDeps } from '../types.js';
739739+740740+export class SessionStore {
741741+ private log: Logger;
742742+ private generatePlanId: () => string;
743743+ private planFiles: PlanFileHandler;
744744+745745+ constructor(
746746+ private sessionName: string,
747747+ private sessionPath: string,
748748+ deps: SessionStoreDeps,
749749+ ) {
750750+ this.log = deps.logger.child({ session: sessionName });
751751+ this.generatePlanId = deps.generatePlanId;
752752+ this.planFiles = deps.planFiles;
753753+ }
754754+755755+ addPlan(input: PlanInput): Plan {
756756+ const id = this.generatePlanId();
757757+ this.log.info({ planId: id, goal: input.goal }, 'Adding plan');
758758+ // ... implementation
759759+ }
760760+}
761761+```
762762+763763+### 12.3 Production Wiring
764764+765765+```typescript
766766+// src/container.ts - Simple factory functions, no framework needed
767767+import { logger } from './logger.js';
768768+import { SessionStore } from './db/session-store.js';
769769+import { PlanFileHandler } from './files/plan-files.js';
770770+import { generatePlanId } from './generators/plan-id.js';
771771+772772+export function createPlanFileHandler(sessionPath: string): PlanFileHandler {
773773+ return new PlanFileHandler(sessionPath, { logger });
774774+}
775775+776776+export function createSessionStore(
777777+ sessionName: string,
778778+ sessionPath: string,
779779+): SessionStore {
780780+ return new SessionStore(sessionName, sessionPath, {
781781+ logger,
782782+ generatePlanId,
783783+ planFiles: createPlanFileHandler(sessionPath),
784784+ });
785785+}
786786+```
787787+788788+### 12.4 Testing with Mocks
789789+790790+```typescript
791791+// tests/unit/session-store.test.ts
792792+import { describe, it, expect, vi, beforeEach } from 'vitest';
793793+import { SessionStore } from '../../src/db/session-store.js';
794794+795795+describe('SessionStore', () => {
796796+ // Mock factory - creates fresh mocks for each test
797797+ const createMocks = () => ({
798798+ logger: {
799799+ info: vi.fn(),
800800+ error: vi.fn(),
801801+ warn: vi.fn(),
802802+ debug: vi.fn(),
803803+ child: vi.fn(() => ({
804804+ info: vi.fn(),
805805+ error: vi.fn(),
806806+ warn: vi.fn(),
807807+ debug: vi.fn(),
808808+ })),
809809+ },
810810+ generatePlanId: vi.fn(() => 'test1'),
811811+ planFiles: {
812812+ write: vi.fn(),
813813+ read: vi.fn(),
814814+ delete: vi.fn(),
815815+ exists: vi.fn(() => false),
816816+ },
817817+ });
818818+819819+ it('should generate ID and log when adding plan', () => {
820820+ const mocks = createMocks();
821821+ const store = new SessionStore('test-session', '/tmp/test', mocks);
822822+823823+ store.addPlan({
824824+ goal: 'Test the thing',
825825+ context: 'Testing context',
826826+ approach: 'Test approach',
827827+ successCriteria: 'Tests pass',
828828+ });
829829+830830+ expect(mocks.generatePlanId).toHaveBeenCalled();
831831+ expect(mocks.logger.child).toHaveBeenCalledWith({ session: 'test-session' });
832832+ });
833833+834834+ it('should write plan file when adding plan', () => {
835835+ const mocks = createMocks();
836836+ const store = new SessionStore('test-session', '/tmp/test', mocks);
837837+838838+ store.addPlan({
839839+ goal: 'Test the thing',
840840+ context: 'Testing context',
841841+ approach: 'Test approach',
842842+ successCriteria: 'Tests pass',
843843+ });
844844+845845+ expect(mocks.planFiles.write).toHaveBeenCalled();
846846+ });
847847+848848+ it('should throw when plan file write fails', () => {
849849+ const mocks = createMocks();
850850+ mocks.planFiles.write.mockImplementation(() => {
851851+ throw new Error('Disk full');
852852+ });
853853+854854+ const store = new SessionStore('test-session', '/tmp/test', mocks);
855855+856856+ expect(() => store.addPlan({
857857+ goal: 'Test',
858858+ context: 'Context',
859859+ approach: 'Approach',
860860+ successCriteria: 'Criteria',
861861+ })).toThrow('Disk full');
862862+ });
863863+});
864864+```
865865+866866+### 12.5 Testing Tools
867867+868868+Tools receive their dependencies through the session store:
869869+870870+```typescript
871871+// tests/unit/tools/queue-add.test.ts
872872+import { describe, it, expect, vi } from 'vitest';
873873+import { handleQueueAdd } from '../../../src/tools/queue-add.js';
874874+875875+describe('9plan_queue_add', () => {
876876+ it('should return plan ID and path on success', async () => {
877877+ const mockStore = {
878878+ addPlan: vi.fn(() => ({
879879+ id: 'abc12',
880880+ queuePosition: 1,
881881+ })),
882882+ getSessionPath: vi.fn(() => '/tmp/sessions/test-session'),
883883+ };
884884+885885+ const result = await handleQueueAdd(mockStore, {
886886+ goal: 'Do something',
887887+ context: 'Context here',
888888+ approach: 'Step by step',
889889+ success_criteria: 'It works',
890890+ });
891891+892892+ expect(result.plan_id).toBe('abc12');
893893+ expect(result.plan_path).toContain('abc12.txt');
894894+ });
895895+});
896896+```
897897+898898+### 12.6 Why Not a DI Container?
899899+900900+| Aspect | Manual Injection | DI Container (inversify/tsyringe) |
901901+|--------|------------------|-----------------------------------|
902902+| **Testability** | โ Full | โ Full |
903903+| **Lines of setup** | ~20 | ~80+ |
904904+| **Extra dependencies** | None | reflect-metadata, decorators |
905905+| **Decorator noise** | None | `@injectable()` on every class |
906906+| **Type safety** | โ Native TS | Requires Symbol tokens |
907907+| **Learning curve** | None | Moderate |
908908+909909+For a project with <10 injectable services, manual wiring is simpler and equally testable.
910910+911911+---
912912+913913+## 13. Implementation Phases
914914+915915+### Phase 1: Foundation
916916+917917+- [ ] Project scaffolding (package.json, tsconfig.json)
918918+- [ ] Install dependencies
919919+- [ ] Logger setup
920920+- [ ] TypeScript interfaces (including dep interfaces)
921921+- [ ] SQLite schema
922922+- [ ] Session name generator (unique-names-generator)
923923+- [ ] Plan ID generator (nanoid)
924924+- [ ] Entry point with transport
925925+926926+### Phase 2: Session Tools
927927+928928+- [ ] `9plan_session_create`
929929+- [ ] `9plan_session_resume`
930930+- [ ] Session directory management
931931+- [ ] Database initialization
932932+- [ ] Container/factory functions
933933+934934+### Phase 3: Queue Tools
935935+936936+- [ ] `9plan_queue_add`
937937+- [ ] `9plan_queue_pull`
938938+- [ ] Plan file creation/reading
939939+- [ ] Queue position management
940940+- [ ] Unit tests for queue operations
941941+942942+### Phase 4: Plan Lifecycle Tools
943943+944944+- [ ] `9plan_plan_defer`
945945+- [ ] `9plan_plan_complete`
946946+- [ ] `9plan_plan_discard`
947947+- [ ] Notes accumulation
948948+- [ ] FTS5 indexing
949949+- [ ] Unit tests for lifecycle
950950+951951+### Phase 5: History Tools
952952+953953+- [ ] `9plan_history_search`
954954+- [ ] `9plan_history_get`
955955+- [ ] FTS5 query implementation
956956+- [ ] Unit tests for search
957957+958958+### Phase 6: Polish
959959+960960+- [ ] `bootstrap` prompt
961961+- [ ] Server instructions
962962+- [ ] Error messages
963963+- [ ] Integration tests
964964+- [ ] Documentation
965965+966966+---
967967+968968+## 14. Known Limitations
969969+970970+### Front-Insertion Order Reversal
971971+972972+Adding A, B, C at front gives `[C, B, A, ...]`. Add subplans in reverse order to achieve correct sequence.
973973+974974+### Context Compaction Mid-Plan
975975+976976+If compaction occurs while executing a plan, the agent loses session awareness. Session name is included in all tool responses to aid recovery.
977977+978978+### Single Agent Per Session
979979+980980+No concurrent access supportโdesigned for single-agent workflows.
981981+982982+---
983983+984984+## Appendix: Server Instructions
985985+986986+The MCP server's `instructions` field:
987987+988988+```
989989+9plan manages session-scoped work queues for agent task sequencing.
990990+991991+QUICK START:
992992+1. Create session: 9plan_session_create
993993+2. Add plans: 9plan_queue_add ('front' for blocking, 'back' for eventual)
994994+3. Get work: 9plan_queue_pull
995995+4. Execute the plan (read file for full context)
996996+5. Complete: 9plan_plan_complete (or defer/discard)
997997+6. Repeat until queue is empty
998998+999999+PLAN FILE FORMAT:
10001000+# Context, # Goal, # Inputs, # Outputs, # Approach, # Success Criteria, # Notes
10011001+10021002+DEPENDENCY RESOLUTION:
10031003+- Use 9plan_history_search to find outputs from completed plans
10041004+- Inputs reference plans by description, not ID
10051005+10061006+SESSION RECOVERY:
10071007+- All responses include [Session: name] prefix
10081008+- Resume with: 9plan_session_resume
10091009+```
10101010+10111011+---
10121012+10131013+*End of Implementation Design Document*
+440
docs/testing-philosophy.md
···11+# 9plan Testing Philosophy
22+33+This document explains what we test, why we test it, and how to maintain testing consistency as the codebase evolves.
44+55+---
66+77+## Testing Goals
88+99+### Primary Goals
1010+1111+1. **Protect invariants**: Queue ordering, active plan exclusivity, session isolation
1212+2. **Verify state transitions**: Plan lifecycle (queued โ active โ completed/deferred/discarded)
1313+3. **Ensure data integrity**: File/database consistency, FTS indexing
1414+4. **Catch regressions**: Prevent working functionality from breaking
1515+1616+### Non-Goals
1717+1818+- **100% coverage**: We don't chase coverage metrics; we test what matters
1919+- **Testing library internals**: We trust nanoid, unique-names-generator, SQLite
2020+- **Performance benchmarks**: Not a primary concern for this tool's use case
2121+2222+---
2323+2424+## What We Test
2525+2626+### Must Test: Invariants
2727+2828+These are properties that must ALWAYS hold. Violations indicate serious bugs.
2929+3030+| Invariant | Test Approach |
3131+|-----------|---------------|
3232+| Only one plan can be active at a time | Test that `queue_pull` with active plan returns error |
3333+| Queue order is preserved | Add plans, verify pull order matches expected |
3434+| Plan IDs are unique within session | Generate many IDs, check for collisions |
3535+| Session names are unique | Generate many names, check for collisions |
3636+| Completed plans are searchable | Complete a plan, search for its outputs |
3737+| Plan files exist for queued/active plans | After add, verify file exists; after complete, verify deleted |
3838+3939+### Must Test: State Transitions
4040+4141+Each plan lifecycle transition should be tested.
4242+4343+```
4444+Created (queued)
4545+ โ
4646+ โโ pull โโโ Active
4747+ โ โ
4848+ โ โโ complete โโโ Completed (in history)
4949+ โ โ
5050+ โ โโ defer โโโ Queued (back in queue)
5151+ โ โ
5252+ โ โโ discard โโโ Discarded (gone)
5353+ โ
5454+ โโ (invalid: can't complete/defer/discard from queued)
5555+```
5656+5757+Test each valid transition AND test that invalid transitions return errors.
5858+5959+### Must Test: Error Conditions
6060+6161+| Scenario | Expected Behavior |
6262+|----------|-------------------|
6363+| Pull with no plans in queue | Returns "queue empty" message |
6464+| Pull with active plan already | Returns error |
6565+| Complete with no active plan | Returns error |
6666+| Defer with no active plan | Returns error |
6767+| Discard with no active plan | Returns error |
6868+| Resume non-existent session | Returns error |
6969+| History search with no matches | Returns empty results |
7070+| History get with invalid ID | Returns error |
7171+7272+### Should Test: Integration Scenarios
7373+7474+These verify components work together correctly.
7575+7676+| Scenario | What It Tests |
7777+|----------|---------------|
7878+| Full plan lifecycle | Create โ Pull โ Complete โ Search |
7979+| Decomposition workflow | Pull โ Add children โ Defer โ Pull children โ Complete children โ Pull parent |
8080+| Dependency resolution | Complete plan A with outputs โ Add plan B with inputs โ Resolve via search |
8181+| Session resume | Create โ Add plans โ "Restart" โ Resume โ Verify state |
8282+| Progress checkpointing | Pull โ Update notes โ Verify notes persist |
8383+8484+### Should Test: Edge Cases
8585+8686+| Edge Case | Why It Matters |
8787+|-----------|----------------|
8888+| Empty inputs/outputs fields | Optional fields shouldn't cause errors |
8989+| Very long plan content | No truncation or corruption |
9090+| Special characters in plans | Proper escaping, no injection |
9191+| Unicode in session names/plans | Correct handling throughout |
9292+| Plan with same goal added twice | Should work (different IDs) |
9393+| Defer then pull same plan | Should work, notes accumulate |
9494+9595+### Don't Test: External Libraries
9696+9797+We trust these to work correctly:
9898+9999+- **nanoid**: Cryptographic randomness
100100+- **unique-names-generator**: Word selection
101101+- **SQLite**: Database operations
102102+- **Zod**: Schema validation
103103+- **Pino**: Logging
104104+105105+If we find bugs in these, we report upstream rather than work around with tests.
106106+107107+---
108108+109109+## Testing Layers
110110+111111+### Unit Tests
112112+113113+**Scope**: Individual functions and classes in isolation.
114114+115115+**Location**: `tests/unit/`
116116+117117+**Dependencies**: Mocked via dependency injection.
118118+119119+**What to test**:
120120+- `SessionStore` methods with mocked file handler and ID generator
121121+- `PlanFileHandler` with mocked filesystem (or temp directory)
122122+- ID/name generators (basic functionality, collision detection)
123123+124124+**Example**:
125125+```typescript
126126+describe('SessionStore.addPlan', () => {
127127+ it('should generate unique ID', () => {
128128+ const mocks = createMocks();
129129+ const store = new SessionStore('test', '/tmp', mocks);
130130+ store.addPlan({ goal: 'Test', ... });
131131+ expect(mocks.generatePlanId).toHaveBeenCalled();
132132+ });
133133+134134+ it('should write plan file', () => {
135135+ const mocks = createMocks();
136136+ const store = new SessionStore('test', '/tmp', mocks);
137137+ store.addPlan({ goal: 'Test', ... });
138138+ expect(mocks.planFiles.write).toHaveBeenCalled();
139139+ });
140140+});
141141+```
142142+143143+### Integration Tests
144144+145145+**Scope**: Multiple components working together with real implementations.
146146+147147+**Location**: `tests/integration/`
148148+149149+**Dependencies**: Real SQLite (in-memory or temp file), real filesystem (temp directory).
150150+151151+**What to test**:
152152+- Complete workflows (bootstrap โ execute โ complete)
153153+- File/database consistency
154154+- FTS5 search accuracy
155155+156156+**Example**:
157157+```typescript
158158+describe('Full plan lifecycle', () => {
159159+ let sessionPath: string;
160160+ let store: SessionStore;
161161+162162+ beforeEach(() => {
163163+ sessionPath = fs.mkdtempSync('/tmp/9plan-test-');
164164+ store = createSessionStore('test-session', sessionPath);
165165+ });
166166+167167+ afterEach(() => {
168168+ fs.rmSync(sessionPath, { recursive: true });
169169+ });
170170+171171+ it('should complete full lifecycle', () => {
172172+ // Add plan
173173+ const { id } = store.addPlan({ goal: 'Test goal', ... });
174174+175175+ // Pull (activates)
176176+ const pulled = store.pull();
177177+ expect(pulled.id).toBe(id);
178178+179179+ // Complete
180180+ store.complete({ outcome: 'Done' });
181181+182182+ // Search should find it
183183+ const results = store.searchHistory('Test goal');
184184+ expect(results).toHaveLength(1);
185185+ expect(results[0].id).toBe(id);
186186+ });
187187+});
188188+```
189189+190190+### Tool Tests
191191+192192+**Scope**: MCP tool handlers with mocked store.
193193+194194+**Location**: `tests/unit/tools/`
195195+196196+**What to test**:
197197+- Input validation (Zod schemas)
198198+- Correct store method calls
199199+- Response formatting
200200+201201+**Example**:
202202+```typescript
203203+describe('9plan_queue_add', () => {
204204+ it('should validate required fields', async () => {
205205+ const mockStore = createMockStore();
206206+207207+ await expect(handleQueueAdd(mockStore, {
208208+ goal: 'Test',
209209+ // missing: context, approach, success_criteria
210210+ })).rejects.toThrow(/required/);
211211+ });
212212+213213+ it('should return plan ID and path', async () => {
214214+ const mockStore = createMockStore();
215215+ mockStore.addPlan.mockReturnValue({ id: 'abc12', queuePosition: 1 });
216216+217217+ const result = await handleQueueAdd(mockStore, validPlanInput);
218218+219219+ expect(result.plan_id).toBe('abc12');
220220+ expect(result.plan_path).toContain('abc12.txt');
221221+ });
222222+});
223223+```
224224+225225+---
226226+227227+## Test Utilities
228228+229229+### Mock Factory Pattern
230230+231231+Create fresh mocks for each test to avoid state leakage:
232232+233233+```typescript
234234+// tests/helpers/mocks.ts
235235+export const createMocks = () => ({
236236+ logger: {
237237+ info: vi.fn(),
238238+ error: vi.fn(),
239239+ warn: vi.fn(),
240240+ debug: vi.fn(),
241241+ child: vi.fn().mockReturnThis(),
242242+ },
243243+ generatePlanId: vi.fn(() => `test${Math.random().toString(36).slice(2, 5)}`),
244244+ planFiles: {
245245+ write: vi.fn(),
246246+ read: vi.fn(),
247247+ delete: vi.fn(),
248248+ exists: vi.fn(() => true),
249249+ },
250250+});
251251+```
252252+253253+### Temp Directory Helper
254254+255255+For integration tests that need real filesystem:
256256+257257+```typescript
258258+// tests/helpers/temp.ts
259259+import { mkdtempSync, rmSync } from 'fs';
260260+import { tmpdir } from 'os';
261261+import { join } from 'path';
262262+263263+export function withTempDir(fn: (dir: string) => void | Promise<void>) {
264264+ const dir = mkdtempSync(join(tmpdir(), '9plan-test-'));
265265+ try {
266266+ return fn(dir);
267267+ } finally {
268268+ rmSync(dir, { recursive: true, force: true });
269269+ }
270270+}
271271+```
272272+273273+### Test Fixtures
274274+275275+Standard plan data for consistent testing:
276276+277277+```typescript
278278+// tests/fixtures/plans.ts
279279+export const validPlan = {
280280+ context: 'Test context for unit testing',
281281+ goal: 'Verify the system works',
282282+ approach: 'Run assertions against expected behavior',
283283+ successCriteria: 'All assertions pass',
284284+ inputs: '',
285285+ outputs: 'Test results',
286286+};
287287+288288+export const planWithDependencies = {
289289+ context: 'Building feature that depends on auth',
290290+ goal: 'Implement feature X',
291291+ approach: 'Use auth module',
292292+ successCriteria: 'Feature works with auth',
293293+ inputs: 'auth_client module from auth work',
294294+ outputs: 'Feature X implementation',
295295+};
296296+```
297297+298298+---
299299+300300+## Test Organization
301301+302302+### File Structure
303303+304304+```
305305+tests/
306306+โโโ unit/
307307+โ โโโ db/
308308+โ โ โโโ session-store.test.ts
309309+โ โโโ files/
310310+โ โ โโโ plan-files.test.ts
311311+โ โโโ generators/
312312+โ โ โโโ session-name.test.ts
313313+โ โ โโโ plan-id.test.ts
314314+โ โโโ tools/
315315+โ โโโ session-create.test.ts
316316+โ โโโ queue-add.test.ts
317317+โ โโโ ...
318318+โโโ integration/
319319+โ โโโ lifecycle.test.ts
320320+โ โโโ decomposition.test.ts
321321+โ โโโ history-search.test.ts
322322+โโโ fixtures/
323323+โ โโโ plans.ts
324324+โโโ helpers/
325325+ โโโ mocks.ts
326326+ โโโ temp.ts
327327+```
328328+329329+### Naming Conventions
330330+331331+- Test files: `{module}.test.ts`
332332+- Describe blocks: Module or function name
333333+- Test names: `should {expected behavior} [when {condition}]`
334334+335335+```typescript
336336+describe('SessionStore', () => {
337337+ describe('addPlan', () => {
338338+ it('should generate unique plan ID', () => {});
339339+ it('should add plan to back of queue by default', () => {});
340340+ it('should add plan to front of queue when position is front', () => {});
341341+ it('should throw when required fields missing', () => {});
342342+ });
343343+});
344344+```
345345+346346+---
347347+348348+## Running Tests
349349+350350+### All Tests
351351+352352+```bash
353353+npm test
354354+```
355355+356356+### Watch Mode (Development)
357357+358358+```bash
359359+npm test -- --watch
360360+```
361361+362362+### Specific File
363363+364364+```bash
365365+npm test -- tests/unit/db/session-store.test.ts
366366+```
367367+368368+### Coverage Report
369369+370370+```bash
371371+npm test -- --coverage
372372+```
373373+374374+### CI Configuration
375375+376376+```yaml
377377+# .github/workflows/test.yml
378378+test:
379379+ runs-on: ubuntu-latest
380380+ steps:
381381+ - uses: actions/checkout@v4
382382+ - uses: actions/setup-node@v4
383383+ with:
384384+ node-version: '22'
385385+ - run: npm ci
386386+ - run: npm test
387387+```
388388+389389+---
390390+391391+## When to Add Tests
392392+393393+### Always Add Tests For
394394+395395+- Bug fixes (test reproduces the bug, then verify fix)
396396+- New tools (full happy path + error cases)
397397+- Invariant changes (if we change what must always be true)
398398+- Complex logic (anything with multiple branches or state)
399399+400400+### Consider Skipping Tests For
401401+402402+- Simple pass-through functions
403403+- Logging statements
404404+- Configuration parsing (Zod handles validation)
405405+- One-line utility functions
406406+407407+### Red Flags (Add Tests Immediately)
408408+409409+- "I'm not sure if this edge case works"
410410+- "This broke once before"
411411+- "This interacts with multiple components"
412412+- "This has complex state transitions"
413413+414414+---
415415+416416+## Maintaining Tests
417417+418418+### When Code Changes
419419+420420+1. **Run tests first**: Verify baseline before changes
421421+2. **Make changes**: Implement feature or fix
422422+3. **Run tests again**: Catch regressions
423423+4. **Update tests**: If behavior intentionally changed
424424+5. **Add new tests**: For new functionality
425425+426426+### Test Hygiene
427427+428428+- **Delete obsolete tests**: If feature removed, remove tests
429429+- **Keep tests fast**: Unit tests should complete in milliseconds
430430+- **Avoid test interdependence**: Each test should run in isolation
431431+- **Use descriptive names**: Future you should understand what's being tested
432432+433433+### Flaky Test Policy
434434+435435+If a test fails intermittently:
436436+437437+1. Mark as `.skip` with comment explaining flakiness
438438+2. File issue to investigate
439439+3. Either fix the flakiness or delete the test
440440+4. Don't leave flaky tests runningโthey erode trust
+601
docs/worked-example.md
···11+# 9plan Worked Example: Ghost Blog Application
22+33+A complete end-to-end walkthrough showing how 9plan manages a realistic multi-phase project from bootstrap to completion.
44+55+---
66+77+## The Task
88+99+**Goal**: Build a simple blog application powered by the Ghost CMS API. The app should:
1010+- Authenticate with Ghost's Content API
1111+- Fetch and display blog posts
1212+- Support pagination
1313+- Cache responses for performance
1414+1515+**Why this example**: It involves multiple components with dependencies, requires decomposition, and demonstrates cross-branch coordination.
1616+1717+---
1818+1919+## Phase 1: Bootstrap
2020+2121+### User Initiates Session
2222+2323+```
2424+User: I want to build a Ghost-powered blog app. It needs auth,
2525+ post fetching with pagination, and caching.
2626+```
2727+2828+### Agent Creates Session
2929+3030+```
3131+Agent calls: 9plan_session_create
3232+ task_description: "Build Ghost-powered blog application with authentication,
3333+ post fetching, pagination, and response caching"
3434+3535+Response:
3636+ [Session: amber-quiet-river]
3737+ Session created: amber-quiet-river
3838+ Directory: /Users/dev/.9plan/sessions/amber-quiet-river
3939+```
4040+4141+### Agent Decomposes into Initial Plans
4242+4343+The agent identifies three major components and their dependencies:
4444+4545+```
4646+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
4747+โ Blog App โ
4848+โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโค
4949+โ Ghost API โ Cache โ Display Layer โ
5050+โ Client โ System โ (Posts + Paging) โ
5151+โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโค
5252+โ Outputs: โ Inputs: โ Inputs: โ
5353+โ - auth'd โ - API client โ - API client โ
5454+โ client โ Outputs: โ - Cache system โ
5555+โ โ - cache API โ Outputs: โ
5656+โ โ โ - working app โ
5757+โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโ
5858+```
5959+6060+**Critical moment**: Right now, all three branches are visible. The agent captures dependencies:
6161+- Cache System needs Ghost API Client's outputs
6262+- Display Layer needs both Ghost API Client AND Cache System outputs
6363+6464+### Adding Plans to Queue
6565+6666+```
6767+Agent calls: 9plan_queue_add
6868+ context: "Building Ghost blog app. This is the foundational component that
6969+ all other parts depend on. Must complete first."
7070+ goal: "Create authenticated Ghost API client module"
7171+ inputs: "- Ghost API credentials: provided by user or environment"
7272+ outputs: "- ghost_client module: authenticated client instance with methods
7373+ for getPosts(), getPost(id), with proper error handling"
7474+ approach: "1. Set up Ghost Content API authentication using API key
7575+ 2. Create client wrapper with typed methods
7676+ 3. Implement error handling for auth failures and API errors
7777+ 4. Export configured client instance"
7878+ success_criteria: "- Client successfully authenticates with Ghost API
7979+ - getPosts() returns post array
8080+ - getPost(id) returns single post
8181+ - Auth errors throw descriptive exceptions"
8282+ position: back
8383+8484+Response:
8585+ [Session: amber-quiet-river]
8686+ Plan added: k7f3m
8787+ Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt
8888+ Queue position: 1
8989+```
9090+9191+```
9292+Agent calls: 9plan_queue_add
9393+ context: "Building Ghost blog app. This provides caching to reduce API calls
9494+ and improve performance. Depends on Ghost API client."
9595+ goal: "Implement response caching system"
9696+ inputs: "- ghost_client module: from Ghost API Client work"
9797+ outputs: "- cache module: wrap-able caching layer with get/set/invalidate
9898+ - cached_client: ghost_client wrapped with caching"
9999+ approach: "1. Design cache interface (get, set, invalidate, TTL)
100100+ 2. Implement in-memory cache with configurable TTL
101101+ 3. Create wrapper that checks cache before API calls
102102+ 4. Add cache invalidation hooks"
103103+ success_criteria: "- Second call to same endpoint returns cached data
104104+ - Cache respects TTL settings
105105+ - Manual invalidation works
106106+ - Cache misses fall through to API"
107107+ position: back
108108+109109+Response:
110110+ [Session: amber-quiet-river]
111111+ Plan added: m2x9p
112112+ Queue position: 2
113113+```
114114+115115+```
116116+Agent calls: 9plan_queue_add
117117+ context: "Building Ghost blog app. This is the user-facing component that
118118+ displays posts with pagination. Depends on both API client and cache."
119119+ goal: "Build post display layer with pagination"
120120+ inputs: "- cached_client: from Cache System work
121121+ - ghost_client module: from Ghost API Client (fallback)"
122122+ outputs: "- Working blog display: renders posts with next/prev navigation"
123123+ approach: "1. Create post list component with pagination controls
124124+ 2. Implement page state management
125125+ 3. Connect to cached client for data fetching
126126+ 4. Add loading and error states"
127127+ success_criteria: "- Posts display in paginated list
128128+ - Next/prev navigation works
129129+ - Loading states show during fetch
130130+ - Errors display gracefully"
131131+ position: back
132132+133133+Response:
134134+ [Session: amber-quiet-river]
135135+ Plan added: p4r2k
136136+ Queue position: 3
137137+```
138138+139139+### State After Bootstrap
140140+141141+**Queue**: `[k7f3m, m2x9p, p4r2k]`
142142+143143+**Files**:
144144+```
145145+amber-quiet-river/
146146+โโโ session.db
147147+โโโ plans/
148148+ โโโ k7f3m.txt # Ghost API Client
149149+ โโโ m2x9p.txt # Cache System
150150+ โโโ p4r2k.txt # Display Layer
151151+```
152152+153153+**k7f3m.txt contents**:
154154+```
155155+# Context
156156+Building Ghost blog app. This is the foundational component that all other
157157+parts depend on. Must complete first.
158158+159159+# Goal
160160+Create authenticated Ghost API client module
161161+162162+# Inputs
163163+- Ghost API credentials: provided by user or environment
164164+165165+# Outputs
166166+- ghost_client module: authenticated client instance with methods for
167167+ getPosts(), getPost(id), with proper error handling
168168+169169+# Approach
170170+1. Set up Ghost Content API authentication using API key
171171+2. Create client wrapper with typed methods
172172+3. Implement error handling for auth failures and API errors
173173+4. Export configured client instance
174174+175175+# Success Criteria
176176+- Client successfully authenticates with Ghost API
177177+- getPosts() returns post array
178178+- getPost(id) returns single post
179179+- Auth errors throw descriptive exceptions
180180+181181+# Notes
182182+```
183183+184184+---
185185+186186+## Phase 2: First Plan Execution
187187+188188+### Agent Pulls First Plan
189189+190190+```
191191+Agent calls: 9plan_queue_pull
192192+193193+Response:
194194+ [Session: amber-quiet-river]
195195+ Active plan: k7f3m
196196+ Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt
197197+198198+ Review the plan file for full context. Check for ambiguities before
199199+ starting execution. If inputs reference other plans, use
200200+ 9plan_history_search to find their outputs.
201201+```
202202+203203+### Agent Reads Plan File
204204+205205+Agent uses filesystem tools to read `k7f3m.txt`, reviews the content, and proceeds with execution.
206206+207207+### Mid-Execution Checkpoint
208208+209209+After implementing the basic client structure, agent updates Notes:
210210+211211+**k7f3m.txt (updated)**:
212212+```
213213+# Notes
214214+[2024-01-15 14:30] Progress checkpoint:
215215+- Created ghost-client.ts with GhostClient class
216216+- Implemented constructor with API key validation
217217+- getPosts() method complete, tested against live API
218218+- Remaining: getPost(id), error handling improvements
219219+```
220220+221221+### Discovering Subwork
222222+223223+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.
224224+225225+### Completing the Plan
226226+227227+```
228228+Agent calls: 9plan_plan_complete
229229+ outcome: "Created ghost_client module in src/lib/ghost-client.ts.
230230+231231+ Exports:
232232+ - GhostClient class with constructor(apiKey, apiUrl)
233233+ - getPosts(options?: {limit, page}) โ Post[]
234234+ - getPost(id: string) โ Post
235235+236236+ Error handling:
237237+ - GhostAuthError for 401/403 responses
238238+ - GhostNotFoundError for 404 responses
239239+ - GhostAPIError for other failures
240240+241241+ Tested against live Ghost instance, all methods working."
242242+243243+Response:
244244+ [Session: amber-quiet-river]
245245+ Plan k7f3m completed and indexed.
246246+247247+ Queue status: 2 plans remaining
248248+ Next: Use 9plan_queue_pull to continue
249249+```
250250+251251+### State After First Completion
252252+253253+**Queue**: `[m2x9p, p4r2k]`
254254+255255+**Database**: k7f3m now in history, searchable
256256+257257+**Files**:
258258+```
259259+amber-quiet-river/
260260+โโโ session.db # k7f3m indexed in history
261261+โโโ plans/
262262+ โโโ m2x9p.txt # Cache System
263263+ โโโ p4r2k.txt # Display Layer
264264+```
265265+266266+---
267267+268268+## Phase 3: Dependency Resolution
269269+270270+### Agent Pulls Cache System Plan
271271+272272+```
273273+Agent calls: 9plan_queue_pull
274274+275275+Response:
276276+ [Session: amber-quiet-river]
277277+ Active plan: m2x9p
278278+ Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/m2x9p.txt
279279+```
280280+281281+### Agent Resolves Input Dependency
282282+283283+The plan's Inputs field says: `ghost_client module: from Ghost API Client work`
284284+285285+```
286286+Agent calls: 9plan_history_search
287287+ query: "ghost_client module Ghost API"
288288+289289+Response:
290290+ [Session: amber-quiet-river]
291291+ Found 1 matching plan:
292292+293293+ Plan k7f3m: "Create authenticated Ghost API client module"
294294+ Outcome: Created ghost_client module in src/lib/ghost-client.ts.
295295+ Exports GhostClient class with getPosts(), getPost() methods...
296296+```
297297+298298+Agent now knows where to import from and what interface to expect.
299299+300300+### Execution and Completion
301301+302302+Agent implements the cache system, wrapping the Ghost client.
303303+304304+```
305305+Agent calls: 9plan_plan_complete
306306+ outcome: "Created caching layer in src/lib/cache.ts.
307307+308308+ Exports:
309309+ - Cache<T> class with get(key), set(key, value, ttl?), invalidate(key)
310310+ - createCachedClient(ghostClient) โ returns proxied client
311311+312312+ The cached_client wraps GhostClient from src/lib/ghost-client.ts.
313313+ Default TTL: 5 minutes. Configurable via Cache constructor.
314314+315315+ Cache hits logged to console for debugging."
316316+```
317317+318318+---
319319+320320+## Phase 4: Plan Decomposition
321321+322322+### Agent Pulls Display Layer Plan
323323+324324+```
325325+Agent calls: 9plan_queue_pull
326326+327327+Response:
328328+ [Session: amber-quiet-river]
329329+ Active plan: p4r2k
330330+ Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/p4r2k.txt
331331+```
332332+333333+### Agent Decides to Decompose
334334+335335+After reviewing the plan, agent realizes "Display Layer with pagination" is too large for a focused session. It involves:
336336+- UI component structure
337337+- State management
338338+- Data fetching integration
339339+- Loading/error states
340340+341341+Agent decides to decompose.
342342+343343+### Checkpointing Before Decomposition
344344+345345+```
346346+Agent updates p4r2k.txt Notes section:
347347+348348+# Notes
349349+[2024-01-15 16:00] Decomposing into subplans:
350350+- p4r2k-1: Post list component (UI structure)
351351+- p4r2k-2: Pagination state management
352352+- p4r2k-3: Integration (connect data + UI)
353353+354354+Will defer this plan after adding children.
355355+```
356356+357357+### Adding Subplans (Reverse Order for Correct Execution)
358358+359359+```
360360+Agent calls: 9plan_queue_add
361361+ context: "Building Ghost blog app display layer. This is the final integration
362362+ step that connects the data layer to the UI components."
363363+ goal: "Integrate data fetching with UI components"
364364+ inputs: "- PostList component: from post list component work
365365+ - usePagination hook: from pagination state work
366366+ - cached_client: from Cache System work"
367367+ outputs: "- Working integrated display: all pieces connected and functional"
368368+ approach: "1. Import PostList and usePagination into main page
369369+ 2. Connect usePagination to cached_client.getPosts()
370370+ 3. Pass paginated data to PostList
371371+ 4. Wire up navigation callbacks"
372372+ success_criteria: "- Page loads and displays posts
373373+ - Pagination controls navigate correctly
374374+ - Loading states display during transitions"
375375+ position: front
376376+377377+Response: Plan added: j8n2q, position 1
378378+```
379379+380380+```
381381+Agent calls: 9plan_queue_add
382382+ context: "Building Ghost blog app display layer. This handles the pagination
383383+ logic separately from UI rendering."
384384+ goal: "Implement pagination state management"
385385+ inputs: "(none - self-contained logic)"
386386+ outputs: "- usePagination hook: manages page state, provides next/prev handlers"
387387+ approach: "1. Create usePagination React hook
388388+ 2. Track current page, total pages, loading state
389389+ 3. Provide nextPage(), prevPage(), goToPage(n) handlers
390390+ 4. Handle edge cases (first page, last page)"
391391+ success_criteria: "- Hook tracks page state correctly
392392+ - Navigation handlers update state
393393+ - Edge cases don't cause invalid states"
394394+ position: front
395395+396396+Response: Plan added: r3t7w, position 1
397397+```
398398+399399+```
400400+Agent calls: 9plan_queue_add
401401+ context: "Building Ghost blog app display layer. This is the presentational
402402+ component, separate from data fetching logic."
403403+ goal: "Create PostList UI component"
404404+ inputs: "(none - receives data via props)"
405405+ outputs: "- PostList component: renders array of posts with pagination controls"
406406+ approach: "1. Create PostList component accepting posts array prop
407407+ 2. Add PostCard subcomponent for individual posts
408408+ 3. Add pagination controls (prev/next buttons)
409409+ 4. Style with basic CSS"
410410+ success_criteria: "- Component renders post list from props
411411+ - Pagination controls render and fire callbacks
412412+ - Empty state handled gracefully"
413413+ position: front
414414+415415+Response: Plan added: x5k9m, position 1
416416+```
417417+418418+### Deferring Parent Plan
419419+420420+```
421421+Agent calls: 9plan_plan_defer
422422+ reason: "Decomposed into subplans: x5k9m (PostList UI), r3t7w (pagination state),
423423+ j8n2q (integration). Will aggregate child outcomes when re-pulled."
424424+ position: back
425425+426426+Response:
427427+ [Session: amber-quiet-river]
428428+ Plan p4r2k deferred to back of queue.
429429+ Reason recorded in Notes.
430430+```
431431+432432+### State After Decomposition
433433+434434+**Queue**: `[x5k9m, r3t7w, j8n2q, p4r2k]`
435435+436436+The subplans are at front in correct execution order. Parent `p4r2k` is at back, waiting to aggregate.
437437+438438+**p4r2k.txt Notes (updated by server)**:
439439+```
440440+# Notes
441441+[2024-01-15 16:00] Decomposing into subplans:
442442+- p4r2k-1: Post list component (UI structure)
443443+- p4r2k-2: Pagination state management
444444+- p4r2k-3: Integration (connect data + UI)
445445+446446+Will defer this plan after adding children.
447447+448448+[2024-01-15 16:05] Deferred: Decomposed into subplans: x5k9m (PostList UI),
449449+r3t7w (pagination state), j8n2q (integration). Will aggregate child outcomes
450450+when re-pulled.
451451+```
452452+453453+---
454454+455455+## Phase 5: Executing Child Plans
456456+457457+Agent executes `x5k9m`, `r3t7w`, and `j8n2q` in sequence. Each follows the normal cycle:
458458+1. Pull
459459+2. Resolve inputs via history search
460460+3. Execute
461461+4. Complete with descriptive outcome
462462+463463+After all three complete:
464464+465465+**Queue**: `[p4r2k]`
466466+467467+**History**: k7f3m, m2x9p, x5k9m, r3t7w, j8n2q (all searchable)
468468+469469+---
470470+471471+## Phase 6: Parent Aggregation
472472+473473+### Agent Pulls Parent Plan Again
474474+475475+```
476476+Agent calls: 9plan_queue_pull
477477+478478+Response:
479479+ [Session: amber-quiet-river]
480480+ Active plan: p4r2k
481481+ Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/p4r2k.txt
482482+```
483483+484484+### Agent Reads Notes, Sees Decomposition Record
485485+486486+From the Notes section, agent sees this plan was decomposed and identifies child plan IDs.
487487+488488+### Agent Retrieves Child Outcomes
489489+490490+```
491491+Agent calls: 9plan_history_get
492492+ plan_id: x5k9m
493493+494494+Response: [Full plan with outcome: "Created PostList component..."]
495495+```
496496+497497+```
498498+Agent calls: 9plan_history_get
499499+ plan_id: r3t7w
500500+501501+Response: [Full plan with outcome: "Created usePagination hook..."]
502502+```
503503+504504+```
505505+Agent calls: 9plan_history_get
506506+ plan_id: j8n2q
507507+508508+Response: [Full plan with outcome: "Integrated all components..."]
509509+```
510510+511511+### Agent Verifies and Aggregates
512512+513513+All children succeeded. Agent tests the integrated result, confirms it works.
514514+515515+```
516516+Agent calls: 9plan_plan_complete
517517+ outcome: "Display layer complete. Aggregated from child plans:
518518+519519+ - x5k9m: PostList component in src/components/PostList.tsx
520520+ - r3t7w: usePagination hook in src/hooks/usePagination.ts
521521+ - j8n2q: Integration in src/pages/index.tsx
522522+523523+ All components working together. Posts display with functional
524524+ pagination. Loading states and error handling in place.
525525+526526+ The blog application is now feature-complete."
527527+```
528528+529529+---
530530+531531+## Phase 7: Session Complete
532532+533533+```
534534+Agent calls: 9plan_queue_pull
535535+536536+Response:
537537+ [Session: amber-quiet-river]
538538+ Queue is empty. Task complete!
539539+540540+ Completed plans: 6
541541+ Use 9plan_history_search to review outcomes.
542542+```
543543+544544+### Final State
545545+546546+**Queue**: Empty
547547+548548+**History**: All 6 plans indexed and searchable
549549+- k7f3m: Ghost API Client
550550+- m2x9p: Cache System
551551+- x5k9m: PostList UI
552552+- r3t7w: Pagination State
553553+- j8n2q: Integration
554554+- p4r2k: Display Layer (parent/aggregator)
555555+556556+**Files**:
557557+```
558558+amber-quiet-river/
559559+โโโ session.db # All history indexed
560560+โโโ plans/ # Empty - all plans completed
561561+```
562562+563563+---
564564+565565+## Key Observations
566566+567567+### Dependency Flow
568568+```
569569+k7f3m (Ghost Client)
570570+ โ
571571+m2x9p (Cache) โโโโโโโโโโโโโโโโโโโโโโ
572572+ โ โ
573573+x5k9m (PostList UI) โโโ no deps โ
574574+ โ โ
575575+r3t7w (Pagination) โโโ no deps โ
576576+ โ โ
577577+j8n2q (Integration) โโโ needs all โโ
578578+ โ
579579+p4r2k (Aggregation) โโโ verifies children
580580+```
581581+582582+### Cross-Branch Visibility
583583+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.
584584+585585+### Semantic Resolution in Action
586586+`j8n2q` declared an input: "cached_client: from Cache System work"
587587+588588+At execution time, agent searched:
589589+```
590590+9plan_history_search("cached_client Cache System")
591591+```
592592+593593+And found `m2x9p`'s outcome describing exactly where to find it.
594594+595595+### Parent Aggregation Value
596596+`p4r2k` completing last allowed:
597597+1. Verification that all UI pieces integrated correctly
598598+2. A milestone record documenting the full Display Layer
599599+3. A single point summarizing three child plans' work
600600+601601+If someone later asks "what was the Display Layer work?", searching for `p4r2k` gives them the complete picture without finding all three children separately.