mdx_rust_core/
artifact.rs1use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use std::path::Path;
6
7#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
8pub struct ArtifactExplanation {
9 pub schema_version: String,
10 pub artifact_path: String,
11 pub artifact_kind: ArtifactKind,
12 pub summary: String,
13 pub mutates_source: bool,
14 pub recommended_next_actions: Vec<String>,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
18pub enum ArtifactKind {
19 AgentContract,
20 AuditPacket,
21 AutopilotRun,
22 CodebaseMap,
23 EvidenceRun,
24 EvolutionScorecard,
25 HardeningRun,
26 RefactorApplyRun,
27 RefactorBatchApplyRun,
28 RefactorPlan,
29 Unknown,
30}
31
32pub fn explain_artifact(path: &Path) -> anyhow::Result<ArtifactExplanation> {
33 let content = std::fs::read_to_string(path)?;
34 let value: serde_json::Value = serde_json::from_str(&content)?;
35 let kind = artifact_kind(&value);
36 let summary = artifact_summary(&kind, &value);
37 let recommended_next_actions = artifact_next_actions(&kind, &value);
38
39 Ok(ArtifactExplanation {
40 schema_version: "0.9".to_string(),
41 artifact_path: path.display().to_string(),
42 artifact_kind: kind,
43 summary,
44 mutates_source: false,
45 recommended_next_actions,
46 })
47}
48
49fn artifact_kind(value: &serde_json::Value) -> ArtifactKind {
50 if value.get("commands").is_some() && value.get("json_mode_contract").is_some() {
51 ArtifactKind::AgentContract
52 } else if value.get("autopilot").is_some() || value.get("edit_scope_contract").is_some() {
53 ArtifactKind::AuditPacket
54 } else if value.get("passes").is_some() && value.get("execution_summary").is_some() {
55 ArtifactKind::AutopilotRun
56 } else if value.get("scorecard_id").is_some() {
57 ArtifactKind::EvolutionScorecard
58 } else if value.get("map_id").is_some() {
59 ArtifactKind::CodebaseMap
60 } else if value.get("run_id").is_some() && value.get("unlocked_recipe_tiers").is_some() {
61 ArtifactKind::EvidenceRun
62 } else if value.get("outcome").is_some() && value.get("changes").is_some() {
63 ArtifactKind::HardeningRun
64 } else if value.get("plan_id").is_some() && value.get("candidate_id").is_some() {
65 ArtifactKind::RefactorApplyRun
66 } else if value.get("plan_id").is_some() && value.get("steps").is_some() {
67 ArtifactKind::RefactorBatchApplyRun
68 } else if value.get("plan_id").is_some() && value.get("candidates").is_some() {
69 ArtifactKind::RefactorPlan
70 } else {
71 ArtifactKind::Unknown
72 }
73}
74
75fn artifact_summary(kind: &ArtifactKind, value: &serde_json::Value) -> String {
76 match kind {
77 ArtifactKind::AutopilotRun => format!(
78 "autopilot {:?}: {} executed, {} planned",
79 value.get("status").and_then(|value| value.as_str()),
80 value
81 .get("total_executed_candidates")
82 .and_then(|value| value.as_u64())
83 .unwrap_or(0),
84 value
85 .get("total_planned_candidates")
86 .and_then(|value| value.as_u64())
87 .unwrap_or(0)
88 ),
89 ArtifactKind::CodebaseMap => format!(
90 "codebase map with quality {:?} and security score {}",
91 value
92 .pointer("/quality/grade")
93 .and_then(|value| value.as_str()),
94 value
95 .pointer("/security/score")
96 .and_then(|value| value.as_u64())
97 .unwrap_or(0)
98 ),
99 ArtifactKind::EvidenceRun => format!(
100 "evidence run graded {:?} with {} profiled file(s)",
101 value.get("grade").and_then(|value| value.as_str()),
102 value
103 .get("file_profiles")
104 .and_then(|value| value.as_array())
105 .map(|profiles| profiles.len())
106 .unwrap_or(0)
107 ),
108 ArtifactKind::EvolutionScorecard => format!(
109 "evolution scorecard {:?}: {} executable candidate(s)",
110 value
111 .pointer("/readiness/grade")
112 .and_then(|value| value.as_str()),
113 value
114 .pointer("/readiness/executable_candidates")
115 .and_then(|value| value.as_u64())
116 .unwrap_or(0)
117 ),
118 ArtifactKind::HardeningRun => format!(
119 "hardening run {:?} with {} proposed change(s)",
120 value
121 .pointer("/outcome/status")
122 .and_then(|value| value.as_str()),
123 value
124 .get("changes")
125 .and_then(|value| value.as_array())
126 .map(|changes| changes.len())
127 .unwrap_or(0)
128 ),
129 ArtifactKind::RefactorPlan => format!(
130 "refactor plan with {} candidate(s) and evidence {:?}",
131 value
132 .get("candidates")
133 .and_then(|value| value.as_array())
134 .map(|candidates| candidates.len())
135 .unwrap_or(0),
136 value
137 .pointer("/evidence/grade")
138 .and_then(|value| value.as_str())
139 ),
140 ArtifactKind::RefactorApplyRun | ArtifactKind::RefactorBatchApplyRun => format!(
141 "plan application {:?}",
142 value.get("status").and_then(|value| value.as_str())
143 ),
144 ArtifactKind::AgentContract => "agent command contract for mdx-rust".to_string(),
145 ArtifactKind::AuditPacket => "optimization audit packet".to_string(),
146 ArtifactKind::Unknown => "unrecognized mdx-rust JSON artifact".to_string(),
147 }
148}
149
150fn artifact_next_actions(kind: &ArtifactKind, value: &serde_json::Value) -> Vec<String> {
151 match kind {
152 ArtifactKind::EvidenceRun => vec![
153 "Run mdx-rust --json map <target> to see evidence-gated risk and recipe eligibility."
154 .to_string(),
155 "Run mdx-rust --json plan <target> before applying autonomous changes.".to_string(),
156 ],
157 ArtifactKind::CodebaseMap => vec![
158 "Inspect recommended_actions and capability_gates before mutation.".to_string(),
159 "Run mdx-rust --json plan <target> to create a stale-checked plan.".to_string(),
160 ],
161 ArtifactKind::EvolutionScorecard => vec![
162 "Inspect readiness and next_commands before choosing mutation.".to_string(),
163 "Only add --apply to suggested commands after explicit human approval.".to_string(),
164 ],
165 ArtifactKind::RefactorPlan => {
166 let has_executable = value
167 .get("candidates")
168 .and_then(|value| value.as_array())
169 .is_some_and(|candidates| {
170 candidates.iter().any(|candidate| {
171 candidate.get("status").and_then(|value| value.as_str())
172 == Some("ApplyViaImprove")
173 })
174 });
175 if has_executable {
176 vec![
177 "Run mdx-rust --json apply-plan <artifact> --all for review mode.".to_string(),
178 "Only add --apply when the user explicitly approves source mutation."
179 .to_string(),
180 ]
181 } else {
182 vec![
183 "No executable candidates are present; treat this as a design review plan."
184 .to_string(),
185 ]
186 }
187 }
188 ArtifactKind::AutopilotRun => vec![
189 "Read execution_summary before reporting progress to a human.".to_string(),
190 "Use artifact_path values to inspect nested plan and hardening reports.".to_string(),
191 ],
192 ArtifactKind::HardeningRun
193 | ArtifactKind::RefactorApplyRun
194 | ArtifactKind::RefactorBatchApplyRun => {
195 vec![
196 "Inspect validation and rollback records before claiming a change landed."
197 .to_string(),
198 ]
199 }
200 ArtifactKind::AgentContract | ArtifactKind::AuditPacket | ArtifactKind::Unknown => {
201 vec![
202 "Use mdx-rust --json agent-contract before selecting the next command.".to_string(),
203 ]
204 }
205 }
206}