Skip to main content

wesley_core/domain/
optic.rs

1//! Runtime optic artifact models.
2//!
3//! These types are intentionally domain-empty. They describe the GraphQL
4//! operation shape, declared bounds, and law claims that a host/runtime can
5//! admit, obstruct, witness, or replay.
6
7use crate::domain::ir::TypeReference;
8use crate::domain::operation::OperationType;
9use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11use std::fmt;
12
13/// Compiled contract for one runtime-declared GraphQL optic operation.
14#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
15#[serde(rename_all = "camelCase")]
16pub struct OpticArtifact {
17    /// Stable artifact identity derived from the schema and operation identity.
18    pub artifact_id: String,
19    /// Stable content hash for the full compiled artifact.
20    pub artifact_hash: String,
21    /// Stable schema identity derived from the lowered Wesley IR.
22    pub schema_id: String,
23    /// Stable digest of admission requirements and law claim templates.
24    pub requirements_digest: String,
25    /// The selected GraphQL operation compiled into an inspectable contract.
26    pub operation: OpticOperation,
27    /// Admission requirements Echo or another runtime must enforce.
28    pub requirements: OpticAdmissionRequirements,
29    /// Descriptor an application can present when registering this artifact.
30    pub registration: OpticRegistrationDescriptor,
31}
32
33/// Wesley-produced descriptor for registering a compiled optic artifact.
34///
35/// This is not a runtime handle and it is not an authority grant. It is the
36/// small descriptor an application can send to Echo alongside the full artifact
37/// so Echo can verify the exact artifact hash and requirements digest it is
38/// accepting into its registry.
39#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
40#[serde(rename_all = "camelCase")]
41pub struct OpticRegistrationDescriptor {
42    /// Stable artifact identity this descriptor refers to.
43    pub artifact_id: String,
44    /// Stable content hash for the full compiled artifact.
45    pub artifact_hash: String,
46    /// Stable schema identity for the referenced artifact.
47    pub schema_id: String,
48    /// Stable operation identity for the referenced artifact.
49    pub operation_id: String,
50    /// Stable digest of admission requirements and law claim templates.
51    pub requirements_digest: String,
52}
53
54/// Echo-owned opaque handle for a registered optic artifact.
55///
56/// Wesley defines the wire shape so callers can name it, but Wesley does not
57/// issue this handle. Echo or another runtime returns it after accepting and
58/// storing a specific artifact hash.
59#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
60#[serde(rename_all = "camelCase")]
61pub struct OpticArtifactHandle {
62    /// Runtime-owned discriminator, normally `optic-artifact-handle`.
63    pub kind: String,
64    /// Runtime-local opaque handle identifier.
65    pub id: String,
66}
67
68/// Admission requirements for an optic artifact.
69#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
70#[serde(rename_all = "camelCase")]
71pub struct OpticAdmissionRequirements {
72    /// Identity binding required before a host/runtime admits the handle.
73    pub identity: IdentityRequirement,
74    /// Permission requirements inferred from the declared optic bounds.
75    pub required_permissions: Vec<PermissionRequirement>,
76    /// Resource labels that must remain inaccessible to the operation.
77    pub forbidden_resources: Vec<String>,
78}
79
80/// Host, user, quorum, or policy authority grant for invoking an artifact.
81///
82/// Wesley core defines this shape so artifacts and runtime receipts can speak a
83/// shared language, but Wesley does not issue these grants. A host, user,
84/// quorum, or policy authority owns issuance, expiry, delegation, and
85/// attestation.
86#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
87#[serde(rename_all = "camelCase")]
88pub struct CapabilityGrant {
89    /// Stable grant identity.
90    pub grant_id: String,
91    /// Principal receiving bounded authority.
92    pub subject: PrincipalRef,
93    /// Artifact hash this grant covers.
94    pub artifact_hash: String,
95    /// Operation identity this grant covers.
96    pub operation_id: String,
97    /// Requirements digest this grant covers.
98    pub requirements_digest: String,
99    /// Optional basis constraint for the invocation.
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub allowed_basis: Option<BasisConstraint>,
102    /// Aperture constraints accepted by the grant.
103    pub allowed_apertures: Vec<ApertureConstraint>,
104    /// Budget constraint for invocations using the grant.
105    pub budget: BudgetConstraint,
106    /// Optional expiration timestamp supplied by the issuing authority.
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub expires_at: Option<String>,
109    /// Rights granted under the artifact requirements.
110    pub rights: Vec<String>,
111    /// Principal or service that issued the grant.
112    pub issuer: PrincipalRef,
113    /// Optional signature supplied by the issuer.
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub issuer_signature: Option<String>,
116    /// Optional digest of the delegation chain.
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub delegation_chain_digest: Option<String>,
119    /// Optional observer class bound by the grant.
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub observer_class: Option<ObserverClass>,
122    /// Whether the grant may be transferred to another subject.
123    pub non_transferable: bool,
124}
125
126/// Invocation-time presentation of a capability grant.
127#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
128#[serde(rename_all = "camelCase")]
129pub struct CapabilityPresentation {
130    /// Grant identity being presented.
131    pub grant_id: String,
132    /// Principal presenting the grant.
133    pub subject: PrincipalRef,
134    /// Echo-owned artifact handle used for this invocation.
135    pub artifact_handle_id: String,
136    /// Operation identity being invoked.
137    pub operation_id: String,
138    /// Digest of the canonical variable bytes for this invocation.
139    pub variables_digest: String,
140    /// Optional digest of the requested basis/aperture.
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub basis_request_digest: Option<String>,
143    /// Nonce preventing replay of the presentation.
144    pub nonce: String,
145    /// Presentation timestamp supplied by the caller or host.
146    pub presented_at: String,
147    /// Optional digest of a proof or signature over the presentation.
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub proof_digest: Option<String>,
150}
151
152/// Echo-owned authorization for one exact admitted invocation.
153#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
154#[serde(rename_all = "camelCase")]
155pub struct AdmissionTicket {
156    /// Stable ticket identity.
157    pub ticket_id: String,
158    /// Echo-owned artifact handle used for this invocation.
159    pub artifact_handle: OpticArtifactHandle,
160    /// Capability grant identity admitted for this invocation.
161    pub capability_grant_id: String,
162    /// Operation identity admitted for this invocation.
163    pub operation_id: String,
164    /// Digest of invocation inputs admitted by the runtime.
165    pub invocation_digest: String,
166    /// Runtime-issued admission timestamp.
167    pub issued_at: String,
168    /// Optional runtime-issued ticket expiry.
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub expires_at: Option<String>,
171}
172
173/// Constraint over the state basis allowed by a capability grant.
174#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
175#[serde(rename_all = "camelCase")]
176pub struct BasisConstraint {
177    /// Optional exact basis reference the grant permits.
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub basis_ref: Option<String>,
180    /// Optional maximum staleness in milliseconds.
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub max_staleness_ms: Option<u64>,
183}
184
185/// Constraint over a read or rewrite aperture.
186#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
187#[serde(rename_all = "camelCase")]
188pub struct ApertureConstraint {
189    /// Aperture kind, such as `file_range` or `symbol_context`.
190    pub kind: String,
191    /// Optional numeric limit owned by the host/runtime policy.
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub limit: Option<u64>,
194}
195
196/// Budget constraint attached to a capability grant.
197#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
198#[serde(rename_all = "camelCase")]
199pub struct BudgetConstraint {
200    /// Optional maximum operation count.
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub max_operations: Option<u64>,
203    /// Optional maximum byte budget.
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub max_bytes: Option<u64>,
206    /// Optional maximum runtime in milliseconds.
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub max_millis: Option<u64>,
209}
210
211/// Observer class bound by a capability grant.
212#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
213#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
214pub enum ObserverClass {
215    /// No runtime observation rights.
216    Oc0,
217    /// Minimal reading rights.
218    Oc1,
219    /// Bounded runtime observation rights.
220    Oc2,
221    /// Broad runtime observation rights under explicit policy.
222    Oc3,
223}
224
225/// Permission granted by a capability grant.
226#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
227#[serde(rename_all = "camelCase")]
228pub struct PermissionGrant {
229    /// Granted action.
230    pub action: PermissionAction,
231    /// Resource label the grant applies to.
232    pub resource: String,
233    /// Host/session-owned source of the grant.
234    pub source: String,
235}
236
237/// Artifact resolver input accepted by Wesley-side registries.
238pub type OpticArtifactRef = OpticRegistrationDescriptor;
239
240/// Resolves compiled optic artifacts from registration descriptors.
241pub trait OpticArtifactResolver {
242    /// Resolves a registration descriptor to its full compiled artifact and
243    /// verifies the descriptor still matches artifact identity and requirements.
244    fn resolve_optic_artifact(
245        &self,
246        registration: &OpticRegistrationDescriptor,
247    ) -> Result<OpticArtifact, ResolveError>;
248}
249
250/// In-memory artifact registry for tests and single-process hosts.
251#[derive(Debug, Default, Clone)]
252pub struct InMemoryOpticArtifactRegistry {
253    artifacts: BTreeMap<String, OpticArtifact>,
254}
255
256impl InMemoryOpticArtifactRegistry {
257    /// Creates an empty registry.
258    pub fn new() -> Self {
259        Self::default()
260    }
261
262    /// Stores an artifact and returns its registration descriptor.
263    pub fn insert(&mut self, mut artifact: OpticArtifact) -> OpticRegistrationDescriptor {
264        let registration = registration_descriptor_for_artifact(&artifact);
265        artifact.registration = registration.clone();
266        self.artifacts
267            .insert(artifact.artifact_id.clone(), artifact);
268        registration
269    }
270
271    /// Returns the number of stored artifacts.
272    pub fn len(&self) -> usize {
273        self.artifacts.len()
274    }
275
276    /// Returns true when no artifacts are stored.
277    pub fn is_empty(&self) -> bool {
278        self.artifacts.is_empty()
279    }
280}
281
282fn registration_descriptor_for_artifact(artifact: &OpticArtifact) -> OpticRegistrationDescriptor {
283    OpticRegistrationDescriptor {
284        artifact_id: artifact.artifact_id.clone(),
285        artifact_hash: artifact.artifact_hash.clone(),
286        schema_id: artifact.schema_id.clone(),
287        operation_id: artifact.operation.operation_id.clone(),
288        requirements_digest: artifact.requirements_digest.clone(),
289    }
290}
291
292impl OpticArtifactResolver for InMemoryOpticArtifactRegistry {
293    fn resolve_optic_artifact(
294        &self,
295        registration: &OpticRegistrationDescriptor,
296    ) -> Result<OpticArtifact, ResolveError> {
297        let artifact = self
298            .artifacts
299            .get(&registration.artifact_id)
300            .ok_or_else(|| ResolveError::ArtifactNotFound {
301                artifact_id: registration.artifact_id.clone(),
302            })?;
303        verify_registration_matches_artifact(registration, artifact)?;
304        Ok(artifact.clone())
305    }
306}
307
308fn verify_registration_matches_artifact(
309    registration: &OpticRegistrationDescriptor,
310    artifact: &OpticArtifact,
311) -> Result<(), ResolveError> {
312    if registration.artifact_hash != artifact.artifact_hash {
313        return Err(ResolveError::ArtifactHashMismatch {
314            expected: artifact.artifact_hash.clone(),
315            actual: registration.artifact_hash.clone(),
316        });
317    }
318
319    if registration.schema_id != artifact.schema_id {
320        return Err(ResolveError::SchemaIdMismatch {
321            expected: artifact.schema_id.clone(),
322            actual: registration.schema_id.clone(),
323        });
324    }
325
326    if registration.operation_id != artifact.operation.operation_id {
327        return Err(ResolveError::OperationIdMismatch {
328            expected: artifact.operation.operation_id.clone(),
329            actual: registration.operation_id.clone(),
330        });
331    }
332
333    if registration.requirements_digest != artifact.requirements_digest {
334        return Err(ResolveError::RequirementsDigestMismatch {
335            expected: artifact.requirements_digest.clone(),
336            actual: registration.requirements_digest.clone(),
337        });
338    }
339
340    Ok(())
341}
342
343/// Error raised when an optic artifact reference cannot resolve cleanly.
344#[derive(Debug, Clone, PartialEq, Eq)]
345pub enum ResolveError {
346    /// No artifact exists for the referenced artifact identity.
347    ArtifactNotFound {
348        /// Artifact identity requested by the descriptor.
349        artifact_id: String,
350    },
351    /// The supplied artifact hash does not match the stored artifact.
352    ArtifactHashMismatch {
353        /// Artifact hash expected by the stored artifact.
354        expected: String,
355        /// Artifact hash supplied by the caller.
356        actual: String,
357    },
358    /// The supplied schema id does not match the stored artifact.
359    SchemaIdMismatch {
360        /// Schema id expected by the stored artifact.
361        expected: String,
362        /// Schema id supplied by the caller.
363        actual: String,
364    },
365    /// The supplied operation id does not match the stored artifact.
366    OperationIdMismatch {
367        /// Operation id expected by the stored artifact.
368        expected: String,
369        /// Operation id supplied by the caller.
370        actual: String,
371    },
372    /// The supplied requirements digest does not match the stored artifact.
373    RequirementsDigestMismatch {
374        /// Requirements digest expected by the stored artifact.
375        expected: String,
376        /// Requirements digest supplied by the caller.
377        actual: String,
378    },
379}
380
381impl fmt::Display for ResolveError {
382    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
383        match self {
384            ResolveError::ArtifactNotFound { artifact_id } => {
385                write!(formatter, "optic artifact '{artifact_id}' was not found")
386            }
387            ResolveError::ArtifactHashMismatch { expected, actual } => write!(
388                formatter,
389                "optic artifact hash mismatch: expected '{expected}', got '{actual}'"
390            ),
391            ResolveError::SchemaIdMismatch { expected, actual } => write!(
392                formatter,
393                "optic schema id mismatch: expected '{expected}', got '{actual}'"
394            ),
395            ResolveError::OperationIdMismatch { expected, actual } => write!(
396                formatter,
397                "optic operation id mismatch: expected '{expected}', got '{actual}'"
398            ),
399            ResolveError::RequirementsDigestMismatch { expected, actual } => write!(
400                formatter,
401                "optic requirements digest mismatch: expected '{expected}', got '{actual}'"
402            ),
403        }
404    }
405}
406
407impl std::error::Error for ResolveError {}
408
409/// Identity requirement a host/runtime must satisfy before admission.
410#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
411#[serde(rename_all = "camelCase")]
412pub struct IdentityRequirement {
413    /// Whether the host/runtime must bind a principal before admission.
414    pub required: bool,
415    /// Accepted principal kinds, or empty when host policy owns the vocabulary.
416    pub accepted_principal_kinds: Vec<String>,
417}
418
419/// Opaque principal reference supplied by a host/session layer.
420#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
421#[serde(rename_all = "camelCase")]
422pub struct PrincipalRef {
423    /// Principal namespace, such as `user`, `agent`, `session`, or `service`.
424    pub kind: String,
425    /// Principal identifier inside the namespace.
426    pub id: String,
427}
428
429/// Permission requirement inferred from an optic declaration.
430#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
431#[serde(rename_all = "camelCase")]
432pub struct PermissionRequirement {
433    /// Required action.
434    pub action: PermissionAction,
435    /// Resource label the action applies to.
436    pub resource: String,
437    /// Compiler source of the requirement.
438    pub source: String,
439}
440
441/// Permission action required for an optic resource label.
442#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
443#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
444pub enum PermissionAction {
445    /// Read access is required.
446    Read,
447    /// Write access is required.
448    Write,
449}
450
451/// Inspectable contract for a selected GraphQL operation.
452#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
453#[serde(rename_all = "camelCase")]
454pub struct OpticOperation {
455    /// Stable operation identity derived from the selected operation shape.
456    pub operation_id: String,
457    /// Optional GraphQL operation name.
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub name: Option<String>,
460    /// GraphQL operation kind.
461    pub kind: OperationKind,
462    /// Root schema field selected by the operation.
463    pub root_field: String,
464    /// Canonical bindings supplied to the selected root field.
465    pub root_arguments: Vec<RootArgumentBinding>,
466    /// Canonical argument bindings supplied to selected payload fields.
467    #[serde(default, skip_serializing_if = "Vec::is_empty")]
468    pub selection_arguments: Vec<SelectionArgumentBinding>,
469    /// Codec shape for the operation variables or root field arguments.
470    pub variable_shape: CodecShape,
471    /// Codec shape for the selected response payload.
472    pub payload_shape: CodecShape,
473    /// Directives preserved from the executable operation.
474    pub directives: Vec<DirectiveRecord>,
475    /// Declared resource footprint, when the operation supplies one.
476    #[serde(skip_serializing_if = "Option::is_none")]
477    pub declared_footprint: Option<Footprint>,
478    /// Compiler-produced templates for laws relevant to this operation.
479    pub law_claims: Vec<LawClaimTemplate>,
480}
481
482/// Canonical binding for one argument supplied to an optic root field.
483#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
484#[serde(rename_all = "camelCase")]
485pub struct RootArgumentBinding {
486    /// Root argument name.
487    pub name: String,
488    /// Schema-declared argument type.
489    pub type_ref: TypeReference,
490    /// Canonical JSON representation of the supplied GraphQL input value.
491    pub value_canonical_json: String,
492}
493
494/// Canonical binding for one argument supplied to a selected payload field.
495#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
496#[serde(rename_all = "camelCase")]
497pub struct SelectionArgumentBinding {
498    /// Response-path field that received the argument.
499    pub path: String,
500    /// Field argument name.
501    pub name: String,
502    /// Schema-declared argument type.
503    pub type_ref: TypeReference,
504    /// Canonical JSON representation of the supplied GraphQL input value.
505    pub value_canonical_json: String,
506}
507
508/// GraphQL executable operation kind.
509#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
510#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
511pub enum OperationKind {
512    /// GraphQL query operation.
513    Query,
514    /// GraphQL mutation operation.
515    Mutation,
516    /// GraphQL subscription operation.
517    Subscription,
518}
519
520impl From<OperationType> for OperationKind {
521    fn from(value: OperationType) -> Self {
522        match value {
523            OperationType::Query => OperationKind::Query,
524            OperationType::Mutation => OperationKind::Mutation,
525            OperationType::Subscription => OperationKind::Subscription,
526        }
527    }
528}
529
530impl From<OperationKind> for OperationType {
531    fn from(value: OperationKind) -> Self {
532        match value {
533            OperationKind::Query => OperationType::Query,
534            OperationKind::Mutation => OperationType::Mutation,
535            OperationKind::Subscription => OperationType::Subscription,
536        }
537    }
538}
539
540/// Named codec view for variables or payload data.
541#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
542#[serde(rename_all = "camelCase")]
543pub struct CodecShape {
544    /// Logical shape name.
545    pub type_name: String,
546    /// Fields visible inside the shape.
547    pub fields: Vec<CodecField>,
548}
549
550/// One field inside a codec shape.
551#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
552#[serde(rename_all = "camelCase")]
553pub struct CodecField {
554    /// Field name or selected response path.
555    pub name: String,
556    /// GraphQL type reference for the field.
557    pub type_ref: TypeReference,
558    /// Whether the field is non-null in the GraphQL type system.
559    pub required: bool,
560    /// Whether the field has an outer or nested list wrapper.
561    pub list: bool,
562}
563
564/// Directive preserved from a compiled executable operation.
565#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
566#[serde(rename_all = "camelCase")]
567pub struct DirectiveRecord {
568    /// Schema coordinate or operation coordinate where the directive was found.
569    pub coordinate: String,
570    /// Directive name without the `@` prefix.
571    pub name: String,
572    /// Canonical JSON object containing the directive arguments.
573    pub arguments_canonical_json: String,
574}
575
576/// Declared resource families an operation may read, write, or must not touch.
577#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
578#[serde(rename_all = "camelCase")]
579pub struct Footprint {
580    /// Resource labels the operation declares it may read.
581    pub reads: Vec<String>,
582    /// Resource labels the operation declares it may write.
583    pub writes: Vec<String>,
584    /// Resource labels the operation declares forbidden.
585    pub forbids: Vec<String>,
586}
587
588/// Compiler-produced declaration that a law is relevant to an operation.
589#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
590#[serde(rename_all = "camelCase")]
591pub struct LawClaimTemplate {
592    /// Stable law identifier.
593    pub law_id: String,
594    /// Stable claim identity for this operation and law pairing.
595    pub claim_id: String,
596    /// Operation identity this claim applies to.
597    pub operation_id: String,
598    /// Evidence categories a runtime or verifier should produce.
599    pub required_evidence: Vec<EvidenceKind>,
600}
601
602/// Evidence category requested by a law claim template.
603#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
604#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
605pub enum EvidenceKind {
606    /// Evidence produced by the Wesley compiler.
607    Compiler,
608    /// Evidence produced by codec inspection or fixture vectors.
609    Codec,
610    /// Evidence produced by host/runtime policy.
611    HostPolicy,
612    /// Evidence produced from runtime trace data.
613    RuntimeTrace,
614    /// Evidence produced by a domain verifier outside Wesley core.
615    DomainVerifier,
616}
617
618/// Runtime or verifier-produced verdict for one law claim.
619#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
620#[serde(rename_all = "camelCase")]
621pub struct LawWitness {
622    /// Stable law identifier.
623    pub law_id: String,
624    /// Claim identity this witness evaluates.
625    pub claim_id: String,
626    /// Optional state basis reference evaluated by the checker.
627    #[serde(skip_serializing_if = "Option::is_none")]
628    pub basis_ref: Option<String>,
629    /// Identifier for the checker that produced the verdict.
630    pub checker_id: String,
631    /// Optional hash of the checker artifact.
632    #[serde(skip_serializing_if = "Option::is_none")]
633    pub checker_artifact_hash: Option<String>,
634    /// Verdict for the law claim.
635    pub verdict: LawVerdict,
636    /// Digests of evidence artifacts considered by the checker.
637    pub evidence_digests: Vec<String>,
638    /// Optional digest of the runtime trace considered by the checker.
639    #[serde(skip_serializing_if = "Option::is_none")]
640    pub runtime_trace_digest: Option<String>,
641    /// Optional reason for an obstructed verdict.
642    #[serde(skip_serializing_if = "Option::is_none")]
643    pub obstruction_reason: Option<String>,
644    /// Replay hints supplied by the runtime or verifier.
645    pub replay_hints: Vec<ReplayHint>,
646}
647
648/// Verdict produced for a law claim.
649#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
650#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
651pub enum LawVerdict {
652    /// The checker found the law satisfied.
653    Satisfied,
654    /// The checker found a concrete obstruction.
655    Obstructed,
656    /// The checker cannot establish satisfaction or obstruction.
657    Unknown,
658}
659
660/// Hint that helps a runtime replay or inspect a witnessed interaction.
661#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
662#[serde(rename_all = "camelCase")]
663pub struct ReplayHint {
664    /// Hint kind, such as `trace`, `basis`, or `artifact`.
665    pub kind: String,
666    /// Hint value, intentionally opaque to Wesley core.
667    pub value: String,
668}