speccoder_runtime/substrate/answers.rs
1//! What the model answers, one type per answer. Each schema it is held to
2//! is derived from its type (`agentplane::tools::strict_schema`), so a field
3//! and its description live in one place; the provider enforces the schema
4//! during generation and agentplane validates the answer against it.
5//!
6//! An enum's variants are explained in the enum's doc, not their own:
7//! documented variants become a `oneOf` that strict decoding refuses.
8
9use schemars::JsonSchema;
10use serde::Deserialize;
11use serde_json::Value;
12
13/// The strict schema of the answer `T`.
14#[must_use]
15pub fn schema<T: JsonSchema>() -> Value {
16 agentplane::tools::strict_schema::<T>()
17}
18
19/// Whether an attempt or a hosted skill finished its work.
20///
21/// - `done`: The work is finished.
22/// - `incomplete`: Part of it is left undone.
23#[allow(missing_docs)]
24#[derive(Debug, Deserialize, JsonSchema)]
25#[serde(rename_all = "snake_case")]
26pub enum Status {
27 Done,
28 Incomplete,
29}
30
31/// An attempt's closing report: the model's claim, never its verdict.
32#[derive(Debug, Deserialize, JsonSchema)]
33pub struct AttemptReport {
34 /// What was done, in a sentence or two.
35 pub summary: String,
36 /// done: this task's work is finished — even while tests that later
37 /// tasks make pass still fail. incomplete: part of this task is left
38 /// undone.
39 pub status: Status,
40 /// The files changed.
41 pub files_changed: Vec<String>,
42 /// The checks run, as commands.
43 pub checks_run: Vec<String>,
44 /// Anything the reviewer should know.
45 pub notes: Vec<String>,
46}
47
48/// How a task would be done; recorded, never executed.
49#[derive(Debug, Deserialize, JsonSchema)]
50pub struct Proposal {
51 /// The approach in a sentence or two.
52 pub summary: String,
53 /// The steps, in order.
54 pub steps: Vec<String>,
55 /// The files expected to change.
56 pub files: Vec<String>,
57 /// The checks that would show it works.
58 pub checks: Vec<String>,
59 /// What is unclear or risky.
60 pub risks: Vec<String>,
61}
62
63/// What kind of gap between the spec and the code.
64///
65/// - `missing`: Not built.
66/// - `partial`: Built in part.
67/// - `contradicts`: Built otherwise than the spec says.
68/// - `unrequested`: Built, not asked for.
69#[allow(missing_docs)]
70#[derive(Debug, Deserialize, JsonSchema)]
71#[serde(rename_all = "snake_case")]
72pub enum GapType {
73 Missing,
74 Partial,
75 Contradicts,
76 Unrequested,
77}
78
79/// How much a gap matters.
80///
81/// - `CRITICAL`: Breaks what the spec promises.
82/// - `HIGH`: A requirement unmet.
83/// - `MEDIUM`: A part of one unmet.
84/// - `LOW`: Cosmetic.
85#[allow(missing_docs)]
86#[derive(Debug, Deserialize, JsonSchema)]
87#[serde(rename_all = "UPPERCASE")]
88pub enum Severity {
89 Critical,
90 High,
91 Medium,
92 Low,
93}
94
95/// One gap convergence found.
96#[derive(Debug, Deserialize, JsonSchema)]
97pub struct Finding {
98 /// The requirement or scenario it is about (`FR-003`, `US1`).
99 pub source_ref: String,
100 /// What kind of gap.
101 pub gap_type: GapType,
102 /// How much it matters.
103 pub severity: Severity,
104 /// The gap, in a sentence.
105 pub description: String,
106 /// Where in the code it shows.
107 pub evidence: String,
108 /// The task that would close it, as a task line.
109 pub task: String,
110}
111
112/// Convergence's assessment of the code against the spec.
113#[derive(Debug, Deserialize, JsonSchema)]
114pub struct Assessment {
115 /// The gaps found.
116 pub findings: Vec<Finding>,
117 /// What was checked.
118 pub checked: Vec<String>,
119}
120
121/// A kind of shortcut the monitor looks for.
122///
123/// - `hardcoded_output`: Returns the expected output instead of computing it.
124/// - `special_cased_input`: Special-cases the tests' inputs.
125/// - `weakened_test`: Weakens or skips a test.
126/// - `test_environment_detection`: Behaves differently under test.
127/// - `swallowed_error`: Swallows an error the spec wants seen.
128/// - `contradicts_spec`: Does what the spec rules out.
129/// - `other`: Anything else.
130#[allow(missing_docs)]
131#[derive(Debug, Deserialize, JsonSchema)]
132#[serde(rename_all = "snake_case")]
133pub enum Shortcut {
134 HardcodedOutput,
135 SpecialCasedInput,
136 WeakenedTest,
137 TestEnvironmentDetection,
138 SwallowedError,
139 ContradictsSpec,
140 Other,
141}
142
143/// One suspicion of the monitor.
144#[derive(Debug, Deserialize, JsonSchema)]
145pub struct Suspicion {
146 /// What kind of shortcut.
147 pub kind: Shortcut,
148 /// The lines that show it.
149 pub evidence: String,
150 /// Why it is one.
151 pub explanation: String,
152}
153
154/// The monitor's review of an attempt's diff.
155#[derive(Debug, Deserialize, JsonSchema)]
156pub struct Monitoring {
157 /// Whether the attempt took a shortcut.
158 pub suspected: bool,
159 /// What shows it.
160 pub findings: Vec<Suspicion>,
161}
162
163/// Where the person's words came from, and the question that settles it.
164#[derive(Debug, Deserialize, JsonSchema)]
165pub struct Assumption {
166 /// The words assumed.
167 pub words: String,
168 /// What they were assumed from.
169 pub from: String,
170 /// The question that would settle them.
171 pub question: String,
172}
173
174/// The person's words shaped into the spec's language.
175#[derive(Debug, Deserialize, JsonSchema)]
176pub struct Shaped {
177 /// The text proposed for the field.
178 pub proposal: String,
179 /// What was assumed.
180 pub assumptions: Vec<Assumption>,
181 /// What to ask the person.
182 pub questions: Vec<String>,
183 /// The language written in.
184 pub language: String,
185 /// The proposal back in the person's own words.
186 pub back_translation: String,
187}
188
189/// A near-miss variant is a wrong implementation the tests must fail, or a
190/// permitted one they must pass.
191///
192/// - `wrong`: Wrong: the tests must fail on it.
193/// - `permitted`: Permitted by the spec: the tests must pass on it.
194#[allow(missing_docs)]
195#[derive(Debug, Deserialize, JsonSchema)]
196#[serde(rename_all = "snake_case")]
197pub enum NearMissKind {
198 Wrong,
199 Permitted,
200}
201
202/// A variant that special-cases the tests' inputs.
203///
204/// - `special_case`: Passes the tests' inputs only.
205#[allow(missing_docs)]
206#[derive(Debug, Deserialize, JsonSchema)]
207#[serde(rename_all = "snake_case")]
208pub enum SpecialCaseKind {
209 SpecialCase,
210}
211
212/// A file as written.
213#[derive(Debug, Deserialize, JsonSchema)]
214pub struct File {
215 /// Project-relative path.
216 pub path: String,
217 /// The whole content.
218 pub content: String,
219}
220
221/// One variant of the code under test.
222#[derive(Debug, Deserialize, JsonSchema)]
223pub struct Variant<K> {
224 /// What kind of variant.
225 pub kind: K,
226 /// The scenario it is about.
227 pub scenario: String,
228 /// Why it is wrong, or permitted.
229 pub rationale: String,
230 /// The files it replaces.
231 pub files: Vec<File>,
232}
233
234/// Variants of the code that measure what the tests tell apart.
235#[derive(Debug, Deserialize, JsonSchema)]
236pub struct Variants<K> {
237 /// The variants.
238 pub variants: Vec<Variant<K>>,
239 /// Anything the reviewer should know.
240 pub notes: Vec<String>,
241}
242
243/// A hosted planning skill's closing report.
244#[derive(Debug, Deserialize, JsonSchema)]
245pub struct Hosted {
246 /// What was written, in a sentence or two.
247 pub summary: String,
248 /// Whether the skill finished.
249 pub status: Status,
250 /// The files written.
251 pub files_written: Vec<String>,
252 /// What is left to ask the person.
253 pub questions: Vec<String>,
254}
255
256/// One way to build an idea.
257#[derive(Debug, Deserialize, JsonSchema)]
258pub struct Approach {
259 /// Its name.
260 pub title: String,
261 /// What it is.
262 pub summary: String,
263 /// What it gives and costs.
264 pub tradeoffs: Vec<String>,
265 /// What could go wrong.
266 pub risks: Vec<String>,
267}
268
269/// An exploration's closing report: approaches for the person to choose.
270#[derive(Debug, Deserialize, JsonSchema)]
271pub struct Explored {
272 /// The idea, as understood.
273 pub summary: String,
274 /// Whether exploring finished.
275 pub status: Status,
276 /// Approaches that differ in kind, not degree.
277 pub approaches: Vec<Approach>,
278 /// What any approach must respect.
279 pub constraints: Vec<String>,
280 /// What is still open.
281 pub open_questions: Vec<String>,
282}
283
284/// An acceptance test file and the scenarios it checks.
285#[derive(Debug, Deserialize, JsonSchema)]
286pub struct TestFile {
287 /// Project-relative path.
288 pub path: String,
289 /// The whole content.
290 pub content: String,
291 /// The scenarios it checks.
292 pub scenarios: Vec<String>,
293}
294
295/// The acceptance tests written before the code.
296#[derive(Debug, Deserialize, JsonSchema)]
297pub struct Acceptance {
298 /// The test files.
299 pub files: Vec<TestFile>,
300 /// The command that runs them.
301 pub argv: Vec<String>,
302 /// Anything the reviewer should know.
303 pub notes: Vec<String>,
304}
305
306/// A reproduction of one statement of a bug.
307#[derive(Debug, Deserialize, JsonSchema)]
308pub struct Reproduction {
309 /// Project-relative path.
310 pub path: String,
311 /// The whole content.
312 pub content: String,
313 /// The statement it reproduces (`E1`) or guards (`U1`).
314 pub statement: String,
315 /// The test's name.
316 pub test: String,
317 /// The command that runs it.
318 pub argv: Vec<String>,
319}
320
321/// A bug's reproductions.
322#[derive(Debug, Deserialize, JsonSchema)]
323pub struct Reproductions {
324 /// One file per statement.
325 pub files: Vec<Reproduction>,
326 /// Anything the reviewer should know.
327 pub notes: Vec<String>,
328}
329
330/// A held-out test file, never shown to the implementer.
331#[derive(Debug, Deserialize, JsonSchema)]
332pub struct HeldOutFile {
333 /// Project-relative path.
334 pub path: String,
335 /// The whole content.
336 pub content: String,
337 /// The scenarios it checks.
338 pub scenarios: Vec<String>,
339 /// The command that runs it.
340 pub argv: Vec<String>,
341 /// Whether it checks the story's scenarios together.
342 pub composition: bool,
343}
344
345/// The held-out tests.
346#[derive(Debug, Deserialize, JsonSchema)]
347pub struct HeldOut {
348 /// The test files.
349 pub files: Vec<HeldOutFile>,
350 /// Anything the reviewer should know.
351 pub notes: Vec<String>,
352}
353
354/// A test file the person reviews before any code.
355#[derive(Debug, Deserialize, JsonSchema)]
356pub struct ReviewFile {
357 /// Project-relative path.
358 pub path: String,
359 /// The whole content.
360 pub content: String,
361 /// The scenarios it checks.
362 pub scenarios: Vec<String>,
363 /// The command that runs it.
364 pub argv: Vec<String>,
365}
366
367/// What a scenario's test expects, in the person's words.
368#[derive(Debug, Deserialize, JsonSchema)]
369pub struct Expectation {
370 /// The scenario.
371 pub scenario: String,
372 /// The situation.
373 pub given: String,
374 /// What must hold.
375 pub expect: String,
376}
377
378/// A scenario no test can check, and why.
379#[derive(Debug, Deserialize, JsonSchema)]
380pub struct Untestable {
381 /// The scenario.
382 pub scenario: String,
383 /// Why no test can check it.
384 pub why: String,
385}
386
387/// The tests for the person's review, with what each expects.
388#[derive(Debug, Deserialize, JsonSchema)]
389pub struct Review {
390 /// The test files.
391 pub files: Vec<ReviewFile>,
392 /// The command that runs them.
393 pub argv: Vec<String>,
394 /// Anything the reviewer should know.
395 pub notes: Vec<String>,
396 /// What each scenario's test expects.
397 pub expectations: Vec<Expectation>,
398 /// Scenarios no test can check.
399 pub untestable: Vec<Untestable>,
400}
401
402/// Why an attempt cannot go on, as its evidence shows.
403///
404/// - `refused`: a call it needs was refused.
405/// - `spec_conflict`: the spec contradicts itself or the code.
406/// - `wrong_test`: a frozen test contradicts the spec.
407/// - `needs_information`: only the person can say.
408#[allow(missing_docs)]
409#[derive(Debug, Deserialize, JsonSchema)]
410#[serde(rename_all = "snake_case")]
411pub enum BlockedKind {
412 Refused,
413 SpecConflict,
414 WrongTest,
415 NeedsInformation,
416}
417
418/// A `report_blocked` call: the honest exit, checked against its evidence.
419#[derive(Debug, Deserialize, JsonSchema)]
420pub struct Blocked {
421 /// Why it cannot go on.
422 pub kind: BlockedKind,
423 /// What shows it: the spec text, the test, the refused call.
424 pub evidence: String,
425 /// The reason, for the person.
426 pub reason: String,
427}
428
429/// An `ask_person` call.
430#[derive(Debug, Deserialize, JsonSchema)]
431pub struct Question {
432 /// One question.
433 pub question: String,
434}