Files
AirCoding/AirPlan/docs/architecture/decisions-round-1.md
AirCoding 82f3140847 Initial commit: AirCoding V1.0.0 Alpha architecture baseline
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
2026-05-28 18:45:01 +08:00

9.2 KiB
Raw Blame History

Architecture Decisions — Round 1

Date: 2026-05-25 Source: idea.md discussion, questions 118

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-id>/session.db per session
    • ~/.air/projects/<project-id>/debug-records.db cross-session
    • ~/.air/projects/<project-id>/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=<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 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.