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