# C4 Code View and UML Class Model

Date: 2026-05-27
Status: Formal V1 code-view design for AirCoding V1.0.0 Alpha skeleton

This document refines `AirPlan/docs/architecture/c4/module.md` from container/module view into package-level code structure, class/service responsibilities, UML class diagrams, and implementation boundaries.

Canonical TypeScript interface contracts are defined in `AirPlan/docs/architecture/interface-contracts-v1.md`.

## 1. Code View Scope

The V1.0.0 Alpha code view covers these packages:

```text
packages/contracts
packages/cli
packages/runtime
packages/llm
packages/toolchain-cpp
packages/tui
```

The code view is implementation-facing but remains architecture-only. It defines expected classes/interfaces and relationships; exact file names may be adjusted during implementation if contracts and dependencies remain stable.

## 2. Package Dependency Diagram

```plantuml
@startuml
skinparam componentStyle rectangle

package "packages/contracts" as contracts
package "packages/cli" as cli
package "packages/runtime" as runtime
package "packages/llm" as llm
package "packages/toolchain-cpp" as cpp
package "packages/tui" as tui

cli --> runtime
cli --> tui
cli --> llm
cli --> cpp

runtime --> contracts
runtime --> llm : adapter interfaces
runtime ..> cpp : capability registration boundary

llm --> contracts
cpp --> contracts
tui --> contracts
runtime --> llm : ProviderManager facade/API

@enduml
```

Rules:

1. `contracts` must have no implementation-package dependencies.
2. `runtime` must not import `tui`.
3. `tui` must depend only on `contracts` and a narrow `ProjectionClient`/UI API boundary; it must not import runtime private services, query SQLite, or subscribe to EventBus directly.
4. `toolchain-cpp` exposes tools through capability registration, not direct runtime coupling.
5. `llm` owns provider adapters, model config, provider conversion, and ProviderManager implementation; `runtime` owns prompt assembly and calls LLM through the provider facade/API.

## 3. Contracts Package Code View

Expected directory:

```text
packages/contracts/src/
  index.ts
  ids.ts
  runtime.ts
  event.ts
  ipc.ts
  task.ts
  worker-result.ts
  tool.ts
  artifact.ts
  evidence.ts
  project.ts
  provider.ts
  permission.ts
  ui.ts
  error.ts
  capability.ts
  platform.ts
```

### UML

```plantuml
@startuml
interface RuntimeEvent<T> {
  id: UUID
  type: string
  version: number
  timestamp: ISOTimeString
  session_id: SessionID
  project_id?: ProjectID
  source: EventSource
  route: string[]
  payload: T
}

interface TaskSpec {
  id: TaskID
  type: TaskType
  title: string
  description: string
  acceptance_criteria: string[]
  scope: TaskScope
  dependencies: TaskDependencySpec[]
  verification: VerificationPolicy
  constraints: TaskConstraints
  context_refs: TaskContextRefs
  output_contract: WorkerOutputContract
}

interface WorkerResult<T> {
  task_id: TaskID
  agent_id: AgentID
  agent_type: AgentType
  status: WorkerStatus
  summary: string
  changed_files: string[]
  artifacts: ArtifactRef[]
  verification: VerificationResult[]
  risks: Risk[]
  follow_up_tasks: FollowUpTask[]
  evidence_refs: EvidenceRef[]
  result: T
}

interface ToolDefinition<I, O> {
  name: string
  version: number
  description: string
  input_schema: JsonSchema<I>
  output_schema: JsonSchema<O>
  category: ToolCategory
  permissions: ToolPermissionSpec
  streaming: boolean
}

interface ProviderCapabilityMatrix
interface AirError
interface ArtifactRef
interface EvidenceRef
interface PermissionDecision
interface ProjectionSnapshot
interface CapabilityManifestV1

RuntimeEvent --> EventSource
TaskSpec --> WorkerResult : output contract
WorkerResult --> ArtifactRef
WorkerResult --> EvidenceRef
ToolDefinition --> PermissionDecision
AirError --> EvidenceRef
@enduml
```

