1#![allow(deprecated)]
2
3use 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#[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";
37pub const PROJECTION_IMPORT_BATCH_V2_SCHEMA: &str = "projection_import_batch_v2";
39pub const PROJECTION_IMPORT_BATCH_V3_SCHEMA: &str = "projection_import_batch_v3";
41
42#[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 pub source_envelope_id: EnvelopeId,
64 pub schema_version: String,
70 #[serde(default)]
75 pub export_schema_version: Option<String>,
76 pub content_digest: ContentDigest,
78 pub source_authority: String,
80 pub scope_key: ScopeKey,
82 pub trace_ctx: Option<TraceCtx>,
84 pub source_exported_at: String,
86 pub transformed_at: String,
88 pub records: Vec<ImportProjectionRecord>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
98pub struct ProjectionImportBatchV2 {
99 pub source_envelope_id: EnvelopeId,
101 pub schema_version: String,
103 #[serde(default)]
105 pub export_schema_version: Option<String>,
106 pub content_digest: ContentDigest,
108 pub source_authority: String,
110 pub scope_key: ScopeKey,
112 pub trace_ctx: Option<TraceCtx>,
114 pub source_exported_at: String,
116 pub transformed_at: String,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub export_meta: Option<ForgeExportMeta>,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub evidence_bundle: Option<EvidenceBundle>,
124 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub episode_bundle: Option<EpisodeBundleV1>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub execution_context: Option<ExecutionContextV1>,
130 pub records: Vec<ImportProjectionRecord>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
140pub struct ProjectionImportBatchV3 {
141 pub source_envelope_id: EnvelopeId,
143 pub schema_version: String,
145 #[serde(default)]
147 pub export_schema_version: Option<String>,
148 pub content_digest: ContentDigest,
150 pub source_authority: String,
152 pub scope_key: ScopeKey,
154 pub trace_ctx: Option<TraceCtx>,
156 pub source_exported_at: String,
158 pub transformed_at: String,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub export_meta: Option<ForgeExportMeta>,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub evidence_bundle: Option<EvidenceBundle>,
166 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub episode_bundle: Option<EpisodeBundleV1>,
169 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub execution_context: Option<ExecutionContextV1>,
172 #[serde(default, skip_serializing_if = "Vec::is_empty")]
174 pub support_sets: Vec<SupportSetV1>,
175 #[serde(default, skip_serializing_if = "Vec::is_empty")]
177 pub contradiction_witnesses: Vec<ContradictionWitnessV1>,
178 #[serde(default, skip_serializing_if = "Vec::is_empty")]
180 pub retraction_records: Vec<RetractionRecordV1>,
181 #[serde(default, skip_serializing_if = "Vec::is_empty")]
183 pub claim_states_v13: Vec<ClaimStateV13>,
184 #[serde(default, skip_serializing_if = "Vec::is_empty")]
186 pub intervention_bundles_v14: Vec<Value>,
187 #[serde(default, skip_serializing_if = "Vec::is_empty")]
189 pub outcome_schemas_v14: Vec<Value>,
190 #[serde(default, skip_serializing_if = "Vec::is_empty")]
192 pub cohort_contracts_v14: Vec<Value>,
193 #[serde(default, skip_serializing_if = "Vec::is_empty")]
195 pub counterfactual_slices_v14: Vec<Value>,
196 #[serde(default, skip_serializing_if = "Vec::is_empty")]
198 pub experiment_cases_v14: Vec<Value>,
199 #[serde(default, skip_serializing_if = "Vec::is_empty")]
201 pub comparability_matrices_v14: Vec<Value>,
202 #[serde(default, skip_serializing_if = "Vec::is_empty")]
204 pub decision_traces_v14: Vec<Value>,
205 #[serde(default, skip_serializing_if = "Vec::is_empty")]
207 pub refuter_suites_v14: Vec<Value>,
208 #[serde(default, skip_serializing_if = "Vec::is_empty")]
210 pub refuter_results_v14: Vec<Value>,
211 #[serde(default, skip_serializing_if = "Vec::is_empty")]
213 pub experiment_budgets_v14: Vec<Value>,
214 #[serde(default, skip_serializing_if = "Vec::is_empty")]
216 pub rollout_decisions_v14: Vec<Value>,
217 #[serde(default, skip_serializing_if = "Vec::is_empty")]
219 pub rollback_decisions_v14: Vec<Value>,
220 #[serde(default, skip_serializing_if = "Vec::is_empty")]
222 pub attestation_envelopes_v15: Vec<Value>,
223 #[serde(default, skip_serializing_if = "Vec::is_empty")]
225 pub trust_root_sets_v15: Vec<Value>,
226 #[serde(default, skip_serializing_if = "Vec::is_empty")]
228 pub artifact_admission_policies_v15: Vec<Value>,
229 #[serde(default, skip_serializing_if = "Vec::is_empty")]
231 pub transparency_receipts_v15: Vec<Value>,
232 #[serde(default, skip_serializing_if = "Vec::is_empty")]
234 pub attestation_revocations_v15: Vec<Value>,
235 #[serde(default, skip_serializing_if = "Vec::is_empty")]
237 pub attestation_supersessions_v15: Vec<Value>,
238 #[serde(default, skip_serializing_if = "Vec::is_empty")]
240 pub remote_oracle_leases_v15: Vec<Value>,
241 #[serde(default, skip_serializing_if = "Vec::is_empty")]
243 pub remote_slice_requests_v15: Vec<Value>,
244 #[serde(default, skip_serializing_if = "Vec::is_empty")]
246 pub remote_slice_results_v15: Vec<Value>,
247 #[serde(default, skip_serializing_if = "Vec::is_empty")]
249 pub cross_runtime_replay_tickets_v15: Vec<Value>,
250 #[serde(default, skip_serializing_if = "Vec::is_empty")]
252 pub dispute_bundles_v15: Vec<Value>,
253 #[serde(default, skip_serializing_if = "Vec::is_empty")]
255 pub disclosure_policies_v15: Vec<Value>,
256 #[serde(default, skip_serializing_if = "Vec::is_empty")]
258 pub disclosure_budgets_v15: Vec<Value>,
259 pub records: Vec<ImportProjectionRecordV3>,
261}
262
263#[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#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
311#[serde(tag = "kind", rename_all = "snake_case")]
312pub enum ImportProjectionRecord {
313 ClaimVersion(ImportClaimVersion),
315 RelationVersion(ImportRelationVersion),
317 Episode(ImportEpisodeRecord),
319 EntityAlias(ImportEntityAlias),
321 EvidenceRef(ImportEvidenceRef),
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
332pub struct ImportClaimVersion {
333 pub claim_id: ClaimId,
335 pub claim_version_id: ClaimVersionId,
337 pub claim_state: ClaimState,
339 pub projection_family: String,
341 pub subject_entity_id: EntityId,
343 pub predicate: String,
345 pub object_anchor: serde_json::Value,
347 pub scope_key: ScopeKey,
349 pub valid_from: Option<String>,
351 pub valid_to: Option<String>,
353 pub preferred_open: bool,
355 pub source_envelope_id: EnvelopeId,
357 pub source_authority: String,
359 pub trace_ctx: Option<TraceCtx>,
361 pub freshness: ProjectionFreshness,
363 pub contradiction_status: ContradictionStatus,
365 pub supersedes_claim_version_id: Option<ClaimVersionId>,
367 pub content: String,
369 pub confidence: f32,
371 pub metadata: Option<serde_json::Value>,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
380pub struct ImportRelationVersion {
381 pub relation_version_id: RelationVersionId,
383 pub subject_entity_id: EntityId,
385 pub predicate: String,
387 pub object_anchor: serde_json::Value,
389 pub scope_key: ScopeKey,
391 pub claim_id: Option<ClaimId>,
393 pub source_episode_id: Option<EpisodeId>,
394 pub valid_from: Option<String>,
396 pub valid_to: Option<String>,
397 pub preferred_open: bool,
399 pub supersedes_relation_version_id: Option<RelationVersionId>,
401 pub contradiction_status: ContradictionStatus,
403 pub source_confidence: f32,
405 pub projection_family: String,
407 pub source_envelope_id: EnvelopeId,
409 pub source_authority: String,
411 pub trace_ctx: Option<TraceCtx>,
413 pub freshness: ProjectionFreshness,
415 pub metadata: Option<serde_json::Value>,
417}
418
419#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
421pub struct ImportEpisodeRecord {
422 pub episode_id: EpisodeId,
424 pub document_id: String,
426 pub cause_ids: Vec<String>,
428 pub effect_type: String,
430 pub outcome: String,
432 pub confidence: f32,
434 pub experiment_id: Option<String>,
436 pub source_envelope_id: EnvelopeId,
438 pub source_authority: String,
440 pub trace_ctx: Option<TraceCtx>,
442 pub metadata: Option<serde_json::Value>,
444}
445
446#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
451pub struct ImportEntityAlias {
452 pub canonical_entity_id: EntityId,
454 pub alias_text: String,
456 pub alias_source: String,
458 pub match_evidence: Option<serde_json::Value>,
460 pub confidence: f32,
462 pub merge_decision: MergeDecision,
464 pub scope: ScopeKey,
466 pub review_state: ReviewState,
468 pub is_human_confirmed: bool,
470 pub is_human_confirmed_final: bool,
472 pub superseded_by_entity_id: Option<EntityId>,
474 pub split_from_entity_id: Option<EntityId>,
476 pub source_envelope_id: EnvelopeId,
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
485pub struct ImportEvidenceRef {
486 pub claim_id: ClaimId,
488 pub claim_version_id: Option<ClaimVersionId>,
490 pub fetch_handle: String,
492 pub source_authority: String,
494 pub source_envelope_id: EnvelopeId,
496 pub metadata: Option<serde_json::Value>,
498}
499
500#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
502#[serde(rename_all = "snake_case")]
503pub enum ClaimState {
504 Active,
506 Superseded,
508 Retracted,
510 Archived,
512 PendingReview,
514 Disputed,
516}
517
518impl ClaimState {
519 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
536#[serde(rename_all = "snake_case")]
537pub enum ProjectionFreshness {
538 Current,
540 Stale,
542 Superseded,
544 ImportFailed,
546 NeverImported,
548 ImportLagging,
550}
551
552impl ProjectionFreshness {
553 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
568#[serde(rename_all = "snake_case")]
569pub enum ContradictionStatus {
570 None,
572 PossibleContradiction { description: String },
574 Confirmed { contradicted_by: ClaimId },
576 Resolved { resolution: String },
578}
579
580#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
582#[serde(rename_all = "snake_case")]
583pub enum MergeDecision {
584 Automated { algorithm: String },
586 HumanReviewed { reviewer: String, at: String },
588 PendingReview,
590 Rejected { reason: String },
592}
593
594#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
598#[serde(rename_all = "snake_case")]
599pub enum ReviewState {
600 Unreviewed,
602 PendingReview,
604 Approved { by: String, at: String },
606 Rejected {
608 by: String,
609 at: String,
610 reason: String,
611 },
612}