# Architecture Decisions — Round 1 Date: 2026-05-25 Source: idea.md discussion, questions 1–18 ## D-001: Bun as JS Runtime - **Decision**: Bun. - **Why**: Both OpenCode and Claude Code use Bun; built-in SQLite removes native addon dependency; native TSX support simplifies build config; C++ modules communicate via subprocess, not native addons. - **Distribution**: Bundle Bun runtime with AirCoding. ## D-002: @opentui/solid as TUI Framework - **Decision**: `@opentui/solid` (MIT licensed, standalone project, not coupled to OpenCode). - **Why**: OpenTUI is an independent library consumed by OpenCode as a regular npm dependency. The TUI rendering layer is cleanly separated from OpenCode's business logic. Reference OpenCode's TUI component patterns for interaction design. - **Reuse strategy**: Direct npm dependency on `@opentui/solid @opentui/core @opentui/keymap`. AirCoding TUI components reference OpenCode patterns but are independently implemented. ## D-003: Bun Monorepo - **Decision**: Monorepo with Bun workspaces + Turborepo. - **MVP packages**: - `packages/tui` — AirCoding TUI components on top of `@opentui/solid` - `packages/runtime` — Agent Loop, Session, Scheduler, Tool Registry, EventBus - `packages/llm` — Provider/Model abstraction (reference `@opencode-ai/llm`, but may fork/adapt) - `packages/toolchain` — C++ BuildTool, DiagnosticParser, TestRunner - `packages/cli` — Entry point, assembles all packages - **Later**: Plugin SDK, debug knowledge, Python worker bridge. ## D-004: Python as Subprocess-Only Tooling - **Decision**: Python workers are called via `Bun.spawn` with JSON-over-stdio. No long-lived Python server. No bundled Python environment. - **Scope**: Python only wraps existing C++ toolchain scripts and Python-specific libraries. Experience mining, context assembly, and memory management stay in TS runtime. - **Re-evaluated from idea.md**: idea.md assigned Python for Hermes-style learning and AirContext compression, but these are LLM + text + SQLite operations that Bun/TS handles natively. Keeping them in TS avoids unnecessary language bridging. ## D-005: Event-Driven Agent Architecture - **Decision**: Event-driven. Main Agent subscribes to EventBus for agent/task/tool events and renders progress to TUI/HUD. - **Main Agent state machine** (see `AirPlan/docs/architecture/main-agent-state-machine.md`): - IDLE → CLASSIFYING → DELEGATING → CONFIRMING → EXECUTING → SUMMARIZING → IDLE - INTERRUPTING for mid-execution user requirement changes (classified via LLM) - Error handling: Main Agent handles what it can, escalates to user only when necessary - **Key constraint**: Main Agent must remain idle-ready for user intervention. Background tasks (ExperienceMiner, DebugKnowledge indexing) are dispatched to sub-agents. ## D-006: Sub-Agent Loops Are Independent - **Decision**: Executor, Reviewer, and Debugger each have their own agent loop implementation. Not a shared generic loop engine. - **Executor loop**: LOADING → THINKING → ACTING → OBSERVING → (loop with debugging sub-loop on failure) → FINALIZING - **Reviewer loop**: LOADING → REVIEWING → DECIDING (approved / changes_requested / blocked) - **Debugger loop**: GATHERING → ANALYZING → FIXING → (RECORDING or ESCALATING) - **Sub-agent execution quality**: Claude Code is the behavioral benchmark (read-first, small edits, verify before return, follow Project Rules). ## D-007: Independent Processes for Sub-Agents - **Decision**: Each Executor/Reviewer/Debugger is an independent Bun process spawned by Scheduler. IPC via stdio + JSON (same mechanism as Python workers). - **Why**: Crash isolation, context isolation, natural worktree support. Single-process approach excluded due to context explosion and lack of fault isolation. - **Communication**: Scheduler passes TaskSpec (worktree path, tool set, permission level) on spawn; sub-agent returns structured WorkerResult JSON on completion. ## D-008: Push Heartbeat + Soft/Hard Timeout - **Decision**: Sub-agents push `AgentHeartbeat` events (status, turn count, tokens) every N seconds. Scheduler subscribes and detects stalls. - **Heartbeat**: Push model. Sub-agent proactively reports state. - **Timeout**: Hybrid. Hard timeout kills on expiration. Soft timeout warns and allows extension requests (sub-agent can justify need for more time). Scheduler decides per-task. - **Loop detection**: Same error signature appearing N+ times triggers escalation to Main Agent (not auto-kill). ## D-009: Per-Session SQLite with Anthropic-Native Storage - **Decision**: - `~/.air/sessions//session.db` per session - `~/.air/projects//debug-records.db` cross-session - `~/.air/projects//learned-memory.db` cross-session - **Message format**: Anthropic-native content blocks (TextBlock, ThinkingBlock, ToolUseBlock, ToolResultBlock), following Claude Code's approach. - **Provider switching**: Same-provider switching (e.g., Opus → Sonnet) is zero-cost. Cross-provider switching converts at API boundary (Anthropic format → target provider format → response → back to Anthropic format for storage). - **Event persistence**: - High-frequency events (TokenDelta, StdoutChunk) → EventBus only, not persisted - Durable events (TaskCompleted, ToolRunCompleted) → SQLite, synchronous write on main thread - WAL mode for concurrent read/write ## D-010: Full Context Dump on Session Exit - **Decision**: On session exit, dump complete context to session storage. On resume, load from full dump, not reconstruct from summaries. - **Recovery**: - Compaction is internal projection optimization, not data removal - Structured summaries serve as index for fast historical lookup - Scheduler rebuilds task queue from AgentTask table; running tasks judged by heartbeat timestamp - User sees full conversation history transparently ## D-011: Build System Auto-Detection - **Priority**: CMake (built-in) > Meson/Bazel/XMake (capability plugin) > Makefile/.sln - **Conflict handling**: When multiple build system files exist (e.g., CMakeLists.txt + meson.build), ask user to choose. - **Generator**: Ninja first, fall back to Make if Ninja fails. - **Config failure**: BuildTool built-in logic attempts fix first (install missing deps, adjust CMake args); if unresolved, hand off to Debugger. ## D-012: compile_commands.json — On-Demand Generation, No Caching - **Decision**: Detect and generate `compile_commands.json` on demand via `cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON`. No persistence/caching between sessions. ## D-013: clangd CLI Mode for MVP - **Decision**: CLI mode (`clangd --check=` or equivalent) for MVP. Spawn on tool call, exit on completion. LSP daemon mode deferred to later phase if CLI latency proves unacceptable. ## D-014: LLM-Based Diagnostic Parsing - **Decision**: All compiler/linker output parsed via LLM (not regex). LLM extracts structured `Diagnostic` records and generates semantic error signatures. - **Error signature**: LLM computes semantic signature that normalizes across GCC/Clang/MSVC wording differences (e.g., "use of undeclared identifier" and "was not declared in this scope" map to same signature). - **Linker errors**: Separately categorized from compiler diagnostics. ## D-015: LLM-Driven Project Initialization — Loose Acceptance - **Decision**: Scanner collects facts → LLM infers `ProjectProfile` → loose schema acceptance (missing fields marked as `unknown`, not rejected) → user confirms/corrects → incremental field update with "may affect related inferences" hint. - **No LLM retry loop** on schema mismatch. User correction is single-pass. ## D-016: Classification by LLM - **Decision**: Main Agent uses LLM to classify user messages (chat, direct-mode, simple-task, needs-planning). ## D-017: Confirmation Gating - **Decision**: Implementation-level changes that don't affect interfaces or architecture → silently proceed to EXECUTING. Architecture-level changes → Arc assessment required. Low permission: user confirms. High permission: auto-proceed with results displayed to user for immediate intervention. ## D-018: Interruption via LLM Intent Detection - **Decision**: User interruption during execution detected via LLM intent classification, not Ctrl+C (ineffective with multi-process architecture). ## D-019: Scheduler — Full Dependency Handling - **Decision**: Scheduler handles both hard dependencies (topological sort) and soft dependencies (optimization hints). Write-area conflict detection: different areas → parallel; same area but different code blocks → git worktree parallel then merge; same area, same code block → serial. - **Concurrency**: Dynamic, API-rate-limit-aware, machine-resource-aware. - **Failure**: Retry 3-5 times. Solvable failures block hard dependents only. Unsolvable failures (e.g., kernel limitation) go back to Architecture Designer. Repeated failures after max retries → Main Agent evaluates (silent resolution vs. user escalation). - **Model selection**: Scheduler decides whether to force a specific model per task or let Executor choose. ## D-020: Memory System — Claude Code Style - **Decision**: Claude Code's persistent memory system (MEMORY.md + frontmatter + typed layers) is the primary reference for memory design, alongside Hermes-style experience mining as a candidate layer.