Skip to main content

axon_frontend/
ir_nodes.rs

1//! AXON IR node definitions — direct port of axon/compiler/ir_nodes.py.
2//!
3//! All nodes serialize to JSON matching the Python IR output format exactly.
4
5#![allow(dead_code)]
6
7use serde::Serialize;
8
9// ── Program root ─────────────────────────────────────────────────────────────
10
11#[derive(Debug, Serialize)]
12pub struct IRProgram {
13    pub node_type: &'static str,
14    pub source_line: u32,
15    pub source_column: u32,
16    pub personas: Vec<IRPersona>,
17    pub contexts: Vec<IRContext>,
18    pub anchors: Vec<IRAnchor>,
19    pub tools: Vec<IRToolSpec>,
20    pub memories: Vec<IRMemory>,
21    pub types: Vec<IRType>,
22    pub flows: Vec<IRFlow>,
23    pub runs: Vec<IRRun>,
24    pub imports: Vec<IRImport>,
25    pub agents: Vec<IRAgent>,
26    pub shields: Vec<IRShield>,
27    pub daemons: Vec<IRDaemon>,
28    pub ots_specs: Vec<IROts>,
29    pub pix_specs: Vec<IRPix>,
30    pub corpus_specs: Vec<IRCorpus>,
31    pub psyche_specs: Vec<IRPsyche>,
32    pub mandate_specs: Vec<IRMandate>,
33    pub lambda_data_specs: Vec<IRLambdaData>,
34    pub compute_specs: Vec<IRCompute>,
35    pub axonstore_specs: Vec<IRAxonStore>,
36    pub endpoints: Vec<IRAxonEndpoint>,
37    /// §Fase 53 — closed-catalog extension declarations (compiled).
38    /// `#[serde(skip)]` so the field is NOT emitted into the IR JSON —
39    /// this keeps the static IR-JSON drift-gate fixtures green without
40    /// regenerating them (same established pattern as `dataspace_specs`).
41    /// The in-memory field feeds the §53.c type-checker + §53.d PCC (both
42    /// read `&IRProgram`); soundness invariant #1 holds via SOURCE
43    /// re-derivation — both the prover and the verifier read the
44    /// source-derived IR, which carries the extensions. §53.x hardening
45    /// (optional): un-skip + regenerate fixtures + bind extensions into
46    /// the PCC `artifact_digest` (today the digest omits them; the
47    /// witness still binds them by re-derivation). Deterministically
48    /// sorted by `name` at the end of IR generation (§53.b founder
49    /// refinement B) so multi-file declaration order can never perturb
50    /// the proof-bundle hash.
51    #[serde(skip)]
52    pub extensions: Vec<IRExtension>,
53    /// Local-only: IRDataspace specs are computed during IR generation so
54    /// the cost estimator and inspectors can reach them, but Python's
55    /// reference IRProgram does not serialise this field. Hidden from JSON
56    /// output for byte-identical parity (§8.2.h.1).
57    #[serde(skip)]
58    pub dataspace_specs: Vec<IRDataspace>,
59    /// §λ-L-E Fase 1 — I/O cognitivo primitives (compiled).
60    pub resources: Vec<IRResource>,
61    pub fabrics: Vec<IRFabric>,
62    pub manifests: Vec<IRManifest>,
63    pub observations: Vec<IRObserve>,
64    /// §λ-L-E Fase 1 (Free Monad root) — populated when the program
65    /// declares manifests/observes. `None` ⇒ serialises as `null`
66    /// (matches Python when the field is `None`).
67    pub intention_tree: Option<IRIntentionTree>,
68    /// §λ-L-E Fase 3 — Control cognitivo primitives (compiled).
69    pub reconciles: Vec<IRReconcile>,
70    pub leases: Vec<IRLease>,
71    pub ensembles: Vec<IREnsemble>,
72    /// §λ-L-E Fase 4 — Topology + Session (compiled).
73    pub sessions: Vec<IRSession>,
74    pub topologies: Vec<IRTopology>,
75    /// §λ-L-E Fase 5 — Immune system (compiled).
76    pub immunes: Vec<IRImmune>,
77    pub reflexes: Vec<IRReflex>,
78    pub heals: Vec<IRHeal>,
79    /// §λ-L-E Fase 9 — UI cognitiva declarativa (compiled).
80    pub components: Vec<IRComponent>,
81    pub views: Vec<IRView>,
82    /// §λ-L-E Fase 13 — Mobile typed channels (compiled).
83    pub channels: Vec<IRChannel>,
84    /// §Fase 41.b — typed WebSocket transports (compiled). Each carries its
85    /// referenced `session` protocol + the credit-window backpressure so
86    /// axon-rs can realise the typed endpoint over a `tokio` WebSocket.
87    pub sockets: Vec<IRSocket>,
88    /// §Fase 23 — algebraic effect declarations (compiled).
89    /// Each declared effect persists into IR so axon-rs can build the
90    /// per-effect operation table at startup. The CPS state graph for
91    /// perform/handle sites lives inline within IRFlow.steps (each
92    /// IRPerform / IRHandlerFrame carries its assigned state_id /
93    /// frame_id).
94    ///
95    /// This Rust port mirrors the Python-side
96    /// `IRProgram.effects: tuple[IREffectDeclaration, ...]` field so
97    /// the byte-identical structural-parity gate stays green. The Rust
98    /// frontend (axon-frontend) does not yet emit Fase 23 IR itself —
99    /// the field exists to preserve serialization shape; the actual
100    /// algebraic-effects compiler lives on the Python side, and the
101    /// Rust runtime (axon-rs/src/effects/) consumes the JSON IR
102    /// emitted by Python.
103    pub effects: Vec<IREffectDeclaration>,
104}
105
106impl IRProgram {
107    pub fn new() -> Self {
108        IRProgram {
109            node_type: "program",
110            source_line: 1,
111            source_column: 1,
112            personas: Vec::new(),
113            contexts: Vec::new(),
114            anchors: Vec::new(),
115            tools: Vec::new(),
116            memories: Vec::new(),
117            types: Vec::new(),
118            flows: Vec::new(),
119            runs: Vec::new(),
120            imports: Vec::new(),
121            agents: Vec::new(),
122            shields: Vec::new(),
123            daemons: Vec::new(),
124            ots_specs: Vec::new(),
125            pix_specs: Vec::new(),
126            corpus_specs: Vec::new(),
127            psyche_specs: Vec::new(),
128            mandate_specs: Vec::new(),
129            lambda_data_specs: Vec::new(),
130            compute_specs: Vec::new(),
131            axonstore_specs: Vec::new(),
132            endpoints: Vec::new(),
133            extensions: Vec::new(),
134            dataspace_specs: Vec::new(),
135            resources: Vec::new(),
136            fabrics: Vec::new(),
137            manifests: Vec::new(),
138            observations: Vec::new(),
139            intention_tree: None,
140            reconciles: Vec::new(),
141            leases: Vec::new(),
142            ensembles: Vec::new(),
143            sessions: Vec::new(),
144            topologies: Vec::new(),
145            immunes: Vec::new(),
146            reflexes: Vec::new(),
147            heals: Vec::new(),
148            components: Vec::new(),
149            views: Vec::new(),
150            channels: Vec::new(),
151            sockets: Vec::new(),
152            effects: Vec::new(),
153        }
154    }
155}
156
157// ── §Fase 23 — Algebraic effect declarations ─────────────────────────────────
158//
159// Mirror of Python's `IREffectDeclaration` and `IREffectOperation`
160// dataclasses. The Rust frontend (axon-frontend) does not yet emit
161// Fase 23 IR itself — these structs exist so the byte-identical
162// structural-parity gate stays green when Python emits an empty
163// `effects: []` field. The actual algebraic-effects compiler lives on
164// the Python side; the Rust runtime (axon-rs/src/effects/) consumes
165// the JSON IR via its own deserialize structs.
166
167#[derive(Debug, Serialize, Default)]
168pub struct IREffectDeclaration {
169    pub node_type: &'static str,
170    pub source_line: u32,
171    pub source_column: u32,
172    pub name: String,
173    pub operations: Vec<IREffectOperation>,
174}
175
176impl IREffectDeclaration {
177    pub fn new() -> Self {
178        Self {
179            node_type: "effect_declaration",
180            source_line: 0,
181            source_column: 0,
182            name: String::new(),
183            operations: Vec::new(),
184        }
185    }
186}
187
188#[derive(Debug, Serialize, Default)]
189pub struct IREffectOperation {
190    pub node_type: &'static str,
191    pub source_line: u32,
192    pub source_column: u32,
193    pub name: String,
194    pub type_parameters: Vec<String>,
195    pub parameter_names: Vec<String>,
196    pub parameter_types: Vec<String>,
197    pub return_type: String,
198}
199
200impl IREffectOperation {
201    pub fn new() -> Self {
202        Self {
203            node_type: "effect_operation",
204            source_line: 0,
205            source_column: 0,
206            name: String::new(),
207            type_parameters: Vec::new(),
208            parameter_names: Vec::new(),
209            parameter_types: Vec::new(),
210            return_type: String::new(),
211        }
212    }
213}
214
215// ── §λ-L-E Fase 1 — IRResource ──────────────────────────────────────────────
216
217/// Compiled resource declaration — linear/affine infrastructure token.
218///
219/// Python counterpart: `axon.compiler.ir_nodes.IRResource`.
220#[derive(Debug, Clone, Serialize)]
221pub struct IRResource {
222    pub node_type: &'static str,
223    pub source_line: u32,
224    pub source_column: u32,
225    pub name: String,
226    pub kind: String,
227    pub endpoint: String,
228    pub capacity: Option<i64>,
229    pub lifetime: String,             // linear | affine | persistent
230    pub certainty_floor: Option<f64>, // c ∈ [0.0, 1.0]
231    pub shield_ref: String,
232}
233
234impl IRResource {
235    pub fn new(name: String, line: u32, column: u32) -> Self {
236        IRResource {
237            node_type: "resource",
238            source_line: line,
239            source_column: column,
240            name,
241            kind: String::new(),
242            endpoint: String::new(),
243            capacity: None,
244            lifetime: "affine".to_string(),
245            certainty_floor: None,
246            shield_ref: String::new(),
247        }
248    }
249}
250
251// ── §λ-L-E Fase 1 — IRFabric ────────────────────────────────────────────────
252
253/// Compiled fabric declaration — topological substrate for resources.
254#[derive(Debug, Clone, Serialize)]
255pub struct IRFabric {
256    pub node_type: &'static str,
257    pub source_line: u32,
258    pub source_column: u32,
259    pub name: String,
260    pub provider: String,
261    pub region: String,
262    pub zones: Option<i64>,
263    pub ephemeral: Option<bool>,
264    pub shield_ref: String,
265}
266
267// ── §λ-L-E Fase 1 — IRManifest ──────────────────────────────────────────────
268
269/// Compiled manifest declaration — declarative belief about desired shape.
270#[derive(Debug, Clone, Serialize)]
271pub struct IRManifest {
272    pub node_type: &'static str,
273    pub source_line: u32,
274    pub source_column: u32,
275    pub name: String,
276    pub resources: Vec<String>,
277    pub fabric_ref: String,
278    pub region: String,
279    pub zones: Option<i64>,
280    pub compliance: Vec<String>,
281}
282
283// ── §λ-L-E Fase 1 — IRObserve ───────────────────────────────────────────────
284
285// ── §λ-L-E Fase 1 — IRIntentionTree (Free Monad root) ──────────────────────
286
287/// A single operation node in the intention tree.
288///
289/// Operations are heterogeneous IR nodes (manifests, observes) that the
290/// Handler layer (Fase 2) interprets via CPS. The enum is `#[serde(untagged)]`
291/// so JSON output is just the inner struct — matching Python's `asdict`
292/// behaviour on a polymorphic `tuple[IRNode, ...]`.
293#[derive(Debug, Clone, Serialize)]
294#[serde(untagged)]
295pub enum IRIntentionOperation {
296    Manifest(IRManifest),
297    Observe(IRObserve),
298}
299
300/// The Free Monad F_Σ(X) — a pure description of I/O intentions. Flat in
301/// Fase 1; nested continuations arrive with handlers + reconcile loops.
302#[derive(Debug, Clone, Serialize)]
303pub struct IRIntentionTree {
304    pub node_type: &'static str,
305    pub source_line: u32,
306    pub source_column: u32,
307    pub operations: Vec<IRIntentionOperation>,
308}
309
310/// Compiled observe declaration — quorum-gated observation with lag τ.
311#[derive(Debug, Clone, Serialize)]
312pub struct IRObserve {
313    pub node_type: &'static str,
314    pub source_line: u32,
315    pub source_column: u32,
316    pub name: String,
317    pub target: String,
318    pub sources: Vec<String>,
319    pub quorum: Option<i64>,
320    pub timeout: String,
321    pub on_partition: String,
322    pub certainty_floor: Option<f64>,
323}
324
325// ── §λ-L-E Fase 3 — IRReconcile / IRLease / IREnsemble ──────────────────────
326
327/// Compiled reconcile declaration — free-energy minimizing control loop.
328#[derive(Debug, Clone, Serialize)]
329pub struct IRReconcile {
330    pub node_type: &'static str,
331    pub source_line: u32,
332    pub source_column: u32,
333    pub name: String,
334    pub observe_ref: String,
335    pub threshold: Option<f64>,
336    pub tolerance: Option<f64>,
337    pub on_drift: String,
338    pub shield_ref: String,
339    pub mandate_ref: String,
340    pub max_retries: i64,
341}
342
343/// Compiled lease declaration — τ-decaying affine resource token.
344#[derive(Debug, Clone, Serialize)]
345pub struct IRLease {
346    pub node_type: &'static str,
347    pub source_line: u32,
348    pub source_column: u32,
349    pub name: String,
350    pub resource_ref: String,
351    pub duration: String,
352    pub acquire: String,
353    pub on_expire: String,
354}
355
356/// Compiled ensemble declaration — Byzantine quorum aggregator.
357#[derive(Debug, Clone, Serialize)]
358pub struct IREnsemble {
359    pub node_type: &'static str,
360    pub source_line: u32,
361    pub source_column: u32,
362    pub name: String,
363    pub observations: Vec<String>,
364    pub quorum: Option<i64>,
365    pub aggregation: String,
366    pub certainty_mode: String,
367}
368
369// ── §λ-L-E Fase 4 — IRSession / IRTopology ──────────────────────────────────
370
371/// One operation in a compiled session protocol
372/// (send / receive / loop / end / select / branch — §Fase 41.b adds the choices).
373#[derive(Debug, Clone, Serialize)]
374pub struct IRSessionStep {
375    pub node_type: &'static str,
376    pub source_line: u32,
377    pub source_column: u32,
378    pub op: String,
379    pub message_type: String,
380    /// §Fase 41.b — labelled branches (only for `op == "select" | "branch"`).
381    #[serde(skip_serializing_if = "Vec::is_empty", default)]
382    pub branches: Vec<IRSessionBranch>,
383}
384
385/// §Fase 41.b — one labelled arm of a compiled `select`/`branch` choice.
386#[derive(Debug, Clone, Serialize)]
387pub struct IRSessionBranch {
388    pub node_type: &'static str,
389    pub label: String,
390    pub steps: Vec<IRSessionStep>,
391}
392
393/// A role's name and its ordered protocol steps.
394#[derive(Debug, Clone, Serialize)]
395pub struct IRSessionRole {
396    pub node_type: &'static str,
397    pub source_line: u32,
398    pub source_column: u32,
399    pub name: String,
400    pub steps: Vec<IRSessionStep>,
401}
402
403/// Compiled binary session — exactly two dual roles (verified at type-check).
404#[derive(Debug, Clone, Serialize)]
405pub struct IRSession {
406    pub node_type: &'static str,
407    pub source_line: u32,
408    pub source_column: u32,
409    pub name: String,
410    pub roles: Vec<IRSessionRole>,
411}
412
413/// Directed, session-typed edge between two topology nodes.
414#[derive(Debug, Clone, Serialize)]
415pub struct IRTopologyEdge {
416    pub node_type: &'static str,
417    pub source_line: u32,
418    pub source_column: u32,
419    pub source: String,
420    pub target: String,
421    pub session_ref: String,
422}
423
424/// Compiled topology — typed graph over Axon entities.
425#[derive(Debug, Clone, Serialize)]
426pub struct IRTopology {
427    pub node_type: &'static str,
428    pub source_line: u32,
429    pub source_column: u32,
430    pub name: String,
431    pub nodes: Vec<String>,
432    pub edges: Vec<IRTopologyEdge>,
433}
434
435// ── §λ-L-E Fase 5 — IRImmune / IRReflex / IRHeal ────────────────────────────
436
437/// Compiled immune sensor — KL+FEP anomaly detector descriptor.
438#[derive(Debug, Clone, Serialize)]
439pub struct IRImmune {
440    pub node_type: &'static str,
441    pub source_line: u32,
442    pub source_column: u32,
443    pub name: String,
444    pub watch: Vec<String>,
445    pub sensitivity: Option<f64>,
446    pub baseline: String,
447    pub window: i64,
448    pub scope: String,
449    pub tau: String,
450    pub decay: String,
451}
452
453/// Compiled reflex — deterministic O(1) motor response descriptor.
454#[derive(Debug, Clone, Serialize)]
455pub struct IRReflex {
456    pub node_type: &'static str,
457    pub source_line: u32,
458    pub source_column: u32,
459    pub name: String,
460    pub trigger: String,
461    pub on_level: String,
462    pub action: String,
463    pub scope: String,
464    pub sla: String,
465}
466
467/// Compiled heal — Linear-Logic one-shot patch kernel descriptor.
468#[derive(Debug, Clone, Serialize)]
469pub struct IRHeal {
470    pub node_type: &'static str,
471    pub source_line: u32,
472    pub source_column: u32,
473    pub name: String,
474    pub source: String,
475    pub on_level: String,
476    pub mode: String,
477    pub scope: String,
478    pub review_sla: String,
479    pub shield_ref: String,
480    pub max_patches: i64,
481}
482
483// ── §λ-L-E Fase 9 — IRComponent / IRView ────────────────────────────────────
484
485/// Compiled UI component — reusable fragment over a typed data source.
486#[derive(Debug, Clone, Serialize)]
487pub struct IRComponent {
488    pub node_type: &'static str,
489    pub source_line: u32,
490    pub source_column: u32,
491    pub name: String,
492    pub renders: String,
493    pub via_shield: String,
494    pub on_interact: String,
495    pub render_hint: String,
496}
497
498/// Compiled UI view — top-level screen composing declared components.
499#[derive(Debug, Clone, Serialize)]
500pub struct IRView {
501    pub node_type: &'static str,
502    pub source_line: u32,
503    pub source_column: u32,
504    pub name: String,
505    pub title: String,
506    pub components: Vec<String>,
507    pub route: String,
508}
509
510// ── Import ───────────────────────────────────────────────────────────────────
511
512#[derive(Debug, Serialize)]
513pub struct IRImport {
514    pub node_type: &'static str,
515    pub source_line: u32,
516    pub source_column: u32,
517    pub module_path: Vec<String>,
518    pub names: Vec<String>,
519}
520
521// ── Persona ──────────────────────────────────────────────────────────────────
522
523#[derive(Debug, Clone, Serialize)]
524pub struct IRPersona {
525    pub node_type: &'static str,
526    pub source_line: u32,
527    pub source_column: u32,
528    pub name: String,
529    pub domain: Vec<String>,
530    pub tone: String,
531    pub confidence_threshold: Option<f64>,
532    pub cite_sources: Option<bool>,
533    pub refuse_if: Vec<String>,
534    pub language: String,
535    pub description: String,
536}
537
538// ── Context ──────────────────────────────────────────────────────────────────
539
540#[derive(Debug, Clone, Serialize)]
541pub struct IRContext {
542    pub node_type: &'static str,
543    pub source_line: u32,
544    pub source_column: u32,
545    pub name: String,
546    pub memory_scope: String,
547    pub language: String,
548    pub depth: String,
549    pub max_tokens: Option<i64>,
550    pub temperature: Option<f64>,
551    pub cite_sources: Option<bool>,
552}
553
554// ── Anchor ───────────────────────────────────────────────────────────────────
555
556#[derive(Debug, Clone, Serialize)]
557pub struct IRAnchor {
558    pub node_type: &'static str,
559    pub source_line: u32,
560    pub source_column: u32,
561    pub name: String,
562    pub description: String,
563    pub require: String,
564    pub reject: Vec<String>,
565    pub enforce: String,
566    pub confidence_floor: Option<f64>,
567    pub unknown_response: String,
568    pub on_violation: String,
569    pub on_violation_target: String,
570}
571
572// ── Tool ─────────────────────────────────────────────────────────────────────
573
574/// §Fase 58.c — one typed parameter of a tool's input schema (the IR mirror of
575/// the AST `Parameter`). `type_name` is the flattened BASE type string
576/// (`String`, `List<String>`); optionality (`T?`) is carried in `optional`, so
577/// `required` is derivable with no parallel bool (§58 D1, single source of
578/// truth). Lossless round-trip is gated in §58.i.
579#[derive(Debug, Clone, Serialize, PartialEq)]
580pub struct IRToolParam {
581    pub name: String,
582    pub type_name: String,
583    pub optional: bool,
584}
585
586/// §Fase 58.c — one bound keyword argument of a `use Tool(k = v, …)` call (the
587/// IR mirror of `UseArgs::Named`). `value` is an expression string (the
588/// frontend has no structured `Expr`). The runtime (§58.e) assembles these into
589/// the structured JSON request body.
590#[derive(Debug, Clone, Serialize, PartialEq)]
591pub struct IRNamedArg {
592    pub name: String,
593    pub value: String,
594    /// §Fase 60 — `"literal"` or `"reference"` (classified by `parse_let_atom`).
595    /// A `"reference"` value (a bare identifier or `Step.output`) is resolved at
596    /// runtime against the bindings (flow-param / `let` / step output), like a
597    /// `let` reference — instead of being passed as the literal name (the pre-60
598    /// bug). `"literal"` values keep `${…}` interpolation + typed coercion.
599    pub value_kind: String,
600}
601
602#[derive(Debug, Serialize)]
603pub struct IRToolSpec {
604    pub node_type: &'static str,
605    pub source_line: u32,
606    pub source_column: u32,
607    pub name: String,
608    pub provider: String,
609    pub max_results: Option<i64>,
610    pub filter_expr: String,
611    pub timeout: String,
612    pub runtime: String,
613    pub sandbox: Option<bool>,
614    pub input_schema: Vec<String>,
615    pub output_schema: String,
616    /// §Fase 58.c — the tool's typed INPUT SCHEMA (D1). Distinct from the §32
617    /// `input_schema`/`output_schema` validation hints (those say HOW to
618    /// validate raw output: JSON/number/…); these are the caller↔tool TYPE
619    /// contract the type-checker enforces (§58.d) and the runtime binds
620    /// structured args against (§58.e). Empty for a schema-less tool (D5).
621    pub parameters: Vec<IRToolParam>,
622    /// §Fase 58.c — the tool's declared OUTPUT type (D8), so `${Step.output}`
623    /// is typed. `None` when undeclared. Single source of truth (lives here,
624    /// not denormalised onto each call site).
625    pub output_type: Option<String>,
626    pub effect_row: Vec<String>,
627}
628
629// ── Memory ───────────────────────────────────────────────────────────────────
630
631#[derive(Debug, Serialize)]
632pub struct IRMemory {
633    pub node_type: &'static str,
634    pub source_line: u32,
635    pub source_column: u32,
636    pub name: String,
637    pub store: String,
638    pub backend: String,
639    pub retrieval: String,
640    pub decay: String,
641}
642
643// ── Type ─────────────────────────────────────────────────────────────────────
644
645#[derive(Debug, Clone, Serialize)]
646pub struct IRTypeField {
647    pub node_type: &'static str,
648    pub source_line: u32,
649    pub source_column: u32,
650    pub name: String,
651    pub type_name: String,
652    pub generic_param: String,
653    pub optional: bool,
654}
655
656#[derive(Debug, Serialize)]
657pub struct IRType {
658    pub node_type: &'static str,
659    pub source_line: u32,
660    pub source_column: u32,
661    pub name: String,
662    pub fields: Vec<IRTypeField>,
663    pub range_min: Option<f64>,
664    pub range_max: Option<f64>,
665    pub where_expression: String,
666    /// §ESK Fase 6.1 — κ regulatory class.
667    pub compliance: Vec<String>,
668}
669
670// ── Flow ─────────────────────────────────────────────────────────────────────
671
672#[derive(Debug, Clone, Serialize)]
673pub struct IRParameter {
674    pub node_type: &'static str,
675    pub source_line: u32,
676    pub source_column: u32,
677    pub name: String,
678    pub type_name: String,
679    pub generic_param: String,
680    pub optional: bool,
681}
682
683#[derive(Debug, Clone, Serialize)]
684pub struct IRDataEdge {
685    pub node_type: &'static str,
686    pub source_line: u32,
687    pub source_column: u32,
688    pub source_step: String,
689    pub target_step: String,
690    pub type_name: String,
691}
692
693#[derive(Debug, Clone, Serialize)]
694pub struct IRStep {
695    pub node_type: &'static str,
696    pub source_line: u32,
697    pub source_column: u32,
698    pub name: String,
699    pub persona_ref: String,
700    pub given: String,
701    pub ask: String,
702    pub use_tool: Option<serde_json::Value>,
703    pub probe: Option<serde_json::Value>,
704    pub reason: Option<serde_json::Value>,
705    pub weave: Option<serde_json::Value>,
706    pub output_type: String,
707    pub confidence_floor: Option<f64>,
708    pub navigate_ref: String,
709    pub apply_ref: String,
710    pub body: Vec<serde_json::Value>,
711}
712
713#[derive(Debug, Clone, Serialize)]
714pub struct IRFlow {
715    pub node_type: &'static str,
716    pub source_line: u32,
717    pub source_column: u32,
718    pub name: String,
719    pub parameters: Vec<IRParameter>,
720    pub return_type_name: String,
721    pub return_type_generic: String,
722    pub return_type_optional: bool,
723    pub steps: Vec<IRFlowNode>,
724    pub edges: Vec<IRDataEdge>,
725    pub execution_levels: Vec<Vec<String>>,
726}
727
728// ── Run ──────────────────────────────────────────────────────────────────────
729
730#[derive(Debug, Serialize)]
731pub struct IRRun {
732    pub node_type: &'static str,
733    pub source_line: u32,
734    pub source_column: u32,
735    pub flow_name: String,
736    pub arguments: Vec<String>,
737    pub persona_name: String,
738    pub context_name: String,
739    pub anchor_names: Vec<String>,
740    pub on_failure: String,
741    pub on_failure_params: Vec<Vec<String>>,
742    pub output_to: String,
743    pub effort: String,
744    pub resolved_flow: Option<IRFlow>,
745    pub resolved_persona: Option<IRPersona>,
746    pub resolved_context: Option<IRContext>,
747    pub resolved_anchors: Vec<IRAnchor>,
748}
749
750// ── Lambda Data (ΛD) — Epistemic State Vectors ─────────────────────────────
751
752#[derive(Debug, Clone, Serialize)]
753pub struct IRLambdaData {
754    pub node_type: &'static str,
755    pub source_line: u32,
756    pub source_column: u32,
757    pub name: String,
758    pub ontology: String,             // T — ontological type
759    pub certainty: f64,               // c ∈ [0,1]
760    pub temporal_frame_start: String, // τ_start
761    pub temporal_frame_end: String,   // τ_end
762    pub provenance: String,           // ρ — EntityRef origin
763    pub derivation: String,           // δ ∈ Δ
764}
765
766#[derive(Debug, Clone, Serialize)]
767pub struct IRLambdaDataApply {
768    pub node_type: &'static str,
769    pub source_line: u32,
770    pub source_column: u32,
771    pub lambda_data_name: String, // reference to declared ΛD
772    pub target: String,           // expression being bound
773    pub output_type: String,      // result type after binding
774}
775
776// ── Flow step IR nodes ──────────────────────────────────────────────────────
777
778/// Polymorphic flow body node — serializes via #[serde(untagged)] so each
779/// variant emits its inner struct's JSON (with its own `node_type` field).
780#[derive(Debug, Clone, Serialize)]
781#[serde(untagged)]
782pub enum IRFlowNode {
783    Step(IRStep),
784    Probe(IRProbe),
785    Reason(IRReasonStep),
786    Validate(IRValidateStep),
787    Refine(IRRefineStep),
788    Weave(IRWeaveStep),
789    UseTool(IRUseToolStep),
790    Remember(IRRememberStep),
791    Recall(IRRecallStep),
792    Conditional(IRConditional),
793    ForIn(IRForIn),
794    Let(IRLetBinding),
795    Return(IRReturnStep),
796    /// Fase 19.e — exit the enclosing for-in body. Payload-free;
797    /// the runner translates it into a sentinel that terminates the
798    /// loop. Parser scope check guarantees this only appears inside
799    /// a for-in body.
800    Break(IRBreakStep),
801    /// Fase 19.e — skip to the next iteration of the enclosing for-in
802    /// body. Same shape as Break — payload-free, sentinel-driven at
803    /// runtime.
804    Continue(IRContinueStep),
805    LambdaDataApply(IRLambdaDataApply),
806    Par(IRParallelBlock),
807    Hibernate(IRHibernateStep),
808    Deliberate(IRDeliberateBlock),
809    Consensus(IRConsensusBlock),
810    Forge(IRForgeBlock),
811    Focus(IRFocusStep),
812    Associate(IRAssociateStep),
813    Aggregate(IRAggregateStep),
814    Explore(IRExploreStep),
815    Ingest(IRIngestStep),
816    ShieldApply(IRShieldApplyStep),
817    Stream(IRStreamBlock),
818    Navigate(IRNavigateStep),
819    Drill(IRDrillStep),
820    Trail(IRTrailStep),
821    Corroborate(IRCorroborateStep),
822    OtsApply(IROtsApplyStep),
823    MandateApply(IRMandateApplyStep),
824    ComputeApply(IRComputeApplyStep),
825    Listen(IRListenStep),
826    DaemonStep(IRDaemonStepNode),
827    /// §λ-L-E Fase 13 — π-calc output prefix (Chan-Output / Chan-Mobility).
828    Emit(IREmit),
829    /// §λ-L-E Fase 13 — capability extrusion (Publish-Ext).
830    Publish(IRPublish),
831    /// §λ-L-E Fase 13 — dual of publish (typed handle import).
832    Discover(IRDiscover),
833    Persist(IRPersistStep),
834    Retrieve(IRRetrieveStep),
835    Mutate(IRMutateStep),
836    Purge(IRPurgeStep),
837    Transact(IRTransactBlock),
838}
839
840#[derive(Debug, Clone, Serialize)]
841pub struct IRProbe {
842    pub node_type: &'static str,
843    pub source_line: u32,
844    pub source_column: u32,
845    pub target: String,
846}
847
848#[derive(Debug, Clone, Serialize)]
849pub struct IRReasonStep {
850    pub node_type: &'static str,
851    pub source_line: u32,
852    pub source_column: u32,
853    pub strategy: String,
854    pub target: String,
855}
856
857#[derive(Debug, Clone, Serialize)]
858pub struct IRValidateStep {
859    pub node_type: &'static str,
860    pub source_line: u32,
861    pub source_column: u32,
862    pub target: String,
863    pub rule: String,
864}
865
866#[derive(Debug, Clone, Serialize)]
867pub struct IRRefineStep {
868    pub node_type: &'static str,
869    pub source_line: u32,
870    pub source_column: u32,
871    pub target: String,
872    pub strategy: String,
873}
874
875#[derive(Debug, Clone, Serialize)]
876pub struct IRWeaveStep {
877    pub node_type: &'static str,
878    pub source_line: u32,
879    pub source_column: u32,
880    pub sources: Vec<String>,
881    pub target: String,
882    pub format_type: String,
883    pub priority: Vec<String>,
884    pub style: String,
885}
886
887#[derive(Debug, Clone, Serialize)]
888pub struct IRUseToolStep {
889    pub node_type: &'static str,
890    pub source_line: u32,
891    pub source_column: u32,
892    pub tool_name: String,
893    pub argument: String,
894    /// §Fase 58.c — the bound keyword args of `use Tool(k = v, …)` (W1: the
895    /// structured args survive to the IR, no longer collapsed to one opaque
896    /// string). Empty for the legacy single-`on <arg>` form (`argument`
897    /// carries that, D5).
898    pub named_args: Vec<IRNamedArg>,
899}
900
901#[derive(Debug, Clone, Serialize)]
902pub struct IRRememberStep {
903    pub node_type: &'static str,
904    pub source_line: u32,
905    pub source_column: u32,
906    pub expression: String,
907    pub memory_target: String,
908}
909
910#[derive(Debug, Clone, Serialize)]
911pub struct IRRecallStep {
912    pub node_type: &'static str,
913    pub source_line: u32,
914    pub source_column: u32,
915    pub query: String,
916    pub memory_source: String,
917}
918
919#[derive(Debug, Clone, Serialize)]
920pub struct IRConditional {
921    pub node_type: &'static str,
922    pub source_line: u32,
923    pub source_column: u32,
924    pub condition: String,
925    pub comparison_op: String,
926    pub comparison_value: String,
927    pub then_body: Vec<IRFlowNode>,
928    pub else_body: Vec<IRFlowNode>,
929    pub conditions: Vec<(String, String, String)>,
930    pub conjunctor: String,
931}
932
933#[derive(Debug, Clone, Serialize)]
934pub struct IRForIn {
935    pub node_type: &'static str,
936    pub source_line: u32,
937    pub source_column: u32,
938    pub variable: String,
939    pub iterable: String,
940    pub body: Vec<IRFlowNode>,
941}
942
943#[derive(Debug, Clone, Serialize)]
944pub struct IRLetBinding {
945    pub node_type: &'static str,
946    pub source_line: u32,
947    pub source_column: u32,
948    pub target: String,
949    pub value: String,
950    /// Fase 17.a — preserves parser tokenization intent.
951    /// One of "literal" | "reference" | "expression".
952    pub value_kind: String,
953}
954
955#[derive(Debug, Clone, Serialize)]
956pub struct IRReturnStep {
957    pub node_type: &'static str,
958    pub source_line: u32,
959    pub source_column: u32,
960    pub value_expr: String,
961}
962
963/// Fase 19.e — `break` keyword IR node. Payload-free (the runner
964/// raises a sentinel; no value is carried). Mirrors Python's
965/// ``IRBreak`` (axon/compiler/ir_nodes.py).
966#[derive(Debug, Clone, Serialize)]
967pub struct IRBreakStep {
968    pub node_type: &'static str,
969    pub source_line: u32,
970    pub source_column: u32,
971}
972
973/// Fase 19.e — `continue` keyword IR node. Same shape as
974/// ``IRBreakStep``; the runner uses a different sentinel type to
975/// distinguish loop-exit (break) from iteration-skip (continue).
976#[derive(Debug, Clone, Serialize)]
977pub struct IRContinueStep {
978    pub node_type: &'static str,
979    pub source_line: u32,
980    pub source_column: u32,
981}
982
983#[derive(Debug, Clone, Serialize)]
984pub struct IRParallelBlock {
985    pub node_type: &'static str,
986    pub source_line: u32,
987    pub source_column: u32,
988}
989
990#[derive(Debug, Clone, Serialize)]
991pub struct IRHibernateStep {
992    pub node_type: &'static str,
993    pub source_line: u32,
994    pub source_column: u32,
995    pub event_name: String,
996    pub timeout: String,
997}
998
999#[derive(Debug, Clone, Serialize)]
1000pub struct IRDeliberateBlock {
1001    pub node_type: &'static str,
1002    pub source_line: u32,
1003    pub source_column: u32,
1004}
1005
1006#[derive(Debug, Clone, Serialize)]
1007pub struct IRConsensusBlock {
1008    pub node_type: &'static str,
1009    pub source_line: u32,
1010    pub source_column: u32,
1011}
1012
1013#[derive(Debug, Clone, Serialize)]
1014pub struct IRForgeBlock {
1015    pub node_type: &'static str,
1016    pub source_line: u32,
1017    pub source_column: u32,
1018}
1019
1020#[derive(Debug, Clone, Serialize)]
1021pub struct IRFocusStep {
1022    pub node_type: &'static str,
1023    pub source_line: u32,
1024    pub source_column: u32,
1025    pub expression: String,
1026}
1027
1028#[derive(Debug, Clone, Serialize)]
1029pub struct IRAssociateStep {
1030    pub node_type: &'static str,
1031    pub source_line: u32,
1032    pub source_column: u32,
1033    pub left: String,
1034    pub right: String,
1035    pub using_field: String,
1036}
1037
1038#[derive(Debug, Clone, Serialize)]
1039pub struct IRAggregateStep {
1040    pub node_type: &'static str,
1041    pub source_line: u32,
1042    pub source_column: u32,
1043    pub target: String,
1044    pub group_by: Vec<String>,
1045    pub alias: String,
1046}
1047
1048#[derive(Debug, Clone, Serialize)]
1049pub struct IRExploreStep {
1050    pub node_type: &'static str,
1051    pub source_line: u32,
1052    pub source_column: u32,
1053    pub target: String,
1054    pub limit: Option<i64>,
1055}
1056
1057#[derive(Debug, Clone, Serialize)]
1058pub struct IRIngestStep {
1059    pub node_type: &'static str,
1060    pub source_line: u32,
1061    pub source_column: u32,
1062    pub source: String,
1063    pub target: String,
1064}
1065
1066#[derive(Debug, Clone, Serialize)]
1067pub struct IRShieldApplyStep {
1068    pub node_type: &'static str,
1069    pub source_line: u32,
1070    pub source_column: u32,
1071    pub shield_name: String,
1072    pub target: String,
1073    pub output_type: String,
1074}
1075
1076#[derive(Debug, Clone, Serialize)]
1077pub struct IRStreamBlock {
1078    pub node_type: &'static str,
1079    pub source_line: u32,
1080    pub source_column: u32,
1081}
1082
1083#[derive(Debug, Clone, Serialize)]
1084pub struct IRNavigateStep {
1085    pub node_type: &'static str,
1086    pub source_line: u32,
1087    pub source_column: u32,
1088    pub pix_ref: String,
1089    pub corpus_ref: String,
1090    pub query: String,
1091    pub trail_enabled: bool,
1092    pub output_name: String,
1093}
1094
1095#[derive(Debug, Clone, Serialize)]
1096pub struct IRDrillStep {
1097    pub node_type: &'static str,
1098    pub source_line: u32,
1099    pub source_column: u32,
1100    pub pix_ref: String,
1101    pub subtree_path: String,
1102    pub query: String,
1103    pub output_name: String,
1104}
1105
1106#[derive(Debug, Clone, Serialize)]
1107pub struct IRTrailStep {
1108    pub node_type: &'static str,
1109    pub source_line: u32,
1110    pub source_column: u32,
1111    pub navigate_ref: String,
1112}
1113
1114#[derive(Debug, Clone, Serialize)]
1115pub struct IRCorroborateStep {
1116    pub node_type: &'static str,
1117    pub source_line: u32,
1118    pub source_column: u32,
1119    pub navigate_ref: String,
1120    pub output_name: String,
1121}
1122
1123#[derive(Debug, Clone, Serialize)]
1124pub struct IROtsApplyStep {
1125    pub node_type: &'static str,
1126    pub source_line: u32,
1127    pub source_column: u32,
1128    pub ots_name: String,
1129    pub target: String,
1130    pub output_type: String,
1131}
1132
1133#[derive(Debug, Clone, Serialize)]
1134pub struct IRMandateApplyStep {
1135    pub node_type: &'static str,
1136    pub source_line: u32,
1137    pub source_column: u32,
1138    pub mandate_name: String,
1139    pub target: String,
1140    pub output_type: String,
1141}
1142
1143#[derive(Debug, Clone, Serialize)]
1144pub struct IRComputeApplyStep {
1145    pub node_type: &'static str,
1146    pub source_line: u32,
1147    pub source_column: u32,
1148    pub compute_name: String,
1149    pub arguments: Vec<String>,
1150    pub output_name: String,
1151}
1152
1153#[derive(Debug, Clone, Serialize)]
1154pub struct IRListenStep {
1155    pub node_type: &'static str,
1156    pub source_line: u32,
1157    pub source_column: u32,
1158    pub channel: String,
1159    /// §λ-L-E Fase 13 D4 — true ⇒ `channel` is a declared
1160    /// `IRChannel` ref; false ⇒ legacy string topic.
1161    pub channel_is_ref: bool,
1162    pub event_alias: String,
1163}
1164
1165#[derive(Debug, Clone, Serialize)]
1166pub struct IRDaemonStepNode {
1167    pub node_type: &'static str,
1168    pub source_line: u32,
1169    pub source_column: u32,
1170    pub daemon_ref: String,
1171}
1172
1173#[derive(Debug, Clone, Serialize)]
1174pub struct IRPersistStep {
1175    pub node_type: &'static str,
1176    pub source_line: u32,
1177    pub source_column: u32,
1178    pub store_name: String,
1179    /// §Fase 35.o — declared `{ col: value }` field block (value
1180    /// expressions kept raw; interpolated at runtime). Empty ⇒ the
1181    /// runtime writes the flow's user bindings (v1.30.0 fallback).
1182    pub fields: Vec<(String, String)>,
1183}
1184
1185#[derive(Debug, Clone, Serialize)]
1186pub struct IRRetrieveStep {
1187    pub node_type: &'static str,
1188    pub source_line: u32,
1189    pub source_column: u32,
1190    pub store_name: String,
1191    pub where_expr: String,
1192    pub alias: String,
1193}
1194
1195#[derive(Debug, Clone, Serialize)]
1196pub struct IRMutateStep {
1197    pub node_type: &'static str,
1198    pub source_line: u32,
1199    pub source_column: u32,
1200    pub store_name: String,
1201    pub where_expr: String,
1202    /// §Fase 35.p — declared `{ col: value }` SET assignments (value
1203    /// expressions kept raw; interpolated at runtime). Empty ⇒ the
1204    /// runtime writes the flow's user bindings (v1.31.0 fallback).
1205    pub fields: Vec<(String, String)>,
1206}
1207
1208#[derive(Debug, Clone, Serialize)]
1209pub struct IRPurgeStep {
1210    pub node_type: &'static str,
1211    pub source_line: u32,
1212    pub source_column: u32,
1213    pub store_name: String,
1214    pub where_expr: String,
1215}
1216
1217#[derive(Debug, Clone, Serialize)]
1218pub struct IRTransactBlock {
1219    pub node_type: &'static str,
1220    pub source_line: u32,
1221    pub source_column: u32,
1222}
1223
1224// ── Tier 2 IR nodes ─────────────────────────────────────────────────────────
1225
1226#[derive(Debug, Clone, Serialize)]
1227pub struct IRAgent {
1228    pub node_type: &'static str,
1229    pub source_line: u32,
1230    pub source_column: u32,
1231    pub name: String,
1232    pub goal: String,
1233    pub tools: Vec<String>,
1234    pub memory_ref: String,
1235    pub strategy: String,
1236    pub on_stuck: String,
1237    pub shield_ref: String,
1238    pub max_iterations: Option<i64>,
1239    pub max_tokens: Option<i64>,
1240    pub max_time: String,
1241    pub max_cost: Option<f64>,
1242}
1243
1244#[derive(Debug, Clone, Serialize)]
1245pub struct IRShield {
1246    pub node_type: &'static str,
1247    pub source_line: u32,
1248    pub source_column: u32,
1249    pub name: String,
1250    pub scan: Vec<String>,
1251    pub strategy: String,
1252    pub on_breach: String,
1253    pub severity: String,
1254    pub quarantine: String,
1255    /// §8.2.h.3 — Python emits concrete 0, not null. AST keeps `Option<i64>`
1256    /// so the parser can distinguish "not set"; IR lowering collapses.
1257    pub max_retries: i64,
1258    pub confidence_threshold: f64,
1259    pub allow_tools: Vec<String>,
1260    pub deny_tools: Vec<String>,
1261    pub sandbox: bool,
1262    pub redact: Vec<String>,
1263    pub log: String,
1264    pub deflect_message: String,
1265    // `taint` exists on `ShieldDefinition` (AST) but Python's reference
1266    // IRShield doesn't emit it. Hidden from JSON output for §8.2.h parity.
1267    #[serde(skip)]
1268    pub taint: String,
1269    /// §ESK Fase 6.1 — covered regulatory classes for this shield.
1270    pub compliance: Vec<String>,
1271}
1272
1273#[derive(Debug, Clone, Serialize)]
1274pub struct IRPix {
1275    pub node_type: &'static str,
1276    pub source_line: u32,
1277    pub source_column: u32,
1278    pub name: String,
1279    pub source: String,
1280    pub depth: Option<i64>,
1281    pub branching: Option<i64>,
1282    pub model: String,
1283}
1284
1285#[derive(Debug, Clone, Serialize)]
1286pub struct IRPsyche {
1287    pub node_type: &'static str,
1288    pub source_line: u32,
1289    pub source_column: u32,
1290    pub name: String,
1291    pub dimensions: Vec<String>,
1292    pub manifold_noise: Option<f64>,
1293    pub manifold_momentum: Option<f64>,
1294    pub safety_constraints: Vec<String>,
1295    pub quantum_enabled: Option<bool>,
1296    pub inference_mode: String,
1297}
1298
1299#[derive(Debug, Clone, Serialize)]
1300pub struct IRCorpus {
1301    pub node_type: &'static str,
1302    pub source_line: u32,
1303    pub source_column: u32,
1304    pub name: String,
1305    pub documents: Vec<String>,
1306    pub mcp_server: String,
1307    pub mcp_resource_uri: String,
1308}
1309
1310#[derive(Debug, Clone, Serialize)]
1311pub struct IRDataspace {
1312    pub node_type: &'static str,
1313    pub source_line: u32,
1314    pub source_column: u32,
1315    pub name: String,
1316}
1317
1318#[derive(Debug, Clone, Serialize)]
1319pub struct IROts {
1320    pub node_type: &'static str,
1321    pub source_line: u32,
1322    pub source_column: u32,
1323    pub name: String,
1324    pub teleology: String,
1325    pub homotopy_search: String,
1326    pub loss_function: String,
1327}
1328
1329#[derive(Debug, Clone, Serialize)]
1330pub struct IRMandate {
1331    pub node_type: &'static str,
1332    pub source_line: u32,
1333    pub source_column: u32,
1334    pub name: String,
1335    pub constraint: String,
1336    pub kp: Option<f64>,
1337    pub ki: Option<f64>,
1338    pub kd: Option<f64>,
1339    pub tolerance: Option<f64>,
1340    pub max_steps: Option<i64>,
1341    pub on_violation: String,
1342}
1343
1344#[derive(Debug, Clone, Serialize)]
1345pub struct IRCompute {
1346    pub node_type: &'static str,
1347    pub source_line: u32,
1348    pub source_column: u32,
1349    pub name: String,
1350    pub shield_ref: String,
1351}
1352
1353#[derive(Debug, Clone, Serialize)]
1354pub struct IRDaemon {
1355    pub node_type: &'static str,
1356    pub source_line: u32,
1357    pub source_column: u32,
1358    pub name: String,
1359    pub goal: String,
1360    pub tools: Vec<String>,
1361    pub memory_ref: String,
1362    pub strategy: String,
1363    pub on_stuck: String,
1364    pub shield_ref: String,
1365    pub max_tokens: Option<i64>,
1366    pub max_time: String,
1367    pub max_cost: Option<f64>,
1368}
1369
1370// ── §Fase 53 — Closed-catalog extension mechanism ────────────────────────────
1371
1372/// §Fase 53 — one compiled member of an `extension`. For `effects`
1373/// the `name` is a provenance base; `default_confidence` is a CEILING
1374/// (§53.d tainted-overriding). Metadata is elided from JSON when absent
1375/// so the serialised shape stays minimal once the `extensions` field is
1376/// un-skipped alongside the Python IR mirror.
1377#[derive(Debug, Clone, Serialize)]
1378pub struct IRExtensionMember {
1379    pub name: String,
1380    #[serde(default, skip_serializing_if = "Option::is_none")]
1381    pub semantics: Option<String>,
1382    #[serde(default, skip_serializing_if = "Option::is_none")]
1383    pub default_confidence: Option<f64>,
1384}
1385
1386/// §Fase 53 — a compiled `extension` declaration. Rides in the IR (and,
1387/// once un-skipped, the proof bundle) so an independent PCC verifier
1388/// re-derives `is_known_base` against the artifact's own extensions
1389/// (soundness invariant #1). `category` ∈ {`effects`, `scan`} — the
1390/// type-checker (§53.c) enforces the closed category + no-shadowing +
1391/// provenance-class invariants before this IR is trusted.
1392#[derive(Debug, Clone, Serialize)]
1393pub struct IRExtension {
1394    pub node_type: &'static str,
1395    pub source_line: u32,
1396    pub source_column: u32,
1397    pub name: String,
1398    pub category: String,
1399    pub members: Vec<IRExtensionMember>,
1400}
1401
1402#[derive(Debug, Clone, Serialize)]
1403pub struct IRAxonStore {
1404    pub node_type: &'static str,
1405    pub source_line: u32,
1406    pub source_column: u32,
1407    pub name: String,
1408    pub backend: String,
1409    pub connection: String,
1410    pub confidence_floor: Option<f64>,
1411    pub isolation: String,
1412    pub on_breach: String,
1413    /// §Fase 35.j (D11) — Pillar IV: the capability slug required to
1414    /// access this store (empty = no gate).
1415    pub capability: String,
1416    /// §Fase 38.b (D1) — the OPTIONAL column-schema declaration. Three
1417    /// closed forms (inline / manifest-ref / env-var). `None` means the
1418    /// 37.x runtime+deploy path applies verbatim (D5 absolute). The
1419    /// §38.d / §38.e type-checker proves every store reference against
1420    /// this when present.
1421    #[serde(default, skip_serializing_if = "Option::is_none")]
1422    pub column_schema: Option<IRStoreColumnSchema>,
1423}
1424
1425/// §Fase 38.b (D1) — IR mirror of [`crate::store_schema::StoreColumnSchema`].
1426/// Serializes as a tagged union: `{"form": "inline" | "manifest_ref" |
1427/// "env_var", …}`.
1428#[derive(Debug, Clone, Serialize)]
1429#[serde(tag = "form", rename_all = "snake_case")]
1430pub enum IRStoreColumnSchema {
1431    Inline { columns: Vec<IRStoreColumn> },
1432    ManifestRef { qualified_name: String },
1433    EnvVar { var_name: String },
1434}
1435
1436/// §Fase 38.b (D1) — IR mirror of [`crate::store_schema::StoreColumn`].
1437/// The serialized `col_type` is the canonical PascalCase name (e.g.
1438/// `"Uuid"`, `"Int"`, `"Timestamptz"`).
1439#[derive(Debug, Clone, Serialize)]
1440pub struct IRStoreColumn {
1441    pub name: String,
1442    pub col_type: String,
1443    #[serde(default, skip_serializing_if = "is_false")]
1444    pub primary_key: bool,
1445    #[serde(default, skip_serializing_if = "is_false")]
1446    pub auto_increment: bool,
1447    #[serde(default, skip_serializing_if = "is_false")]
1448    pub not_null: bool,
1449    #[serde(default, skip_serializing_if = "is_false")]
1450    pub unique: bool,
1451    #[serde(default, skip_serializing_if = "String::is_empty")]
1452    pub default_value: String,
1453    /// §Fase 38.x.c (D2, D5) — `true` iff the column is declared with
1454    /// `GENERATED ALWAYS AS IDENTITY` or `GENERATED BY DEFAULT AS
1455    /// IDENTITY`. Distinct from `auto_increment` (legacy SERIAL via
1456    /// `nextval(...)` default). `skip_serializing_if` keeps IR JSON
1457    /// byte-identical to v1.38.2 for any column where `identity = false`.
1458    #[serde(default, skip_serializing_if = "is_false")]
1459    pub identity: bool,
1460}
1461
1462#[inline]
1463fn is_false(b: &bool) -> bool {
1464    !*b
1465}
1466
1467#[derive(Debug, Clone, Serialize)]
1468pub struct IRAxonEndpoint {
1469    pub node_type: &'static str,
1470    pub source_line: u32,
1471    pub source_column: u32,
1472    pub name: String,
1473    pub method: String,
1474    pub path: String,
1475    pub body_type: String,
1476    pub execute_flow: String,
1477    pub output_type: String,
1478    pub shield_ref: String,
1479    /// §8.2.h.3 — Python emits concrete `0`; AST stays `Option<i64>`.
1480    pub retries: i64,
1481    pub timeout: String,
1482    /// §ESK Fase 6.1 — κ regulatory class on the boundary.
1483    pub compliance: Vec<String>,
1484    /// §Fase 37.y (D1) — Path parameter names extracted from the
1485    /// `path:` string. Mirrors `AxonEndpointDefinition.path_params`.
1486    /// **`skip_serializing_if = Vec::is_empty`** so a pre-v1.38.5 IR
1487    /// JSON snapshot (without the field) is byte-identical to a
1488    /// v1.38.5 IR JSON for the same endpoint — D5 backwards-compat
1489    /// absolute. The runtime + adopter tools that consume the IR
1490    /// JSON parse `path_params` as an absent key → empty Vec via
1491    /// serde's `default` semantics.
1492    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1493    pub path_params: Vec<String>,
1494    /// §Fase 37.y (D2) — Query parameters from the inline
1495    /// `query: { … }` block. Mirrors `AxonEndpointDefinition.query_params`
1496    /// using `IRTypeField` (shared with body type fields → uniform
1497    /// downstream tooling). **`skip_serializing_if = Vec::is_empty`**
1498    /// — same D5 IR-JSON byte-identity guarantee as `path_params`.
1499    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1500    pub query_params: Vec<IRTypeField>,
1501    /// §Fase 51.x — capability scopes the request bearer must hold
1502    /// (the `requires: [scope.dotted]` declaration, §Fase 32.g). Mirror
1503    /// of `AxonEndpointDefinition.requires_capabilities`, lowered into
1504    /// the IR so the PCC CapabilityContainment property can prove that
1505    /// the stores this endpoint's flow reaches are all covered by the
1506    /// declared requires. **`skip_serializing_if = Vec::is_empty`** so a
1507    /// pre-§51.x IR-JSON snapshot (no `requires:`) stays byte-identical
1508    /// (D5 backwards-compat — empty key parses back to empty Vec).
1509    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1510    pub requires_capabilities: Vec<String>,
1511}
1512
1513// ── §λ-L-E Fase 13 — Mobile Typed Channels IR ───────────────────────────────
1514
1515/// Compiled `channel Name { … }` declaration.
1516///
1517/// Direct port of `axon.compiler.ir_nodes.IRChannel`.  Lives in
1518/// `IRProgram.channels`; emit/publish/discover reductions embed in
1519/// their containing flow/listener (paper §3 + §4 — π-calc prefix
1520/// discipline preserved structurally, not lifted to top-level ops).
1521#[derive(Debug, Clone, Serialize)]
1522pub struct IRChannel {
1523    pub node_type: &'static str,
1524    pub source_line: u32,
1525    pub source_column: u32,
1526    pub name: String,
1527    pub message: String, // surface spelling — Order | Channel<Order> | …
1528    pub qos: String,
1529    pub lifetime: String,
1530    pub persistence: String,
1531    pub shield_ref: String,
1532}
1533
1534/// §Fase 41.b — compiled typed WebSocket transport. `protocol` names the
1535/// `session` it carries; `backpressure_credit` is the typed-resource window
1536/// (`null` if unspecified). axon-rs realises the endpoint over a `tokio` WS,
1537/// crediting/decrementing the window per §4.2 of the paper.
1538#[derive(Debug, Clone, Serialize)]
1539pub struct IRSocket {
1540    pub node_type: &'static str,
1541    pub source_line: u32,
1542    pub source_column: u32,
1543    pub name: String,
1544    pub protocol: String,
1545    pub backpressure_credit: Option<i64>,
1546    pub reconnect: bool,
1547    pub legal_basis: Option<String>,
1548}
1549
1550/// Compiled emit step — `c⟨v⟩.P` (Chan-Output / Chan-Mobility).
1551///
1552/// `value_is_channel = true` ⇒ resolved at lowering time as a channel
1553/// handle (second-order mobility, paper §3.2); the runtime dispatches
1554/// on this flag without re-resolving symbols.
1555#[derive(Debug, Clone, Serialize)]
1556pub struct IREmit {
1557    pub node_type: &'static str,
1558    pub source_line: u32,
1559    pub source_column: u32,
1560    pub channel_ref: String,
1561    pub value_ref: String,
1562    pub value_is_channel: bool,
1563}
1564
1565/// Compiled publish step — capability extrusion (Publish-Ext, paper §4.3).
1566#[derive(Debug, Clone, Serialize)]
1567pub struct IRPublish {
1568    pub node_type: &'static str,
1569    pub source_line: u32,
1570    pub source_column: u32,
1571    pub channel_ref: String,
1572    pub shield_ref: String,
1573}
1574
1575/// Compiled discover step — dual of publish.
1576#[derive(Debug, Clone, Serialize)]
1577pub struct IRDiscover {
1578    pub node_type: &'static str,
1579    pub source_line: u32,
1580    pub source_column: u32,
1581    pub capability_ref: String,
1582    pub alias: String,
1583}