### Contract Ownership

| Contract | File | Primary consumers |
|---|---|---|
| IDs and aliases | `ids.ts` | all packages |
| RuntimeEvent/EventSource | `event.ts` | EventStore, EventBus, IPC, ProjectionStore |
| TaskSpec | `task.ts` | Scheduler, workers, ContextAssembler |
| WorkerResult | `worker-result.ts` | workers, Scheduler, Main Agent |
| ToolDefinition/ToolResult | `tool.ts` | ToolRegistry, capabilities, workers |
| ArtifactRef/EvidenceRef | `artifact.ts`, `evidence.ts` | ArtifactStore, EvidenceStore, reports |
| ProviderCapabilityMatrix | `provider.ts` | ProviderManager, Scheduler, Doctor |
| AirError/ErrorKind | `error.ts` | tools, Scheduler, agents, UI |
| IpcMessage/ControlMessage | `ipc.ts` | WorkerManager, child workers |
| PermissionDecision | `permission.ts` | PermissionEngine, ToolRegistry, UI |
| ProjectionSnapshot | `ui.ts` | ProjectionStore, TUI/HUD |
| CapabilityManifestV1 | `capability.ts` | CapabilityRegistry, Doctor |

## 4. Runtime Package Code View

Expected directory:

```text
packages/runtime/src/
  index.ts
  app/
    RuntimeApp.ts
    ServiceRegistry.ts
  config/
    ConfigLoader.ts
    ResourceLoader.ts
  project/
    ProjectLocator.ts
    ProjectInitializer.ts
    ProjectStore.ts
  storage/
    DatabaseManager.ts
    MigrationRunner.ts
    repositories/
  events/
    EventBus.ts
    EventStore.ts
    EventIngestor.ts
    EventSchemaRegistry.ts
  sessions/
    SessionManager.ts
    SessionStore.ts
  scheduler/
    Scheduler.ts
    TaskGraph.ts
    WavePlanner.ts
    RetryPlanner.ts
    WorkspaceManager.ts
    AgentMonitor.ts
  workers/
    WorkerManager.ts
    WorkerProcess.ts
    WorkerProtocol.ts
    roles/
  tools/
    ToolRegistry.ts
    BuiltInToolRegistrar.ts
    fs/
    shell/
    git/
    project/
    artifact/
    context/
    permission/
    doctor/
  security/
    PermissionEngine.ts
    PathClassifier.ts
    CommandRiskAnalyzer.ts
    SecretRedactor.ts
  capabilities/
    CapabilityRegistry.ts
    CapabilityManifestValidator.ts
  context/
    ContextAssembler.ts
    PromptLayerLoader.ts
    CompactionPolicy.ts
  artifacts/
    ArtifactStore.ts
    EvidenceStore.ts
  knowledge/
    DebugKnowledgeStore.ts
    LearnedMemoryStore.ts
  projection/
    ProjectionStore.ts
    projections/
  doctor/
    DoctorService.ts
    checks/
  logging/
    Logger.ts
    DeveloperLogEncryptor.ts
  agents/
    main/
    architecture/
```

### Runtime Service UML

