Skip to main content
Longshot uses a multi-agent system where specialized agents handle different aspects of the development workflow. Each agent has a distinct role, prompt, and responsibility.

Agent Roles

export type AgentRole = "root-planner" | "subplanner" | "worker" | "reconciler";
Each agent operates independently with its own system prompt and behavioral constraints.

Root Planner

Role: Iterative task decomposition and project orchestration Responsibilities:
  • Break down projects into parallelizable tasks through sprint-based planning
  • Maintain awareness of full project scope (SPEC.md, FEATURES.json)
  • Process worker handoffs and adapt future sprints based on feedback
  • Track unresolved concerns across iterations
  • Decide when all work is complete
Capabilities:
  • Read-only codebase exploration (read, grep, find, ls)
  • Read-only git commands (git log, git diff, git show, etc.)
  • Continuous conversation with preserved memory via scratchpad
Does NOT:
  • Write code directly
  • Execute tasks themselves
  • Make assumptions without exploring the codebase first

Planning Strategy

The root planner operates in sprints, planning only what can be confidently specified:
// From planner.ts:189-303
async runLoop(request: string): Promise<void> {
  let iteration = 0;
  let planningDone = false;
  
  while (this.running && iteration < this.plannerConfig.maxIterations) {
    this.collectCompletedHandoffs();
    
    const needsPlan = hasCapacity && 
      (iteration === 0 || hasEnoughHandoffs || noActiveWork);
    
    if (needsPlan && !planningDone) {
      const repoState = await this.readRepoState();
      const tasks = await this.plan(request, repoState, newHandoffs);
      
      if (tasks.length === 0 && this.activeTasks.size === 0) {
        planningDone = true;
      } else {
        this.dispatchTasks(tasks);
      }
    }
  }
}
The planner re-plans after receiving MIN_HANDOFFS_FOR_REPLAN (3) handoffs or when no active work remains. This allows rapid adaptation to worker discoveries.

Subplanner

Role: Decompose complex tasks into smaller subtasks When activated:
  • Task scope exceeds 5 files
  • Task spans multiple modules
  • Complexity threshold met
// Subplanner decision logic
function shouldDecompose(task: Task, config: SubplannerConfig, depth: number): boolean {
  if (depth >= config.maxDepth) return false;
  if (task.scope.length > config.minScopeSize) return true;
  return false;
}
Process:
  1. Receive complex task from root planner
  2. Break it into 2-5 subtasks
  3. Coordinate execution of subtasks
  4. Aggregate results into single handoff
  5. Return to root planner
Subplanners create a hierarchy of work but maintain the illusion of a single task from the root planner’s perspective.

Worker

Role: Execute individual tasks in isolated sandboxes Responsibilities:
  • Implement the solution described in the task
  • Follow acceptance criteria precisely
  • Commit all changes to assigned branch
  • Write detailed handoffs with concerns and suggestions
Workflow: Plan → Execute → Verify → Commit
// Worker execution in sandbox
export async function runWorker(): Promise<void> {
  const { task, systemPrompt, llmConfig } = payload;
  
  // Create agent session with full Pi capabilities
  const { session } = await createAgentSession({
    cwd: WORK_DIR,
    model,
    tools: fullPiTools,  // read, bash, edit, write, grep, find, ls
  });
  
  // Execute task
  await session.prompt(buildTaskPrompt(task));
  
  // Commit changes
  safeExec("git add -A", WORK_DIR);
  safeExec(`git commit -m "feat(${task.id}): auto-commit"`, WORK_DIR);
  
  // Build handoff
  writeResult(handoff);
}

Non-Negotiable Constraints

Workers must:
  • NEVER leave TODOs, placeholders, or partial implementations
  • NEVER modify files outside task scope
  • NEVER delete or disable tests
  • NEVER use any types, @ts-ignore, or @ts-expect-error
  • ALWAYS commit before handoff
  • ALWAYS run verification (compile + test)
After 3 failed fix cycles, workers must stop and report as “blocked” rather than continuing to produce broken code.

Worker Handoff

Every worker returns a detailed handoff:
interface Handoff {
  taskId: string;
  status: "complete" | "partial" | "blocked" | "failed";
  summary: string;        // What was done and how (2-4 sentences)
  diff: string;           // Git diff output
  filesChanged: string[]; // Changed file paths
  concerns: string[];     // Risks, unexpected findings
  suggestions: string[];  // Ideas for follow-up work
  buildExitCode?: number; // Post-task tsc --noEmit result
  metrics: {
    linesAdded: number;
    linesRemoved: number;
    filesCreated: number;
    filesModified: number;
    tokensUsed: number;
    toolCallCount: number;
    durationMs: number;
  };
}
Handoffs with empty concerns and suggestions are almost always wrong. Workers should ALWAYS notice something worth mentioning.

