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