```plantuml
@startuml
class RuntimeApp {
  +start(options): Promise<void>
  +shutdown(): Promise<void>
}

class ServiceRegistry {
  +get<T>(token): T
  +register(token, service): void
}

class ProjectStore {
  +open(root): Promise<ProjectContext>
  +initialize(root): Promise<ProjectContext>
}

class SessionManager {
  +openSession(project, options): Promise<SessionContext>
  +closeSession(sessionId): Promise<void>
}

class DatabaseManager {
  +open(path): DatabaseHandle
  +transaction(fn): Promise<T>
}

class MigrationRunner {
  +migrate(db): Promise<void>
}

class EventStore {
  +append(event): Promise<void>
  +query(filter): Promise<RuntimeEvent[]>
}

class EventIngestor {
  +ingest(event): Promise<void>
  +ingest_ephemeral(event): Promise<void>
}

class EventBus {
  +publish(event): void
  +subscribe(filter, handler): Subscription
}

class ProjectionStore {
  +hydrate(sessionId): Promise<void>
  +apply(event): void
  +snapshot(): ProjectionSnapshot
}

RuntimeApp --> ServiceRegistry
RuntimeApp --> ProjectStore
RuntimeApp --> SessionManager
SessionManager --> DatabaseManager
DatabaseManager --> MigrationRunner
EventIngestor --> EventStore : durable events
EventIngestor --> EventBus : ephemeral events
EventStore --> DatabaseManager
EventStore --> EventBus : publishes after commit
ProjectionStore --> SessionStore : hydrate via repositories
ProjectionStore --> EventBus : subscribes to live events
@enduml
```

### Scheduler UML

```plantuml
@startuml
class Scheduler {
  +create_tasks(session_id, specs): Promise<void>
  +add_dependency(session_id, task_id, dependency): Promise<void>
  +load_graph(session_id): Promise<TaskGraph>
  +run_until_idle(session_id): Promise<SchedulerRunResult>
  +cancel_task(task_id, reason): Promise<void>
  -plan_wave(graph): SchedulerWavePlan
  -dispatch(wave): Promise<void>
  -collect_results(): Promise<void>
}

class TaskGraph {
  +tasks: Map<TaskID, TaskNode>
  +dependencies: TaskDependencyRecord[]
  +getRunnableTasks(): TaskNode[]
  +markTerminal(taskId, status): void
}

class WavePlanner {
  +plan(graph, resources): SchedulerWavePlan
}

class RetryPlanner {
  +decide(task, attempts, error): RetryDecision
}

class WorkspaceManager {
  +createWorkspace(plan): Promise<WorkspaceRef>
  +mergeWorkspace(workspaceId): Promise<MergeResult>
  +cleanupWorkspace(workspaceId): Promise<void>
}

class AgentMonitor {
  +recordHeartbeat(event): void
  +detectLostAgents(): Promise<AgentLost[]>
  +enforceTimeouts(): Promise<void>
}

class WorkerManager {
  +spawn(taskSpec, contextPack): Promise<WorkerProcess>
  +cancel(agentId, reason): Promise<void>
}

class WorkerProcess {
  +agent_id: AgentID
  +pid?: number
  +send(envelope): void
  +on_message(handler): void
}

Scheduler --> TaskGraph
Scheduler --> WavePlanner
Scheduler --> RetryPlanner
Scheduler --> WorkspaceManager
Scheduler --> AgentMonitor
Scheduler --> WorkerManager
Scheduler --> EventStore
Scheduler --> ContextAssembler
WorkerManager --> WorkerProcess
@enduml
```

### Tool/Permission UML

```plantuml
@startuml
class ToolRegistry {
  +register(definition, executor): void
  +call(name, input, context): Promise<ToolResultEnvelope>
  +list(): ToolDefinition[]
}

class PermissionEngine {
  +evaluate(request): Promise<PermissionDecision>
  +record(decision, context): Promise<void>
}

class PathClassifier {
  +classify(path, projectRoot): PathRiskClassification
}

class CommandRiskAnalyzer {
  +analyze(command, cwd): CommandRiskAnalysis
}

class CapabilityRegistry {
  +discover(): Promise<CapabilityManifestV1[]>
  +validate(manifest): Promise<ValidationResult>
  +enable(capability_id): Promise<void>
  +disable(capability_id): Promise<void>
  +register_tools(tool_registry): Promise<void>
}

class DoctorService {
  +run(input): Promise<DoctorRunOutput>
  +checkCapability(capability): Promise<DoctorIssue[]>
}

ToolRegistry --> PermissionEngine
ToolRegistry --> EventIngestor
ToolRegistry --> ArtifactStore
PermissionEngine --> PathClassifier
PermissionEngine --> CommandRiskAnalyzer
PermissionEngine --> EventIngestor
CapabilityRegistry --> ToolRegistry : registers enabled tools
DoctorService --> CapabilityRegistry : reads manifests/checks dependencies
@enduml
```

