Add a name-addressed tap construction op so declared taps aren't authored by raw node index
#284
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Surfaced by the measurement-milestone fieldtest (#282 declared taps), as the friction behind the tap-wire panic.
Problem
Declared taps (C27) are only authorable today by hand-patching the raw serialized blueprint envelope: a
tapsarray of{ name, from: { node, field } }wherenodeis a raw flat/interior index. There is no construction op (theaura graph/op-script surface) that adds a tap by a name-addressed wire. So the author must count node indices by hand — and an index miscount is the expected mistake, which (until the companion bug fix) panicked the run.Direction (not prescribing)
A
tapop in the construction op-script vocabulary that names its source wire the way the rest of the op surface addresses nodes (by the handle/name the builder already uses forconnect/feed), so the author writestap("p_long", <node-handle>.output("value"))rather than patching{ "node": 14, "field": 0 }. This eliminates the index-miscount class at the source and makes taps a first-class authoring act rather than an envelope patch.Context
The milestone's three capabilities are reachable and correct from the public surface (fieldtest confirmed the origin study is answerable by a hand-authored blueprint + one recorded run). This issue is the ergonomic follow-up: make the tap-authoring path as safe and named as the rest of the construction surface.
Design settled for the tap construction op (autonomous, 2026-07-18) — no fork; the op is the exact output-side twin of the existing expose op (same node.field addressing via split_port + resolve_output_field, same as-renamed op-doc serde shape), differing only in appending a Tap to the composite instead of an OutField.
Two small derived decisions recorded:
Scope: the op + its CLI op-doc serde only; no change to the Tap/FlatTap/bind_tap/aura-run machinery. Ready for spec production → plan → implement.
Correction to the prior decision note (2 of 2): reversing the "no op-layer duplicate-tap-name rejection" call. The grounding-check surfaced that the sibling expose op DOES dedup its boundary output names (out_names → OpError::DuplicateOutput). Being a faithful twin, the tap op therefore dedups tap names too (tap_names → new OpError::DuplicateTap) — and it is the better choice: a duplicate tap name collides as a recorded series (two .json in the trace store), and catching it at authoring time with a clean typed op error is exactly the ergonomic win #284 is about (stricter than the raw-envelope blueprint layer and than the runtime CLI DuplicateBind). Also corrected in the spec: the unknown-source-node path resolves via node_index → OpError::UnknownIdentifier (the actual expose behaviour), not a non-existent UnknownNode variant (a false claim the grounding-check caught and I fixed at the spec root before re-grounding).
Spec signed (grounding-check PASS — /boss auto-sign)
The
tap-construction-op spec cleared the grounding-check hard-gate (fresh-context re-ground on the corrected spec). Under /boss the Step-6 signature IS this PASS.Ratified current-behaviour assumptions (each backed by a currently-green test or a compiler-enforced structural fact):
OpError::UnknownIdentifiervianode_index(test:connect_unknown_node_identifier_rejected)expose:out_names/DuplicateOutput-> newtap_names/DuplicateTap(test:expose_success_and_duplicate_output_rejected)UnknownOutPortviaresolve_output_field(test:expose_unknown_out_port_rejected)Composite.tapshoists intoFlatGraph.tapsthroughcompile_with_params(test:compile_hoists_an_interior_composite_tap_to_the_root)finish()currently drops taps (.with_gangsbut no.with_taps) — the latent gap this op fixesformat_op_error/apply/kind_label/From<OpDoc>are exhaustive matches -> the newTap/DuplicateTaparms are compiler-enumeratedProceeding to plan + implement (2 tasks: engine op in construction.rs, CLI serde in graph_construct.rs).