# AirCoding Event Payload Registry V1 Date: 2026-05-27 Status: Canonical event and payload registry for V1.0.0 Alpha skeleton This document defines the V1 RuntimeEvent type registry, persistence policy, payload shapes, and producer/consumer rules. Event envelope is defined in `baselineV1.md` and `packages/contracts/event.ts`: ```ts interface RuntimeEvent { id: string type: string version: number timestamp: string session_id: string project_id?: string source: EventSource route: string[] payload: T } ``` Registry rule: ```text event_type → payload schema → persistence policy → domain transaction → producer → consumers ``` Event producers must not decide persistence ad hoc. `EventStore` owns persistence policy by event type. ## 1. Common Payload Conventions All payloads use snake_case JSON keys to match persisted DB column names and artifact metadata. Common aliases: ```ts type ISOTimeString = string type JsonObject = Record type EventPersistence = "durable" | "ephemeral" type EventDelivery = "eventbus" | "eventbus_and_db" ``` Common lightweight references: `EntityRef` is defined in `interface-contracts-v1.md` §4 with the canonical `EntityType` enum (12 values including `capability` and `provider`). This document uses `EntityRef` by reference. ```ts interface EvidenceLink { evidence_ref_id?: string artifact_id?: string diagnostic_id?: string claim: string } interface FailurePayload { error: AirError evidence_refs?: EvidenceLink[] metadata?: JsonObject } ``` Payloads should contain identifiers needed for routing/querying even when the same identifiers are also extracted into top-level event columns and domain tables. ## 2. Persistence and Transaction Rules Persistence categories: ```text durable → inserted into events table and applied to domain tables in one transaction ephemeral → published on EventBus only, not required for crash recovery ``` Rules: 1. Durable event insert and corresponding domain table update must happen in the same SQLite transaction. 2. Ephemeral events may be throttled or coalesced by EventBus/ProjectionStore. 3. Ephemeral events that become important for diagnosis should be captured as artifacts or durable summaries, not promoted ad hoc. 4. Streaming deltas are ephemeral; completed message/tool/command records are durable. 5. `route` is append-only. Forwarders append their route segment; they never rewrite earlier route entries. 6. `route_text` in SQLite is derived from `route.join("/")` for indexing. 7. Payload schema changes require incrementing the event `version` for that event type. ## 3. Durable Event Registry ### 3.1 Session Events #### `session.created` v1 Persistence: durable. Producer: Main Agent / CLI startup. Domain update: insert `sessions` row. ```ts interface SessionCreatedPayload { session_id: string project_id: string project_root: string title?: string model_provider_id?: string model_id?: string metadata?: JsonObject } ``` #### `session.archived` v1 Persistence: durable. Domain update: update `sessions.status = archived`. ```ts interface SessionArchivedPayload { session_id: string reason?: string } ``` #### `session.deleted` v1 Persistence: durable. Domain update: update `sessions.status = deleted`. ```ts interface SessionDeletedPayload { session_id: string reason?: string } ``` ### 3.2 Message Events #### `user.message.created` v1 Persistence: durable. Domain update: insert `messages` row. ```ts interface UserMessageCreatedPayload { message_id: string canonical_format: "anthropic" content_json: unknown parent_message_id?: string token_estimate?: number metadata?: JsonObject } ``` #### `assistant.message.started` v1 Persistence: durable. Domain update: insert/update `message_drafts` row with `status = streaming`. ```ts interface AssistantMessageStartedPayload { message_id: string canonical_format: "anthropic" parent_message_id?: string route?: string[] metadata?: JsonObject } ``` #### `assistant.message.created` v1 Persistence: durable. Domain update: insert `messages` row and delete matching `message_drafts` row. ```ts interface AssistantMessageCreatedPayload { message_id: string canonical_format: "anthropic" content_json: unknown parent_message_id?: string route?: string[] token_estimate?: number metadata?: JsonObject } ``` #### `assistant.message.failed` v1 Persistence: durable. Domain update: update `message_drafts.status = error` or create failure artifact if draft is absent. ```ts interface AssistantMessageFailedPayload extends FailurePayload { message_id: string partial_content_json?: unknown } ``` ### 3.3 Agent Events #### `agent.started` v1 Persistence: durable. Domain update: insert `agents` row with `status = running` or `starting`. ```ts interface AgentStartedPayload { agent_id: string agent_type: "executor" | "reviewer" | "debugger" | "compactor" | "experience_miner" task_id?: string pid?: number model_provider_id?: string model_id?: string workspace_id?: string metadata?: JsonObject } ``` #### `agent.completed` v1 Persistence: durable. Domain update: update `agents.status = completed`. ```ts interface AgentCompletedPayload { agent_id: string task_id?: string summary: string worker_result_ref?: string metadata?: JsonObject } ``` #### `agent.failed` v1 Persistence: durable. Domain update: update `agents.status = failed`. ```ts interface AgentFailedPayload extends FailurePayload { agent_id: string task_id?: string } ``` #### `agent.lost` v1 Persistence: durable. Domain update: update `agents.status = lost`. ```ts interface AgentLostPayload { agent_id: string task_id?: string last_heartbeat_at?: ISOTimeString detection_reason: "heartbeat_timeout" | "process_exit_without_result" | "ipc_broken" } ``` #### `agent.cancelled` v1 Persistence: durable. Domain update: update `agents.status = cancelled`. ```ts interface AgentCancelledPayload { agent_id: string task_id?: string reason: string } ``` ### 3.4 Task Events #### `task.created` v1 Persistence: durable. Domain update: insert `tasks` row and optional `task_dependencies` rows. ```ts interface TaskDependencySpec { depends_on_task_id: string dependency_type: "hard" | "soft" | "conflict" | "serialization" reason?: string source?: "architecture" | "scheduler" | "worker" | "user" | "system" } interface TaskCreatedPayload { task_id: string type: "execute" | "review" | "debug" | "compact" | "mine_experience" title: string task_spec_json: unknown dependencies?: TaskDependencySpec[] metadata?: JsonObject } ``` #### `task.started` v1 Persistence: durable. Domain update: update `tasks.status = running`, set `started_at`, `assigned_agent_id`, `workspace_id`; insert `task_attempts` row. ```ts interface TaskStartedPayload { task_id: string agent_id: string attempt_id: string attempt_index: number workspace_id?: string } ``` #### `task.completed` v1 Persistence: durable. Domain update: update `tasks.status = completed`, set `worker_result_json`, `completed_at`; update `task_attempts`. ```ts interface TaskCompletedPayload { task_id: string agent_id?: string attempt_id?: string worker_result_json: unknown summary: string changed_files?: string[] evidence_refs?: EvidenceLink[] } ``` #### `task.blocked` v1 Persistence: durable. Domain update: update `tasks.status = blocked`; update `task_attempts` if applicable. ```ts interface TaskBlockedPayload { task_id: string agent_id?: string reason: string blocker_kind: "user_decision" | "architecture_decision" | "environment" | "permission" | "dependency" | "external" evidence_refs?: EvidenceLink[] suggested_next_step?: string } ``` #### `task.failed` v1 Persistence: durable. Domain update: update `tasks.status = failed`; update `task_attempts.failure_*`. ```ts interface TaskFailedPayload extends FailurePayload { task_id: string agent_id?: string attempt_id?: string } ``` #### `task.cancelled` v1 Persistence: durable. Domain update: update `tasks.status = cancelled`. ```ts interface TaskCancelledPayload { task_id: string reason: string cancelled_by: "user" | "main" | "scheduler" | "system" } ``` #### `task.interrupted` v1 Persistence: durable. Domain update: update `tasks.status = interrupted`. ```ts interface TaskInterruptedPayload { task_id: string reason: string resumable: boolean resume_ref?: string } ``` ### 3.5 Tool Events #### `tool.started` v1 Persistence: durable. Domain update: insert `tool_runs` row with `status = running`. ```ts interface ToolStartedPayload { tool_run_id: string tool_name: string task_id?: string agent_id?: string origin_message_id?: string input_json: unknown metadata?: JsonObject } ``` #### `tool.completed` v1 Persistence: durable. Domain update: update `tool_runs.status = ok`, set output/artifacts/evidence/duration. ```ts interface ToolCompletedPayload { tool_run_id: string output_json?: unknown duration_ms?: number artifact_ids?: string[] evidence_refs?: EvidenceLink[] metadata?: JsonObject } ``` #### `tool.failed` v1 Persistence: durable. Domain update: update `tool_runs.status = error`. ```ts interface ToolFailedPayload extends FailurePayload { tool_run_id: string duration_ms?: number } ``` #### `tool.cancelled` v1 Persistence: durable. Domain update: update `tool_runs.status = cancelled`. ```ts interface ToolCancelledPayload { tool_run_id: string reason: string } ``` ### 3.6 Command Events #### `command.started` v1 Persistence: durable. Domain update: insert `command_runs` row. ```ts interface CommandStartedPayload { command_run_id: string task_id?: string agent_id?: string origin_message_id?: string tool_run_id?: string command: string cwd: string metadata?: JsonObject } ``` #### `command.completed` v1 Persistence: durable. Domain update: update `command_runs.exit_code`, artifact refs, diagnostics, duration. ```ts interface CommandCompletedPayload { command_run_id: string exit_code: number duration_ms?: number stdout_artifact_id?: string stderr_artifact_id?: string combined_artifact_id?: string diagnostic_ids?: string[] parsed_diagnostics_json?: unknown metadata?: JsonObject } ``` #### `command.failed` v1 Persistence: durable. Domain update: update `command_runs.exit_code` when available and create failure artifacts. ```ts interface CommandFailedPayload extends FailurePayload { command_run_id: string exit_code?: number duration_ms?: number stdout_artifact_id?: string stderr_artifact_id?: string combined_artifact_id?: string } ``` ### 3.7 Artifact, Diagnostic, and Evidence Events #### `artifact.created` v1 Persistence: durable. Domain update: insert `artifacts` row after file has been written temp → atomic rename. ```ts interface ArtifactCreatedPayload { artifact_id: string type: string uri: string path: string original_name?: string size_bytes?: number sha256?: string task_id?: string agent_id?: string tool_run_id?: string command_run_id?: string associated_entity_type?: string associated_entity_id?: string metadata?: JsonObject } ``` #### `diagnostic.created` v1 Persistence: durable. Domain update: insert `diagnostics` row. ```ts interface DiagnosticCreatedPayload { diagnostic_id: string task_id?: string agent_id?: string command_run_id?: string artifact_id?: string language?: string toolchain?: string severity?: string file?: string line?: number column?: number code?: string message: string semantic_signature: string metadata?: JsonObject } ``` #### `evidence.created` v1 Persistence: durable. Domain update: insert `evidence_refs` row. ```ts interface EvidenceCreatedPayload { evidence_ref_id: string kind: string ref: string location_json?: unknown claim: string task_id?: string agent_id?: string tool_run_id?: string command_run_id?: string artifact_id?: string diagnostic_id?: string message_id?: string } ``` ### 3.8 Context and Summary Events #### `context.compaction.requested` v1 Persistence: durable. Domain update: insert compaction task if Scheduler accepts it. ```ts interface ContextCompactionRequestedPayload { reason: "token_budget" | "user_request" | "session_checkpoint" | "manual" range_start_message_id?: string range_end_message_id?: string target_budget_tokens?: number } ``` #### `context.compaction.started` v1 Persistence: durable. Domain update: mark compaction task running. ```ts interface ContextCompactionStartedPayload { task_id: string agent_id: string range_start_message_id?: string range_end_message_id?: string } ``` #### `context.compaction.completed` v1 Persistence: durable. Domain update: mark compaction task complete. The `summaries` row is inserted by `summary.created`, not by this event. ```ts 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 } ``` #### `context.compaction.failed` v1 Persistence: durable. Domain update: mark compaction task failed or blocked. ```ts interface ContextCompactionFailedPayload extends FailurePayload { task_id?: string agent_id?: string range_start_message_id?: string range_end_message_id?: string } ``` #### `summary.created` v1 Persistence: durable. Domain update: insert `summaries` row. ```ts interface SummaryCreatedPayload { summary_id: string type: string range_start_message_id?: string range_end_message_id?: string content_json: unknown metadata?: JsonObject } ``` ### 3.9 Permission Events #### `permission.decision.recorded` v1 Persistence: durable. Domain update: append permission decision record to events; optional future projection table. ```ts interface PermissionDecisionRecordedPayload { decision_id: string subject: "tool" | "command" | "path" | "network" | "dependency" | "migration" action: "allow" | "deny" | "ask_user" | "block" | "refuse" | "announce_then_run" grant_scope: "none" | "once" | "session" | "project" | "global" reason?: string risk_level: "low" | "medium" | "high" | "critical" decided_by: "user" | "policy" | "high_permission_mode" | "system" scope_json?: unknown expires_at?: ISOTimeString } ``` #### `permission.prompt.requested` v1 Persistence: durable. Domain update: append event; optional UI projection. ```ts interface PermissionPromptRequestedPayload { prompt_id: string subject: string risk_level: "low" | "medium" | "high" | "critical" reason: string options: string[] default_option?: string request_ref?: EntityRef } ``` #### `permission.prompt.resolved` v1 Persistence: durable. Domain update: append event and create `permission.decision.recorded` if applicable. ```ts interface PermissionPromptResolvedPayload { prompt_id: string selected_option: string decision_id?: string resolved_by: "user" | "timeout" | "system" } ``` ### 3.10 Doctor and Dependency Events #### `doctor.run.started` v1 Persistence: durable. Domain update: append event and create optional report artifact later. ```ts interface DoctorRunStartedPayload { run_id: string mode: "read_only" | "fix" trigger: "startup" | "manual" | "dependency_request" | "release_gate" } ``` #### `doctor.issue.found` v1 Persistence: durable. Domain update: append event and optional diagnostic/report artifact. ```ts interface DoctorIssueFoundPayload { run_id: string issue_id: string severity: "info" | "warning" | "error" | "blocking" capability?: string dependency?: string message: string fix_available: boolean fix_requires_confirmation?: boolean } ``` #### `doctor.fix.started` v1 Persistence: durable. Domain update: append event and optional command/tool run rows. ```ts interface DoctorFixStartedPayload { run_id: string issue_id: string fix_id: string strategy: string } ``` #### `doctor.fix.completed` v1 Persistence: durable. Domain update: append event and update report artifact. ```ts interface DoctorFixCompletedPayload { run_id: string issue_id: string fix_id: string evidence_refs?: EvidenceLink[] } ``` #### `doctor.fix.failed` v1 Persistence: durable. Domain update: append event and update report artifact. ```ts interface DoctorFixFailedPayload extends FailurePayload { run_id: string issue_id: string fix_id: string } ``` #### `doctor.run.completed` v1 Persistence: durable. Domain update: append event and create doctor report artifact. ```ts interface DoctorRunCompletedPayload { run_id: string status: "passed" | "issues_found" | "fixed" | "failed" issue_count: number blocking_issue_count: number report_artifact_id?: string } ``` ### 3.11 Requirement and Architecture Events #### `requirement.changed` v1 Persistence: durable. Domain update: append event and mark impacted tasks when Scheduler applies it. ```ts interface RequirementChangedPayload { change_id: string origin_message_id: string summary: string change_type: "clarification" | "scope_change" | "architecture_change" | "constraint_change" | "cancellation" affected_refs?: EntityRef[] } ``` #### `architecture.plan.updated` v1 Persistence: durable. Domain update: append event and artifact/plan references. ```ts interface ArchitecturePlanUpdatedPayload { plan_ref: string update_kind: "created" | "revised" | "superseded" summary: string affected_task_ids?: string[] adr_refs?: string[] c4_refs?: string[] } ``` #### `architecture.impact.completed` v1 Persistence: durable. Domain update: append event; Scheduler consumes to continue/replan/block. ```ts interface ArchitectureImpactCompletedPayload { assessment_id: string requirement_change_id?: string impact_level: "implementation" | "interface" | "architecture" | "product" decision: "silent_continue" | "requires_user_confirmation" | "requires_replan" | "reject_or_escalate" summary: string affected_task_ids?: string[] evidence_refs?: EvidenceLink[] } ``` ### 3.12 Workspace and Merge Events #### `workspace.created` v1 Persistence: durable. Domain update: insert `workspaces` row. ```ts interface WorkspaceCreatedPayload { workspace_id: string task_id?: string agent_id?: string path: string strategy: "main" | "worktree" | "isolated_copy" base_ref?: string branch_name?: string } ``` #### `workspace.merge.started` v1 Persistence: durable. Domain update: append event and mark workspace merge in progress in metadata. ```ts interface WorkspaceMergeStartedPayload { workspace_id: string task_id?: string strategy: "fast_forward" | "patch_apply" | "manual_merge" | "copy_back" target_ref?: string } ``` #### `workspace.merge.completed` v1 Persistence: durable. Domain update: update `workspaces.status = merged`, set `merged_at`. ```ts interface WorkspaceMergeCompletedPayload { workspace_id: string task_id?: string merged_ref?: string diff_artifact_id?: string } ``` #### `workspace.merge.conflicted` v1 Persistence: durable. Domain update: update `workspaces.status = conflicted`. ```ts interface WorkspaceMergeConflictedPayload { workspace_id: string task_id?: string conflict_files: string[] conflict_artifact_id?: string suggested_resolution?: "retry_serial" | "debugger" | "architecture_review" | "user_decision" } ``` #### `workspace.cleaned` v1 Persistence: durable. Domain update: update `workspaces.status = cleaned` or append cleanup metadata. ```ts interface WorkspaceCleanedPayload { workspace_id: string reason: "merged" | "cancelled" | "abandoned" | "manual" } ``` ### 3.13 Memory and Debug Knowledge Events #### `memory.candidate.created` v1 Persistence: durable. Domain update: append event; future memory table may project it. ```ts interface MemoryCandidateCreatedPayload { candidate_id: string source_ref: EntityRef memory_type: "project_rule" | "toolchain_rule" | "skill_update" | "debug_experience" summary: string evidence_refs?: EvidenceLink[] } ``` #### `memory.promoted` v1 Persistence: durable. Domain update: append event and update `.air/shared/rules`, skill, or `learned-memory.db` through the owning subsystem. ```ts interface MemoryPromotedPayload { candidate_id: string target_ref: string promoted_by: "user" | "curator" | "system" summary: string } ``` #### `memory.archived` v1 Persistence: durable. Domain update: append event and mark memory inactive in owning subsystem. ```ts interface MemoryArchivedPayload { candidate_id?: string memory_ref?: string reason: string } ``` #### `debug.record.created` v1 Persistence: durable. Domain update: insert/update `debug-records.db` and append event in session DB. ```ts interface DebugRecordCreatedPayload { debug_record_id: string task_id?: string failure_signature: string summary: string evidence_refs?: EvidenceLink[] verification_refs?: EvidenceLink[] } ``` ## 4. Ephemeral Event Registry Ephemeral events are for live rendering, streaming, and heartbeat. They are not required for crash recovery. ### `agent.heartbeat` v1 Producer: child agent process. Consumer: Scheduler, ProjectionStore. ```ts interface AgentHeartbeatPayload { agent_id: string task_id?: string status: "starting" | "running" progress_text?: string current_step?: string resource_snapshot?: { pid?: number cpu_percent?: number memory_bytes?: number } } ``` ### `task.progress` v1 Producer: Scheduler or worker agent. Consumer: ProjectionStore, Main Agent summaries. ```ts interface TaskProgressPayload { task_id: string agent_id?: string phase?: string progress_text: string percent?: number } ``` ### `assistant.message.delta` v1 Producer: Main Agent / provider adapter. Consumer: ProjectionStore; `message_drafts` may be periodically flushed by draft writer. ```ts interface AssistantMessageDeltaPayload { message_id: string delta: unknown sequence: number } ``` ### `tool.progress` v1 Producer: streaming tool. Consumer: ProjectionStore, command/tool monitor. ```ts interface ToolProgressPayload { tool_run_id: string message: string progress_json?: unknown } ``` ### `command.stdout.delta` v1 and `command.stderr.delta` v1 Producer: shell/command runner. Consumer: ProjectionStore, artifact writer. ```ts interface CommandStreamDeltaPayload { command_run_id: string chunk: string sequence: number truncated?: boolean } ``` ### `hud.frame.rendered` v1 Producer: TUI/HUD. Consumer: local performance/debug logging only. ```ts interface HudFrameRenderedPayload { frame_id: string duration_ms?: number dropped_frame_count?: number } ``` ## 5. Producer and Consumer Boundaries Producer responsibilities: 1. Emit valid RuntimeEvent envelopes. 2. Provide IDs generated by the subsystem that owns the entity. 3. Append route segment when forwarding. 4. Never update SQLite domain tables directly unless the producer is the owning store/service. EventStore responsibilities: 1. Validate event version and payload schema. 2. Apply durable events and domain table updates transactionally. 3. Derive query columns such as `task_id`, `agent_id`, `tool_run_id`, `command_run_id`, `route_text`. 4. Publish committed durable events to EventBus after commit. 5. Reject unknown durable event types unless explicitly allowed by development-mode config. ProjectionStore responsibilities: 1. Hydrate from DB at startup/resume. 2. Apply live durable and ephemeral events. 3. Coalesce noisy deltas for TUI/HUD. 4. Never become the source of truth for scheduling or recovery. Scheduler responsibilities: 1. Consume task/agent/workspace/architecture events. 2. Drive retries, dependency unblocking, merge waves, and escalation. 3. Treat missing heartbeat as `agent.lost` and persist that durable event. ## 6. V1 Event Type Index Durable: ```text session.created session.archived session.deleted user.message.created assistant.message.started assistant.message.created assistant.message.failed agent.started agent.completed agent.failed agent.lost agent.cancelled task.created task.started task.completed task.blocked task.failed task.cancelled task.interrupted tool.started tool.completed tool.failed tool.cancelled command.started command.completed command.failed artifact.created diagnostic.created evidence.created context.compaction.requested context.compaction.started context.compaction.completed context.compaction.failed summary.created permission.decision.recorded permission.prompt.requested permission.prompt.resolved doctor.run.started doctor.issue.found doctor.fix.started doctor.fix.completed doctor.fix.failed doctor.run.completed requirement.changed architecture.plan.updated architecture.impact.completed workspace.created workspace.merge.started workspace.merge.completed workspace.merge.conflicted workspace.cleaned memory.candidate.created memory.promoted memory.archived debug.record.created ``` Ephemeral: ```text agent.heartbeat task.progress assistant.message.delta tool.progress command.stdout.delta command.stderr.delta hud.frame.rendered ``` ## 7. V1.0.0 Alpha Cut Line The V1.0.0 Alpha skeleton must implement schema validation and EventStore handling for all durable event names in the V1 index, even if some subsystem producers are initially stubs. The first implementation may keep payload schemas in TypeScript with runtime validation generated from the same definitions. Unknown plugin/capability events must use a namespaced prefix and must not be durable until registered.