Reconciler

Role: Self-healing system that keeps the main branch green Responsibilities:
  • Periodically check build and test health
  • Detect merge conflict markers, type errors, test failures
  • Generate targeted fix tasks when failures detected
  • Adjust sweep frequency based on health (adaptive intervals)
Does NOT:
  • Write code directly
  • Fix issues themselves
  • Create enhancement tasks
  • Handle warnings (errors only)

Sweep Process

// From reconciler.ts:228-518
async sweep(): Promise<SweepResult> {
  // 1. Run build checks
  const tscResult = await this.runCommand("npx", ["tsc", "--noEmit"]);
  const buildRunResult = await this.runCommand("npm", ["run", "build"]);
  
  // 2. Run tests
  const testResult = await this.runCommand("npm", ["test"]);
  
  // 3. Check for conflict markers
  const conflictResult = await this.runCommand(
    "git", ["grep", "-rl", "<<<<<<<"]
  );
  
  // 4. If all green, adjust to slower interval
  if (buildOk && testsOk && !hasConflictMarkers) {
    this.consecutiveGreenSweeps++;
    if (this.consecutiveGreenSweeps >= 3) {
      this.adjustInterval(this.maxIntervalMs);
    }
    return { buildOk: true, testsOk: true, fixTasks: [] };
  }
  
  // 5. Call LLM to generate fix tasks
  const response = await this.completeLLM(messages);
  const fixTasks = parseLLMTaskArray(response.content);
  
  // 6. Reset to faster interval when errors detected
  this.consecutiveGreenSweeps = 0;
  this.adjustInterval(this.minIntervalMs);
  
  return { buildOk, testsOk, hasConflictMarkers, fixTasks };
}

Adaptive Sweep Intervals

The reconciler adjusts its checking frequency based on health:
  • Green state (3+ consecutive passes): Slow down to maxIntervalMs (5 minutes)
  • Red state (errors detected): Speed up to minIntervalMs (1 minute)
This balances responsiveness during active development with efficiency during stable periods.

Error Priority

When multiple error types exist, reconciler fixes in this order:
  1. Merge conflict markers: Nothing works until resolved
  2. Build failures: Project cannot be used
  3. Compiler errors: Type safety violations compound
  4. Test failures: Functional regressions
Reconciler NEVER creates tasks for multiple priority levels in the same sweep. Lower-priority errors often resolve as side effects of fixing higher-priority ones.

Error Grouping

Reconciler groups related errors into single fix tasks:
  • Same file, same cause: 5 errors from renamed type → 1 task
  • Import chain: File A can’t find export from File B → 1 task scoped to both
  • Interface mismatch: Type changed in A, callers in B and C break → 1 task
Maximum of 5 fix tasks per sweep to avoid overwhelming the system.

Agent Communication Flow

Specialized Prompts

Each agent has a specialized system prompt that defines its behavior:
  • Root Planner: prompts/root-planner.md — Iterative discovery, sprint planning, scratchpad management
  • Subplanner: prompts/subplanner.md — Hierarchical decomposition, subtask coordination
  • Worker: prompts/worker.md — Plan-execute-verify workflow, handoff requirements
  • Reconciler: prompts/reconciler.md — Error classification, fix task generation
Prompts are loaded from the filesystem and can be customized per deployment without code changes.

Agent Isolation

Workers operate in complete isolation:
  • Each gets its own ephemeral sandbox (see Sandboxes)
  • No visibility into other workers
  • No coordination required
  • Full filesystem and git access within sandbox
Planner and Reconciler share the orchestrator process:
  • Access to global task queue and merge queue
  • Visibility into all active work
  • Can read the target repository directly

Logging and Observability

All agents use structured logging:
interface LogEntry {
  timestamp: number;
  level: "debug" | "info" | "warn" | "error";
  agentId: string;
  agentRole: AgentRole;
  taskId?: string;
  message: string;
  data?: Record<string, unknown>;
}
Logs are aggregated and available through the monitoring dashboard for debugging and performance analysis.

Build docs developers (and LLMs) love