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 116.a (D116.9) — the authorization scopes this tool's operation
2330    /// requires (`requires: ["w_organization_social"]`): flat capability atoms,
2331    /// the same vocabulary as `credential.grants` (§92) and endpoint
2332    /// `requires_capabilities` (§51.x). `axon-T956` enforces subset coverage —
2333    /// every `use` of a tool with a non-empty `requires` must occur where the
2334    /// program's granted set covers it. Empty = no scope demand (every
2335    /// pre-§116 tool, unchanged). Flat SETS by design: OAuth scopes are
2336    /// per-platform atoms with no hierarchy — a scope tree would model
2337    /// structure the domain does not have.
2338    pub requires: Vec<String>,
2339    /// §Fase 94.c — the per-tenant secret KEY injected into every dispatch
2340    /// of this tool (doctrine `rotation_without_revelation`): at `use`
2341    /// time the runtime resolves the key against the tenant's secret
2342    /// custody and injects the value into the tool-server request under
2343    /// the reserved `axon_secret` field — the flow never touches it. The
2344    /// §80.c posture extended to tools: this is a config KEY, never a
2345    /// credential literal (`axon-T902`, the T850 charset mirror). Empty =
2346    /// no injection (every pre-§94 tool). Meaningless on a
2347    /// `target:`-bound technician tool (execve dispatch, no HTTP request
2348    /// to inject into) — declaring both is `axon-T902`.
2349    pub secret: String,
2350    /// §Fase 95.a — the `secret_partition:` field (doctrine
2351    /// `selection_without_revelation`): the name of one of THIS tool's own
2352    /// `parameters:` whose runtime value is appended as a single key
2353    /// SEGMENT to `secret:` at dispatch, so one tool serves N sub-tenants
2354    /// multiplexed under one axon-tenant. With `secret: crm.hubspot` and
2355    /// `secret_partition: tenant_id`, a `use CrmCrearContacto(tenant_id =
2356    /// "acme", …)` resolves the custody key `crm.hubspot.acme`. The
2357    /// `secret:` class prefix is pinned at compile time (a literal); only
2358    /// this bounded segment is dynamic — the resolved key can NEVER leave
2359    /// the tool's declared class (the segment is charset-checked to a
2360    /// single dot-free run at dispatch, fail-closed). Empty = the §94
2361    /// static-key behaviour, unchanged. `axon-T903` governs its laws:
2362    /// requires a non-empty `secret:`, must name a `String` parameter of
2363    /// this tool, forbidden on a technician tool. The value SELECTED is
2364    /// still never revealed to cognition — `secret_partition` chooses
2365    /// WHICH borrowed authority to spend, never reads it.
2366    pub secret_partition: String,
2367    /// §Fase 84.b — Remote Hands. The `socket` this technician tool dispatches
2368    /// over: a program acting on a real machine dials `axon` as a `socket`
2369    /// client, and a `target:`-bound tool call sends its rendered argv down
2370    /// that connection. `None` ⇒ today's unchanged in-process / model-surface
2371    /// behaviour (zero regression; the whole §84 surface is inert unless
2372    /// `target:` is set). Resolved to a declared `socket` and duality-checked
2373    /// by `axon-T861`.
2374    pub target: Option<String>,
2375    /// §Fase 84.b — the operation's risk class, a v1-closed catalog of exactly
2376    /// `safe | destructive` (`technician::VALID_RISK_LEVELS`). `destructive`
2377    /// forces the bound session to carry a reachable `branch{approved/denied}`
2378    /// confirmation (`axon-T860`). `None` on a non-technician tool.
2379    pub risk: Option<String>,
2380    /// §Fase 84.b — the **argv template**: an ordered list of argv elements,
2381    /// each either a literal token (`"ping"`, `"-c"`) or a *whole-element*
2382    /// `${param}` placeholder (`"${host}"`). A placeholder binds to a declared
2383    /// `parameters:` entry and is substituted as ONE opaque argv argument at
2384    /// dispatch — never concatenated, never re-parsed by a shell (D84.1). This
2385    /// is the injection-safety keystone: the market's free `template:` STRING
2386    /// is deliberately NOT offered. Empty for a non-technician tool; required
2387    /// (`axon-T858`) when `target:` is set on a `provider: bash` tool.
2388    pub argv: Vec<String>,
2389    /// §Fase 85.b — the result-memoization policy for this tool. Names a
2390    /// declared `cache` (`axon-T864`), or the reserved sentinel `none` to opt
2391    /// OUT of an active `cache { default: true }` policy (the escape hatch for
2392    /// a rare mislabeled-`pure` tool). Empty ⇒ governed by the module default
2393    /// if one exists and this tool is eligible (`pure`, or covered by the
2394    /// default's `apply_to_effects`). Distinct from `memory` (D85.6).
2395    pub cache: String,
2396    /// §Fase 98.b — Native Web Acquisition. The closed-catalog scrape
2397    /// configuration for a tool whose `provider:` is one of the three
2398    /// web-acquisition engines (`scrape_http` | `scrape_dom` |
2399    /// `scrape_crawl`). `None` ⇒ this is not a scrape tool — the entire
2400    /// §98 surface is inert (zero regression). Present ⇒ the tool acquires
2401    /// content from the OPEN, ADVERSARIAL web: its output is born
2402    /// epistemically Untrusted (⊥, D98.1) and its `effects:` row MUST
2403    /// carry the first-class `web` base (`axon-T904`, effect honesty).
2404    /// The sub-block is a closed catalog — an unknown field is a hard
2405    /// parse error (the §83/§84 discipline, D98.2).
2406    pub scrape: Option<ScrapeSpec>,
2407    pub loc: Loc,
2408    /// Fase 14.b — leading comment trivia attached to this declaration
2409    /// (comments preceding the declaration's first token, since the
2410    /// previous declaration or file start). Empty by default.
2411    pub leading_trivia: Vec<crate::tokens::Trivia>,
2412    /// Fase 14.b — trailing comment trivia (same line as the
2413    /// declaration's last effective token). Empty by default.
2414    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2415}
2416
2417/// §Fase 98.b — the closed-catalog scrape configuration block
2418/// (`scrape: { … }`) on a web-acquisition `tool`. Every field is
2419/// optional/defaulted so a minimal `scrape: {}` is legal; the fields
2420/// that apply depend on the tool's `provider:` (the type-checker cross-
2421/// validates provider ↔ field applicability, `axon-T905`). The whole
2422/// struct is deliberately flat + serializable-friendly (`Option`/`Vec`/
2423/// scalar), mirroring the §84 technician-field discipline, so the IR
2424/// stays byte-stable and the runtime classifies identically.
2425#[derive(Debug, Default)]
2426pub struct ScrapeSpec {
2427    /// The acquisition engine: `impersonate` (HTTP-fingerprint stealth,
2428    /// the GA tier) | `browser` (headless-render sidecar, the gray tier).
2429    /// `None` ⇒ `impersonate` (D98.3). Closed catalog (`axon-T905`).
2430    /// Applies to `scrape_http` / `scrape_crawl`.
2431    pub engine: Option<String>,
2432    /// The browser-fingerprint impersonation PROFILE name
2433    /// (`chrome`, `firefox`, `safari` — closed catalog). Only meaningful
2434    /// with `engine: impersonate`. The concrete JA3/JA4 + HTTP/2 profile
2435    /// is resolved by the enterprise engine (§98.g); OSS records the
2436    /// declared intent. `None` ⇒ the engine's default profile.
2437    pub impersonate: Option<String>,
2438    /// The post-navigation settle wait for `engine: browser` (a Duration,
2439    /// e.g. `2s`) — how long to let JS render before snapshotting. Bounded
2440    /// (D98.11). Ignored by the impersonate engine (no JS runtime).
2441    pub render_wait: Option<String>,
2442    /// The per-tenant proxy-pool config KEY (a dotted key, resolved via
2443    /// the same SecretResolver `secret:`/`tool.base_url` use — D98.9),
2444    /// never a proxy URL literal. Empty ⇒ direct connection.
2445    pub proxy: String,
2446    /// Whether `robots.txt` is honored (default TRUE, D98.6). Setting
2447    /// `respect_robots: false` is the audited, `scrape.aggressive`-gated
2448    /// override (enforced enterprise-side, §98.h); in OSS it is recorded.
2449    pub respect_robots: Option<bool>,
2450    /// `scrape_dom` extraction spec: an ordered list of `name=selector`
2451    /// FieldSpecs (`["title=h1", "price=.amount"]`). A closed, bracketed
2452    /// string list (reuses the §83/§84 list helper). Each entry must be a
2453    /// single `name=selector` pair (`axon-T906`).
2454    pub extract: Vec<String>,
2455    /// `scrape_dom` adaptive relocation: when a declared selector misses,
2456    /// the engine attempts a HEURISTIC relocation above `similarity_floor`
2457    /// (D98.4 — a heuristic, NOT a proof). `None`/`false` ⇒ strict
2458    /// selectors only. Enabling it makes the tool carry `<storage>` (the
2459    /// per-tenant selector-memory, §98.h).
2460    pub adaptive: Option<bool>,
2461    /// The similarity threshold ∈ [0,1] governing adaptive relocation
2462    /// (`axon-T907`). Only meaningful with `adaptive: true`.
2463    pub similarity_floor: Option<f64>,
2464    /// `scrape_crawl` link-follow selector/pattern: which links to enqueue
2465    /// from each fetched page. Empty ⇒ no expansion (single-page crawl).
2466    pub follow: String,
2467    /// `scrape_crawl` maximum link depth from the seed (bounded, D98.11).
2468    pub max_depth: Option<i64>,
2469    /// `scrape_crawl` maximum total pages fetched (bounded, D98.11). A
2470    /// hostile/infinite site can never exhaust the crawler (`axon-T908`).
2471    pub max_pages: Option<i64>,
2472    /// `scrape_crawl` fetch concurrency (bounded, ≥ 1).
2473    pub concurrency: Option<i64>,
2474    /// `scrape_crawl` politeness/rate reference: a declared `budget`
2475    /// (`budget{rate:/max:}`, §72) governing per-host request pacing
2476    /// (D98 reuse of the budget kernel). Empty ⇒ engine default pacing.
2477    pub politeness: String,
2478    /// `scrape_crawl` checkpoint store reference: a declared `axonstore`
2479    /// the crawler persists frontier/visited state into for resumable,
2480    /// at-least-once crawling. Empty ⇒ in-memory (non-resumable).
2481    pub checkpoint: String,
2482    pub loc: Loc,
2483}
2484
2485#[derive(Debug)]
2486pub struct EffectRow {
2487    pub effects: Vec<String>,
2488    pub epistemic_level: String,
2489    pub loc: Loc,
2490}
2491
2492// ── Type ─────────────────────────────────────────────────────────────────────
2493
2494#[derive(Debug)]
2495pub struct TypeDefinition {
2496    pub name: String,
2497    pub fields: Vec<TypeField>,
2498    pub range_constraint: Option<RangeConstraint>,
2499    pub where_clause: Option<WhereClause>,
2500    /// §ESK Fase 6.1 — κ regulatory class attached to a type.
2501    pub compliance: Vec<String>,
2502    pub loc: Loc,
2503    /// Fase 14.b — leading comment trivia attached to this declaration
2504    /// (comments preceding the declaration's first token, since the
2505    /// previous declaration or file start). Empty by default.
2506    pub leading_trivia: Vec<crate::tokens::Trivia>,
2507    /// Fase 14.b — trailing comment trivia (same line as the
2508    /// declaration's last effective token). Empty by default.
2509    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2510}
2511
2512#[derive(Debug, Clone)]
2513pub struct TypeExpr {
2514    pub name: String,
2515    pub generic_param: String,
2516    pub optional: bool,
2517    pub loc: Loc,
2518}
2519
2520#[derive(Debug)]
2521pub struct TypeField {
2522    pub name: String,
2523    pub type_expr: TypeExpr,
2524    pub loc: Loc,
2525}
2526
2527#[derive(Debug)]
2528pub struct RangeConstraint {
2529    pub min_value: f64,
2530    pub max_value: f64,
2531    pub loc: Loc,
2532}
2533
2534#[derive(Debug)]
2535pub struct WhereClause {
2536    pub expression: String,
2537    pub loc: Loc,
2538}
2539
2540// ── Flow ─────────────────────────────────────────────────────────────────────
2541
2542#[derive(Debug)]
2543pub struct FlowDefinition {
2544    pub name: String,
2545    pub parameters: Vec<Parameter>,
2546    pub return_type: Option<TypeExpr>,
2547    pub body: Vec<FlowStep>,
2548    pub loc: Loc,
2549    /// Fase 14.b — leading comment trivia attached to this declaration
2550    /// (comments preceding the declaration's first token, since the
2551    /// previous declaration or file start). Empty by default.
2552    pub leading_trivia: Vec<crate::tokens::Trivia>,
2553    /// Fase 14.b — trailing comment trivia (same line as the
2554    /// declaration's last effective token). Empty by default.
2555    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2556}
2557
2558#[derive(Debug)]
2559pub struct Parameter {
2560    pub name: String,
2561    pub type_expr: TypeExpr,
2562    pub loc: Loc,
2563}
2564
2565/// Statements that can appear inside a flow body.
2566#[derive(Debug)]
2567pub enum FlowStep {
2568    Step(StepNode),
2569    If(ConditionalNode),
2570    ForIn(ForInStatement),
2571    Let(LetStatement),
2572    Return(ReturnStatement),
2573    /// Fase 19.e — `break` keyword. Payload-free; carries only its
2574    /// source location for error reporting.
2575    Break(BreakStatement),
2576    /// Fase 19.e — `continue` keyword. Payload-free; same shape as
2577    /// `Break`.
2578    Continue(ContinueStatement),
2579    /// Lambda Data application in a flow step.
2580    LambdaDataApply(LambdaDataApplyNode),
2581    // ── Tier 2 flow steps ──
2582    Probe(ProbeStep),
2583    Reason(ReasonStep),
2584    Validate(ValidateStep),
2585    Refine(RefineStep),
2586    Weave(WeaveStep),
2587    UseTool(UseToolStep),
2588    Remember(RememberStep),
2589    Recall(RecallStep),
2590    Par(ParBlock),
2591    Hibernate(HibernateStep),
2592    Deliberate(DeliberateBlock),
2593    Consensus(ConsensusBlock),
2594    Forge(ForgeBlock),
2595    Focus(FocusStep),
2596    /// §Fase 109 — the proof-carrying derivative step.
2597    Grad(GradStep),
2598    Associate(AssociateStep),
2599    Aggregate(AggregateStep),
2600    ExploreStep(ExploreStepNode),
2601    Ingest(IngestStep),
2602    ShieldApply(ShieldApplyStep),
2603    Stream(StreamBlock),
2604    Navigate(NavigateStep),
2605    Drill(DrillStep),
2606    Trail(TrailStep),
2607    Corroborate(CorroborateStep),
2608    OtsApply(OtsApplyStep),
2609    MandateApply(MandateApplyStep),
2610    ComputeApply(ComputeApplyStep),
2611    Listen(ListenStep),
2612    DaemonStep(DaemonStepNode),
2613    /// §λ-L-E Fase 13 — π-calculus output prefix `c⟨v⟩.P` (Chan-Output / Chan-Mobility).
2614    Emit(EmitStatement),
2615    /// §Fase 92.b — `mint <Credential> as <binding>`: ephemeral-credential
2616    /// minting (attenuated, TTL-bounded; `authority_only_attenuates`).
2617    Mint(MintStep),
2618    /// §Fase 94.b — `rotate <SecretsStore> [where "…"] with <Tool> as
2619    /// <binding>`: mediated secret renewal (`rotation_without_revelation`).
2620    Rotate(RotateStep),
2621    /// §λ-L-E Fase 13 — capability extrusion (Publish-Ext, paper §4.3).
2622    Publish(PublishStatement),
2623    /// §λ-L-E Fase 13 — dual of publish (dynamic typed handle import).
2624    Discover(DiscoverStatement),
2625    Persist(PersistStep),
2626    Retrieve(RetrieveStep),
2627    Mutate(MutateStep),
2628    Purge(PurgeStep),
2629    Transact(TransactBlock),
2630    /// §Fase 88.a — `warden(<target>) within <Scope> { … }` adversarial
2631    /// security-analysis block. A flow-body block (like `quant`): a target
2632    /// reference + a mandatory `within <Scope>` authorization clause + a nested
2633    /// body (`find_exploits()` → `list[Vulnerability]`, `fortify`). NOT a
2634    /// top-level declaration.
2635    Warden(WardenBlock),
2636    /// §Fase 51.a — `quant { … }` cognitive block (Hilbert-space projection).
2637    /// Carries an optional attribute header + a real nested body of flow steps
2638    /// (so §51.b's Continuous Type Invariant can scan it). Lives inside a flow
2639    /// body like `par`; NOT a top-level declaration.
2640    Quant(QuantBlock),
2641    /// §Fase 51.d.2 — `yield <expr>` measurement point inside a `quant` block.
2642    /// Collapses the evolved amplitudes back to classical silicon; the effect
2643    /// operation whose resolution is a one-shot delimited continuation. Only
2644    /// well-formed inside a `quant` block (the checker rejects it elsewhere).
2645    Yield(YieldStatement),
2646    /// §Fase 52.c — `run <Flow>(args)` as a flow-step: invoke a declared flow
2647    /// from inside a body (notably a `daemon`'s `listen` handler — the Q3 ask).
2648    /// Reuses the top-level [`RunStatement`] shape (flow name + args + optional
2649    /// persona/context/anchors). Distinct from `Declaration::Run` only by
2650    /// position (a step inside a body vs. a program-root run).
2651    Run(RunStatement),
2652    /// Flow-level statements we recognize but parse structurally.
2653    GenericStep(GenericFlowStep),
2654}
2655
2656/// A flow step we recognize by keyword but parse only structurally.
2657#[derive(Debug)]
2658pub struct GenericFlowStep {
2659    pub keyword: String,
2660    pub loc: Loc,
2661}
2662
2663// ── Step ─────────────────────────────────────────────────────────────────────
2664
2665#[derive(Debug)]
2666pub struct StepNode {
2667    pub name: String,
2668    pub persona_ref: String,
2669    pub given: String,
2670    pub ask: String,
2671    pub output_type: String,
2672    pub confidence_floor: Option<f64>,
2673    pub navigate_ref: String,
2674    pub apply_ref: String,
2675    /// §Fase 68.b — the step's declared MODEL CAPABILITY requirement: the
2676    /// context window (in tokens) the cognitive act needs. The §68.c resolver
2677    /// maps it to the smallest concrete model that satisfies it (per the
2678    /// resolved backend's §68.a catalog); `None` → the backend default
2679    /// (back-compat). Declare the NEED, not the vendor SKU (D68.1).
2680    pub requires_context: Option<u32>,
2681    /// §Fase 91.a — the step's declared cognitive timezone: an IANA name
2682    /// (`"America/Bogota"`, `"UTC"`). When present, the runtime injects the
2683    /// run's captured instant — rendered in THIS zone — into the step's
2684    /// cognitive context (`time_is_an_explicit_input`, the §71 doctrine
2685    /// applied to cognition). Format-checked at compile time (`axon-T892`);
2686    /// full IANA membership is the runtime's job (chrono-tz, §91.b).
2687    /// Overrides a bound `context`'s `now:` for this step. `None` → no
2688    /// temporal injection (back-compat).
2689    pub now_tz: Option<String>,
2690    pub loc: Loc,
2691}
2692
2693// ── Intent ───────────────────────────────────────────────────────────────────
2694
2695#[derive(Debug)]
2696pub struct IntentNode {
2697    pub name: String,
2698    pub given: String,
2699    pub ask: String,
2700    pub output_type: Option<TypeExpr>,
2701    pub confidence_floor: Option<f64>,
2702    pub loc: Loc,
2703    /// Fase 14.b — leading comment trivia attached to this declaration
2704    /// (comments preceding the declaration's first token, since the
2705    /// previous declaration or file start). Empty by default.
2706    pub leading_trivia: Vec<crate::tokens::Trivia>,
2707    /// Fase 14.b — trailing comment trivia (same line as the
2708    /// declaration's last effective token). Empty by default.
2709    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2710}
2711
2712// ── Run ──────────────────────────────────────────────────────────────────────
2713
2714#[derive(Debug)]
2715pub struct RunStatement {
2716    pub flow_name: String,
2717    pub arguments: Vec<String>,
2718    pub persona: String,
2719    pub context: String,
2720    pub anchors: Vec<String>,
2721    pub on_failure: String,
2722    pub on_failure_params: Vec<(String, String)>,
2723    pub output_to: String,
2724    pub effort: String,
2725    pub loc: Loc,
2726    /// Fase 14.b — leading comment trivia attached to this declaration
2727    /// (comments preceding the declaration's first token, since the
2728    /// previous declaration or file start). Empty by default.
2729    pub leading_trivia: Vec<crate::tokens::Trivia>,
2730    /// Fase 14.b — trailing comment trivia (same line as the
2731    /// declaration's last effective token). Empty by default.
2732    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2733}
2734
2735// ── Epistemic ────────────────────────────────────────────────────────────────
2736
2737#[derive(Debug)]
2738pub struct EpistemicBlock {
2739    pub mode: String,
2740    pub body: Vec<Declaration>,
2741    pub loc: Loc,
2742    /// Fase 14.b — leading comment trivia attached to this declaration
2743    /// (comments preceding the declaration's first token, since the
2744    /// previous declaration or file start). Empty by default.
2745    pub leading_trivia: Vec<crate::tokens::Trivia>,
2746    /// Fase 14.b — trailing comment trivia (same line as the
2747    /// declaration's last effective token). Empty by default.
2748    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2749}
2750
2751// ── §Fase 70.a — the pure expression engine (`Expr`) ─────────────────────────
2752
2753/// A pure, total expression in AXON's closed-catalog expression sublanguage
2754/// (§Fase 70). Evaluates to a value with no side effects, no I/O, no recursion
2755/// and no unbounded loops — so it is decidable and const-foldable. Mounted as
2756/// the condition of an `if` (and, in later sub-fases, `let` values + `where:`
2757/// predicates). Field/index access and the builtin catalog land in §70.c/d.
2758#[derive(Debug, Clone)]
2759pub enum Expr {
2760    /// A typed literal (`42`, `3.14`, `true`, `"hello"`).
2761    Lit(ExprLit),
2762    /// A reference to a binding or dotted path (`x`, `User.tier`).
2763    Ref(String),
2764    /// A unary operation (`-x`, `not x`).
2765    Unary(UnOp, Box<Expr>),
2766    /// A binary operation (`a + b`, `a >= b`, `a and b`).
2767    Binary(BinOp, Box<Expr>, Box<Expr>),
2768    /// §Fase 70.c — a closed-catalog builtin call. `args[0]` is the receiver
2769    /// (the value before the `.`); any further entries are the call arguments.
2770    /// E.g. `recent.length` → `Call(Length, [Ref("recent")])`,
2771    /// `name.starts_with("Dr")` → `Call(StartsWith, [Ref("name"), Lit(Str)])`.
2772    Call(Builtin, Vec<Expr>),
2773    /// §Fase 70.d — field access on a non-reference base (`items[0].name`,
2774    /// `(expr).field`). A plain dotted path stays a `Ref` (`a.b.c`) for
2775    /// back-compat; this node is the structured form the JSONB SQL lowering
2776    /// (deferred §73) consumes. The `String` is the field name.
2777    Field(Box<Expr>, String),
2778    /// §Fase 70.d — index access `base[index]` (array element / string char).
2779    Index(Box<Expr>, Box<Expr>),
2780}
2781
2782/// The closed catalog of pure builtins (§Fase 70.c). All are total + pure.
2783/// Collection/string predicates only; the predicate-taking folds (`any`/`all`/
2784/// `none`) need lambdas and are deferred, as are `sum`/`min`/`max` and `in`/`??`.
2785#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2786pub enum Builtin {
2787    /// `.length` — collection element count, or character count of a string.
2788    Length,
2789    /// `.count` — alias of `length`.
2790    Count,
2791    /// `.is_empty` — `length == 0`.
2792    IsEmpty,
2793    /// `.is_null` — the value is absent / empty / `null`.
2794    IsNull,
2795    /// `.contains(x)` — array membership, or string substring.
2796    Contains,
2797    /// `.starts_with(s)` — string prefix test.
2798    StartsWith,
2799    /// `.ends_with(s)` — string suffix test.
2800    EndsWith,
2801    /// §Fase 73.c — `.as_int` — honest coercion of a `Json` value to an
2802    /// integer. Fail-closed: a value that is not a JSON integer resolves
2803    /// to `null`, never a panic (doctrine `open_data_is_total`).
2804    AsInt,
2805    /// §Fase 73.c — `.as_float` — honest coercion to a float (an integer
2806    /// widens; anything else → `null`).
2807    AsFloat,
2808    /// §Fase 73.c — `.as_string` — honest coercion to a string (only a
2809    /// JSON string succeeds; a number / bool / null → `null`).
2810    AsString,
2811    /// §Fase 73.c — `.as_bool` — honest coercion to a boolean (only a
2812    /// JSON bool succeeds; anything else → `null`).
2813    AsBool,
2814}
2815
2816impl Builtin {
2817    /// The number of arguments AFTER the receiver (`args[0]`).
2818    pub fn extra_arity(self) -> usize {
2819        match self {
2820            Builtin::Length
2821            | Builtin::Count
2822            | Builtin::IsEmpty
2823            | Builtin::IsNull
2824            | Builtin::AsInt
2825            | Builtin::AsFloat
2826            | Builtin::AsString
2827            | Builtin::AsBool => 0,
2828            Builtin::Contains | Builtin::StartsWith | Builtin::EndsWith => 1,
2829        }
2830    }
2831
2832    /// The surface name (after the `.`).
2833    pub fn surface(self) -> &'static str {
2834        match self {
2835            Builtin::Length => "length",
2836            Builtin::Count => "count",
2837            Builtin::IsEmpty => "is_empty",
2838            Builtin::IsNull => "is_null",
2839            Builtin::Contains => "contains",
2840            Builtin::StartsWith => "starts_with",
2841            Builtin::EndsWith => "ends_with",
2842            Builtin::AsInt => "as_int",
2843            Builtin::AsFloat => "as_float",
2844            Builtin::AsString => "as_string",
2845            Builtin::AsBool => "as_bool",
2846        }
2847    }
2848
2849    /// Resolve a name (after a `.`) to a builtin, or `None` if it is an ordinary
2850    /// field / path segment.
2851    pub fn from_name(name: &str) -> Option<Builtin> {
2852        Some(match name {
2853            "length" => Builtin::Length,
2854            "count" => Builtin::Count,
2855            "is_empty" => Builtin::IsEmpty,
2856            "is_null" => Builtin::IsNull,
2857            "contains" => Builtin::Contains,
2858            "starts_with" => Builtin::StartsWith,
2859            "ends_with" => Builtin::EndsWith,
2860            "as_int" => Builtin::AsInt,
2861            "as_float" => Builtin::AsFloat,
2862            "as_string" => Builtin::AsString,
2863            "as_bool" => Builtin::AsBool,
2864            _ => return None,
2865        })
2866    }
2867}
2868
2869/// A literal value inside an [`Expr`]. The lexical form is preserved enough to
2870/// round-trip; the runtime evaluator (§70.f) coerces across these per the
2871/// existing string-runtime discipline.
2872#[derive(Debug, Clone)]
2873pub enum ExprLit {
2874    Int(i64),
2875    Float(f64),
2876    Bool(bool),
2877    Str(String),
2878}
2879
2880/// Unary operators (closed catalog).
2881#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2882pub enum UnOp {
2883    /// Arithmetic negation `-`.
2884    Neg,
2885    /// Boolean negation `not`.
2886    Not,
2887}
2888
2889/// Binary operators (closed catalog). Precedence is encoded in the Pratt parser.
2890#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2891pub enum BinOp {
2892    Add,
2893    Sub,
2894    Mul,
2895    Div,
2896    Mod,
2897    Eq,
2898    Ne,
2899    Lt,
2900    Le,
2901    Gt,
2902    Ge,
2903    And,
2904    Or,
2905}
2906
2907// ── Control flow ─────────────────────────────────────────────────────────────
2908
2909#[derive(Debug)]
2910pub struct ConditionalNode {
2911    pub condition: String,
2912    pub comparison_op: String,
2913    pub comparison_value: String,
2914    pub then_body: Vec<FlowStep>,
2915    pub else_body: Vec<FlowStep>,
2916    pub conditions: Vec<(String, String, String)>,
2917    pub conjunctor: String,
2918    /// §Fase 70.a — the parsed expression form of the condition. `None` when
2919    /// the condition fits the legacy `(condition, op, value)` + `or` shape
2920    /// (then the legacy fields drive evaluation, byte-identical to pre-§70);
2921    /// `Some` only for the richer forms the legacy triple cannot express
2922    /// (`and`, `not`, arithmetic, parentheses, nesting), which the runtime
2923    /// evaluates via the pure expression evaluator. Zero IR drift for existing
2924    /// programs.
2925    pub cond: Option<Expr>,
2926    pub loc: Loc,
2927}
2928
2929#[derive(Debug)]
2930pub struct ForInStatement {
2931    pub variable: String,
2932    pub iterable: String,
2933    pub body: Vec<FlowStep>,
2934    pub loc: Loc,
2935}
2936
2937#[derive(Debug)]
2938pub struct LetStatement {
2939    pub identifier: String,
2940    pub value_expr: String,
2941    /// Fase 17.a — preserves the parser's tokenization intent so the
2942    /// runtime dispatcher can distinguish a quoted literal from a
2943    /// dotted-identifier reference. One of "literal", "reference",
2944    /// "expression". Defaults to "literal" so any pre-Fase-17 caller
2945    /// that constructs a LetStatement directly behaves as a literal.
2946    pub value_kind: String,
2947    /// §Fase 51.c.3 — optional type annotation `let x: <TypeExpr> = …`.
2948    /// `None` for the bare `let x = …` form (all pre-51.c.3 lets). Inside a
2949    /// `quant` block the Continuous Type Invariant inspects this to enforce the
2950    /// continuous-carrier discipline (`DensityMatrix[D]` D=2ⁿ; reject discrete
2951    /// conversational types). Carries the typed encoder-boundary contract.
2952    pub type_annotation: Option<TypeExpr>,
2953    /// §Fase 70.f — the parsed expression form of the value, present only when
2954    /// `value_kind == "expression"` (`let total = price * qty + tax`). The
2955    /// runtime evaluates it via the pure expression evaluator instead of the
2956    /// pre-§70 behaviour (which treated an expression as an opaque literal
2957    /// string). `None` for literal / reference / list values (byte-identical to
2958    /// pre-§70.f).
2959    pub value_ast: Option<Expr>,
2960    pub loc: Loc,
2961    /// Fase 14.b — leading comment trivia attached to this declaration
2962    /// (comments preceding the declaration's first token, since the
2963    /// previous declaration or file start). Empty by default.
2964    pub leading_trivia: Vec<crate::tokens::Trivia>,
2965    /// Fase 14.b — trailing comment trivia (same line as the
2966    /// declaration's last effective token). Empty by default.
2967    pub trailing_trivia: Vec<crate::tokens::Trivia>,
2968}
2969
2970#[derive(Debug)]
2971pub struct ReturnStatement {
2972    pub value_expr: String,
2973    pub loc: Loc,
2974}
2975
2976/// §Fase 51.d.2 — `yield <expr>` measurement point inside a `quant` block.
2977#[derive(Debug)]
2978pub struct YieldStatement {
2979    /// The measured expression (the structural hypothesis / density-matrix
2980    /// surrogate collapsed out of the Hilbert-space scope).
2981    pub value_expr: String,
2982    /// Tokenization intent (`literal` / `reference` / `expression`), mirroring
2983    /// `LetStatement.value_kind` so the runtime resolves the yielded value.
2984    pub value_kind: String,
2985    pub loc: Loc,
2986}
2987
2988/// Fase 19.e — `break` keyword inside a for-in body. Carries no
2989/// payload; the runner translates it into a sentinel that
2990/// terminates the loop. Parser scope check (`loop_depth`)
2991/// guarantees this only appears inside a for-in body.
2992#[derive(Debug)]
2993pub struct BreakStatement {
2994    pub loc: Loc,
2995}
2996
2997/// Fase 19.e — `continue` keyword inside a for-in body. Same
2998/// shape as ``BreakStatement``; the runner uses a different
2999/// sentinel type to distinguish loop-exit from iteration-skip.
3000#[derive(Debug)]
3001pub struct ContinueStatement {
3002    pub loc: Loc,
3003}
3004
3005// ── Lambda Data (ΛD) — Epistemic State Vectors ─────────────────────────────
3006
3007/// Top-level ΛD definition: ψ = ⟨T, V, E⟩ where E = ⟨c, τ, ρ, δ⟩.
3008#[derive(Debug)]
3009pub struct LambdaDataDefinition {
3010    pub name: String,
3011    pub ontology: String,             // T ∈ O — ontological type
3012    pub certainty: f64,               // c ∈ [0,1] — epistemic certainty scalar
3013    pub temporal_frame_start: String, // τ_start
3014    pub temporal_frame_end: String,   // τ_end
3015    pub provenance: String,           // ρ ∈ EntityRef — causal origin
3016    pub derivation: String, // δ ∈ Δ — see derivation catalogue (raw, derived, inferred, aggregated, transformed)
3017    pub loc: Loc,
3018    /// Fase 14.b — leading comment trivia attached to this declaration
3019    /// (comments preceding the declaration's first token, since the
3020    /// previous declaration or file start). Empty by default.
3021    pub leading_trivia: Vec<crate::tokens::Trivia>,
3022    /// Fase 14.b — trailing comment trivia (same line as the
3023    /// declaration's last effective token). Empty by default.
3024    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3025}
3026
3027/// In-flow ΛD application: binds epistemic state vector to a data target.
3028#[derive(Debug)]
3029pub struct LambdaDataApplyNode {
3030    pub lambda_data_name: String, // reference to LambdaDataDefinition
3031    pub target: String,           // expression to bind
3032    pub output_type: String,      // result type after epistemic binding
3033    pub loc: Loc,
3034}
3035
3036// ── Tier 2 flow step nodes ──────────────────────────────────────────────────
3037
3038#[derive(Debug)]
3039pub struct ProbeStep {
3040    pub target: String,
3041    pub loc: Loc,
3042}
3043#[derive(Debug)]
3044pub struct ReasonStep {
3045    pub strategy: String,
3046    pub target: String,
3047    pub loc: Loc,
3048}
3049#[derive(Debug)]
3050pub struct ValidateStep {
3051    pub target: String,
3052    pub rule: String,
3053    pub loc: Loc,
3054}
3055#[derive(Debug)]
3056pub struct RefineStep {
3057    pub target: String,
3058    pub strategy: String,
3059    pub loc: Loc,
3060}
3061#[derive(Debug)]
3062pub struct WeaveStep {
3063    pub sources: Vec<String>,
3064    pub target: String,
3065    pub format_type: String,
3066    pub priority: Vec<String>,
3067    pub style: String,
3068    pub loc: Loc,
3069}
3070/// §Fase 58.b — the closed catalog of `use <Tool>` argument forms. The
3071/// invocation surfaces are mutually exclusive, so a sum type models them
3072/// exactly (no ambiguous dual-empty state). NOTE: `apply: Tool given: <struct>`
3073/// (the splat form) is NOT here — it rides `StepNode.apply_ref` and is
3074/// validated against the tool schema in §58.d, not parsed as a `use`.
3075#[derive(Debug, Clone, PartialEq)]
3076pub enum UseArgs {
3077    /// `use Tool on "${arg}"` / `use Tool on query` — the §54.b single
3078    /// positional argument. D5 back-compat: behaves byte-identically to the
3079    /// pre-58 `argument: String` (empty string when no `on` clause).
3080    LegacyPositional(String),
3081    /// `use Tool(query = "${q}", max_results = 5)` — D2 canonical multi-field
3082    /// keyword args. Each entry is `(name, value, value_kind)`: `value` is the
3083    /// expression STRING (the frontend has no structured `Expr`; mirrors
3084    /// `argument` / `parse_argument_list`); `value_kind` is `"literal"` or
3085    /// `"reference"` — the §Fase 60 classification from `parse_let_atom`, so the
3086    /// runtime resolves a bare identifier / `Step.output` as a binding lookup
3087    /// (like `let`) instead of passing the name literally. The type-checker
3088    /// (§58.d + §60.c) validates each entry against the tool's declared input
3089    /// schema (W2 / CT-2 caller blame) and references against their source.
3090    Named(Vec<(String, String, String)>),
3091}
3092
3093impl UseArgs {
3094    /// §58.b transitional — the legacy single-arg string for the IR `argument`
3095    /// field (still `String` until §58.c carries structured named args).
3096    /// `Named` projects an empty argument here; the type-checker validates
3097    /// named args from the AST, and §58.c/e wire their structured dispatch.
3098    pub fn legacy_argument(&self) -> String {
3099        match self {
3100            UseArgs::LegacyPositional(s) => s.clone(),
3101            UseArgs::Named(_) => String::new(),
3102        }
3103    }
3104}
3105
3106#[derive(Debug)]
3107pub struct UseToolStep {
3108    pub tool_name: String,
3109    pub args: UseArgs,
3110    pub loc: Loc,
3111}
3112#[derive(Debug)]
3113pub struct RememberStep {
3114    pub expression: String,
3115    pub memory_target: String,
3116    pub loc: Loc,
3117}
3118#[derive(Debug)]
3119pub struct RecallStep {
3120    pub query: String,
3121    pub memory_source: String,
3122    pub loc: Loc,
3123}
3124#[derive(Debug)]
3125pub struct ParBlock {
3126    /// §Fase 65 — the concurrent branches. Each top-level statement inside
3127    /// `par { … }` is one branch (a single-statement body); they execute
3128    /// concurrently at runtime. Empty for a `par {}` with no statements
3129    /// (degenerate no-op). Before §65 this was payload-free (the branches were
3130    /// skipped at parse time), so `par` ran as a stub.
3131    pub branches: Vec<Vec<FlowStep>>,
3132    pub loc: Loc,
3133}
3134#[derive(Debug)]
3135pub struct HibernateStep {
3136    pub event_name: String,
3137    pub timeout: String,
3138    pub loc: Loc,
3139}
3140#[derive(Debug)]
3141pub struct DeliberateBlock {
3142    pub loc: Loc,
3143}
3144#[derive(Debug)]
3145pub struct ConsensusBlock {
3146    pub loc: Loc,
3147}
3148/// §Fase 86 — `forge <Name>(seed: <string>) -> <Type> { mode:, novelty:,
3149/// depth:, branches:, constraints: }` — Directed Creative Synthesis. A
3150/// flow-body block that runs the Poincaré-Hadamard four-phase creative process
3151/// (Preparation → Incubation → Illumination → Verification) under a **measured,
3152/// fail-closed novelty guarantee** (D86.4/D86.6): the returned typed value must
3153/// clear a Normalized-Compression-Distance novelty floor against the obvious
3154/// baseline AND its `constraints:` anchor, or the forge fails structurally.
3155///
3156/// Before §86 this was a no-op stub (`{ loc }` only, body discarded). §86 makes
3157/// the README's long-standing claim true.
3158#[derive(Debug, Default)]
3159pub struct ForgeBlock {
3160    /// The synthesis name (`forge Artwork(...)` → `"Artwork"`).
3161    pub name: String,
3162    /// The creative seed — the conceptual starting point (`seed: "..."`).
3163    pub seed: String,
3164    /// The declared output type (`-> Visual` → `"Visual"`).
3165    pub output_type: String,
3166    /// Boden creativity mode (closed catalog: `combinatorial | exploratory |
3167    /// transformational`, `axon-T868`). Empty ⇒ defaults to `exploratory`.
3168    pub mode: String,
3169    /// Novelty target `[0.0, 1.0]` (`axon-T869`) — sets the fail-closed novelty
3170    /// floor and blends the incubation temperature. Default 0.5.
3171    pub novelty: f64,
3172    /// Incubation iterations (`depth ≥ 1`, `axon-T870`). Default 1.
3173    pub depth: i64,
3174    /// Illumination parallel branches (best-of-N, `branches ≥ 1`, `axon-T870`).
3175    /// Default 1.
3176    pub branches: i64,
3177    /// Optional `constraints:` reference to a declared `anchor` (`axon-T871`) —
3178    /// the verification predicate + coherence floor. Empty ⇒ novelty-floor-only
3179    /// verification.
3180    pub constraints_ref: String,
3181    pub loc: Loc,
3182}
3183#[derive(Debug)]
3184pub struct FocusStep {
3185    /// §Fase 108.d — the declared dataspace this σ∘π reads (the field
3186    /// keeps its historical name; T930 requires it to resolve to a
3187    /// `dataspace` symbol).
3188    pub expression: String,
3189    /// §Fase 108.d — the data-plane `where:` clause (the §35 closed
3190    /// filter grammar, shared with retrieve/navigate — D108.9). Empty
3191    /// ⇒ no filter. Validated fail-closed at dispatch, like retrieve.
3192    pub where_expr: String,
3193    /// §Fase 108.d — π: the projected columns (empty ⇒ all).
3194    pub select: Vec<String>,
3195    /// §Fase 108.d — the binding name for the result (`as:`). Empty ⇒
3196    /// the dataspace name.
3197    pub output: String,
3198    pub loc: Loc,
3199}
3200/// §Fase 109.a — `grad <letName> wrt <x> [as <name>]`: differentiate the
3201/// EXPRESSION a prior rich `let` bound (its AST rides the IR), at compile
3202/// time, symbolically. The derivative is checked (T931/T932), simplified,
3203/// and stored in the IR — a proof-carrying artifact, re-derived at deploy.
3204#[derive(Debug)]
3205pub struct GradStep {
3206    /// The prior rich `let` whose expression is differentiated.
3207    pub target: String,
3208    /// The variables to differentiate against (`wrt x` / `wrt [x, y]`).
3209    pub wrt: Vec<String>,
3210    /// Result binding (`as:`). Empty ⇒ `d_<target>`.
3211    pub output: String,
3212    pub loc: Loc,
3213}
3214
3215#[derive(Debug)]
3216pub struct AssociateStep {
3217    pub left: String,
3218    pub right: String,
3219    pub using_field: String,
3220    /// §Fase 108.d — result binding name (`as:`). Empty ⇒ `<L>_<R>`.
3221    pub output: String,
3222    pub loc: Loc,
3223}
3224#[derive(Debug)]
3225pub struct AggregateStep {
3226    pub target: String,
3227    pub group_by: Vec<String>,
3228    pub alias: String,
3229    /// §Fase 108.d — the closed aggregate catalog entries
3230    /// (`count` | `count(col)` | `sum(col)` | `avg(col)` | `min(col)` |
3231    /// `max(col)`), kept RAW here; T930 validates shape + columns.
3232    pub compute: Vec<String>,
3233    /// §Fase 108.d — data-plane `where:` (D108.9). Empty ⇒ no filter.
3234    pub where_expr: String,
3235    pub loc: Loc,
3236}
3237#[derive(Debug)]
3238pub struct ExploreStepNode {
3239    pub target: String,
3240    pub limit: Option<i64>,
3241    /// §Fase 108.d — result binding name (`as:`). Empty ⇒ the target.
3242    pub output: String,
3243    pub loc: Loc,
3244}
3245#[derive(Debug)]
3246pub struct IngestStep {
3247    pub source: String,
3248    pub target: String,
3249    /// §Fase 108.c — the declared wire format of the source bytes
3250    /// (closed catalog: `csv` | `json`). Kept RAW at parse; the §108.c
3251    /// type-checker requires it and validates it (`axon-T929`) — an
3252    /// ingest that does not declare what it is parsing is refused.
3253    pub format: String,
3254    /// §Fase 108.c — bounds enforced on the RAW byte stream BEFORE any
3255    /// parsing (the §100 discipline). `None` ⇒ the engine's conservative
3256    /// defaults apply (bounded by default, never unbounded).
3257    pub max_bytes: Option<u64>,
3258    pub max_rows: Option<u64>,
3259    pub loc: Loc,
3260}
3261#[derive(Debug)]
3262pub struct ShieldApplyStep {
3263    pub shield_name: String,
3264    pub target: String,
3265    pub output_type: String,
3266    pub loc: Loc,
3267}
3268/// §Fase 34 / **§Fase 111.e** — `stream { <steps> }`.
3269///
3270/// The body used to NOT EXIST. `parse_block_step` — shared with `deliberate`,
3271/// `consensus` and (pre-retraction) `transact` — called `skip_braced_block()`
3272/// and threw the block's contents away at PARSE time. The handler was not a
3273/// no-op because someone forgot to implement it; it was a no-op because the
3274/// body never reached the AST for anything to execute. Four advertised
3275/// primitives died in that one function.
3276#[derive(Debug)]
3277pub struct StreamBlock {
3278    /// The steps inside the block. Executed in order; each one's fragments are
3279    /// emitted on the flow's event channel as they are produced.
3280    pub body: Vec<FlowStep>,
3281    pub loc: Loc,
3282}
3283#[derive(Debug)]
3284pub struct NavigateStep {
3285    pub pix_name: String,
3286    pub corpus_name: String,
3287    pub query_expr: String,
3288    pub trail_enabled: bool,
3289    pub output_name: String,
3290    /// §Fase 63.B — for MDN corpus-graph navigation: the seed document `from:`
3291    /// to start the ε-informative traversal. Empty for PIX tree navigation.
3292    pub seed: String,
3293    /// §Fase 63.B — for MDN: the `budget:` (max documents). `None` = default.
3294    pub budget: Option<i64>,
3295    /// §Fase 66 (Q2) — optional column-scope filter (`where:`) for a
3296    /// `corpus from axonstore`. A raw filter expr (same shape as `retrieve …
3297    /// where`) pushed to the SELECT sourcing the corpus rows, so an adopter
3298    /// multiplexing sub-tenants in one axon-tenant via a column can scope the
3299    /// MDN graph to a single sub-tenant. Empty = no column filter (RLS-only).
3300    pub where_expr: String,
3301    pub loc: Loc,
3302}
3303#[derive(Debug)]
3304pub struct DrillStep {
3305    pub pix_name: String,
3306    pub subtree_path: String,
3307    pub query_expr: String,
3308    pub output_name: String,
3309    pub loc: Loc,
3310}
3311#[derive(Debug)]
3312pub struct TrailStep {
3313    pub navigate_ref: String,
3314    pub loc: Loc,
3315}
3316#[derive(Debug)]
3317pub struct CorroborateStep {
3318    pub navigate_ref: String,
3319    pub output_name: String,
3320    pub loc: Loc,
3321}
3322#[derive(Debug)]
3323pub struct OtsApplyStep {
3324    pub ots_name: String,
3325    pub target: String,
3326    pub output_type: String,
3327    pub loc: Loc,
3328}
3329#[derive(Debug)]
3330pub struct MandateApplyStep {
3331    pub mandate_name: String,
3332    pub target: String,
3333    pub output_type: String,
3334    pub loc: Loc,
3335}
3336#[derive(Debug)]
3337pub struct ComputeApplyStep {
3338    pub compute_name: String,
3339    pub arguments: Vec<String>,
3340    pub output_name: String,
3341    pub loc: Loc,
3342}
3343/// §λ-L-E Fase 13 D4 — dual-mode listen.
3344///
3345/// `channel_is_ref = true` ⇒ `channel` is the name of a declared
3346/// `ChannelDefinition` (canonical Fase 13 form).  `false` ⇒ legacy
3347/// string topic (deprecated; type checker emits a warning).
3348#[derive(Debug)]
3349pub struct ListenStep {
3350    pub channel: String,
3351    pub channel_is_ref: bool,
3352    pub event_alias: String,
3353    /// §Fase 52.a — the handler body: real flow-steps executed on each event /
3354    /// scheduled tick. Pre-§52.a the `{ … }` block was `skip_braced_block`'d
3355    /// (the listener was inert); now it is parsed so a `daemon` can run logic
3356    /// (e.g. `run <Flow>(…)`) per trigger. Empty for a bodyless `listen`.
3357    pub body: Vec<FlowStep>,
3358    pub loc: Loc,
3359}
3360#[derive(Debug)]
3361pub struct DaemonStepNode {
3362    pub daemon_ref: String,
3363    pub loc: Loc,
3364}
3365#[derive(Debug)]
3366pub struct PersistStep {
3367    pub store_name: String,
3368    /// §Fase 35.o — the `{ col: value }` field block. Empty when the
3369    /// step is written without a block (`persist <store>`), in which
3370    /// case the runtime falls back to writing the flow's user
3371    /// bindings as a row (backward-compatible with v1.30.0).
3372    pub fields: Vec<(String, String)>,
3373    pub loc: Loc,
3374}
3375#[derive(Debug)]
3376pub struct RetrieveStep {
3377    pub store_name: String,
3378    pub where_expr: String,
3379    pub alias: String,
3380    /// §Fase 67.b — optional `order_by:` clause: a closed
3381    /// comma-separated list of `column [asc|desc]` (same identifier
3382    /// discipline as `where:` columns — no injection). Empty = no
3383    /// ordering. Raw string, parsed + validated by the runtime
3384    /// (`filter::render_bounds`) and at `axon check` (§38.d `axon-T807`).
3385    pub order_by: String,
3386    /// §Fase 67.b — optional `limit:` clause: a `u32` literal OR a
3387    /// `${binding}` resolved to a `u32` at runtime. Empty = no limit.
3388    /// Raw string (`"100"` or `"${max}"`), validated at `axon check`
3389    /// (§38.d `axon-T808`).
3390    pub limit_expr: String,
3391    /// §Fase 76.d — optional `aggregate:` clause: a member of the CLOSED
3392    /// catalog `count` | `sum(<col>)` | `avg(<col>)` | `min(<col>)` |
3393    /// `max(<col>)`. Empty = a plain `SELECT *` retrieve. Raw string,
3394    /// parsed + validated by the runtime (`filter::parse_aggregate_clause`)
3395    /// and at `axon check` (§76.d `axon-T843`/`T844`/`T845`).
3396    pub aggregate: String,
3397    /// §Fase 76.d — optional `group_by:` clause: a comma-separated list
3398    /// of column identifiers (same discipline as `order_by:` columns).
3399    /// Requires an `aggregate:`. Empty = no grouping.
3400    pub group_by: String,
3401    /// §Fase 85.b — optional `cache:` reference. A `retrieve` reads a store
3402    /// (a `storage` effect — never `pure`), so caching it is always a WIDENING
3403    /// that accepts staleness: the named `cache` MUST carry a finite `ttl:`
3404    /// (`axon-T865`) and typically an `invalidate_on:`. Names a declared
3405    /// `cache` (`axon-T864`); empty = uncached. Never governed by a
3406    /// `default: true` policy (defaults only auto-cover provably-`pure` tools).
3407    pub cache: String,
3408    pub loc: Loc,
3409}
3410#[derive(Debug)]
3411pub struct MutateStep {
3412    pub store_name: String,
3413    pub where_expr: String,
3414    /// §Fase 35.p — the `{ col: value }` SET assignments. Empty when
3415    /// the step declares no columns, in which case the runtime falls
3416    /// back to writing the flow's user bindings as the `SET` clause
3417    /// (backward-compatible with v1.31.0).
3418    pub fields: Vec<(String, String)>,
3419    pub loc: Loc,
3420}
3421#[derive(Debug)]
3422pub struct PurgeStep {
3423    pub store_name: String,
3424    pub where_expr: String,
3425    pub loc: Loc,
3426}
3427#[derive(Debug)]
3428pub struct TransactBlock {
3429    pub loc: Loc,
3430}
3431
3432/// §Fase 88.a — `scope <Name> { targets:, depth:, approver: }` — the
3433/// authorization scope a `warden` block runs `within`. The load-bearing safety
3434/// construct (paper §5.2): it declares which resources may be analysed
3435/// (`targets` allowlist), how invasively (`depth` ceiling), and who authorised
3436/// it (`approver` capability). A `warden` with no resolvable in-scope
3437/// authorization does not compile (fail-closed). Named + referenced, like
3438/// `cache`/`cors`. **Unknown fields are a hard parse error** (D83.7): a scope
3439/// governs an offensive-capable analysis, so a typo can never silently widen it.
3440#[derive(Debug, Default)]
3441pub struct ScopeDefinition {
3442    pub name: String,
3443    /// `targets: [ "<resource>", … ]` — the allowlist of resources the operator
3444    /// owns/controls and authorises for analysis. Required + non-empty (§88.c
3445    /// `axon-T88x`); a target outside this list is a typed rejection.
3446    pub targets: Vec<String>,
3447    /// `depth: static_artifact | memory_dump | live_network` — the MOST invasive
3448    /// analysis depth this scope permits (the ceiling). Closed catalog, ordered
3449    /// least→most invasive; empty ⇒ the safest default `static_artifact` (§88.c).
3450    pub depth: String,
3451    /// `approver: [requires] "<capability>"` — the capability whose holder
3452    /// authorised this scope (segregation of duties, the `mandate` §21 model).
3453    /// Required (§88.c).
3454    pub approver: String,
3455    pub loc: Loc,
3456    /// Fase 14.b — leading comment trivia.
3457    pub leading_trivia: Vec<crate::tokens::Trivia>,
3458    /// Fase 14.b — trailing comment trivia.
3459    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3460}
3461
3462/// §Fase 88.a — the `warden(<target>) within <Scope> { … }` adversarial
3463/// security-analysis block. A flow-body block (like `quant`): it audits a
3464/// `target` under a paraconsistent adversarial framing, emitting attested
3465/// `Vulnerability` findings — but ONLY `within` a signed authorization `scope`.
3466/// §88.a ships the SURFACE only; scope resolution + the depth/witness discipline
3467/// is §88.c, and the real analysis engine is §88.d/f (enterprise).
3468#[derive(Debug, Default)]
3469pub struct WardenBlock {
3470    /// `warden(<target>)` — a reference to the resource under analysis (a
3471    /// let-bound value / declared target). §88.c checks it is within the scope's
3472    /// `targets` allowlist.
3473    pub target: String,
3474    /// `within <Scope>` — the MANDATORY authorization scope reference. Empty is a
3475    /// hard error (§88.c `axon-T88x`, fail-closed): no scope ⇒ no analysis.
3476    pub scope_ref: String,
3477    /// The nested flow-body statements (`find_exploits()`, `fortify`, `emit`),
3478    /// parsed like `par`/`quant` branches so §88.c can walk them.
3479    pub body: Vec<FlowStep>,
3480    pub loc: Loc,
3481}
3482
3483/// §Fase 51.a — the `quant` cognitive primitive block surface
3484/// (`docs/papers/paper_primitiva_quant.md`; enterprise §Fase 51).
3485///
3486/// `quant` projects an MEK semantic tensor into a complex Hilbert space,
3487/// evolves it under a variational / kernel-feature map, and collapses back to
3488/// classical silicon. The attribute header is OPTIONAL — the bare `quant { … }`
3489/// form (the paper's example) leaves every attribute defaulted. The richer form
3490/// `quant(encoding: amplitude, observable: M, qubits: 10, depth: 4,
3491/// bandwidth: 0.5, backend: quant_sim) { … }` pins the encoding scheme (D2),
3492/// the Pauli-sum observable (D5), the register width / circuit depth, the
3493/// projected-kernel bandwidth γ (D7), and the algebraic-effect backend (D1/D9).
3494///
3495/// §51.a ships the SURFACE only. The Continuous Type Invariant over `body`
3496/// (§51.b), the typed continuous grammar incl. typed `let` + `Observable`
3497/// (§51.c), and the `quant_sim`/`qpu_native` effect injection + `yield`
3498/// measurement point (§51.d) land in subsequent sub-fases.
3499#[derive(Debug, Default)]
3500pub struct QuantBlock {
3501    /// `encoding:` — `amplitude` (default) or `angle` (shallow). `None` = the
3502    /// compiler default (amplitude). Carried as the surface spelling; §51.c
3503    /// validates against the closed scheme set.
3504    pub encoding: Option<String>,
3505    /// `observable:` — the name of a declared `Observable` (Pauli-sum, D5).
3506    /// `None` if unspecified (§51.c resolves + Hermiticity-checks it).
3507    pub observable: Option<String>,
3508    /// `qubits:` — the register width n (D = 2ⁿ). `None` = inferred from the
3509    /// encoded tensor dimensionality. The OSS reference backend caps n ≤ 10
3510    /// (D1); that bound is enforced at §51.e, not here.
3511    pub qubits: Option<i64>,
3512    /// `depth:` — the variational circuit depth L. `None` = backend default.
3513    pub depth: Option<i64>,
3514    /// `bandwidth:` — the projected-quantum-kernel bandwidth γ (D7). `None` =
3515    /// backend default.
3516    pub bandwidth: Option<f64>,
3517    /// §Fase 69.c — `reupload:` L, the number of DATA RE-UPLOADING layers. `None`
3518    /// or `1` = no re-uploading (the data enters once → a quadratic form, provably
3519    /// classical for amplitude+Pauli, §69.b). `L ≥ 2` interleaves the data
3520    /// encoding with entangling layers L times — the ONLY provable escape from the
3521    /// quadratic bound (Havlíček-style; canonical with `encoding: angle`). The
3522    /// resulting kernel must still pass an Advantage Witness to be deployed
3523    /// claiming advantage (§69.a/b).
3524    pub reupload: Option<i64>,
3525    /// The algebraic-effect backend tag: `quant_sim` (default) or `qpu_native`
3526    /// (D1/D9). Stored as the bare backend name; §51.d injects the full
3527    /// `ots:backend:<tag>` effect into the enclosing flow's effect row.
3528    pub effect: String,
3529    /// The nested flow-body statements (parsed like `par` branches, so §51.b
3530    /// can apply the Continuous Type Invariant to real AST). Empty for an
3531    /// empty `quant {}`.
3532    pub body: Vec<FlowStep>,
3533    pub loc: Loc,
3534}
3535
3536/// §Fase 51.c.2 — one term `cₖ · Pₖ` of a Pauli-sum observable.
3537///
3538/// `coefficient` is a real scalar (parsed as `f64`); `pauli` is a Pauli string
3539/// over the closed alphabet `{I, X, Y, Z}` (one char per qubit), e.g. `"ZZ"` or
3540/// `"XI"`. A real linear combination of Pauli strings is **Hermitian by
3541/// construction** (each Pauli string is Hermitian; real-weighted sums preserve
3542/// Hermiticity), which is why the observable needs no separate Hermiticity check.
3543#[derive(Debug, Default, Clone)]
3544pub struct PauliTerm {
3545    pub coefficient: f64,
3546    pub pauli: String,
3547    pub loc: Loc,
3548}
3549
3550/// §Fase 51.c.2 — the `observable <Name> { qubits, term: cₖ·Pₖ … }` declaration
3551/// (paper §3.2; plan D5). A typed Pauli-sum `M = Σ cₖ Pₖ` that a `quant` block
3552/// measures the evolved state against. The type-checker validates the closed
3553/// `{I,X,Y,Z}` alphabet + equal term lengths + non-empty sum; Hermiticity is
3554/// guaranteed by construction (real coefficients).
3555#[derive(Debug, Default)]
3556pub struct ObservableDefinition {
3557    pub name: String,
3558    /// `qubits: n` — the register width every Pauli string must span. `None`
3559    /// = inferred from the (equal) term lengths.
3560    pub qubits: Option<i64>,
3561    pub terms: Vec<PauliTerm>,
3562    pub loc: Loc,
3563    /// Fase 14.b — leading comment trivia.
3564    pub leading_trivia: Vec<crate::tokens::Trivia>,
3565    /// Fase 14.b — trailing comment trivia.
3566    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3567}
3568
3569/// §Fase 69.a — `witness <Name> { claim: <ref>  against: <baseline>
3570/// metric: <metric>  threshold: <ε>  data: <source> }`. The Advantage-Witness
3571/// proof obligation. The compiler proves it WELL-FORMED (§69.a, `axon-E0790`);
3572/// the advantage VALUE is computed on real `data` at deploy/runtime and carried
3573/// as a verdict (§69.b+). Fields are order-free `key: value` pairs.
3574#[derive(Debug)]
3575pub struct WitnessDefinition {
3576    pub name: String,
3577    /// The primitive instance whose advantage is claimed (e.g. an `observable` /
3578    /// `corpus` name, or a quant kernel reference).
3579    pub claim: String,
3580    /// The cheaper alternative the claim must beat (a closed-catalog baseline
3581    /// like `cosine` / `flat_retrieval` / `single_shot`, or a reference).
3582    pub baseline: String,
3583    /// How advantage is measured — a closed-catalog metric (`geometric_difference`,
3584    /// `kernel_target_alignment`, `ranking_lift`, `outcome_lift`).
3585    pub metric: String,
3586    /// The minimum advantage that justifies the cost (ε ≥ 0).
3587    pub threshold: f64,
3588    /// The real-data source the witness is evaluated on (a ref to an axonstore /
3589    /// corpus / labelled set). Required — advantage cannot be claimed in the abstract.
3590    pub data: String,
3591    pub loc: Loc,
3592    pub leading_trivia: Vec<crate::tokens::Trivia>,
3593    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3594}
3595
3596// ── §λ-L-E Fase 13 — Mobile Typed Channels ──────────────────────────────────
3597
3598/// `channel Name { message: T, qos: X, lifetime: ℓ, persistence: π, shield: σ }`.
3599///
3600/// First-class affine resource carrying a typed message.  Direct port
3601/// of `axon.compiler.ast_nodes.ChannelDefinition`.  `message` retains
3602/// the surface spelling (e.g. `"Order"` or `"Channel<Order>"`) so the
3603/// type checker can resolve nested mobility (paper §3.3).
3604#[derive(Debug)]
3605pub struct ChannelDefinition {
3606    pub name: String,
3607    pub message: String,     // type name OR "Channel<T>" for second-order
3608    pub qos: String,         // at_most_once | at_least_once | exactly_once | broadcast | queue
3609    pub lifetime: String,    // linear | affine | persistent (D1 default: affine)
3610    pub persistence: String, // ephemeral | persistent_axonstore
3611    pub shield_ref: String,  // optional σ-shield gate for publish (D8)
3612    pub loc: Loc,
3613    /// Fase 14.b — leading comment trivia attached to this declaration
3614    /// (comments preceding the declaration's first token, since the
3615    /// previous declaration or file start). Empty by default.
3616    pub leading_trivia: Vec<crate::tokens::Trivia>,
3617    /// Fase 14.b — trailing comment trivia (same line as the
3618    /// declaration's last effective token). Empty by default.
3619    pub trailing_trivia: Vec<crate::tokens::Trivia>,
3620}
3621
3622/// `emit ChannelName(value_ref)` — π-calculus output prefix `c⟨v⟩.P`.
3623///
3624/// Direct port of `axon.compiler.ast_nodes.EmitStatement`.  Handles
3625/// both Chan-Output (scalar payload) and Chan-Mobility (channel-as-
3626/// value); the type checker dispatches based on whether `value_ref`
3627/// resolves to a `ChannelDefinition`.
3628#[derive(Debug)]
3629pub struct EmitStatement {
3630    pub channel_ref: String,
3631    pub value_ref: String,
3632    pub loc: Loc,
3633}
3634
3635/// §Fase 92.b — `mint <Credential> as <binding>`: the flow-step verb that
3636/// mints a declared ephemeral `credential` at runtime. The binding
3637/// receives the raw bearer string (shown once — the type checker forbids
3638/// it from flowing into a `persist` payload, `axon-T896`: credentials do
3639/// not enter stores). Undeclared credential reference = `axon-T895`.
3640#[derive(Debug)]
3641pub struct MintStep {
3642    pub credential_ref: String,
3643    pub binding: String,
3644    pub loc: Loc,
3645}
3646
3647/// §Fase 94.b — `rotate <SecretsStore> [where "<filter>"] with <Tool> as
3648/// <binding>`: the mediated secret-renewal flow verb (doctrine
3649/// `rotation_without_revelation`). Set-oriented like `mutate`: every
3650/// custody entry of the store's class matching the filter (whole class
3651/// when the filter is omitted — the post-breach bulk-rotation shape) is
3652/// renewed through ONE mediated exchange per key: the runtime reveals
3653/// the current value only into the tool call, the tool returns the new
3654/// value, the runtime commits it (CAS on version — concurrent rotators
3655/// cannot double-spend a refresh credential). The binding receives the
3656/// METADATA-ONLY summary `{attempted, rotated, failed}` — no term
3657/// evaluates to a secret value. `rotate` on a non-secrets store =
3658/// `axon-T898`; an undeclared tool = `axon-T899`.
3659#[derive(Debug)]
3660pub struct RotateStep {
3661    pub store_ref: String,
3662    /// The §67-grammar metadata filter (`expires_at < now() + interval
3663    /// '10 minutes'`, `key LIKE 'crm.%'`, …). Empty = the whole class.
3664    pub where_expr: String,
3665    pub tool_ref: String,
3666    pub binding: String,
3667    pub loc: Loc,
3668}
3669
3670/// `publish ChannelName within ShieldName` — capability extrusion.
3671///
3672/// Paper §4.3 (Publish-Ext) materialized as a flow step.  The `within
3673/// <Shield>` clause is mandatory (D8) — the parser rejects bare
3674/// `publish C`, the type checker rejects publishes whose shield does
3675/// not cover κ(message_type) (Fase 6.1 + paper §3.4).
3676#[derive(Debug)]
3677pub struct PublishStatement {
3678    pub channel_ref: String,
3679    pub shield_ref: String,
3680    pub loc: Loc,
3681}
3682
3683/// `discover ChannelName as alias` — dual of publish.
3684///
3685/// Imports a previously-published handle into a fresh affine local
3686/// binding.  The `as <alias>` is mandatory; the type checker rejects
3687/// discovery of channels that were never declared with `shield_ref`.
3688#[derive(Debug)]
3689pub struct DiscoverStatement {
3690    pub capability_ref: String,
3691    pub alias: String,
3692    pub loc: Loc,
3693}