### Context/Agent UML

```plantuml
@startuml
class ContextAssembler {
  +assemble(input): Promise<AssembledContext>
  -loadLayers(profile): Promise<PromptLayer[]>
  -fitBudget(context): BudgetFitResult
}

class PromptLayerLoader {
  +loadRuntimeInvariant(): PromptLayer
  +loadRole(role): PromptLayer
  +loadProjectRules(project): PromptLayer[]
}

class MainAgent {
  +handleUserMessage(message): Promise<void>
  +presentProgress(): Promise<void>
  +presentBlocker(blocker): Promise<void>
}

class ArchitectureDesigner {
  +assessImpact(change): Promise<ArchitectureImpact>
  +updateArchitectureDocs(update): Promise<DocumentUpdate>
}

interface ProviderManager {
  +select_model(requirement): Promise<ModelAssignment>
  +complete(request): AsyncIterable<ProviderStreamEvent>
}

ContextAssembler --> PromptLayerLoader
MainAgent --> ContextAssembler
MainAgent --> ProviderManager : LLM facade from packages/llm
MainAgent --> Scheduler
ArchitectureDesigner --> ContextAssembler
ArchitectureDesigner --> ProviderManager : LLM facade from packages/llm
ArchitectureDesigner --> EventIngestor

class DebugKnowledgeStore {
  +insert(record): Promise<void>
  +lookup_by_signature(sig): Promise<DebugRecord[]>
  +lookup_by_task(task_id): Promise<DebugRecord[]>
  +update(id, patch): Promise<void>
}

class LearnedMemoryStore {
  +insert(memory): Promise<void>
  +lookup_by_type(type): Promise<LearnedMemory[]>
  +update_status(id, status): Promise<void>
  +scan_stale(): Promise<LearnedMemory[]>
}

class CompactionPolicy {
  +should_compact(messages, budget): boolean
  +compact(messages, target): Promise<CompactionResult>
}

ExperienceMinerRole --> LearnedMemoryStore
DebuggerRole --> DebugKnowledgeStore
ContextAssembler --> CompactionPolicy
@enduml
```

## 5. LLM Package Code View

Expected directory:

```text
packages/llm/src/
  index.ts
  ProviderManager.ts
  ModelConfigLoader.ts
  CapabilityMatrix.ts
  ConversionReport.ts
  adapters/
    AnthropicAdapter.ts
    OpenAICompatibleAdapter.ts
  canonical/
    AnthropicCanonical.ts
    ToolUseConverter.ts
    StreamNormalizer.ts
```

### UML

```plantuml
@startuml
interface ProviderAdapter {
  +provider_id: string
  +list_models(): Promise<ProviderCapabilityMatrix[]>
  +validate_model(model_id): Promise<ProviderCapabilityMatrix>
  +complete(input): AsyncIterable<ProviderStreamEvent>
  +count_tokens?(input): Promise<number>
}

class ProviderManager {
  +load_config(): Promise<void>
  +select_model(requirement): Promise<ModelAssignment>
  +complete(input): AsyncIterable<ProviderStreamEvent>
}

class ModelConfigLoader
class CapabilityMatrixRegistry
class AnthropicAdapter
class OpenAICompatibleAdapter
class AnthropicCanonicalConverter
class StreamNormalizer

ProviderManager --> ProviderAdapter
ProviderManager --> ModelConfigLoader
ProviderManager --> CapabilityMatrixRegistry
AnthropicAdapter ..|> ProviderAdapter
OpenAICompatibleAdapter ..|> ProviderAdapter
OpenAICompatibleAdapter --> AnthropicCanonicalConverter
ProviderAdapter --> StreamNormalizer
@enduml
```

