Files
AirCoding/AirPlan/docs/architecture/runtime-semantics-v1.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

14 KiB

AirCoding Runtime Semantics V1

Date: 2026-05-27 Status: Canonical runtime semantics corrections for V1.0.0 Alpha

This document resolves implementation-critical semantics that cut across the event registry, DB schema, Scheduler, ArtifactStore, PermissionEngine, ContextAssembler, and ExperienceMiner.

1. Source of Truth

This document refines, not replaces:

  • interface-contracts-v1.md
  • event-registry-v1.md
  • db-schema-v1.md
  • scheduler-state-machine-v1.md
  • artifact-naming-v1.md
  • security-model-v1.md

If a runtime behavior conflicts with older wording, this document is authoritative for V1.0.0 Alpha runtime semantics.

2. Event Ingestion Boundary

All runtime events from workers, tools, agents, and internal services enter through EventIngestor.

producer
  → EventIngestor.ingest(event)
  → schema/version validation
  → persistence policy lookup
  → durable: EventStore transaction + domain projection + post-commit EventBus publish
  → ephemeral: EventBus publish/coalescing only

Responsibilities:

Component Responsibility
EventIngestor accepts events, validates routing, chooses durable vs ephemeral path
EventStore validates durable event payload, inserts event, applies domain projection transactionally
EventBus live publish/subscribe only
Domain services emit follow-up events; do not hide policy inside EventStore

EventStore must not create scheduler tasks, permission decisions, memory promotions, or doctor fixes by policy. Those are follow-up actions emitted by the owning services.

3. Durable Projection Rule

For same-session DB domain updates:

BEGIN
  insert events row
  apply domain table projection
COMMIT
publish committed event to EventBus

For external DB/file side effects, use the cross-store semantics in section 6.

4. Heartbeat Semantics

agent.heartbeat remains an ephemeral event for live UI updates, but Scheduler must coalesce heartbeat timestamps into domain rows.

Rule:

agent.heartbeat event
  → EventBus live publish
  → Scheduler/AgentMonitor updates agents.last_heartbeat_at and tasks.heartbeat_at at a throttled interval

Default coalescing interval:

5 seconds or meaningful status change, whichever comes first

Recovery uses agents.last_heartbeat_at, tasks.heartbeat_at, process liveness, and task attempt state. It does not require replaying ephemeral heartbeat events.

5. Command Run Status Semantics

command_runs V1 schema has no explicit status column. Runtime derives status:

Row state Derived status
completed_at is null running
exit_code = 0 ok
exit_code non-zero error
cancellation metadata present cancelled
inconsistent row unknown

ProjectionStore may expose derived command status. A later schema version may add a physical status column if needed.

6. Cross-DB and File Transaction Semantics

Some durable events refer to project-level DBs or files outside the session DB:

