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

185 lines
8.2 KiB
Markdown

# 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 `../escape``c331_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 `name`**render name** (keys `traces/<name>/`, use-splice instance default)
- `aura graph register --name`**registry label** ("Label the registered
content id for `use` by name (#317)")
- `aura sweep --name`**family 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.