Skip to main content

axon_frontend/
ast.rs

1//! AXON AST node definitions — direct port of axon/compiler/ast_nodes.py.
2//!
3//! Tier 1 constructs have fully typed structs.
4//! Tier 2+ constructs use `GenericDeclaration` (structural fallback).
5
6#![allow(dead_code)]
7
8use crate::tokens::Trivia;
9
10// ── Location helper ──────────────────────────────────────────────────────────
11
12/// Source location shared by all AST nodes.
13#[derive(Debug, Clone, Default)]
14pub struct Loc {
15    pub line: u32,
16    pub column: u32,
17}
18
19// ── Trivia channel (Fase 14.a — Lossless lexing) ─────────────────────────────
20//
21// The Python AST attaches `leading_trivia` / `trailing_trivia` directly
22// to each ASTNode (97+ subclasses inherit empty defaults). The Rust
23// structs do not have inheritance, so adding two `Vec<Trivia>` fields
24// to every node would require touching each of the 97 structs and
25// every fixture test that constructs them — high mechanical churn for
26// a use case (LSP / formatter / doc gen) that can be served just as
27// well by indexing trivia by declaration position.
28//
29// `DeclarationTrivia` is a side-channel attached to `Program`. The
30// parser populates it in lockstep with `declarations` so consumer code
31// can do `program.declaration_trivia[i]` to get the leading/trailing
32// trivia of `program.declarations[i]`. This preserves the AST shape
33// (no breaking changes), keeps `IRProgram` JSON byte-identical with
34// the Python reference (trivia is never serialised), and ships the
35// adopter-reported feature end-to-end.
36//
37// If a future sub-phase wants per-node trivia inside the AST itself
38// (mirror of the Python ASTNode shape), this side-channel is the seed:
39// every `DeclarationTrivia` already carries the data; spreading it
40// into the structs is a mechanical refactor at that point.
41
42/// Comments attached to a single top-level declaration. Indexed
43/// in parallel with `Program.declarations`.
44#[derive(Debug, Clone, Default)]
45pub struct DeclarationTrivia {
46    /// Comment trivia that appeared before the declaration's first
47    /// token (since the previous declaration or file start).
48    pub leading: Vec<Trivia>,
49    /// Comment trivia on the same line as the declaration's last
50    /// effective token, before the next newline.
51    pub trailing: Vec<Trivia>,
52}
53
54// ── Top-level ────────────────────────────────────────────────────────────────
55
56#[derive(Debug)]
57pub struct Program {
58    pub declarations: Vec<Declaration>,
59    /// Fase 14.a — comment trivia attached per declaration (parallel
60    /// with `declarations`). Empty by default; populated by the parser
61    /// when source carries comments. Defaults preserve every existing
62    /// `Program { declarations, loc }` constructor — `..Default::default()`
63    /// on a `Program` literal fills in the new field.
64    pub declaration_trivia: Vec<DeclarationTrivia>,
65    pub loc: Loc,
66}
67
68/// A single top-level declaration in an AXON program.
69#[derive(Debug)]
70pub enum Declaration {
71    Import(ImportNode),
72    Persona(PersonaDefinition),
73    Context(ContextDefinition),
74    Anchor(AnchorConstraint),
75    Memory(MemoryDefinition),
76    Tool(ToolDefinition),
77    Type(TypeDefinition),
78    Flow(FlowDefinition),
79    Intent(IntentNode),
80    Run(RunStatement),
81    Epistemic(EpistemicBlock),
82    Let(LetStatement),
83    /// Lambda Data (ΛD) — Epistemic State Vector definition.
84    LambdaData(LambdaDataDefinition),
85    // ── Tier 2 declarations (full AST) ──
86    Agent(AgentDefinition),
87    Shield(ShieldDefinition),
88    /// §Fase 71.a — a temporal execution-window guard.
89    Window(WindowDefinition),
90    /// §Fase 114.a — a TOP-LEVEL `budget <Name> { … }`. Governs every flow that
91    /// calls the tools its quotas name — not just a daemon's.
92    Budget(BudgetBlock),
93    Pix(PixDefinition),
94    Ledger(LedgerDefinition),
95    Psyche(PsycheDefinition),
96    Corpus(CorpusDefinition),
97    Dataspace(DataspaceDefinition),
98    Ots(OtsDefinition),
99    Mandate(MandateDefinition),
100    Compute(ComputeDefinition),
101    Daemon(DaemonDefinition),
102    AxonStore(AxonStoreDefinition),
103    AxonEndpoint(AxonEndpointDefinition),
104    /// §Fase 53 — Closed-catalog extension mechanism. Declares
105    /// adopter-specific PROVENANCE members for a closed catalog
106    /// (`effects` bases or shield `scan` categories) so the
107    /// type-checker + PCC treat them as first-class. Auditable +
108    /// gateable; never extends the enforceable effect set (invariant
109    /// #2 — provenance-class only).
110    Extension(ExtensionDefinition),
111    /// §λ-L-E Fase 1 — I/O cognitivo primitives.
112    Resource(ResourceDefinition),
113    Fabric(FabricDefinition),
114    Manifest(ManifestDefinition),
115    Observe(ObserveDefinition),
116    /// §λ-L-E Fase 3 — Control cognitivo primitives.
117    Reconcile(ReconcileDefinition),
118    Lease(LeaseDefinition),
119    Ensemble(EnsembleDefinition),
120    /// §λ-L-E Fase 4 — Topology + π-calculus binary sessions.
121    Session(SessionDefinition),
122    Topology(TopologyDefinition),
123    /// §λ-L-E Fase 5 — Cognitive immune system (per docs/paper_immune_v2.md).
124    Immune(ImmuneDefinition),
125    Reflex(ReflexDefinition),
126    Heal(HealDefinition),
127    /// §λ-L-E Fase 9 — UI cognitiva declarativa.
128    Component(ComponentDefinition),
129    View(ViewDefinition),
130    /// §λ-L-E Fase 13 — Mobile typed channels (paper_mobile_channels.md).
131    Channel(ChannelDefinition),
132    /// §Fase 41.b — typed WebSocket transport binding a `session` protocol
133    /// (paper_websocket_cognitive_primitive.md).
134    Socket(SocketDefinition),
135    /// §Fase 80.b — the dual transport role of `socket`: a persistent,
136    /// config-resolved OUTBOUND connection to a third-party vendor, typed by
137    /// the same §41.a session algebra on the axon-facing side and transcoded
138    /// to the vendor's wire frames by a declared total projection
139    /// (docs/fase/fase_80_upstream_design.md).
140    Upstream(UpstreamDefinition),
141    /// §Fase 80.g — the voice-agent simplicity layer: macro-expands
142    /// (inspectable via `axon desugar`, D80.6) to `ots` + carrier
143    /// `session`/`socket` + `upstream` legs. The declaration stays in the
144    /// AST for provenance + §80.c validation (T852); the IR carries only
145    /// the expansion — sugar the compliance reviewer can always see through.
146    Voice(VoiceDefinition),
147    /// §Fase 83.a — a named, referenced browser-origin policy (mirrors
148    /// `shield`'s shape exactly), resolved per `axonendpoint.cors:`
149    /// reference (docs/fase/fase_83_cors_first_class_endpoint_property.md).
150    Cors(CorsDefinition),
151    /// §Fase 85.a — a named, referenced result-memoization policy (mirrors
152    /// `cors`'s shape), resolved per `tool.cache:` / `retrieve.cache:`
153    /// (docs/fase/fase_85_native_cache_primitive.md).
154    Cache(CacheDefinition),
155    /// §Fase 87.a — the long-horizon autonomous research primitive: a governed
156    /// ORCHESTRATOR (not a monolith) that composes existing primitives
157    /// (`memory`/`corpus`, `par`, `quant`, `forge`, `daemon`, the §72 budget,
158    /// the §79 interruptible session, the §77 signed egress) into a
159    /// budget-bounded, interruptible, fail-closed, provenance-witnessed
160    /// research loop. Enterprise-exclusive at scale (charter split R1); the
161    /// keyword + type discipline + ports live in OSS
162    /// (docs/fase/fase_87_savant.md).
163    Savant(SavantDefinition),
164    /// §Fase 87.d — a dynamic tool-synthesis policy: the closed set of
165    /// conditions (risk ceiling, source language, mandatory WASM zero-trust
166    /// sandbox, Coder/Reviewer consensus) under which a `savant` may
167    /// synthesise + execute a tool at runtime. The paper's "OTS = Ontological
168    /// Tool Synthesis" grounded to a real keyword (`ots` already means
169    /// one-shot media transform — paper §9.1). OSS declares + statically
170    /// disciplines the policy and ships a DENY-BY-DEFAULT reference; the real
171    /// Extism/WASM executor is enterprise (§87.j).
172    Synth(SynthDefinition),
173    /// §Fase 88.a — an authorization scope: the signed envelope (`targets`
174    /// allowlist + `depth` ceiling + `approver`) a `warden` adversarial-analysis
175    /// block MUST run `within`. The load-bearing safety construct that makes
176    /// warden a governed auditor, not a weapon: no in-scope authorization ⇒ no
177    /// analysis (fail-closed). Referenced by `warden(t) within <Scope>`
178    /// (docs/fase/fase_88_warden.md).
179    Scope(ScopeDefinition),
180    /// §Fase 92.a — a named ephemeral-credential contract: TTL-bounded,
181    /// capability-attenuated bearer minting (`authority_only_attenuates` —
182    /// grants ⊆ the minter's own capabilities at mint, TTL ≤ the closed
183    /// ceiling). Declared once (the `cors`/`scope` shape), referenced by the
184    /// `mint <Credential> as <binding>` flow verb
185    /// (docs/fase/fase_92_ephemeral_visitor_credentials.md).
186    Credential(CredentialDefinition),
187    /// §Fase 51.c.2 — a Pauli-sum observable `M = Σ cₖ Pₖ` that a `quant`
188    /// block measures against (paper §3.2; plan D5).
189    Observable(ObservableDefinition),
190    /// §Fase 69.a — an Advantage Witness: a machine-checkable proof obligation
191    /// that a primitive's `claim` beats a cheaper `baseline` by a `metric` above
192    /// a `threshold` on real `data` (doctrine `axon://logic/no_unwitnessed_advantage`).
193    Witness(WitnessDefinition),
194    /// §Fase 99.a — Native Document Synthesis: a declarative, compile-time-
195    /// validated DOCX/PPTX/XLSX structure that is the point where a value LEAVES
196    /// the epistemic lattice and becomes a human artifact. `target:` selects a
197    /// serializer, not a capability (D99.6 — identical effect rows). The
198    /// assertion-laundering barrier (D99.1) refuses a value below `believe` in
199    /// an assertive slot without an `attribute:` or a shield
200    /// (docs/fase/fase_99_native_document_synthesis.md).
201    Document(DocumentDefinition),
202    /// §Fase 105 — Governed CRM Delivery: a declarative, compile-time-validated
203    /// egress of assertions into a system of record (a CRM). The dual of
204    /// acquisition (`scrape`, §98): where `document` leaves the lattice into a
205    /// human artifact, `deliver` leaves it into a machine system others treat as
206    /// fact. The provenance-stripping barrier (D105.2, axon-T920) refuses a
207    /// `provenance: cleared` delivery of an unshielded flow value — a guess must
208    /// arrive labeled as a guess (docs/fase/fase_105_governed_crm_delivery.md).
209    Deliver(DeliverDefinition),
210    /// §Fase 110 — governed human notification.
211    Notify(NotifyDefinition),
212    /// Tier 3+ declarations parsed structurally (balanced braces, no detailed AST).
213    Generic(GenericDeclaration),
214}
215
216/// §Fase 115.b — the named surface of a declaration: `(name, kind, loc)`,
217/// or `None` for the statement-like declarations that do not bind a
218/// referenceable top-level name (`import` / `run` / `let` / the epistemic
219/// block wrapper, whose body registers recursively).
220///
221/// # Parity contract
222///
223/// The `kind` strings here MUST match the kinds the type-checker's
224/// `register_declarations` assigns (that is what `TypeChecker::lookup`
225/// compares against when a `run` / reference names the symbol). The match
226/// below is deliberately **exhaustive — no wildcard arm** — so adding a
227/// `Declaration` variant without deciding its export surface is a compile
228/// error, not a silent gap in the module system.
229pub fn declaration_surface(decl: &Declaration) -> Option<(String, String, Loc)> {
230    match decl {
231        Declaration::Import(_)
232        | Declaration::Run(_)
233        | Declaration::Let(_)
234        | Declaration::Epistemic(_) => None,
235        Declaration::Persona(n) => Some((n.name.clone(), "persona".into(), n.loc.clone())),
236        Declaration::Context(n) => Some((n.name.clone(), "context".into(), n.loc.clone())),
237        Declaration::Anchor(n) => Some((n.name.clone(), "anchor".into(), n.loc.clone())),
238        Declaration::Memory(n) => Some((n.name.clone(), "memory".into(), n.loc.clone())),
239        Declaration::Tool(n) => Some((n.name.clone(), "tool".into(), n.loc.clone())),
240        Declaration::Type(n) => Some((n.name.clone(), "type".into(), n.loc.clone())),
241        Declaration::Flow(n) => Some((n.name.clone(), "flow".into(), n.loc.clone())),
242        Declaration::Intent(n) => Some((n.name.clone(), "intent".into(), n.loc.clone())),
243        Declaration::LambdaData(n) => Some((n.name.clone(), "lambda_data".into(), n.loc.clone())),
244        Declaration::Agent(n) => Some((n.name.clone(), "agent".into(), n.loc.clone())),
245        Declaration::Shield(n) => Some((n.name.clone(), "shield".into(), n.loc.clone())),
246        Declaration::Window(n) => Some((n.name.clone(), "window".into(), n.loc.clone())),
247        Declaration::Budget(n) => Some((n.name.clone(), "budget".into(), n.loc.clone())),
248        Declaration::Pix(n) => Some((n.name.clone(), "pix".into(), n.loc.clone())),
249        Declaration::Ledger(n) => Some((n.name.clone(), "ledger".into(), n.loc.clone())),
250        Declaration::Psyche(n) => Some((n.name.clone(), "psyche".into(), n.loc.clone())),
251        Declaration::Corpus(n) => Some((n.name.clone(), "corpus".into(), n.loc.clone())),
252        Declaration::Dataspace(n) => Some((n.name.clone(), "dataspace".into(), n.loc.clone())),
253        Declaration::Ots(n) => Some((n.name.clone(), "ots".into(), n.loc.clone())),
254        Declaration::Mandate(n) => Some((n.name.clone(), "mandate".into(), n.loc.clone())),
255        Declaration::Compute(n) => Some((n.name.clone(), "compute".into(), n.loc.clone())),
256        Declaration::Daemon(n) => Some((n.name.clone(), "daemon".into(), n.loc.clone())),
257        Declaration::AxonStore(n) => Some((n.name.clone(), "axonstore".into(), n.loc.clone())),
258        Declaration::AxonEndpoint(n) => {
259            Some((n.name.clone(), "axonendpoint".into(), n.loc.clone()))
260        }
261        Declaration::Extension(n) => Some((n.name.clone(), "extension".into(), n.loc.clone())),
262        Declaration::Resource(n) => Some((n.name.clone(), "resource".into(), n.loc.clone())),
263        Declaration::Fabric(n) => Some((n.name.clone(), "fabric".into(), n.loc.clone())),
264        Declaration::Manifest(n) => Some((n.name.clone(), "manifest".into(), n.loc.clone())),
265        Declaration::Observe(n) => Some((n.name.clone(), "observe".into(), n.loc.clone())),
266        Declaration::Reconcile(n) => Some((n.name.clone(), "reconcile".into(), n.loc.clone())),
267        Declaration::Lease(n) => Some((n.name.clone(), "lease".into(), n.loc.clone())),
268        Declaration::Ensemble(n) => Some((n.name.clone(), "ensemble".into(), n.loc.clone())),
269        Declaration::Session(n) => Some((n.name.clone(), "session".into(), n.loc.clone())),
270        Declaration::Topology(n) => Some((n.name.clone(), "topology".into(), n.loc.clone())),
271        Declaration::Immune(n) => Some((n.name.clone(), "immune".into(), n.loc.clone())),
272        Declaration::Reflex(n) => Some((n.name.clone(), "reflex".into(), n.loc.clone())),
273        Declaration::Heal(n) => Some((n.name.clone(), "heal".into(), n.loc.clone())),
274        Declaration::Component(n) => Some((n.name.clone(), "component".into(), n.loc.clone())),
275        Declaration::View(n) => Some((n.name.clone(), "view".into(), n.loc.clone())),
276        Declaration::Channel(n) => Some((n.name.clone(), "channel".into(), n.loc.clone())),
277        Declaration::Socket(n) => Some((n.name.clone(), "socket".into(), n.loc.clone())),
278        Declaration::Upstream(n) => Some((n.name.clone(), "upstream".into(), n.loc.clone())),
279        Declaration::Voice(n) => Some((n.name.clone(), "voice".into(), n.loc.clone())),
280        Declaration::Cors(n) => Some((n.name.clone(), "cors".into(), n.loc.clone())),
281        Declaration::Cache(n) => Some((n.name.clone(), "cache".into(), n.loc.clone())),
282        Declaration::Savant(n) => Some((n.name.clone(), "savant".into(), n.loc.clone())),
283        Declaration::Synth(n) => Some((n.name.clone(), "synth".into(), n.loc.clone())),
284        Declaration::Scope(n) => Some((n.name.clone(), "scope".into(), n.loc.clone())),
285        Declaration::Credential(n) => Some((n.name.clone(), "credential".into(), n.loc.clone())),
286        Declaration::Observable(n) => Some((n.name.clone(), "observable".into(), n.loc.clone())),
287        Declaration::Witness(n) => Some((n.name.clone(), "witness".into(), n.loc.clone())),
288        Declaration::Document(n) => Some((n.name.clone(), "document".into(), n.loc.clone())),
289        Declaration::Deliver(n) => Some((n.name.clone(), "deliver".into(), n.loc.clone())),
290        Declaration::Notify(n) => Some((n.name.clone(), "notify".into(), n.loc.clone())),
291        Declaration::Generic(n) => {
292            if n.name.is_empty() {
293                None
294            } else {
295                Some((n.name.clone(), n.keyword.clone(), n.loc.clone()))
296            }
297        }
298    }
299}
300
301// ── §Fase 99 — Native Document Synthesis ─────────────────────────────────────
302
303/// §Fase 99.b — a declarative document. `target:` picks the serializer
304/// (docx|pptx|xlsx); `provenance:` picks how the provenance part is emitted
305/// (none|embedded|signed); `template:` names an enterprise template; `effects:`
306/// carries the propagated `sensitive:`/`legal:` basis. `blocks` is the body —
307/// a closed-catalog tree of [`DocBlock`]s whose vocabulary the checker validates
308/// against `target` (a `slide` in a `docx` is `axon-T9xx`, D99.6).
309#[derive(Debug, Default)]
310pub struct DocumentDefinition {
311    pub name: String,
312    /// `docx | pptx | xlsx` — closed catalog (axon-T910).
313    pub target: String,
314    /// Optional enterprise template reference (`.dotx/.potx/.xltx`, §99.g).
315    pub template: String,
316    /// `none | embedded | signed` — how the provenance part is emitted
317    /// (D99.2). Empty ⇒ `none`. Closed catalog (axon-T911).
318    pub provenance: String,
319    /// The propagated effect row — `io`, `storage` (blob sink), and any
320    /// `sensitive:<cat>`/`legal:<basis>` the bound data carries (D99.4).
321    pub effects: Option<EffectRow>,
322    /// The document body — the closed-catalog block tree.
323    pub blocks: Vec<DocBlock>,
324    pub loc: Loc,
325    pub leading_trivia: Vec<crate::tokens::Trivia>,
326    pub trailing_trivia: Vec<crate::tokens::Trivia>,
327}
328
329/// §Fase 99.b — one block in a document body. A generic-but-closed node: `kind`
330/// is validated against the `target`'s vocabulary, `fields` against the kind's
331/// allowed set, and `children` are nested blocks (a `section` holds `para`s; a
332/// `slide` holds `bullets`; a `sheet` holds `row`s). Keeping the node generic
333/// (rather than one struct per block kind) keeps the grammar/IR small while the
334/// checker (`check_document`) enforces the same closed-catalog discipline.
335#[derive(Debug, Default)]
336pub struct DocBlock {
337    /// `section|heading|para|table|chart|image|toc|page_break|footnote|slide|
338    /// placeholder|bullets|notes|sheet|row|formula|range|format` — validated
339    /// per `target` at check time.
340    pub kind: String,
341    /// Scalar/list fields (`text:`, `columns:`, `range:`, `attribute:`, …).
342    pub fields: Vec<(String, DocScalar)>,
343    /// Nested child blocks.
344    pub children: Vec<DocBlock>,
345    pub loc: Loc,
346}
347
348impl DocBlock {
349    /// Look up a scalar field by name.
350    pub fn field(&self, name: &str) -> Option<&DocScalar> {
351        self.fields.iter().find(|(k, _)| k == name).map(|(_, v)| v)
352    }
353    /// Whether a field is present.
354    pub fn has_field(&self, name: &str) -> bool {
355        self.fields.iter().any(|(k, _)| k == name)
356    }
357}
358
359/// §Fase 99.b — a document field value. A `Ref` is a binding to a flow value
360/// (the epistemic-egress barrier reads its level); a `Text` is a literal; a
361/// `List` is a bracketed set; `Int` is a scalar count.
362#[derive(Debug, Clone, PartialEq)]
363pub enum DocScalar {
364    /// A quoted string literal (`"Q3 Results"`, `"B2:B9"`).
365    Text(String),
366    /// A bare identifier — a reference to a flow value / declared name. THIS is
367    /// what the assertion-laundering barrier inspects at an assertive slot.
368    Ref(String),
369    /// A bracketed list of strings/identifiers (`["Region","Revenue"]`).
370    List(Vec<String>),
371    /// An integer scalar.
372    Int(i64),
373    /// A boolean scalar.
374    Bool(bool),
375}
376
377impl DocScalar {
378    /// The referenced name, if this value is a `Ref`.
379    pub fn as_ref_name(&self) -> Option<&str> {
380        match self {
381            DocScalar::Ref(s) => Some(s.as_str()),
382            _ => None,
383        }
384    }
385    /// The literal text, if this value is `Text`.
386    pub fn as_text(&self) -> Option<&str> {
387        match self {
388            DocScalar::Text(s) => Some(s.as_str()),
389            _ => None,
390        }
391    }
392}
393
394// ── §Fase 105 — Governed CRM Delivery ────────────────────────────────────────
395
396/// §Fase 105 — a declarative CRM delivery. `target:` picks the destination class
397/// (`crm`, closed catalog — axon-T921); `provenance:` picks how the epistemic
398/// origin of each delivered field is treated at the boundary (`attached` default
399/// | `cleared` — axon-T922); `secret:` names the per-tenant credential key
400/// (§94 custody, required — axon-T923); `effects:` carries the propagated row
401/// (must include `web` — axon-T924). `ops` is the body — a non-empty list of
402/// [`DeliverOp`]s whose `kind` the checker validates against a closed operation
403/// catalog (axon-T925). Field values reuse [`DocScalar`]: a `Ref` is a binding to
404/// a flow value (what the T920 barrier inspects), the rest are literals.
405#[derive(Debug, Default)]
406
407pub struct DeliverDefinition {
408    pub name: String,
409    /// `crm` — closed catalog (axon-T921).
410    pub target: String,
411    /// `attached | cleared` — how field provenance crosses the boundary
412    /// (D105.2). Empty ⇒ `attached` (the safe default: provenance travels).
413    /// Closed catalog (axon-T922).
414    pub provenance: String,
415    /// The per-tenant credential key resolved via §94 custody at dispatch — the
416    /// value never enters cognition. Required (axon-T923).
417    pub secret: String,
418    /// The propagated effect row — must include `web` (a CRM write crosses the
419    /// trust boundary over the network, axon-T924).
420    pub effects: Option<EffectRow>,
421    /// The delivery body — the closed-catalog operation list.
422    pub ops: Vec<DeliverOp>,
423    pub loc: Loc,
424    pub leading_trivia: Vec<crate::tokens::Trivia>,
425    pub trailing_trivia: Vec<crate::tokens::Trivia>,
426}
427
428/// §Fase 110 — Governed Human Notification: the third egress dual
429/// (`deliver` = systems of record, `document` = artifacts, `notify` =
430/// human attention). Three laws: T933 (the evidence barrier — a guess
431/// reaches a human labeled as a guess, or is refused), T934 (structure:
432/// closed channel catalog; the recipient is a §94 secret-class ref,
433/// NEVER a literal — PII never rides source or IR), T935 (attention:
434/// a `window:` is mandatory — unbounded interruption is refused).
435#[derive(Debug, Default)]
436pub struct NotifyDefinition {
437    pub name: String,
438    /// `sms | whatsapp | telegram` — closed catalog (axon-T934).
439    pub channel: String,
440    /// The §94 secret-class ref the recipient resolves from AT DISPATCH
441    /// (`to: secret(ops.oncall_phone)`). The literal number/chat-id never
442    /// appears anywhere axon stores or reasons over.
443    pub to_secret: String,
444    /// True iff `to:` was written in the `secret(...)` form. A literal
445    /// recipient is an axon-T934 refusal (with a teaching message).
446    pub to_is_secret: bool,
447    /// The message template; `${ref}` slots bind flow values post-run.
448    pub template: String,
449    /// §71-style duration (`30m`, `4h`, `1d`) — at-most-once-per-window
450    /// per recipient (axon-T935; enforced durably by the ENT ledger).
451    pub window: String,
452    /// `attached | cleared` — how epistemic labels cross to the human
453    /// (D110.2). Empty ⇒ `attached` (the safe default).
454    pub provenance: String,
455    /// Must include `web` (a notification crosses the trust boundary).
456    pub effects: Option<EffectRow>,
457    pub loc: Loc,
458    pub leading_trivia: Vec<crate::tokens::Trivia>,
459    pub trailing_trivia: Vec<crate::tokens::Trivia>,
460}
461
462/// §Fase 105 — one CRM operation in a delivery body. `kind ∈ {upsert_contact,
463/// create_deal, add_note}` (validated per-target, axon-T925). Each operation
464/// binds a set of `(field, value)` pairs; a `Ref` field carries a flow value
465/// into the CRM and is the T920 barrier's subject. Every operation requires a
466/// `key:` field — the idempotency key (D105.5, axon-T926) so an at-least-once
467/// retry never double-creates a record.
468#[derive(Debug, Default)]
469pub struct DeliverOp {
470    /// `upsert_contact | create_deal | add_note` — validated at check time.
471    pub kind: String,
472    /// Scalar/ref fields (`key:`, `email:`, `firstname:`, `amount:`, …).
473    pub fields: Vec<(String, DocScalar)>,
474    pub loc: Loc,
475}
476
477impl DeliverOp {
478    /// Look up a scalar field by name.
479    pub fn field(&self, name: &str) -> Option<&DocScalar> {
480        self.fields.iter().find(|(k, _)| k == name).map(|(_, v)| v)
481    }
482    /// Whether a field is present.
483    pub fn has_field(&self, name: &str) -> bool {
484        self.fields.iter().any(|(k, _)| k == name)
485    }
486    /// The flow-value references this operation binds (the barrier's subjects).
487    pub fn ref_fields(&self) -> impl Iterator<Item = (&str, &str)> {
488        self.fields.iter().filter_map(|(k, v)| match v {
489            DocScalar::Ref(name) => Some((k.as_str(), name.as_str())),
490            _ => None,
491        })
492    }
493}
494
495// ── §λ-L-E Fase 1 — Resource primitive ───────────────────────────────────────
496
497/// `resource Name { kind, endpoint, capacity, lifetime, certainty_floor, shield }`
498///
499/// An infrastructure resource declared as a linear, affine, or persistent
500/// token. Linear/affine resources cannot be aliased across manifests
501/// (Separation Logic `*` disjointness).
502#[derive(Debug, Default)]
503pub struct ResourceDefinition {
504    pub name: String,
505    /// §Fase 113 — a CLOSED catalog (`VALID_RESOURCE_KINDS`). Until §113 this
506    /// was a free string that **nothing validated**: `check_resource` never
507    /// read it, and no catalog const existed anywhere in the workspace.
508    pub kind: String,
509    /// §Fase 113 — the DSN, as a **per-tenant config key** (`axon-T944`).
510    ///
511    /// A production DB URI in source is exactly what `axon-T850` already
512    /// forbids one declaration over (*"URLs and credentials never appear in
513    /// source"*) and what §94 custody exists to refuse. `resource` was a
514    /// grandfathered violation of the language's own law.
515    pub endpoint: String,
516    /// §Fase 113 — **the pool size**, and the proof this fase is a wire and not
517    /// a label. Before §113 every axonstore pool was hardcoded at 10.
518    pub capacity: Option<i64>,
519    /// §Fase 113 — **how many holders may name this resource** (Linear Logic):
520    /// `linear` = exactly one · `affine` = at most one (sharing is a breach) ·
521    /// `persistent` = the `!` exponential, freely shared. Default: `affine`.
522    pub lifetime: String,
523    pub certainty_floor: Option<f64>, // epistemic gate c ∈ [0.0, 1.0]
524    pub shield_ref: String,           // optional shield reference
525    /// §Fase 113 — the `fabric` this resource lives in. **One field ⇒
526    /// Separation-Logic disjointness is unrepresentable, not verified.**
527    pub within: String,
528    pub loc: Loc,
529    /// Fase 14.b — leading comment trivia attached to this declaration
530    /// (comments preceding the declaration's first token, since the
531    /// previous declaration or file start). Empty by default.
532    pub leading_trivia: Vec<crate::tokens::Trivia>,
533    /// Fase 14.b — trailing comment trivia (same line as the
534    /// declaration's last effective token). Empty by default.
535    pub trailing_trivia: Vec<crate::tokens::Trivia>,
536}
537
538/// `fabric Name { provider, region, zones, ephemeral, shield }`
539///
540/// A tagged substrate — the topological container where resources are
541/// provisioned. Maps to VPC / cluster / namespace.
542#[derive(Debug, Default)]
543pub struct FabricDefinition {
544    pub name: String,
545    pub provider: String, // aws | gcp | azure | kubernetes | bare_metal | custom
546    pub region: String,   // provider-specific region id
547    pub zones: Option<i64>, // number of availability zones
548    pub ephemeral: Option<bool>, // true = destroy on program end
549    pub shield_ref: String, // optional shield reference
550    pub loc: Loc,
551    /// Fase 14.b — leading comment trivia attached to this declaration
552    /// (comments preceding the declaration's first token, since the
553    /// previous declaration or file start). Empty by default.
554    pub leading_trivia: Vec<crate::tokens::Trivia>,
555    /// Fase 14.b — trailing comment trivia (same line as the
556    /// declaration's last effective token). Empty by default.
557    pub trailing_trivia: Vec<crate::tokens::Trivia>,
558}
559
560/// `manifest Name { resources, fabric, region, zones, compliance }`
561///
562/// A declarative specification of desired shape — not a "desired state" in
563/// the Terraform sense, a *belief* about structure. Linear/affine resources
564/// in `resources` must be disjoint (Separation Logic `*`).
565#[derive(Debug, Default)]
566pub struct ManifestDefinition {
567    pub name: String,
568    pub resources: Vec<String>, // references to ResourceDefinition names
569    pub fabric_ref: String,     // reference to FabricDefinition name
570    pub region: String,
571    pub zones: Option<i64>,
572    pub compliance: Vec<String>, // κ — regulatory class (Fase 6.1)
573    pub loc: Loc,
574    /// Fase 14.b — leading comment trivia attached to this declaration
575    /// (comments preceding the declaration's first token, since the
576    /// previous declaration or file start). Empty by default.
577    pub leading_trivia: Vec<crate::tokens::Trivia>,
578    /// Fase 14.b — trailing comment trivia (same line as the
579    /// declaration's last effective token). Empty by default.
580    pub trailing_trivia: Vec<crate::tokens::Trivia>,
581}
582
583/// `observe Name from Manifest { sources, quorum, timeout, on_partition, certainty_floor }`
584///
585/// A quorum-gated observation of a manifest's real state. Each output
586/// carries ΛD envelope E = ⟨c, τ, ρ, δ⟩; `τ` records observation lag.
587/// `on_partition: fail` raises a CT-3 (Network Error) — partitions are ⊥ void.
588#[derive(Debug, Default)]
589pub struct ObserveDefinition {
590    pub name: String,
591    pub target: String, // name of ManifestDefinition being observed
592    pub sources: Vec<String>,
593    pub quorum: Option<i64>,  // Byzantine quorum threshold
594    pub timeout: String,      // duration literal "5s", "100ms"
595    pub on_partition: String, // fail (CT-3) | shield_quarantine
596    pub certainty_floor: Option<f64>,
597    pub loc: Loc,
598    /// Fase 14.b — leading comment trivia attached to this declaration
599    /// (comments preceding the declaration's first token, since the
600    /// previous declaration or file start). Empty by default.
601    pub leading_trivia: Vec<crate::tokens::Trivia>,
602    /// Fase 14.b — trailing comment trivia (same line as the
603    /// declaration's last effective token). Empty by default.
604    pub trailing_trivia: Vec<crate::tokens::Trivia>,
605}
606
607// ── §λ-L-E Fase 3 — Control cognitivo primitives ─────────────────────────────
608
609/// `reconcile Name { observe, threshold, tolerance, on_drift, shield, mandate, max_retries }`
610///
611/// A cognitive control loop that minimises variational free energy
612/// `F = D_KL(q(s) || p(s, o))` between a manifest belief and an observe
613/// evidence. Acting on the environment (`on_drift: provision`) is one of
614/// the two classical routes to reducing F (the other is belief revision).
615#[derive(Debug, Default)]
616pub struct ReconcileDefinition {
617    pub name: String,
618    pub observe_ref: String,
619    pub threshold: Option<f64>, // epistemic gate c ∈ [0.0, 1.0]
620    pub tolerance: Option<f64>, // drift tolerance [0.0, 1.0]
621    pub on_drift: String,       // provision | alert | refine (default: provision)
622    pub shield_ref: String,
623    pub mandate_ref: String,
624    pub max_retries: i64, // default: 3
625    pub loc: Loc,
626    /// Fase 14.b — leading comment trivia attached to this declaration
627    /// (comments preceding the declaration's first token, since the
628    /// previous declaration or file start). Empty by default.
629    pub leading_trivia: Vec<crate::tokens::Trivia>,
630    /// Fase 14.b — trailing comment trivia (same line as the
631    /// declaration's last effective token). Empty by default.
632    pub trailing_trivia: Vec<crate::tokens::Trivia>,
633}
634
635/// `lease Name { resource, duration, acquire, on_expire }`
636///
637/// Affine/linear lease on a resource, with explicit Δt encoded in the `τ`
638/// of the ΛD envelope. Runtime materializes each lease as a revocable
639/// token; use post-expiry raises `LeaseExpiredError` (CT-2) per D2.
640#[derive(Debug, Default)]
641pub struct LeaseDefinition {
642    pub name: String,
643    pub resource_ref: String,
644    pub duration: String,  // "30s", "5m", "2h"
645    pub acquire: String,   // on_start | on_demand (default: on_start)
646    pub on_expire: String, // anchor_breach | release | extend (default: anchor_breach)
647    pub loc: Loc,
648    /// Fase 14.b — leading comment trivia attached to this declaration
649    /// (comments preceding the declaration's first token, since the
650    /// previous declaration or file start). Empty by default.
651    pub leading_trivia: Vec<crate::tokens::Trivia>,
652    /// Fase 14.b — trailing comment trivia (same line as the
653    /// declaration's last effective token). Empty by default.
654    pub trailing_trivia: Vec<crate::tokens::Trivia>,
655}
656
657/// `ensemble Name { observations, quorum, aggregation, certainty_mode }`
658///
659/// Byzantine quorum aggregator over ≥2 independent observations. Yields
660/// common knowledge `Cφ` (Fagin-Halpern) when at least `quorum` observers
661/// agree. Failed observations are excluded; below quorum raises CT-3.
662#[derive(Debug, Default)]
663pub struct EnsembleDefinition {
664    pub name: String,
665    pub observations: Vec<String>,
666    pub quorum: Option<i64>,
667    pub aggregation: String, // majority | weighted | byzantine (default: majority)
668    pub certainty_mode: String, // min | weighted | harmonic (default: min)
669    pub loc: Loc,
670    /// Fase 14.b — leading comment trivia attached to this declaration
671    /// (comments preceding the declaration's first token, since the
672    /// previous declaration or file start). Empty by default.
673    pub leading_trivia: Vec<crate::tokens::Trivia>,
674    /// Fase 14.b — trailing comment trivia (same line as the
675    /// declaration's last effective token). Empty by default.
676    pub trailing_trivia: Vec<crate::tokens::Trivia>,
677}
678
679// ── §λ-L-E Fase 4 — Topology + π-calculus binary sessions ──────────────────
680
681/// One step in a session protocol.
682///
683/// §Fase 4: `send T` | `receive T` | `loop` | `end`. §Fase 41.b adds **choice**:
684/// `select { ℓ: [..], … }` (⊕ — this role chooses) and `branch { ℓ: [..], … }`
685/// (& — this role offers); for those `op`s the labelled continuations live in
686/// [`SessionStep::branches`] (a nested sub-protocol per label).
687#[derive(Debug, Clone, Default)]
688pub struct SessionStep {
689    pub op: String,           // send | receive | loop | end | select | branch | interrupt
690    pub message_type: String, // send/receive: payload type · interrupt: the `on <Signal>` cause
691    /// §Fase 41.b — populated only for `op == "select" | "branch"`: the labelled
692    /// branches, each a nested step sequence (its own sub-protocol).
693    ///
694    /// §Fase 79.b — reused for `op == "interrupt"`: exactly two labelled arms,
695    /// `body` (the interruptible region) and `handler` (runs on the signal). The
696    /// handler may end in a `resume` step (back to `body`) or reach `end` (the
697    /// abandon exit) — see the paper §3.5 two-exit construct.
698    pub branches: Vec<SessionBranch>,
699    /// §Fase 79.b — `op == "interrupt"` only: the handler's signal binder from
700    /// `... as <sig> ...`. Empty for every other op. The handler references the
701    /// received `CallInterruptCause` value under this name.
702    pub binder: String,
703    /// §Fase 79.b — `op == "interrupt"` only: `true` when the block declares a
704    /// `resumable { … }` handler (the v1 surface always does). Default `false`
705    /// keeps every non-interrupt step byte-identical in the IR (skip-if-false).
706    pub resumable: bool,
707    pub loc: Loc,
708}
709
710/// §Fase 41.b — one labelled arm of a `select`/`branch` choice: `ℓ: [steps]`.
711#[derive(Debug, Clone, Default)]
712pub struct SessionBranch {
713    pub label: String,
714    pub steps: Vec<SessionStep>,
715    pub loc: Loc,
716}
717
718/// One role in a binary session — name + ordered list of steps.
719#[derive(Debug, Default)]
720pub struct SessionRole {
721    pub name: String,
722    pub steps: Vec<SessionStep>,
723    pub loc: Loc,
724}
725
726/// `session Name { role1: [step, …]  role2: [step, …] }`
727///
728/// A binary session type — exactly two roles whose protocols MUST be
729/// pairwise Honda-Vasconcelos dual. Duality is verified by the type
730/// checker; non-dual programs are rejected at compile time.
731#[derive(Debug, Default)]
732pub struct SessionDefinition {
733    pub name: String,
734    pub roles: Vec<SessionRole>,
735    pub loc: Loc,
736    /// Fase 14.b — leading comment trivia attached to this declaration
737    /// (comments preceding the declaration's first token, since the
738    /// previous declaration or file start). Empty by default.
739    pub leading_trivia: Vec<crate::tokens::Trivia>,
740    /// Fase 14.b — trailing comment trivia (same line as the
741    /// declaration's last effective token). Empty by default.
742    pub trailing_trivia: Vec<crate::tokens::Trivia>,
743}
744
745/// `socket Name { protocol: SessionRef, backpressure: credit(n), reconnect:
746/// cognitive_state, legal_basis: ... }`
747///
748/// §Fase 41.b — the typed WebSocket transport (paper_websocket_cognitive_primitive.md).
749/// `socket` is NOT the protocol — the protocol is a `session` it references by
750/// name (protocol and transport kept separate but composable). The type checker
751/// resolves `protocol` to a declared `session` (whose two roles are already
752/// duality-checked via the §41.a algebra), so the dialogue carried over the WS
753/// connection is conformant + deadlock-free by construction.
754#[derive(Debug, Default)]
755pub struct SocketDefinition {
756    pub name: String,
757    /// The referenced `session` declaration's name — the protocol.
758    pub protocol: String,
759    /// The credit window of the typed-resource backpressure (`credit(n)`);
760    /// `None` if unspecified. A `0` credit is rejected by the type checker.
761    pub backpressure_credit: Option<i64>,
762    /// `reconnect: cognitive_state` → `true` (resume mid-dialogue via a sealed
763    /// §40.t snapshot); absent or `reconnect: none` → `false`.
764    pub reconnect: bool,
765    /// Optional `legal_basis:` annotation (enterprise audit/shield gate).
766    pub legal_basis: Option<String>,
767    pub loc: Loc,
768    /// Fase 14.b — leading comment trivia.
769    pub leading_trivia: Vec<crate::tokens::Trivia>,
770    /// Fase 14.b — trailing comment trivia.
771    pub trailing_trivia: Vec<crate::tokens::Trivia>,
772}
773
774/// `upstream Name { transport:, protocol:, role:, resolve:, secret:, auth:,
775/// map: [...], reconnect: {...}, overflow:, backpressure: credit(n) }`
776///
777/// §Fase 80.b — the client dual of `socket`: axon dials OUT to a third-party
778/// vendor (STT/TTS/realtime speech APIs). The axon-facing interface is a
779/// declared `session` (referenced by `protocol:`, same field meaning as
780/// `socket`) of which axon plays `role:`; the vendor side is realised by the
781/// `map:` projection rules — a compile-time-total transcoding contract
782/// (§80.c, axon-T849). `resolve:`/`secret:` name per-tenant config keys,
783/// never URL/credential literals (axon-T850) — the same "config, not code"
784/// property §58.g gave `tool`, extended to persistent duplex streams.
785#[derive(Debug, Default)]
786pub struct UpstreamDefinition {
787    pub name: String,
788    /// Wire transport axon dials. Closed catalog; v1 sole member `websocket`.
789    pub transport: String,
790    /// The referenced `session` declaration's name — the axon-facing protocol.
791    pub protocol: String,
792    /// Which of the session's two roles axon plays; the peer role is the
793    /// vendor's, realised by the `map:` transcoding, never by axon code.
794    pub role: String,
795    /// Per-tenant config key holding the vendor URL (dot-separated,
796    /// `SecretKeyPolicy`-shaped — never a URL literal).
797    pub resolve: String,
798    /// §Fase 114.u — the `resource` this upstream's channel runs on
799    /// (`upstream X { resource: Api }`). When present, the upstream DERIVES
800    /// its dial address from `resource.endpoint` (a per-tenant config key,
801    /// axon-T944) and its **max concurrent connection INSTANCES** from
802    /// `resource.capacity` — frames are already governed by
803    /// `backpressure_credit`, so capacity bounds CONNECTIONS, never frames
804    /// (making it frames would state one fact twice). Mutually exclusive
805    /// with `resolve:` (axon-T951): `resource:` beside a `resolve:` would
806    /// declare the channel's address twice — the §113 islands defect.
807    /// Authority attenuation: the resource encapsulates its own address
808    /// resolution; the upstream only names WHICH channel it rides.
809    pub resource_ref: String,
810    /// Per-tenant secret binding for the vendor credential (same charset).
811    pub secret: String,
812    /// Auth handshake kind: `header` | `query` | `signed_url`.
813    pub auth_kind: String,
814    /// Header/query-param name (`header("Authorization")` / `query("token")`).
815    pub auth_name: Option<String>,
816    /// Optional header value prefix (`header("Authorization", "Token ")`).
817    pub auth_prefix: Option<String>,
818    /// The wire↔session transcoding contract; totality checked at §80.c.
819    pub map: Vec<UpstreamMapRule>,
820    /// `reconnect: { backoff_ms:, max_attempts:, on_exhausted: }`.
821    pub reconnect: Option<UpstreamReconnect>,
822    /// Outbound-queue policy when the VENDOR is the slow side — a member of
823    /// the existing `BackpressurePolicy` catalog. `None` ⇒ `fail` (honest
824    /// default: no silently-lossy audio unless the adopter opts in).
825    pub overflow: Option<String>,
826    /// Axon-facing credit window, identical semantics to `socket`.
827    pub backpressure_credit: Option<i64>,
828    /// §Fase 80.f — set when declared via `upstream X from Preset@vN {…}`;
829    /// carries the `Preset@vN` reference the desugar pass expanded.
830    pub preset: Option<String>,
831    pub loc: Loc,
832    /// Fase 14.b — leading comment trivia.
833    pub leading_trivia: Vec<crate::tokens::Trivia>,
834    /// Fase 14.b — trailing comment trivia.
835    pub trailing_trivia: Vec<crate::tokens::Trivia>,
836}
837
838/// One `map:` projection rule: `send M as json [tag "X"]` /
839/// `send M as binary` / `receive M as json [when "f" [= "v"]]` /
840/// `receive M as binary`. Inbound `json` payloads land as §73 `Json` (total
841/// navigation); the session message name is the routing + duality skeleton.
842#[derive(Debug, Default)]
843pub struct UpstreamMapRule {
844    /// `send` (axon → vendor) or `receive` (vendor → axon).
845    pub direction: String,
846    /// The session message type this rule transcodes.
847    pub message: String,
848    /// `json` or `binary` (raw passthrough).
849    pub framing: String,
850    /// send-json only. Absent ⇒ the payload is sent VERBATIM (the vendor's
851    /// exact wire shape, e.g. ElevenLabs `{"text": …}`); present ⇒ the tag
852    /// is injected as `"type": "<tag>"` into the payload object (the
853    /// Deepgram/OpenAI-Realtime envelope family).
854    pub tag: Option<String>,
855    /// receive-json only: discriminator field. Absent ⇒ the default
856    /// equality discriminator `"type" = "<MessageName>"`.
857    pub when_field: Option<String>,
858    /// receive-json only. `Some(v)` ⇒ equality match on `when_field`;
859    /// `None` with `when_field` present ⇒ field-PRESENCE match (vendors
860    /// like Gemini Live mark frame kinds by which key exists). Equality
861    /// rules dispatch before presence rules.
862    pub when_value: Option<String>,
863    pub loc: Loc,
864}
865
866/// `voice Name { stt:/tts: XOR realtime:, carrier:, interruptible:,
867/// legal_basis:, persona:, context: }`
868///
869/// §Fase 80.g — the simplicity layer over §80's `upstream`: a blessed-
870/// preset phone agent in under 20 lines. `stt:`+`tts:` (cascaded) XOR
871/// `realtime:` (fused) — D80.1: one grammar, both architectures, never a
872/// special-cased second path. Each leg is a `Preset@vN` reference or a
873/// declared `upstream` name. Expansion is pure macro-lowering to existing
874/// primitives; `axon desugar` prints it (D80.6).
875#[derive(Debug, Default)]
876pub struct VoiceDefinition {
877    pub name: String,
878    /// Cascaded STT leg (preset ref like `DeepgramSTT@v1` or upstream name).
879    pub stt: Option<String>,
880    /// Cascaded TTS leg.
881    pub tts: Option<String>,
882    /// Fused speech-to-speech leg (mutually exclusive with stt/tts — T852).
883    pub realtime: Option<String>,
884    /// Carrier codec: `mulaw8k` (default — PSTN; expansion emits the §ots
885    /// μ-law↔PCM16 pair) or `pcm16` (browser/WebRTC-style, no transcode).
886    pub carrier: String,
887    /// `true` ⇒ the carrier session is a §79 interruptible region (barge-in
888    /// capable) and the socket parks residuals — which REQUIRES
889    /// `legal_basis:` (T852; the sugar must not generate a program
890    /// `ParkedResidualSoundness` refutes).
891    pub interruptible: bool,
892    /// Rides onto the generated socket (the §79 data-at-rest obligation).
893    pub legal_basis: Option<String>,
894    /// Optional references wired for the flow layer (validated to exist).
895    pub persona: Option<String>,
896    pub context: Option<String>,
897    pub loc: Loc,
898    /// Fase 14.b — leading comment trivia.
899    pub leading_trivia: Vec<crate::tokens::Trivia>,
900    /// Fase 14.b — trailing comment trivia.
901    pub trailing_trivia: Vec<crate::tokens::Trivia>,
902}
903
904/// `cors Name { allow_origins:, allow_methods:, allow_headers:,
905/// allow_credentials:, max_age:, expose_headers: }`
906///
907/// §Fase 83.a — a named, referenced origin-policy declaration, mirroring
908/// `shield`'s shape exactly: declared once, referenced from any number of
909/// `axonendpoint`s via `cors: <Name>` (`AxonEndpointDefinition::cors_ref`).
910/// Makes the browser-facing origin policy a property of the ENDPOINT,
911/// resolved per the tenant's live deployed bundle — the shape a single
912/// process-wide CORS knob (the market-standard pattern) cannot express for
913/// a multi-tenant deploy where different bundles need different origins
914/// for a path with the same name (D83.1/D83.3).
915///
916/// **Unknown fields are a hard parse error** (D83.7) — a CORS policy is
917/// security-relevant, so `upstream`/`voice`'s stricter posture is followed
918/// here, not `shield`'s lenient `axon-W010` record-and-skip.
919#[derive(Debug, Default)]
920pub struct CorsDefinition {
921    pub name: String,
922    /// `["https://app.example.com", "https://*.kivi.io"]` — exact origins
923    /// or a single leading-wildcard host-label glob (D83.1/T854); no full
924    /// regex, matching the closed/decidable spirit of the rest of the
925    /// language. `["*"]` (any-origin) is legal UNLESS `allow_credentials`
926    /// is also `true` — that combination is `axon-T853` (D83.2, the CORS
927    /// spec's own rule, caught at compile time instead of a silent browser
928    /// rejection).
929    pub allow_origins: Vec<String>,
930    /// Reuses the closed `axonendpoint` method catalog (GET/POST/PUT/
931    /// PATCH/DELETE) — validated against the same list, not a free string
932    /// (T855).
933    pub allow_methods: Vec<String>,
934    /// Request headers the preflight may allow (e.g. `["Content-Type",
935    /// "Authorization"]`) — free-form header-name strings (hyphens are
936    /// common in real header names, hence string literals, not bare
937    /// identifiers).
938    pub allow_headers: Vec<String>,
939    /// `true` ⇒ `Access-Control-Allow-Credentials: true` is emitted.
940    /// Forbidden together with an any-origin `allow_origins` (T853).
941    pub allow_credentials: bool,
942    /// `Access-Control-Max-Age` — a duration literal (`"3600s"`, `"1h"`),
943    /// same lexer/token convention as `axonendpoint.timeout`. `None` ⇒ the
944    /// header is omitted (browser default caching applies).
945    pub max_age: Option<String>,
946    /// `Access-Control-Expose-Headers` — response headers the browser's
947    /// JS may read beyond the CORS-safelisted set. Free-form strings, same
948    /// rationale as `allow_headers`.
949    pub expose_headers: Vec<String>,
950    pub loc: Loc,
951    /// Fase 14.b — leading comment trivia.
952    pub leading_trivia: Vec<crate::tokens::Trivia>,
953    /// Fase 14.b — trailing comment trivia.
954    pub trailing_trivia: Vec<crate::tokens::Trivia>,
955}
956
957/// §Fase 92.a — `credential <Name> { ttl: grants: }`: a named
958/// ephemeral-credential contract. `mint <Name> as <binding>` (§92.b) mints
959/// a TTL-bounded bearer carrying exactly `grants` — and the runtime law
960/// (`authority_only_attenuates`) admits the mint only when
961/// `grants ⊆ capabilities(minter)`. An unknown field in a `credential { }`
962/// block is a HARD PARSE ERROR (the §83 posture — this is security
963/// surface; a typo'd field must not silently produce a permissive
964/// contract).
965#[derive(Debug, Default)]
966pub struct CredentialDefinition {
967    pub name: String,
968    /// The bearer's lifetime — a duration literal (`"15m"`, `"900s"`),
969    /// REQUIRED. Validated by `axon-T894`: parseable, > 0, and ≤ the closed
970    /// 24h ceiling (an "ephemeral" credential that lives for days is a
971    /// service account wearing a costume — §81 covers that shape).
972    pub ttl: String,
973    /// The capability slugs the minted bearer carries — REQUIRED,
974    /// non-empty (`axon-T893`), each a dotted slug per
975    /// `is_valid_capability_slug` (the `requires:` grammar). Attenuation
976    /// (`⊆ minter`) is the runtime/mint-time half of the law.
977    pub grants: Vec<String>,
978    pub loc: Loc,
979    /// Fase 14.b — leading comment trivia.
980    pub leading_trivia: Vec<crate::tokens::Trivia>,
981    /// Fase 14.b — trailing comment trivia.
982    pub trailing_trivia: Vec<crate::tokens::Trivia>,
983}
984
985/// `cache Name { backend:, ttl:, key:, default:, apply_to_effects:,
986/// invalidate_on: }` — §Fase 85.a, a named, referenced result-memoization
987/// policy resolved per `tool.cache:` / `retrieve.cache:` (mirrors
988/// `CorsDefinition`'s named-and-referenced shape).
989///
990/// The load-bearing idea (D85.1): cacheability derives from the type system's
991/// existing `effects: pure` proof — a `pure` tool is safe to cache by
992/// construction. A `default: true` cache auto-covers every `pure` tool with
993/// zero per-tool annotation; widening `apply_to_effects:` beyond `[pure]` is
994/// allowed but the compiler names every non-pure tool it covers (W013) AND
995/// forces a finite `ttl:` (T865 — you may cache a proven-deterministic result
996/// forever, never a non-deterministic one).
997///
998/// **Unknown fields are a hard parse error** (the §83 D83.7 discipline) — a
999/// cache governs correctness (serving a stale/foreign result is a real bug), so
1000/// a typo'd field can never silently mean "no policy."
1001#[derive(Debug, Default)]
1002pub struct CacheDefinition {
1003    pub name: String,
1004    /// `redis` (multi-replica, enterprise tier) or `in_process` (the OSS
1005    /// default single-replica tier). Empty ⇒ `in_process`. Validated against a
1006    /// closed catalog at check time.
1007    pub backend: String,
1008    /// Time-to-live as a duration literal (`"10s"`, `"5m"`, `"1h"`), same lexer
1009    /// convention as `cors.max_age`/`tool.timeout`. `None` ⇒ "cache forever" —
1010    /// sound ONLY for a provably-`pure` cache; a non-pure cache with no `ttl:`
1011    /// is `axon-T865` (D85.9).
1012    pub ttl: Option<String>,
1013    /// `key: [param, ...]` — the SUBSET of the covered tool's `parameters:`
1014    /// whose bound values form the cache key. Empty ⇒ ALL bound parameters
1015    /// (the automatic, zero-friction default). Used when one bound arg — e.g. a
1016    /// `request_id` — must NOT affect the key.
1017    pub key_params: Vec<String>,
1018    /// `default: true` ⇒ this cache auto-covers every eligible tool in the
1019    /// module without a per-tool `cache:` reference. At most one per module
1020    /// (`axon-T863`). Default `false`.
1021    pub default_policy: bool,
1022    /// `apply_to_effects: [pure, ...]` — the effect classes this cache is
1023    /// willing to memoise. Empty ⇒ `[pure]` (the only provably-safe default).
1024    /// Any member beyond `pure` is a WIDENING (W013 names each covered tool)
1025    /// and forces a finite `ttl:` (T865).
1026    pub apply_to_effects: Vec<String>,
1027    /// `invalidate_on: [Channel, ...]` — an `emit` on any listed channel
1028    /// flushes this cache's namespace. Each must resolve to a declared
1029    /// `channel` (`axon-T864`). Reuses the §13 pub/sub, not a second mechanism.
1030    pub invalidate_on: Vec<String>,
1031    pub loc: Loc,
1032    /// Fase 14.b — leading comment trivia.
1033    pub leading_trivia: Vec<crate::tokens::Trivia>,
1034    /// Fase 14.b — trailing comment trivia.
1035    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1036}
1037
1038/// §Fase 87.a — `savant <Name> { domain:, cognition{…}, memory{…}, budget{…},
1039/// mandate <M> {…} … }` — the long-horizon autonomous research primitive.
1040///
1041/// A `savant` is a declarative ORCHESTRATOR, not a new engine: it composes the
1042/// primitives Axon already ships (memory/corpus retention, `par` swarms, the
1043/// `quant` Hilbert substrate, `forge` novelty synthesis, the `daemon` host, the
1044/// §72 linear budget, the §79 interruptible session, the §77 signed egress)
1045/// into a single governed research loop. The paper's vision (docs/papers/
1046/// paper_primitiva_savant.md) grounded to real primitives in its §9.
1047///
1048/// §87.a parses the FULL block surface; the semantics land incrementally:
1049/// param-catalog + typed-output validation is §87.b (checker), memory-ref
1050/// resolution + §72 budget binding + §79 interruptibility is §87.c, and the
1051/// `SavantSoundness` PCC proof is §87.g. **Unknown fields are a hard parse
1052/// error** (the §83 D83.7 discipline, as `cache`/`cors`): a savant governs an
1053/// expensive, risk-bearing autonomous process, so a typo'd field can never
1054/// silently mean "no policy."
1055#[derive(Debug, Default)]
1056pub struct SavantDefinition {
1057    pub name: String,
1058    /// `domain: "…"` — the ontological scope of the generative boundary (the
1059    /// research topic the free-energy loop minimises surprise over). Required
1060    /// (§87.b `axon-T873`); empty until parsed.
1061    pub domain: String,
1062    /// `cognition { … }` — the epistemic parameters of the active-inference
1063    /// engine (depth / entropic threshold / divergence). `None` ⇒ engine
1064    /// defaults (§87.b supplies a catalog-checked default).
1065    pub cognition: Option<SavantCognition>,
1066    /// `memory { … }` — the retention layer. `backend` resolves to a declared
1067    /// `memory`/`corpus` primitive in §87.c (`axon-T875`); `None` ⇒ ephemeral.
1068    pub memory: Option<SavantMemory>,
1069    /// `budget { … }` — the compute ceiling. Bound to a §72 linear budget
1070    /// (`RateLease`) in §87.c so `max_iterations` is a TYPE-enforced ceiling,
1071    /// not a hope. `None` ⇒ §87.c rejects (a weeks-long autonomous loop with no
1072    /// budget is uninsurable).
1073    pub budget: Option<SavantBudget>,
1074    /// `mandate <Name> { objective:, output: }` — one or more epistemic
1075    /// mandates the savant autonomously decomposes into tasks. At least one
1076    /// required (§87.b `axon-T874`).
1077    pub mandates: Vec<SavantMandate>,
1078    pub loc: Loc,
1079    /// Fase 14.b — leading comment trivia.
1080    pub leading_trivia: Vec<crate::tokens::Trivia>,
1081    /// Fase 14.b — trailing comment trivia.
1082    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1083}
1084
1085/// §Fase 87.a — the `cognition { … }` sub-block of a [`SavantDefinition`]: the
1086/// active-inference engine's epistemic parameters (paper §3, §7.1).
1087#[derive(Debug, Default)]
1088pub struct SavantCognition {
1089    /// `depth: standard | deep | hyper` — the HRR dimensionality tier of the
1090    /// holographic memory codec. Closed catalog (§87.b `axon-T876`).
1091    pub depth: String,
1092    /// `entropic_threshold: <float>` — the Expected-Free-Energy convergence
1093    /// bound: the loop halts synthesis when no policy reduces EFE below this.
1094    /// Must be `> 0` (§87.b). `None` ⇒ engine default.
1095    pub entropic_threshold: Option<f64>,
1096    /// `divergence: low | med | high` — how aggressively the loop explores
1097    /// epistemic (β₂) voids vs. exploiting known structure. Closed catalog.
1098    pub divergence: String,
1099    pub loc: Loc,
1100}
1101
1102/// §Fase 87.a — the `memory { … }` sub-block of a [`SavantDefinition`]: the
1103/// durable retention layer, composed from existing `memory`/`corpus` primitives.
1104#[derive(Debug, Default)]
1105pub struct SavantMemory {
1106    /// `backend: <Name>` — a reference to a declared `memory` or `corpus`
1107    /// primitive (resolved in §87.c `axon-T875`). Empty ⇒ ephemeral memory.
1108    pub backend: String,
1109    /// `corpus_graph: <bool>` — whether to iteratively index the ingested
1110    /// corpus as a simplicial-complex graph for the topological (β_n) reading.
1111    pub corpus_graph: bool,
1112    /// `isolation_level: strict | …` — per-tenant tensor partitioning. The
1113    /// enterprise engine enforces it; OSS records it. Empty ⇒ default.
1114    pub isolation_level: String,
1115    pub loc: Loc,
1116}
1117
1118/// §Fase 87.a — the `budget { … }` sub-block of a [`SavantDefinition`]: the
1119/// compute ceiling, bound to a §72 linear budget in §87.c.
1120#[derive(Debug, Default)]
1121pub struct SavantBudget {
1122    /// `max_iterations: <int>` — the hard ceiling on FEP-loop iterations before
1123    /// the savant pauses (the paper's `compute_budget`). `> 0` (§87.b).
1124    pub max_iterations: Option<i64>,
1125    /// `max_tool_synth: <int>` — the hard ceiling on `synth` (§87.d) dynamic
1126    /// tool-creation events per mandate. `None` ⇒ 0 (no synthesis).
1127    pub max_tool_synth: Option<i64>,
1128    pub loc: Loc,
1129}
1130
1131/// §Fase 87.a — a `mandate <Name> { objective:, output: }` sub-block of a
1132/// [`SavantDefinition`]: one epistemic research goal the savant pursues.
1133#[derive(Debug, Default)]
1134pub struct SavantMandate {
1135    pub name: String,
1136    /// `objective: "…"` — the natural-language research goal. Required
1137    /// (non-empty, §87.b `axon-T874`).
1138    pub objective: String,
1139    /// `output: <Type>` — the declared type the final report must inhabit
1140    /// (resolved to a declared `type` in §87.b). Required.
1141    pub output_type: String,
1142    pub loc: Loc,
1143}
1144
1145/// §Fase 87.d — `synth <Name> { target:, risk:, language:, sandbox:, review:,
1146/// max_lines: }` — a dynamic tool-synthesis policy.
1147///
1148/// A `savant` that hits an epistemic gap it has no tool for can, under such a
1149/// policy, deduce + write a tool (a Coder sub-agent, a Reviewer sub-agent — a
1150/// `par` with an agreement condition), compile it to `wasm32-wasi`, and run it
1151/// in an Extism zero-trust sandbox, feeding stdout back as empirical evidence
1152/// (paper §6). The policy declares the SAFETY ENVELOPE; the runtime enforces it.
1153///
1154/// **Deny-by-default (D87.d):** OSS parses + statically disciplines this policy
1155/// but ships a `SynthBackend` reference that REFUSES to execute — running
1156/// untrusted synthesised code needs the enterprise Extism/gVisor isolation
1157/// (§87.j). The checker therefore requires `sandbox: wasm` (T882): a synth
1158/// policy that would run code outside a sandbox can never compile.
1159///
1160/// **Unknown fields are a hard parse error** (D83.7): a synth policy governs
1161/// arbitrary-code execution — the highest-stakes surface in the language.
1162#[derive(Debug, Default)]
1163pub struct SynthDefinition {
1164    pub name: String,
1165    /// `target: "…"` — what the synthesised tools are for (the capability scope).
1166    /// Required (§87.d `axon-T879`).
1167    pub target: String,
1168    /// `risk: low | medium | high | critical` — the ceiling risk class the
1169    /// policy admits; governs review + isolation strictness. Required
1170    /// (§87.d `axon-T880`).
1171    pub risk: String,
1172    /// `language: rust | c | python` — the allowed synthesis source language
1173    /// (all compiled to `wasm32-wasi`). Empty ⇒ any admitted language
1174    /// (§87.d `axon-T881` validates when present).
1175    pub language: String,
1176    /// `sandbox: wasm` — the isolation tier. MUST be `wasm` (§87.d `axon-T882`
1177    /// deny-by-default): synthesised code may only run in a zero-trust WASM
1178    /// sandbox. Empty ⇒ error (never a silent "no sandbox").
1179    pub sandbox: String,
1180    /// `review: required | none` — the Coder/Reviewer consensus requirement.
1181    /// Empty ⇒ `required` (the safe default). `none` is FORBIDDEN for
1182    /// `high`/`critical` risk (§87.d `axon-T883`).
1183    pub review: String,
1184    /// `max_lines: <int>` — an optional hard cap on synthesised source length
1185    /// (a smaller attack + review surface). `None` ⇒ engine default.
1186    pub max_lines: Option<i64>,
1187    pub loc: Loc,
1188    /// Fase 14.b — leading comment trivia.
1189    pub leading_trivia: Vec<crate::tokens::Trivia>,
1190    /// Fase 14.b — trailing comment trivia.
1191    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1192}
1193
1194/// `reconnect: { backoff_ms: 500, max_attempts: 5, on_exhausted: fail }` —
1195/// exponential backoff (doubling from `backoff_ms`, jittered at runtime), at
1196/// most `max_attempts` redials; `on_exhausted` is a closed catalog mirroring
1197/// `budget`'s style (v1 sole member `fail` — fail-closed, the flow sees the
1198/// exhaustion; `degrade`/`park` are named deferred scope).
1199#[derive(Debug, Default)]
1200pub struct UpstreamReconnect {
1201    pub backoff_ms: i64,
1202    pub max_attempts: i64,
1203    pub on_exhausted: String,
1204}
1205
1206/// `source -> target : Session` — one directed edge of a topology.
1207///
1208/// Convention: the source plays the FIRST role of the session; the target
1209/// plays the SECOND role. Fixed so assignment is unambiguous.
1210#[derive(Debug, Default)]
1211pub struct TopologyEdge {
1212    pub source: String,
1213    pub target: String,
1214    pub session_ref: String,
1215    pub loc: Loc,
1216}
1217
1218/// `topology Name { nodes: […]  edges: [A -> B : Session, …] }`
1219///
1220/// A typed directed graph over Axon entities. Edges carry session references
1221/// whose duality the type checker enforces; the graph is statically analysed
1222/// for Honda-liveness (deadlock-prone cycles).
1223#[derive(Debug, Default)]
1224pub struct TopologyDefinition {
1225    pub name: String,
1226    pub nodes: Vec<String>,
1227    pub edges: Vec<TopologyEdge>,
1228    pub loc: Loc,
1229    /// Fase 14.b — leading comment trivia attached to this declaration
1230    /// (comments preceding the declaration's first token, since the
1231    /// previous declaration or file start). Empty by default.
1232    pub leading_trivia: Vec<crate::tokens::Trivia>,
1233    /// Fase 14.b — trailing comment trivia (same line as the
1234    /// declaration's last effective token). Empty by default.
1235    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1236}
1237
1238// ── §λ-L-E Fase 5 — Cognitive immune system (per paper_immune_v2.md) ────────
1239
1240/// `immune Name { watch, sensitivity, baseline, window, scope, tau, decay }`
1241///
1242/// A continuous anomaly sensor over a declared observation vector.
1243/// Computes D_KL(q_baseline || p_observed) (paper §3.2) and emits a
1244/// HealthReport at an epistemic level derived from the KL magnitude.
1245///
1246/// Pure sensor — `immune` takes NO action. Actions belong to `reflex`
1247/// and `heal`, which consume its HealthReport.
1248#[derive(Debug, Default)]
1249pub struct ImmuneDefinition {
1250    pub name: String,
1251    pub watch: Vec<String>,       // observe / ensemble / any declared ref
1252    pub sensitivity: Option<f64>, // [0.0, 1.0]
1253    pub baseline: String,         // "learned" (default) or name of a prior
1254    pub window: i64,              // samples used to estimate baseline (default: 100)
1255    pub scope: String,            // tenant | flow | global (MANDATORY, paper §8.2)
1256    pub tau: String,              // duration half-life
1257    pub decay: String,            // exponential (default) | linear | none
1258    pub loc: Loc,
1259    /// Fase 14.b — leading comment trivia attached to this declaration
1260    /// (comments preceding the declaration's first token, since the
1261    /// previous declaration or file start). Empty by default.
1262    pub leading_trivia: Vec<crate::tokens::Trivia>,
1263    /// Fase 14.b — trailing comment trivia (same line as the
1264    /// declaration's last effective token). Empty by default.
1265    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1266}
1267
1268/// `reflex Name { trigger, on_level, action, scope, sla }`
1269///
1270/// Deterministic, O(1) motor response. Contract invariants (paper §4.2):
1271/// never invokes an LLM; no long-running I/O; every activation emits a
1272/// signed_trace; idempotent on the same HealthReport.
1273#[derive(Debug, Default)]
1274pub struct ReflexDefinition {
1275    pub name: String,
1276    pub trigger: String,  // immune name (MANDATORY)
1277    pub on_level: String, // know | believe | speculate | doubt (default: doubt)
1278    pub action: String,   // drop | revoke | emit | redact | quarantine | terminate | alert
1279    pub scope: String,    // MANDATORY, paper §8.2
1280    pub sla: String,      // duration budget (e.g. "1ms")
1281    pub loc: Loc,
1282    /// Fase 14.b — leading comment trivia attached to this declaration
1283    /// (comments preceding the declaration's first token, since the
1284    /// previous declaration or file start). Empty by default.
1285    pub leading_trivia: Vec<crate::tokens::Trivia>,
1286    /// Fase 14.b — trailing comment trivia (same line as the
1287    /// declaration's last effective token). Empty by default.
1288    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1289}
1290
1291/// `heal Name { source, on_level, mode, scope, review_sla, shield, max_patches }`
1292///
1293/// Linear-Logic one-shot patch synthesis. Patch type:
1294/// `!Synthesized ⊸ Applied ⊸ Collapsed` (paper §6) — each transition
1295/// consumes its predecessor, guaranteeing single application + forced collapse.
1296///
1297/// Mode ∈ {audit_only | human_in_loop | adversarial} controls automation
1298/// (paper §7); `adversarial` REQUIRES a shield gate (paper §7.3).
1299#[derive(Debug, Default)]
1300pub struct HealDefinition {
1301    pub name: String,
1302    pub source: String,     // immune name (MANDATORY)
1303    pub on_level: String,   // know | believe | speculate | doubt
1304    pub mode: String,       // audit_only | human_in_loop | adversarial
1305    pub scope: String,      // MANDATORY
1306    pub review_sla: String, // duration
1307    pub shield_ref: String, // optional shield gate (required for adversarial)
1308    pub max_patches: i64,   // bounded heal attempts (default: 3)
1309    pub loc: Loc,
1310    /// Fase 14.b — leading comment trivia attached to this declaration
1311    /// (comments preceding the declaration's first token, since the
1312    /// previous declaration or file start). Empty by default.
1313    pub leading_trivia: Vec<crate::tokens::Trivia>,
1314    /// Fase 14.b — trailing comment trivia (same line as the
1315    /// declaration's last effective token). Empty by default.
1316    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1317}
1318
1319// ── §λ-L-E Fase 9 — UI cognitiva (component / view) ─────────────────────────
1320
1321/// `component Name { renders, via_shield, on_interact, render_hint }`.
1322///
1323/// A reusable UI fragment. `renders` is the data type the component
1324/// visualizes; if that type has κ, `via_shield` is mandatory and its
1325/// compliance set MUST cover the type's κ (compile-time enforcement).
1326#[derive(Debug, Default)]
1327pub struct ComponentDefinition {
1328    pub name: String,
1329    pub renders: String,
1330    pub via_shield: String,
1331    pub on_interact: String,
1332    pub render_hint: String, // card | list | form | chart | custom
1333    pub loc: Loc,
1334    /// Fase 14.b — leading comment trivia attached to this declaration
1335    /// (comments preceding the declaration's first token, since the
1336    /// previous declaration or file start). Empty by default.
1337    pub leading_trivia: Vec<crate::tokens::Trivia>,
1338    /// Fase 14.b — trailing comment trivia (same line as the
1339    /// declaration's last effective token). Empty by default.
1340    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1341}
1342
1343/// `view Name { title, components: [...], route }`.
1344///
1345/// A top-level screen. `components` is an ordered list of declared
1346/// `component` names composed in the view's primary layout.
1347#[derive(Debug, Default)]
1348pub struct ViewDefinition {
1349    pub name: String,
1350    pub title: String,
1351    pub components: Vec<String>,
1352    pub route: String,
1353    pub loc: Loc,
1354    /// Fase 14.b — leading comment trivia attached to this declaration
1355    /// (comments preceding the declaration's first token, since the
1356    /// previous declaration or file start). Empty by default.
1357    pub leading_trivia: Vec<crate::tokens::Trivia>,
1358    /// Fase 14.b — trailing comment trivia (same line as the
1359    /// declaration's last effective token). Empty by default.
1360    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1361}
1362
1363// ── Tier 2+ structural fallback ──────────────────────────────────────────────
1364
1365/// A declaration we recognize by keyword but parse only structurally.
1366/// Validates brace balance and captures keyword + name.
1367#[derive(Debug)]
1368pub struct GenericDeclaration {
1369    pub keyword: String,
1370    pub name: String,
1371    pub loc: Loc,
1372    /// Fase 14.b — leading comment trivia attached to this declaration
1373    /// (comments preceding the declaration's first token, since the
1374    /// previous declaration or file start). Empty by default.
1375    pub leading_trivia: Vec<crate::tokens::Trivia>,
1376    /// Fase 14.b — trailing comment trivia (same line as the
1377    /// declaration's last effective token). Empty by default.
1378    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1379}
1380
1381// ── Agent ────────────────────────────────────────────────────────────────────
1382
1383#[derive(Debug)]
1384pub struct AgentDefinition {
1385    pub name: String,
1386    pub goal: String,
1387    pub tools: Vec<String>,
1388    pub memory_ref: String,
1389    pub strategy: String, // react | reflexion | plan_and_execute | custom
1390    pub on_stuck: String, // forge | hibernate | escalate | retry
1391    pub shield_ref: String,
1392    pub max_iterations: Option<i64>,
1393    pub max_tokens: Option<i64>,
1394    pub max_time: String,
1395    pub max_cost: Option<f64>,
1396    pub loc: Loc,
1397    /// Fase 14.b — leading comment trivia attached to this declaration
1398    /// (comments preceding the declaration's first token, since the
1399    /// previous declaration or file start). Empty by default.
1400    pub leading_trivia: Vec<crate::tokens::Trivia>,
1401    /// Fase 14.b — trailing comment trivia (same line as the
1402    /// declaration's last effective token). Empty by default.
1403    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1404}
1405
1406// ── §Fase 53 — Closed-catalog extension mechanism ────────────────────────────
1407
1408/// One member of an `extension` declaration. For `category: effects`
1409/// the `name` is a provenance base (e.g. `"epistemic:believe"`) with
1410/// optional `semantics` + `default_confidence` (a CEILING, never a
1411/// floor — §53.d tainted-overriding). For `category: scan` the `name`
1412/// is a scan-category identifier and the metadata is typically absent.
1413#[derive(Debug, Clone)]
1414pub struct ExtensionMember {
1415    pub name: String,
1416    pub semantics: Option<String>,
1417    pub default_confidence: Option<f64>,
1418    pub loc: Loc,
1419}
1420
1421/// `extension Name { category: effects|scan, members: [ "x" : { … }, … ] }`
1422///
1423/// §Fase 53. A first-class, auditable + gateable declaration that
1424/// expands a closed catalog with adopter-specific PROVENANCE members.
1425/// Soundness invariants (validated in §53.c/§53.d): members are
1426/// provenance-class only (never the enforceable effect set), must not
1427/// shadow a canonical base/category, and ride in the IR + proof bundle
1428/// so an independent PCC verifier re-derives against the same artifact.
1429#[derive(Debug)]
1430pub struct ExtensionDefinition {
1431    pub name: String,
1432    /// `effects` | `scan` — validated against the closed category set
1433    /// in §53.c (the type-checker), not the parser.
1434    pub category: String,
1435    pub members: Vec<ExtensionMember>,
1436    pub loc: Loc,
1437    /// Fase 14.b — leading comment trivia. Empty by default.
1438    pub leading_trivia: Vec<crate::tokens::Trivia>,
1439    /// Fase 14.b — trailing comment trivia. Empty by default.
1440    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1441}
1442
1443// ── Shield ───────────────────────────────────────────────────────────────────
1444
1445/// §Fase 71.a — a temporal execution-window guard. Where `shield` guards the
1446/// CONTENT of an emission and `anchor` guards its TRUTH, `window` guards its
1447/// TIMING: whether a scheduled (cron) tick runs, by timezone-aware day/hour
1448/// windows. The runtime decision (`is_in_window`) lands in §71.b; the daemon
1449/// binding + the defer ledger in §71.c/d.
1450#[derive(Debug)]
1451pub struct WindowDefinition {
1452    pub name: String,
1453    /// IANA timezone (`"America/Bogota"`). Format-checked at compile time
1454    /// (§71.a); full IANA membership validated by the runtime (§71.b, chrono-tz).
1455    pub timezone: String,
1456    /// The allowed day/hour spans (at least one). A tick inside ANY span runs.
1457    pub allow: Vec<WindowSpan>,
1458    /// §Fase 71.e — excluded dates (holidays): ISO `YYYY-MM-DD` date-string
1459    /// literals (`exclude: [ "2026-12-25", "2026-01-01" ]`). A tick whose local
1460    /// date (in `timezone`) is in this set is OUTSIDE the window regardless of the
1461    /// hour spans. The dates are LITERAL — part of the verified program, so the
1462    /// decision stays a pure, replayable function of `(now, the window, the tz-db
1463    /// version)` (the `time_is_an_explicit_input` doctrine). Empty ⇒ no holidays.
1464    pub exclude: Vec<String>,
1465    /// What to do when a tick falls OUTSIDE every allowed span:
1466    /// `skip` | `defer` | `warn` (closed catalog).
1467    pub on_outside: String,
1468    pub loc: Loc,
1469    pub leading_trivia: Vec<crate::tokens::Trivia>,
1470    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1471}
1472
1473/// §Fase 71.a — one allowed day/hour span, `{ days: Mon..Fri, hours: 9..18 }`.
1474/// `days` are inclusive weekday-name bounds; `hours` are inclusive 0–23 bounds.
1475#[derive(Debug)]
1476pub struct WindowSpan {
1477    pub day_start: String,
1478    pub day_end: String,
1479    pub hour_start: i64,
1480    pub hour_end: i64,
1481    pub loc: Loc,
1482}
1483
1484#[derive(Debug)]
1485pub struct ShieldDefinition {
1486    pub name: String,
1487    pub scan: Vec<String>,
1488    pub strategy: String, // pattern | classifier | dual_llm | canary | perplexity | ensemble
1489    pub on_breach: String, // halt | sanitize_and_retry | escalate | quarantine | deflect
1490    pub severity: String, // low | medium | high | critical
1491    pub quarantine: String,
1492    pub max_retries: Option<i64>,
1493    pub confidence_threshold: Option<f64>,
1494    pub allow_tools: Vec<String>,
1495    pub deny_tools: Vec<String>,
1496    pub sandbox: Option<bool>,
1497    pub redact: Vec<String>,
1498    pub log: String,
1499    pub deflect_message: String,
1500    pub taint: String,
1501    /// §ESK Fase 6.1 — regulatory coverage (HIPAA, PCI_DSS, GDPR, …).
1502    pub compliance: Vec<String>,
1503    /// §Fase 77.a — egress signing algorithm (closed catalog: `hmac_sha256`,
1504    /// `axon-T846`). Empty = the shield does not sign. A shield with `sign:`
1505    /// is an EGRESS shield: `publish <Channel> within <Shield>` marks the
1506    /// channel for signed external delivery (§77.b).
1507    pub sign: String,
1508    /// §Fase 77.a (`axon-W010`) — block fields the parser did not recognize,
1509    /// with their source locations. Pre-77 the parser silently discarded
1510    /// these (`_ => skip_value()`), so a typo or an unsupported field passed
1511    /// `axon check` unremarked (Kivi brief #51 §B.3). The parser still skips
1512    /// the VALUE (leniency preserved — existing programs keep compiling) but
1513    /// records the NAME so the type checker can warn honestly.
1514    pub unknown_fields: Vec<(String, Loc)>,
1515    pub loc: Loc,
1516    /// Fase 14.b — leading comment trivia attached to this declaration
1517    /// (comments preceding the declaration's first token, since the
1518    /// previous declaration or file start). Empty by default.
1519    pub leading_trivia: Vec<crate::tokens::Trivia>,
1520    /// Fase 14.b — trailing comment trivia (same line as the
1521    /// declaration's last effective token). Empty by default.
1522    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1523}
1524
1525// ── Pix ──────────────────────────────────────────────────────────────────────
1526
1527#[derive(Debug)]
1528pub struct PixDefinition {
1529    pub name: String,
1530    pub source: String,
1531    pub depth: Option<i64>,
1532    pub branching: Option<i64>,
1533    pub model: String,
1534    pub loc: Loc,
1535    /// Fase 14.b — leading comment trivia attached to this declaration
1536    /// (comments preceding the declaration's first token, since the
1537    /// previous declaration or file start). Empty by default.
1538    pub leading_trivia: Vec<crate::tokens::Trivia>,
1539    /// Fase 14.b — trailing comment trivia (same line as the
1540    /// declaration's last effective token). Empty by default.
1541    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1542}
1543
1544// ── Ledger ─────────────────────────────────────────────────────────────────
1545// §Fase 62.0 — the append-only, hash-linked audit chain. Took over the
1546// Provenance-Index role that `pix` historically (and only in the ℰMCP doc)
1547// occupied, so `pix` is freed for its true meaning: the PIX retrieval
1548// navigator (paper `paper_pix_formal_research.md`). A `ledger` binds a chain
1549// recorder to an audited surface (`axonstore://X`, `flow://X`, …); `depth`
1550// is chain retention, `branching` the Merkle factor, `model` the hash slug.
1551
1552#[derive(Debug)]
1553pub struct LedgerDefinition {
1554    pub name: String,
1555    pub source: String,
1556    pub depth: Option<i64>,
1557    pub branching: Option<i64>,
1558    pub model: String,
1559    pub loc: Loc,
1560    pub leading_trivia: Vec<crate::tokens::Trivia>,
1561    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1562}
1563
1564// ── Psyche ───────────────────────────────────────────────────────────────────
1565
1566#[derive(Debug)]
1567pub struct PsycheDefinition {
1568    pub name: String,
1569    pub dimensions: Vec<String>,
1570    pub manifold_noise: Option<f64>,
1571    pub manifold_momentum: Option<f64>,
1572    pub safety_constraints: Vec<String>,
1573    pub quantum_enabled: Option<bool>,
1574    pub inference_mode: String, // active | passive
1575    pub loc: Loc,
1576    /// Fase 14.b — leading comment trivia attached to this declaration
1577    /// (comments preceding the declaration's first token, since the
1578    /// previous declaration or file start). Empty by default.
1579    pub leading_trivia: Vec<crate::tokens::Trivia>,
1580    /// Fase 14.b — trailing comment trivia (same line as the
1581    /// declaration's last effective token). Empty by default.
1582    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1583}
1584
1585// ── Corpus ───────────────────────────────────────────────────────────────────
1586
1587/// §Fase 63.A — a typed, weighted edge of an MDN corpus graph: `etype(from, to,
1588/// weight)`. `etype` is from the closed relation catalog (cite / elaborate /
1589/// corroborate / depend / implement / exemplify / contradict / supersede);
1590/// `from`/`to` name documents declared in the corpus; `weight ∈ (0, 1]`.
1591#[derive(Debug, Clone)]
1592pub struct CorpusRelation {
1593    pub etype: String,
1594    pub from: String,
1595    pub to: String,
1596    pub weight: f64,
1597    pub loc: Loc,
1598}
1599
1600#[derive(Debug)]
1601pub struct CorpusDefinition {
1602    pub name: String,
1603    pub documents: Vec<String>, // simplified: list of pix refs
1604    /// §Fase 63.A — the typed weighted edges that make this corpus an MDN graph
1605    /// `C = (D, R, τ, ω, σ)`. Empty ⇒ the flat (edgeless) corpus.
1606    pub relations: Vec<CorpusRelation>,
1607    /// §Fase 63.C — `adaptive: true` enables the memory endofunctor: navigations
1608    /// over this corpus learn (semantic edge reinforcement + procedural bias),
1609    /// and subsequent navigations use the memory-modified EPR. Requires the
1610    /// graph to carry edges (static `relations:` OR a store-sourced edge store).
1611    pub adaptive: bool,
1612    pub mcp_server: String,
1613    pub mcp_resource_uri: String,
1614    /// §Fase 64.A — when `Some`, this is a DYNAMIC store-sourced MDN graph
1615    /// (`corpus N from axonstore { documents: DocStore(id, title)  relations:
1616    /// EdgeStore(from, to, etype, weight) }`): the documents and typed edges live
1617    /// as ROWS in two declared `axonstore`s and the graph is built from the live
1618    /// rows at navigate-time (per-tenant, growing). Mutually exclusive with the
1619    /// static §63 form — the `documents`/`relations`/`mcp_*` fields stay empty.
1620    /// `None` ⇒ the static compile-time corpus (back-compat byte-identical).
1621    pub store_source: Option<CorpusStoreSource>,
1622    pub loc: Loc,
1623    /// Fase 14.b — leading comment trivia attached to this declaration
1624    /// (comments preceding the declaration's first token, since the
1625    /// previous declaration or file start). Empty by default.
1626    pub leading_trivia: Vec<crate::tokens::Trivia>,
1627    /// Fase 14.b — trailing comment trivia (same line as the
1628    /// declaration's last effective token). Empty by default.
1629    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1630}
1631
1632/// §Fase 64.A — the dynamic, `axonstore`-sourced backing of an MDN corpus graph.
1633/// The graph's documents and typed edges are ROWS in two declared `axonstore`s,
1634/// so the graph grows at runtime (a new `persist` = a new node/edge) and is
1635/// per-tenant by inheritance from the store's §40 column-proof / RLS scope. The
1636/// runtime builds the `mdn::Corpus` from the live rows at navigate-time.
1637///
1638/// Surface:
1639/// ```text
1640/// corpus LtmGraph from axonstore {
1641///     documents: LtmSummaries( id, summary )                 // (id-col, title-col)
1642///     relations: LtmEdges( from_id, to_id, etype, weight )   // (from, to, etype, weight)
1643///     adaptive: true
1644/// }
1645/// ```
1646/// Documents and edges live in SEPARATE stores (an `axonstore` is one table with
1647/// one column schema). The type-checker (`check_corpus`) validates that both
1648/// stores are declared and — when they carry a §38 column schema — that the
1649/// mapped columns exist with compatible types (id present; title text-like;
1650/// from/to match the id type; etype text-like; weight numeric). The weight-range
1651/// invariant `ω ∈ (0, 1]` (G4) becomes a RUNTIME check here, since weights are
1652/// per-row dynamic (clamp on read + store CHECK), not a compile-time literal.
1653#[derive(Debug, Clone)]
1654pub struct CorpusStoreSource {
1655    pub doc_store: String,
1656    pub doc_id_col: String,
1657    pub doc_title_col: String,
1658    pub edge_store: String,
1659    pub edge_from_col: String,
1660    pub edge_to_col: String,
1661    pub edge_type_col: String,
1662    pub edge_weight_col: String,
1663    pub loc: Loc,
1664}
1665
1666// ── Dataspace ────────────────────────────────────────────────────────────────
1667
1668/// §Fase 108.b (D108.1) — the closed dataspace column-type catalog.
1669///
1670/// Six types, because a dataspace column type maps 1:1 to a physical
1671/// columnar buffer layout in the deterministic engine (§108.b, plan §5.1):
1672///
1673/// | Type        | Physical layout                                    |
1674/// |-------------|----------------------------------------------------|
1675/// | `Text`      | offsets `O ∈ ℤ₊^{N+1}` + raw UTF-8 byte buffer     |
1676/// | `Int`       | contiguous `i64` buffer (8 bytes/element)          |
1677/// | `Float`     | contiguous `f64` buffer (8 bytes/element)          |
1678/// | `Bool`      | bit-packed buffer                                  |
1679/// | `Timestamp` | contiguous `i64` epoch-microseconds buffer         |
1680/// | `Json`      | offsets + raw serialized-JSON byte buffer          |
1681///
1682/// Deliberately NOT `StoreColumnType` (§38): that catalog is
1683/// SQL-backend-oriented (Uuid, Numeric, Bytea, …); this one is the
1684/// engine's physical truth. `Float` is **f64** — diverging from the
1685/// research paper's Float32 on purpose: axon's determinism norm is
1686/// exact f64 (the §51 reference-simulator precedent), and halving
1687/// width is a performance claim we do not make pre-Sandbox (D101.19).
1688/// Every column is nullable via the validity bitmap; nullability is
1689/// the ONLY flexibility (D108.7).
1690#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1691pub enum DataspaceColumnType {
1692    Text,
1693    Int,
1694    Float,
1695    Bool,
1696    Timestamp,
1697    Json,
1698}
1699
1700impl DataspaceColumnType {
1701    /// The closed catalog in canonical declaration order.
1702    pub fn all() -> [DataspaceColumnType; 6] {
1703        [
1704            DataspaceColumnType::Text,
1705            DataspaceColumnType::Int,
1706            DataspaceColumnType::Float,
1707            DataspaceColumnType::Bool,
1708            DataspaceColumnType::Timestamp,
1709            DataspaceColumnType::Json,
1710        ]
1711    }
1712
1713    pub fn canonical_name(self) -> &'static str {
1714        match self {
1715            DataspaceColumnType::Text => "Text",
1716            DataspaceColumnType::Int => "Int",
1717            DataspaceColumnType::Float => "Float",
1718            DataspaceColumnType::Bool => "Bool",
1719            DataspaceColumnType::Timestamp => "Timestamp",
1720            DataspaceColumnType::Json => "Json",
1721        }
1722    }
1723
1724    pub fn all_canonical_names() -> Vec<&'static str> {
1725        Self::all().iter().map(|t| t.canonical_name()).collect()
1726    }
1727
1728    /// Resolve a declared type token — canonical names plus the common
1729    /// lowercase aliases (the §38 `from_token` convention).
1730    pub fn from_token(name: &str) -> Option<DataspaceColumnType> {
1731        match name {
1732            "Text" | "text" | "string" | "String" => Some(DataspaceColumnType::Text),
1733            "Int" | "int" | "integer" | "i64" => Some(DataspaceColumnType::Int),
1734            "Float" | "float" | "double" | "f64" => Some(DataspaceColumnType::Float),
1735            "Bool" | "bool" | "boolean" => Some(DataspaceColumnType::Bool),
1736            "Timestamp" | "timestamp" | "datetime" => Some(DataspaceColumnType::Timestamp),
1737            "Json" | "json" => Some(DataspaceColumnType::Json),
1738            _ => None,
1739        }
1740    }
1741}
1742
1743/// §Fase 108.b — one declared dataspace column: `column <name>: <Type>`.
1744/// The type is kept RAW at parse time (the string the adopter wrote);
1745/// the §108.b type-checker resolves it against the closed catalog and
1746/// emits `axon-T928` on a miss — so ALL schema errors in a declaration
1747/// accumulate in one compile, instead of dying at the first bad token.
1748#[derive(Debug)]
1749pub struct DataspaceColumn {
1750    pub name: String,
1751    pub declared_type: String,
1752    pub loc: Loc,
1753}
1754
1755#[derive(Debug)]
1756pub struct DataspaceDefinition {
1757    pub name: String,
1758    /// §Fase 108.b — the typed columnar schema. A dataspace IS its
1759    /// schema: the §108.b type-checker refuses an empty one (axon-T928).
1760    pub columns: Vec<DataspaceColumn>,
1761    pub loc: Loc,
1762    /// Fase 14.b — leading comment trivia attached to this declaration
1763    /// (comments preceding the declaration's first token, since the
1764    /// previous declaration or file start). Empty by default.
1765    pub leading_trivia: Vec<crate::tokens::Trivia>,
1766    /// Fase 14.b — trailing comment trivia (same line as the
1767    /// declaration's last effective token). Empty by default.
1768    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1769}
1770
1771// ── OTS ──────────────────────────────────────────────────────────────────────
1772
1773#[derive(Debug)]
1774pub struct OtsDefinition {
1775    pub name: String,
1776    pub teleology: String,
1777    pub homotopy_search: String, // shallow | deep | speculative
1778    pub loss_function: String,
1779    pub loc: Loc,
1780    /// Fase 14.b — leading comment trivia attached to this declaration
1781    /// (comments preceding the declaration's first token, since the
1782    /// previous declaration or file start). Empty by default.
1783    pub leading_trivia: Vec<crate::tokens::Trivia>,
1784    /// Fase 14.b — trailing comment trivia (same line as the
1785    /// declaration's last effective token). Empty by default.
1786    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1787}
1788
1789// ── Mandate ──────────────────────────────────────────────────────────────────
1790
1791#[derive(Debug)]
1792pub struct MandateDefinition {
1793    pub name: String,
1794    pub constraint: String,
1795    pub kp: Option<f64>,
1796    pub ki: Option<f64>,
1797    pub kd: Option<f64>,
1798    pub tolerance: Option<f64>,
1799    pub max_steps: Option<i64>,
1800    pub on_violation: String, // coerce | halt | retry
1801    pub loc: Loc,
1802    /// Fase 14.b — leading comment trivia attached to this declaration
1803    /// (comments preceding the declaration's first token, since the
1804    /// previous declaration or file start). Empty by default.
1805    pub leading_trivia: Vec<crate::tokens::Trivia>,
1806    /// Fase 14.b — trailing comment trivia (same line as the
1807    /// declaration's last effective token). Empty by default.
1808    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1809}
1810
1811// ── Compute ──────────────────────────────────────────────────────────────────
1812
1813#[derive(Debug)]
1814pub struct ComputeDefinition {
1815    pub name: String,
1816    pub shield_ref: String,
1817    /// §Fase 111.f — the typed parameters. Before §111, `parse_compute` SKIPPED
1818    /// everything between the name and the brace ("Skip optional parameters/
1819    /// return type"), so a compute had no inputs at all — which is one reason it
1820    /// could not compute anything.
1821    pub parameters: Vec<Parameter>,
1822    /// §Fase 111.f — the declared result type.
1823    pub return_type: String,
1824    /// §Fase 111.f — **the body**: a §70 `Expr`.
1825    ///
1826    /// This is what makes `compute` honest. The README sells it as "Deterministic
1827    /// muscle — native Fast-Path execution BYPASSING the LLM" and even asserts a
1828    /// complexity class ("compute steps: O(n)"). A §70 expression is exactly that
1829    /// and nothing more: a closed, total, side-effect-free term the runtime
1830    /// evaluates with `eval_expr` — the same native evaluator `let`, `grad` and
1831    /// `conditional` already use. Linear in the term. No model in the loop.
1832    ///
1833    /// `None` ⇒ a compute that cannot compute; applying it is refused
1834    /// (axon-T941), rather than binding the literal string `"compute:Name(args)"`
1835    /// as the pre-§111 runtime did — which a downstream step then consumed as if
1836    /// it were a number.
1837    pub body: Option<Expr>,
1838    pub loc: Loc,
1839    /// Fase 14.b — leading comment trivia attached to this declaration
1840    /// (comments preceding the declaration's first token, since the
1841    /// previous declaration or file start). Empty by default.
1842    pub leading_trivia: Vec<crate::tokens::Trivia>,
1843    /// Fase 14.b — trailing comment trivia (same line as the
1844    /// declaration's last effective token). Empty by default.
1845    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1846}
1847
1848// ── Daemon ───────────────────────────────────────────────────────────────────
1849
1850#[derive(Debug)]
1851pub struct DaemonDefinition {
1852    pub name: String,
1853    pub goal: String,
1854    pub tools: Vec<String>,
1855    pub memory_ref: String,
1856    pub strategy: String, // react | reflexion | plan_and_execute | custom
1857    pub on_stuck: String, // hibernate | escalate | retry | forge
1858    pub shield_ref: String,
1859    /// §Fase 71.c — the `window:` temporal binding (a `window` primitive name,
1860    /// §71.a). When set, the supervisor evaluates the bound window before
1861    /// claiming a scheduled tick: inside ⇒ fire; outside ⇒ `skip`/`warn`/`defer`
1862    /// per the window's `on_outside`. Empty for daemons with no temporal guard.
1863    pub window_ref: String,
1864    /// §Fase 72.a — the `budget { … }` linear-effect rate limit. When set, each
1865    /// `on Tool(X)` quota gates that tool's dispatch on a renewable token bucket
1866    /// (the §72.b `RateLease`): a call consumes a token, exhaustion applies
1867    /// `on_exhausted`. `None` for daemons with no effect budget.
1868    pub budget: Option<BudgetBlock>,
1869    pub max_tokens: Option<i64>,
1870    pub max_time: String,
1871    pub max_cost: Option<f64>,
1872    /// §λ-L-E Fase 13 D4 — listen blocks captured for type-checker
1873    /// validation (typed-channel ref + dual-mode deprecation warning).
1874    /// Pre-Fase 13 the parser discarded these structurally; we now
1875    /// retain them so 13.b/13.f can validate emit/publish/discover
1876    /// inside listener bodies and surface D4 string-topic warnings.
1877    pub listeners: Vec<ListenStep>,
1878    /// §Fase 52.d — the capability scope a daemon's runs are confined to
1879    /// (`requires: [cap, …]`, the same closed slug grammar as `axonendpoint
1880    /// requires:`). A scheduled (cron) daemon MUST declare this (it is a
1881    /// standing autonomous privilege); the enterprise supervisor mints a
1882    /// per-run principal scoped to EXACTLY these capabilities (least privilege,
1883    /// §52.d). Empty for event-only daemons / pre-§52 daemons.
1884    pub requires_capabilities: Vec<String>,
1885    pub loc: Loc,
1886    /// Fase 14.b — leading comment trivia attached to this declaration
1887    /// (comments preceding the declaration's first token, since the
1888    /// previous declaration or file start). Empty by default.
1889    pub leading_trivia: Vec<crate::tokens::Trivia>,
1890    /// Fase 14.b — trailing comment trivia (same line as the
1891    /// declaration's last effective token). Empty by default.
1892    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1893}
1894
1895/// §Fase 72.a — a `budget { … }` block: a set of per-effect rate quotas plus the
1896/// exhaustion policy. Where `window` guards an effect's TIMING, `budget` guards
1897/// its RATE — a tool call consumes one token from a renewable bucket, and
1898/// over-emission is impossible by construction (the Logic pillar's linearity
1899/// made real for external effects).
1900#[derive(Debug)]
1901pub struct BudgetBlock {
1902    /// §Fase 114.a — the budget's NAME when it is declared **top-level**.
1903    ///
1904    /// Empty ⇒ the daemon-attached form (`daemon D { budget { … } }`), which is
1905    /// anonymous and scoped to that daemon's ticks.
1906    ///
1907    /// # Why top-level had to exist
1908    ///
1909    /// Until §114, `budget` was a **field of `daemon` and nothing else**. So an
1910    /// adopter deploying an HTTP endpoint that calls a vendor tool had **no way in
1911    /// the language to bound how often it does that.** Not "the bound did not
1912    /// work" — **the bound could not be written.** And the HTTP endpoint is what
1913    /// people actually deploy.
1914    pub name: String,
1915    /// The per-effect quotas (`rate:`/`max:` lines). At least one.
1916    pub quotas: Vec<BudgetQuota>,
1917    /// What to do when a quota is exhausted: `block` (fail-closed, the default) |
1918    /// `defer` (reschedule via the §71 defer ledger) | `shed` (skip the call).
1919    pub on_exhausted: String,
1920    pub loc: Loc,
1921    /// §Fase 114.a — comment trivia, so a top-level `budget` does not silently lose
1922    /// its doc comments through the formatter (Fase 14.b). Empty for the
1923    /// daemon-attached form.
1924    pub leading_trivia: Vec<crate::tokens::Trivia>,
1925    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1926}
1927
1928/// §Fase 72.a — one quota line of a [`BudgetBlock`]:
1929/// `<kind>: <limit> per <period> on Tool(<effect>)`.
1930#[derive(Debug)]
1931pub struct BudgetQuota {
1932    /// `rate` (a renewable bucket that refills `limit` tokens per `period`) or
1933    /// `max` (a windowed hard cap of `limit` per `period`, no intra-window refill).
1934    pub kind: String,
1935    /// The token allowance per period (> 0).
1936    pub limit: i64,
1937    /// The renewal/window period: `second` | `minute` | `hour` | `day`.
1938    pub period: String,
1939    /// The effect this quota governs — a declared `Tool` name (`on Tool(X)`).
1940    pub effect: String,
1941    pub loc: Loc,
1942}
1943
1944// ── AxonStore ────────────────────────────────────────────────────────────────
1945
1946#[derive(Debug)]
1947pub struct AxonStoreDefinition {
1948    pub name: String,
1949    pub backend: String, // in_memory | postgresql | secrets
1950    /// The DSN — **the field that actually runs today**. `connection:` →
1951    /// `resolve_dsn` → a real sqlx pool, with no global-pool fallback.
1952    ///
1953    /// §Fase 113: still accepted (the live deployment depends on it), but it
1954    /// **warns**, and a store declared this way is INELIGIBLE for
1955    /// `lease` / `observe` / `reconcile`. *You cannot govern what you did not
1956    /// declare.*
1957    pub connection: String,
1958    /// §Fase 113 — the `resource` this store runs on. When present, the store
1959    /// DERIVES its DSN, its **pool size** and its sharing discipline from the
1960    /// resource. The derivation is the point; a bare reference would be the
1961    /// nominal link this fase exists to avoid.
1962    pub resource_ref: String,
1963    pub confidence_floor: Option<f64>,
1964    pub isolation: String, // read_committed | repeatable_read | serializable
1965    pub on_breach: String, // rollback | raise | log
1966    /// §Fase 35.j (D11) — Pillar IV: the capability slug required to
1967    /// access this store. Empty = no capability gate. Validated at
1968    /// parse time against the closed slug grammar (shared with the
1969    /// Fase 32.g `requires:` grammar).
1970    pub capability: String,
1971    /// §Fase 94.a — the secret-class prefix of a `backend: secrets`
1972    /// metadata store (doctrine `rotation_without_revelation`). A
1973    /// dotted lowercase identifier, e.g. `crm` — the store enumerates
1974    /// the tenant's secrets whose keys live under `<class>.` (so
1975    /// `class: crm` covers `crm.hubspot`, `crm.zoho.acct_x`, …).
1976    /// REQUIRED when `backend: secrets` and FORBIDDEN otherwise
1977    /// (`axon-T900`): a class-less secrets store would enumerate the
1978    /// tenant's ENTIRE secret namespace (`llm.*` included) — that
1979    /// over-broad view is unrepresentable, not discouraged.
1980    pub class: String,
1981    /// §Fase 38.b (D1) — the OPTIONAL column-schema declaration. Three
1982    /// closed forms (inline / manifest-ref / env-var); `None` means the
1983    /// 37.x runtime+deploy path applies verbatim (D5 absolute). The
1984    /// §38.d / §38.e `StoreColumnProof` pass consumes this; the §38.h
1985    /// CLI exports it. For a `backend: secrets` store this is always
1986    /// `None` in the AST (declaring one is `axon-T900`) — the fixed
1987    /// metadata schema is synthesized at IR time
1988    /// (`store_schema::secrets_metadata_schema`).
1989    pub column_schema: Option<crate::store_schema::StoreColumnSchema>,
1990    pub loc: Loc,
1991    /// Fase 14.b — leading comment trivia attached to this declaration
1992    /// (comments preceding the declaration's first token, since the
1993    /// previous declaration or file start). Empty by default.
1994    pub leading_trivia: Vec<crate::tokens::Trivia>,
1995    /// Fase 14.b — trailing comment trivia (same line as the
1996    /// declaration's last effective token). Empty by default.
1997    pub trailing_trivia: Vec<crate::tokens::Trivia>,
1998}
1999
2000// ── AxonEndpoint ─────────────────────────────────────────────────────────────
2001
2002#[derive(Debug)]
2003pub struct AxonEndpointDefinition {
2004    pub name: String,
2005    pub method: String, // GET | POST | PUT | DELETE
2006    pub path: String,
2007    pub body_type: String,
2008    pub execute_flow: String,
2009    pub output_type: String,
2010    pub shield_ref: String,
2011    /// §Fase 83.a — the `cors: <Name>` reference, or `""` if absent
2012    /// (D83.5: absent ⇒ no CORS headers, ever — secure by default). Same
2013    /// empty-string sentinel convention as `shield_ref`, not `Option`.
2014    pub cors_ref: String,
2015    pub retries: Option<i64>,
2016    pub timeout: String,
2017    /// §ESK Fase 6.1 — regulatory coverage on the boundary.
2018    pub compliance: Vec<String>,
2019    /// §Fase 30 — HTTP wire transport for the response. Closed enum
2020    /// per D2 ratified 2026-05-10: {"json" | "sse" | "ndjson"}.
2021    /// Default "json" (D1 — backwards-compat preserved). When set
2022    /// to "sse", the type-checker (30.c) verifies that
2023    /// `execute_flow` produces a Stream<T> (D3).
2024    pub transport: String,
2025    /// §Fase 30 — Keepalive comment interval for SSE transport (D6).
2026    /// Optional; default applied at runtime when transport == "sse"
2027    /// (default 15s). Closed enum at parse time:
2028    /// {"5s" | "15s" | "30s" | "60s"}. Empty string means
2029    /// "use runtime default".
2030    pub keepalive: String,
2031    /// §Fase 31.b — Type-Driven Wire Inference (D1, D7).
2032    /// `transport_explicit` is `true` if the source declared
2033    /// `transport:` explicitly (any of json/sse/ndjson). `false` if
2034    /// the field was omitted, in which case `transport` reflects the
2035    /// D1 default `"json"` but `implicit_transport` (computed by the
2036    /// `axon_frontend::type_checker::compute_implicit_transports`
2037    /// pass) carries the inferred value.
2038    pub transport_explicit: bool,
2039    /// §Fase 31.b — Inferred wire transport per D1:
2040    ///   implicit_transport(E) =
2041    ///     declared_transport(E)   if transport_explicit
2042    ///     "sse"                    if produces_stream(execute_flow) ∧ ¬explicit
2043    ///     "json"                   otherwise
2044    /// Empty string `""` before the type-checker runs. The Python
2045    /// reference implementation in `axon/compiler/type_checker.py`
2046    /// sets the field byte-identically (D7 cross-stack contract).
2047    pub implicit_transport: String,
2048    /// §Fase 32.g (D8) — Auth scope: capability slugs the request
2049    /// bearer must hold for the endpoint to dispatch. Empty vec
2050    /// means "no auth gate" (D9 backwards-compat). Slug grammar
2051    /// (closed): `^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$`. Examples:
2052    /// `admin`, `legal.read`, `hipaa.phi.read`. The runtime checks
2053    /// declared_requires ⊆ token_capabilities (AND semantics — every
2054    /// declared capability must be present in the bearer's claims).
2055    pub requires_capabilities: Vec<String>,
2056    /// §Fase 89.a — `public: true` is the EXPLICIT authorization-coverage
2057    /// opt-out (doctrine `every_boundary_is_guarded`). An `axonendpoint`
2058    /// compiles iff it is covered by ≥1 discipline (`requires:` / `shield:` /
2059    /// `compliance:`) OR declares `public: true`. Omitting BOTH is a hard
2060    /// error (`axon-T890`, §89.b) — this is what closes Modo 1 (a guard
2061    /// bypassable by silent omission). `public: true` does NOT bypass HTTP
2062    /// authentication (the enterprise `require_auth` middleware is a
2063    /// SEPARATE concern); it declares — deliberately + auditably — that the
2064    /// endpoint carries no capability/shield/compliance coverage. Default
2065    /// `false` (parser + programmatic construction); the §89.b rule reads it.
2066    pub public: bool,
2067    /// §Fase 32.h — Replay-token binding (D9 plan-vivo).
2068    /// `replay_explicit` is `true` when the source declared `replay:`
2069    /// explicitly. `false` when the field was omitted, in which case
2070    /// `replay` reflects the method-default (POST/PUT → true, GET/
2071    /// DELETE → false) computed at deploy time. When the effective
2072    /// value resolves to `true`, every successful 2xx response is
2073    /// recorded in the runtime's axonendpoint replay log keyed by
2074    /// trace_id; auditors retrieve it via GET /v1/replay/<trace_id>.
2075    pub replay_explicit: bool,
2076    pub replay: bool,
2077    /// §Fase 33.z.k.b (v1.28.0) — Selected SSE wire-format dialect.
2078    ///
2079    /// Populated when the source uses the parametrized grammar
2080    /// `transport: sse(<dialect>)`. Closed catalog
2081    /// (`AXONENDPOINT_TRANSPORT_DIALECTS`): `{axon, openai, anthropic}`.
2082    /// Empty string `""` when:
2083    ///   - the source declared a non-SSE transport (`json`/`ndjson`), OR
2084    ///   - the source declared bare `transport: sse` without parens, OR
2085    ///   - the source omitted `transport:` entirely (D1 implicit path).
2086    ///
2087    /// When empty + the effective wire is SSE (per the runtime
2088    /// classifier `classify_dynamic_route_wire`), the runtime
2089    /// resolves the dialect via the Q1 algebraic-effect-driven
2090    /// default: openai for tool-streaming flows (algebraic predicate
2091    /// true), axon for type-annotation-only flows (algebraic predicate
2092    /// false). D3 explicit `transport: sse(<dialect>)` overrides the
2093    /// default.
2094    pub transport_dialect: String,
2095    /// §Fase 33.z.k.1 (v1.27.1) — Algebraic-effect override predicate.
2096    ///
2097    /// `true` when `execute_flow` references a tool that declares
2098    /// `effects: <stream:<policy>>` (Fase 30 algebraic-effect surface).
2099    /// Mirrors `type_checker::flow_uses_streaming_tool(execute_flow, program)`.
2100    ///
2101    /// Used by the runtime classifier
2102    /// (`axon_server::classify_dynamic_route_wire`) to OVERRIDE the
2103    /// v1.22.0 D6 backwards-compat gate: a tool with a declared stream
2104    /// effect is a LANGUAGE-LEVEL commitment to streaming, not a
2105    /// client preference. When this field is `true` AND
2106    /// `transport: json` is NOT explicitly declared (D3 opt-out remains
2107    /// sacred), the route wire is unconditionally `Sse` — no
2108    /// `Accept: text/event-stream` header required, no
2109    /// `AXON_STRICT_TYPE_DRIVEN_TRANSPORT=1` runtime flag required.
2110    ///
2111    /// Computed in lockstep with `implicit_transport` by the
2112    /// `compute_implicit_transports` pass. Default `false` before the
2113    /// pass runs (matches AST construction defaults; D9 backwards-
2114    /// compat preserved for older AST consumers).
2115    pub has_algebraic_stream_effect: bool,
2116    /// §Fase 36.d (D2) — the declared execution backend for the flow
2117    /// behind this endpoint. Empty string `""` means "not declared"
2118    /// (the endpoint resolves its backend down the Fase 36 D1
2119    /// precedence ladder — server default → environment-available
2120    /// `auto`). When non-empty the parser has validated it against the
2121    /// closed catalog [`crate::parser::AXONENDPOINT_BACKEND_VALUES`]
2122    /// and the type-checker rejects an unknown name as a compile
2123    /// error. A declared `backend:` is rung 2 of the resolution
2124    /// contract.
2125    pub backend: String,
2126    /// §Fase 37.y (D1) — Path parameter names extracted from the
2127    /// `path:` string at parse time. For `path: "/api/tenants/{tenant_id}/secrets/{secret_name}"`
2128    /// this is `["tenant_id", "secret_name"]`. Empty Vec when the
2129    /// path has no `{name}` placeholders (D5 backwards-compat — an
2130    /// endpoint without path params produces byte-identical IR to
2131    /// v1.38.4).
2132    ///
2133    /// Names are deduplicated + recorded in declaration order. A
2134    /// duplicate `{tenant_id}` in the same path is a parse error
2135    /// (HTTP route patterns reject duplicates structurally — `axum`
2136    /// would panic at registration). Type binding is always `Text`
2137    /// in v1.38.5 (HTTP path-segment convention); a future Fase 37.z
2138    /// may add per-placeholder type-override grammar `{tenant_id: Uuid}`.
2139    ///
2140    /// The Fase 37 D2 totality check (extended by 37.y D3) treats
2141    /// every name here as covering an equivalent flow parameter
2142    /// declared `Text`. Collision with a body field of the same name
2143    /// is a compile error (`axon-T901`, D4).
2144    pub path_params: Vec<String>,
2145    /// §Fase 37.y (D2) — Query parameters declared via the inline
2146    /// `query: { name: Type, name: Type? }` block on the endpoint.
2147    /// Empty Vec when the source omits the block (D5 backwards-compat).
2148    ///
2149    /// Closed type catalog (parser-enforced):
2150    /// `{Text, Int, Float, Bool, Uuid}`. The runtime receives every
2151    /// query value as a textual `String` and binds it to the same-named
2152    /// flow parameter; the closed catalog enables future per-type
2153    /// parsing/validation without breaking the manifest format.
2154    ///
2155    /// The optional flag (`?` suffix in the source) reuses
2156    /// `TypeExpr.optional`. An optional query param need not be
2157    /// covered by a flow parameter (D3 totality is over required
2158    /// params); a required query param missing from the flow signature
2159    /// is a `axon-T?nn` future arm. For v1.38.5 the totality check
2160    /// treats every query param as a binding-source candidate for any
2161    /// same-named flow param.
2162    ///
2163    /// Reusing `TypeField` (shared with body type declarations) keeps
2164    /// the D2 totality check uniform — the same `field.type_expr.name
2165    /// == param.type_expr.name` comparator works for body fields AND
2166    /// query params.
2167    pub query_params: Vec<TypeField>,
2168    pub loc: Loc,
2169    /// Fase 14.b — leading comment trivia attached to this declaration
2170    /// (comments preceding the declaration's first token, since the
2171    /// previous declaration or file start). Empty by default.
2172    pub leading_trivia: Vec<crate::tokens::Trivia>,
2173    /// Fase 14.b — trailing comment trivia (same line as the
2174    /// declaration's last effective token). Empty by default.
2175    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2176}
2177
2178// ── Import ───────────────────────────────────────────────────────────────────
2179
2180#[derive(Debug)]
2181pub struct ImportNode {
2182    pub module_path: Vec<String>,
2183    pub names: Vec<String>,
2184    /// §Fase 115.c — the `@allow_downgrade` ECC valve: acknowledges an
2185    /// epistemic downgrade across this import edge (silences `axon-W017`,
2186    /// downgrades `axon-T954` to a *visible* `axon-W017`). `false` for
2187    /// every import written before §115.
2188    pub allow_downgrade: bool,
2189    pub loc: Loc,
2190    /// Fase 14.b — leading comment trivia attached to this declaration
2191    /// (comments preceding the declaration's first token, since the
2192    /// previous declaration or file start). Empty by default.
2193    pub leading_trivia: Vec<crate::tokens::Trivia>,
2194    /// Fase 14.b — trailing comment trivia (same line as the
2195    /// declaration's last effective token). Empty by default.
2196    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2197}
2198
2199// ── Persona ──────────────────────────────────────────────────────────────────
2200
2201#[derive(Debug)]
2202pub struct PersonaDefinition {
2203    pub name: String,
2204    pub domain: Vec<String>,
2205    pub tone: String,
2206    pub confidence_threshold: Option<f64>,
2207    pub cite_sources: Option<bool>,
2208    pub refuse_if: Vec<String>,
2209    pub language: String,
2210    pub description: String,
2211    pub loc: Loc,
2212    /// Fase 14.b — leading comment trivia attached to this declaration
2213    /// (comments preceding the declaration's first token, since the
2214    /// previous declaration or file start). Empty by default.
2215    pub leading_trivia: Vec<crate::tokens::Trivia>,
2216    /// Fase 14.b — trailing comment trivia (same line as the
2217    /// declaration's last effective token). Empty by default.
2218    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2219}
2220
2221// ── Context ──────────────────────────────────────────────────────────────────
2222
2223#[derive(Debug)]
2224pub struct ContextDefinition {
2225    pub name: String,
2226    pub memory_scope: String,
2227    pub language: String,
2228    pub depth: String,
2229    pub max_tokens: Option<i64>,
2230    pub temperature: Option<f64>,
2231    pub cite_sources: Option<bool>,
2232    /// §Fase 91.a — the conversational frame's declared cognitive timezone
2233    /// (IANA name). Every step running within this context carries the run's
2234    /// captured instant rendered in this zone, unless the step declares its
2235    /// own `now:` override. Format-checked (`axon-T892`); `None` → no
2236    /// temporal injection (back-compat).
2237    pub now_tz: Option<String>,
2238    pub loc: Loc,
2239    /// Fase 14.b — leading comment trivia attached to this declaration
2240    /// (comments preceding the declaration's first token, since the
2241    /// previous declaration or file start). Empty by default.
2242    pub leading_trivia: Vec<crate::tokens::Trivia>,
2243    /// Fase 14.b — trailing comment trivia (same line as the
2244    /// declaration's last effective token). Empty by default.
2245    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2246}
2247
2248// ── Anchor ───────────────────────────────────────────────────────────────────
2249
2250#[derive(Debug)]
2251pub struct AnchorConstraint {
2252    pub name: String,
2253    pub require: String,
2254    pub reject: Vec<String>,
2255    pub enforce: String,
2256    pub description: String,
2257    pub confidence_floor: Option<f64>,
2258    pub unknown_response: String,
2259    pub on_violation: String,
2260    pub on_violation_target: String,
2261    pub loc: Loc,
2262    /// Fase 14.b — leading comment trivia attached to this declaration
2263    /// (comments preceding the declaration's first token, since the
2264    /// previous declaration or file start). Empty by default.
2265    pub leading_trivia: Vec<crate::tokens::Trivia>,
2266    /// Fase 14.b — trailing comment trivia (same line as the
2267    /// declaration's last effective token). Empty by default.
2268    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2269}
2270
2271// ── Memory ───────────────────────────────────────────────────────────────────
2272
2273#[derive(Debug)]
2274pub struct MemoryDefinition {
2275    pub name: String,
2276    pub store: String,
2277    pub backend: String,
2278    pub retrieval: String,
2279    pub decay: String,
2280    pub loc: Loc,
2281    /// Fase 14.b — leading comment trivia attached to this declaration
2282    /// (comments preceding the declaration's first token, since the
2283    /// previous declaration or file start). Empty by default.
2284    pub leading_trivia: Vec<crate::tokens::Trivia>,
2285    /// Fase 14.b — trailing comment trivia (same line as the
2286    /// declaration's last effective token). Empty by default.
2287    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2288}
2289
2290// ── Tool ─────────────────────────────────────────────────────────────────────
2291
2292#[derive(Debug)]
2293pub struct ToolDefinition {
2294    pub name: String,
2295    pub provider: String,
2296    pub max_results: Option<i64>,
2297    pub filter_expr: String,
2298    pub timeout: String,
2299    pub runtime: String,
2300    /// §Fase 114.c — the `resource` this tool's channel runs on (`tool T { resource: Api }`).
2301    ///
2302    /// When present, the tool DERIVES its endpoint from `resource.endpoint`, its
2303    /// concurrency bound from `resource.capacity`, and (via `lease`/`observe`) its
2304    /// lifecycle and health from the resource. `runtime:` then names the PATH
2305    /// within that channel, not the channel itself.
2306    ///
2307    /// This is what governs `tool.runtime` — the third island. An absolute
2308    /// `runtime: "https://…"` (a production URL in source, with no lifetime, no
2309    /// capacity, no shield) is now refused; the address lives on the resource.
2310    ///
2311    /// Empty ⇒ the legacy form (slug `runtime:` joined onto a per-tenant base URL,
2312    /// which already conforms). Ungoverned, and therefore ineligible for the
2313    /// channel `shield` / `lease` / `observe`.
2314    pub resource_ref: String,
2315    pub sandbox: Option<bool>,
2316    pub effects: Option<EffectRow>,
2317    /// §Fase 58.a — the tool's typed INPUT SCHEMA (W2: the caller↔tool
2318    /// contract). Each entry is a named, typed parameter that the canonical
2319    /// `use Tool(k = v, …)` invocation binds against and the type-checker
2320    /// validates the caller's args against (CT-2 caller blame, pre-HTTP).
2321    /// Empty for a schema-less tool — the legacy single-`on <arg>` form still
2322    /// applies (§58 D5 back-compat). Reuses `Parameter` (same `TypeExpr`
2323    /// grammar as flow params).
2324    pub parameters: Vec<Parameter>,
2325    /// §Fase 58.a — the tool's declared OUTPUT type, so a tool-step's result
2326    /// is referenceable as `${Step.output}` with a real type (§58 D8). Flat
2327    /// string (mirrors step `output:`); `None` when undeclared.
2328    pub output_type: Option<String>,
2329    /// §Fase 94.c — the per-tenant secret KEY injected into every dispatch
2330    /// of this tool (doctrine `rotation_without_revelation`): at `use`
2331    /// time the runtime resolves the key against the tenant's secret
2332    /// custody and injects the value into the tool-server request under
2333    /// the reserved `axon_secret` field — the flow never touches it. The
2334    /// §80.c posture extended to tools: this is a config KEY, never a
2335    /// credential literal (`axon-T902`, the T850 charset mirror). Empty =
2336    /// no injection (every pre-§94 tool). Meaningless on a
2337    /// `target:`-bound technician tool (execve dispatch, no HTTP request
2338    /// to inject into) — declaring both is `axon-T902`.
2339    pub secret: String,
2340    /// §Fase 95.a — the `secret_partition:` field (doctrine
2341    /// `selection_without_revelation`): the name of one of THIS tool's own
2342    /// `parameters:` whose runtime value is appended as a single key
2343    /// SEGMENT to `secret:` at dispatch, so one tool serves N sub-tenants
2344    /// multiplexed under one axon-tenant. With `secret: crm.hubspot` and
2345    /// `secret_partition: tenant_id`, a `use CrmCrearContacto(tenant_id =
2346    /// "acme", …)` resolves the custody key `crm.hubspot.acme`. The
2347    /// `secret:` class prefix is pinned at compile time (a literal); only
2348    /// this bounded segment is dynamic — the resolved key can NEVER leave
2349    /// the tool's declared class (the segment is charset-checked to a
2350    /// single dot-free run at dispatch, fail-closed). Empty = the §94
2351    /// static-key behaviour, unchanged. `axon-T903` governs its laws:
2352    /// requires a non-empty `secret:`, must name a `String` parameter of
2353    /// this tool, forbidden on a technician tool. The value SELECTED is
2354    /// still never revealed to cognition — `secret_partition` chooses
2355    /// WHICH borrowed authority to spend, never reads it.
2356    pub secret_partition: String,
2357    /// §Fase 84.b — Remote Hands. The `socket` this technician tool dispatches
2358    /// over: a program acting on a real machine dials `axon` as a `socket`
2359    /// client, and a `target:`-bound tool call sends its rendered argv down
2360    /// that connection. `None` ⇒ today's unchanged in-process / model-surface
2361    /// behaviour (zero regression; the whole §84 surface is inert unless
2362    /// `target:` is set). Resolved to a declared `socket` and duality-checked
2363    /// by `axon-T861`.
2364    pub target: Option<String>,
2365    /// §Fase 84.b — the operation's risk class, a v1-closed catalog of exactly
2366    /// `safe | destructive` (`technician::VALID_RISK_LEVELS`). `destructive`
2367    /// forces the bound session to carry a reachable `branch{approved/denied}`
2368    /// confirmation (`axon-T860`). `None` on a non-technician tool.
2369    pub risk: Option<String>,
2370    /// §Fase 84.b — the **argv template**: an ordered list of argv elements,
2371    /// each either a literal token (`"ping"`, `"-c"`) or a *whole-element*
2372    /// `${param}` placeholder (`"${host}"`). A placeholder binds to a declared
2373    /// `parameters:` entry and is substituted as ONE opaque argv argument at
2374    /// dispatch — never concatenated, never re-parsed by a shell (D84.1). This
2375    /// is the injection-safety keystone: the market's free `template:` STRING
2376    /// is deliberately NOT offered. Empty for a non-technician tool; required
2377    /// (`axon-T858`) when `target:` is set on a `provider: bash` tool.
2378    pub argv: Vec<String>,
2379    /// §Fase 85.b — the result-memoization policy for this tool. Names a
2380    /// declared `cache` (`axon-T864`), or the reserved sentinel `none` to opt
2381    /// OUT of an active `cache { default: true }` policy (the escape hatch for
2382    /// a rare mislabeled-`pure` tool). Empty ⇒ governed by the module default
2383    /// if one exists and this tool is eligible (`pure`, or covered by the
2384    /// default's `apply_to_effects`). Distinct from `memory` (D85.6).
2385    pub cache: String,
2386    /// §Fase 98.b — Native Web Acquisition. The closed-catalog scrape
2387    /// configuration for a tool whose `provider:` is one of the three
2388    /// web-acquisition engines (`scrape_http` | `scrape_dom` |
2389    /// `scrape_crawl`). `None` ⇒ this is not a scrape tool — the entire
2390    /// §98 surface is inert (zero regression). Present ⇒ the tool acquires
2391    /// content from the OPEN, ADVERSARIAL web: its output is born
2392    /// epistemically Untrusted (⊥, D98.1) and its `effects:` row MUST
2393    /// carry the first-class `web` base (`axon-T904`, effect honesty).
2394    /// The sub-block is a closed catalog — an unknown field is a hard
2395    /// parse error (the §83/§84 discipline, D98.2).
2396    pub scrape: Option<ScrapeSpec>,
2397    pub loc: Loc,
2398    /// Fase 14.b — leading comment trivia attached to this declaration
2399    /// (comments preceding the declaration's first token, since the
2400    /// previous declaration or file start). Empty by default.
2401    pub leading_trivia: Vec<crate::tokens::Trivia>,
2402    /// Fase 14.b — trailing comment trivia (same line as the
2403    /// declaration's last effective token). Empty by default.
2404    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2405}
2406
2407/// §Fase 98.b — the closed-catalog scrape configuration block
2408/// (`scrape: { … }`) on a web-acquisition `tool`. Every field is
2409/// optional/defaulted so a minimal `scrape: {}` is legal; the fields
2410/// that apply depend on the tool's `provider:` (the type-checker cross-
2411/// validates provider ↔ field applicability, `axon-T905`). The whole
2412/// struct is deliberately flat + serializable-friendly (`Option`/`Vec`/
2413/// scalar), mirroring the §84 technician-field discipline, so the IR
2414/// stays byte-stable and the runtime classifies identically.
2415#[derive(Debug, Default)]
2416pub struct ScrapeSpec {
2417    /// The acquisition engine: `impersonate` (HTTP-fingerprint stealth,
2418    /// the GA tier) | `browser` (headless-render sidecar, the gray tier).
2419    /// `None` ⇒ `impersonate` (D98.3). Closed catalog (`axon-T905`).
2420    /// Applies to `scrape_http` / `scrape_crawl`.
2421    pub engine: Option<String>,
2422    /// The browser-fingerprint impersonation PROFILE name
2423    /// (`chrome`, `firefox`, `safari` — closed catalog). Only meaningful
2424    /// with `engine: impersonate`. The concrete JA3/JA4 + HTTP/2 profile
2425    /// is resolved by the enterprise engine (§98.g); OSS records the
2426    /// declared intent. `None` ⇒ the engine's default profile.
2427    pub impersonate: Option<String>,
2428    /// The post-navigation settle wait for `engine: browser` (a Duration,
2429    /// e.g. `2s`) — how long to let JS render before snapshotting. Bounded
2430    /// (D98.11). Ignored by the impersonate engine (no JS runtime).
2431    pub render_wait: Option<String>,
2432    /// The per-tenant proxy-pool config KEY (a dotted key, resolved via
2433    /// the same SecretResolver `secret:`/`tool.base_url` use — D98.9),
2434    /// never a proxy URL literal. Empty ⇒ direct connection.
2435    pub proxy: String,
2436    /// Whether `robots.txt` is honored (default TRUE, D98.6). Setting
2437    /// `respect_robots: false` is the audited, `scrape.aggressive`-gated
2438    /// override (enforced enterprise-side, §98.h); in OSS it is recorded.
2439    pub respect_robots: Option<bool>,
2440    /// `scrape_dom` extraction spec: an ordered list of `name=selector`
2441    /// FieldSpecs (`["title=h1", "price=.amount"]`). A closed, bracketed
2442    /// string list (reuses the §83/§84 list helper). Each entry must be a
2443    /// single `name=selector` pair (`axon-T906`).
2444    pub extract: Vec<String>,
2445    /// `scrape_dom` adaptive relocation: when a declared selector misses,
2446    /// the engine attempts a HEURISTIC relocation above `similarity_floor`
2447    /// (D98.4 — a heuristic, NOT a proof). `None`/`false` ⇒ strict
2448    /// selectors only. Enabling it makes the tool carry `<storage>` (the
2449    /// per-tenant selector-memory, §98.h).
2450    pub adaptive: Option<bool>,
2451    /// The similarity threshold ∈ [0,1] governing adaptive relocation
2452    /// (`axon-T907`). Only meaningful with `adaptive: true`.
2453    pub similarity_floor: Option<f64>,
2454    /// `scrape_crawl` link-follow selector/pattern: which links to enqueue
2455    /// from each fetched page. Empty ⇒ no expansion (single-page crawl).
2456    pub follow: String,
2457    /// `scrape_crawl` maximum link depth from the seed (bounded, D98.11).
2458    pub max_depth: Option<i64>,
2459    /// `scrape_crawl` maximum total pages fetched (bounded, D98.11). A
2460    /// hostile/infinite site can never exhaust the crawler (`axon-T908`).
2461    pub max_pages: Option<i64>,
2462    /// `scrape_crawl` fetch concurrency (bounded, ≥ 1).
2463    pub concurrency: Option<i64>,
2464    /// `scrape_crawl` politeness/rate reference: a declared `budget`
2465    /// (`budget{rate:/max:}`, §72) governing per-host request pacing
2466    /// (D98 reuse of the budget kernel). Empty ⇒ engine default pacing.
2467    pub politeness: String,
2468    /// `scrape_crawl` checkpoint store reference: a declared `axonstore`
2469    /// the crawler persists frontier/visited state into for resumable,
2470    /// at-least-once crawling. Empty ⇒ in-memory (non-resumable).
2471    pub checkpoint: String,
2472    pub loc: Loc,
2473}
2474
2475#[derive(Debug)]
2476pub struct EffectRow {
2477    pub effects: Vec<String>,
2478    pub epistemic_level: String,
2479    pub loc: Loc,
2480}
2481
2482// ── Type ─────────────────────────────────────────────────────────────────────
2483
2484#[derive(Debug)]
2485pub struct TypeDefinition {
2486    pub name: String,
2487    pub fields: Vec<TypeField>,
2488    pub range_constraint: Option<RangeConstraint>,
2489    pub where_clause: Option<WhereClause>,
2490    /// §ESK Fase 6.1 — κ regulatory class attached to a type.
2491    pub compliance: Vec<String>,
2492    pub loc: Loc,
2493    /// Fase 14.b — leading comment trivia attached to this declaration
2494    /// (comments preceding the declaration's first token, since the
2495    /// previous declaration or file start). Empty by default.
2496    pub leading_trivia: Vec<crate::tokens::Trivia>,
2497    /// Fase 14.b — trailing comment trivia (same line as the
2498    /// declaration's last effective token). Empty by default.
2499    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2500}
2501
2502#[derive(Debug, Clone)]
2503pub struct TypeExpr {
2504    pub name: String,
2505    pub generic_param: String,
2506    pub optional: bool,
2507    pub loc: Loc,
2508}
2509
2510#[derive(Debug)]
2511pub struct TypeField {
2512    pub name: String,
2513    pub type_expr: TypeExpr,
2514    pub loc: Loc,
2515}
2516
2517#[derive(Debug)]
2518pub struct RangeConstraint {
2519    pub min_value: f64,
2520    pub max_value: f64,
2521    pub loc: Loc,
2522}
2523
2524#[derive(Debug)]
2525pub struct WhereClause {
2526    pub expression: String,
2527    pub loc: Loc,
2528}
2529
2530// ── Flow ─────────────────────────────────────────────────────────────────────
2531
2532#[derive(Debug)]
2533pub struct FlowDefinition {
2534    pub name: String,
2535    pub parameters: Vec<Parameter>,
2536    pub return_type: Option<TypeExpr>,
2537    pub body: Vec<FlowStep>,
2538    pub loc: Loc,
2539    /// Fase 14.b — leading comment trivia attached to this declaration
2540    /// (comments preceding the declaration's first token, since the
2541    /// previous declaration or file start). Empty by default.
2542    pub leading_trivia: Vec<crate::tokens::Trivia>,
2543    /// Fase 14.b — trailing comment trivia (same line as the
2544    /// declaration's last effective token). Empty by default.
2545    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2546}
2547
2548#[derive(Debug)]
2549pub struct Parameter {
2550    pub name: String,
2551    pub type_expr: TypeExpr,
2552    pub loc: Loc,
2553}
2554
2555/// Statements that can appear inside a flow body.
2556#[derive(Debug)]
2557pub enum FlowStep {
2558    Step(StepNode),
2559    If(ConditionalNode),
2560    ForIn(ForInStatement),
2561    Let(LetStatement),
2562    Return(ReturnStatement),
2563    /// Fase 19.e — `break` keyword. Payload-free; carries only its
2564    /// source location for error reporting.
2565    Break(BreakStatement),
2566    /// Fase 19.e — `continue` keyword. Payload-free; same shape as
2567    /// `Break`.
2568    Continue(ContinueStatement),
2569    /// Lambda Data application in a flow step.
2570    LambdaDataApply(LambdaDataApplyNode),
2571    // ── Tier 2 flow steps ──
2572    Probe(ProbeStep),
2573    Reason(ReasonStep),
2574    Validate(ValidateStep),
2575    Refine(RefineStep),
2576    Weave(WeaveStep),
2577    UseTool(UseToolStep),
2578    Remember(RememberStep),
2579    Recall(RecallStep),
2580    Par(ParBlock),
2581    Hibernate(HibernateStep),
2582    Deliberate(DeliberateBlock),
2583    Consensus(ConsensusBlock),
2584    Forge(ForgeBlock),
2585    Focus(FocusStep),
2586    /// §Fase 109 — the proof-carrying derivative step.
2587    Grad(GradStep),
2588    Associate(AssociateStep),
2589    Aggregate(AggregateStep),
2590    ExploreStep(ExploreStepNode),
2591    Ingest(IngestStep),
2592    ShieldApply(ShieldApplyStep),
2593    Stream(StreamBlock),
2594    Navigate(NavigateStep),
2595    Drill(DrillStep),
2596    Trail(TrailStep),
2597    Corroborate(CorroborateStep),
2598    OtsApply(OtsApplyStep),
2599    MandateApply(MandateApplyStep),
2600    ComputeApply(ComputeApplyStep),
2601    Listen(ListenStep),
2602    DaemonStep(DaemonStepNode),
2603    /// §λ-L-E Fase 13 — π-calculus output prefix `c⟨v⟩.P` (Chan-Output / Chan-Mobility).
2604    Emit(EmitStatement),
2605    /// §Fase 92.b — `mint <Credential> as <binding>`: ephemeral-credential
2606    /// minting (attenuated, TTL-bounded; `authority_only_attenuates`).
2607    Mint(MintStep),
2608    /// §Fase 94.b — `rotate <SecretsStore> [where "…"] with <Tool> as
2609    /// <binding>`: mediated secret renewal (`rotation_without_revelation`).
2610    Rotate(RotateStep),
2611    /// §λ-L-E Fase 13 — capability extrusion (Publish-Ext, paper §4.3).
2612    Publish(PublishStatement),
2613    /// §λ-L-E Fase 13 — dual of publish (dynamic typed handle import).
2614    Discover(DiscoverStatement),
2615    Persist(PersistStep),
2616    Retrieve(RetrieveStep),
2617    Mutate(MutateStep),
2618    Purge(PurgeStep),
2619    Transact(TransactBlock),
2620    /// §Fase 88.a — `warden(<target>) within <Scope> { … }` adversarial
2621    /// security-analysis block. A flow-body block (like `quant`): a target
2622    /// reference + a mandatory `within <Scope>` authorization clause + a nested
2623    /// body (`find_exploits()` → `list[Vulnerability]`, `fortify`). NOT a
2624    /// top-level declaration.
2625    Warden(WardenBlock),
2626    /// §Fase 51.a — `quant { … }` cognitive block (Hilbert-space projection).
2627    /// Carries an optional attribute header + a real nested body of flow steps
2628    /// (so §51.b's Continuous Type Invariant can scan it). Lives inside a flow
2629    /// body like `par`; NOT a top-level declaration.
2630    Quant(QuantBlock),
2631    /// §Fase 51.d.2 — `yield <expr>` measurement point inside a `quant` block.
2632    /// Collapses the evolved amplitudes back to classical silicon; the effect
2633    /// operation whose resolution is a one-shot delimited continuation. Only
2634    /// well-formed inside a `quant` block (the checker rejects it elsewhere).
2635    Yield(YieldStatement),
2636    /// §Fase 52.c — `run <Flow>(args)` as a flow-step: invoke a declared flow
2637    /// from inside a body (notably a `daemon`'s `listen` handler — the Q3 ask).
2638    /// Reuses the top-level [`RunStatement`] shape (flow name + args + optional
2639    /// persona/context/anchors). Distinct from `Declaration::Run` only by
2640    /// position (a step inside a body vs. a program-root run).
2641    Run(RunStatement),
2642    /// Flow-level statements we recognize but parse structurally.
2643    GenericStep(GenericFlowStep),
2644}
2645
2646/// A flow step we recognize by keyword but parse only structurally.
2647#[derive(Debug)]
2648pub struct GenericFlowStep {
2649    pub keyword: String,
2650    pub loc: Loc,
2651}
2652
2653// ── Step ─────────────────────────────────────────────────────────────────────
2654
2655#[derive(Debug)]
2656pub struct StepNode {
2657    pub name: String,
2658    pub persona_ref: String,
2659    pub given: String,
2660    pub ask: String,
2661    pub output_type: String,
2662    pub confidence_floor: Option<f64>,
2663    pub navigate_ref: String,
2664    pub apply_ref: String,
2665    /// §Fase 68.b — the step's declared MODEL CAPABILITY requirement: the
2666    /// context window (in tokens) the cognitive act needs. The §68.c resolver
2667    /// maps it to the smallest concrete model that satisfies it (per the
2668    /// resolved backend's §68.a catalog); `None` → the backend default
2669    /// (back-compat). Declare the NEED, not the vendor SKU (D68.1).
2670    pub requires_context: Option<u32>,
2671    /// §Fase 91.a — the step's declared cognitive timezone: an IANA name
2672    /// (`"America/Bogota"`, `"UTC"`). When present, the runtime injects the
2673    /// run's captured instant — rendered in THIS zone — into the step's
2674    /// cognitive context (`time_is_an_explicit_input`, the §71 doctrine
2675    /// applied to cognition). Format-checked at compile time (`axon-T892`);
2676    /// full IANA membership is the runtime's job (chrono-tz, §91.b).
2677    /// Overrides a bound `context`'s `now:` for this step. `None` → no
2678    /// temporal injection (back-compat).
2679    pub now_tz: Option<String>,
2680    pub loc: Loc,
2681}
2682
2683// ── Intent ───────────────────────────────────────────────────────────────────
2684
2685#[derive(Debug)]
2686pub struct IntentNode {
2687    pub name: String,
2688    pub given: String,
2689    pub ask: String,
2690    pub output_type: Option<TypeExpr>,
2691    pub confidence_floor: Option<f64>,
2692    pub loc: Loc,
2693    /// Fase 14.b — leading comment trivia attached to this declaration
2694    /// (comments preceding the declaration's first token, since the
2695    /// previous declaration or file start). Empty by default.
2696    pub leading_trivia: Vec<crate::tokens::Trivia>,
2697    /// Fase 14.b — trailing comment trivia (same line as the
2698    /// declaration's last effective token). Empty by default.
2699    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2700}
2701
2702// ── Run ──────────────────────────────────────────────────────────────────────
2703
2704#[derive(Debug)]
2705pub struct RunStatement {
2706    pub flow_name: String,
2707    pub arguments: Vec<String>,
2708    pub persona: String,
2709    pub context: String,
2710    pub anchors: Vec<String>,
2711    pub on_failure: String,
2712    pub on_failure_params: Vec<(String, String)>,
2713    pub output_to: String,
2714    pub effort: String,
2715    pub loc: Loc,
2716    /// Fase 14.b — leading comment trivia attached to this declaration
2717    /// (comments preceding the declaration's first token, since the
2718    /// previous declaration or file start). Empty by default.
2719    pub leading_trivia: Vec<crate::tokens::Trivia>,
2720    /// Fase 14.b — trailing comment trivia (same line as the
2721    /// declaration's last effective token). Empty by default.
2722    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2723}
2724
2725// ── Epistemic ────────────────────────────────────────────────────────────────
2726
2727#[derive(Debug)]
2728pub struct EpistemicBlock {
2729    pub mode: String,
2730    pub body: Vec<Declaration>,
2731    pub loc: Loc,
2732    /// Fase 14.b — leading comment trivia attached to this declaration
2733    /// (comments preceding the declaration's first token, since the
2734    /// previous declaration or file start). Empty by default.
2735    pub leading_trivia: Vec<crate::tokens::Trivia>,
2736    /// Fase 14.b — trailing comment trivia (same line as the
2737    /// declaration's last effective token). Empty by default.
2738    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2739}
2740
2741// ── §Fase 70.a — the pure expression engine (`Expr`) ─────────────────────────
2742
2743/// A pure, total expression in AXON's closed-catalog expression sublanguage
2744/// (§Fase 70). Evaluates to a value with no side effects, no I/O, no recursion
2745/// and no unbounded loops — so it is decidable and const-foldable. Mounted as
2746/// the condition of an `if` (and, in later sub-fases, `let` values + `where:`
2747/// predicates). Field/index access and the builtin catalog land in §70.c/d.
2748#[derive(Debug, Clone)]
2749pub enum Expr {
2750    /// A typed literal (`42`, `3.14`, `true`, `"hello"`).
2751    Lit(ExprLit),
2752    /// A reference to a binding or dotted path (`x`, `User.tier`).
2753    Ref(String),
2754    /// A unary operation (`-x`, `not x`).
2755    Unary(UnOp, Box<Expr>),
2756    /// A binary operation (`a + b`, `a >= b`, `a and b`).
2757    Binary(BinOp, Box<Expr>, Box<Expr>),
2758    /// §Fase 70.c — a closed-catalog builtin call. `args[0]` is the receiver
2759    /// (the value before the `.`); any further entries are the call arguments.
2760    /// E.g. `recent.length` → `Call(Length, [Ref("recent")])`,
2761    /// `name.starts_with("Dr")` → `Call(StartsWith, [Ref("name"), Lit(Str)])`.
2762    Call(Builtin, Vec<Expr>),
2763    /// §Fase 70.d — field access on a non-reference base (`items[0].name`,
2764    /// `(expr).field`). A plain dotted path stays a `Ref` (`a.b.c`) for
2765    /// back-compat; this node is the structured form the JSONB SQL lowering
2766    /// (deferred §73) consumes. The `String` is the field name.
2767    Field(Box<Expr>, String),
2768    /// §Fase 70.d — index access `base[index]` (array element / string char).
2769    Index(Box<Expr>, Box<Expr>),
2770}
2771
2772/// The closed catalog of pure builtins (§Fase 70.c). All are total + pure.
2773/// Collection/string predicates only; the predicate-taking folds (`any`/`all`/
2774/// `none`) need lambdas and are deferred, as are `sum`/`min`/`max` and `in`/`??`.
2775#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2776pub enum Builtin {
2777    /// `.length` — collection element count, or character count of a string.
2778    Length,
2779    /// `.count` — alias of `length`.
2780    Count,
2781    /// `.is_empty` — `length == 0`.
2782    IsEmpty,
2783    /// `.is_null` — the value is absent / empty / `null`.
2784    IsNull,
2785    /// `.contains(x)` — array membership, or string substring.
2786    Contains,
2787    /// `.starts_with(s)` — string prefix test.
2788    StartsWith,
2789    /// `.ends_with(s)` — string suffix test.
2790    EndsWith,
2791    /// §Fase 73.c — `.as_int` — honest coercion of a `Json` value to an
2792    /// integer. Fail-closed: a value that is not a JSON integer resolves
2793    /// to `null`, never a panic (doctrine `open_data_is_total`).
2794    AsInt,
2795    /// §Fase 73.c — `.as_float` — honest coercion to a float (an integer
2796    /// widens; anything else → `null`).
2797    AsFloat,
2798    /// §Fase 73.c — `.as_string` — honest coercion to a string (only a
2799    /// JSON string succeeds; a number / bool / null → `null`).
2800    AsString,
2801    /// §Fase 73.c — `.as_bool` — honest coercion to a boolean (only a
2802    /// JSON bool succeeds; anything else → `null`).
2803    AsBool,
2804}
2805
2806impl Builtin {
2807    /// The number of arguments AFTER the receiver (`args[0]`).
2808    pub fn extra_arity(self) -> usize {
2809        match self {
2810            Builtin::Length
2811            | Builtin::Count
2812            | Builtin::IsEmpty
2813            | Builtin::IsNull
2814            | Builtin::AsInt
2815            | Builtin::AsFloat
2816            | Builtin::AsString
2817            | Builtin::AsBool => 0,
2818            Builtin::Contains | Builtin::StartsWith | Builtin::EndsWith => 1,
2819        }
2820    }
2821
2822    /// The surface name (after the `.`).
2823    pub fn surface(self) -> &'static str {
2824        match self {
2825            Builtin::Length => "length",
2826            Builtin::Count => "count",
2827            Builtin::IsEmpty => "is_empty",
2828            Builtin::IsNull => "is_null",
2829            Builtin::Contains => "contains",
2830            Builtin::StartsWith => "starts_with",
2831            Builtin::EndsWith => "ends_with",
2832            Builtin::AsInt => "as_int",
2833            Builtin::AsFloat => "as_float",
2834            Builtin::AsString => "as_string",
2835            Builtin::AsBool => "as_bool",
2836        }
2837    }
2838
2839    /// Resolve a name (after a `.`) to a builtin, or `None` if it is an ordinary
2840    /// field / path segment.
2841    pub fn from_name(name: &str) -> Option<Builtin> {
2842        Some(match name {
2843            "length" => Builtin::Length,
2844            "count" => Builtin::Count,
2845            "is_empty" => Builtin::IsEmpty,
2846            "is_null" => Builtin::IsNull,
2847            "contains" => Builtin::Contains,
2848            "starts_with" => Builtin::StartsWith,
2849            "ends_with" => Builtin::EndsWith,
2850            "as_int" => Builtin::AsInt,
2851            "as_float" => Builtin::AsFloat,
2852            "as_string" => Builtin::AsString,
2853            "as_bool" => Builtin::AsBool,
2854            _ => return None,
2855        })
2856    }
2857}
2858
2859/// A literal value inside an [`Expr`]. The lexical form is preserved enough to
2860/// round-trip; the runtime evaluator (§70.f) coerces across these per the
2861/// existing string-runtime discipline.
2862#[derive(Debug, Clone)]
2863pub enum ExprLit {
2864    Int(i64),
2865    Float(f64),
2866    Bool(bool),
2867    Str(String),
2868}
2869
2870/// Unary operators (closed catalog).
2871#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2872pub enum UnOp {
2873    /// Arithmetic negation `-`.
2874    Neg,
2875    /// Boolean negation `not`.
2876    Not,
2877}
2878
2879/// Binary operators (closed catalog). Precedence is encoded in the Pratt parser.
2880#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2881pub enum BinOp {
2882    Add,
2883    Sub,
2884    Mul,
2885    Div,
2886    Mod,
2887    Eq,
2888    Ne,
2889    Lt,
2890    Le,
2891    Gt,
2892    Ge,
2893    And,
2894    Or,
2895}
2896
2897// ── Control flow ─────────────────────────────────────────────────────────────
2898
2899#[derive(Debug)]
2900pub struct ConditionalNode {
2901    pub condition: String,
2902    pub comparison_op: String,
2903    pub comparison_value: String,
2904    pub then_body: Vec<FlowStep>,
2905    pub else_body: Vec<FlowStep>,
2906    pub conditions: Vec<(String, String, String)>,
2907    pub conjunctor: String,
2908    /// §Fase 70.a — the parsed expression form of the condition. `None` when
2909    /// the condition fits the legacy `(condition, op, value)` + `or` shape
2910    /// (then the legacy fields drive evaluation, byte-identical to pre-§70);
2911    /// `Some` only for the richer forms the legacy triple cannot express
2912    /// (`and`, `not`, arithmetic, parentheses, nesting), which the runtime
2913    /// evaluates via the pure expression evaluator. Zero IR drift for existing
2914    /// programs.
2915    pub cond: Option<Expr>,
2916    pub loc: Loc,
2917}
2918
2919#[derive(Debug)]
2920pub struct ForInStatement {
2921    pub variable: String,
2922    pub iterable: String,
2923    pub body: Vec<FlowStep>,
2924    pub loc: Loc,
2925}
2926
2927#[derive(Debug)]
2928pub struct LetStatement {
2929    pub identifier: String,
2930    pub value_expr: String,
2931    /// Fase 17.a — preserves the parser's tokenization intent so the
2932    /// runtime dispatcher can distinguish a quoted literal from a
2933    /// dotted-identifier reference. One of "literal", "reference",
2934    /// "expression". Defaults to "literal" so any pre-Fase-17 caller
2935    /// that constructs a LetStatement directly behaves as a literal.
2936    pub value_kind: String,
2937    /// §Fase 51.c.3 — optional type annotation `let x: <TypeExpr> = …`.
2938    /// `None` for the bare `let x = …` form (all pre-51.c.3 lets). Inside a
2939    /// `quant` block the Continuous Type Invariant inspects this to enforce the
2940    /// continuous-carrier discipline (`DensityMatrix[D]` D=2ⁿ; reject discrete
2941    /// conversational types). Carries the typed encoder-boundary contract.
2942    pub type_annotation: Option<TypeExpr>,
2943    /// §Fase 70.f — the parsed expression form of the value, present only when
2944    /// `value_kind == "expression"` (`let total = price * qty + tax`). The
2945    /// runtime evaluates it via the pure expression evaluator instead of the
2946    /// pre-§70 behaviour (which treated an expression as an opaque literal
2947    /// string). `None` for literal / reference / list values (byte-identical to
2948    /// pre-§70.f).
2949    pub value_ast: Option<Expr>,
2950    pub loc: Loc,
2951    /// Fase 14.b — leading comment trivia attached to this declaration
2952    /// (comments preceding the declaration's first token, since the
2953    /// previous declaration or file start). Empty by default.
2954    pub leading_trivia: Vec<crate::tokens::Trivia>,
2955    /// Fase 14.b — trailing comment trivia (same line as the
2956    /// declaration's last effective token). Empty by default.
2957    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2958}
2959
2960#[derive(Debug)]
2961pub struct ReturnStatement {
2962    pub value_expr: String,
2963    pub loc: Loc,
2964}
2965
2966/// §Fase 51.d.2 — `yield <expr>` measurement point inside a `quant` block.
2967#[derive(Debug)]
2968pub struct YieldStatement {
2969    /// The measured expression (the structural hypothesis / density-matrix
2970    /// surrogate collapsed out of the Hilbert-space scope).
2971    pub value_expr: String,
2972    /// Tokenization intent (`literal` / `reference` / `expression`), mirroring
2973    /// `LetStatement.value_kind` so the runtime resolves the yielded value.
2974    pub value_kind: String,
2975    pub loc: Loc,
2976}
2977
2978/// Fase 19.e — `break` keyword inside a for-in body. Carries no
2979/// payload; the runner translates it into a sentinel that
2980/// terminates the loop. Parser scope check (`loop_depth`)
2981/// guarantees this only appears inside a for-in body.
2982#[derive(Debug)]
2983pub struct BreakStatement {
2984    pub loc: Loc,
2985}
2986
2987/// Fase 19.e — `continue` keyword inside a for-in body. Same
2988/// shape as ``BreakStatement``; the runner uses a different
2989/// sentinel type to distinguish loop-exit from iteration-skip.
2990#[derive(Debug)]
2991pub struct ContinueStatement {
2992    pub loc: Loc,
2993}
2994
2995// ── Lambda Data (ΛD) — Epistemic State Vectors ─────────────────────────────
2996
2997/// Top-level ΛD definition: ψ = ⟨T, V, E⟩ where E = ⟨c, τ, ρ, δ⟩.
2998#[derive(Debug)]
2999pub struct LambdaDataDefinition {
3000    pub name: String,
3001    pub ontology: String,             // T ∈ O — ontological type
3002    pub certainty: f64,               // c ∈ [0,1] — epistemic certainty scalar
3003    pub temporal_frame_start: String, // τ_start
3004    pub temporal_frame_end: String,   // τ_end
3005    pub provenance: String,           // ρ ∈ EntityRef — causal origin
3006    pub derivation: String, // δ ∈ Δ — see derivation catalogue (raw, derived, inferred, aggregated, transformed)
3007    pub loc: Loc,
3008    /// Fase 14.b — leading comment trivia attached to this declaration
3009    /// (comments preceding the declaration's first token, since the
3010    /// previous declaration or file start). Empty by default.
3011    pub leading_trivia: Vec<crate::tokens::Trivia>,
3012    /// Fase 14.b — trailing comment trivia (same line as the
3013    /// declaration's last effective token). Empty by default.
3014    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3015}
3016
3017/// In-flow ΛD application: binds epistemic state vector to a data target.
3018#[derive(Debug)]
3019pub struct LambdaDataApplyNode {
3020    pub lambda_data_name: String, // reference to LambdaDataDefinition
3021    pub target: String,           // expression to bind
3022    pub output_type: String,      // result type after epistemic binding
3023    pub loc: Loc,
3024}
3025
3026// ── Tier 2 flow step nodes ──────────────────────────────────────────────────
3027
3028#[derive(Debug)]
3029pub struct ProbeStep {
3030    pub target: String,
3031    pub loc: Loc,
3032}
3033#[derive(Debug)]
3034pub struct ReasonStep {
3035    pub strategy: String,
3036    pub target: String,
3037    pub loc: Loc,
3038}
3039#[derive(Debug)]
3040pub struct ValidateStep {
3041    pub target: String,
3042    pub rule: String,
3043    pub loc: Loc,
3044}
3045#[derive(Debug)]
3046pub struct RefineStep {
3047    pub target: String,
3048    pub strategy: String,
3049    pub loc: Loc,
3050}
3051#[derive(Debug)]
3052pub struct WeaveStep {
3053    pub sources: Vec<String>,
3054    pub target: String,
3055    pub format_type: String,
3056    pub priority: Vec<String>,
3057    pub style: String,
3058    pub loc: Loc,
3059}
3060/// §Fase 58.b — the closed catalog of `use <Tool>` argument forms. The
3061/// invocation surfaces are mutually exclusive, so a sum type models them
3062/// exactly (no ambiguous dual-empty state). NOTE: `apply: Tool given: <struct>`
3063/// (the splat form) is NOT here — it rides `StepNode.apply_ref` and is
3064/// validated against the tool schema in §58.d, not parsed as a `use`.
3065#[derive(Debug, Clone, PartialEq)]
3066pub enum UseArgs {
3067    /// `use Tool on "${arg}"` / `use Tool on query` — the §54.b single
3068    /// positional argument. D5 back-compat: behaves byte-identically to the
3069    /// pre-58 `argument: String` (empty string when no `on` clause).
3070    LegacyPositional(String),
3071    /// `use Tool(query = "${q}", max_results = 5)` — D2 canonical multi-field
3072    /// keyword args. Each entry is `(name, value, value_kind)`: `value` is the
3073    /// expression STRING (the frontend has no structured `Expr`; mirrors
3074    /// `argument` / `parse_argument_list`); `value_kind` is `"literal"` or
3075    /// `"reference"` — the §Fase 60 classification from `parse_let_atom`, so the
3076    /// runtime resolves a bare identifier / `Step.output` as a binding lookup
3077    /// (like `let`) instead of passing the name literally. The type-checker
3078    /// (§58.d + §60.c) validates each entry against the tool's declared input
3079    /// schema (W2 / CT-2 caller blame) and references against their source.
3080    Named(Vec<(String, String, String)>),
3081}
3082
3083impl UseArgs {
3084    /// §58.b transitional — the legacy single-arg string for the IR `argument`
3085    /// field (still `String` until §58.c carries structured named args).
3086    /// `Named` projects an empty argument here; the type-checker validates
3087    /// named args from the AST, and §58.c/e wire their structured dispatch.
3088    pub fn legacy_argument(&self) -> String {
3089        match self {
3090            UseArgs::LegacyPositional(s) => s.clone(),
3091            UseArgs::Named(_) => String::new(),
3092        }
3093    }
3094}
3095
3096#[derive(Debug)]
3097pub struct UseToolStep {
3098    pub tool_name: String,
3099    pub args: UseArgs,
3100    pub loc: Loc,
3101}
3102#[derive(Debug)]
3103pub struct RememberStep {
3104    pub expression: String,
3105    pub memory_target: String,
3106    pub loc: Loc,
3107}
3108#[derive(Debug)]
3109pub struct RecallStep {
3110    pub query: String,
3111    pub memory_source: String,
3112    pub loc: Loc,
3113}
3114#[derive(Debug)]
3115pub struct ParBlock {
3116    /// §Fase 65 — the concurrent branches. Each top-level statement inside
3117    /// `par { … }` is one branch (a single-statement body); they execute
3118    /// concurrently at runtime. Empty for a `par {}` with no statements
3119    /// (degenerate no-op). Before §65 this was payload-free (the branches were
3120    /// skipped at parse time), so `par` ran as a stub.
3121    pub branches: Vec<Vec<FlowStep>>,
3122    pub loc: Loc,
3123}
3124#[derive(Debug)]
3125pub struct HibernateStep {
3126    pub event_name: String,
3127    pub timeout: String,
3128    pub loc: Loc,
3129}
3130#[derive(Debug)]
3131pub struct DeliberateBlock {
3132    pub loc: Loc,
3133}
3134#[derive(Debug)]
3135pub struct ConsensusBlock {
3136    pub loc: Loc,
3137}
3138/// §Fase 86 — `forge <Name>(seed: <string>) -> <Type> { mode:, novelty:,
3139/// depth:, branches:, constraints: }` — Directed Creative Synthesis. A
3140/// flow-body block that runs the Poincaré-Hadamard four-phase creative process
3141/// (Preparation → Incubation → Illumination → Verification) under a **measured,
3142/// fail-closed novelty guarantee** (D86.4/D86.6): the returned typed value must
3143/// clear a Normalized-Compression-Distance novelty floor against the obvious
3144/// baseline AND its `constraints:` anchor, or the forge fails structurally.
3145///
3146/// Before §86 this was a no-op stub (`{ loc }` only, body discarded). §86 makes
3147/// the README's long-standing claim true.
3148#[derive(Debug, Default)]
3149pub struct ForgeBlock {
3150    /// The synthesis name (`forge Artwork(...)` → `"Artwork"`).
3151    pub name: String,
3152    /// The creative seed — the conceptual starting point (`seed: "..."`).
3153    pub seed: String,
3154    /// The declared output type (`-> Visual` → `"Visual"`).
3155    pub output_type: String,
3156    /// Boden creativity mode (closed catalog: `combinatorial | exploratory |
3157    /// transformational`, `axon-T868`). Empty ⇒ defaults to `exploratory`.
3158    pub mode: String,
3159    /// Novelty target `[0.0, 1.0]` (`axon-T869`) — sets the fail-closed novelty
3160    /// floor and blends the incubation temperature. Default 0.5.
3161    pub novelty: f64,
3162    /// Incubation iterations (`depth ≥ 1`, `axon-T870`). Default 1.
3163    pub depth: i64,
3164    /// Illumination parallel branches (best-of-N, `branches ≥ 1`, `axon-T870`).
3165    /// Default 1.
3166    pub branches: i64,
3167    /// Optional `constraints:` reference to a declared `anchor` (`axon-T871`) —
3168    /// the verification predicate + coherence floor. Empty ⇒ novelty-floor-only
3169    /// verification.
3170    pub constraints_ref: String,
3171    pub loc: Loc,
3172}
3173#[derive(Debug)]
3174pub struct FocusStep {
3175    /// §Fase 108.d — the declared dataspace this σ∘π reads (the field
3176    /// keeps its historical name; T930 requires it to resolve to a
3177    /// `dataspace` symbol).
3178    pub expression: String,
3179    /// §Fase 108.d — the data-plane `where:` clause (the §35 closed
3180    /// filter grammar, shared with retrieve/navigate — D108.9). Empty
3181    /// ⇒ no filter. Validated fail-closed at dispatch, like retrieve.
3182    pub where_expr: String,
3183    /// §Fase 108.d — π: the projected columns (empty ⇒ all).
3184    pub select: Vec<String>,
3185    /// §Fase 108.d — the binding name for the result (`as:`). Empty ⇒
3186    /// the dataspace name.
3187    pub output: String,
3188    pub loc: Loc,
3189}
3190/// §Fase 109.a — `grad <letName> wrt <x> [as <name>]`: differentiate the
3191/// EXPRESSION a prior rich `let` bound (its AST rides the IR), at compile
3192/// time, symbolically. The derivative is checked (T931/T932), simplified,
3193/// and stored in the IR — a proof-carrying artifact, re-derived at deploy.
3194#[derive(Debug)]
3195pub struct GradStep {
3196    /// The prior rich `let` whose expression is differentiated.
3197    pub target: String,
3198    /// The variables to differentiate against (`wrt x` / `wrt [x, y]`).
3199    pub wrt: Vec<String>,
3200    /// Result binding (`as:`). Empty ⇒ `d_<target>`.
3201    pub output: String,
3202    pub loc: Loc,
3203}
3204
3205#[derive(Debug)]
3206pub struct AssociateStep {
3207    pub left: String,
3208    pub right: String,
3209    pub using_field: String,
3210    /// §Fase 108.d — result binding name (`as:`). Empty ⇒ `<L>_<R>`.
3211    pub output: String,
3212    pub loc: Loc,
3213}
3214#[derive(Debug)]
3215pub struct AggregateStep {
3216    pub target: String,
3217    pub group_by: Vec<String>,
3218    pub alias: String,
3219    /// §Fase 108.d — the closed aggregate catalog entries
3220    /// (`count` | `count(col)` | `sum(col)` | `avg(col)` | `min(col)` |
3221    /// `max(col)`), kept RAW here; T930 validates shape + columns.
3222    pub compute: Vec<String>,
3223    /// §Fase 108.d — data-plane `where:` (D108.9). Empty ⇒ no filter.
3224    pub where_expr: String,
3225    pub loc: Loc,
3226}
3227#[derive(Debug)]
3228pub struct ExploreStepNode {
3229    pub target: String,
3230    pub limit: Option<i64>,
3231    /// §Fase 108.d — result binding name (`as:`). Empty ⇒ the target.
3232    pub output: String,
3233    pub loc: Loc,
3234}
3235#[derive(Debug)]
3236pub struct IngestStep {
3237    pub source: String,
3238    pub target: String,
3239    /// §Fase 108.c — the declared wire format of the source bytes
3240    /// (closed catalog: `csv` | `json`). Kept RAW at parse; the §108.c
3241    /// type-checker requires it and validates it (`axon-T929`) — an
3242    /// ingest that does not declare what it is parsing is refused.
3243    pub format: String,
3244    /// §Fase 108.c — bounds enforced on the RAW byte stream BEFORE any
3245    /// parsing (the §100 discipline). `None` ⇒ the engine's conservative
3246    /// defaults apply (bounded by default, never unbounded).
3247    pub max_bytes: Option<u64>,
3248    pub max_rows: Option<u64>,
3249    pub loc: Loc,
3250}
3251#[derive(Debug)]
3252pub struct ShieldApplyStep {
3253    pub shield_name: String,
3254    pub target: String,
3255    pub output_type: String,
3256    pub loc: Loc,
3257}
3258/// §Fase 34 / **§Fase 111.e** — `stream { <steps> }`.
3259///
3260/// The body used to NOT EXIST. `parse_block_step` — shared with `deliberate`,
3261/// `consensus` and (pre-retraction) `transact` — called `skip_braced_block()`
3262/// and threw the block's contents away at PARSE time. The handler was not a
3263/// no-op because someone forgot to implement it; it was a no-op because the
3264/// body never reached the AST for anything to execute. Four advertised
3265/// primitives died in that one function.
3266#[derive(Debug)]
3267pub struct StreamBlock {
3268    /// The steps inside the block. Executed in order; each one's fragments are
3269    /// emitted on the flow's event channel as they are produced.
3270    pub body: Vec<FlowStep>,
3271    pub loc: Loc,
3272}
3273#[derive(Debug)]
3274pub struct NavigateStep {
3275    pub pix_name: String,
3276    pub corpus_name: String,
3277    pub query_expr: String,
3278    pub trail_enabled: bool,
3279    pub output_name: String,
3280    /// §Fase 63.B — for MDN corpus-graph navigation: the seed document `from:`
3281    /// to start the ε-informative traversal. Empty for PIX tree navigation.
3282    pub seed: String,
3283    /// §Fase 63.B — for MDN: the `budget:` (max documents). `None` = default.
3284    pub budget: Option<i64>,
3285    /// §Fase 66 (Q2) — optional column-scope filter (`where:`) for a
3286    /// `corpus from axonstore`. A raw filter expr (same shape as `retrieve …
3287    /// where`) pushed to the SELECT sourcing the corpus rows, so an adopter
3288    /// multiplexing sub-tenants in one axon-tenant via a column can scope the
3289    /// MDN graph to a single sub-tenant. Empty = no column filter (RLS-only).
3290    pub where_expr: String,
3291    pub loc: Loc,
3292}
3293#[derive(Debug)]
3294pub struct DrillStep {
3295    pub pix_name: String,
3296    pub subtree_path: String,
3297    pub query_expr: String,
3298    pub output_name: String,
3299    pub loc: Loc,
3300}
3301#[derive(Debug)]
3302pub struct TrailStep {
3303    pub navigate_ref: String,
3304    pub loc: Loc,
3305}
3306#[derive(Debug)]
3307pub struct CorroborateStep {
3308    pub navigate_ref: String,
3309    pub output_name: String,
3310    pub loc: Loc,
3311}
3312#[derive(Debug)]
3313pub struct OtsApplyStep {
3314    pub ots_name: String,
3315    pub target: String,
3316    pub output_type: String,
3317    pub loc: Loc,
3318}
3319#[derive(Debug)]
3320pub struct MandateApplyStep {
3321    pub mandate_name: String,
3322    pub target: String,
3323    pub output_type: String,
3324    pub loc: Loc,
3325}
3326#[derive(Debug)]
3327pub struct ComputeApplyStep {
3328    pub compute_name: String,
3329    pub arguments: Vec<String>,
3330    pub output_name: String,
3331    pub loc: Loc,
3332}
3333/// §λ-L-E Fase 13 D4 — dual-mode listen.
3334///
3335/// `channel_is_ref = true` ⇒ `channel` is the name of a declared
3336/// `ChannelDefinition` (canonical Fase 13 form).  `false` ⇒ legacy
3337/// string topic (deprecated; type checker emits a warning).
3338#[derive(Debug)]
3339pub struct ListenStep {
3340    pub channel: String,
3341    pub channel_is_ref: bool,
3342    pub event_alias: String,
3343    /// §Fase 52.a — the handler body: real flow-steps executed on each event /
3344    /// scheduled tick. Pre-§52.a the `{ … }` block was `skip_braced_block`'d
3345    /// (the listener was inert); now it is parsed so a `daemon` can run logic
3346    /// (e.g. `run <Flow>(…)`) per trigger. Empty for a bodyless `listen`.
3347    pub body: Vec<FlowStep>,
3348    pub loc: Loc,
3349}
3350#[derive(Debug)]
3351pub struct DaemonStepNode {
3352    pub daemon_ref: String,
3353    pub loc: Loc,
3354}
3355#[derive(Debug)]
3356pub struct PersistStep {
3357    pub store_name: String,
3358    /// §Fase 35.o — the `{ col: value }` field block. Empty when the
3359    /// step is written without a block (`persist <store>`), in which
3360    /// case the runtime falls back to writing the flow's user
3361    /// bindings as a row (backward-compatible with v1.30.0).
3362    pub fields: Vec<(String, String)>,
3363    pub loc: Loc,
3364}
3365#[derive(Debug)]
3366pub struct RetrieveStep {
3367    pub store_name: String,
3368    pub where_expr: String,
3369    pub alias: String,
3370    /// §Fase 67.b — optional `order_by:` clause: a closed
3371    /// comma-separated list of `column [asc|desc]` (same identifier
3372    /// discipline as `where:` columns — no injection). Empty = no
3373    /// ordering. Raw string, parsed + validated by the runtime
3374    /// (`filter::render_bounds`) and at `axon check` (§38.d `axon-T807`).
3375    pub order_by: String,
3376    /// §Fase 67.b — optional `limit:` clause: a `u32` literal OR a
3377    /// `${binding}` resolved to a `u32` at runtime. Empty = no limit.
3378    /// Raw string (`"100"` or `"${max}"`), validated at `axon check`
3379    /// (§38.d `axon-T808`).
3380    pub limit_expr: String,
3381    /// §Fase 76.d — optional `aggregate:` clause: a member of the CLOSED
3382    /// catalog `count` | `sum(<col>)` | `avg(<col>)` | `min(<col>)` |
3383    /// `max(<col>)`. Empty = a plain `SELECT *` retrieve. Raw string,
3384    /// parsed + validated by the runtime (`filter::parse_aggregate_clause`)
3385    /// and at `axon check` (§76.d `axon-T843`/`T844`/`T845`).
3386    pub aggregate: String,
3387    /// §Fase 76.d — optional `group_by:` clause: a comma-separated list
3388    /// of column identifiers (same discipline as `order_by:` columns).
3389    /// Requires an `aggregate:`. Empty = no grouping.
3390    pub group_by: String,
3391    /// §Fase 85.b — optional `cache:` reference. A `retrieve` reads a store
3392    /// (a `storage` effect — never `pure`), so caching it is always a WIDENING
3393    /// that accepts staleness: the named `cache` MUST carry a finite `ttl:`
3394    /// (`axon-T865`) and typically an `invalidate_on:`. Names a declared
3395    /// `cache` (`axon-T864`); empty = uncached. Never governed by a
3396    /// `default: true` policy (defaults only auto-cover provably-`pure` tools).
3397    pub cache: String,
3398    pub loc: Loc,
3399}
3400#[derive(Debug)]
3401pub struct MutateStep {
3402    pub store_name: String,
3403    pub where_expr: String,
3404    /// §Fase 35.p — the `{ col: value }` SET assignments. Empty when
3405    /// the step declares no columns, in which case the runtime falls
3406    /// back to writing the flow's user bindings as the `SET` clause
3407    /// (backward-compatible with v1.31.0).
3408    pub fields: Vec<(String, String)>,
3409    pub loc: Loc,
3410}
3411#[derive(Debug)]
3412pub struct PurgeStep {
3413    pub store_name: String,
3414    pub where_expr: String,
3415    pub loc: Loc,
3416}
3417#[derive(Debug)]
3418pub struct TransactBlock {
3419    pub loc: Loc,
3420}
3421
3422/// §Fase 88.a — `scope <Name> { targets:, depth:, approver: }` — the
3423/// authorization scope a `warden` block runs `within`. The load-bearing safety
3424/// construct (paper §5.2): it declares which resources may be analysed
3425/// (`targets` allowlist), how invasively (`depth` ceiling), and who authorised
3426/// it (`approver` capability). A `warden` with no resolvable in-scope
3427/// authorization does not compile (fail-closed). Named + referenced, like
3428/// `cache`/`cors`. **Unknown fields are a hard parse error** (D83.7): a scope
3429/// governs an offensive-capable analysis, so a typo can never silently widen it.
3430#[derive(Debug, Default)]
3431pub struct ScopeDefinition {
3432    pub name: String,
3433    /// `targets: [ "<resource>", … ]` — the allowlist of resources the operator
3434    /// owns/controls and authorises for analysis. Required + non-empty (§88.c
3435    /// `axon-T88x`); a target outside this list is a typed rejection.
3436    pub targets: Vec<String>,
3437    /// `depth: static_artifact | memory_dump | live_network` — the MOST invasive
3438    /// analysis depth this scope permits (the ceiling). Closed catalog, ordered
3439    /// least→most invasive; empty ⇒ the safest default `static_artifact` (§88.c).
3440    pub depth: String,
3441    /// `approver: [requires] "<capability>"` — the capability whose holder
3442    /// authorised this scope (segregation of duties, the `mandate` §21 model).
3443    /// Required (§88.c).
3444    pub approver: String,
3445    pub loc: Loc,
3446    /// Fase 14.b — leading comment trivia.
3447    pub leading_trivia: Vec<crate::tokens::Trivia>,
3448    /// Fase 14.b — trailing comment trivia.
3449    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3450}
3451
3452/// §Fase 88.a — the `warden(<target>) within <Scope> { … }` adversarial
3453/// security-analysis block. A flow-body block (like `quant`): it audits a
3454/// `target` under a paraconsistent adversarial framing, emitting attested
3455/// `Vulnerability` findings — but ONLY `within` a signed authorization `scope`.
3456/// §88.a ships the SURFACE only; scope resolution + the depth/witness discipline
3457/// is §88.c, and the real analysis engine is §88.d/f (enterprise).
3458#[derive(Debug, Default)]
3459pub struct WardenBlock {
3460    /// `warden(<target>)` — a reference to the resource under analysis (a
3461    /// let-bound value / declared target). §88.c checks it is within the scope's
3462    /// `targets` allowlist.
3463    pub target: String,
3464    /// `within <Scope>` — the MANDATORY authorization scope reference. Empty is a
3465    /// hard error (§88.c `axon-T88x`, fail-closed): no scope ⇒ no analysis.
3466    pub scope_ref: String,
3467    /// The nested flow-body statements (`find_exploits()`, `fortify`, `emit`),
3468    /// parsed like `par`/`quant` branches so §88.c can walk them.
3469    pub body: Vec<FlowStep>,
3470    pub loc: Loc,
3471}
3472
3473/// §Fase 51.a — the `quant` cognitive primitive block surface
3474/// (`docs/papers/paper_primitiva_quant.md`; enterprise §Fase 51).
3475///
3476/// `quant` projects an MEK semantic tensor into a complex Hilbert space,
3477/// evolves it under a variational / kernel-feature map, and collapses back to
3478/// classical silicon. The attribute header is OPTIONAL — the bare `quant { … }`
3479/// form (the paper's example) leaves every attribute defaulted. The richer form
3480/// `quant(encoding: amplitude, observable: M, qubits: 10, depth: 4,
3481/// bandwidth: 0.5, backend: quant_sim) { … }` pins the encoding scheme (D2),
3482/// the Pauli-sum observable (D5), the register width / circuit depth, the
3483/// projected-kernel bandwidth γ (D7), and the algebraic-effect backend (D1/D9).
3484///
3485/// §51.a ships the SURFACE only. The Continuous Type Invariant over `body`
3486/// (§51.b), the typed continuous grammar incl. typed `let` + `Observable`
3487/// (§51.c), and the `quant_sim`/`qpu_native` effect injection + `yield`
3488/// measurement point (§51.d) land in subsequent sub-fases.
3489#[derive(Debug, Default)]
3490pub struct QuantBlock {
3491    /// `encoding:` — `amplitude` (default) or `angle` (shallow). `None` = the
3492    /// compiler default (amplitude). Carried as the surface spelling; §51.c
3493    /// validates against the closed scheme set.
3494    pub encoding: Option<String>,
3495    /// `observable:` — the name of a declared `Observable` (Pauli-sum, D5).
3496    /// `None` if unspecified (§51.c resolves + Hermiticity-checks it).
3497    pub observable: Option<String>,
3498    /// `qubits:` — the register width n (D = 2ⁿ). `None` = inferred from the
3499    /// encoded tensor dimensionality. The OSS reference backend caps n ≤ 10
3500    /// (D1); that bound is enforced at §51.e, not here.
3501    pub qubits: Option<i64>,
3502    /// `depth:` — the variational circuit depth L. `None` = backend default.
3503    pub depth: Option<i64>,
3504    /// `bandwidth:` — the projected-quantum-kernel bandwidth γ (D7). `None` =
3505    /// backend default.
3506    pub bandwidth: Option<f64>,
3507    /// §Fase 69.c — `reupload:` L, the number of DATA RE-UPLOADING layers. `None`
3508    /// or `1` = no re-uploading (the data enters once → a quadratic form, provably
3509    /// classical for amplitude+Pauli, §69.b). `L ≥ 2` interleaves the data
3510    /// encoding with entangling layers L times — the ONLY provable escape from the
3511    /// quadratic bound (Havlíček-style; canonical with `encoding: angle`). The
3512    /// resulting kernel must still pass an Advantage Witness to be deployed
3513    /// claiming advantage (§69.a/b).
3514    pub reupload: Option<i64>,
3515    /// The algebraic-effect backend tag: `quant_sim` (default) or `qpu_native`
3516    /// (D1/D9). Stored as the bare backend name; §51.d injects the full
3517    /// `ots:backend:<tag>` effect into the enclosing flow's effect row.
3518    pub effect: String,
3519    /// The nested flow-body statements (parsed like `par` branches, so §51.b
3520    /// can apply the Continuous Type Invariant to real AST). Empty for an
3521    /// empty `quant {}`.
3522    pub body: Vec<FlowStep>,
3523    pub loc: Loc,
3524}
3525
3526/// §Fase 51.c.2 — one term `cₖ · Pₖ` of a Pauli-sum observable.
3527///
3528/// `coefficient` is a real scalar (parsed as `f64`); `pauli` is a Pauli string
3529/// over the closed alphabet `{I, X, Y, Z}` (one char per qubit), e.g. `"ZZ"` or
3530/// `"XI"`. A real linear combination of Pauli strings is **Hermitian by
3531/// construction** (each Pauli string is Hermitian; real-weighted sums preserve
3532/// Hermiticity), which is why the observable needs no separate Hermiticity check.
3533#[derive(Debug, Default, Clone)]
3534pub struct PauliTerm {
3535    pub coefficient: f64,
3536    pub pauli: String,
3537    pub loc: Loc,
3538}
3539
3540/// §Fase 51.c.2 — the `observable <Name> { qubits, term: cₖ·Pₖ … }` declaration
3541/// (paper §3.2; plan D5). A typed Pauli-sum `M = Σ cₖ Pₖ` that a `quant` block
3542/// measures the evolved state against. The type-checker validates the closed
3543/// `{I,X,Y,Z}` alphabet + equal term lengths + non-empty sum; Hermiticity is
3544/// guaranteed by construction (real coefficients).
3545#[derive(Debug, Default)]
3546pub struct ObservableDefinition {
3547    pub name: String,
3548    /// `qubits: n` — the register width every Pauli string must span. `None`
3549    /// = inferred from the (equal) term lengths.
3550    pub qubits: Option<i64>,
3551    pub terms: Vec<PauliTerm>,
3552    pub loc: Loc,
3553    /// Fase 14.b — leading comment trivia.
3554    pub leading_trivia: Vec<crate::tokens::Trivia>,
3555    /// Fase 14.b — trailing comment trivia.
3556    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3557}
3558
3559/// §Fase 69.a — `witness <Name> { claim: <ref>  against: <baseline>
3560/// metric: <metric>  threshold: <ε>  data: <source> }`. The Advantage-Witness
3561/// proof obligation. The compiler proves it WELL-FORMED (§69.a, `axon-E0790`);
3562/// the advantage VALUE is computed on real `data` at deploy/runtime and carried
3563/// as a verdict (§69.b+). Fields are order-free `key: value` pairs.
3564#[derive(Debug)]
3565pub struct WitnessDefinition {
3566    pub name: String,
3567    /// The primitive instance whose advantage is claimed (e.g. an `observable` /
3568    /// `corpus` name, or a quant kernel reference).
3569    pub claim: String,
3570    /// The cheaper alternative the claim must beat (a closed-catalog baseline
3571    /// like `cosine` / `flat_retrieval` / `single_shot`, or a reference).
3572    pub baseline: String,
3573    /// How advantage is measured — a closed-catalog metric (`geometric_difference`,
3574    /// `kernel_target_alignment`, `ranking_lift`, `outcome_lift`).
3575    pub metric: String,
3576    /// The minimum advantage that justifies the cost (ε ≥ 0).
3577    pub threshold: f64,
3578    /// The real-data source the witness is evaluated on (a ref to an axonstore /
3579    /// corpus / labelled set). Required — advantage cannot be claimed in the abstract.
3580    pub data: String,
3581    pub loc: Loc,
3582    pub leading_trivia: Vec<crate::tokens::Trivia>,
3583    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3584}
3585
3586// ── §λ-L-E Fase 13 — Mobile Typed Channels ──────────────────────────────────
3587
3588/// `channel Name { message: T, qos: X, lifetime: ℓ, persistence: π, shield: σ }`.
3589///
3590/// First-class affine resource carrying a typed message.  Direct port
3591/// of `axon.compiler.ast_nodes.ChannelDefinition`.  `message` retains
3592/// the surface spelling (e.g. `"Order"` or `"Channel<Order>"`) so the
3593/// type checker can resolve nested mobility (paper §3.3).
3594#[derive(Debug)]
3595pub struct ChannelDefinition {
3596    pub name: String,
3597    pub message: String,     // type name OR "Channel<T>" for second-order
3598    pub qos: String,         // at_most_once | at_least_once | exactly_once | broadcast | queue
3599    pub lifetime: String,    // linear | affine | persistent (D1 default: affine)
3600    pub persistence: String, // ephemeral | persistent_axonstore
3601    pub shield_ref: String,  // optional σ-shield gate for publish (D8)
3602    pub loc: Loc,
3603    /// Fase 14.b — leading comment trivia attached to this declaration
3604    /// (comments preceding the declaration's first token, since the
3605    /// previous declaration or file start). Empty by default.
3606    pub leading_trivia: Vec<crate::tokens::Trivia>,
3607    /// Fase 14.b — trailing comment trivia (same line as the
3608    /// declaration's last effective token). Empty by default.
3609    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3610}
3611
3612/// `emit ChannelName(value_ref)` — π-calculus output prefix `c⟨v⟩.P`.
3613///
3614/// Direct port of `axon.compiler.ast_nodes.EmitStatement`.  Handles
3615/// both Chan-Output (scalar payload) and Chan-Mobility (channel-as-
3616/// value); the type checker dispatches based on whether `value_ref`
3617/// resolves to a `ChannelDefinition`.
3618#[derive(Debug)]
3619pub struct EmitStatement {
3620    pub channel_ref: String,
3621    pub value_ref: String,
3622    pub loc: Loc,
3623}
3624
3625/// §Fase 92.b — `mint <Credential> as <binding>`: the flow-step verb that
3626/// mints a declared ephemeral `credential` at runtime. The binding
3627/// receives the raw bearer string (shown once — the type checker forbids
3628/// it from flowing into a `persist` payload, `axon-T896`: credentials do
3629/// not enter stores). Undeclared credential reference = `axon-T895`.
3630#[derive(Debug)]
3631pub struct MintStep {
3632    pub credential_ref: String,
3633    pub binding: String,
3634    pub loc: Loc,
3635}
3636
3637/// §Fase 94.b — `rotate <SecretsStore> [where "<filter>"] with <Tool> as
3638/// <binding>`: the mediated secret-renewal flow verb (doctrine
3639/// `rotation_without_revelation`). Set-oriented like `mutate`: every
3640/// custody entry of the store's class matching the filter (whole class
3641/// when the filter is omitted — the post-breach bulk-rotation shape) is
3642/// renewed through ONE mediated exchange per key: the runtime reveals
3643/// the current value only into the tool call, the tool returns the new
3644/// value, the runtime commits it (CAS on version — concurrent rotators
3645/// cannot double-spend a refresh credential). The binding receives the
3646/// METADATA-ONLY summary `{attempted, rotated, failed}` — no term
3647/// evaluates to a secret value. `rotate` on a non-secrets store =
3648/// `axon-T898`; an undeclared tool = `axon-T899`.
3649#[derive(Debug)]
3650pub struct RotateStep {
3651    pub store_ref: String,
3652    /// The §67-grammar metadata filter (`expires_at < now() + interval
3653    /// '10 minutes'`, `key LIKE 'crm.%'`, …). Empty = the whole class.
3654    pub where_expr: String,
3655    pub tool_ref: String,
3656    pub binding: String,
3657    pub loc: Loc,
3658}
3659
3660/// `publish ChannelName within ShieldName` — capability extrusion.
3661///
3662/// Paper §4.3 (Publish-Ext) materialized as a flow step.  The `within
3663/// <Shield>` clause is mandatory (D8) — the parser rejects bare
3664/// `publish C`, the type checker rejects publishes whose shield does
3665/// not cover κ(message_type) (Fase 6.1 + paper §3.4).
3666#[derive(Debug)]
3667pub struct PublishStatement {
3668    pub channel_ref: String,
3669    pub shield_ref: String,
3670    pub loc: Loc,
3671}
3672
3673/// `discover ChannelName as alias` — dual of publish.
3674///
3675/// Imports a previously-published handle into a fresh affine local
3676/// binding.  The `as <alias>` is mandatory; the type checker rejects
3677/// discovery of channels that were never declared with `shield_ref`.
3678#[derive(Debug)]
3679pub struct DiscoverStatement {
3680    pub capability_ref: String,
3681    pub alias: String,
3682    pub loc: Loc,
3683}