Skip to main content
Longshot uses an iterative discovery approach to task decomposition, rather than upfront planning. The root planner operates in sprints, decomposing work incrementally as new information emerges from completed tasks.

Planning Philosophy

The core principle: plan only what you can confidently specify. Discover the rest.
The planner doesn’t enumerate all tasks for an entire project upfront. It maintains awareness of the full goal set (from SPEC.md and FEATURES.json) but only emits tasks for work it can scope precisely given current knowledge.

Sprint-Based Planning

Each planning iteration is a sprint focused on a specific set of work:
interface Task {
  id: string;              // Unique task ID (e.g., "task-001")
  parentId?: string;       // Parent task ID if this is a subtask
  description: string;     // What to do (natural language)
  scope: string[];         // File paths the worker should focus on
  acceptance: string;      // How to know when done
  branch: string;          // Git branch name for this task
  status: TaskStatus;      // pending | assigned | running | complete | failed
  priority: number;        // 1 (highest) to 10 (lowest)
  createdAt: number;       // Unix timestamp ms
}

Planning Phases

Projects naturally progress through phases, each with different sprint sizes:
PhaseFocusSprint Size
DiscoveryUnderstand codebase, read specs, identify architecture3-8 foundational tasks
FoundationCore infrastructure, shared utilities, database setup5-15 tasks
Core Build-outPrimary features, main application logic10-30 tasks
Integration & HardeningWiring systems together, edge cases, bug fixes5-20 tasks
These numbers are ceilings, not targets. If only 5 things can be precisely specified, emit 5. If 30 are clear, emit 30.

What Determines Sprint Size

Sprint sizing is driven by confidence, not capacity: Confidence: Can you write a complete, self-contained description for each task? If you’re guessing at file paths, patterns, or interfaces — you’re not ready to emit that task. Independence: All tasks at the same priority must be fully parallel. If task B needs task A’s output, A must have higher priority or ship in an earlier sprint. Stability: If the last sprint surfaced architectural problems, pause feature work. Fix foundations before fanning out. Worker Feedback: Handoffs are bug reports from your team. If 3 workers reported the same missing utility, that’s your next task — not more features.

Continuous Conversation Model

The planner operates as a persistent, continuous conversation — not stateless batch calls:
  • First message: Full request and initial repo state
  • Follow-up messages: Only new handoffs since last response + fresh repo state snapshot
  • Conversation history preserved: The planner has memory across iterations
  • Scratchpad survives: The planner’s working memory persists even when context is compacted

Scratchpad: Working Memory

Every planning response includes a scratchpad — the planner’s working memory that gets rewritten completely each iteration:
{
  "scratchpad": "Sprint 3 / Core build-out.\n\nGOALS:\n- [DONE] Scaffolding, types, DB (sprints 1-2)\n- [IN PROGRESS] Tasks CRUD\n- [NOT STARTED] Notifications\n\nDISCOVERIES: Workers established a Zod-validation pattern...",
  "tasks": [
    {
      "id": "task-020",
      "description": "Implement POST /api/tasks endpoint...",
      "scope": ["src/routes/tasks.ts"],
      "acceptance": "Returns 201 with created Task. Tests cover valid creation, missing title...",
      "branch": "worker/task-020-create-task-endpoint",
      "priority": 3
    }
  ]
}
The scratchpad tracks:
  1. Goals & Specs: Full goal set from SPEC.md/FEATURES.json with coverage status
  2. Current State: What’s built, broken, in progress, and key architectural decisions
  3. Sprint Reasoning: Why this set of tasks, what’s being deferred, and likely next focus
  4. Worker Intelligence: Patterns from handoffs, unresolved concerns, recurring issues

Exploration Before Planning

Before each sprint, the planner can explore the codebase using read-only tools:
  • read: Read file contents by path
  • grep: Search file contents with regex
  • find: Find files by glob pattern
  • ls: List directory contents
  • bash: Execute read-only git commands (git log, git diff, git show, etc.)
The planner uses these tools to verify assumptions before emitting tasks. For example, checking if a dependency exists before planning features that use it.

Processing Worker Handoffs

Handoffs provide critical feedback that shapes future sprints:
interface Handoff {
  taskId: string;
  status: "complete" | "partial" | "blocked" | "failed";
  summary: string;           // What was done
  diff: string;              // Git diff output
  filesChanged: string[];    // List of changed file paths
  concerns: string[];        // Issues discovered
  suggestions: string[];     // Recommendations for planner
  metrics: {
    linesAdded: number;
    linesRemoved: number;
    tokensUsed: number;
    toolCallCount: number;
    durationMs: number;
  };
}

Concern Triage

When handoffs include concerns, the planner classifies each one:
ClassificationActionExample
BlockingCreate fix task this sprint”Type mismatch breaks callers in 3 files”
ArchitecturalUpdate scratchpad, adjust future tasks”Auth doesn’t handle token refresh”
InformationalNote in scratchpad, no immediate action”Found dead code in utils.ts”
The scratchpad MUST track unresolved concerns across iterations. A concern raised in sprint 3 that isn’t addressed by sprint 5 is a planning failure.

Subplanner Decomposition

When a task’s scope is broad (many files, multiple concerns), the system may assign a subplanner to decompose it further:
// From planner.ts:669-680
if (this.subplanner && shouldDecompose(task, DEFAULT_SUBPLANNER_CONFIG, 0)) {
  logger.info("Task scope is complex — routing through subplanner", {
    taskId: task.id,
    scopeSize: task.scope.length,
  });
  handoff = await this.subplanner.decomposeAndExecute(task, 0, dispatchSpan);
} else {
  handoff = await this.workerPool.assignTask(task);
}
Tasks targeting more than 5 files or spanning multiple modules are flagged for subplanner decomposition.

Binding Constraints: SPEC.md and FEATURES.json

These documents are constraints on planner output — not background context: SPEC.md defines:
  • Allowed dependencies
  • File structure
  • Technical parameters
  • Acceptance tests
  • Non-negotiables
FEATURES.json defines:
  • What to build
  • Feature dependencies
  • Completion status
Every task must trace to a feature in FEATURES.json. Tasks that contradict these documents will produce work that cannot be integrated.

Priority Ordering

Priority expresses ordering within a sprint:
  • 1-2: Infrastructure, types, interfaces (foundations)
  • 3-5: Core feature implementation
  • 6-7: Secondary features, integration
  • 8-10: Polish, documentation, nice-to-have
Tasks at the same priority level must be fully independent. No sequential dependencies allowed.

Definition of Done

Every task includes a clear definition of done in its acceptance field: Bad: “Function works correctly. Tests pass.” Good: “createUser() rejects duplicate emails with DuplicateEmailError. Tests cover: valid creation, duplicate email, missing required fields, invalid email format. tsc —noEmit exits 0.” Acceptance criteria must specify:
  1. Verification: Build/type-check commands and expected results
  2. Integration: What call sites should work, API contracts
  3. Quality bar: Patterns to follow, edge cases to handle

Anti-Patterns

Avoid these common planning mistakes:
  • Upfront enumeration: Planning all 50 tasks in sprint 1 without enough information
  • Mega-tasks: “Build the authentication system” instead of specific, scoped tasks
  • Premature fan-out: Emitting 40 feature tasks when foundation hasn’t landed yet
  • Sequential chains: Task B needs task A’s output but both are same priority
  • Stale scratchpad: Copy-pasting previous scratchpad without updating it
  • Ignoring the spec: Generating tasks based on general knowledge instead of SPEC.md

Build docs developers (and LLMs) love