1use schemars::JsonSchema;
19use serde::{Deserialize, Serialize};
20use serde_json::Value as JsonValue;
21
22use super::{
23 BlockingState, CliSpec, Config, GitPublishMode, GitRevertMode, QueueFile, RunnerApprovalMode,
24 Task,
25};
26
27pub const MACHINE_SYSTEM_INFO_VERSION: u32 = 1;
28pub const MACHINE_QUEUE_READ_VERSION: u32 = 1;
29pub const MACHINE_QUEUE_VALIDATE_VERSION: u32 = 1;
30pub const MACHINE_QUEUE_REPAIR_VERSION: u32 = 1;
31pub const MACHINE_QUEUE_UNDO_VERSION: u32 = 1;
32pub const MACHINE_CONFIG_RESOLVE_VERSION: u32 = 3;
33pub const MACHINE_WORKSPACE_OVERVIEW_VERSION: u32 = 1;
34pub const MACHINE_TASK_CREATE_VERSION: u32 = 1;
35pub const MACHINE_TASK_MUTATION_VERSION: u32 = 2;
36pub const MACHINE_GRAPH_READ_VERSION: u32 = 1;
37pub const MACHINE_DASHBOARD_READ_VERSION: u32 = 1;
38pub const MACHINE_DECOMPOSE_VERSION: u32 = 2;
39pub const MACHINE_RUN_EVENT_VERSION: u32 = 3;
40pub const MACHINE_RUN_SUMMARY_VERSION: u32 = 2;
41pub const MACHINE_DOCTOR_REPORT_VERSION: u32 = 2;
42pub const MACHINE_PARALLEL_STATUS_VERSION: u32 = 3;
43pub const MACHINE_CLI_SPEC_VERSION: u32 = 2;
44pub const MACHINE_ERROR_VERSION: u32 = 1;
45pub const MACHINE_QUEUE_UNLOCK_INSPECT_VERSION: u32 = 1;
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
48#[serde(rename_all = "snake_case")]
49pub enum MachineErrorCode {
50 CliUnavailable,
51 PermissionDenied,
52 ConfigIncompatible,
53 ParseError,
54 NetworkError,
55 QueueCorrupted,
56 ResourceBusy,
57 VersionMismatch,
58 TaskMutationConflict,
59 Unknown,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
63#[serde(deny_unknown_fields)]
64pub struct MachineErrorDocument {
65 pub version: u32,
66 pub code: MachineErrorCode,
67 pub message: String,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub detail: Option<String>,
70 pub retryable: bool,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
74#[serde(deny_unknown_fields)]
75pub struct MachineSystemInfoDocument {
76 pub version: u32,
77 pub cli_version: String,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
81#[serde(deny_unknown_fields)]
82pub struct MachineQueuePaths {
83 pub repo_root: String,
84 pub queue_path: String,
85 pub done_path: String,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub project_config_path: Option<String>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub global_config_path: Option<String>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
93#[serde(deny_unknown_fields)]
94pub struct MachineQueueReadDocument {
95 pub version: u32,
96 pub paths: MachineQueuePaths,
97 pub active: QueueFile,
98 pub done: QueueFile,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub next_runnable_task_id: Option<String>,
101 #[schemars(schema_with = "json_value_schema")]
102 pub runnability: JsonValue,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
106#[serde(deny_unknown_fields)]
107pub struct MachineContinuationAction {
108 pub title: String,
109 pub command: String,
110 pub detail: String,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
114#[serde(deny_unknown_fields)]
115pub struct MachineContinuationSummary {
116 pub headline: String,
117 pub detail: String,
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub blocking: Option<BlockingState>,
120 #[serde(default)]
121 pub next_steps: Vec<MachineContinuationAction>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
125#[serde(deny_unknown_fields)]
126pub struct MachineValidationWarning {
127 pub task_id: String,
128 pub message: String,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
132#[serde(deny_unknown_fields)]
133pub struct MachineQueueValidateDocument {
134 pub version: u32,
135 pub valid: bool,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub blocking: Option<BlockingState>,
138 #[serde(default)]
139 pub warnings: Vec<MachineValidationWarning>,
140 pub continuation: MachineContinuationSummary,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
144#[serde(deny_unknown_fields)]
145pub struct MachineQueueRepairDocument {
146 pub version: u32,
147 pub dry_run: bool,
148 pub changed: bool,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub blocking: Option<BlockingState>,
151 #[schemars(schema_with = "json_value_schema")]
152 pub report: JsonValue,
153 pub continuation: MachineContinuationSummary,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
157#[serde(deny_unknown_fields)]
158pub struct MachineQueueUndoDocument {
159 pub version: u32,
160 pub dry_run: bool,
161 pub restored: bool,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub blocking: Option<BlockingState>,
164 #[schemars(schema_with = "option_json_value_schema")]
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub result: Option<JsonValue>,
167 pub continuation: MachineContinuationSummary,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
171#[serde(rename_all = "snake_case")]
172pub enum MachineQueueUnlockCondition {
173 Clear,
174 Live,
175 Stale,
176 OwnerMissing,
177 OwnerUnreadable,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
181#[serde(deny_unknown_fields)]
182pub struct MachineQueueUnlockInspectDocument {
183 pub version: u32,
184 pub condition: MachineQueueUnlockCondition,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub blocking: Option<BlockingState>,
187 pub unlock_allowed: bool,
188 pub continuation: MachineContinuationSummary,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
192#[serde(deny_unknown_fields)]
193pub struct MachineResumeDecision {
194 pub status: String,
195 pub scope: String,
196 pub reason: String,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub task_id: Option<String>,
199 pub message: String,
200 pub detail: String,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
204#[serde(deny_unknown_fields)]
205pub struct MachineConfigResolveDocument {
206 pub version: u32,
207 pub paths: MachineQueuePaths,
208 pub safety: MachineConfigSafetySummary,
209 pub config: Config,
210 #[serde(skip_serializing_if = "Option::is_none")]
211 pub resume_preview: Option<MachineResumeDecision>,
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
215#[serde(deny_unknown_fields)]
216pub struct MachineWorkspaceOverviewDocument {
217 pub version: u32,
218 pub queue: MachineQueueReadDocument,
219 pub config: MachineConfigResolveDocument,
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
223#[serde(deny_unknown_fields)]
224pub struct MachineConfigSafetySummary {
225 pub repo_trusted: bool,
226 pub dirty_repo: bool,
227 pub git_publish_mode: GitPublishMode,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub approval_mode: Option<RunnerApprovalMode>,
230 pub ci_gate_enabled: bool,
231 pub git_revert_mode: GitRevertMode,
232 pub parallel_configured: bool,
233 pub execution_interactivity: String,
234 pub interactive_approval_supported: bool,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
238#[serde(deny_unknown_fields)]
239pub struct MachineCliSpecDocument {
240 pub version: u32,
241 pub spec: CliSpec,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
245#[serde(deny_unknown_fields)]
246pub struct MachineTaskCreateRequest {
247 pub version: u32,
248 pub title: String,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub description: Option<String>,
251 pub priority: String,
252 #[serde(default)]
253 pub tags: Vec<String>,
254 #[serde(default, skip_serializing_if = "Vec::is_empty")]
255 pub scope: Vec<String>,
256 #[serde(skip_serializing_if = "Option::is_none")]
257 pub template: Option<String>,
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub target: Option<String>,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
263#[serde(deny_unknown_fields)]
264pub struct MachineTaskCreateDocument {
265 pub version: u32,
266 pub task: Task,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
270#[serde(deny_unknown_fields)]
271pub struct MachineTaskMutationDocument {
272 pub version: u32,
273 #[serde(skip_serializing_if = "Option::is_none")]
274 pub blocking: Option<BlockingState>,
275 #[schemars(schema_with = "json_value_schema")]
276 pub report: JsonValue,
277 pub continuation: MachineContinuationSummary,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
281#[serde(deny_unknown_fields)]
282pub struct MachineGraphReadDocument {
283 pub version: u32,
284 #[schemars(schema_with = "json_value_schema")]
285 pub graph: JsonValue,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
289#[serde(deny_unknown_fields)]
290pub struct MachineDashboardReadDocument {
291 pub version: u32,
292 #[schemars(schema_with = "json_value_schema")]
293 pub dashboard: JsonValue,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
297#[serde(deny_unknown_fields)]
298pub struct MachineDecomposeDocument {
299 pub version: u32,
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub blocking: Option<BlockingState>,
302 #[schemars(schema_with = "json_value_schema")]
303 pub result: JsonValue,
304 pub continuation: MachineContinuationSummary,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
308#[serde(deny_unknown_fields)]
309pub struct MachineDoctorReportDocument {
310 pub version: u32,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub blocking: Option<BlockingState>,
313 #[schemars(schema_with = "json_value_schema")]
314 pub report: JsonValue,
315}
316
317#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
319#[serde(deny_unknown_fields)]
320pub struct MachineParallelLifecycleCounts {
321 pub running: u32,
322 pub integrating: u32,
323 pub completed: u32,
324 pub failed: u32,
325 pub blocked: u32,
326 pub total: u32,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
330#[serde(deny_unknown_fields)]
331pub struct MachineParallelStatusDocument {
332 pub version: u32,
333 pub lifecycle_counts: MachineParallelLifecycleCounts,
334 #[serde(skip_serializing_if = "Option::is_none")]
335 pub blocking: Option<BlockingState>,
336 pub continuation: MachineContinuationSummary,
337 #[schemars(schema_with = "json_value_schema")]
338 pub status: JsonValue,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
342#[serde(rename_all = "snake_case")]
343pub enum MachineRunEventKind {
344 RunStarted,
345 QueueSnapshot,
346 ConfigResolved,
347 ResumeDecision,
348 TaskSelected,
349 PhaseEntered,
350 PhaseCompleted,
351 RunnerOutput,
352 BlockedStateChanged,
353 BlockedStateCleared,
354 Warning,
355 RunFinished,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
359#[serde(deny_unknown_fields)]
360pub struct MachineRunEventEnvelope {
361 pub version: u32,
362 pub kind: MachineRunEventKind,
363 pub timestamp: String,
364 #[serde(skip_serializing_if = "Option::is_none")]
365 pub run_mode: Option<String>,
366 #[serde(skip_serializing_if = "Option::is_none")]
367 pub task_id: Option<String>,
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub phase: Option<String>,
370 #[serde(skip_serializing_if = "Option::is_none")]
371 pub exit_code: Option<i32>,
372 #[serde(skip_serializing_if = "Option::is_none")]
373 pub message: Option<String>,
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub stream: Option<String>,
376 #[schemars(schema_with = "option_json_value_schema")]
377 #[serde(skip_serializing_if = "Option::is_none")]
378 pub payload: Option<JsonValue>,
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
382#[serde(deny_unknown_fields)]
383pub struct MachineRunSummaryDocument {
384 pub version: u32,
385 pub task_id: Option<String>,
386 pub exit_code: i32,
387 pub outcome: String,
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub blocking: Option<BlockingState>,
390}
391
392fn json_value_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
393 <JsonValue as JsonSchema>::json_schema(generator)
394}
395
396fn option_json_value_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
397 <Option<JsonValue> as JsonSchema>::json_schema(generator)
398}