1use serde::{Deserialize, Serialize};
8
9pub const CONTEXT_BUNDLE_SCHEMA_VERSION: u32 = 1;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum AgentRole {
15 Coder,
16 Reviewer,
17 Planner,
18 Researcher,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(rename_all = "snake_case")]
23pub enum RiskClass {
24 Low,
25 Medium,
26 High,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
33#[serde(rename_all = "snake_case")]
34pub enum DegradedMode {
35 Full,
36 CanonicalOnly,
37 Blocked,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "snake_case")]
45pub enum SourceKind {
46 Canonical,
47 Generated,
48 GraphDerived,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(rename_all = "snake_case")]
53pub enum ItemValidity {
54 Current,
55 Stale,
56 Superseded,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60#[serde(rename_all = "snake_case")]
61pub enum TrustClass {
62 Trusted,
63 Standard,
64 Quarantined,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
68#[serde(rename_all = "snake_case")]
69pub enum ContextIntent {
70 SessionStart,
71 ResumeWork,
75 ExplainDecision,
76 DebugFailure,
77 ApplyPreference,
78 ReviewChange,
79 ExploreHistory,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
85#[serde(rename_all = "snake_case")]
86pub enum ChannelKind {
87 Preferences,
88 Lessons,
89 Core,
90 Workstreams,
91 MemoryIndex,
92 Sessions,
93}
94
95impl ChannelKind {
96 pub const ORDERED: [ChannelKind; 6] = [
99 ChannelKind::Preferences,
100 ChannelKind::Lessons,
101 ChannelKind::Core,
102 ChannelKind::Workstreams,
103 ChannelKind::MemoryIndex,
104 ChannelKind::Sessions,
105 ];
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
109pub struct ProjectRef {
110 pub key: String,
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
114pub struct ContextRequest {
115 pub schema_version: u32,
116 pub task: String,
117 pub project: ProjectRef,
118 pub branch: Option<String>,
119 pub worktree: Option<String>,
120 pub role: AgentRole,
121 pub as_of_epoch: i64,
122 pub token_budget: u32,
123 pub risk: RiskClass,
124 pub include_superseded: bool,
125}
126
127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
130pub struct ContextItem {
131 pub stable_key: String,
132 pub channel: ChannelKind,
133 pub title: String,
134 pub text: String,
135 pub source_kind: SourceKind,
136 pub canonical_ref: Option<String>,
137 pub projection_ref: Option<String>,
138 pub evidence_refs: Vec<String>,
139 pub validity: ItemValidity,
140 pub trust: TrustClass,
141 pub project: Option<String>,
142 pub branch: Option<String>,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146pub struct PlannedChannel {
147 pub channel: ChannelKind,
148 pub item_limit: u32,
149 pub relevance_governed: bool,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub struct ContextFilters {
155 pub project: String,
156 pub branch: Option<String>,
157 pub include_superseded: bool,
158 pub as_of_epoch: i64,
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
163pub struct SectionBudgets {
164 pub total_tokens: u32,
165 pub preferences: u32,
166 pub lessons: u32,
167 pub core: u32,
168 pub workstreams: u32,
169 pub memory_index: u32,
170 pub sessions: u32,
171}
172
173impl SectionBudgets {
174 pub fn for_channel(&self, channel: ChannelKind) -> u32 {
175 match channel {
176 ChannelKind::Preferences => self.preferences,
177 ChannelKind::Lessons => self.lessons,
178 ChannelKind::Core => self.core,
179 ChannelKind::Workstreams => self.workstreams,
180 ChannelKind::MemoryIndex => self.memory_index,
181 ChannelKind::Sessions => self.sessions,
182 }
183 }
184}
185
186#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
187#[serde(deny_unknown_fields)]
188pub struct AuditEntry {
189 pub stable_key: String,
190 pub channel: ChannelKind,
191 pub source_kind: SourceKind,
192 pub validity: ItemValidity,
193 pub selected: bool,
194 pub reason: String,
197 pub relevance_score: Option<f64>,
198 pub token_estimate: u32,
199}
200
201#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202#[serde(deny_unknown_fields)]
203pub struct ContextAudit {
204 pub schema_version: u32,
205 pub policy_version: String,
206 pub relevance_policy_version: String,
207 pub plan_hash: String,
208 pub degraded_mode: DegradedMode,
209 pub candidates_considered: u32,
210 pub selected_count: u32,
211 pub dropped_count: u32,
212 pub token_estimate: u32,
213 pub token_budget: u32,
214 pub truncation_reason: Option<String>,
215 pub entries: Vec<AuditEntry>,
216 #[serde(default, skip_serializing_if = "Vec::is_empty")]
219 pub shadow_comparison: Vec<CurrentTruthShadowDiff>,
220}
221
222#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
225#[serde(deny_unknown_fields)]
226pub struct CurrentTruthShadowDiff {
227 pub stable_key: String,
228 pub verdict: String,
230 pub projection_ref: Option<String>,
231 pub claim_refs: Vec<String>,
232 pub reason: String,
233}
234
235#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
236pub struct ContextBundle {
237 pub schema_version: u32,
238 pub plan_hash: String,
239 pub degraded_mode: DegradedMode,
240 pub preferences: Vec<ContextItem>,
241 pub failure_lessons: Vec<ContextItem>,
242 pub current_truth: Vec<ContextItem>,
243 pub workstreams: Vec<ContextItem>,
244 pub memory_index: Vec<ContextItem>,
245 pub recent_sessions: Vec<ContextItem>,
246 pub audit: ContextAudit,
247}
248
249impl ContextBundle {
250 pub fn section_mut(&mut self, channel: ChannelKind) -> &mut Vec<ContextItem> {
251 match channel {
252 ChannelKind::Preferences => &mut self.preferences,
253 ChannelKind::Lessons => &mut self.failure_lessons,
254 ChannelKind::Core => &mut self.current_truth,
255 ChannelKind::Workstreams => &mut self.workstreams,
256 ChannelKind::MemoryIndex => &mut self.memory_index,
257 ChannelKind::Sessions => &mut self.recent_sessions,
258 }
259 }
260}