Skip to main content

dejadb_core/format/
field_map.rs

1use std::collections::HashMap;
2use std::sync::LazyLock;
3
4/// Core FIELD_MAP: maps long field names to short (compact) names.
5/// OMS 1.2 canonical names — writers MUST emit these.
6static FIELD_MAP: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
7    let mut m = HashMap::new();
8    // Core fields
9    m.insert("type", "t");
10    m.insert("subject", "s");
11    m.insert("relation", "r");
12    m.insert("object", "o");
13    m.insert("confidence", "c");
14    m.insert("source_type", "st");
15    m.insert("created_at", "ca");
16    m.insert("temporal_type", "tt");
17    m.insert("valid_from", "vf");
18    m.insert("valid_to", "vt");
19    m.insert("system_valid_from", "svf");
20    m.insert("system_valid_to", "svt");
21    m.insert("context", "ctx");
22    m.insert("superseded_by", "sb");
23    m.insert("importance", "im");
24    m.insert("author_did", "adid");
25    m.insert("namespace", "ns");
26    m.insert("user_id", "user");
27    m.insert("structural_tags", "tags");
28    m.insert("derived_from", "df");
29    m.insert("consolidation_level", "cl");
30    m.insert("success_count", "sc");
31    m.insert("failure_count", "fc");
32    m.insert("provenance_chain", "pc");
33    m.insert("origin_did", "odid");
34    m.insert("origin_namespace", "ons");
35    m.insert("content_refs", "cr");
36    m.insert("embedding_refs", "er");
37    m.insert("related_to", "rt");
38    m.insert("_elided", "_e");
39    m.insert("_disclosure_of", "_do");
40    m.insert("invalidation_policy", "ip");
41    m.insert("supersession_justification", "sj");
42    m.insert("supersession_auth", "sa");
43
44    // verification_status
45    m.insert("verification_status", "vstatus");
46
47    // OMS 1.2 new common fields (§6.1)
48    m.insert("owner", "own");
49    m.insert("category", "cat");
50    m.insert("run_id", "rid");
51    m.insert("role", "role");
52    m.insert("access_count", "ac");
53    m.insert("last_accessed_at", "laa");
54    m.insert("timestamp_ms", "tms");
55    m.insert("observer_did", "obsdid");
56    m.insert("subject_did", "sdid");
57    m.insert("session_id", "sid2");
58    m.insert("entity_id", "eid");
59    m.insert("epistemic_status", "epstat");
60    m.insert("requires_human_review", "rhr");
61    m.insert("processing_basis", "pbasis");
62    m.insert("identity_state", "idst");
63    m.insert("license", "lic");
64    m.insert("trusted_timestamp", "tts");
65    m.insert("invalidation_type", "itype");
66    m.insert("invalidation_reason", "ireason");
67    m.insert("invalidation_initiator", "iinit");
68    m.insert("retention_policy", "rpol");
69    m.insert("recall_priority", "rpri");
70
71    // Tool fields
72    m.insert("tool_name", "tn");
73    m.insert("input", "inp"); // was: arguments → args
74    m.insert("tool_content", "cnt"); // Tool's content field (distinct from Event's uncompacted "content")
75    m.insert("is_error", "iserr"); // was: success → ok (inverted polarity)
76    m.insert("error", "err");
77    m.insert("duration_ms", "dur");
78    m.insert("parent_task_id", "ptid");
79    // OMS 1.2 new Tool fields
80    m.insert("tool_phase", "aphase");
81    m.insert("tool_call_id", "tcid");
82    m.insert("call_batch_id", "cbid");
83    m.insert("tool_type", "ttype");
84    m.insert("tool_version", "tver");
85    m.insert("execution_mode", "emode");
86    m.insert("code", "code");
87    m.insert("stdout", "stdout");
88    m.insert("stderr", "stderr");
89    m.insert("exit_code", "xcode");
90    m.insert("interpreter_id", "iid");
91    m.insert("error_type", "etype");
92    m.insert("tool_description", "tdesc");
93    m.insert("input_schema", "ischema");
94    m.insert("strict", "strict");
95    m.insert("async_mode", "amode");
96    // OMS 1.3 new Tool field
97    m.insert("output_schema", "osch");
98
99    // Phase 1 (2026-04-19) — Tool definition fields promoted from extra_fields.
100    m.insert("kind", "aknd");
101    m.insert("executor_uri", "axu");
102    m.insert("locked_params", "lprm");
103    m.insert("examples", "exmp");
104    m.insert("annotations", "anno");
105    m.insert("spec_hash", "shsh");
106    // HPL Phase 4.1 (2026-04-22): per-binding executor classifier.
107    // Default Host is omitted from the serialized blob (see
108    // `serialize.rs`) so pre-HPL grains stay byte-identical.
109    m.insert("executor_kind", "exk");
110
111    // Async execution + EU AI Act / failure typing.
112    // All Option-typed on Tool; absent on legacy grains. Defaults at
113    // deserialize: status=None reads as Completed; the rest stay None.
114    m.insert("status", "ast");
115    m.insert("correlation_id", "acid");
116    m.insert("expires_at_sec", "axp");
117    m.insert("transient_definition_hash", "tdh");
118    m.insert("failure_cause", "afc");
119    m.insert("failure_detail", "afd");
120    m.insert("actor_execution_environment", "aex");
121
122    // OMS 1.3 Integration Domain Profile (int:* context map keys)
123    // Tool fields (REST API connectors)
124    m.insert("int:base_url", "ib");
125    m.insert("int:http_method", "ihm"); // "im" collides with "importance"
126    m.insert("int:http_path", "ihp"); // "ip" collides with "invalidation_policy"
127    m.insert("int:path_params", "ipp");
128    m.insert("int:query_params", "iqp");
129    m.insert("int:body_params", "ibp");
130    m.insert("int:response_mapping", "irm");
131    m.insert("int:auth_type", "iat");
132    m.insert("int:auth_scopes", "ias");
133    m.insert("int:read_only", "iro");
134    m.insert("int:connector", "ic");
135    m.insert("int:docs_url", "idu");
136    m.insert("int:rate_limit", "irl");
137    m.insert("int:category", "icat");
138    m.insert("int:sunset_date", "isd");
139    m.insert("int:content_type", "ict");
140    // Trigger-specific fields
141    m.insert("int:poll_interval_secs", "ipis");
142    m.insert("int:cursor_field", "icf");
143    m.insert("int:cursor_type", "icft");
144    m.insert("int:webhook_path", "iwp");
145    m.insert("int:webhook_secret_header", "iwsh");
146    m.insert("int:cron_expression", "icron");
147    m.insert("int:timezone", "itz");
148    m.insert("int:config_schema", "icfg");
149    m.insert("int:event_schema", "ievt");
150
151    // Observation fields
152    m.insert("observer_id", "oid");
153    m.insert("observer_type", "otype");
154    m.insert("frame_id", "fid");
155    m.insert("sync_group", "sg");
156    m.insert("observation_mode", "omode");
157    m.insert("observation_scope", "oscope");
158    m.insert("observer_model", "omdl");
159    m.insert("compression_ratio", "ocmp");
160
161    // Goal fields
162    m.insert("description", "desc");
163    m.insert("goal_state", "gs");
164    m.insert("criteria", "crit");
165    m.insert("criteria_structured", "crs");
166    m.insert("priority", "pri");
167    m.insert("parent_goals", "pgs");
168    m.insert("state_reason", "sr");
169    m.insert("satisfaction_evidence", "se");
170    m.insert("progress", "prog");
171    m.insert("delegate_to", "dto");
172    m.insert("delegate_from", "dfo");
173    m.insert("expiry_policy", "ep");
174    m.insert("recurrence", "rec");
175    m.insert("evidence_required", "evreq");
176    m.insert("rollback_on_failure", "rof");
177    m.insert("allowed_transitions", "atr");
178    // OMS 1.2 new Goal fields
179    m.insert("depends_on", "depg");
180    m.insert("assigned_agent", "asgn");
181    m.insert("expected_output", "expout");
182    m.insert("output_grain", "outg");
183    m.insert("deadline", "dline");
184
185    // Event fields — "content" stays uncompacted
186    m.insert("content_blocks", "cblocks");
187    m.insert("model_id", "mdl");
188    m.insert("stop_reason", "stopr");
189    m.insert("token_usage", "toku");
190    m.insert("parent_message_id", "pmid");
191
192    // Reasoning fields (OMS 1.2 new grain type)
193    m.insert("premises", "prem");
194    m.insert("conclusion", "conc");
195    m.insert("inference_method", "imethod");
196    m.insert("alternatives_considered", "altc");
197    m.insert("thinking_content", "think");
198    m.insert("thinking_redacted", "tredact");
199    m.insert("statistical_context", "statctx");
200    m.insert("software_environment", "swenv");
201    m.insert("parameter_set", "params");
202    m.insert("random_seed", "rseed");
203
204    // Consensus fields (OMS 1.2 new grain type)
205    m.insert("participating_observers", "pobs");
206    m.insert("threshold", "thr");
207    m.insert("agreement_count", "agrc");
208    m.insert("dissent_count", "disc");
209    m.insert("dissent_grains", "dgrains");
210    m.insert("agreed_content", "acnt");
211
212    // Consent fields (OMS 1.2 new grain type)
213    // subject_did already mapped above as "sdid"
214    m.insert("grantee_did", "gdid");
215    m.insert("scope", "scope");
216    m.insert("is_withdrawal", "isw");
217    m.insert("basis", "basis");
218    m.insert("jurisdiction", "jur");
219    m.insert("prior_consent", "pcon");
220    m.insert("witness_dids", "wdids");
221
222    // Skill fields (OMS 1.4 new grain type, 0x0B). `description` reuses the
223    // shared `desc` key (mapped above with Goal); `proficiency` aliases
224    // `confidence` (D3) but still carries a dedicated `prof` key on the wire
225    // for held instances. 16 keys, collision-audited clean (design §11).
226    m.insert("name", "skname");
227    m.insert("instructions", "instr");
228    m.insert("when_to_use", "wtu");
229    m.insert("version", "sver");
230    m.insert("allowed_tools", "atls");
231    m.insert("resources", "res");
232    m.insert("dependencies", "deps");
233    m.insert("input_modalities", "imod");
234    m.insert("output_modalities", "omod");
235    m.insert("domain", "dom");
236    m.insert("holder_did", "hdid");
237    m.insert("proficiency", "prof");
238    m.insert("practice_count", "prcnt");
239    m.insert("last_practiced_at", "lpa");
240    m.insert("strategies", "strat");
241    m.insert("transferable", "xfer");
242
243    // Embedding text override
244    m.insert("embedding_text", "et");
245
246    // Scoped memory fields
247    m.insert("scope_path", "scp");
248    m.insert("scope_depth", "scd");
249
250    // Delegation fields (§6.10)
251    m.insert("authorized_namespaces", "ans");
252    m.insert("authorized_types", "atypes");
253    m.insert("authorized_tools", "atools");
254    m.insert("delegation_depth", "ddepth");
255    m.insert("delegation_expiry", "dexp");
256    m.insert("context_grains", "cgrains");
257    m.insert("return_to", "retdid");
258
259    // Event/State/Workflow fields — not compacted per spec
260    // "content" stays as "content" (Event, uncompacted)
261    // "consolidated" stays as "consolidated"
262    // "plan" stays as "plan"
263    // "history" stays as "history"
264    // "trigger" stays as "trigger"
265    // "nodes" stays as "nodes"
266    // "edges" stays as "edges"
267    // "retries" stays as "retries"
268    m.insert("bindings", "bind");
269
270    m
271});
272
273/// Reverse map: short name → long name.
274static REVERSE_FIELD_MAP: LazyLock<HashMap<&'static str, &'static str>> =
275    LazyLock::new(|| FIELD_MAP.iter().map(|(&k, &v)| (v, k)).collect());
276
277/// Content reference nested field map.
278static CONTENT_REF_FIELD_MAP: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
279    let mut m = HashMap::new();
280    m.insert("uri", "u");
281    m.insert("modality", "m");
282    m.insert("mime_type", "mt");
283    m.insert("size_bytes", "sz");
284    m.insert("checksum", "ck");
285    m.insert("metadata", "md");
286    m
287});
288
289/// Embedding reference nested field map.
290static EMBEDDING_REF_FIELD_MAP: LazyLock<HashMap<&'static str, &'static str>> =
291    LazyLock::new(|| {
292        let mut m = HashMap::new();
293        m.insert("vector_id", "vi");
294        m.insert("model", "mo");
295        m.insert("dimensions", "dm");
296        m.insert("modality_source", "ms");
297        m.insert("distance_metric", "di");
298        // OMS 1.2 new embedding ref fields
299        m.insert("chunk_index", "ci");
300        m.insert("chunk_text", "ct");
301        m.insert("chunk_strategy", "cs");
302        m.insert("chunk_overlap", "co");
303        m
304    });
305
306/// Related-to nested field map.
307static RELATED_TO_FIELD_MAP: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
308    let mut m = HashMap::new();
309    m.insert("hash", "h");
310    m.insert("relation_type", "rl");
311    m.insert("weight", "w");
312    m
313});
314
315/// Workflow edge nested field map.
316static WORKFLOW_EDGE_FIELD_MAP: LazyLock<HashMap<&'static str, &'static str>> =
317    LazyLock::new(|| {
318        let mut m = HashMap::new();
319        // src, dst, cond stay as-is (already short)
320        m.insert("max_cycles", "mxc");
321        m
322    });
323
324/// Compact a field name using the FIELD_MAP.
325pub fn compact_field(name: &str) -> &str {
326    FIELD_MAP.get(name).copied().unwrap_or(name)
327}
328
329/// Expand a short field name back to its full name.
330pub fn expand_field(short: &str) -> &str {
331    REVERSE_FIELD_MAP.get(short).copied().unwrap_or(short)
332}
333
334/// Compact content_ref nested fields.
335pub fn compact_content_ref_field(name: &str) -> &str {
336    CONTENT_REF_FIELD_MAP.get(name).copied().unwrap_or(name)
337}
338
339/// Compact embedding_ref nested fields.
340pub fn compact_embedding_ref_field(name: &str) -> &str {
341    EMBEDDING_REF_FIELD_MAP.get(name).copied().unwrap_or(name)
342}
343
344/// Compact related_to nested fields.
345pub fn compact_related_to_field(name: &str) -> &str {
346    RELATED_TO_FIELD_MAP.get(name).copied().unwrap_or(name)
347}
348
349/// Compact workflow edge nested fields.
350pub fn compact_workflow_edge_field(name: &str) -> &str {
351    WORKFLOW_EDGE_FIELD_MAP.get(name).copied().unwrap_or(name)
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357
358    #[test]
359    fn test_compact_field() {
360        assert_eq!(compact_field("subject"), "s");
361        assert_eq!(compact_field("relation"), "r");
362        assert_eq!(compact_field("object"), "o");
363        assert_eq!(compact_field("confidence"), "c");
364        assert_eq!(compact_field("type"), "t");
365        assert_eq!(compact_field("created_at"), "ca");
366        assert_eq!(compact_field("namespace"), "ns");
367        assert_eq!(compact_field("author_did"), "adid");
368        assert_eq!(compact_field("source_type"), "st");
369        assert_eq!(compact_field("unknown_field"), "unknown_field");
370        // OMS 1.2 fields
371        assert_eq!(compact_field("input"), "inp");
372        assert_eq!(compact_field("is_error"), "iserr");
373        assert_eq!(compact_field("verification_status"), "vstatus");
374        assert_eq!(compact_field("premises"), "prem");
375        assert_eq!(compact_field("conclusion"), "conc");
376        // OMS 1.3 fields
377        assert_eq!(compact_field("output_schema"), "osch");
378        assert_eq!(compact_field("int:base_url"), "ib");
379        assert_eq!(compact_field("int:http_method"), "ihm");
380        assert_eq!(compact_field("int:connector"), "ic");
381        assert_eq!(compact_field("int:poll_interval_secs"), "ipis");
382        assert_eq!(compact_field("int:webhook_path"), "iwp");
383        // Async-execution fields
384        assert_eq!(compact_field("status"), "ast");
385        assert_eq!(compact_field("correlation_id"), "acid");
386        assert_eq!(compact_field("expires_at_sec"), "axp");
387        assert_eq!(compact_field("transient_definition_hash"), "tdh");
388        assert_eq!(compact_field("failure_cause"), "afc");
389        assert_eq!(compact_field("failure_detail"), "afd");
390        assert_eq!(compact_field("actor_execution_environment"), "aex");
391    }
392
393    #[test]
394    fn test_expand_field() {
395        assert_eq!(expand_field("s"), "subject");
396        assert_eq!(expand_field("r"), "relation");
397        assert_eq!(expand_field("o"), "object");
398        assert_eq!(expand_field("t"), "type");
399        assert_eq!(expand_field("unknown"), "unknown");
400        // OMS 1.2 fields
401        assert_eq!(expand_field("inp"), "input");
402        assert_eq!(expand_field("iserr"), "is_error");
403        assert_eq!(expand_field("vstatus"), "verification_status");
404        // OMS 1.1 aliases removed — these should pass through unchanged
405        assert_eq!(expand_field("args"), "args");
406        assert_eq!(expand_field("ok"), "ok");
407        assert_eq!(expand_field("ct"), "ct");
408        // OMS 1.3 fields
409        assert_eq!(expand_field("osch"), "output_schema");
410        assert_eq!(expand_field("ib"), "int:base_url");
411        assert_eq!(expand_field("ihm"), "int:http_method");
412        assert_eq!(expand_field("ic"), "int:connector");
413        assert_eq!(expand_field("ipis"), "int:poll_interval_secs");
414        // Async-execution fields
415        assert_eq!(expand_field("ast"), "status");
416        assert_eq!(expand_field("acid"), "correlation_id");
417        assert_eq!(expand_field("axp"), "expires_at_sec");
418        assert_eq!(expand_field("tdh"), "transient_definition_hash");
419        assert_eq!(expand_field("afc"), "failure_cause");
420        assert_eq!(expand_field("afd"), "failure_detail");
421        assert_eq!(expand_field("aex"), "actor_execution_environment");
422    }
423
424    #[test]
425    fn test_nested_compaction() {
426        assert_eq!(compact_content_ref_field("uri"), "u");
427        assert_eq!(compact_content_ref_field("mime_type"), "mt");
428        assert_eq!(compact_embedding_ref_field("vector_id"), "vi");
429        assert_eq!(compact_related_to_field("hash"), "h");
430        assert_eq!(compact_related_to_field("relation_type"), "rl");
431        // OMS 1.2 new embedding ref fields
432        assert_eq!(compact_embedding_ref_field("chunk_index"), "ci");
433        assert_eq!(compact_embedding_ref_field("chunk_text"), "ct");
434    }
435
436    #[test]
437    fn test_no_compact_key_collisions() {
438        // Verify every compact key maps to exactly one long name.
439        // A collision means REVERSE_FIELD_MAP lost an entry.
440        let map = &*FIELD_MAP;
441        let reverse = &*REVERSE_FIELD_MAP;
442        assert_eq!(map.len(), reverse.len(),
443            "FIELD_MAP has {} entries but REVERSE_FIELD_MAP has {} — compact key collision detected",
444            map.len(), reverse.len());
445    }
446
447    #[test]
448    fn test_integration_profile_roundtrip() {
449        // All 25 int:* keys must roundtrip through compact → expand
450        let int_keys = [
451            "int:base_url",
452            "int:http_method",
453            "int:http_path",
454            "int:path_params",
455            "int:query_params",
456            "int:body_params",
457            "int:response_mapping",
458            "int:auth_type",
459            "int:auth_scopes",
460            "int:read_only",
461            "int:connector",
462            "int:docs_url",
463            "int:rate_limit",
464            "int:category",
465            "int:sunset_date",
466            "int:content_type",
467            "int:poll_interval_secs",
468            "int:cursor_field",
469            "int:cursor_type",
470            "int:webhook_path",
471            "int:webhook_secret_header",
472            "int:cron_expression",
473            "int:timezone",
474            "int:config_schema",
475            "int:event_schema",
476        ];
477        for key in &int_keys {
478            let short = compact_field(key);
479            assert_ne!(short, *key, "int:* key '{}' has no compact mapping", key);
480            let expanded = expand_field(short);
481            assert_eq!(
482                expanded, *key,
483                "roundtrip failed for '{}' → '{}' → '{}'",
484                key, short, expanded
485            );
486        }
487    }
488}