summaryrefslogtreecommitdiff
path: root/internal/event/event.go
blob: 78188bdf56eb816b930c970c1f0884dcfed1d21c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// 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"`
}