Skip to content

Architecture

cybersenses edited this page Nov 15, 2026 · 1 revision

Architecture

CodingDude is three process roles over one SQLite store. The canonical, claim-by-claim description is docs/TECHNICAL-DESIGN-MANUAL.md; this page is the map.

The shape

 Browser (React/Vite SPA, frontend/src)
    │  SSE (run events, artifact deltas) + HTTP JSON
    ▼
 Server (server.py — FastAPI/uvicorn, loopback-only)
    │  spawns one subprocess per run; bridges worker WebSocket ⇄ browser SSE
    │  persists sessions/messages/runs/artifacts/planning to SQLite (db.py)
    ▼
 Worker (worker.py — python -m codingdude.worker)
    │  builds config, constructs the engine, streams trace events
    ▼
 Engine (engine.py — pydantic-ai agent loop)
    │  tool calls (workdir-contained)          │ MCP servers / plugins
    ▼                                          ▼
 Filesystem · shell · git · gh            LLM providers (user-configured)

Headless (codingdude run) is the same engine in-process — no server, no worker.

Why three roles

  1. Server never runs the agent (ADR-0002). It owns HTTP/SSE, auth, persistence, the run registry and worker supervision.
  2. Worker — one subprocess per run, dials back over a local WebSocket, dies with its run. A crash or a killed run cannot take the server with it.
  3. Engine — the pydantic-ai loop, shared by the worker and headless paths; run_pipeline.py orchestrates a whole run (onboarding, context, memory, planning loop, toolsets, gates, memory-extraction tail).

Lifecycle guarantees

  • One live worker per session. Starting a new run terminates any live worker for that session and pushes RUN_CANCELLED to the old viewers.
  • Reaper. After a worker's socket closes, if no terminal event was delivered the server synthesises RUN_ERROR (worker_died) and records the run end — idempotently.
  • Janitor. A 2 s sweep, independent of any socket: reaps exited workers, and terminates a worker that never connected within 60 s.
  • Terminal replay grace. A finished channel stays registered for 300 s so EventSource auto-reconnects replay the terminal and settle.
  • Boot reconciliation. At startup, runs still marked running become interrupted; dead process rows get durable end states.
  • Prompts travel by file (~/.codingdude/prompts/<run_id>.prompt), not argv — Windows has a 32k command-line limit. Stale files older than 24 h are cleaned at boot.

Module map (src/codingdude/)

Module Responsibility
server.py FastAPI app factory, HTTP/SSE/WS endpoints, static serving, run channels, janitor
worker.py per-run subprocess; applies project [models] overrides, runs the pipeline, streams events
engine.py agent construction: model resolution, toolsets, approvals, budget, failover
run_pipeline.py full-run orchestration, including the per-request compaction guard
context.py token estimate, needs_compaction, plan_compaction, mechanical compaction
agent_caps.py cache capability profiles (RUN / ONE_SHOT) — the single seam every agent takes caching from
limits.py the per-agent.run() spend ceilings (max_cost_usd, max_total_tokens)
review_gate.py pre-push review gate: content-addressed diff key, record_review, require_review
approvals.py tiered approval engine (cards, pattern approvals, always-gated set)
tools.py core toolsets: files, shell, git/gh, web, delegation, coverage, knip, CI, audit, ask-user
ast_tools.py, callgraph.py, cfg.py, structure.py, impact.py, deadcode.py, mutation.py, fuzzing.py, bench.py analysis tools
roadmap.py planning store: roadmap, tickets, decisions, PRDs, tech designs (+ ROADMAP.md parser)
planning.py plan store and step tracking for the planning loop
memory.py, skills.py, directives.py, onboarding.py continuity and instruction layering
checkpoints.py, artifacts.py, processes.py snapshots/rewind, interactive artifacts, shell process tracking
sandbox.py, worktrees.py, delegation.py sandboxes, git worktrees, sub-agent plumbing
telemetry.py, traces.py, evals.py usage/cost accounting, JSONL traces, scenario record/replay
security.py security gate (Bandit, Semgrep, secret regexes) — fails closed
scaffold.py, seed.py project scaffolding; ~/.codingdude seeding
config.py, db.py, cli.py, mcp_config.py, codegraph_boot.py config, store, CLI, MCP, index boot

Data model

SQLite under the config home, in WAL mode. Tables (all additive):

sessions, messages, session_notes, project_notes, approvals, memory, usage, embeddings, plans, projects, roadmap_items, tickets, ticket_execution, reference_sections, reference_meta, artifacts, renders, processes, runs, design_decisions, prds, tech_designs.

Notes:

  • runs.status has a CHECK constraint; RUN_CANCELLED is stored as interrupted.
  • Session traces are JSONL files, not a table.
  • ROADMAP.md in the workdir is canonical when present — GET /planning parses it directly and the DB is the fallback. PRDs and tech designs are DB-only, keyed by workdir; designs carry an optional prd_id.

Frontend

React + Vite + Tailwind SPA. The workspace is a single dockable shell (dockview-react, pinned): every surface is a panel registered in workspace/catalogue.ts — the single source of truth for panels and presets — arranged by Workspace.jsx, moved via the command palette (Ctrl+Shift+P) or drag, persistable to localStorage, and poppable to OS windows. Data flows through a visibility-gated store (one 2 s scheduler, visible topics only) rather than per-panel polling. App.jsx keeps only the session/SSE state machine and login.

Build output is frontend/dist, served by the server when present — single command deployment. The frontend bakes __APP_VERSION__ at build; the backend version comes from package metadata, which is why a stale dist/ is visible as a version mismatch in the StatusFooter.

Extension points

Extend by How
New LLM provider config-only ([providers.name] with base_url)
New MCP server mcp.json (global or project), toggled per session
New skill SKILL.md in a skills dir — see Skills and Directives
New tool module-level toolset builder + registration; must honour path containment
New checklist reference/checklists/*.md (seeded into projects)
Plugins the plugins/ package hook points

Clone this wiki locally