## 6. Toolchain C++ Package Code View

Expected directory:

```text
packages/toolchain-cpp/src/
  index.ts
  capability.ts
  CppToolRegistrar.ts
  detect/
    CppProjectDetector.ts
  build/
    CMakeConfigurator.ts
    CppBuilder.ts
  test/
    CppTestRunner.ts
  analysis/
    CppcheckRunner.ts
    ClangdClient.ts
    DiagnosticParser.ts
```

### UML

```plantuml
@startuml
class CppToolRegistrar {
  +register(toolRegistry): void
}

class CppProjectDetector {
  +detect(projectRoot): Promise<CppDetectOutput>
}

class CMakeConfigurator {
  +configure(input): Promise<CppCmakeConfigureOutput>
}

class CppBuilder {
  +build(input): Promise<CppBuildOutput>
}

class CppTestRunner {
  +runTests(input): Promise<CppTestOutput>
}

class CppcheckRunner {
  +run(input): Promise<CppcheckOutput>
}

class ClangdClient {
  +query(input): Promise<ClangdQueryOutput>
}

class DiagnosticParser {
  +parse_compiler_output(output): Diagnostic[]
  +semantic_signature(diagnostic): string
}

CppToolRegistrar --> CppProjectDetector
CppToolRegistrar --> CMakeConfigurator
CppToolRegistrar --> CppBuilder
CppToolRegistrar --> CppTestRunner
CppToolRegistrar --> CppcheckRunner
CppToolRegistrar --> ClangdClient
CMakeConfigurator --> DiagnosticParser
CppBuilder --> DiagnosticParser
CppTestRunner --> DiagnosticParser
CppcheckRunner --> DiagnosticParser
@enduml
```

## 7. TUI Package Code View

Expected directory:

```text
packages/tui/src/
  index.ts
  TuiApp.tsx
  ProjectionClient.ts
  components/
    SessionView.tsx
    TaskListView.tsx
    AgentStatusView.tsx
    ToolRunView.tsx
    DiffView.tsx
    EvidenceView.tsx
    PermissionPrompt.tsx
    BlockerReport.tsx
    HudView.tsx
  theme/
    theme.ts
  keymap/
    keymap.ts
```

### UML

```plantuml
@startuml
class TuiApp {
  +start(): void
  +stop(): void
}

class ProjectionClient {
  +subscribe(handler): Subscription
  +snapshot(): ProjectionSnapshot
}

class SessionView
class TaskListView
class AgentStatusView
class ToolRunView
class EvidenceView
class PermissionPrompt
class BlockerReport
class HudView

TuiApp --> ProjectionClient
TuiApp --> SessionView
TuiApp --> TaskListView
TuiApp --> AgentStatusView
TuiApp --> ToolRunView
TuiApp --> EvidenceView
TuiApp --> PermissionPrompt
TuiApp --> BlockerReport
TuiApp --> HudView
@enduml
```

Rules:

1. UI components render projections only.
2. Permission prompts emit user decisions through the narrow runtime UI command API, not private runtime services.
3. UI never mutates domain tables directly.
4. UI never imports `packages/runtime/src/*` private implementation modules.
5. Diff/evidence views must link back to artifact/evidence refs.

## 8. CLI Package Code View

Expected directory:

```text
packages/cli/src/
  index.ts
  commands/
    run.ts
    init.ts
    doctor.ts
    provider.ts
    e2e.ts
    release.ts
  bootstrap/
    createRuntime.ts
    loadConfig.ts
```

### UML

