Complete architecture document set with multi-model review remediation: - Frozen interface contracts, runtime semantics, DB schemas - Event/tool/error/provider registries - Scheduler and main agent state machines - C4 module/code views, solution architecture, baseline V1 - Multi-model review reports and joint assessment - Phase-gate remediation complete (P0/P1/P2/UX resolved) - Implementation plan with T-000A through T-045 - Reference folders kept as placeholders only
9.2 KiB
9.2 KiB
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/solidpackages/runtime— Agent Loop, Session, Scheduler, Tool Registry, EventBuspackages/llm— Provider/Model abstraction (reference@opencode-ai/llm, but may fork/adapt)packages/toolchain— C++ BuildTool, DiagnosticParser, TestRunnerpackages/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.spawnwith 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
AgentHeartbeatevents (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-id>/session.dbper session~/.air/projects/<project-id>/debug-records.dbcross-session~/.air/projects/<project-id>/learned-memory.dbcross-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.jsonon demand viacmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON. No persistence/caching between sessions.
D-013: clangd CLI Mode for MVP
- Decision: CLI mode (
clangd --check=<file>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
Diagnosticrecords 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 asunknown, 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.