docs(ledger): C27 — declared taps contract (#282)
Records the tap/binding contract: taps are the output-side twin of input_roles (C26), resolved and hoisted at compile, bound run-mode-aware via a caller-built sink (engine stays aura-core-only), recorded on the single run and inert in a sweep. Documents the deliberate DCE-deferral — build-time elision now, chain-pruning when DCE (C23) lands — and the unbound-is-inert non-error asymmetry vs a mandatory input role. refs #282
This commit is contained in:
@@ -2542,6 +2542,49 @@ plan so the two halves cannot drift (C1/C4 determinism).
|
||||
|
||||
---
|
||||
|
||||
### C27 — Declared taps: named measurement points bind sinks run-mode-aware
|
||||
**Guarantee.** A blueprint may declare **taps** — named, pure output-side
|
||||
declarations `{ name, from: {node, field} }`, the output-side twin of
|
||||
`input_roles` (C26). A tap names an interior producer's output field without
|
||||
naming a sink, exactly as a `Role` names an abstract input without naming a
|
||||
source. At compile the tap resolves — and, for an interior composite, **hoists**
|
||||
to the root — through the same lowering remap edges and `OutField` re-exports
|
||||
use (`resolve_tap_wire`, a `flat_taps` accumulator threaded through the lowering
|
||||
recursion), landing in `FlatGraph.taps` as a `FlatTap { name, node, field }`
|
||||
whose name survives compile and is load-bearing for by-name binding (like
|
||||
`SourceSpec.role`, #275). Binding is run-mode-aware: the run-mode-owning layer
|
||||
constructs a sink (a `Recorder`) at a bound tap via `FlatGraph::bind_tap`, which
|
||||
takes a **caller-built** `Box<dyn Node>` sink (so the engine keeps its
|
||||
`aura-core`-only production dependency — it never constructs a domain sink type)
|
||||
and appends it plus an edge before bootstrap. The single-run path (`run_signal_r`)
|
||||
binds and records each declared tap, persisting the series through the existing
|
||||
trace store (`env.trace_store()`), so the tap columns surface through the same
|
||||
tooling the campaign path feeds; a sweep/reduce run leaves taps unbound.
|
||||
**Forbids.** A tap carrying a channel endpoint or effect in the serialized
|
||||
artefact — recording policy is run-mode authority, not fragment-embedded (a
|
||||
fragment must not drag its measurement decisions into every harness that embeds
|
||||
it; the tier ontology, C20/C21). The engine constructing a domain sink type
|
||||
(the `aura-core`-only wall — the sink is caller-built). Order statistics
|
||||
(median, etc.) inside the graph — they stay sink/analysis-side (C18);
|
||||
multi-instrument study inputs stay harness/World tier.
|
||||
**Non-error.** An **unbound** tap is inert, not a fault — unlike an unbound root
|
||||
input role, which `check_root_roles_bound` rejects (C26): observation is optional,
|
||||
a fed input is mandatory. A declared-but-unbound tap compiles and runs, its
|
||||
producer evaluating and its output discarded (a no-out-edge producer is a valid
|
||||
runnable sink — the Kahn sort emits it, `check_ports_connected` gates only inputs).
|
||||
**Why.** Observability must be expressible in a hand-authored blueprint — the
|
||||
measurement-shaped study computes in the graph and surfaces via taps, no
|
||||
throwaway Rust harness — while recording stays a run-mode decision, not a
|
||||
fragment-embedded effect. Taps are designed **DCE-compatible** (a bound tap is a
|
||||
natural DCE root, an unbound tap a dead declaration) but this contract does
|
||||
**not** depend on DCE: an unbound tap's sink is simply never constructed
|
||||
(build-time elision, which the engine already tolerates). The chain-pruning
|
||||
benefit — a sweep paying zero for the study wires behind an unbound tap — is
|
||||
**deferred to the future DCE cycle** (C23); the mechanism ships now, verified
|
||||
sound. (#282, 2026-07-18.)
|
||||
|
||||
---
|
||||
|
||||
## Open architectural threads not yet resolved
|
||||
|
||||
- **Playground & World UI surface** — the playground is core (C22); the surface
|
||||
|
||||
Reference in New Issue
Block a user