// Package event defines the Event type and canonical kinds/actors for the // task observability stream. Events are immutable, per-task, monotonically // sequenced records persisted by internal/storage. package event import ( "encoding/json" "time" ) // Kind enumerates the canonical event types. type Kind string const ( KindStateChange Kind = "state_change" KindAgentMessage Kind = "agent_message" KindClarificationRequest Kind = "clarification_request" KindClarificationAnswer Kind = "clarification_answer" KindHumanMessage Kind = "human_message" KindSummary Kind = "summary" KindCommitMade Kind = "commit_made" KindSubtaskSpawned Kind = "subtask_spawned" KindCostReport Kind = "cost_report" KindExecutionStarted Kind = "execution_started" KindExecutionEnded Kind = "execution_ended" // KindEscalated records a scheduler decision on a role-typed task's // escalation ladder (internal/scheduler): either bumping it to the next // tier, or declining to (budget denied or ladder exhausted, payload // includes "final": true — the task stays FAILED for human attention). // Payload shape: from_rung, to_rung, from_provider, to_provider, final. KindEscalated Kind = "escalated" // The following Kinds are part of the Phase 7 planning-layer/story-model // ceremony (epics/stories, internal/story). This phase (7a) only adds the // constants — nothing emits them yet; a later phase's orchestration logic // writes them against a story or epic ID (events.task_id has no enforced // FK, so it tolerates carrying a non-task ID). KindEpicProposed Kind = "epic_proposed" KindDiscoveryProposed Kind = "discovery_proposed" KindFramingDecided Kind = "framing_decided" KindGroomed Kind = "groomed" KindPrioritized Kind = "prioritized" KindEvalVerdict Kind = "eval_verdict" KindArbitrationDecided Kind = "arbitration_decided" KindRetroCaptured Kind = "retro_captured" KindHumanAccepted Kind = "human_accepted" // KindRoleConfigProposed records a single AgentChannel.ProposeRoleConfig // call (Phase 8): a retro-role agent proposing a new draft role_configs // version for a role. Attached to the *calling task's* own ID (the retro // task), payload {role, version} — internal/scheduler.StoryOrchestrator // reads a retro task's own event stream for these once the task // completes, to assemble the aggregate KindRetroCaptured payload it // attaches to the story's ID. Mirrors KindEpicProposed's "one event per // proposal call" convention. KindRoleConfigProposed Kind = "role_config_proposed" // KindVerdictReported records a single AgentChannel.ReportVerdict call: // an evaluating agent (e.g. an arbitration-role task) reporting a // structured approve/reject decision instead of leaving it to be // inferred from free-text summary parsing. Attached to the *calling // task's own* ID, payload {approved, reasoning} — // internal/scheduler.StoryOrchestrator's finalizeArbitration reads the // arbitration task's own event stream for this once the task completes, // to decide REVIEW_READY vs NEEDS_FIX. Mirrors KindRoleConfigProposed's // "one event per call, attached to the calling task" convention. KindVerdictReported Kind = "verdict_reported" ) // Actor identifies the originator of an event. type Actor string const ( ActorAgent Actor = "agent" ActorUser Actor = "user" ActorChatbot Actor = "chatbot" ActorSystem Actor = "system" ActorWebhook Actor = "webhook" ) // Event is a single timestamped record on a task's audit/observability stream. // Once written, events are append-only — the seq column is per-task monotonic // and assigned by the storage layer on insert. type Event struct { ID string `json:"id"` TaskID string `json:"task_id"` Seq int64 `json:"seq"` Timestamp time.Time `json:"timestamp"` Kind Kind `json:"kind"` Actor Actor `json:"actor"` Payload json.RawMessage `json:"payload"` }