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