🐱 Medium-horizon agent planning MCP server
9plan docs data-flows.md
23 kB
Markdown
at main

9plan Data Flow Diagrams #

Visual documentation of how data moves through the system during key operations.


Storage Overview #

┌─────────────────────────────────────────────────────────────────────────┐
│                         Session Directory                                │
│                   ~/.9plan/sessions/{session-name}/                      │
│                                                                          │
│   ┌─────────────────────────────┐    ┌─────────────────────────────┐   │
│   │       session.db            │    │        plans/               │   │
│   │       (SQLite)              │    │     (Flat Files)            │   │
│   │                             │    │                             │   │
│   │  ┌───────────────────────┐  │    │  ┌───────────────────────┐  │   │
│   │  │ metadata              │  │    │  │ {plan-id}.txt         │  │   │
│   │  │ - task_description    │  │    │  │ - Context             │  │   │
│   │  │ - created_at          │  │    │  │ - Goal                │  │   │
│   │  └───────────────────────┘  │    │  │ - Inputs              │  │   │
│   │                             │    │  │ - Outputs             │  │   │
│   │  ┌───────────────────────┐  │    │  │ - Approach            │  │   │
│   │  │ queue                 │  │    │  │ - Success Criteria    │  │   │
│   │  │ - ordered plan IDs    │  │    │  │ - Notes               │  │   │
│   │  │ - active plan ref     │  │    │  └───────────────────────┘  │   │
│   │  └───────────────────────┘  │    │                             │   │
│   │                             │    │  (Only queued + active      │   │
│   │  ┌───────────────────────┐  │    │   plans have files)         │   │
│   │  │ history (FTS5)        │  │    │                             │   │
│   │  │ - completed plans     │  │    └─────────────────────────────┘   │
│   │  │ - full content        │  │                                      │
│   │  │ - outcomes            │  │                                      │
│   │  └───────────────────────┘  │                                      │
│   │                             │                                      │
│   └─────────────────────────────┘                                      │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Session Creation Flow #

9plan_session_create(task_description)
              │
              ▼
    ┌─────────────────────┐
    │  Generate Session   │
    │  Name (3 words)     │
    │  e.g. "amber-quiet- │
    │        river"       │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Check for          │
    │  Collision          │◄─── Regenerate if exists
    └──────────┬──────────┘
               │ (unique)
               ▼
    ┌─────────────────────┐
    │  Create Directory   │
    │  sessions/amber-    │
    │  quiet-river/       │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Initialize SQLite  │
    │  session.db         │
    │  - Create tables    │
    │  - Insert metadata  │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Create plans/      │
    │  subdirectory       │
    └──────────┬──────────┘
               │
               ▼
        Return: session_name,
                session_path

Plan Addition Flow #

9plan_queue_add(context, goal, approach, success_criteria, inputs?, outputs?, position?)
              │
              ▼
    ┌─────────────────────┐
    │  Validate Plan      │
    │  (required fields)  │──── Error if invalid
    └──────────┬──────────┘
               │ (valid)
               ▼
    ┌─────────────────────┐
    │  Generate Plan ID   │
    │  (alphanumeric,     │
    │   5+ chars)         │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Check for          │
    │  Collision          │◄─── Regenerate if exists
    └──────────┬──────────┘
               │ (unique)
               ▼
    ┌───────────────────────────────────────────────────┐
    │                     PARALLEL                       │
    │  ┌──────────────────┐    ┌──────────────────────┐ │
    │  │ Write Plan File  │    │ Insert DB Record     │ │
    │  │ plans/{id}.txt   │    │ - plan metadata      │ │
    │  │ - Full content   │    │ - queue position     │ │
    │  │ - Empty Notes    │    │ - status: queued     │ │
    │  └──────────────────┘    └──────────────────────┘ │
    └───────────────────────────────────────────────────┘
               │
               ▼
        Return: plan_id,
                plan_path,
                queue_position

Position Logic:

position = "front"                     position = "back" (default)
                                       
Queue: [B, C, D]                       Queue: [B, C, D]
Add A at front                         Add A at back
Queue: [A, B, C, D]                    Queue: [B, C, D, A]