```plantuml
@startuml
class CliEntrypoint {
  +main(argv): Promise<number>
}

class RunCommand
class InitCommand
class DoctorCommand
class ProviderCommand
class E2ECommand
class ReleaseCommand
class RuntimeFactory {
  +create(options): Promise<RuntimeApp>
}

CliEntrypoint --> RunCommand
CliEntrypoint --> InitCommand
CliEntrypoint --> DoctorCommand
CliEntrypoint --> ProviderCommand
CliEntrypoint --> E2ECommand
CliEntrypoint --> ReleaseCommand
RunCommand --> RuntimeFactory
InitCommand --> RuntimeFactory
DoctorCommand --> RuntimeFactory
@enduml
```

## 9. Repository Interface Code View

Domain repositories live under `packages/runtime/src/storage/repositories/`.

```plantuml
@startuml
interface SessionRepository
interface MessageRepository
interface EventRepository
interface TaskRepository
interface AgentRepository
interface ToolRunRepository
interface CommandRunRepository
interface ArtifactRepository
interface DiagnosticRepository
interface EvidenceRepository
interface WorkspaceRepository
interface SummaryRepository
interface UiStateRepository

class SessionStore {
  +sessions: SessionRepository
  +messages: MessageRepository
  +events: EventRepository
  +tasks: TaskRepository
  +agents: AgentRepository
  +toolRuns: ToolRunRepository
  +commandRuns: CommandRunRepository
  +artifacts: ArtifactRepository
  +diagnostics: DiagnosticRepository
  +evidence: EvidenceRepository
  +workspaces: WorkspaceRepository
  +summaries: SummaryRepository
  +uiState: UiStateRepository
}
@enduml
```

Repositories must be thin persistence adapters. Scheduling, permission, and projection logic must not be hidden inside repositories.

## 10. Worker Role Code View

```plantuml
@startuml
interface WorkerRole {
  +run(taskSpec, contextPack, runtime): Promise<WorkerResult>
}

class WorkerRuntime {
  +emit(event): void
  +callTool(name, input): Promise<ToolResultEnvelope>
  +checkpoint(data): Promise<void>
}

class ExecutorRole
class ReviewerRole
class DebuggerRole
class CompactorRole
class ExperienceMinerRole

ExecutorRole ..|> WorkerRole
ReviewerRole ..|> WorkerRole
DebuggerRole ..|> WorkerRole
CompactorRole ..|> WorkerRole
ExperienceMinerRole ..|> WorkerRole
WorkerRole --> WorkerRuntime
@enduml
```

Role constraints:

| Role | Write access | Required output |
|---|---|---|
| Executor | scoped project writes | ExecutorResult in WorkerResult |
| Reviewer | read-only | ReviewerResult |
| Debugger | scoped writes only when assigned | DebuggerResult |
| Compactor | summaries/artifacts only | CompactorResult |
| ExperienceMiner | candidates/rules/skills only when assigned | ExperienceMinerResult |

## 11. State Ownership Rules

| State | Owner | Access rule |
|---|---|---|
| session DB | SessionStore/EventStore | runtime services only |
| live events | EventBus | runtime services publish/subscribe |
| UI projections | ProjectionStore | TUI read only |
| artifacts | ArtifactStore | tools/workers via runtime API |
| evidence refs | EvidenceStore | reports/reviews/debug via runtime API |
| tasks/agents | Scheduler | repositories are storage only |
| permission decisions | PermissionEngine | ToolRegistry requests decisions |
| model config | ProviderManager | runtime/Doctor read through API |
| project rules | ContextAssembler/ProjectStore | workers receive context excerpts |

## 12. Implementation Cut Lines

V1.0.0 Alpha implementation must create public interfaces matching `interface-contracts-v1.md`. Classes may be implemented as functions/modules where idiomatic TypeScript is simpler, but module ownership, dependency direction, and service boundaries must remain intact.

Do not implement:

```text
TUI direct DB access
worker direct SQLite writes
capability direct dependency install
provider adapter changing prompt semantics silently
tool execution without PermissionEngine
repositories containing scheduling policy
```
