# 9plan Worked Example: Ghost Blog Application A complete end-to-end walkthrough showing how 9plan manages a realistic multi-phase project from bootstrap to completion. --- ## The Task **Goal**: Build a simple blog application powered by the Ghost CMS API. The app should: - Authenticate with Ghost's Content API - Fetch and display blog posts - Support pagination - Cache responses for performance **Why this example**: It involves multiple components with dependencies, requires decomposition, and demonstrates cross-branch coordination. --- ## Phase 1: Bootstrap ### User Initiates Session ``` User: I want to build a Ghost-powered blog app. It needs auth, post fetching with pagination, and caching. ``` ### Agent Creates Session ``` Agent calls: 9plan_session_create task_description: "Build Ghost-powered blog application with authentication, post fetching, pagination, and response caching" Response: [Session: amber-quiet-river] Session created: amber-quiet-river Directory: /Users/dev/.9plan/sessions/amber-quiet-river ``` ### Agent Decomposes into Initial Plans The agent identifies three major components and their dependencies: ``` ┌─────────────────────────────────────────────────────┐ │ Blog App │ ├──────────────┬──────────────┬──────────────────────┤ │ Ghost API │ Cache │ Display Layer │ │ Client │ System │ (Posts + Paging) │ ├──────────────┼──────────────┼──────────────────────┤ │ Outputs: │ Inputs: │ Inputs: │ │ - auth'd │ - API client │ - API client │ │ client │ Outputs: │ - Cache system │ │ │ - cache API │ Outputs: │ │ │ │ - working app │ └──────────────┴──────────────┴──────────────────────┘ ``` **Critical moment**: Right now, all three branches are visible. The agent captures dependencies: - Cache System needs Ghost API Client's outputs - Display Layer needs both Ghost API Client AND Cache System outputs ### Adding Plans to Queue ``` Agent calls: 9plan_queue_add context: "Building Ghost blog app. This is the foundational component that all other parts depend on. Must complete first." goal: "Create authenticated Ghost API client module" inputs: "- Ghost API credentials: provided by user or environment" outputs: "- ghost_client module: authenticated client instance with methods for getPosts(), getPost(id), with proper error handling" approach: "1. Set up Ghost Content API authentication using API key 2. Create client wrapper with typed methods 3. Implement error handling for auth failures and API errors 4. Export configured client instance" success_criteria: "- Client successfully authenticates with Ghost API - getPosts() returns post array - getPost(id) returns single post - Auth errors throw descriptive exceptions" position: back Response: [Session: amber-quiet-river] Plan added: k7f3m Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt Queue position: 1 ``` ``` Agent calls: 9plan_queue_add context: "Building Ghost blog app. This provides caching to reduce API calls and improve performance. Depends on Ghost API client." goal: "Implement response caching system" inputs: "- ghost_client module: from Ghost API Client work" outputs: "- cache module: wrap-able caching layer with get/set/invalidate - cached_client: ghost_client wrapped with caching" approach: "1. Design cache interface (get, set, invalidate, TTL) 2. Implement in-memory cache with configurable TTL 3. Create wrapper that checks cache before API calls 4. Add cache invalidation hooks" success_criteria: "- Second call to same endpoint returns cached data - Cache respects TTL settings - Manual invalidation works - Cache misses fall through to API" position: back Response: [Session: amber-quiet-river] Plan added: m2x9p Queue position: 2 ``` ``` Agent calls: 9plan_queue_add context: "Building Ghost blog app. This is the user-facing component that displays posts with pagination. Depends on both API client and cache." goal: "Build post display layer with pagination" inputs: "- cached_client: from Cache System work - ghost_client module: from Ghost API Client (fallback)" outputs: "- Working blog display: renders posts with next/prev navigation" approach: "1. Create post list component with pagination controls 2. Implement page state management 3. Connect to cached client for data fetching 4. Add loading and error states" success_criteria: "- Posts display in paginated list - Next/prev navigation works - Loading states show during fetch - Errors display gracefully" position: back Response: [Session: amber-quiet-river] Plan added: p4r2k Queue position: 3 ``` ### State After Bootstrap **Queue**: `[k7f3m, m2x9p, p4r2k]` **Files**: ``` amber-quiet-river/ ├── session.db └── plans/ ├── k7f3m.txt # Ghost API Client ├── m2x9p.txt # Cache System └── p4r2k.txt # Display Layer ``` **k7f3m.txt contents**: ``` # Context Building Ghost blog app. This is the foundational component that all other parts depend on. Must complete first. # Goal Create authenticated Ghost API client module # Inputs - Ghost API credentials: provided by user or environment # Outputs - ghost_client module: authenticated client instance with methods for getPosts(), getPost(id), with proper error handling # Approach 1. Set up Ghost Content API authentication using API key 2. Create client wrapper with typed methods 3. Implement error handling for auth failures and API errors 4. Export configured client instance # Success Criteria - Client successfully authenticates with Ghost API - getPosts() returns post array - getPost(id) returns single post - Auth errors throw descriptive exceptions # Notes ``` --- ## Phase 2: First Plan Execution ### Agent Pulls First Plan ``` Agent calls: 9plan_queue_pull Response: [Session: amber-quiet-river] Active plan: k7f3m Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/k7f3m.txt Review the plan file for full context. Check for ambiguities before starting execution. If inputs reference other plans, use 9plan_history_search to find their outputs. ``` ### Agent Reads Plan File Agent uses filesystem tools to read `k7f3m.txt`, reviews the content, and proceeds with execution. ### Mid-Execution Checkpoint After implementing the basic client structure, agent updates Notes: **k7f3m.txt (updated)**: ``` # Notes [2024-01-15 14:30] Progress checkpoint: - Created ghost-client.ts with GhostClient class - Implemented constructor with API key validation - getPosts() method complete, tested against live API - Remaining: getPost(id), error handling improvements ``` ### Discovering Subwork 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. ### Completing the Plan ``` Agent calls: 9plan_plan_complete outcome: "Created ghost_client module in src/lib/ghost-client.ts. Exports: - GhostClient class with constructor(apiKey, apiUrl) - getPosts(options?: {limit, page}) → Post[] - getPost(id: string) → Post Error handling: - GhostAuthError for 401/403 responses - GhostNotFoundError for 404 responses - GhostAPIError for other failures Tested against live Ghost instance, all methods working." Response: [Session: amber-quiet-river] Plan k7f3m completed and indexed. Queue status: 2 plans remaining Next: Use 9plan_queue_pull to continue ``` ### State After First Completion **Queue**: `[m2x9p, p4r2k]` **Database**: k7f3m now in history, searchable **Files**: ``` amber-quiet-river/ ├── session.db # k7f3m indexed in history └── plans/ ├── m2x9p.txt # Cache System └── p4r2k.txt # Display Layer ``` --- ## Phase 3: Dependency Resolution ### Agent Pulls Cache System Plan ``` Agent calls: 9plan_queue_pull Response: [Session: amber-quiet-river] Active plan: m2x9p Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/m2x9p.txt ``` ### Agent Resolves Input Dependency The plan's Inputs field says: `ghost_client module: from Ghost API Client work` ``` Agent calls: 9plan_history_search query: "ghost_client module Ghost API" Response: [Session: amber-quiet-river] Found 1 matching plan: Plan k7f3m: "Create authenticated Ghost API client module" Outcome: Created ghost_client module in src/lib/ghost-client.ts. Exports GhostClient class with getPosts(), getPost() methods... ``` Agent now knows where to import from and what interface to expect. ### Execution and Completion Agent implements the cache system, wrapping the Ghost client. ``` Agent calls: 9plan_plan_complete outcome: "Created caching layer in src/lib/cache.ts. Exports: - Cache class with get(key), set(key, value, ttl?), invalidate(key) - createCachedClient(ghostClient) → returns proxied client The cached_client wraps GhostClient from src/lib/ghost-client.ts. Default TTL: 5 minutes. Configurable via Cache constructor. Cache hits logged to console for debugging." ``` --- ## Phase 4: Plan Decomposition ### Agent Pulls Display Layer Plan ``` Agent calls: 9plan_queue_pull Response: [Session: amber-quiet-river] Active plan: p4r2k Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/p4r2k.txt ``` ### Agent Decides to Decompose After reviewing the plan, agent realizes "Display Layer with pagination" is too large for a focused session. It involves: - UI component structure - State management - Data fetching integration - Loading/error states Agent decides to decompose. ### Checkpointing Before Decomposition ``` Agent updates p4r2k.txt Notes section: # Notes [2024-01-15 16:00] Decomposing into subplans: - p4r2k-1: Post list component (UI structure) - p4r2k-2: Pagination state management - p4r2k-3: Integration (connect data + UI) Will defer this plan after adding children. ``` ### Adding Subplans (Reverse Order for Correct Execution) ``` Agent calls: 9plan_queue_add context: "Building Ghost blog app display layer. This is the final integration step that connects the data layer to the UI components." goal: "Integrate data fetching with UI components" inputs: "- PostList component: from post list component work - usePagination hook: from pagination state work - cached_client: from Cache System work" outputs: "- Working integrated display: all pieces connected and functional" approach: "1. Import PostList and usePagination into main page 2. Connect usePagination to cached_client.getPosts() 3. Pass paginated data to PostList 4. Wire up navigation callbacks" success_criteria: "- Page loads and displays posts - Pagination controls navigate correctly - Loading states display during transitions" position: front Response: Plan added: j8n2q, position 1 ``` ``` Agent calls: 9plan_queue_add context: "Building Ghost blog app display layer. This handles the pagination logic separately from UI rendering." goal: "Implement pagination state management" inputs: "(none - self-contained logic)" outputs: "- usePagination hook: manages page state, provides next/prev handlers" approach: "1. Create usePagination React hook 2. Track current page, total pages, loading state 3. Provide nextPage(), prevPage(), goToPage(n) handlers 4. Handle edge cases (first page, last page)" success_criteria: "- Hook tracks page state correctly - Navigation handlers update state - Edge cases don't cause invalid states" position: front Response: Plan added: r3t7w, position 1 ``` ``` Agent calls: 9plan_queue_add context: "Building Ghost blog app display layer. This is the presentational component, separate from data fetching logic." goal: "Create PostList UI component" inputs: "(none - receives data via props)" outputs: "- PostList component: renders array of posts with pagination controls" approach: "1. Create PostList component accepting posts array prop 2. Add PostCard subcomponent for individual posts 3. Add pagination controls (prev/next buttons) 4. Style with basic CSS" success_criteria: "- Component renders post list from props - Pagination controls render and fire callbacks - Empty state handled gracefully" position: front Response: Plan added: x5k9m, position 1 ``` ### Deferring Parent Plan ``` Agent calls: 9plan_plan_defer reason: "Decomposed into subplans: x5k9m (PostList UI), r3t7w (pagination state), j8n2q (integration). Will aggregate child outcomes when re-pulled." position: back Response: [Session: amber-quiet-river] Plan p4r2k deferred to back of queue. Reason recorded in Notes. ``` ### State After Decomposition **Queue**: `[x5k9m, r3t7w, j8n2q, p4r2k]` The subplans are at front in correct execution order. Parent `p4r2k` is at back, waiting to aggregate. **p4r2k.txt Notes (updated by server)**: ``` # Notes [2024-01-15 16:00] Decomposing into subplans: - p4r2k-1: Post list component (UI structure) - p4r2k-2: Pagination state management - p4r2k-3: Integration (connect data + UI) Will defer this plan after adding children. [2024-01-15 16:05] Deferred: Decomposed into subplans: x5k9m (PostList UI), r3t7w (pagination state), j8n2q (integration). Will aggregate child outcomes when re-pulled. ``` --- ## Phase 5: Executing Child Plans Agent executes `x5k9m`, `r3t7w`, and `j8n2q` in sequence. Each follows the normal cycle: 1. Pull 2. Resolve inputs via history search 3. Execute 4. Complete with descriptive outcome After all three complete: **Queue**: `[p4r2k]` **History**: k7f3m, m2x9p, x5k9m, r3t7w, j8n2q (all searchable) --- ## Phase 6: Parent Aggregation ### Agent Pulls Parent Plan Again ``` Agent calls: 9plan_queue_pull Response: [Session: amber-quiet-river] Active plan: p4r2k Path: /Users/dev/.9plan/sessions/amber-quiet-river/plans/p4r2k.txt ``` ### Agent Reads Notes, Sees Decomposition Record From the Notes section, agent sees this plan was decomposed and identifies child plan IDs. ### Agent Retrieves Child Outcomes ``` Agent calls: 9plan_history_get plan_id: x5k9m Response: [Full plan with outcome: "Created PostList component..."] ``` ``` Agent calls: 9plan_history_get plan_id: r3t7w Response: [Full plan with outcome: "Created usePagination hook..."] ``` ``` Agent calls: 9plan_history_get plan_id: j8n2q Response: [Full plan with outcome: "Integrated all components..."] ``` ### Agent Verifies and Aggregates All children succeeded. Agent tests the integrated result, confirms it works. ``` Agent calls: 9plan_plan_complete outcome: "Display layer complete. Aggregated from child plans: - x5k9m: PostList component in src/components/PostList.tsx - r3t7w: usePagination hook in src/hooks/usePagination.ts - j8n2q: Integration in src/pages/index.tsx All components working together. Posts display with functional pagination. Loading states and error handling in place. The blog application is now feature-complete." ``` --- ## Phase 7: Session Complete ``` Agent calls: 9plan_queue_pull Response: [Session: amber-quiet-river] Queue is empty. Task complete! Completed plans: 6 Use 9plan_history_search to review outcomes. ``` ### Final State **Queue**: Empty **History**: All 6 plans indexed and searchable - k7f3m: Ghost API Client - m2x9p: Cache System - x5k9m: PostList UI - r3t7w: Pagination State - j8n2q: Integration - p4r2k: Display Layer (parent/aggregator) **Files**: ``` amber-quiet-river/ ├── session.db # All history indexed └── plans/ # Empty - all plans completed ``` --- ## Key Observations ### Dependency Flow ``` k7f3m (Ghost Client) ↓ m2x9p (Cache) ←────────────────────┐ ↓ │ x5k9m (PostList UI) ←── no deps │ ↓ │ r3t7w (Pagination) ←── no deps │ ↓ │ j8n2q (Integration) ←── needs all ─┘ ↓ p4r2k (Aggregation) ←── verifies children ``` ### Cross-Branch Visibility 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. ### Semantic Resolution in Action `j8n2q` declared an input: "cached_client: from Cache System work" At execution time, agent searched: ``` 9plan_history_search("cached_client Cache System") ``` And found `m2x9p`'s outcome describing exactly where to find it. ### Parent Aggregation Value `p4r2k` completing last allowed: 1. Verification that all UI pieces integrated correctly 2. A milestone record documenting the full Display Layer 3. A single point summarizing three child plans' work If someone later asks "what was the Display Layer work?", searching for `p4r2k` gives them the complete picture without finding all three children separately.