Plan Pull Flow #

9plan_queue_pull()
              │
              ▼
    ┌─────────────────────┐
    │  Check Active Plan  │
    │  Exists?            │
    └──────────┬──────────┘
               │
       ┌───────┴───────┐
       ▼               ▼
    (yes)           (no)
       │               │
       ▼               ▼
    ERROR:      ┌─────────────────────┐
    "Plan       │  Check Queue        │
    already     │  Empty?             │
    active"     └──────────┬──────────┘
                           │
                   ┌───────┴───────┐
                   ▼               ▼
                (yes)           (no)
                   │               │
                   ▼               ▼
            "Queue empty,   ┌─────────────────────┐
             task           │  Get Front Plan ID  │
             complete"      └──────────┬──────────┘
                                       │
                                       ▼
                           ┌─────────────────────┐
                           │  Update DB          │
                           │  - Remove from queue│
                           │  - Mark as active   │
                           └──────────┬──────────┘
                                       │
                                       ▼
                           ┌─────────────────────┐
                           │  Verify Plan File   │
                           │  Exists             │
                           └──────────┬──────────┘
                                       │
                                       ▼
                                Return: plan_id,
                                        plan_path

Agent then reads plans/{id}.txt via filesystem tools

Plan Completion Flow #

9plan_plan_complete(outcome)
              │
              ▼
    ┌─────────────────────┐
    │  Get Active Plan    │
    │  from DB            │──── Error if none active
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Read Plan File     │
    │  plans/{id}.txt     │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Index into         │
    │  History (FTS5)     │
    │  - Full plan content│
    │  - Outcome          │
    │  - Timestamp        │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Delete Plan File   │
    │  plans/{id}.txt     │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Clear Active Plan  │
    │  in DB              │
    └──────────┬──────────┘
               │
               ▼
         Return: success,
                 "pull next plan"

Data Migration on Completion:

BEFORE:                              AFTER:
                                     
plans/k7f3m.txt ─────────┐           plans/k7f3m.txt
  # Context              │             (DELETED)
  Building Ghost...      │           
  # Goal                 │           session.db
  Create API client...   │             history table:
  # Notes                │             ┌────────────────────┐
  Progress: done auth    │             │ id: k7f3m          │
                         ▼             │ content: (full)    │
                    completion         │ outcome: Created   │
                    process            │   ghost_client...  │
                         │             │ completed_at: ...  │
                         └────────────►└────────────────────┘
                                         (searchable via FTS5)

Plan Deferral Flow #

9plan_plan_defer(reason, position?)
              │
              ▼
    ┌─────────────────────┐
    │  Get Active Plan    │
    │  from DB            │──── Error if none active
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Append to Notes    │
    │  in Plan File       │
    │  [timestamp] reason │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Add Back to Queue  │
    │  (front or back)    │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Clear Active Plan  │
    │  in DB              │
    └──────────┬──────────┘
               │
               ▼
         Return: success,
                 new queue position

State Change:

BEFORE:                              AFTER:
                                     
Active: k7f3m                        Active: (none)
Queue: [m2x9p, p4r2k]                Queue: [m2x9p, p4r2k, k7f3m]
                                            (if position=back)
plans/k7f3m.txt                      
  # Notes                            plans/k7f3m.txt
  (empty)                              # Notes
                                       [2024-01-15 14:30] Deferred:
                                       Waiting for auth dependency

Decomposition Flow #

Agent pulls plan, decides to decompose

1. Agent updates Notes in plan file:
   "Decomposing into: A.1, A.2, A.3"

2. Agent adds subplans (reverse order for correct execution):

   9plan_queue_add(A.3) → front    Queue: [A.3, B, C]
   9plan_queue_add(A.2) → front    Queue: [A.2, A.3, B, C]  
   9plan_queue_add(A.1) → front    Queue: [A.1, A.2, A.3, B, C]

3. Agent defers parent:

   9plan_plan_defer("Decomposed into A.1, A.2, A.3", back)

   Result: Queue: [A.1, A.2, A.3, B, C, A]
                                       │
                                       └── Parent at back

