1use serde_json::{Value, json};
4use sim_kernel::Symbol;
5
6use super::{
7 AgentMission, AtelierAction, GuardCapability, GuardDecision, guard_action,
8 self_hosting::cassette_content_hash,
9};
10
11pub const CONTRACT_NATIVE_SCHEMA: &str = "sim.atelier.contract-native.v1";
13
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
16pub enum AtelierBackend {
17 #[default]
19 SourceRadar,
20 ContractNative,
22}
23
24impl AtelierBackend {
25 pub fn parse(value: &str) -> Option<Self> {
27 match value {
28 "source-radar" => Some(Self::SourceRadar),
29 "contract-native" => Some(Self::ContractNative),
30 _ => None,
31 }
32 }
33
34 pub fn as_str(self) -> &'static str {
36 match self {
37 Self::SourceRadar => "source-radar",
38 Self::ContractNative => "contract-native",
39 }
40 }
41}
42
43#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct ContractNativeDeckSummary {
46 pub cards: u64,
48 pub complete_cards: u64,
50 pub partial_cards: u64,
52 pub diagnostics: Vec<String>,
54}
55
56#[derive(Clone, Debug, PartialEq, Eq)]
58pub struct ContractNativeProjectionSummary {
59 pub token_budget: u64,
61 pub tokens: u64,
63 pub included: u64,
65 pub summary_only: u64,
67 pub dropped: u64,
69 pub diagnostics: Vec<String>,
71}
72
73#[derive(Clone, Debug, PartialEq, Eq)]
75pub struct ContractNativeGrammarSummary {
76 pub dialect: String,
78 pub target_codec: String,
80 pub return_shape: String,
82 pub strict: bool,
84}
85
86#[derive(Clone, Debug, PartialEq, Eq)]
88pub struct ContractNativeRouteAttempt {
89 pub target: String,
91 pub status: String,
93 pub reason: Option<String>,
95}
96
97#[derive(Clone, Debug, PartialEq, Eq)]
99pub struct ContractNativeGuardDenial {
100 pub id: String,
102 pub action: String,
104 pub reason: String,
106 pub required_capability: String,
108}
109
110#[derive(Clone, Debug, PartialEq, Eq)]
112pub struct ContractNativeAtelierReport {
113 pub backend: AtelierBackend,
115 pub deck: ContractNativeDeckSummary,
117 pub projection: ContractNativeProjectionSummary,
119 pub grammar: ContractNativeGrammarSummary,
121 pub route_attempts: Vec<ContractNativeRouteAttempt>,
123 pub diagnostics: Vec<String>,
125 pub cassette_hash: String,
127 pub guard_denials: Vec<ContractNativeGuardDenial>,
129}
130
131impl ContractNativeAtelierReport {
132 pub fn to_json(&self) -> Value {
134 json!({
135 "schema": CONTRACT_NATIVE_SCHEMA,
136 "backend": self.backend.as_str(),
137 "contract_deck": {
138 "cards": self.deck.cards,
139 "complete_cards": self.deck.complete_cards,
140 "partial_cards": self.deck.partial_cards,
141 "diagnostics": self.deck.diagnostics,
142 },
143 "projection": {
144 "token_budget": self.projection.token_budget,
145 "tokens": self.projection.tokens,
146 "included": self.projection.included,
147 "summary_only": self.projection.summary_only,
148 "dropped": self.projection.dropped,
149 "diagnostics": self.projection.diagnostics,
150 },
151 "grammar": {
152 "dialect": self.grammar.dialect,
153 "target_codec": self.grammar.target_codec,
154 "return_shape": self.grammar.return_shape,
155 "strict": self.grammar.strict,
156 },
157 "route_attempts": self.route_attempts.iter().map(|attempt| {
158 json!({
159 "target": attempt.target,
160 "status": attempt.status,
161 "reason": attempt.reason,
162 })
163 }).collect::<Vec<_>>(),
164 "diagnostics": self.diagnostics,
165 "cassette_hash": self.cassette_hash,
166 "guard_denials": self.guard_denials.iter().map(|denial| {
167 json!({
168 "id": denial.id,
169 "action": denial.action,
170 "reason": denial.reason,
171 "required_capability": denial.required_capability,
172 })
173 }).collect::<Vec<_>>(),
174 })
175 }
176}
177
178pub fn deterministic_contract_native_report() -> ContractNativeAtelierReport {
180 let mission = AgentMission::new(
181 Symbol::qualified("agent/mission", "contract-native-atelier"),
182 "sim-agent-net",
183 )
184 .with_capability(GuardCapability::EditRepo("sim-agent-net".to_owned()));
185 let events = [
186 "contract-native:deck:task-scoped",
187 "contract-native:projection:tokens=114",
188 "contract-native:grammar:shapegrammar",
189 "contract-native:route:cheap-downshift:failed",
190 "contract-native:route:escalation-downshift:accepted",
191 "contract-native:guard:denials-retained",
192 ];
193 ContractNativeAtelierReport {
194 backend: AtelierBackend::ContractNative,
195 deck: ContractNativeDeckSummary {
196 cards: 4,
197 complete_cards: 3,
198 partial_cards: 1,
199 diagnostics: vec![
200 "missing authored example preserved with Shape-synthesized fallback".to_owned(),
201 ],
202 },
203 projection: ContractNativeProjectionSummary {
204 token_budget: 160,
205 tokens: 114,
206 included: 3,
207 summary_only: 1,
208 dropped: 1,
209 diagnostics: vec![
210 "contract projection reduced table/entries to summary only under token budget"
211 .to_owned(),
212 "contract projection dropped unrelated export under token budget".to_owned(),
213 ],
214 },
215 grammar: ContractNativeGrammarSummary {
216 dialect: "shapegrammar".to_owned(),
217 target_codec: "codec:lisp".to_owned(),
218 return_shape: "(list-rest () Any)".to_owned(),
219 strict: true,
220 },
221 route_attempts: vec![
222 ContractNativeRouteAttempt {
223 target: "cheap-downshift".to_owned(),
224 status: "failed".to_owned(),
225 reason: Some("terminal output failed codec or Shape check".to_owned()),
226 },
227 ContractNativeRouteAttempt {
228 target: "escalation-downshift".to_owned(),
229 status: "accepted".to_owned(),
230 reason: None,
231 },
232 ],
233 diagnostics: vec![
234 "contract-native cache evidence is deterministic and source-free".to_owned(),
235 "sim-web-shell serves this cache without issuing model requests".to_owned(),
236 ],
237 cassette_hash: cassette_content_hash(&events),
238 guard_denials: contract_native_guard_denials(&mission),
239 }
240}
241
242pub fn contract_native_guard_denials(mission: &AgentMission) -> Vec<ContractNativeGuardDenial> {
244 [
245 (
246 "meta-workspace-edit",
247 AtelierAction::edit_file(
248 mission.leased_repo(),
249 ".meta-workspace/packages/sim-lib-agent/src/atelier.rs",
250 ),
251 "EditRepo(sim-agent-net)",
252 ),
253 (
254 "cross-repo-write",
255 AtelierAction::edit_file("sim-web", "crates/sim-web-shell/src/atelier.rs"),
256 "EditRepo(sim-web)",
257 ),
258 (
259 "github-outward-action",
260 AtelierAction::AddGithubRemote {
261 remote: "https://github.com/sim-nest/sim-private.git".to_owned(),
262 },
263 "PlanPin",
264 ),
265 ]
266 .into_iter()
267 .filter_map(|(id, action, required_capability)| {
268 let decision = guard_action(mission, action);
269 let GuardDecision::Refused(refusal) = decision else {
270 return None;
271 };
272 Some(ContractNativeGuardDenial {
273 id: id.to_owned(),
274 action: refusal.action().label(),
275 reason: refusal.reason().to_owned(),
276 required_capability: required_capability.to_owned(),
277 })
278 })
279 .collect()
280}