debug-records.db
learned-memory.db
.air/shared/rules/*.md
skills
artifact files
workspace files

SQLite cannot provide one transaction across arbitrary files and DBs. V1.0.0 Alpha uses an outbox/compensation model.

6.1 Session DB First For Intent

For external side effects:

1. Insert durable session event recording intent/request.
2. Insert or update session domain row with pending/external status where applicable.
3. Perform external DB/file operation through owning service.
4. Emit durable completed/failed event with evidence.
5. On restart, recovery scans pending external intents and reconciles.

6.2 Artifact Files

Artifact files use:

write temp file
compute hash/size
atomic rename
insert artifact row + artifact.created event

If the DB insert fails after rename, startup recovery scans orphaned files and either registers or quarantines them.

6.3 Debug Records

debug.record.created means:

session DB records debug-record intent/completion event
DebugKnowledgeStore writes debug-records.db
if debug-records.db write fails, emit debug.record.failed or task.failed with AirError

6.4 Memory and Rule Promotion

memory.promoted means:

candidate was approved/promoted by owning service
rule/skill/learned-memory write is performed by ExperienceMiner/Curator service
session event records completed promotion and target ref

If the file/DB write fails, emit a failure event and leave the candidate unpromoted or pending repair.

7. Summary and Compaction Ownership

Only summary.created inserts a summaries row.

context.compaction.completed records compaction task completion and references the created summary:

interface ContextCompactionCompletedPayload {
  task_id?: string
  agent_id?: string
  summary_id: string
  range_start_message_id?: string
  range_end_message_id?: string
  token_estimate_before?: number
  token_estimate_after?: number
}

Sequence:

context.compaction.requested
context.compaction.started
summary.created
context.compaction.completed

This avoids duplicate summary rows.

8. Permission Layering

Permission evaluation order:

1. tool capability declaration
2. permission profile
3. TaskSpec scope allowed/denied paths
4. path/command/network risk classification
5. credential/system-sensitive override
6. user prompt workflow if required

Project-level allow does not override task scope. Credential and system-sensitive boundaries override broad project/local allow.

Canonical decision:

action: allow | deny | ask_user | block | refuse | announce_then_run
grant_scope: none | once | session | project | global

9. Execution Primitive Semantics

Claude Code-quality execution is enforced through tool contracts, not only prompts.

9.1 Read-Before-Edit Token

fs.read returns or records a read observation:

interface FileReadObservation {
  path: string
  sha256?: string
  observed_at: ISOTimeString
  task_id?: TaskID
  agent_id?: AgentID
}

fs.edit and fs.patch require either:

active task read observation for the target file
or explicit expected_existing_sha256

9.2 Exact Edit Behavior

fs.edit rules:

  1. old_string must match exactly.
  2. If replace_all is false, old_string must occur exactly once.
  3. If no match or ambiguous match, fail with tool_error and no file write.
  4. The tool does not guess indentation or nearby replacements.
  5. Successful edit emits a diff artifact.

9.3 Patch Behavior

fs.patch rules:

  1. Patch paths must be within task scope and permission policy.
  2. Rejected hunks become artifacts.
  3. Partial patch application is allowed only if the patch tool can prove unchanged rejected paths were not written; otherwise fail atomically.
  4. Successful patch emits a diff artifact.

9.4 Completion Gate

A code-changing WorkerResult cannot be completed unless:

required verification passed, or
verification is explicitly skipped with reason/evidence/risk, and fallback_allowed is true

10. Project Scanner Semantics

Project initialization scanner collects full directory tree metadata with no directory exclusion and no depth limit.

Rationale: the user wants complete project shape visibility, and directory tree metadata alone is not comparable to reading file contents.

Safety boundaries:

  1. Do not recurse through symlinks by default; record symlink target metadata instead.
  2. Handle permission errors as entries with error metadata, not fatal scanner failure.
  3. Avoid reading file contents during tree scan.
  4. Record special file types without opening them.
  5. Provide progress and cancellation hooks for UI responsiveness.
  6. Detect obvious filesystem cycles or mount anomalies and record them as scanner warnings.

No directories such as .git, node_modules, build directories, or vendor directories are excluded from the directory tree.

11. Learning, Skills, and Experience Lifecycle

ExperienceMiner produces candidates; it does not silently rewrite durable project rules or skills unless assigned an explicit promotion task.

Lifecycle:

candidate.created
  → evidence threshold check
  → user/project policy approval
  → promotion write through owning store
  → memory.promoted or promotion failed
  → rollback/archive if stale or harmful

Candidate types:

project_rule
toolchain_rule
skill_update
debug_experience

Promotion requirements:

Candidate Promotion requirement
debug_experience verified failure + fix evidence
project_rule repeated pattern or explicit user confirmation
toolchain_rule verified toolchain evidence or explicit user confirmation
skill_update reviewed patch and trust policy approval

Skills use Claude Skills-style directory structure where applicable:

SKILL.md
scripts/
references/
assets/

Skill invocation must pass through capability/tool trust rules when it causes side effects.

12. C++ Diagnostic Ownership

toolchain-cpp owns deterministic extraction of compiler/test/static-analysis diagnostics and semantic signatures.

LLM-based interpretation belongs to runtime Debugger/Reviewer context, not hidden inside low-level toolchain code, unless a future injected diagnostic interpretation service is explicitly added.

This preserves package direction:

toolchain-cpp → contracts
runtime/debugger → llm/provider facade

13. V1.0.0 Alpha Cut Line

V1.0.0 Alpha includes:

complete C++ configure/build/test/static-analysis/debug/fix/review workflow
local and built-in capability manifests
GUI/network evidence tools
TUI/HUD projection
release gates for Linux tier 1

V1.0.0 Alpha may defer:

third-party plugin registry/signing
container sandboxing
multi-machine scheduling
Windows-native deep support
advanced semantic merge
production bitmap image generation providers

14. FK-Off Application Invariants

With foreign_keys = OFF, the runtime must enforce referential consistency in application code.

Invariants:

  1. tasks.session_id must reference an existing sessions.id.
  2. task_attempts.task_id must reference an existing tasks.id.
  3. agents.task_id must reference an existing tasks.id when not null.
  4. tool_runs.task_id, tool_runs.agent_id must reference existing rows when not null.
  5. command_runs.task_id, command_runs.agent_id, command_runs.tool_run_id must reference existing rows when not null.
  6. workspaces.task_id, workspaces.agent_id must reference existing rows when not null.
  7. diagnostics.command_run_id, diagnostics.artifact_id must reference existing rows when not null.
  8. evidence_refs foreign columns must reference existing rows when not null.

Enforcement:

  • Repository insert methods validate foreign key existence before insert.
  • On session startup recovery, run orphan scan: rows referencing deleted parents are logged and either re-parented or archived.
  • Release gate includes orphan scan validation.

15. Workspace GC Policy

<project>/.air/local/workspaces/ and the workspaces table grow over time.

Retention rules:

  1. Active workspaces (status = active) are preserved until merge or explicit cancel.
  2. Merged workspaces (status = merged) are preserved for 7 days after merged_at, then cleaned.
  3. Conflicted workspaces (status = conflicted) are preserved until user/system decision, then moved to abandoned.
  4. Abandoned workspaces are preserved for 3 days, then cleaned.
  5. cleaned status means filesystem artifacts removed; DB row retained with metadata.

Cleanup runs on session startup and periodically during idle.

16. Direct Mode Semantics

/direct enters a foreground execution lane where Main Agent acts with Executor-level permissions without dispatching to Scheduler.

Rules:

  1. Direct mode is a Main Agent state, not a separate agent type.
  2. Direct mode uses permission_template: "main_direct".
  3. Direct mode tasks write to the main workspace only; no worktree creation.
  4. Direct mode does not block Scheduler-owned background tasks from running concurrently.
  5. /done exits direct mode, triggers evidence collection, and returns to IDLE.
  6. Direct mode changes are recorded in the event log with source.kind = "main" and metadata indicating direct mode.

17. ExperienceMiner Trigger Ownership

ExperienceMiner is triggered by:

  1. debug.record.created event → Scheduler creates mine_experience task.
  2. Session end → Scheduler creates final mine_experience task.
  3. N-turn/tool-call interval (configurable, default 10) → Scheduler creates periodic mine_experience task.
  4. Stale rule/skill discovered during execution → worker emits memory.candidate.created with memory_type = skill_update, Scheduler creates mine_experience task.

The Scheduler owns trigger creation; ExperienceMiner never self-triggers. Curator periodic dedup runs as a scheduled mine_experience task with memory_type = "curator_dedup".

18. Doctor Self-Bootstrap

Doctor validates its own prerequisites before running capability checks:

  1. Verify Bun runtime version.
  2. Verify SQLite availability.
  3. Verify basic shell access.
  4. Verify .air/ directory writability.
  5. Then proceed to capability/dependency checks.

If self-bootstrap fails, Doctor reports a blocking issue and skips remaining checks.

19. air restore Semantics

air restore supports three granularities:

  1. air restore file <path> — restore most recent backup of a single file.
  2. air restore time <timestamp> — restore all files backed up since a given timestamp.
  3. air restore session <session-id> — restore all files backed up during a specific session.

All restore operations use the git-backed backup repository at <project>/.air/local/backups/. Restore creates a new commit with the restored content, preserving full history.