Files
claude 32eb5a6a9e fieldtest: blueprint-name — 4 examples, 1 bug / 0 friction / 1 spec-gap / 4 working
Source-blind consumer run over the four cycle axes on the release
binary at 4ff85b9: the authoring loop lands the name in bytes, store
document, and traces/<name>/ (with the documented default collision
observable on the unnamed twin); the refusal battery is uniform and
op-indexed across the op route and the file intakes; a named blueprint
defaults its use-splice instance identifier as documented, two unnamed
splices collide, two named ones coexist; help/guide/glossary agree on
eleven ops and the render-name vs registry-label split. Finding B1
(sweep --list-axes ungated, mangled listing at exit 0) is fixed
RED-first in the preceding commit; SG1 (prefix variance in the refusal
prose) is ratified there in C24.

refs #331
2026-07-25 05:15:01 +02:00

8.2 KiB

Fieldtest transcript — cycle #331 (blueprint name op)

Source-blind consumer field test of the eleventh op-script op {"op":"name","name":"<name>"} — the composite render name that flows to store documents, traces/<name>/, and use-splice instance defaults, gated by a deterministic shape check at every authored intake.

Binary: target/release/aura, built from HEAD 4ff85b9 via cargo build --release -p aura-cli (Finished in 5.48s — HEAD builds clean). Scratch project aura new nm_lab in /tmp/ftwork/nm_lab; real data from the built-in archive (US500). Public interface only (README, authoring-guide, glossary, design ledger C23/C24/C29, aura … --help) — no crate source read.


Example 1 — authoring loop end to end (c331_1_named_crossover.ops.json)

A named SMA-crossover with a spread tap, all knobs bound (closed → runnable).

$ aura graph build < c331_1_named_crossover.ops.json > c331_1_named_crossover.bp.json   # exit 0
# blueprint.name == "ny_momentum", taps == [{"name":"spread",...}]
$ aura graph register c331_1_named_crossover.bp.json
registered blueprint 347a361997e8a65d4122b5ab50367031f78df24a1ff6ad14477ed2c04ec3b603 (…)
# store doc runs/blueprints/347a…json -> name: ny_momentum
$ aura run c331_1_named_crossover.bp.json --real US500 --tap spread=mean          # exit 0
$ find runs/traces
runs/traces/ny_momentum
runs/traces/ny_momentum/index.json
runs/traces/ny_momentum/spread.json

The authored name ny_momentum shows up in all three documented places — the blueprint bytes, the store document's name, and the run's trace directory traces/ny_momentum/. (Finding W1.)

Default collision story (omitting the name)

Two different unnamed crossovers (A: fast=3/slow=8, B: fast=5/slow=20), both build to name: "graph", both --tap spread=record over US500:

$ aura run c331_1b_unnamed_A.bp.json --real US500 --tap spread=record   # exit 0
  sha256(runs/traces/graph/spread.json) = 5bb5dcfc…
$ aura run c331_1c_unnamed_B.bp.json --real US500 --tap spread=record   # exit 0
  sha256(runs/traces/graph/spread.json) = 3be8025b…   ← overwritten, silently
$ ls runs/traces/ → graph  ny_momentum

Both unnamed strategies share traces/graph/; B silently overwrote A's spread.json (differing sha). This is exactly the documented "later runs overwrite earlier ones" consequence — observable, no warning (as documented). (Finding W1.)


Example 2 — refusal battery (c331_2*)

Op route (op-indexed prose)

$ aura graph build < c331_2a_dup_name.ops.json       # exit 1
aura: op 1 (name): a script names its blueprint at most once
$ aura graph build < c331_2b_slash_name.ops.json     # exit 1
aura: op 0 (name): blueprint name "traces/evil" is invalid: must not contain '/' or '\' (a single path segment)
$ aura graph build < c331_2c_empty_name.ops.json     # exit 1
aura: op 0 (name): blueprint name "" is invalid: must be non-empty (a single path segment)
$ aura graph build < c331_2d_dotdot_name.ops.json    # exit 1
aura: op 0 (name): blueprint name ".." is invalid: must not be "." or ".." (a single path segment)

Each refusal is op-indexed, quotes the offending name, and states the exact rule. Copy-paste fixable. (Finding W2.)

Hand-edited envelope with a bad root name (../escape)

c331_2e_open_named.bp.json (clean name clean_name, fast.length open), root name hand-edited to ../escapec331_2e_badroot.bp.json, fed to every file intake:

