From 024e8652c0afc186b2718c0c0f5189a6874fa5d7 Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 26 Jul 2026 14:25:26 +0200 Subject: [PATCH] =?UTF-8?q?fix(aura-cli,=20docs):=20fieldtest=20bugs=20?= =?UTF-8?q?=E2=80=94=20the=20non-literal=20override=20prose=20+=20the=20gu?= =?UTF-8?q?ide's=20family-id=20shape?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - parse_override_tokens distinguishes the grammar fault (no '=' / no dotted path — prose unchanged) from a shape-valid token whose VALUE fails the scalar-literal parse: the refusal now names the value and the literal requirement (styled after the kind-mismatch sibling) instead of falsely claiming a NODE.PARAM=VALUE violation. RED-first; exit stays 2 on both branches. - the authoring guide's multi-regime family ids corrected to the executor's real shape (infix -r{k} after the window segment, -r0 explicit on the default regime), and §1's name-op paragraph no longer claims naming dissolves same-name trace overwriting (that trace-dir question is tracked on #311, not resolved here). --- crates/aura-cli/src/main.rs | 15 ++++++++++++++- crates/aura-cli/tests/exec.rs | 19 +++++++++++++++++++ docs/authoring-guide.md | 29 +++++++++++++++++++---------- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index ef3a006..3c2e4f1 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -646,12 +646,25 @@ fn parse_override_tokens(tokens: &[String]) -> Vec<(String, Scalar)> { eprintln!("aura: exec: --override expects NODE.PARAM=VALUE, got `{tok}`"); std::process::exit(2); } + // B2 (2026-07-26 harvest fieldtest): `path=val_s` already satisfies the + // NODE.PARAM=VALUE grammar (the two checks above passed) — a value + // that fails BOTH scalar parses is a literal fault, not a shape + // fault, and must not be misreported as one. Styled after the + // compile-boundary kind-mismatch sibling + // (`aura_runner::member::compile_error_prose`): name the offending + // value and what this lexer accepts (its own vocabulary is + // int-then-float, #319 Task 3/4 — no bool/timestamp override + // literal exists here). let val = match val_s.trim().parse::() { Ok(i) => Scalar::i64(i), Err(_) => match val_s.trim().parse::() { Ok(f) => Scalar::f64(f), Err(_) => { - eprintln!("aura: exec: --override expects NODE.PARAM=VALUE, got `{tok}`"); + eprintln!( + "aura: exec: --override {path}: expects a scalar literal, got `{}` — \ + write a bare integer (e.g. 2) or a decimal (e.g. 2.0)", + val_s.trim() + ); std::process::exit(2); } }, diff --git a/crates/aura-cli/tests/exec.rs b/crates/aura-cli/tests/exec.rs index 8c35e34..3d52448 100644 --- a/crates/aura-cli/tests/exec.rs +++ b/crates/aura-cli/tests/exec.rs @@ -667,6 +667,25 @@ fn exec_override_malformed_token_refuses_with_usage() { assert!(out.contains("NODE.PARAM=VALUE"), "stdout/stderr: {out}"); } +/// B2 (2026-07-26 harvest fieldtest): `fast.length=abc` IS in the +/// `NODE.PARAM=VALUE` grammar — a dotted path, an `=`, a value — so the +/// grammar is not what is wrong here; `abc` simply is not a scalar literal. +/// `parse_override_tokens` must not misreport this as a shape violation +/// (the same "expects NODE.PARAM=VALUE" prose a token missing `=` gets); +/// it must name the offending value and that a scalar literal is required, +/// styled after the kind-mismatch sibling +/// (`exec_override_kind_mismatch_refuses_with_prose_not_the_debug_struct`). +#[test] +fn exec_override_non_literal_value_names_the_value_not_a_grammar_fault() { + let (out, code) = run_code(&["exec", "examples/r_sma.json", "--override", "fast.length=abc"]); + assert_eq!(code, Some(2), "stdout/stderr: {out}"); + assert!(out.contains("abc"), "the offending value must be named: {out}"); + assert!( + !out.contains("NODE.PARAM=VALUE"), + "must not misreport a non-literal value as a NODE.PARAM=VALUE shape violation: {out}" + ); +} + /// An override path naming no param of the blueprint (open or bound) refuses /// with `override_paths`' own prose (`member.rs:768-771`), pointing at the /// live axis-discovery verb (`aura graph introspect --params`) — the retired diff --git a/docs/authoring-guide.md b/docs/authoring-guide.md index 944db19..7d55ea3 100644 --- a/docs/authoring-guide.md +++ b/docs/authoring-guide.md @@ -248,10 +248,17 @@ are dotted `.` on both sides of a wire. Omitting `name` is fine for a single strategy, but it has three live consequences once a project has more than one: every unnamed store document is indistinguishable by name (`"graph"` again), every unnamed strategy's -default tap recording lands in the same shared `traces/graph/` directory -(later runs overwrite earlier ones), and two `use`-splices of unnamed -blueprints collide on the same default instance identifier (`graph`) inside -the same composing script. A one-line `name` op dissolves all three at once. +default tap recording lands in the same shared `traces/graph/` directory (a +second unnamed strategy's run overwrites the first's), and two `use`-splices +of unnamed blueprints collide on the same default instance identifier +(`graph`) inside the same composing script. A one-line `name` op dissolves +the first and third of these — giving each blueprint its own distinct name +stops *different* blueprints from colliding with each other. It does NOT by +itself dissolve the second: two runs of the *identical*, now uniquely-named +blueprint still share one `traces//` directory, so a later run still +overwrites an earlier one's trace — naming separates blueprints from each +other, not a blueprint's own repeated runs from each other (that trace-dir +question is tracked separately, not resolved here). Value forms are the typed-tag representations used everywhere in this family of artifacts: @@ -797,8 +804,8 @@ campaign document valid (executable): pipeline shape and static guards pass $ aura campaign register mra_3_campaign_full_v2.json registered campaign 42edebd2159de708009ba21e1ed4aea2cffabc373bf761c5765f79c190b677bd (…/runs/campaigns/42ed…json) $ aura exec 42edebd2159de708009ba21e1ed4aea2cffabc373bf761c5765f79c190b677bd -{"family_id":"42edebd2-0-GER40-w0-s0-0","report":{...}} -{"family_id":"42edebd2-0-GER40-w0-s0-0-r1","report":{...}} +{"family_id":"42edebd2-0-GER40-w0-r0-s0-0","report":{...}} +{"family_id":"42edebd2-0-GER40-w0-r1-s0-0","report":{...}} … {"campaign_run":{"campaign":"42edebd2…","process":"cd9127…","run":0,"seed":42, "cells":[{"strategy":"597d719b…","instrument":"GER40","window_ms":[...], @@ -817,10 +824,12 @@ $ aura exec 42edebd2159de708009ba21e1ed4aea2cffabc373bf761c5765f79c190b677bd ``` With two stop regimes every (instrument, window) cell runs twice — the four -cells above are the "4 cell(s)" the validate summary counted. The second -regime's family ids and trace dirs carry the `-r1` ordinal suffix (the -default/first regime stays unsuffixed), each cell record names its regime, -and generalization is keyed per regime — regimes are compared, never pooled. +cells above are the "4 cell(s)" the validate summary counted. The regime +ordinal is an INFIX segment, `-r{k}`, right after the window segment +(`{id8}-{ordinal}-{instrument}-w{k}-r{k}-s{seed}-{member}`) — the +default/first regime carries `-r0` explicitly, never left unmarked — each +cell record names its regime, and generalization is keyed per regime — +regimes are compared, never pooled. `aura exec` (#319) is register-then-run sugar for a `.json` file, but the canonical address is always the content id — running a bare file the first