1#![cfg_attr(test, allow(clippy::expect_used))]
14
15use serde::{Deserialize, Serialize};
16
17pub const DAGDB_INTAKE_RESPONSE_SCHEMA_VERSION: &str = "dagdb_intake_response_v1";
19pub const DAGDB_ROUTE_RESPONSE_SCHEMA_VERSION: &str = "dagdb_route_response_v1";
21pub const DAGDB_CONTEXT_PACKET_RESPONSE_SCHEMA_VERSION: &str = "dagdb_context_packet_response_v1";
23pub const DAGDB_VALIDATE_RESPONSE_SCHEMA_VERSION: &str = "dagdb_validate_response_v1";
25pub const DAGDB_WRITEBACK_RESPONSE_SCHEMA_VERSION: &str = "dagdb_writeback_response_v1";
27pub const DAGDB_IMPORT_RESPONSE_SCHEMA_VERSION: &str = "dagdb_import_response_v1";
29pub const DAGDB_EXPORT_RESPONSE_SCHEMA_VERSION: &str = "dagdb_export_response_v1";
31pub const DAGDB_TRUST_CHECK_RESPONSE_SCHEMA_VERSION: &str = "dagdb_trust_check_response_v1";
33pub const DAGDB_COUNCIL_DECISION_RESPONSE_SCHEMA_VERSION: &str =
35 "dagdb_council_decision_response_v1";
36pub const DAGDB_RECEIPT_LOOKUP_RESPONSE_SCHEMA_VERSION: &str = "dagdb_receipt_lookup_response_v1";
38pub const DAGDB_CATALOG_LOOKUP_RESPONSE_SCHEMA_VERSION: &str = "dagdb_catalog_lookup_response_v1";
40pub const DAGDB_ROUTE_LOOKUP_RESPONSE_SCHEMA_VERSION: &str = "dagdb_route_lookup_response_v1";
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
45#[serde(rename_all = "snake_case")]
46pub enum SafeMetadataDecision {
47 Allow,
49 Redact,
51 Reject,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
57#[serde(rename_all = "snake_case")]
58pub enum RedactionCode {
59 Ssn,
61 Card,
63 ConfidentialMarker,
65 Phi,
67 CustomerPrivate,
69 CodeExcerpt,
71 LengthTruncation,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(deny_unknown_fields)]
78pub struct SafeMetadata {
79 pub decision: SafeMetadataDecision,
80 pub text: String,
81 pub redaction_codes: Vec<RedactionCode>,
82 pub original_hash: String,
83 pub truncated: bool,
84 pub byte_len: u32,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub enum MemoryNodeType {
91 Source,
92 Excerpt,
93 Embedding,
94 Summary,
95 Answer,
96 ValidationReport,
97 Catalog,
98 Route,
99 ContextPacket,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
104#[serde(rename_all = "snake_case")]
105pub enum SourceType {
106 PublicWeb,
107 PrivateCustomer,
108 IpSensitive,
109 Generated,
110 OpenSource,
111 UnknownProvenance,
112 BenchmarkFixture,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
117#[serde(rename_all = "snake_case")]
118pub enum ConsentPurpose {
119 Retrieval,
120 Validation,
121 Writeback,
122 Import,
123 Export,
124 Benchmark,
125 TrustCheck,
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
130#[serde(rename_all = "snake_case")]
131pub enum MemoryStatus {
132 Pending,
133 Approved,
134 Routable,
135 Blocked,
136 Revoked,
137 Superseded,
138 Rejected,
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
143#[serde(rename_all = "snake_case")]
144pub enum ValidationStatus {
145 NotRequired,
146 Pending,
147 Passed,
148 Failed,
149 Contradictory,
150 Expired,
151 NeedsCouncil,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
156#[serde(rename_all = "snake_case")]
157pub enum RouteStatus {
158 Pending,
159 Active,
160 Stale,
161 Invalidated,
162 Blocked,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
167#[serde(rename_all = "snake_case")]
168pub enum CouncilReviewStatus {
169 NotRequired,
170 Required,
171 Pending,
172 Approved,
173 Denied,
174 Expired,
175 Escalated,
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
180#[serde(rename_all = "snake_case")]
181pub enum CouncilDecisionStatus {
182 Approved,
183 Denied,
184 Expired,
185 Escalated,
186 Revoked,
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
191#[serde(rename_all = "snake_case")]
192pub enum DagFinalityStatus {
193 Pending,
194 Committed,
195 Failed,
196 Compensated,
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
201pub enum RiskClass {
202 R0,
203 R1,
204 R2,
205 R3,
206 R4,
207 R5,
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
212#[serde(rename_all = "snake_case")]
213pub enum SubjectKind {
214 Memory,
215 Catalog,
216 Route,
217 ContextPacket,
218 ValidationReport,
219 AgentSafetyScore,
220 InboundAgentCredential,
221 CouncilDecision,
222}
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
226#[serde(rename_all = "snake_case")]
227pub enum ReceiptEventType {
228 IntakeCreated,
229 DuplicateRejected,
230 ValidationCreated,
231 ValidationPassed,
232 ValidationFailed,
233 MemoryApproved,
234 MemoryRoutable,
235 MemoryRevoked,
236 MemorySuperseded,
237 RouteCreated,
238 RouteActivated,
239 RouteStale,
240 RouteInvalidated,
241 ContextPacketCreated,
242 WritebackCreated,
243 TrustCheckCreated,
244 CouncilDecisionRecorded,
245 DagFinalityCommitted,
246 DagFinalityFailed,
247 DagFinalityCompensated,
248 DagdbApprovalRequestSubmitted,
249 DagdbApprovalGranted,
250 DagdbApprovalDenied,
251 DagdbRecordAccepted,
252 DagdbImportCompleted,
253 DagdbExportCompleted,
254 DagdbReplayDetected,
255 DagdbIdempotencyConflict,
256 DagdbRlsTenantViolation,
257 DagdbSignatureFailure,
258 DagdbCouncilOperatorDecision,
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
263#[serde(rename_all = "snake_case")]
264pub enum DecisionSource {
265 Human,
266 Council,
267 Policy,
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
272#[serde(rename_all = "snake_case")]
273pub enum MemoryEdgeType {
274 Parent,
275 DerivedFrom,
276 Cites,
277 Contradicts,
278 Supersedes,
279 Validates,
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
284#[serde(rename_all = "snake_case")]
285pub enum ValidationDecision {
286 Allow,
287 Block,
288 NeedsCouncil,
289 Invalidate,
290 Revoke,
291 Supersede,
292}
293
294#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
296#[serde(rename_all = "snake_case")]
297pub enum CredentialStatus {
298 Pending,
299 Active,
300 Expired,
301 Revoked,
302 Blocked,
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
307#[serde(rename_all = "snake_case")]
308pub enum MemoryGraphStyle {
309 ProvenanceReceiptDag,
310 CanonicalMemoryGraph,
311 SemanticCatalogGraph,
312 SimilarityOverlayGraph,
313 DependencyDag,
314 RoutingViewGraph,
315 ContradictionSupersessionGraph,
316 ContextPacketGraph,
317}
318
319#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
321#[serde(rename_all = "snake_case")]
322pub enum MemoryNodeKind {
323 Raw,
324 Chunk,
325 Summary,
326 Concept,
327 Canonical,
328 DuplicateReference,
329 Related,
330 Replacement,
331 Contradiction,
332 Supersession,
333 AlternateSummary,
334 Decision,
335 Route,
336 ValidationReport,
337 SavingsReport,
338}
339
340#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
342#[serde(rename_all = "snake_case")]
343pub enum MemoryEdgeKind {
344 DerivedFrom,
345 Summarizes,
346 Supports,
347 Contradicts,
348 Supersedes,
349 Replaces,
350 DuplicateOf,
351 NearDuplicateOf,
352 RelatedTo,
353 AlternativeSummaryOf,
354 DependsOn,
355 PartOf,
356 OwnedBy,
357 AccessGrantedBy,
358 VerifiedBy,
359 UsedByRoute,
360 IncludedInContextPacket,
361 RevokedBy,
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
366#[serde(rename_all = "snake_case")]
367pub enum MemoryCandidateKind {
368 Decision,
369 Summary,
370 Plan,
371 Schema,
372 RouteFeedback,
373 Contradiction,
374 Preference,
375 SavingsObservation,
376}
377
378#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
380#[serde(rename_all = "snake_case")]
381pub enum MemoryCandidateUse {
382 Inference,
383 Routing,
384 Audit,
385}
386
387#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
389#[serde(deny_unknown_fields)]
390pub struct MemoryCandidate {
391 #[serde(rename = "type")]
392 pub candidate_type: String,
393 pub source_request_id: String,
394 pub candidate_kind: MemoryCandidateKind,
395 pub summary: String,
396 pub full_output_hash: String,
397 pub parent_context_packet_id: String,
398 pub evidence_receipts: Vec<String>,
399 pub risk_hint: RiskClass,
400 pub allowed_future_uses: Vec<MemoryCandidateUse>,
401 pub reason_to_remember: String,
402}
403
404#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
406#[serde(rename_all = "snake_case")]
407pub enum SimilarityType {
408 ExactHash,
409 NearDuplicate,
410 ConceptOverlap,
411 WeakRelated,
412}
413
414#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
416#[serde(deny_unknown_fields)]
417pub struct SimilarityResult {
418 pub candidate_memory_id: String,
419 pub similarity_type: SimilarityType,
420 pub similarity_bp: u16,
421 pub matched_fields: Vec<String>,
422 pub reason: String,
423}
424
425#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
427#[serde(rename_all = "snake_case")]
428pub enum CanonicalizationDecisionKind {
429 NewCanonical,
430 ExactDuplicate,
431 NearDuplicate,
432 Related,
433 Replacement,
434 Contradiction,
435 Supersession,
436 AlternateSummary,
437 RejectedNeedsReview,
438}
439
440#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
442#[serde(deny_unknown_fields)]
443pub struct GraphEdgeRef {
444 pub from_memory_id: String,
445 pub to_memory_id: String,
446 pub edge_kind: MemoryEdgeKind,
447}
448
449#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
451#[serde(deny_unknown_fields)]
452pub struct CanonicalizationDecision {
453 pub decision_id: String,
454 pub input_memory_id: String,
455 pub canonical_memory_id: Option<String>,
456 pub matched_memory_ids: Vec<String>,
457 pub decision_kind: CanonicalizationDecisionKind,
458 pub decision_reason: String,
459 pub confidence_bp: u16,
460 pub risk_class: RiskClass,
461 pub validator_status: ValidationStatus,
462 pub required_edges_to_create: Vec<GraphEdgeRef>,
463 pub receipt_intent: String,
464 pub receipt_id: Option<String>,
465}
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
469#[serde(rename_all = "snake_case")]
470pub enum GraphViewType {
471 FullProvenance,
472 RoutingView,
473 CanonicalView,
474 DependencyView,
475 ContradictionView,
476 ContextPacketView,
477}
478
479#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
481#[serde(deny_unknown_fields)]
482pub struct GraphView {
483 pub view_id: String,
484 pub graph_style: MemoryGraphStyle,
485 pub source_root_id: String,
486 pub included_node_ids: Vec<String>,
487 pub included_edge_ids: Vec<String>,
488 pub view_type: GraphViewType,
489 pub topological_order: Vec<String>,
490 pub transitive_reduction_edges: Vec<GraphEdgeRef>,
491 pub omitted_edges: Vec<GraphEdgeRef>,
492 pub reason_edges_omitted: Vec<String>,
493}
494
495#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
497#[serde(rename_all = "snake_case")]
498pub enum RouteInvalidationTrigger {
499 Revoked,
500 Superseded,
501 Contradicted,
502 Replaced,
503 PermissionChanged,
504 RiskChanged,
505}
506
507#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
509#[serde(rename_all = "snake_case")]
510pub enum RouteInvalidationStatus {
511 Stale,
512 Invalidated,
513 NeedsReview,
514 Superseded,
515}
516
517#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
519#[serde(deny_unknown_fields)]
520pub struct RouteInvalidationReceipt {
521 pub route_id: String,
522 pub affected_memory_ids: Vec<String>,
523 pub trigger_type: RouteInvalidationTrigger,
524 pub triggering_receipt_id: String,
525 pub prior_route_status: RouteStatus,
526 pub new_route_status: RouteInvalidationStatus,
527 pub invalidation_reason: String,
528 pub created_at: String,
529 pub validator_id: Option<String>,
530 pub validation_report_id: Option<String>,
531 pub receipt_intent: String,
532 pub receipt_id: Option<String>,
533}
534
535#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
537#[serde(deny_unknown_fields)]
538pub struct PlacementResult {
539 pub input_memory_id: String,
540 pub canonicalization_decision: CanonicalizationDecision,
541 pub similarity_results: Vec<SimilarityResult>,
542 pub proposed_canonical_node: Option<String>,
543 pub edges_to_create: Vec<GraphEdgeRef>,
544 pub catalog_updates: Vec<String>,
545 pub graph_views_to_refresh: Vec<MemoryGraphStyle>,
546 pub route_invalidations: Vec<RouteInvalidationReceipt>,
547 pub validator_report: String,
548 pub receipt_id: Option<String>,
549 pub receipt_intent: Option<String>,
550}
551
552#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
554#[serde(deny_unknown_fields)]
555pub struct DagDbErrorEnvelope {
556 pub error_code: String,
557 pub message: String,
558 pub receipt_hash: Option<String>,
559 pub validation_report_id: Option<String>,
560 pub requires_council_review: bool,
561 #[serde(default, skip_serializing_if = "Option::is_none")]
562 pub operational_event_type: Option<String>,
563}
564
565#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
567#[serde(deny_unknown_fields)]
568pub struct DagDbIntakeRequest {
569 pub tenant_id: String,
570 pub namespace: String,
571 pub idempotency_key: String,
572 pub source_type: SourceType,
573 pub source_hash: String,
574 pub payload_hash: String,
575 pub owner_did: String,
576 pub controller_did: String,
577 pub submitted_by_did: String,
578 pub consent_purpose: ConsentPurpose,
579 pub requested_action: String,
580 pub title_text: String,
581 pub summary_text: String,
582 pub payload_uri_hash: Option<String>,
583 pub parent_memory_ids: Option<Vec<String>>,
584 pub edge_types: Option<Vec<MemoryEdgeType>>,
585 pub access_policy_hash: Option<String>,
586 pub declared_rights_hash: Option<String>,
587 pub keyword_texts: Option<Vec<String>>,
588}
589
590#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
592#[serde(deny_unknown_fields)]
593pub struct DagDbRouteRequest {
594 pub tenant_id: String,
595 pub namespace: String,
596 pub idempotency_key: String,
597 pub requesting_agent_did: String,
598 pub task_signature_hash: String,
599 pub approved_scope_hash: String,
600 pub token_budget: u32,
601 pub start_catalog_id: Option<String>,
602 pub requested_memory_ids: Option<Vec<String>>,
603 pub credential_id: Option<String>,
604}
605
606#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
608#[serde(deny_unknown_fields)]
609pub struct DagDbContextPacketRequest {
610 pub tenant_id: String,
611 pub namespace: String,
612 pub idempotency_key: String,
613 pub request_id: String,
614 pub route_id: String,
615 pub task_hash: String,
616 pub requesting_agent_did: String,
617 pub token_budget: u32,
618 pub force_revalidate: Option<bool>,
619 pub max_memory_refs: Option<u32>,
620 #[serde(default, skip_serializing_if = "Option::is_none")]
621 pub task: Option<String>,
622 #[serde(default, skip_serializing_if = "Option::is_none")]
623 pub layered_mode: Option<String>,
624 #[serde(default, skip_serializing_if = "Option::is_none")]
625 pub max_layer_depth: Option<u32>,
626 #[serde(default, skip_serializing_if = "Option::is_none")]
627 pub require_layer_evidence: Option<bool>,
628 #[serde(default, skip_serializing_if = "Option::is_none")]
635 pub drilldown_reserve_bp: Option<u32>,
636}
637
638#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
640#[serde(deny_unknown_fields)]
641pub struct DagDbValidateRequest {
642 pub tenant_id: String,
643 pub namespace: String,
644 pub idempotency_key: String,
645 pub subject_kind: SubjectKind,
646 pub subject_id: String,
647 pub validator_did: String,
648 pub requested_status: Option<ValidationStatus>,
649 pub council_decision_id: Option<String>,
650 pub validation_notes_text: Option<String>,
651}
652
653#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
655#[serde(deny_unknown_fields)]
656pub struct DagDbWritebackRequest {
657 pub tenant_id: String,
658 pub namespace: String,
659 pub idempotency_key: String,
660 pub requesting_agent_did: String,
661 pub parent_memory_ids: Vec<String>,
662 pub answer_hash: String,
663 pub route_id: String,
664 pub context_packet_id: String,
665 pub validation_report_id: String,
666 pub summary_text: Option<String>,
667 pub citation_hashes: Option<Vec<String>>,
668 pub safety_score_id: Option<String>,
669 pub keyword_texts: Option<Vec<String>>,
670 #[serde(default, skip_serializing_if = "Option::is_none")]
676 pub knowledge_class: Option<String>,
677 #[serde(default, skip_serializing_if = "Option::is_none")]
678 pub layered_mode: Option<String>,
679 #[serde(default, skip_serializing_if = "Option::is_none")]
680 pub target_layer_path: Option<String>,
681 #[serde(default, skip_serializing_if = "Option::is_none")]
682 pub target_layer_depth: Option<u32>,
683 #[serde(default, skip_serializing_if = "Option::is_none")]
684 pub target_layer_reason: Option<String>,
685}
686
687#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
689#[serde(deny_unknown_fields)]
690pub struct DagDbImportRequest {
691 pub tenant_id: String,
692 pub namespace: String,
693 pub idempotency_key: String,
694 pub db_set_version: String,
695 pub source_hash: String,
696 pub requester_did: String,
697 pub import_report: serde_json::Value,
698}
699
700#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
702#[serde(deny_unknown_fields)]
703pub struct DagDbExportRequest {
704 pub tenant_id: String,
705 pub namespace: String,
706 pub idempotency_key: String,
707 pub db_set_version: String,
708 pub requester_did: String,
709 pub included_memory_ids: Vec<String>,
710 pub included_graph_styles: Vec<String>,
711 pub included_writeback_idempotency_keys: Vec<String>,
712 pub source_commit_or_repo_ref: Option<String>,
713 pub include_preview_context: bool,
714}
715
716#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
718#[serde(deny_unknown_fields)]
719pub struct DagDbTrustCheckRequest {
720 pub tenant_id: String,
721 pub namespace: String,
722 pub idempotency_key: String,
723 pub agent_did: String,
724 pub operator_did: String,
725 pub model_name: String,
726 pub model_version: String,
727 pub provider_or_builder: String,
728 pub requested_action: String,
729 pub requested_scope_hash: String,
730 pub purpose: ConsentPurpose,
731 pub autonomy_level: String,
732 pub nonce: String,
733 pub expires_at: String,
734 pub signature: String,
735 pub checkpoint_hash: Option<String>,
736 pub attestation_hash: Option<String>,
737 pub evidence_receipt_hashes: Option<Vec<String>>,
738 pub prior_trust_receipt_hash: Option<String>,
739}
740
741#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
743#[serde(deny_unknown_fields)]
744pub struct DagDbCouncilDecisionRequest {
745 pub tenant_id: String,
746 pub namespace: String,
747 pub idempotency_key: String,
748 pub subject_kind: SubjectKind,
749 pub subject_id: String,
750 pub requested_action: String,
751 pub approved_scope_hash: String,
752 pub risk_class: RiskClass,
753 pub approver_did: String,
754 pub decision_source: DecisionSource,
755 pub decision_status: CouncilDecisionStatus,
756 pub reason_code: String,
757 pub created_at: String,
758 pub expires_at: String,
759 pub validation_report_id: Option<String>,
760 pub route_id: Option<String>,
761 pub context_packet_id: Option<String>,
762 pub notes_text: Option<String>,
763}
764
765#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
767#[serde(deny_unknown_fields)]
768pub struct DagDbReceiptLookupRequest {
769 pub receipt_hash: String,
770 pub tenant_id: String,
771 pub namespace: String,
772 pub include_body: Option<bool>,
773}
774
775#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
777#[serde(deny_unknown_fields)]
778pub struct DagDbCatalogLookupRequest {
779 pub catalog_id: String,
780 pub tenant_id: String,
781 pub namespace: String,
782 pub include_children: Option<bool>,
783 pub include_routes: Option<bool>,
784}
785
786#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
788#[serde(deny_unknown_fields)]
789pub struct DagDbRouteLookupRequest {
790 pub route_id: String,
791 pub tenant_id: String,
792 pub namespace: String,
793 pub include_memory_refs: Option<bool>,
794 pub include_validation: Option<bool>,
795}
796
797#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
799#[serde(deny_unknown_fields)]
800pub struct DagDbIntakeResponse {
801 pub schema_version: String,
803 pub tenant_id: String,
804 pub namespace: String,
805 pub idempotency_key: String,
806 pub memory_id: String,
807 pub receipt_hash: String,
808 pub validation_status: ValidationStatus,
809 pub council_status: CouncilReviewStatus,
810 pub dag_finality_status: DagFinalityStatus,
811 pub risk_class: RiskClass,
812 pub risk_bp: u16,
813 pub created_new: bool,
814 pub title: SafeMetadata,
815 pub summary: SafeMetadata,
816 pub keywords: Vec<SafeMetadata>,
817 pub validation_report_id: Option<String>,
818 pub council_decision_id: Option<String>,
819 pub duplicate_of_memory_id: Option<String>,
820}
821
822#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
824#[serde(deny_unknown_fields)]
825pub struct DagDbRouteResponse {
826 pub schema_version: String,
828 pub tenant_id: String,
829 pub namespace: String,
830 pub idempotency_key: String,
831 pub route_id: String,
832 pub receipt_hash: String,
833 pub validation_status: ValidationStatus,
834 pub council_status: CouncilReviewStatus,
835 pub route_status: RouteStatus,
836 pub dag_finality_status: DagFinalityStatus,
837 pub selected_memory_ids: Vec<String>,
838 pub route_score_bp: u16,
839 pub token_budget: u32,
840 pub token_estimate: u32,
841 pub stale_at: String,
842 pub created_new: bool,
843 pub validation_report_id: Option<String>,
844 pub council_decision_id: Option<String>,
845 pub rejected_memory_ids: Option<Vec<String>>,
846}
847
848#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
850#[serde(deny_unknown_fields)]
851pub struct ContextPacketMemoryRef {
852 pub memory_id: String,
853 pub title: SafeMetadata,
854 pub summary: SafeMetadata,
855 pub keywords: Vec<SafeMetadata>,
856 pub latest_receipt_hash: String,
857}
858
859#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
861#[serde(deny_unknown_fields)]
862pub struct ContextPacketLayerRef {
863 pub layer_id: String,
864 pub layer_path: String,
865 pub layer_depth: u32,
866 pub layer_kind: String,
867 pub selected_ref_count: u32,
868}
869
870#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
872#[serde(deny_unknown_fields)]
873pub struct ContextPacketLayerEdgeRef {
874 pub layer_edge_id: String,
875 pub from_layer_id: String,
876 pub to_layer_id: String,
877 pub edge_kind: String,
878}
879
880#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
882#[serde(deny_unknown_fields)]
883pub struct ContextPacketLayerBudgetReport {
884 pub layered_mode: String,
885 pub max_layer_depth: u32,
886 pub required_layer_evidence: bool,
887 pub budget_status: String,
888}
889
890#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
892#[serde(deny_unknown_fields)]
893pub struct DagDbContextPacketResponse {
894 pub schema_version: String,
896 pub tenant_id: String,
897 pub namespace: String,
898 pub idempotency_key: String,
899 pub context_packet_id: String,
900 pub route_id: String,
901 pub receipt_hash: String,
902 pub validation_status: ValidationStatus,
903 pub council_status: CouncilReviewStatus,
904 pub dag_finality_status: DagFinalityStatus,
905 pub memory_refs: Vec<ContextPacketMemoryRef>,
906 pub packet_hash: String,
907 pub token_budget: u32,
908 pub token_estimate: u32,
909 pub created_new: bool,
910 pub validation_report_id: Option<String>,
911 pub council_decision_id: Option<String>,
912 #[serde(default, skip_serializing_if = "Option::is_none")]
913 pub context_packet_mode: Option<String>,
914 #[serde(default, skip_serializing_if = "Option::is_none")]
915 pub selection_warning: Option<String>,
916 #[serde(default, skip_serializing_if = "Option::is_none")]
917 pub layered_mode: Option<String>,
918 #[serde(default, skip_serializing_if = "Option::is_none")]
919 pub selected_layers: Option<Vec<ContextPacketLayerRef>>,
920 #[serde(default, skip_serializing_if = "Option::is_none")]
921 pub selected_layer_edges: Option<Vec<ContextPacketLayerEdgeRef>>,
922 #[serde(default, skip_serializing_if = "Option::is_none")]
923 pub layer_budget_report: Option<ContextPacketLayerBudgetReport>,
924 #[serde(default, skip_serializing_if = "Option::is_none")]
925 pub flat_fallback_used: Option<bool>,
926 #[serde(default, skip_serializing_if = "Option::is_none")]
927 pub layered_status: Option<String>,
928 #[serde(default, skip_serializing_if = "Vec::is_empty")]
931 pub selected_graph_edges: Vec<DagDbSelectedGraphEdgeRef>,
932 #[serde(default, skip_serializing_if = "Vec::is_empty")]
935 pub citation_refs: Vec<DagDbContextPacketCitationRef>,
936 #[serde(default, skip_serializing_if = "Option::is_none")]
938 pub packet_metrics: Option<DagDbContextPacketMetrics>,
939 #[serde(default, skip_serializing_if = "Option::is_none")]
941 pub boundaries: Option<DagDbContextPacketBoundaries>,
942 #[serde(default, skip_serializing_if = "Option::is_none")]
944 pub packet_markdown: Option<String>,
945}
946
947#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
949#[serde(deny_unknown_fields)]
950pub struct DagDbValidateResponse {
951 pub schema_version: String,
953 pub tenant_id: String,
954 pub namespace: String,
955 pub idempotency_key: String,
956 pub validation_report_id: String,
957 pub subject_kind: SubjectKind,
958 pub subject_id: String,
959 pub receipt_hash: String,
960 pub validation_status: ValidationStatus,
961 pub council_status: CouncilReviewStatus,
962 pub risk_class: RiskClass,
963 pub risk_bp: u16,
964 pub decision: ValidationDecision,
965 pub created_new: bool,
966 pub council_decision_id: Option<String>,
967 pub contradictory_report_ids: Option<Vec<String>>,
968 pub notes: Option<SafeMetadata>,
969}
970
971#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
973#[serde(deny_unknown_fields)]
974pub struct DagDbWritebackResponse {
975 pub schema_version: String,
977 pub tenant_id: String,
978 pub namespace: String,
979 pub idempotency_key: String,
980 pub memory_id: String,
981 pub receipt_hash: String,
982 pub validation_status: ValidationStatus,
983 pub council_status: CouncilReviewStatus,
984 pub dag_finality_status: DagFinalityStatus,
985 pub risk_class: RiskClass,
986 pub risk_bp: u16,
987 pub created_new: bool,
988 pub validation_report_id: Option<String>,
989 pub council_decision_id: Option<String>,
990 pub summary: Option<SafeMetadata>,
991 pub keywords: Option<Vec<SafeMetadata>>,
992 #[serde(default, skip_serializing_if = "Option::is_none")]
993 pub target_layer_path: Option<String>,
994 #[serde(default, skip_serializing_if = "Option::is_none")]
995 pub target_layer_depth: Option<u32>,
996 #[serde(default, skip_serializing_if = "Option::is_none")]
997 pub target_layer_reason: Option<String>,
998 #[serde(default, skip_serializing_if = "Option::is_none")]
999 pub created_child_layer_id: Option<String>,
1000 #[serde(default, skip_serializing_if = "Option::is_none")]
1001 pub layered_writeback_status: Option<String>,
1002}
1003
1004#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1006#[serde(deny_unknown_fields)]
1007pub struct DagDbImportResponse {
1008 pub schema_version: String,
1010 pub operation_id: String,
1011 pub tenant_id: String,
1012 pub namespace: String,
1013 pub idempotency_key: String,
1014 pub db_set_version: String,
1015 pub import_status: String,
1016 pub import_receipt_id: Option<String>,
1017 pub source_hash: String,
1018 pub imported_record_count: u32,
1019 pub receipt_path: Option<String>,
1020 pub non_claims: Vec<String>,
1021 #[serde(default, skip_serializing_if = "Option::is_none")]
1022 pub idempotency_status: Option<String>,
1023}
1024
1025#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1027#[serde(deny_unknown_fields)]
1028pub struct DagDbExportResponse {
1029 pub schema_version: String,
1031 pub operation_id: String,
1032 pub tenant_id: String,
1033 pub namespace: String,
1034 pub idempotency_key: String,
1035 pub db_set_version: String,
1036 pub export_status: String,
1037 pub export_artifact_id: Option<String>,
1038 pub export_hash: Option<String>,
1039 pub exported_record_count: u32,
1040 pub report_path: Option<String>,
1041 pub non_claims: Vec<String>,
1042 #[serde(default, skip_serializing_if = "Option::is_none")]
1043 pub idempotency_status: Option<String>,
1044}
1045
1046#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1048#[serde(deny_unknown_fields)]
1049pub struct DagDbTrustCheckResponse {
1050 pub schema_version: String,
1052 pub tenant_id: String,
1053 pub namespace: String,
1054 pub idempotency_key: String,
1055 pub credential_id: String,
1056 pub safety_score_id: String,
1057 pub receipt_hash: String,
1058 pub validation_status: ValidationStatus,
1059 pub council_status: CouncilReviewStatus,
1060 pub credential_status: CredentialStatus,
1061 pub total_score_bp: u16,
1062 pub created_new: bool,
1063 pub block_reason: Option<String>,
1064 pub expires_at: Option<String>,
1065}
1066
1067#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1069#[serde(deny_unknown_fields)]
1070pub struct DagDbCouncilDecisionResponse {
1071 pub schema_version: String,
1073 pub tenant_id: String,
1074 pub namespace: String,
1075 pub idempotency_key: String,
1076 pub decision_id: String,
1077 pub subject_kind: SubjectKind,
1078 pub subject_id: String,
1079 pub receipt_hash: String,
1080 pub validation_status: ValidationStatus,
1081 pub council_status: CouncilReviewStatus,
1082 pub decision_status: CouncilDecisionStatus,
1083 pub approved_scope_hash: String,
1084 pub risk_class: RiskClass,
1085 pub expires_at: String,
1086 pub created_new: bool,
1087 pub validation_report_id: Option<String>,
1088 pub route_id: Option<String>,
1089 pub context_packet_id: Option<String>,
1090 pub notes: Option<SafeMetadata>,
1091}
1092
1093#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1095#[serde(deny_unknown_fields)]
1096pub struct DagDbReceiptLookupResponse {
1097 pub schema_version: String,
1099 pub tenant_id: String,
1100 pub namespace: String,
1101 pub receipt_hash: String,
1102 pub subject_kind: SubjectKind,
1103 pub subject_id: String,
1104 pub prev_receipt_hash: String,
1105 pub seq: u64,
1106 pub event_type: ReceiptEventType,
1107 pub actor_did: String,
1108 pub event_hlc: String,
1109 pub created_at: String,
1110 pub receipt_body: Option<serde_json::Value>,
1111 pub validation_report_id: Option<String>,
1112}
1113
1114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1116#[serde(deny_unknown_fields)]
1117pub struct CatalogEntryResponse {
1118 pub catalog_id: String,
1119 pub title: SafeMetadata,
1120 pub summary: SafeMetadata,
1121}
1122
1123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1125#[serde(deny_unknown_fields)]
1126pub struct DagDbCatalogLookupResponse {
1127 pub schema_version: String,
1129 pub tenant_id: String,
1130 pub namespace: String,
1131 pub catalog_id: String,
1132 pub catalog_level: u32,
1133 pub title: SafeMetadata,
1134 pub summary: SafeMetadata,
1135 pub keywords: Vec<SafeMetadata>,
1136 pub status: MemoryStatus,
1137 pub validation_status: ValidationStatus,
1138 pub council_status: CouncilReviewStatus,
1139 pub dag_finality_status: DagFinalityStatus,
1140 pub latest_receipt_hash: String,
1141 pub memory_id: Option<String>,
1142 pub parent_catalog_id: Option<String>,
1143 pub children: Option<Vec<CatalogEntryResponse>>,
1144 pub routes: Option<Vec<String>>,
1145}
1146
1147#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1149#[serde(rename_all = "snake_case")]
1150pub enum DagDbGraphContextSelectionStatus {
1151 Selected,
1152 Empty,
1153 Failed,
1154}
1155
1156#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1158#[serde(deny_unknown_fields)]
1159pub struct DagDbGraphContextSelectionRequest {
1160 pub tenant_id: String,
1161 pub namespace: String,
1162 pub request_id: String,
1163 pub task: String,
1164 pub task_hash: String,
1165 pub token_budget: u32,
1166 pub max_memory_refs: u32,
1167 pub catalog_hints: Vec<String>,
1168 pub requested_memory_ids: Vec<String>,
1169 pub force_revalidate: bool,
1170}
1171
1172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1174#[serde(deny_unknown_fields)]
1175pub struct DagDbSelectedContextRef {
1176 pub memory_id: String,
1177 pub catalog_id: Option<String>,
1178 pub title: SafeMetadata,
1179 pub summary: SafeMetadata,
1180 pub catalog_path: Vec<String>,
1181 pub document_type: String,
1182 pub selection_reason: String,
1183 pub token_estimate: u32,
1184 pub validation_status: ValidationStatus,
1185 pub citation_ref: String,
1186 pub boundary_flags: Vec<String>,
1187}
1188
1189#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1191#[serde(deny_unknown_fields)]
1192pub struct DagDbSelectedGraphEdgeRef {
1193 pub graph_edge_id: String,
1194 pub from_memory_id: String,
1195 pub to_memory_id: String,
1196 pub edge_kind: MemoryEdgeKind,
1197 pub graph_style: MemoryGraphStyle,
1198 pub selection_reason: String,
1199}
1200
1201#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1203#[serde(deny_unknown_fields)]
1204pub struct DagDbOmittedContextRef {
1205 pub memory_id: String,
1206 pub omission_reason: String,
1207 pub token_estimate_if_selected: u32,
1208}
1209
1210#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1212#[serde(deny_unknown_fields)]
1213pub struct DagDbGraphSelectionTraceStep {
1214 pub graph_style: MemoryGraphStyle,
1215 pub candidate_count_before: u32,
1216 pub candidate_count_after: u32,
1217 pub selected_count_after: u32,
1218 pub reason: String,
1219}
1220
1221#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1223#[serde(deny_unknown_fields)]
1224pub struct DagDbGraphContextSelectionResponse {
1225 pub tenant_id: String,
1226 pub namespace: String,
1227 pub request_id: String,
1228 pub task_hash: String,
1229 pub selection_status: DagDbGraphContextSelectionStatus,
1230 pub selected_memory_refs: Vec<DagDbSelectedContextRef>,
1231 pub selected_graph_edges: Vec<DagDbSelectedGraphEdgeRef>,
1232 pub omitted_memory_refs: Vec<DagDbOmittedContextRef>,
1233 pub selection_trace: Vec<DagDbGraphSelectionTraceStep>,
1234 pub selected_token_estimate: u32,
1235 pub token_budget: u32,
1236 pub boundary_warnings: Vec<String>,
1237}
1238
1239#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1241#[serde(deny_unknown_fields)]
1242pub struct DagDbGraphContextPacketBuildRequest {
1243 pub tenant_id: String,
1244 pub namespace: String,
1245 pub request_id: String,
1246 pub task: String,
1247 pub task_hash: String,
1248 pub audit_id: String,
1249 pub token_budget: u32,
1250 pub selection: DagDbGraphContextSelectionResponse,
1251 pub import_tracking_status: Option<DagDbContextPacketImportTrackingStatus>,
1252}
1253
1254#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1256#[serde(deny_unknown_fields)]
1257pub struct DagDbGraphContextPacket {
1258 pub schema_version: String,
1259 pub tenant_id: String,
1260 pub namespace: String,
1261 pub request_id: String,
1262 pub task: String,
1263 pub task_hash: String,
1264 pub packet_hash: String,
1265 pub selected_memory_refs: Vec<DagDbSelectedContextRef>,
1266 pub selected_graph_edges: Vec<DagDbSelectedGraphEdgeRef>,
1267 pub citation_refs: Vec<DagDbContextPacketCitationRef>,
1268 pub packet_metrics: DagDbContextPacketMetrics,
1269 pub boundaries: DagDbContextPacketBoundaries,
1270 pub agent_usage_instructions: Vec<String>,
1271 pub markdown: String,
1272}
1273
1274#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1276#[serde(deny_unknown_fields)]
1277pub struct DagDbContextPacketCitationRef {
1278 pub citation_ref: String,
1279 pub memory_id: String,
1280 pub citation_status: String,
1281}
1282
1283#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1285#[serde(deny_unknown_fields)]
1286pub struct DagDbContextPacketMetrics {
1287 pub token_budget: u32,
1288 pub selected_token_estimate: u32,
1289 pub selected_memory_ref_count: u32,
1290 pub selected_graph_edge_count: u32,
1291 pub citation_ref_count: u32,
1292 pub end_to_end_savings_status: String,
1293 pub cost_savings_status: String,
1294}
1295
1296#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1298#[serde(deny_unknown_fields)]
1299pub struct DagDbContextPacketBoundaries {
1300 pub repository_test_level_only: bool,
1301 pub production_runtime: String,
1302 pub default_context_replacement: String,
1303 pub citation_locator_status: String,
1304 pub billing_savings: String,
1305}
1306
1307#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1309#[serde(deny_unknown_fields)]
1310pub struct DagDbContextPacketImportTrackingStatus {
1311 pub manifest_json: String,
1312 pub manifest_status: String,
1313 pub tracked_clean_evidence_enforced: bool,
1314 pub source_path_status: String,
1315}
1316
1317#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1319#[serde(deny_unknown_fields)]
1320pub struct DagDbRouteLookupResponse {
1321 pub schema_version: String,
1323 pub tenant_id: String,
1324 pub namespace: String,
1325 pub route_id: String,
1326 pub route_status: RouteStatus,
1327 pub validation_status: ValidationStatus,
1328 pub council_status: CouncilReviewStatus,
1329 pub dag_finality_status: DagFinalityStatus,
1330 pub selected_memory_ids: Vec<String>,
1331 pub route_score_bp: u16,
1332 pub token_budget: u32,
1333 pub token_estimate: u32,
1334 pub stale_at: String,
1335 pub latest_receipt_hash: String,
1336 pub memory_refs: Option<Vec<ContextPacketMemoryRef>>,
1337 pub validation_report: Option<DagDbValidateResponse>,
1338}
1339
1340#[cfg(test)]
1341mod tests {
1342 use serde::{Serialize, de::DeserializeOwned};
1343
1344 use super::*;
1345
1346 #[test]
1347 fn dagdb_json_fixtures() {
1348 let fixtures: serde_json::Value =
1349 serde_json::from_str(include_str!("../fixtures/json/all_dto_fixtures.json"))
1350 .expect("parse complete DAG DB fixture set");
1351
1352 assert_fixture::<DagDbIntakeRequest>(&fixtures, "requests", "intake");
1353 assert_fixture::<DagDbRouteRequest>(&fixtures, "requests", "route");
1354 assert_fixture::<DagDbContextPacketRequest>(&fixtures, "requests", "context_packet");
1355 assert_fixture::<DagDbValidateRequest>(&fixtures, "requests", "validate");
1356 assert_fixture::<DagDbWritebackRequest>(&fixtures, "requests", "writeback");
1357 assert_fixture::<DagDbTrustCheckRequest>(&fixtures, "requests", "trust_check");
1358 assert_fixture::<DagDbCouncilDecisionRequest>(&fixtures, "requests", "council_decision");
1359 assert_fixture::<DagDbReceiptLookupRequest>(&fixtures, "requests", "receipt_lookup");
1360 assert_fixture::<DagDbCatalogLookupRequest>(&fixtures, "requests", "catalog_lookup");
1361 assert_fixture::<DagDbRouteLookupRequest>(&fixtures, "requests", "route_lookup");
1362
1363 assert_fixture::<DagDbIntakeResponse>(&fixtures, "responses", "intake");
1364 assert_fixture::<DagDbRouteResponse>(&fixtures, "responses", "route");
1365 assert_fixture::<DagDbContextPacketResponse>(&fixtures, "responses", "context_packet");
1366 assert_fixture::<DagDbValidateResponse>(&fixtures, "responses", "validate");
1367 assert_fixture::<DagDbWritebackResponse>(&fixtures, "responses", "writeback");
1368 assert_fixture::<DagDbImportResponse>(&fixtures, "responses", "import");
1369 assert_fixture::<DagDbExportResponse>(&fixtures, "responses", "export");
1370 assert_fixture::<DagDbTrustCheckResponse>(&fixtures, "responses", "trust_check");
1371 assert_fixture::<DagDbCouncilDecisionResponse>(&fixtures, "responses", "council_decision");
1372 assert_fixture::<DagDbReceiptLookupResponse>(&fixtures, "responses", "receipt_lookup");
1373 assert_fixture::<DagDbCatalogLookupResponse>(&fixtures, "responses", "catalog_lookup");
1374 assert_fixture::<DagDbRouteLookupResponse>(&fixtures, "responses", "route_lookup");
1375
1376 assert_fixture::<DagDbErrorEnvelope>(&fixtures, "errors", "tenant_scope_mismatch");
1377 }
1378
1379 #[test]
1380 fn receipt_event_type_serializes_operational_audit_categories() {
1381 for (event_type, expected) in [
1382 (
1383 ReceiptEventType::DagdbApprovalRequestSubmitted,
1384 "dagdb_approval_request_submitted",
1385 ),
1386 (
1387 ReceiptEventType::DagdbApprovalGranted,
1388 "dagdb_approval_granted",
1389 ),
1390 (
1391 ReceiptEventType::DagdbApprovalDenied,
1392 "dagdb_approval_denied",
1393 ),
1394 (
1395 ReceiptEventType::DagdbRecordAccepted,
1396 "dagdb_record_accepted",
1397 ),
1398 (
1399 ReceiptEventType::DagdbImportCompleted,
1400 "dagdb_import_completed",
1401 ),
1402 (
1403 ReceiptEventType::DagdbExportCompleted,
1404 "dagdb_export_completed",
1405 ),
1406 (
1407 ReceiptEventType::DagdbReplayDetected,
1408 "dagdb_replay_detected",
1409 ),
1410 (
1411 ReceiptEventType::DagdbIdempotencyConflict,
1412 "dagdb_idempotency_conflict",
1413 ),
1414 (
1415 ReceiptEventType::DagdbRlsTenantViolation,
1416 "dagdb_rls_tenant_violation",
1417 ),
1418 (
1419 ReceiptEventType::DagdbSignatureFailure,
1420 "dagdb_signature_failure",
1421 ),
1422 (
1423 ReceiptEventType::DagdbCouncilOperatorDecision,
1424 "dagdb_council_operator_decision",
1425 ),
1426 ] {
1427 assert_eq!(
1428 serde_json::to_value(event_type).expect("serialize event"),
1429 serde_json::json!(expected)
1430 );
1431 }
1432 }
1433
1434 #[test]
1435 fn dagdb_runtime_import_export_dtos_deny_unknown_fields() {
1436 let import_request = DagDbImportRequest {
1437 tenant_id: "tenant-a".into(),
1438 namespace: "primary".into(),
1439 idempotency_key: "idem-import-1".into(),
1440 db_set_version: "dag_db-project_memory_v3".into(),
1441 source_hash: "1111111111111111111111111111111111111111111111111111111111111111".into(),
1442 requester_did: "did:exo:importer".into(),
1443 import_report: serde_json::json!({
1444 "schema_version": "dagdb_kg_dry_run_import_report_v1",
1445 "tenant_id": "tenant-a",
1446 "namespace": "primary"
1447 }),
1448 };
1449 let parsed: DagDbImportRequest = serde_json::from_value(
1450 serde_json::to_value(&import_request).expect("serialize import request"),
1451 )
1452 .expect("deserialize import request");
1453 assert_eq!(parsed, import_request);
1454 let import_err = serde_json::from_str::<DagDbImportRequest>(
1455 r#"{
1456 "tenant_id": "tenant-a",
1457 "namespace": "primary",
1458 "idempotency_key": "idem-import-1",
1459 "db_set_version": "dag_db-project_memory_v3",
1460 "source_hash": "1111111111111111111111111111111111111111111111111111111111111111",
1461 "requester_did": "did:exo:importer",
1462 "import_report": {},
1463 "raw_source_body": "forbidden"
1464 }"#,
1465 )
1466 .expect_err("unknown import request field must fail");
1467 assert!(import_err.to_string().contains("unknown field"));
1468
1469 let export_request = DagDbExportRequest {
1470 tenant_id: "tenant-a".into(),
1471 namespace: "primary".into(),
1472 idempotency_key: "idem-export-1".into(),
1473 db_set_version: "dag_db-project_memory_v3".into(),
1474 requester_did: "did:exo:exporter".into(),
1475 included_memory_ids: vec![
1476 "2222222222222222222222222222222222222222222222222222222222222222".into(),
1477 ],
1478 included_graph_styles: vec!["semantic_catalog_graph".into()],
1479 included_writeback_idempotency_keys: Vec::new(),
1480 source_commit_or_repo_ref: Some("c706242d36f1c275e05d8a132778491da08f61c7".into()),
1481 include_preview_context: false,
1482 };
1483 let parsed: DagDbExportRequest = serde_json::from_value(
1484 serde_json::to_value(&export_request).expect("serialize export request"),
1485 )
1486 .expect("deserialize export request");
1487 assert_eq!(parsed, export_request);
1488 let export_err = serde_json::from_str::<DagDbExportRequest>(
1489 r#"{
1490 "tenant_id": "tenant-a",
1491 "namespace": "primary",
1492 "idempotency_key": "idem-export-1",
1493 "db_set_version": "dag_db-project_memory_v3",
1494 "requester_did": "did:exo:exporter",
1495 "included_memory_ids": [],
1496 "included_graph_styles": [],
1497 "included_writeback_idempotency_keys": [],
1498 "source_commit_or_repo_ref": null,
1499 "include_preview_context": false,
1500 "receipt_path": "/Users/example/private"
1501 }"#,
1502 )
1503 .expect_err("unknown export request field must fail");
1504 assert!(export_err.to_string().contains("unknown field"));
1505 }
1506
1507 #[test]
1508 fn dagdb_graph_json_fixtures() {
1509 let fixtures: serde_json::Value =
1510 serde_json::from_str(include_str!("../fixtures/json/all_dto_fixtures.json"))
1511 .expect("parse complete DAG DB fixture set");
1512
1513 assert_fixture::<MemoryCandidate>(&fixtures, "graph", "memory_candidate");
1514 assert_fixture::<SimilarityResult>(&fixtures, "graph", "similarity_result");
1515 assert_fixture::<CanonicalizationDecision>(&fixtures, "graph", "canonicalization_decision");
1516 assert_fixture::<GraphView>(&fixtures, "graph", "graph_view");
1517 assert_fixture::<RouteInvalidationReceipt>(
1518 &fixtures,
1519 "graph",
1520 "route_invalidation_receipt",
1521 );
1522 assert_fixture::<PlacementResult>(&fixtures, "graph", "placement_result");
1523
1524 let all_styles = [
1525 MemoryGraphStyle::ProvenanceReceiptDag,
1526 MemoryGraphStyle::CanonicalMemoryGraph,
1527 MemoryGraphStyle::SemanticCatalogGraph,
1528 MemoryGraphStyle::SimilarityOverlayGraph,
1529 MemoryGraphStyle::DependencyDag,
1530 MemoryGraphStyle::RoutingViewGraph,
1531 MemoryGraphStyle::ContradictionSupersessionGraph,
1532 MemoryGraphStyle::ContextPacketGraph,
1533 ];
1534 let encoded = serde_json::to_value(all_styles).expect("serialize graph styles");
1535 assert_eq!(
1536 encoded,
1537 serde_json::json!([
1538 "provenance_receipt_dag",
1539 "canonical_memory_graph",
1540 "semantic_catalog_graph",
1541 "similarity_overlay_graph",
1542 "dependency_dag",
1543 "routing_view_graph",
1544 "contradiction_supersession_graph",
1545 "context_packet_graph"
1546 ])
1547 );
1548 }
1549
1550 #[test]
1551 fn dagdb_graph_context_selection_dtos_deny_unknown_fields() {
1552 let request = DagDbGraphContextSelectionRequest {
1553 tenant_id: "tenant-a".into(),
1554 namespace: "primary".into(),
1555 request_id: "req-1".into(),
1556 task: "Select next implementation step".into(),
1557 task_hash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".into(),
1558 token_budget: 1_000,
1559 max_memory_refs: 4,
1560 catalog_hints: vec!["04_Plans".into()],
1561 requested_memory_ids: Vec::new(),
1562 force_revalidate: false,
1563 };
1564 let serialized = serde_json::to_value(&request).expect("serialize request");
1565 let parsed: DagDbGraphContextSelectionRequest =
1566 serde_json::from_value(serialized).expect("deserialize request");
1567 assert_eq!(parsed, request);
1568
1569 let forged = r#"{
1570 "tenant_id": "tenant-a",
1571 "namespace": "primary",
1572 "request_id": "req-1",
1573 "task": "Select next implementation step",
1574 "task_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1575 "token_budget": 1000,
1576 "max_memory_refs": 4,
1577 "catalog_hints": [],
1578 "requested_memory_ids": [],
1579 "force_revalidate": false,
1580 "raw_markdown": "forbidden"
1581 }"#;
1582 let err = serde_json::from_str::<DagDbGraphContextSelectionRequest>(forged)
1583 .expect_err("unknown request field must fail");
1584 assert!(err.to_string().contains("unknown field"));
1585
1586 let response = DagDbGraphContextSelectionResponse {
1587 tenant_id: "tenant-a".into(),
1588 namespace: "primary".into(),
1589 request_id: "req-1".into(),
1590 task_hash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".into(),
1591 selection_status: DagDbGraphContextSelectionStatus::Selected,
1592 selected_memory_refs: vec![DagDbSelectedContextRef {
1593 memory_id: "mem-plan".into(),
1594 catalog_id: Some("catalog-plan".into()),
1595 title: SafeMetadata {
1596 decision: SafeMetadataDecision::Allow,
1597 text: "Plan".into(),
1598 redaction_codes: Vec::new(),
1599 original_hash:
1600 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".into(),
1601 truncated: false,
1602 byte_len: 4,
1603 },
1604 summary: SafeMetadata {
1605 decision: SafeMetadataDecision::Allow,
1606 text: "Safe summary".into(),
1607 redaction_codes: Vec::new(),
1608 original_hash:
1609 "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc".into(),
1610 truncated: false,
1611 byte_len: 12,
1612 },
1613 catalog_path: vec!["04_Plans".into(), "Next Steps".into()],
1614 document_type: "plan".into(),
1615 selection_reason: "task_term_match".into(),
1616 token_estimate: 120,
1617 validation_status: ValidationStatus::Passed,
1618 citation_ref: "citation:plan".into(),
1619 boundary_flags: vec!["repository_test_only".into()],
1620 }],
1621 selected_graph_edges: Vec::new(),
1622 omitted_memory_refs: Vec::new(),
1623 selection_trace: vec![DagDbGraphSelectionTraceStep {
1624 graph_style: MemoryGraphStyle::SemanticCatalogGraph,
1625 candidate_count_before: 1,
1626 candidate_count_after: 1,
1627 selected_count_after: 1,
1628 reason: "semantic_catalog_graph_considered".into(),
1629 }],
1630 selected_token_estimate: 120,
1631 token_budget: 1_000,
1632 boundary_warnings: vec!["production_runtime_not_approved".into()],
1633 };
1634 let serialized = serde_json::to_value(&response).expect("serialize response");
1635 let parsed: DagDbGraphContextSelectionResponse =
1636 serde_json::from_value(serialized).expect("deserialize response");
1637 assert_eq!(parsed, response);
1638 }
1639
1640 #[test]
1641 fn dagdb_graph_context_packet_dtos_deny_unknown_fields() {
1642 let selection = DagDbGraphContextSelectionResponse {
1643 tenant_id: "tenant-a".into(),
1644 namespace: "primary".into(),
1645 request_id: "req-1".into(),
1646 task_hash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".into(),
1647 selection_status: DagDbGraphContextSelectionStatus::Selected,
1648 selected_memory_refs: Vec::new(),
1649 selected_graph_edges: Vec::new(),
1650 omitted_memory_refs: Vec::new(),
1651 selection_trace: Vec::new(),
1652 selected_token_estimate: 0,
1653 token_budget: 1_000,
1654 boundary_warnings: vec!["production_runtime_not_approved".into()],
1655 };
1656 let request = DagDbGraphContextPacketBuildRequest {
1657 tenant_id: "tenant-a".into(),
1658 namespace: "primary".into(),
1659 request_id: "req-1".into(),
1660 task: "Build bounded context packet".into(),
1661 task_hash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".into(),
1662 audit_id: "audit-1".into(),
1663 token_budget: 1_000,
1664 selection,
1665 import_tracking_status: None,
1666 };
1667 let serialized = serde_json::to_value(&request).expect("serialize request");
1668 let parsed: DagDbGraphContextPacketBuildRequest =
1669 serde_json::from_value(serialized).expect("deserialize request");
1670 assert_eq!(parsed, request);
1671
1672 let forged = r#"{
1673 "tenant_id": "tenant-a",
1674 "namespace": "primary",
1675 "request_id": "req-1",
1676 "task": "Build bounded context packet",
1677 "task_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1678 "audit_id": "audit-1",
1679 "token_budget": 1000,
1680 "selection": {
1681 "tenant_id": "tenant-a",
1682 "namespace": "primary",
1683 "request_id": "req-1",
1684 "task_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1685 "selection_status": "empty",
1686 "selected_memory_refs": [],
1687 "selected_graph_edges": [],
1688 "omitted_memory_refs": [],
1689 "selection_trace": [],
1690 "selected_token_estimate": 0,
1691 "token_budget": 1000,
1692 "boundary_warnings": []
1693 },
1694 "import_tracking_status": null,
1695 "raw_markdown": "forbidden"
1696 }"#;
1697 let err = serde_json::from_str::<DagDbGraphContextPacketBuildRequest>(forged)
1698 .expect_err("unknown request field must fail");
1699 assert!(err.to_string().contains("unknown field"));
1700 }
1701
1702 #[test]
1703 fn dagdb_graph_context_selection_status_serializes_all_variants() {
1704 let variants = [
1705 DagDbGraphContextSelectionStatus::Selected,
1706 DagDbGraphContextSelectionStatus::Empty,
1707 DagDbGraphContextSelectionStatus::Failed,
1708 ];
1709 let encoded = serde_json::to_value(variants).expect("serialize statuses");
1710 assert_eq!(encoded, serde_json::json!(["selected", "empty", "failed"]));
1711 }
1712
1713 #[test]
1714 fn dagdb_rejects_forged_safe_metadata() {
1715 let forged = r#"{
1716 "tenant_id": "tenant-a",
1717 "namespace": "primary",
1718 "idempotency_key": "idem-1",
1719 "source_type": "public_web",
1720 "source_hash": "1111111111111111111111111111111111111111111111111111111111111111",
1721 "payload_hash": "2222222222222222222222222222222222222222222222222222222222222222",
1722 "owner_did": "did:exo:owner",
1723 "controller_did": "did:exo:controller",
1724 "submitted_by_did": "did:exo:submitter",
1725 "consent_purpose": "retrieval",
1726 "requested_action": "memory:intake",
1727 "title_text": "Safe public title",
1728 "summary_text": "Safe public summary",
1729 "title": {
1730 "decision": "allow",
1731 "text": "forged trusted value",
1732 "redaction_codes": [],
1733 "original_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1734 "truncated": false,
1735 "byte_len": 20
1736 }
1737 }"#;
1738 let err = serde_json::from_str::<DagDbIntakeRequest>(forged)
1739 .expect_err("unknown trusted SafeMetadata field must fail");
1740 assert!(err.to_string().contains("unknown field"));
1741 }
1742
1743 fn assert_fixture<T>(fixtures: &serde_json::Value, section: &str, name: &str)
1744 where
1745 T: DeserializeOwned + Serialize,
1746 {
1747 let fixture = fixtures
1748 .get(section)
1749 .and_then(|section| section.get(name))
1750 .unwrap_or_else(|| panic!("missing fixture {section}.{name}"));
1751 let parsed: T = serde_json::from_value(fixture.clone())
1752 .unwrap_or_else(|err| panic!("parse fixture {section}.{name}: {err}"));
1753 let serialized = serde_json::to_value(parsed)
1754 .unwrap_or_else(|err| panic!("serialize fixture {section}.{name}: {err}"));
1755 assert_eq!(serialized, *fixture, "fixture {section}.{name} drifted");
1756 }
1757}