Skip to main content

rustev_contract/
definition.rs

1//! Decision definitions, `rustev.definition/1` (spec 002, 3.7 to 3.11) and
2//! `rustev.definition/2` (spec 032, 3.1.1).
3//!
4//! These are the wire types. Their validation, typing and compilation live
5//! in `rustev-core`; the typed builder there produces these same values, so
6//! the builder and the JSON form enter one validated representation (R-02).
7//! Every field is required: absence is always an explicit variant. The
8//! exception is spec 032's optional members, which version 1 never carries
9//! and which are omitted from canonical bytes when unused (spec 032, 3.1.2);
10//! the compiler refuses them in a version 1 definition. Spec 033 adds
11//! `actionability` and its policy constructs on the same terms.
12
13use serde::{Deserialize, Serialize};
14use serde_json::Value;
15
16use crate::actionability::{Actionability, Address, RemediationUse};
17use crate::decimal::Decimal;
18use crate::grade::{Grade, PresentWhen};
19use crate::ids::{ArtifactId, CalibrationId, DefinitionId};
20use crate::limits::DEFINITION_V1;
21use crate::schema;
22use crate::time::Timestamp;
23
24/// A versioned, authored description of a recurring decision.
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
26#[serde(deny_unknown_fields)]
27pub struct Definition {
28    pub schema: String,
29    pub name: String,
30    /// `MAJOR.MINOR.PATCH`, each without leading zeros.
31    pub version: String,
32    pub package: String,
33    pub inputs: Vec<InputDecl>,
34    #[serde(deserialize_with = "crate::generation::steps")]
35    pub steps: Vec<StepDecl>,
36    pub policy: PolicyDecl,
37    pub limits: LimitsDecl,
38    /// Version 2: provenance-conditional checks and actionable outcomes
39    /// (spec 033, 3.1.1); omitted when unused.
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub actionability: Option<Actionability>,
42    /// Optional screening contract (spec 035); omitted when unused.
43    #[serde(
44        default,
45        skip_serializing_if = "Option::is_none",
46        deserialize_with = "crate::screening::present"
47    )]
48    #[schemars(with = "crate::screening::Screening")]
49    pub screening: Option<crate::screening::Screening>,
50}
51
52crate::document::versioned_document!(
53    Definition,
54    [schema::DEFINITION, schema::DEFINITION_V2],
55    "rustev.definition/1 or rustev.definition/2",
56    DEFINITION_V1,
57    DefinitionId
58);
59
60/// One declared input field (spec 002, 3.7.1).
61#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
62#[serde(deny_unknown_fields)]
63pub struct InputDecl {
64    pub name: String,
65    #[serde(rename = "type")]
66    pub ty: TypeDecl,
67    /// The provenance classes this input accepts; non-empty.
68    pub provenance: Vec<ProvenanceClass>,
69    pub freshness: Freshness,
70    /// Version 2: the sources that grade this input (spec 032, 3.3.3);
71    /// an input without it is ungraded.
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub evidence: Option<EvidenceDecl>,
74    /// Version 2: how disagreeing entries are resolved (spec 032, 3.8);
75    /// omitted means `require_agreement`.
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub on_disagreement: Option<Disagreement>,
78}
79
80/// How an input is graded (spec 032, 3.3.3).
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
82#[serde(rename_all = "snake_case", deny_unknown_fields)]
83pub enum EvidenceDecl {
84    Graded {
85        /// Non-empty, sorted, duplicate-free source names.
86        sources: Vec<String>,
87        /// Lower the ceiling of a path and its descendants; never raise.
88        caps: Vec<Cap>,
89    },
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
93#[serde(deny_unknown_fields)]
94pub struct Cap {
95    pub path: String,
96    pub max_grade: Grade,
97}
98
99/// The conflict policy of a graded input (spec 032, 3.8). There is no
100/// recency rule: entry order never chooses a value.
101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
102#[serde(rename_all = "snake_case", deny_unknown_fields)]
103pub enum Disagreement {
104    RequireAgreement,
105    Prefer { sources: Vec<String> },
106    TreatAsHint { sources: Vec<String> },
107    Abstain,
108}
109
110/// The closed set of provenance classes (spec 002, 3.7.1).
111#[derive(
112    Debug,
113    Clone,
114    Copy,
115    PartialEq,
116    Eq,
117    PartialOrd,
118    Ord,
119    Hash,
120    Serialize,
121    Deserialize,
122    schemars::JsonSchema,
123)]
124#[serde(rename_all = "kebab-case")]
125pub enum ProvenanceClass {
126    AuthenticatedAppField,
127    SystemOfRecord,
128    UserSupplied,
129    ThirdParty,
130    AttributedClaim,
131    ModelDerived,
132}
133
134/// How old an input may be at evaluation time.
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
136#[serde(rename_all = "snake_case", deny_unknown_fields)]
137pub enum Freshness {
138    MaxAgeMs(u64),
139    NotRequired,
140}
141
142/// Input value types (spec 002, 3.7.2). Every record field is required.
143#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
144#[serde(rename_all = "snake_case", deny_unknown_fields)]
145pub enum TypeDecl {
146    Bool,
147    Integer,
148    Decimal,
149    Text {
150        max_bytes: u64,
151    },
152    Timestamp,
153    Enum {
154        options: Vec<String>,
155    },
156    List {
157        item: Box<TypeDecl>,
158        max_items: u64,
159    },
160    Record {
161        fields: Vec<FieldDecl>,
162        /// Version 2: when a decoded record is present (spec 032, 3.4.2).
163        #[serde(default, skip_serializing_if = "Option::is_none")]
164        present_when: Option<PresentWhen>,
165    },
166    /// Version 2, record fields only: JSON `null` or a missing key, stored
167    /// as `null` (spec 032, 3.4.1).
168    Nullable {
169        #[serde(rename = "type")]
170        ty: Box<TypeDecl>,
171    },
172    /// Version 2: a JSON object with text keys (spec 032, 3.4.1).
173    Map {
174        key_max_bytes: u64,
175        value: Box<TypeDecl>,
176        max_entries: u64,
177    },
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
181#[serde(deny_unknown_fields)]
182pub struct FieldDecl {
183    pub name: String,
184    #[serde(rename = "type")]
185    pub ty: TypeDecl,
186}
187
188/// One step of the plan graph.
189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
190#[serde(deny_unknown_fields)]
191pub struct StepDecl {
192    pub id: String,
193    pub body: StepBody,
194}
195
196#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
197#[serde(rename_all = "snake_case", deny_unknown_fields)]
198pub enum StepBody {
199    Acquire(crate::acquisition_step::Acquire),
200    Widen(crate::acquisition_step::Widen),
201    Exact(ExactDecl),
202    Semantic(SemanticDecl),
203    /// Version 2: the only way a grade rises (spec 032, 3.7).
204    Corroborate(CorroborateDecl),
205    Generate(Box<crate::generation::Generate>),
206    Extract(Box<crate::extraction::Extract>),
207    AcceptModelDerived(Box<crate::extraction::AcceptModelDerived>),
208}
209
210/// A corroboration step (spec 032, 3.7.1).
211#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
212#[serde(deny_unknown_fields)]
213pub struct CorroborateDecl {
214    /// An input path or step value.
215    pub claim: String,
216    /// A grade above `hint`.
217    pub upgrade_to: Grade,
218    /// Non-empty.
219    pub by: Vec<Check>,
220    pub mode: CorroborationMode,
221    pub on_contradiction: OnContradiction,
222}
223
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
225#[serde(deny_unknown_fields)]
226pub struct Check {
227    pub predicate: Predicate,
228    pub on_false: OnFalse,
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
232#[serde(rename_all = "snake_case")]
233pub enum OnFalse {
234    Contradicts,
235    NoEvidence,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
239#[serde(rename_all = "snake_case")]
240pub enum CorroborationMode {
241    All,
242    Any,
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
246#[serde(rename_all = "snake_case")]
247pub enum OnContradiction {
248    Conflict,
249    TreatAsHint,
250    Abstain,
251}
252
253/// An exact step: a registry operator, its version and its arguments. The
254/// arguments are decoded against the operator's own schema by the compiler,
255/// so an unknown operator is refused as `unknown_operator`, not as a parse
256/// failure (spec 002, 3.9.5).
257#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
258#[serde(deny_unknown_fields)]
259pub struct ExactDecl {
260    pub op: String,
261    pub version: u32,
262    pub args: Value,
263}
264
265/// The four semantic operations (R-05).
266#[derive(
267    Debug,
268    Clone,
269    Copy,
270    PartialEq,
271    Eq,
272    PartialOrd,
273    Ord,
274    Hash,
275    Serialize,
276    Deserialize,
277    schemars::JsonSchema,
278)]
279#[serde(rename_all = "snake_case")]
280pub enum Operation {
281    Classify,
282    Proposition,
283    Rubric,
284    Rank,
285    Generate,
286    Extract,
287}
288
289/// The value kind a semantic step requires (spec 002, 3.9.3).
290#[derive(
291    Debug,
292    Clone,
293    Copy,
294    PartialEq,
295    Eq,
296    PartialOrd,
297    Ord,
298    Hash,
299    Serialize,
300    Deserialize,
301    schemars::JsonSchema,
302)]
303#[serde(rename_all = "snake_case")]
304pub enum RequiredKind {
305    Label,
306    Distribution,
307    CalibratedProbability,
308    OrdinalDistribution,
309    Scores,
310    ScreenVerdict,
311    TextRecord,
312    TypedRecord,
313}
314
315/// A question answered by a decision backend.
316#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
317#[serde(deny_unknown_fields)]
318pub struct SemanticDecl {
319    pub operation: Operation,
320    /// The task identity a calibration binds to.
321    pub task: String,
322    /// The question or statement put to the backend.
323    pub question: String,
324    /// Labels (classify), `["false", "true"]` (proposition), levels from low to
325    /// high (rubric); empty for rank.
326    pub options: Vec<String>,
327    /// For rank: the step whose id list is ranked.
328    pub candidates: Candidates,
329    pub requires: RequiredKind,
330    /// References (`input:<name>`, `step:<id>`, `bind:<name>`) the backend sees.
331    pub project: Vec<String>,
332    /// Zero, one or two id lists this step fans out over.
333    pub for_each: Vec<ForEach>,
334    pub backend: BackendReq,
335    pub calibration: CalibrationRef,
336    pub fallback: Fallback,
337}
338
339#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
340#[serde(rename_all = "snake_case", deny_unknown_fields)]
341pub enum Candidates {
342    None,
343    From(String),
344}
345
346/// Fan-out over the ids produced by a step.
347#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
348#[serde(deny_unknown_fields)]
349pub struct ForEach {
350    /// `step:<id>` whose result is an id list, a filter result or a shortlist.
351    pub over: String,
352    /// The name the instance key carries; `bind:<name>` projects its item.
353    pub bind: String,
354    /// Where the item record for each id comes from, if it is projected.
355    pub items: ItemSource,
356}
357
358#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
359#[serde(rename_all = "snake_case", deny_unknown_fields)]
360pub enum ItemSource {
361    None,
362    /// A list of records (`input:<name>`) and the text field holding the id.
363    List {
364        list: String,
365        id_field: String,
366    },
367}
368
369/// Declared backend requirements beyond the operation and value kind.
370#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
371#[serde(deny_unknown_fields)]
372pub struct BackendReq {
373    pub min_determinism: Determinism,
374    pub artifact: ArtifactPin,
375    pub binding: BindingChoice,
376}
377
378/// Declared determinism of a backend, weakest first.
379#[derive(
380    Debug,
381    Clone,
382    Copy,
383    PartialEq,
384    Eq,
385    PartialOrd,
386    Ord,
387    Hash,
388    Serialize,
389    Deserialize,
390    schemars::JsonSchema,
391)]
392#[serde(rename_all = "snake_case")]
393pub enum Determinism {
394    Unspecified,
395    Tolerance,
396    Bitwise,
397}
398
399#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
400#[serde(rename_all = "snake_case", deny_unknown_fields)]
401pub enum ArtifactPin {
402    Any,
403    Pinned(ArtifactId),
404}
405
406/// An explicit binding makes that backend the only candidate (3.9.4).
407#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
408#[serde(rename_all = "snake_case", deny_unknown_fields)]
409pub enum BindingChoice {
410    Auto,
411    Backend(String),
412}
413
414#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
415#[serde(rename_all = "snake_case", deny_unknown_fields)]
416pub enum CalibrationRef {
417    None,
418    Id(CalibrationId),
419}
420
421/// What happens when no candidate satisfies the step (3.9.6).
422#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
423#[serde(rename_all = "snake_case", deny_unknown_fields)]
424pub enum Fallback {
425    None,
426    /// The step is always `unsupported{capability}`.
427    Unsupported,
428    /// Bind with this alternative required kind instead.
429    Kind {
430        requires: RequiredKind,
431        reason: String,
432    },
433}
434
435/// Declared ceilings (spec 002, 3.9.5 category 7, and 3.10.3).
436#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
437#[serde(deny_unknown_fields)]
438pub struct LimitsDecl {
439    /// Spec 036 acquisition ceilings, omitted when unused.
440    #[serde(
441        default,
442        skip_serializing_if = "Option::is_none",
443        deserialize_with = "crate::screening::present"
444    )]
445    #[schemars(with = "crate::acquisition_step::AcquisitionBudget")]
446    pub acquisition: Option<crate::acquisition_step::AcquisitionBudget>,
447    pub max_semantic_requests: u64,
448    pub on_excess: ExcessPolicy,
449    pub max_projection_bytes: u64,
450    /// Allowed `|sum - 1|` for a supplied distribution.
451    pub distribution_tolerance: Decimal,
452    /// Recorded for the runtime; not enforced by the pure core.
453    pub deadline_ms: u64,
454}
455
456#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
457#[serde(rename_all = "snake_case")]
458pub enum ExcessPolicy {
459    Refuse,
460    TruncateVisible,
461}
462
463/// A typed literal.
464#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
465#[serde(rename_all = "snake_case", deny_unknown_fields)]
466pub enum Literal {
467    Bool(bool),
468    Integer(i64),
469    Decimal(Decimal),
470    Text(String),
471    Timestamp(Timestamp),
472    DurationMs(u64),
473    Enum(String),
474}
475
476/// Expressions inside exact-operator arguments and policy predicates
477/// (registry `rustev.exact/1`). References are `input:<name>`,
478/// `step:<id>`, `item:<field>[.<field>...]` (inside a per-item operator) and
479/// `now`.
480#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
481#[serde(rename_all = "snake_case", deny_unknown_fields)]
482pub enum Expr {
483    Ref(String),
484    Lit(Literal),
485    Add {
486        left: Box<Expr>,
487        right: Box<Expr>,
488    },
489    Sub {
490        left: Box<Expr>,
491        right: Box<Expr>,
492    },
493    /// Decimal product rounded half-even to 10^-9; integers are exact.
494    Mul {
495        left: Box<Expr>,
496        right: Box<Expr>,
497    },
498    /// Decimal quotient rounded half-even to 10^-9.
499    Div {
500        left: Box<Expr>,
501        right: Box<Expr>,
502    },
503    /// Rescale a decimal half-even to `scale` digits.
504    Round {
505        value: Box<Expr>,
506        scale: u32,
507    },
508    /// Currency conversion through a rate table (`fx_convert@1` semantics).
509    Fx {
510        amount: Box<Expr>,
511        from: Box<Expr>,
512        to: Box<Expr>,
513        rates: String,
514        scale: u32,
515    },
516    /// Length of a list.
517    Len(Box<Expr>),
518    /// Integer to decimal, the only numeric conversion.
519    ToDecimal(Box<Expr>),
520    /// Registry `rustev.exact/2`: a reference with a grade requirement
521    /// (spec 032, 3.9.1).
522    Graded {
523        #[serde(rename = "ref")]
524        reference: String,
525        require: GradeRequirement,
526    },
527}
528
529/// What a graded read requires (spec 032, 3.9.1).
530#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
531#[serde(deny_unknown_fields)]
532pub struct GradeRequirement {
533    pub min: Grade,
534    pub max_age_ms: AgeLimit,
535}
536
537#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
538#[serde(rename_all = "snake_case", deny_unknown_fields)]
539pub enum AgeLimit {
540    Ms(u64),
541    NotRequired,
542}
543
544#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
545#[serde(rename_all = "snake_case")]
546pub enum CmpOp {
547    Eq,
548    Ne,
549    Lt,
550    Le,
551    Gt,
552    Ge,
553}
554
555/// Boolean predicates. `all([])` is true; `any([])` is false.
556#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
557#[serde(rename_all = "snake_case", deny_unknown_fields)]
558pub enum Predicate {
559    Cmp {
560        left: Expr,
561        op: CmpOp,
562        right: Expr,
563    },
564    All(Vec<Predicate>),
565    Any(Vec<Predicate>),
566    Not(Box<Predicate>),
567    Member {
568        value: Expr,
569        set: Vec<Literal>,
570    },
571    /// True when the named input resolved (`input:<name>`).
572    Present(String),
573    /// Registry 2: whether the referenced value was constructed (spec 036).
574    IsConstructed {
575        #[serde(rename = "ref")]
576        reference: String,
577    },
578    /// Registry `rustev.exact/2`: true when the value is a member of the
579    /// list, or the keys of the map, `set` references (spec 032, 3.7.2).
580    MemberRef {
581        value: Expr,
582        set: String,
583    },
584}
585
586/// The selection policy (spec 002, 3.11).
587#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
588#[serde(deny_unknown_fields)]
589pub struct PolicyDecl {
590    pub on_unresolved: Vec<Handler>,
591    pub rules: Vec<Rule>,
592    pub adjustments: Vec<Adjustment>,
593}
594
595/// Handles one input or step being unresolved.
596#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
597#[serde(deny_unknown_fields)]
598pub struct Handler {
599    /// `input:<name>` or `step:<id>`.
600    pub target: String,
601    pub reasons: ReasonSet,
602    pub action: HandlerAction,
603}
604
605#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
606#[serde(rename_all = "snake_case", deny_unknown_fields)]
607pub enum ReasonSet {
608    Any,
609    Only(Vec<ReasonKind>),
610}
611
612/// The names of the closed `Unresolved` reasons.
613#[derive(
614    Debug,
615    Clone,
616    Copy,
617    PartialEq,
618    Eq,
619    PartialOrd,
620    Ord,
621    Hash,
622    Serialize,
623    Deserialize,
624    schemars::JsonSchema,
625)]
626#[serde(rename_all = "snake_case")]
627pub enum ReasonKind {
628    MissingEvidence,
629    StaleEvidence,
630    InvalidInput,
631    Abstained,
632    BackendUnavailable,
633    InvalidBackendOutput,
634    BudgetExhausted,
635    DeadlineExceeded,
636    Conflict,
637    Unsupported,
638    /// Present but below a required grade (spec 032, 3.9.4).
639    InsufficientGrade,
640    ScreenUnavailable,
641    /// A declared skipped path, distinct from an unresolved failure.
642    Skipped,
643}
644
645impl ReasonKind {
646    pub const ALL: [ReasonKind; 13] = [
647        ReasonKind::MissingEvidence,
648        ReasonKind::StaleEvidence,
649        ReasonKind::InvalidInput,
650        ReasonKind::Abstained,
651        ReasonKind::BackendUnavailable,
652        ReasonKind::InvalidBackendOutput,
653        ReasonKind::BudgetExhausted,
654        ReasonKind::DeadlineExceeded,
655        ReasonKind::Conflict,
656        ReasonKind::Unsupported,
657        ReasonKind::InsufficientGrade,
658        ReasonKind::ScreenUnavailable,
659        ReasonKind::Skipped,
660    ];
661}
662
663#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
664#[serde(rename_all = "snake_case", deny_unknown_fields)]
665pub enum HandlerAction {
666    /// The judgment is the target's unresolved reason.
667    Propagate,
668    Escalate {
669        reason: String,
670    },
671    /// Atomic conditions reading the target are false; a rule whose outcome
672    /// reads it does not match.
673    AsUnmet,
674    /// `propagate` with a declared remediation or address override (spec
675    /// 033, 3.5.5).
676    PropagateWith {
677        remediation: Option<RemediationUse>,
678        #[serde(default, skip_serializing_if = "Address::is_computed")]
679        address: Address,
680    },
681    /// The caller is asked for these fields (spec 033, 3.7).
682    NeedsInput {
683        fields: Vec<String>,
684        questions: Vec<String>,
685    },
686}
687
688#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
689#[serde(deny_unknown_fields)]
690pub struct Rule {
691    pub id: String,
692    pub when: Cond,
693    pub then: OutcomeDecl,
694}
695
696/// Whether a probability threshold may read an uncalibrated distribution.
697#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
698#[serde(rename_all = "snake_case", deny_unknown_fields)]
699pub enum UncalibratedThreshold {
700    NotDeclared,
701    Declared { reason: String },
702}
703
704/// Policy conditions (spec 002, 3.11.3).
705#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
706#[serde(rename_all = "snake_case", deny_unknown_fields)]
707pub enum Cond {
708    Always,
709    TopLabel {
710        step: String,
711        label: String,
712    },
713    MassAtLeast {
714        step: String,
715        option: String,
716        threshold: Decimal,
717        uncalibrated_threshold: UncalibratedThreshold,
718    },
719    TopMassBelow {
720        step: String,
721        threshold: Decimal,
722        uncalibrated_threshold: UncalibratedThreshold,
723    },
724    ExpectationAtLeast {
725        step: String,
726        value: Decimal,
727    },
728    Exact(Predicate),
729    Empty(String),
730    Nonempty(String),
731    All(Vec<Cond>),
732    Any(Vec<Cond>),
733    Not(Box<Cond>),
734    /// True when the referenced value's provenance is within `classes`
735    /// (spec 033, 3.4.7).
736    ProvenanceWithin {
737        #[serde(rename = "ref")]
738        reference: String,
739        classes: Vec<ProvenanceClass>,
740    },
741}
742
743/// What a matching rule proposes. Never a grant (spec 001, 3.2).
744#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
745#[serde(rename_all = "snake_case", deny_unknown_fields)]
746pub enum OutcomeDecl {
747    Propose {
748        action: String,
749        params: Vec<ParamDecl>,
750    },
751    Escalate {
752        reason: String,
753    },
754    /// `missing_evidence` naming the fields listed by a step.
755    MissingEvidenceFrom {
756        step: String,
757    },
758    /// The caller is asked for these fields (spec 033, 3.7).
759    NeedsInput {
760        fields: Vec<String>,
761        questions: Vec<String>,
762    },
763}
764
765#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
766#[serde(deny_unknown_fields)]
767pub struct ParamDecl {
768    pub name: String,
769    pub value: ParamSource,
770}
771
772#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
773#[serde(rename_all = "snake_case", deny_unknown_fields)]
774pub enum ParamSource {
775    Ref(String),
776    Lit(Literal),
777}
778
779/// Raises a proposal parameter one position on an ordered scale, saturating.
780#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
781#[serde(deny_unknown_fields)]
782pub struct Adjustment {
783    pub id: String,
784    pub when: Cond,
785    pub param: String,
786    pub scale: Vec<String>,
787}