4. Execution proceeds:
   - A.1 executes, completes
   - A.2 executes, completes  
   - A.3 executes, completes
   - B executes, completes
   - C executes, completes
   - A (parent) pulled again for aggregation

History Search Flow #

9plan_history_search(query)
              │
              ▼
    ┌─────────────────────┐
    │  FTS5 Query on      │
    │  history table      │
    │                     │
    │  Searches:          │
    │  - context          │
    │  - goal             │
    │  - inputs           │
    │  - outputs          │
    │  - approach         │
    │  - outcome          │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Rank Results by    │
    │  Relevance          │
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Return Top N       │
    │  Matches            │
    │                     │
    │  For each:          │
    │  - plan_id          │
    │  - goal             │
    │  - outcome          │
    └──────────┬──────────┘
               │
               ▼
         Agent uses results
         to resolve inputs

Example Search:

Query: "ghost client API authentication"

History:
┌─────────────────────────────────────────────────────────┐
│ k7f3m: "Create authenticated Ghost API client module"   │
│ Outcome: "Created ghost_client module in src/lib/..."   │ ← MATCH
├─────────────────────────────────────────────────────────┤
│ m2x9p: "Implement response caching system"              │
│ Outcome: "Created caching layer..."                     │ ← no match
├─────────────────────────────────────────────────────────┤
│ x5k9m: "Create PostList UI component"                   │
│ Outcome: "Created PostList component..."                │ ← no match
└─────────────────────────────────────────────────────────┘

Returns: [{id: k7f3m, goal: "Create authenticated...", outcome: "..."}]

Session Resume Flow #

9plan_session_resume(session_name)
              │
              ▼
    ┌─────────────────────┐
    │  Find Session       │
    │  Directory          │──── Error if not found
    └──────────┬──────────┘
               │
               ▼
    ┌─────────────────────┐
    │  Open session.db    │
    └──────────┬──────────┘
               │
               ▼
    ┌───────────────────────────────────────────────────┐
    │                    GATHER STATE                    │
    │                                                    │
    │  ┌────────────────┐  ┌────────────────────────┐  │
    │  │ Read metadata  │  │ Read queue             │  │
    │  │ - task desc    │  │ - ordered plan IDs     │  │
    │  └────────────────┘  │ - goals for each       │  │
    │                      └────────────────────────┘  │
    │  ┌────────────────┐  ┌────────────────────────┐  │
    │  │ Get active     │  │ Count completed        │  │
    │  │ plan (if any)  │  │ plans in history       │  │
    │  └────────────────┘  └────────────────────────┘  │
    │                                                    │
    └───────────────────────────────────────────────────┘
               │
               ▼
         Return: session_name,
                 session_path,
                 task_description,
                 queue (id + goal for each),
                 active_plan (if any),
                 completed_count

Tool Response Prefix Pattern #

All tool responses include the session name as a prefix:

┌─────────────────────────────────────────────────────────────┐
│                     Tool Response                            │
│                                                              │
│  [Session: amber-quiet-river]  ◄── Always present           │
│                                                              │
│  Plan added: k7f3m                                          │
│  Path: /Users/dev/.9plan/sessions/amber-quiet-river/...     │
│  Queue position: 1                                          │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Purpose: Keep session name fresh in context for recovery
         after context compaction

File System States by Plan Status #

┌─────────────────────────────────────────────────────────────┐
│                      Plan Status                             │
├──────────────┬──────────────┬──────────────┬───────────────┤
│    QUEUED    │    ACTIVE    │  COMPLETED   │   DISCARDED   │
├──────────────┼──────────────┼──────────────┼───────────────┤
│ DB: queue[]  │ DB: active   │ DB: history  │ DB: (removed) │
│ File: YES    │ File: YES    │ File: NO     │ File: NO      │
├──────────────┼──────────────┼──────────────┼───────────────┤
│ plans/       │ plans/       │ (deleted)    │ (deleted)     │
│  {id}.txt    │  {id}.txt    │              │               │
│              │              │ Content in   │ No record     │
│              │              │ session.db   │ preserved     │
└──────────────┴──────────────┴──────────────┴───────────────┘