docs(ledger, glossary, guide): the ten-verb grammar, the use seam, open patterns

C24: the op-script grammar section is rewritten to the ten verbs — repairing a
pre-existing drift on the way (it read "The eight verbs" and omitted the doc
op shipped with #316) — with the new use row and the open-pattern reading of
input roles; a "Registered-blueprint splice" paragraph records the resolution
split (all store I/O CLI-side at DTO conversion; the engine's second injected
closure mirrors vocab, the §"Enforcement shift" permission extended to
registered-blueprint references); the construction-gates paragraph moves
root-role boundness from the holistic finalize list to compile/bootstrap only
(#317 comment 4627).

C29: the register-seam wording drops "named" — the code gates EVERY nested
composite, recon-confirmed pre-existing prose/code mismatch — and the use seam
joins the seam list (build-time gate on fetched store content; no retroactive
invalidation of existing reads).

Glossary: use (op) and blueprint label entries. Authoring guide: ten ops
(the stale "nine ops" heading + the input row's obsolete finalize-refusal
claim corrected), the use row, and the register --name / discovery paragraph.

Prose truth per review: instance params are sweepable, NOT gangable (the gang
op refuses a composite instance's member path) — stated as such everywhere;
the gangability of spliced instances is recorded residue, not a claim.

refs #317
This commit is contained in:
2026-07-24 19:45:27 +02:00
parent 623d304b7f
commit 2c2c2fdef6
4 changed files with 79 additions and 17 deletions
+10 -2
View File
@@ -221,14 +221,15 @@ $ aura graph build < smacross.json > blueprint.json
Nodes are referenced by an **identifier** (given by `add`, see below); ports Nodes are referenced by an **identifier** (given by `add`, see below); ports
are dotted `<identifier>.<port>` on both sides of a wire. are dotted `<identifier>.<port>` on both sides of a wire.
### The nine ops ### The ten ops
| op | JSON shape | does | | op | JSON shape | does |
|---|---|---| |---|---|---|
| `doc` | `{"op":"doc","text":<str>}` | declare the composite's one-line meaning (C29) — the op-script twin of the Rust builder's `.doc(...)`. Required before `aura graph register` accepts the product (the store refuses a doc-less composite); at most one per script (a second refuses: `a doc op may appear at most once`). The text is gated: empty or merely restating the composite's name refuses at register. | | `doc` | `{"op":"doc","text":<str>}` | declare the composite's one-line meaning (C29) — the op-script twin of the Rust builder's `.doc(...)`. Required before `aura graph register` accepts the product (the store refuses a doc-less composite); at most one per script (a second refuses: `a doc op may appear at most once`). The text is gated: empty or merely restating the composite's name refuses at register. |
| `source` | `{"op":"source","role":<str>,"kind":<ScalarKind>}` | reserve a bound root **source** role of `kind` — a real input the harness feeds (e.g. `"price"`). | | `source` | `{"op":"source","role":<str>,"kind":<ScalarKind>}` | reserve a bound root **source** role of `kind` — a real input the harness feeds (e.g. `"price"`). |
| `input` | `{"op":"input","role":<str>}` | reserve an open root **input** role (kind inferred from the slots it feeds) — for a fragment meant to be wired by an *enclosing* graph. A standalone document built with `aura graph build` finalizes as a closed root, so an `input` role that is never bound refuses at the end: `finalize: root input role <name> is unbound`. | | `input` | `{"op":"input","role":<str>}` | reserve an open root **input** role (kind inferred from the slots it feeds) — the formal parameter of an **open pattern**, a fragment meant to be wired by an *enclosing* graph. An open pattern builds and registers like any blueprint; only *running* it standalone refuses (bootstrap's unbound-root-role gate — there is no enclosing graph to wire it). |
| `add` | `{"op":"add","type":<TypeId>,"name":<str>?,"bind":{<param>:<Scalar>}?}` | instantiate a node of a type in the closed vocabulary (`aura graph introspect --vocabulary`) — see §0 below for how a type gets into that vocabulary in the first place. `name` becomes the node's identifier for later ops (default: the type's own lowercase label — two unnamed nodes of the same type then collide). `bind` sets zero or more of its params. | | `add` | `{"op":"add","type":<TypeId>,"name":<str>?,"bind":{<param>:<Scalar>}?}` | instantiate a node of a type in the closed vocabulary (`aura graph introspect --vocabulary`) — see §0 below for how a type gets into that vocabulary in the first place. `name` becomes the node's identifier for later ops (default: the type's own lowercase label — two unnamed nodes of the same type then collide). `bind` sets zero or more of its params. |
| `use` | `{"op":"use","ref":{"content_id":<id-or-prefix>}\|{"name":<label>},"name":<str>?,"bind":{<path>:<Scalar>}?}` | splice a **registered blueprint** into the graph as a nested composite under an instance identifier (`name`; default: the stored composite's own name). The ref resolves by content id (full or unique prefix) or by a register-time label; `graph build` echoes every resolution (`aura: note: use "…": … -> <id>`) so the exact id can be pinned. The instance's open input roles wire as `<instance>.<role>`, its outputs as `<instance>.<field>`; its open params surface path-qualified (`<instance>.<node>.<param>`), sweepable (ganging an instance's params is not yet supported — the `gang` op refuses a composite instance's member path). A doc-less stored source refuses at the op (C29). The emitted blueprint contains the subgraph *inline* — no reference survives into the artifact. |
| `feed` | `{"op":"feed","role":<str>,"into":[<port>, …]}` | fan a previously-declared role into one or more interior input slots, all-or-nothing (a failing target leaves none of the batch wired). | | `feed` | `{"op":"feed","role":<str>,"into":[<port>, …]}` | fan a previously-declared role into one or more interior input slots, all-or-nothing (a failing target leaves none of the batch wired). |
| `connect` | `{"op":"connect","from":<port>,"to":<port>}` | wire one interior output field to one interior input slot. A `connect` that would close a dataflow cycle is rejected immediately — the only legal feedback path is an explicit delay/state node (domain invariant 5). | | `connect` | `{"op":"connect","from":<port>,"to":<port>}` | wire one interior output field to one interior input slot. A `connect` that would close a dataflow cycle is rejected immediately — the only legal feedback path is an explicit delay/state node (domain invariant 5). |
| `expose` | `{"op":"expose","from":<port>,"as":<str>}` | promote an interior output field to a boundary output under the alias `as` — a real *alias* (a terminal boundary name, not a referenceable identifier like `add`'s `name`). Together with `tap`, one of the two ops whose `as` key is a terminal name rather than an identifier. | | `expose` | `{"op":"expose","from":<port>,"as":<str>}` | promote an interior output field to a boundary output under the alias `as` — a real *alias* (a terminal boundary name, not a referenceable identifier like `add`'s `name`). Together with `tap`, one of the two ops whose `as` key is a terminal name rather than an identifier. |
@@ -405,6 +406,13 @@ registered blueprint 597d719b7ac607158cda3e68cd497387620397a5e93087e23da512876da
The printed content id is the address a campaign document's `strategies[].ref` The printed content id is the address a campaign document's `strategies[].ref`
points at (§3). points at (§3).
`graph register <file> --name <label>` additionally records a **blueprint
label** — a legible, re-pointable handle for the `use` op's `{"name":…}`
ref form (re-registering under the same label repoints it; the echo names
the previous target). `aura graph introspect --registered` lists the
labels with their id prefix and root doc line — the discovery surface for
what `use` can reference.
## 2. Process documents — a validation methodology (role 5) ## 2. Process documents — a validation methodology (role 5)
A process document is a named, versionable pipeline of **std stage blocks** A process document is a named, versionable pipeline of **std stage blocks**
+49 -13
View File
@@ -144,17 +144,38 @@ per-op checks — name resolution, edge kind-match (`edge_kind_check`), the doub
arm, param bind, and acyclicity (a `connect` that would close a cycle is rejected arm, param bind, and acyclicity (a `connect` that would close a cycle is rejected
eagerly at the closing op, `GraphSession::closes_cycle`, #161) — and **holistic** eagerly at the closing op, `GraphSession::closes_cycle`, #161) — and **holistic**
finalize checks — wiring totality (`validate_wiring`), param-namespace injectivity finalize checks — wiring totality (`validate_wiring`), param-namespace injectivity
(`check_param_namespace_injective`), root-role boundness (`check_root_roles_bound`). (`check_param_namespace_injective`). Both cadences call the **same extracted
Both cadences call the **same extracted predicates** — no second validator. The predicates** — no second validator. The holistic faults read **by-identifier**
holistic faults read **by-identifier** (#162): `finish()` translates the (#162): `finish()` translates the index-carrying `CompileError`s (`UnconnectedPort` /
index-carrying `CompileError`s (`UnconnectedPort` / `RoleKindMismatch` / `RoleKindMismatch`) into by-identifier `OpError` variants while still *calling* the
`UnboundRootRole`) into by-identifier `OpError` variants while still *calling* the unchanged holistic gates. **Root-role boundness moved off this list (#317, open
unchanged holistic gates. Build-free introspection answers over the closed vocabulary: patterns):** `check_root_roles_bound` no longer runs in `finish()` — a `finish()`ed
`graph introspect --vocabulary | --node <T> | --unwired`. The engine `Op` stays op-script may still carry an open (unbound) root role, declaring a reusable
serde-free; the wire DTO (`OpDoc`) is CLI-side (`aura-cli::graph_construct`). pattern's formal parameter; only `compile`/bootstrap (the runnability gate, not a
Acceptance: a graph built purely through the ops compiles identical (C1) to its construction gate) still call it, unchanged. Build-free introspection answers over
Rust-built twin, an invalid op is rejected at the op naming the cause, introspection the closed vocabulary: `graph introspect --vocabulary | --node <T> | --unwired`. The
answers without a build. engine `Op` stays serde-free; the wire DTO (`OpDoc`) is CLI-side
(`aura-cli::graph_construct`). Acceptance: a graph built purely through the ops
compiles identical (C1) to its Rust-built twin, an invalid op is rejected at the op
naming the cause, introspection answers without a build.
**Registered-blueprint splice (#317).** The `use` op resolves a reference — a store
content id, a unique content-id prefix (#302 semantics), or a registry **name
label** (`graph register --name`, latest-wins, `aura-registry`'s
`blueprint_names.jsonl` sidecar) — to a previously registered blueprint and
splices it into the building graph as a nested `BlueprintNode::Composite` under an
instance identifier. The **engine never resolves**: `GraphSession`/`replay` take a
second injected closure, `subgraph: &dyn Fn(&str) -> Option<Composite>`, mirroring
`vocab`'s closed-lookup posture exactly — this is the same enforcement-shift
permission §"Enforcement shift" above already grants the node vocabulary, extended
to registered-blueprint references. All store I/O — label/prefix resolution, the
`get_blueprint` fetch, the C29 doc-gate re-walk over the fetched composite (before
it ever reaches the session, so a doc-less fetched entry cannot enter a NEW
composition), and the resolution echo — happens CLI-side, at DTO conversion, before
replay; build-free introspection paths pass `&|_| None`. The echo
(`aura: note: use "<instance>": <label-or-prefix> -> <full id>`) is the existing
`aura: note:` benign-diagnostic marker (C14) — a new instance of the existing
class, not a new one, so the exit-code/marker taxonomy is unchanged.
**Invariant-5 lockstep.** The eager acyclicity gate is a *second* home of invariant 5 **Invariant-5 lockstep.** The eager acyclicity gate is a *second* home of invariant 5
alongside the bootstrap Kahn sort (`harness.rs`, `BootstrapError::Cycle`); the two alongside the bootstrap Kahn sort (`harness.rs`, `BootstrapError::Cycle`); the two
@@ -166,12 +187,14 @@ rejected at construction.
The op-script is a JSON **array of ops**, each object internally tagged by `"op"`, The op-script is a JSON **array of ops**, each object internally tagged by `"op"`,
replayed in order; nodes are referenced **by identifier**, ports as dotted replayed in order; nodes are referenced **by identifier**, ports as dotted
`<identifier>.<port>`. The eight verbs: `<identifier>.<port>`. The ten verbs:
- `source` — `{"op":"source","role":<str>,"kind":<ScalarKind>}` — declare a root - `source` — `{"op":"source","role":<str>,"kind":<ScalarKind>}` — declare a root
source role producing a base column of `kind`. source role producing a base column of `kind`.
- `input` — `{"op":"input","role":<str>}` — declare a root input role (kind inferred - `input` — `{"op":"input","role":<str>}` — declare a root input role (kind inferred
from the slots it feeds). from the slots it feeds) — a reusable **pattern**'s formal parameter, left open
until an enclosing graph wires it (#317's open patterns): `finish()` accepts an
op-script that never binds it, only `compile`/bootstrap require it bound.
- `add` — `{"op":"add","type":<TypeId>,"name":<str>?,"bind":{<param>:<Scalar>}?}` — - `add` — `{"op":"add","type":<TypeId>,"name":<str>?,"bind":{<param>:<Scalar>}?}` —
add a node of compiled-in type identity `type`; **`name` is its identifier** add a node of compiled-in type identity `type`; **`name` is its identifier**
(mirrors the builder's `.named(...)`; defaults to the lowercased type label, so (mirrors the builder's `.named(...)`; defaults to the lowercased type label, so
@@ -196,6 +219,19 @@ replayed in order; nodes are referenced **by identifier**, ports as dotted
leave the sweepable param space and `as` replaces them; the bound or swept leave the sweepable param space and `as` replaces them; the bound or swept
value fans out to every member at bootstrap. Members must share one scalar value fans out to every member at bootstrap. Members must share one scalar
kind and stay open (un-bound). kind and stay open (un-bound).
- `doc` — `{"op":"doc","text":<str>}` — declare the composite's one-line meaning
(C29, #316) — the op-script twin of the builder's `.doc(...)`; at most one per
script, a second is a fault (a declarative script carries no last-wins
ambiguity).
- `use` — `{"op":"use","ref":{"content_id":<str>}|{"name":<str>},"name":<str>?,"bind":{<path>:<Scalar>}?}`
— splice a **registered** blueprint into the building graph as a nested
composite under an instance identifier (`name`, defaulting to the fetched
composite's own name); `bind` path-qualifies the spliced instance's params
(e.g. `"sma.length"`), applied after the splice (#317). An instance's open
input roles become its `<instance>.<role>` in-ports, its output boundary
aliases its `<instance>.<field>` out-fields — the same `Composite` arm every
other add/wiring path walks, not a new mechanism. See §"The construction
service" below for the resolution split.
Value forms are the serialized representations: a `Scalar` bind value is the typed tag Value forms are the serialized representations: a `Scalar` bind value is the typed tag
form `{"I64":2}` / `{"F64":0.5}` / `{"Bool":true}` / `{"Timestamp":<i64>}`, and `kind` form `{"I64":2}` / `{"F64":0.5}` / `{"Bool":true}` / `{"Timestamp":<i64>}`, and `kind`
+12 -2
View File
@@ -27,13 +27,23 @@ cannot pass its short-name alibi
restatement. restatement.
3. **Register seam (data plane).** The registry's public `put_blueprint` 3. **Register seam (data plane).** The registry's public `put_blueprint`
parses the canonical bytes and requires a gate-passing doc on the root parses the canonical bytes and requires a gate-passing doc on the root
composite and on every named nested composite the gate guards the composite and on **every** nested composite (the walk recurses
store boundary itself, so the register verb, every register-then-run unconditionally — it does not stop at an unnamed one) — the gate guards
the store boundary itself, so the register verb, every register-then-run
sugar path, and any future caller are gated by construction. The sugar path, and any future caller are gated by construction. The
op-script vocabulary carries the `doc` op as the builder `.doc(...)`'s op-script vocabulary carries the `doc` op as the builder `.doc(...)`'s
twin ([C25](c25-role-model.md): a closed, typed metadata construct). twin ([C25](c25-role-model.md): a closed, typed metadata construct).
Process/campaign documents take an additive-optional top-level Process/campaign documents take an additive-optional top-level
`description`, gated only when present (`DocFault::BadDescription`). `description`, gated only when present (`DocFault::BadDescription`).
4. **Use seam (data plane, construction-time, #317).** The `use`
construction op re-runs the SAME `gate_composite_docs` walk over a
blueprint FETCHED from the store, before it ever splices into a new
composition — a build-time gate on already-registered content, not a
second write-path check. A doc-less entry (reachable only through the
raw in-crate write survivor class, since `put_blueprint`'s own gate keeps
the store clean going forward) cannot enter a NEW composition this way,
but stays readable and runnable on its own — no retroactive invalidation,
the same discipline the Forbids clause states for the register seam.
**Scope at this record's writing (#321).** The gate walks five **Scope at this record's writing (#321).** The gate walks five
vocabularies — process/campaign blocks, metrics, tap slots, tap folds, and vocabularies — process/campaign blocks, metrics, tap slots, tap folds, and
+8
View File
@@ -31,6 +31,10 @@ A strategy's primary, backtestable DAG output: one signed, bounded `f64 ∈ [-1,
**Avoid:** — **Avoid:** —
The param-generic, input-role-generic graph-as-data produced by running a Rust builder; it carries free numeric params and free input roles before bootstrap. Bootstrapped into a frozen instance by binding params + data + seed. Registered and inspected headless (`aura graph register`, `aura graph introspect --params` — the raw `param_space` namespace campaign axes bind against; cycle 0107/#196); a *bound* param is an overridable **default** — a sweep axis naming it re-opens it per family (#246), while `run` uses it as-is. The param-generic, input-role-generic graph-as-data produced by running a Rust builder; it carries free numeric params and free input roles before bootstrap. Bootstrapped into a frozen instance by binding params + data + seed. Registered and inspected headless (`aura graph register`, `aura graph introspect --params` — the raw `param_space` namespace campaign axes bind against; cycle 0107/#196); a *bound* param is an overridable **default** — a sweep axis naming it re-opens it per family (#246), while `run` uses it as-is.
### blueprint label
**Avoid:** —
A registry-level name pointing at a registered blueprint's content id (`graph register --name <label>`, #317) — a mutable, latest-wins pointer over the immutable content-addressed store, not a second identity: re-registering under an existing label *repoints* it (the earlier content id stays reachable by its own id, never invalidated). `graph introspect --registered` lists every label with its content-id prefix and root `doc` line — the discovery surface a `use (op)` reference by name resolves against.
### bootstrap ### bootstrap
**Avoid:** — **Avoid:** —
The distinct, recursive construction phase that binds `(blueprint + param-set + data bindings + seed)` into a frozen instance — buffers sized, topology fixed. The explicit name for the "wiring / graph build" that C7/C12 reference — the construction/compilation sense. Disambiguation: the *statistical* moving-block bootstrap of a trade-R series (`r_bootstrap`, `RBootstrap`) is a different thing that keeps its statistics name — it appears as the deflation null, the `aura mc` R path, and since 0108 the `std::monte_carlo` stage's `stage bootstrap` annotation; context (construction vs annotation) disambiguates. The distinct, recursive construction phase that binds `(blueprint + param-set + data bindings + seed)` into a frozen instance — buffers sized, topology fixed. The explicit name for the "wiring / graph build" that C7/C12 reference — the construction/compilation sense. Disambiguation: the *statistical* moving-block bootstrap of a trade-R series (`r_bootstrap`, `RBootstrap`) is a different thing that keeps its statistics name — it appears as the deflation null, the `aura mc` R path, and since 0108 the `std::monte_carlo` stage's `stage bootstrap` annotation; context (construction vs annotation) disambiguates.
@@ -333,6 +337,10 @@ Since #283 what CONSUMES a declared tap is itself declared per run by a **tap pl
**Avoid:** — **Avoid:** —
The `content id` of a run's signal blueprint in its run-record role: stamped into the manifest as `topology_hash`, keying the reproduction store and anchoring `aura reproduce` — the same SHA-256 over the same canonical bytes. Distinct from the debug-name-blind `identity id`. The `content id` of a run's signal blueprint in its run-record role: stamped into the manifest as `topology_hash`, keying the reproduction store and anchoring `aura reproduce` — the same SHA-256 over the same canonical bytes. Distinct from the debug-name-blind `identity id`.
### use (op)
**Avoid:** —
The construction op that splices a **registered** blueprint into the building graph as a nested `composite` under a chosen instance name (`{"op":"use","ref":{"content_id"|"name":…},"name":…,"bind":{…}}`, #317): the reference — a content id, a unique content-id prefix, or a `blueprint label` — resolves CLI-side, before the engine ever sees it; the fetched composite is C29-gated, spliced, and its resolution echoed on stderr (`aura: note:`, the existing benign marker). The emitted blueprint carries the splice inline as an ordinary nested composite — no reference or registry dependency survives into the artifact. An instance's open input roles become its `<instance>.<role>` in-ports, its output boundary fields its `<instance>.<field>` out-fields — the existing nested-composite param-prefix discipline extends unchanged (`graph.<instance>.<node>.<param>` on `sweep --list-axes`). Pairs with the `input` op: a pattern meant to be `use`d declares its formal parameters as open `input` roles, left unbound at `finish()` (#317's open-pattern amendment) — only running it standalone requires them bound.
### veto ### veto
**Avoid:** risk-manager **Avoid:** risk-manager
The **optional** documented pre-trade-gate seam in the execution chain (`stop-rule → [Veto] → position-management`): position / exposure / notional caps that may reject or scale a trade. A pass-through identity DCE'd away when absent (C19/C23); kept as a named seam — it is what LEAN calls "Risk Management" and nautilus the RiskEngine. The **optional** documented pre-trade-gate seam in the execution chain (`stop-rule → [Veto] → position-management`): position / exposure / notional caps that may reject or scale a trade. A pass-through identity DCE'd away when absent (C19/C23); kept as a named seam — it is what LEAN calls "Risk Management" and nautilus the RiskEngine.