Skip to main content

forge_memory_bridge/
batch.rs

1#![allow(deprecated)]
2
3//! Bridge-owned projection import batch types.
4//!
5//! `ProjectionImportBatchV1` remains available only as a compatibility output.
6//! `ProjectionImportBatchV2` remains the normalized storage/import shape for
7//! compatibility callers.
8//! The canonical bridge output for the kernel-ready lane is
9//! `ProjectionImportBatchV3`.
10//!
11//! ## Phase status: current / implemented now
12
13use schemars::JsonSchema;
14use semantic_memory_forge::{
15    ClaimStateV13, ContradictionWitnessV1, EpisodeBundleV1, EvidenceBundle, ExecutionContextV1,
16    ExportRecordSemanticsV3, ForgeExportMeta, RetractionRecordV1, SupportSetV1,
17};
18use serde::{Deserialize, Serialize};
19use serde_json::Value;
20use stack_ids::{
21    ClaimId, ClaimVersionId, ContentDigest, EntityId, EnvelopeId, EpisodeId, RelationVersionId,
22    ScopeKey, TraceCtx,
23};
24
25/// Legacy compatibility import-batch schema version emitted by the bridge.
26///
27/// This is distinct from the source export envelope schema version. The latter
28/// is preserved in [`ProjectionImportBatchV1::export_schema_version`].
29///
30/// Phase status: migration-only
31/// Removal condition: remove when all consumers have migrated to `ProjectionImportBatchV3`
32#[deprecated(
33    since = "0.2.0",
34    note = "ProjectionImportBatchV1 is compatibility-only. Use ProjectionImportBatchV3 for the canonical bridge output."
35)]
36pub const PROJECTION_IMPORT_BATCH_V1_SCHEMA: &str = "projection_import_batch_v1";
37/// Canonical import-batch schema version emitted by the bridge.
38pub const PROJECTION_IMPORT_BATCH_V2_SCHEMA: &str = "projection_import_batch_v2";
39/// Rich import-batch schema version emitted by the bridge for the kernel path.
40pub const PROJECTION_IMPORT_BATCH_V3_SCHEMA: &str = "projection_import_batch_v3";
41
42/// A projection import batch produced by the bridge.
43///
44/// This is the unit of atomicity for projection imports. All records
45/// are committed together or rolled back entirely.
46///
47/// Temporal provenance is intentionally split:
48/// - `source_exported_at` = when the source envelope was emitted
49/// - `transformed_at` = when the bridge produced this batch
50///
51/// The importing store assigns authoritative imported `recorded_at`
52/// when it commits the rows.
53///
54/// Phase status: migration-only
55/// Removal condition: remove when all consumers have migrated to `ProjectionImportBatchV3`
56#[deprecated(
57    since = "0.2.0",
58    note = "ProjectionImportBatchV1 is compatibility-only. Use ProjectionImportBatchV3 for the canonical bridge output."
59)]
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
61pub struct ProjectionImportBatchV1 {
62    /// Source envelope ID (for provenance).
63    pub source_envelope_id: EnvelopeId,
64    /// Import-side schema version this batch conforms to (I005).
65    ///
66    /// For V1, the bridge emits the canonical import-side token
67    /// `projection_import_batch_v1`. The source export schema version is
68    /// preserved separately in `export_schema_version`.
69    pub schema_version: String,
70    /// The original export-side schema version from the source envelope.
71    ///
72    /// Recorded for provenance; may differ from `schema_version` when the
73    /// bridge maps across versions.
74    #[serde(default)]
75    pub export_schema_version: Option<String>,
76    /// Content digest carried through from the source envelope.
77    pub content_digest: ContentDigest,
78    /// Source authority (e.g. "forge").
79    pub source_authority: String,
80    /// Target scope.
81    pub scope_key: ScopeKey,
82    /// Trace context carried through from the source.
83    pub trace_ctx: Option<TraceCtx>,
84    /// When the source envelope was exported.
85    pub source_exported_at: String,
86    /// When this batch was produced by the bridge.
87    pub transformed_at: String,
88    /// The import records, ready for memory ingestion.
89    pub records: Vec<ImportProjectionRecord>,
90}
91
92/// A projection import batch produced by the bridge for the V2 contract.
93///
94/// V2 preserves the V1 projection records and additionally carries the
95/// export-time metadata plus the canonical evidence bundle payload that must
96/// survive into import receipts.
97#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
98pub struct ProjectionImportBatchV2 {
99    /// Source envelope ID (for provenance).
100    pub source_envelope_id: EnvelopeId,
101    /// Import-side schema version this batch conforms to.
102    pub schema_version: String,
103    /// The original export-side schema version from the source envelope.
104    #[serde(default)]
105    pub export_schema_version: Option<String>,
106    /// Content digest carried through from the source envelope.
107    pub content_digest: ContentDigest,
108    /// Source authority (e.g. "forge").
109    pub source_authority: String,
110    /// Target scope.
111    pub scope_key: ScopeKey,
112    /// Trace context carried through from the source.
113    pub trace_ctx: Option<TraceCtx>,
114    /// When the source envelope was exported.
115    pub source_exported_at: String,
116    /// When this batch was produced by the bridge.
117    pub transformed_at: String,
118    /// Optional export metadata from the source envelope.
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub export_meta: Option<ForgeExportMeta>,
121    /// Optional canonical evidence bundle from the source envelope.
122    #[serde(default, skip_serializing_if = "Option::is_none")]
123    pub evidence_bundle: Option<EvidenceBundle>,
124    /// Canonical v9 episode bundle proof surface.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub episode_bundle: Option<EpisodeBundleV1>,
127    /// Canonical v9 execution context artifact.
128    #[serde(default, skip_serializing_if = "Option::is_none")]
129    pub execution_context: Option<ExecutionContextV1>,
130    /// The import records, ready for memory ingestion.
131    pub records: Vec<ImportProjectionRecord>,
132}
133
134/// Canonical kernel-oriented projection import batch produced by the bridge.
135///
136/// V3 extends V2 with rich record semantics, support algebra artifacts,
137/// contradiction witnesses, retraction lineage, and v14/v15 experimental
138/// and exchange surfaces preserved verbatim from the Forge export.
139#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
140pub struct ProjectionImportBatchV3 {
141    /// Source envelope ID (for provenance).
142    pub source_envelope_id: EnvelopeId,
143    /// Import-side schema version this batch conforms to.
144    pub schema_version: String,
145    /// The original export-side schema version from the source envelope.
146    #[serde(default)]
147    pub export_schema_version: Option<String>,
148    /// Content digest carried through from the source envelope.
149    pub content_digest: ContentDigest,
150    /// Source authority (e.g. "forge").
151    pub source_authority: String,
152    /// Target scope.
153    pub scope_key: ScopeKey,
154    /// Trace context carried through from the source.
155    pub trace_ctx: Option<TraceCtx>,
156    /// When the source envelope was exported.
157    pub source_exported_at: String,
158    /// When this batch was produced by the bridge.
159    pub transformed_at: String,
160    /// Optional export metadata from the source envelope.
161    #[serde(default, skip_serializing_if = "Option::is_none")]
162    pub export_meta: Option<ForgeExportMeta>,
163    /// Optional canonical evidence bundle from the source envelope.
164    #[serde(default, skip_serializing_if = "Option::is_none")]
165    pub evidence_bundle: Option<EvidenceBundle>,
166    /// Canonical v9 episode bundle proof surface.
167    #[serde(default, skip_serializing_if = "Option::is_none")]
168    pub episode_bundle: Option<EpisodeBundleV1>,
169    /// Canonical v9 execution context artifact.
170    #[serde(default, skip_serializing_if = "Option::is_none")]
171    pub execution_context: Option<ExecutionContextV1>,
172    /// Additive v13 support algebra artifacts preserved verbatim from Forge.
173    #[serde(default, skip_serializing_if = "Vec::is_empty")]
174    pub support_sets: Vec<SupportSetV1>,
175    /// Additive v13 contradiction witnesses preserved verbatim from Forge.
176    #[serde(default, skip_serializing_if = "Vec::is_empty")]
177    pub contradiction_witnesses: Vec<ContradictionWitnessV1>,
178    /// Additive v13 retraction lineage artifacts preserved verbatim from Forge.
179    #[serde(default, skip_serializing_if = "Vec::is_empty")]
180    pub retraction_records: Vec<RetractionRecordV1>,
181    /// Additive v13 claim-state artifacts preserved verbatim from Forge.
182    #[serde(default, skip_serializing_if = "Vec::is_empty")]
183    pub claim_states_v13: Vec<ClaimStateV13>,
184    /// Additive v14 intervention and control artifacts preserved verbatim from Forge.
185    #[serde(default, skip_serializing_if = "Vec::is_empty")]
186    pub intervention_bundles_v14: Vec<Value>,
187    /// V14 outcome schema definitions preserved verbatim from Forge.
188    #[serde(default, skip_serializing_if = "Vec::is_empty")]
189    pub outcome_schemas_v14: Vec<Value>,
190    /// V14 cohort contract artifacts preserved verbatim from Forge.
191    #[serde(default, skip_serializing_if = "Vec::is_empty")]
192    pub cohort_contracts_v14: Vec<Value>,
193    /// V14 counterfactual slice artifacts preserved verbatim from Forge.
194    #[serde(default, skip_serializing_if = "Vec::is_empty")]
195    pub counterfactual_slices_v14: Vec<Value>,
196    /// V14 experiment case artifacts preserved verbatim from Forge.
197    #[serde(default, skip_serializing_if = "Vec::is_empty")]
198    pub experiment_cases_v14: Vec<Value>,
199    /// V14 comparability matrix artifacts preserved verbatim from Forge.
200    #[serde(default, skip_serializing_if = "Vec::is_empty")]
201    pub comparability_matrices_v14: Vec<Value>,
202    /// V14 decision trace artifacts preserved verbatim from Forge.
203    #[serde(default, skip_serializing_if = "Vec::is_empty")]
204    pub decision_traces_v14: Vec<Value>,
205    /// V14 refuter suite definitions preserved verbatim from Forge.
206    #[serde(default, skip_serializing_if = "Vec::is_empty")]
207    pub refuter_suites_v14: Vec<Value>,
208    /// V14 refuter result artifacts preserved verbatim from Forge.
209    #[serde(default, skip_serializing_if = "Vec::is_empty")]
210    pub refuter_results_v14: Vec<Value>,
211    /// V14 experiment budget artifacts preserved verbatim from Forge.
212    #[serde(default, skip_serializing_if = "Vec::is_empty")]
213    pub experiment_budgets_v14: Vec<Value>,
214    /// V14 rollout decision artifacts preserved verbatim from Forge.
215    #[serde(default, skip_serializing_if = "Vec::is_empty")]
216    pub rollout_decisions_v14: Vec<Value>,
217    /// V14 rollback decision artifacts preserved verbatim from Forge.
218    #[serde(default, skip_serializing_if = "Vec::is_empty")]
219    pub rollback_decisions_v14: Vec<Value>,
220    /// Additive v15 exchange and remote-admission artifacts preserved verbatim.
221    #[serde(default, skip_serializing_if = "Vec::is_empty")]
222    pub attestation_envelopes_v15: Vec<Value>,
223    /// V15 trust root set artifacts preserved verbatim.
224    #[serde(default, skip_serializing_if = "Vec::is_empty")]
225    pub trust_root_sets_v15: Vec<Value>,
226    /// V15 artifact admission policy artifacts preserved verbatim.
227    #[serde(default, skip_serializing_if = "Vec::is_empty")]
228    pub artifact_admission_policies_v15: Vec<Value>,
229    /// V15 transparency receipt artifacts preserved verbatim.
230    #[serde(default, skip_serializing_if = "Vec::is_empty")]
231    pub transparency_receipts_v15: Vec<Value>,
232    /// V15 attestation revocation artifacts preserved verbatim.
233    #[serde(default, skip_serializing_if = "Vec::is_empty")]
234    pub attestation_revocations_v15: Vec<Value>,
235    /// V15 attestation supersession artifacts preserved verbatim.
236    #[serde(default, skip_serializing_if = "Vec::is_empty")]
237    pub attestation_supersessions_v15: Vec<Value>,
238    /// V15 remote oracle lease artifacts preserved verbatim.
239    #[serde(default, skip_serializing_if = "Vec::is_empty")]
240    pub remote_oracle_leases_v15: Vec<Value>,
241    /// V15 remote slice request artifacts preserved verbatim.
242    #[serde(default, skip_serializing_if = "Vec::is_empty")]
243    pub remote_slice_requests_v15: Vec<Value>,
244    /// V15 remote slice result artifacts preserved verbatim.
245    #[serde(default, skip_serializing_if = "Vec::is_empty")]
246    pub remote_slice_results_v15: Vec<Value>,
247    /// V15 cross-runtime replay ticket artifacts preserved verbatim.
248    #[serde(default, skip_serializing_if = "Vec::is_empty")]
249    pub cross_runtime_replay_tickets_v15: Vec<Value>,
250    /// V15 dispute bundle artifacts preserved verbatim.
251    #[serde(default, skip_serializing_if = "Vec::is_empty")]
252    pub dispute_bundles_v15: Vec<Value>,
253    /// V15 disclosure policy artifacts preserved verbatim.
254    #[serde(default, skip_serializing_if = "Vec::is_empty")]
255    pub disclosure_policies_v15: Vec<Value>,
256    /// V15 disclosure budget artifacts preserved verbatim.
257    #[serde(default, skip_serializing_if = "Vec::is_empty")]
258    pub disclosure_budgets_v15: Vec<Value>,
259    /// Rich import records preserving export-time semantics for kernel compilation.
260    pub records: Vec<ImportProjectionRecordV3>,
261}
262
263/// A single import record enriched with export-time semantics for kernel compilation.
264#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
265pub struct ImportProjectionRecordV3 {
266    pub record: ImportProjectionRecord,
267    #[serde(default, skip_serializing_if = "Option::is_none")]
268    pub semantics: Option<ExportRecordSemanticsV3>,
269}
270
271impl From<ProjectionImportBatchV1> for ProjectionImportBatchV2 {
272    fn from(value: ProjectionImportBatchV1) -> Self {
273        Self {
274            source_envelope_id: value.source_envelope_id,
275            schema_version: PROJECTION_IMPORT_BATCH_V2_SCHEMA.into(),
276            export_schema_version: value.export_schema_version,
277            content_digest: value.content_digest,
278            source_authority: value.source_authority,
279            scope_key: value.scope_key,
280            trace_ctx: value.trace_ctx,
281            source_exported_at: value.source_exported_at,
282            transformed_at: value.transformed_at,
283            export_meta: None,
284            evidence_bundle: None,
285            episode_bundle: None,
286            execution_context: None,
287            records: value.records,
288        }
289    }
290}
291
292impl From<ProjectionImportBatchV2> for ProjectionImportBatchV1 {
293    fn from(value: ProjectionImportBatchV2) -> Self {
294        Self {
295            source_envelope_id: value.source_envelope_id,
296            schema_version: PROJECTION_IMPORT_BATCH_V1_SCHEMA.into(),
297            export_schema_version: value.export_schema_version,
298            content_digest: value.content_digest,
299            source_authority: value.source_authority,
300            scope_key: value.scope_key,
301            trace_ctx: value.trace_ctx,
302            source_exported_at: value.source_exported_at,
303            transformed_at: value.transformed_at,
304            records: value.records,
305        }
306    }
307}
308
309/// A single record in a projection import batch.
310#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
311#[serde(tag = "kind", rename_all = "snake_case")]
312pub enum ImportProjectionRecord {
313    /// A claim projection version to import.
314    ClaimVersion(ImportClaimVersion),
315    /// A relation version to import.
316    RelationVersion(ImportRelationVersion),
317    /// An episode to import.
318    Episode(ImportEpisodeRecord),
319    /// An entity alias to import.
320    EntityAlias(ImportEntityAlias),
321    /// An evidence reference to import.
322    EvidenceRef(ImportEvidenceRef),
323}
324
325/// A claim projection version ready for import.
326///
327/// ## Required fields (from MASTER_SUPPORTING_DELTA §5.1 and spec)
328///
329/// All fields listed here are mandatory in the storage model. Optional
330/// fields are explicitly marked with `Option<>`.
331#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
332pub struct ImportClaimVersion {
333    /// Claim identity (stable across versions).
334    pub claim_id: ClaimId,
335    /// Version identity (unique per mutation).
336    pub claim_version_id: ClaimVersionId,
337    /// Current state of this claim version.
338    pub claim_state: ClaimState,
339    /// Projection family (e.g. "forge_verification").
340    pub projection_family: String,
341    /// The entity this claim is about.
342    pub subject_entity_id: EntityId,
343    /// The predicate.
344    pub predicate: String,
345    /// The object anchor.
346    pub object_anchor: serde_json::Value,
347    /// Target scope.
348    pub scope_key: ScopeKey,
349    /// Validity start (ISO 8601).
350    pub valid_from: Option<String>,
351    /// Validity end (ISO 8601). None = open-ended.
352    pub valid_to: Option<String>,
353    /// Whether this is the preferred open version for its logical key.
354    pub preferred_open: bool,
355    /// Source envelope provenance.
356    pub source_envelope_id: EnvelopeId,
357    /// Source authority.
358    pub source_authority: String,
359    /// Trace context.
360    pub trace_ctx: Option<TraceCtx>,
361    /// Freshness status.
362    pub freshness: ProjectionFreshness,
363    /// Contradiction status.
364    pub contradiction_status: ContradictionStatus,
365    /// Which previous version this supersedes, if any.
366    pub supersedes_claim_version_id: Option<ClaimVersionId>,
367    /// The content text for embedding/search.
368    pub content: String,
369    /// Source confidence.
370    pub confidence: f32,
371    /// Additional metadata.
372    pub metadata: Option<serde_json::Value>,
373}
374
375/// A relation version ready for import.
376///
377/// Preserves audit-grade metadata parity with claim versions
378/// (MASTER_SUPPORTING_DELTA §5.2).
379#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
380pub struct ImportRelationVersion {
381    /// Relation version identity.
382    pub relation_version_id: RelationVersionId,
383    /// The subject entity.
384    pub subject_entity_id: EntityId,
385    /// The relation predicate.
386    pub predicate: String,
387    /// The object anchor.
388    pub object_anchor: serde_json::Value,
389    /// Target scope.
390    pub scope_key: ScopeKey,
391    /// Linked claim or episode.
392    pub claim_id: Option<ClaimId>,
393    pub source_episode_id: Option<EpisodeId>,
394    /// Validity times.
395    pub valid_from: Option<String>,
396    pub valid_to: Option<String>,
397    /// Preferred open flag.
398    pub preferred_open: bool,
399    /// Which previous version this supersedes.
400    pub supersedes_relation_version_id: Option<RelationVersionId>,
401    /// Contradiction status.
402    pub contradiction_status: ContradictionStatus,
403    /// Source confidence.
404    pub source_confidence: f32,
405    /// Projection family.
406    pub projection_family: String,
407    /// Source envelope provenance.
408    pub source_envelope_id: EnvelopeId,
409    /// Source authority.
410    pub source_authority: String,
411    /// Trace context.
412    pub trace_ctx: Option<TraceCtx>,
413    /// Freshness.
414    pub freshness: ProjectionFreshness,
415    /// Additional metadata.
416    pub metadata: Option<serde_json::Value>,
417}
418
419/// An episode ready for import.
420#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
421pub struct ImportEpisodeRecord {
422    /// Episode identity.
423    pub episode_id: EpisodeId,
424    /// Linked document.
425    pub document_id: String,
426    /// Cause IDs.
427    pub cause_ids: Vec<String>,
428    /// Effect type.
429    pub effect_type: String,
430    /// Outcome.
431    pub outcome: String,
432    /// Confidence.
433    pub confidence: f32,
434    /// Experiment ID.
435    pub experiment_id: Option<String>,
436    /// Source envelope provenance.
437    pub source_envelope_id: EnvelopeId,
438    /// Source authority.
439    pub source_authority: String,
440    /// Trace context.
441    pub trace_ctx: Option<TraceCtx>,
442    /// Additional metadata.
443    pub metadata: Option<serde_json::Value>,
444}
445
446/// An entity alias ready for import.
447///
448/// Includes explicit scope semantics and durable review state
449/// (MASTER_SUPPORTING_DELTA §5.3, §5.4).
450#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
451pub struct ImportEntityAlias {
452    /// The canonical entity.
453    pub canonical_entity_id: EntityId,
454    /// Alias text.
455    pub alias_text: String,
456    /// Source of the alias.
457    pub alias_source: String,
458    /// Match evidence blob (opaque).
459    pub match_evidence: Option<serde_json::Value>,
460    /// Confidence.
461    pub confidence: f32,
462    /// Merge decision provenance.
463    pub merge_decision: MergeDecision,
464    /// Explicit scope for this alias.
465    pub scope: ScopeKey,
466    /// Review state.
467    pub review_state: ReviewState,
468    /// Whether this has been human-confirmed.
469    pub is_human_confirmed: bool,
470    /// Whether this is final (only set by human review or audited migration).
471    pub is_human_confirmed_final: bool,
472    /// If this entity was superseded by a merge.
473    pub superseded_by_entity_id: Option<EntityId>,
474    /// Split history pointer.
475    pub split_from_entity_id: Option<EntityId>,
476    /// Source envelope provenance.
477    pub source_envelope_id: EnvelopeId,
478}
479
480/// An evidence reference ready for import.
481///
482/// Evidence refs are opaque by default with explicit audit dereference only
483/// (MASTER_SUPPORTING_DELTA §5.5).
484#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
485pub struct ImportEvidenceRef {
486    /// The claim this evidence supports.
487    pub claim_id: ClaimId,
488    /// Version-local linkage.
489    pub claim_version_id: Option<ClaimVersionId>,
490    /// Canonical raw-evidence fetch handle.
491    pub fetch_handle: String,
492    /// Source authority.
493    pub source_authority: String,
494    /// Source envelope provenance.
495    pub source_envelope_id: EnvelopeId,
496    /// Additional audit metadata.
497    pub metadata: Option<serde_json::Value>,
498}
499
500/// Lifecycle state of a claim.
501#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
502#[serde(rename_all = "snake_case")]
503pub enum ClaimState {
504    /// Active and current.
505    Active,
506    /// Superseded by a newer version.
507    Superseded,
508    /// Retracted (found to be incorrect).
509    Retracted,
510    /// Archived (no longer relevant but preserved).
511    Archived,
512    /// Pending review.
513    PendingReview,
514    /// Disputed (contradicted but not resolved).
515    Disputed,
516}
517
518impl ClaimState {
519    /// Returns the stable snake_case wire label for this claim state.
520    pub fn as_str(&self) -> &'static str {
521        match self {
522            Self::Active => "active",
523            Self::Superseded => "superseded",
524            Self::Retracted => "retracted",
525            Self::Archived => "archived",
526            Self::PendingReview => "pending_review",
527            Self::Disputed => "disputed",
528        }
529    }
530}
531
532/// Projection freshness status.
533///
534/// Extended from the original enum per spec requirements.
535#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
536#[serde(rename_all = "snake_case")]
537pub enum ProjectionFreshness {
538    /// Projection was recently imported and is current.
539    Current,
540    /// Projection exists but is older than the staleness threshold.
541    Stale,
542    /// A newer version has superseded this projection's data.
543    Superseded,
544    /// The last import attempt failed.
545    ImportFailed,
546    /// No import has ever been recorded.
547    NeverImported,
548    /// Import is lagging behind the source.
549    ImportLagging,
550}
551
552impl ProjectionFreshness {
553    /// Returns the stable snake_case wire label for this freshness status.
554    pub fn as_str(&self) -> &'static str {
555        match self {
556            Self::Current => "current",
557            Self::Stale => "stale",
558            Self::Superseded => "superseded",
559            Self::ImportFailed => "import_failed",
560            Self::NeverImported => "never_imported",
561            Self::ImportLagging => "import_lagging",
562        }
563    }
564}
565
566/// Contradiction status for claims and relations.
567#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
568#[serde(rename_all = "snake_case")]
569pub enum ContradictionStatus {
570    /// No contradictions known.
571    None,
572    /// Possible contradiction detected (structural check only).
573    PossibleContradiction { description: String },
574    /// Confirmed contradiction.
575    Confirmed { contradicted_by: ClaimId },
576    /// Resolved (e.g. one claim retracted).
577    Resolved { resolution: String },
578}
579
580/// Merge decision provenance for entity aliases.
581#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
582#[serde(rename_all = "snake_case")]
583pub enum MergeDecision {
584    /// Automated merge (must not set human_confirmed_final).
585    Automated { algorithm: String },
586    /// Human-reviewed merge.
587    HumanReviewed { reviewer: String, at: String },
588    /// Pending human review.
589    PendingReview,
590    /// Rejected (not a merge).
591    Rejected { reason: String },
592}
593
594/// Durable review state for alias/merge decisions.
595///
596/// Must survive restart and be queryable (MASTER_SUPPORTING_DELTA §5.4).
597#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
598#[serde(rename_all = "snake_case")]
599pub enum ReviewState {
600    /// Not yet reviewed.
601    Unreviewed,
602    /// Awaiting human review.
603    PendingReview,
604    /// Reviewed and approved.
605    Approved { by: String, at: String },
606    /// Reviewed and rejected.
607    Rejected {
608        by: String,
609        at: String,
610        reason: String,
611    },
612}