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
+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
eagerly at the closing op, `GraphSession::closes_cycle`, #161) — and **holistic**
finalize checks — wiring totality (`validate_wiring`), param-namespace injectivity
(`check_param_namespace_injective`), root-role boundness (`check_root_roles_bound`).
Both cadences call the **same extracted predicates** — no second validator. The
holistic faults read **by-identifier** (#162): `finish()` translates the
index-carrying `CompileError`s (`UnconnectedPort` / `RoleKindMismatch` /
`UnboundRootRole`) into by-identifier `OpError` variants while still *calling* the
unchanged holistic gates. Build-free introspection answers over the closed vocabulary:
`graph introspect --vocabulary | --node <T> | --unwired`. The 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.
(`check_param_namespace_injective`). Both cadences call the **same extracted
predicates** — no second validator. The holistic faults read **by-identifier**
(#162): `finish()` translates the index-carrying `CompileError`s (`UnconnectedPort` /
`RoleKindMismatch`) into by-identifier `OpError` variants while still *calling* the
unchanged holistic gates. **Root-role boundness moved off this list (#317, open
patterns):** `check_root_roles_bound` no longer runs in `finish()` — a `finish()`ed
op-script may still carry an open (unbound) root role, declaring a reusable
pattern's formal parameter; only `compile`/bootstrap (the runnability gate, not a
construction gate) still call it, unchanged. Build-free introspection answers over
the closed vocabulary: `graph introspect --vocabulary | --node <T> | --unwired`. The
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
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"`,
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 role producing a base column of `kind`.
- `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 a node of compiled-in type identity `type`; **`name` is its identifier**
(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
value fans out to every member at bootstrap. Members must share one scalar
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
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.
3. **Register seam (data plane).** The registry's public `put_blueprint`
parses the canonical bytes and requires a gate-passing doc on the root
composite and on every named nested composite the gate guards the
store boundary itself, so the register verb, every register-then-run
composite and on **every** nested composite (the walk recurses
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
op-script vocabulary carries the `doc` op as the builder `.doc(...)`'s
twin ([C25](c25-role-model.md): a closed, typed metadata construct).
Process/campaign documents take an additive-optional top-level
`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
vocabularies — process/campaign blocks, metrics, tap slots, tap folds, and