$ aura graph register       c331_2e_badroot.bp.json     # exit 1  GATED
aura: blueprint name "../escape" is invalid: must not contain '/' or '\' (a single path segment)
$ aura run                  c331_2e_badroot.bp.json --real US500   # exit 2  GATED
aura: …/c331_2e_badroot.bp.json: blueprint name "../escape" is invalid: …
$ aura sweep                c331_2e_badroot.bp.json --axis fast.length=2,4   # exit 2  GATED
aura: blueprint name "../escape" is invalid: …
$ aura graph introspect --content-id c331_2e_badroot.bp.json   # exit 1  GATED
$ aura graph introspect --params     c331_2e_badroot.bp.json   # exit 1  GATED
$ aura graph                c331_2e_badroot.bp.json     # exit 2  GATED
aura: …/c331_2e_badroot.bp.json: blueprint name "../escape" is invalid: …

$ aura sweep c331_2e_badroot.bp.json --list-axes         # exit 0  *** NOT GATED ***
./escape.fast.length:I64
slow.length:I64 default=8
bias.scale:F64 default=0.5

Six of seven file intakes gate uniformly (Finding W2). sweep --list-axes does not — it exits 0 and prints a mangled, retired wrapped axis form ./escape.fast.length for the open param only. The same on a . root: sweep --list-axes.fast.length:I64 (exit 0) while register/--params refuse. Pasting the leaked ./escape.fast.length back as --axis refuses — so discovery emits a name the bind surface rejects. (Finding B1.)

Prose note: run and graph prepend the full file path; register, sweep --axis, introspect do not — the core sentence is uniform, the prefix is not. (Finding SG1.)


Example 3 — use-splice interplay (c331_3*)

Registered patterns: agree (input x, SMA length open), refute (input y), unnamedsm (unnamed → graph, input z).

# A — splice agree, NO instance name:
$ aura graph build < c331_3A_splice_named.ops.json    # exit 0
aura: note: use "agree": agree -> 4f78a9f6e1f3…
$ aura graph introspect --params c331_3A.bp.json
agree.sma.length:I64                                   ← instance defaulted to authored name

# B — splice unnamedsm TWICE, no instance names:
$ aura graph build < c331_3B_two_unnamed.ops.json     # exit 1
aura: note: use "graph": unnamedsm -> 25cdd4942f21…
aura: note: use "graph": unnamedsm -> 25cdd4942f21…
aura: op 3 (use): duplicate identifier "graph"          ← collide, op-indexed

# C — splice agree + refute, no instance names:
$ aura graph build < c331_3C_two_named.ops.json       # exit 0
$ aura graph introspect --params c331_3C.bp.json
agree.sma.length:I64
refute.sma.length:I64                                   ← distinct names, no collision

Instance identifier defaults to the authored render name; two unnamed (graph) splices collide with an op-indexed duplicate identifier "graph"; distinct authored names dissolve the collision. Matches guide + glossary blueprint label entry exactly. (Finding W3.)


Example 4 — discovery / docs parity (c331_4_*)

aura graph build --help lists the name op last of the eleven:

{"op":"name","name":"ny_momentum"}
    set the composite's render name, at most once per script (default
    "graph" if omitted)

Consistent with the authoring guide's eleven-ops table (§1). Three distinct --name-ish concepts, each with distinct help phrasing:

  • op namerender name (keys traces/<name>/, use-splice instance default)
  • aura graph register --nameregistry label ("Label the registered content id for use by name (#317)")
  • aura sweep --namefamily name ("Family name (records to the registry…)")

The glossary blueprint label entry separates render name from registry label explicitly. Distinction is learnable from the public docs alone, though a consumer must consult the glossary to fully disambiguate the three. (Finding W4.)


Findings summary

  • W1 (working, axis 1): authored name → blueprint bytes + store doc + traces/<name>/; default graph collision observable (silent overwrite).
  • W2 (working, axis 2): op-route refusals op-indexed + specific; hand-edited bad root name gated uniformly on 6 of 7 file intakes.
  • W3 (working, axis 3): use-splice instance defaults to authored name; unnamed splices collide (op-indexed); named splices coexist.
  • W4 (working, axis 4): graph build --help lists the name op consistently with the guide; render-name vs registry-label distinction learnable.
  • B1 (BUG, axis 2): sweep --list-axes does not gate a bad root name (exit 0) and leaks a retired, non-bindable wrapped axis form (./escape.fast.length), breaking #328's --params/--list-axes line-identity and the every-discovered-name-is-verbatim-legal promise.
  • SG1 (spec_gap, axis 2): "uniform refusal prose" — run/graph prepend the file path, other seams don't; unclear whether "uniform" means the core sentence or byte-identical.