feat: the document-first surface closes — show read-back, typed stops, bare plateau
The #300 keystone cycle: the residual run-quintet flags were already document vocabulary on the --real arm (#210/#220); this closes the loop and ratifies the surface. - `aura process|campaign show <content-id>` prints a registered document's canonical bytes (print!, no framing — #164), so generate -> retrieve -> hand-extend -> re-register needs no direct store filesystem access. Refusals reuse the store-hint prose, exit 1. - walkforward/mc --stop-length/--stop-k become clap-typed i64/f64 (generalize's form); stop_knob_or and its parse_csv_list helper are deleted (multi-value/invalid input is now clap's exit-2 rejection — the two dissolved-verb refusal tests retarget, the type-dead unit test is deleted). - --select accepts bare `plateau` as the documented default plateau:mean (#227 carry); document schema untouched — the two spellings generate content-id-identical campaigns (test-pinned). - family.rs's quadruplicated demo stop literal collapses onto one DEFAULT_STOP const (maintainer-lens drift finding). - Ledger: C25's "which projection next" line resolved (document-first delivered, per-verb identity re-ratified — F8; host/MCP stay demand-driven); C18 records the read-back. Design triage, fork decisions F1-F8, and the auto-signed spec live on the reference issue. Verification: full workspace suite 1482 passed / 0 failed with the real GER40 archive exercised (the byte-identity round-trips ran, not skipped); clippy -D warnings clean. closes #300
This commit is contained in:
@@ -3220,7 +3220,7 @@ fn walkforward_dissolved_refuses_a_multi_value_stop() {
|
||||
.expect("spawn aura");
|
||||
assert_eq!(out.status.code(), Some(2), "multi-value stop is a usage refusal");
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(stderr.contains("single risk regime"), "stop-regime refusal: {stderr}");
|
||||
assert!(stderr.contains("invalid value"), "stop-regime refusal: {stderr}");
|
||||
}
|
||||
|
||||
/// #215: --select plateau is no longer refused on the dissolved path — it now
|
||||
@@ -3647,6 +3647,96 @@ fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() {
|
||||
);
|
||||
}
|
||||
|
||||
/// #227: bare `--select plateau` is the documented default aggregation
|
||||
/// (`plateau:mean`), not a distinct spelling. Same dedup-pin idiom as
|
||||
/// `walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2`:
|
||||
/// running the same grid-sweep blueprint in the same store first with
|
||||
/// `--select plateau`, then with `--select plateau:mean`, must produce
|
||||
/// byte-identical campaign documents — content-addressed storage collapses
|
||||
/// them onto EXACTLY ONE stored campaign — while each invocation still
|
||||
/// records its own campaign-run line. Gated on the shared GER40 archive;
|
||||
/// skips cleanly on a data refusal.
|
||||
#[test]
|
||||
fn walkforward_bare_plateau_generates_the_same_campaign_as_plateau_mean() {
|
||||
const FROM_MS: &str = "1735689600000";
|
||||
const TO_MS: &str = "1747353600000";
|
||||
let (cwd, _g) = fresh_project();
|
||||
let fixture = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
|
||||
// Bare `plateau`: today this is an unknown-token usage refusal (exit 2);
|
||||
// the feature makes it an alias for `plateau:mean`.
|
||||
let bare = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args([
|
||||
"walkforward", &fixture, "--real", "GER40",
|
||||
"--axis", "sma_signal.fast.length=3,5", "--axis", "sma_signal.slow.length=12,20",
|
||||
"--stop-length", "14", "--stop-k", "2.0",
|
||||
"--select", "plateau",
|
||||
"--from", FROM_MS, "--to", TO_MS,
|
||||
])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura");
|
||||
if bare.status.code() == Some(1) {
|
||||
let stderr = String::from_utf8_lossy(&bare.stderr);
|
||||
assert!(
|
||||
stderr.contains("no local data") || stderr.contains("no recorded geometry"),
|
||||
"exit 1 must be a data refusal, got: {stderr}"
|
||||
);
|
||||
eprintln!("skip: no local GER40 data");
|
||||
return;
|
||||
}
|
||||
assert_eq!(
|
||||
bare.status.code(),
|
||||
Some(0),
|
||||
"bare --select plateau must no longer be refused (it aliases plateau:mean): {}",
|
||||
String::from_utf8_lossy(&bare.stderr)
|
||||
);
|
||||
|
||||
// Explicit `plateau:mean`: names the very aggregation the bare spelling
|
||||
// resolves to. Same blueprint, same axes, same window, same stop.
|
||||
let explicit = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args([
|
||||
"walkforward", &fixture, "--real", "GER40",
|
||||
"--axis", "sma_signal.fast.length=3,5", "--axis", "sma_signal.slow.length=12,20",
|
||||
"--stop-length", "14", "--stop-k", "2.0",
|
||||
"--select", "plateau:mean",
|
||||
"--from", FROM_MS, "--to", TO_MS,
|
||||
])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura");
|
||||
assert_eq!(
|
||||
explicit.status.code(),
|
||||
Some(0),
|
||||
"the explicit --select plateau:mean walkforward must succeed: {}",
|
||||
String::from_utf8_lossy(&explicit.stderr)
|
||||
);
|
||||
|
||||
// Byte-identity: bare `plateau` IS `plateau:mean`, so both invocations
|
||||
// generate the same campaign document and content-addressed storage
|
||||
// collapses them onto one content id — exactly one campaign document in
|
||||
// the store, but two independent campaign-run lines.
|
||||
let count = |sub: &str| {
|
||||
std::fs::read_dir(cwd.join("runs").join(sub))
|
||||
.map(|d| d.count())
|
||||
.unwrap_or(0)
|
||||
};
|
||||
assert_eq!(
|
||||
count("campaigns"),
|
||||
1,
|
||||
"bare plateau binds the SAME aggregation as plateau:mean, so the two campaign \
|
||||
documents are byte-identical and dedup to one content id"
|
||||
);
|
||||
let runs_log = std::fs::read_to_string(cwd.join("runs").join("campaign_runs.jsonl"))
|
||||
.expect("campaign_runs.jsonl exists after two sugar runs");
|
||||
assert_eq!(
|
||||
runs_log.lines().count(),
|
||||
2,
|
||||
"each invocation still records its own campaign run (a run is an event, a document \
|
||||
is content)"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property: `aura generalize` is now thin sugar over the one campaign path — a
|
||||
/// successful run durably auto-registers exactly one generated process document,
|
||||
/// one generated campaign document (carrying the `--name` handle and the stop as
|
||||
@@ -5665,7 +5755,7 @@ fn mc_dissolved_refuses_a_multi_value_stop() {
|
||||
.expect("spawn aura");
|
||||
assert_eq!(out.status.code(), Some(2), "multi-value stop is a usage refusal");
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(stderr.contains("single risk regime"), "stop-regime refusal: {stderr}");
|
||||
assert!(stderr.contains("invalid value"), "stop-regime refusal: {stderr}");
|
||||
}
|
||||
|
||||
/// Property: an `--axis` name that is not among the loaded blueprint's sweepable
|
||||
@@ -6286,3 +6376,334 @@ fn data_list_enumerates_archive_symbols_one_per_line_sorted() {
|
||||
// scoping an instrument matrix.
|
||||
assert_eq!(stdout, "SYMA\nSYMB\n", "sorted symbol enumeration: {stdout:?}");
|
||||
}
|
||||
|
||||
/// Property (#300): `aura campaign show <CID>` prints the registered
|
||||
/// document's exact canonical bytes to stdout — round-tripping them back
|
||||
/// through `aura campaign register` re-derives the SAME content id (canonical
|
||||
/// bytes are content-id-identical), and the store stays deduplicated at one
|
||||
/// document. Gated on the shared GER40 archive; skips cleanly on a data
|
||||
/// refusal (same guard shape as
|
||||
/// `walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2`).
|
||||
#[test]
|
||||
fn campaign_show_round_trips_the_registered_document() {
|
||||
const FROM_MS: &str = "1735689600000";
|
||||
const TO_MS: &str = "1767225599000";
|
||||
let (cwd, _g) = fresh_project();
|
||||
let fixture = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
|
||||
let run = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args([
|
||||
"walkforward", &fixture, "--real", "GER40",
|
||||
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
|
||||
"--select", "plateau:mean",
|
||||
"--from", FROM_MS, "--to", TO_MS,
|
||||
])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura");
|
||||
if run.status.code() == Some(1) {
|
||||
let stderr = String::from_utf8_lossy(&run.stderr);
|
||||
assert!(
|
||||
stderr.contains("no local data") || stderr.contains("no recorded geometry"),
|
||||
"exit 1 must be a data refusal, got: {stderr}"
|
||||
);
|
||||
eprintln!("skip: no local GER40 data");
|
||||
return;
|
||||
}
|
||||
assert_eq!(
|
||||
run.status.code(),
|
||||
Some(0),
|
||||
"walkforward must succeed: {}",
|
||||
String::from_utf8_lossy(&run.stderr)
|
||||
);
|
||||
|
||||
let campaigns_dir = cwd.join("runs").join("campaigns");
|
||||
let cid = std::fs::read_dir(&campaigns_dir)
|
||||
.expect("campaigns dir exists")
|
||||
.next()
|
||||
.expect("exactly one campaign document")
|
||||
.expect("readable dir entry")
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("campaign filename has a stem")
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
|
||||
let show = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["campaign", "show", &cid])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura campaign show");
|
||||
assert_eq!(
|
||||
show.status.code(),
|
||||
Some(0),
|
||||
"campaign show must succeed: {}",
|
||||
String::from_utf8_lossy(&show.stderr)
|
||||
);
|
||||
|
||||
// Directly pin the #164 no-framing invariant: `show` must print the
|
||||
// stored canonical bytes byte-for-byte, with no added trailing newline
|
||||
// (a `println!` regression in `show_campaign` would still pass the
|
||||
// re-register check below via `campaign_to_json` -> `content_id_of`
|
||||
// stripping it back out before hashing, so this comparison is the only
|
||||
// thing that would catch it).
|
||||
let stored_bytes = std::fs::read(campaigns_dir.join(format!("{cid}.json"))).expect("read stored campaign document");
|
||||
assert_eq!(
|
||||
show.stdout, stored_bytes,
|
||||
"campaign show must print the stored canonical bytes byte-for-byte, no framing"
|
||||
);
|
||||
|
||||
let roundtrip_path = cwd.join("roundtrip.campaign.json");
|
||||
std::fs::write(&roundtrip_path, &show.stdout).expect("write roundtrip campaign doc");
|
||||
|
||||
let register = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["campaign", "register", roundtrip_path.to_str().expect("utf-8 path")])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura campaign register");
|
||||
assert_eq!(
|
||||
register.status.code(),
|
||||
Some(0),
|
||||
"re-registering the shown document must succeed: {}",
|
||||
String::from_utf8_lossy(®ister.stderr)
|
||||
);
|
||||
let register_out = String::from_utf8_lossy(®ister.stdout).into_owned();
|
||||
assert!(
|
||||
register_out.contains(&format!("registered campaign {cid}")),
|
||||
"canonical bytes must re-register content-id-identically to {cid}: {register_out}"
|
||||
);
|
||||
|
||||
let count = std::fs::read_dir(&campaigns_dir).expect("campaigns dir exists").count();
|
||||
assert_eq!(count, 1, "the shown bytes dedup onto the same one stored document");
|
||||
}
|
||||
|
||||
/// Property (#300): identical to `campaign_show_round_trips_the_registered_document`
|
||||
/// but over the generated process document — `aura process show <PID>` prints
|
||||
/// canonical bytes that re-register content-id-identically through `aura
|
||||
/// process register`, with the store staying deduplicated at one document.
|
||||
#[test]
|
||||
fn process_show_round_trips_the_registered_document() {
|
||||
const FROM_MS: &str = "1735689600000";
|
||||
const TO_MS: &str = "1767225599000";
|
||||
let (cwd, _g) = fresh_project();
|
||||
let fixture = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
|
||||
let run = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args([
|
||||
"walkforward", &fixture, "--real", "GER40",
|
||||
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
|
||||
"--select", "plateau:mean",
|
||||
"--from", FROM_MS, "--to", TO_MS,
|
||||
])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura");
|
||||
if run.status.code() == Some(1) {
|
||||
let stderr = String::from_utf8_lossy(&run.stderr);
|
||||
assert!(
|
||||
stderr.contains("no local data") || stderr.contains("no recorded geometry"),
|
||||
"exit 1 must be a data refusal, got: {stderr}"
|
||||
);
|
||||
eprintln!("skip: no local GER40 data");
|
||||
return;
|
||||
}
|
||||
assert_eq!(
|
||||
run.status.code(),
|
||||
Some(0),
|
||||
"walkforward must succeed: {}",
|
||||
String::from_utf8_lossy(&run.stderr)
|
||||
);
|
||||
|
||||
let processes_dir = cwd.join("runs").join("processes");
|
||||
let pid = std::fs::read_dir(&processes_dir)
|
||||
.expect("processes dir exists")
|
||||
.next()
|
||||
.expect("exactly one process document")
|
||||
.expect("readable dir entry")
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("process filename has a stem")
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
|
||||
let show = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["process", "show", &pid])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura process show");
|
||||
assert_eq!(
|
||||
show.status.code(),
|
||||
Some(0),
|
||||
"process show must succeed: {}",
|
||||
String::from_utf8_lossy(&show.stderr)
|
||||
);
|
||||
|
||||
// Directly pin the #164 no-framing invariant (see the sibling campaign
|
||||
// test's comment): `show` must print the stored canonical bytes
|
||||
// byte-for-byte, with no added trailing newline.
|
||||
let stored_bytes = std::fs::read(processes_dir.join(format!("{pid}.json"))).expect("read stored process document");
|
||||
assert_eq!(
|
||||
show.stdout, stored_bytes,
|
||||
"process show must print the stored canonical bytes byte-for-byte, no framing"
|
||||
);
|
||||
|
||||
let roundtrip_path = cwd.join("roundtrip.process.json");
|
||||
std::fs::write(&roundtrip_path, &show.stdout).expect("write roundtrip process doc");
|
||||
|
||||
let register = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["process", "register", roundtrip_path.to_str().expect("utf-8 path")])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura process register");
|
||||
assert_eq!(
|
||||
register.status.code(),
|
||||
Some(0),
|
||||
"re-registering the shown document must succeed: {}",
|
||||
String::from_utf8_lossy(®ister.stderr)
|
||||
);
|
||||
let register_out = String::from_utf8_lossy(®ister.stdout).into_owned();
|
||||
assert!(
|
||||
register_out.contains(&format!("registered process {pid}")),
|
||||
"canonical bytes must re-register content-id-identically to {pid}: {register_out}"
|
||||
);
|
||||
|
||||
let count = std::fs::read_dir(&processes_dir).expect("processes dir exists").count();
|
||||
assert_eq!(count, 1, "the shown bytes dedup onto the same one stored document");
|
||||
}
|
||||
|
||||
/// Property (#300): `show` over an unknown id refuses with the same
|
||||
/// project-store-not-found prose the referential tier already uses for a
|
||||
/// missing `process.ref`/`strategy.ref` (`ref_fault_prose`'s
|
||||
/// `ProcessNotFound`/campaign-equivalent hint shape) — no project run needed,
|
||||
/// so this test needs no archive gate.
|
||||
#[test]
|
||||
fn show_refuses_an_unknown_id_with_the_store_hint() {
|
||||
let (cwd, _g) = fresh_project();
|
||||
|
||||
let campaign_show = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["campaign", "show", "deadbeef"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura campaign show");
|
||||
assert_eq!(campaign_show.status.code(), Some(1), "unknown campaign id is a runtime refusal");
|
||||
let campaign_stderr = String::from_utf8_lossy(&campaign_show.stderr);
|
||||
assert!(
|
||||
campaign_stderr.contains("campaign deadbeef not found in the project store"),
|
||||
"campaign show stderr: {campaign_stderr}"
|
||||
);
|
||||
|
||||
let process_show = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["process", "show", "deadbeef"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura process show");
|
||||
assert_eq!(process_show.status.code(), Some(1), "unknown process id is a runtime refusal");
|
||||
let process_stderr = String::from_utf8_lossy(&process_show.stderr);
|
||||
assert!(
|
||||
process_stderr.contains("process deadbeef not found in the project store"),
|
||||
"process show stderr: {process_stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#300): `show`'s not-found prose reuses the existing `prefix_hint`
|
||||
/// authoring-trap detector (#194) — an id pasted WITH the display-only
|
||||
/// `content:` prefix (the form `ref_fault_prose` already prints elsewhere for
|
||||
/// an unresolved `process.ref`/`strategy.ref`) still gets the "drop the
|
||||
/// 'content:' prefix" hint through the new `show` code path, not a bare
|
||||
/// not-found. This is the one authoring mistake the store can name outright,
|
||||
/// and it must survive `show` reusing `ref_fault_prose`'s helper rather than
|
||||
/// re-deriving its own prose.
|
||||
#[test]
|
||||
fn show_hints_the_content_prefix_authoring_mistake() {
|
||||
let (cwd, _g) = fresh_project();
|
||||
|
||||
let campaign_show = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["campaign", "show", "content:deadbeef"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura campaign show");
|
||||
assert_eq!(campaign_show.status.code(), Some(1), "unresolvable id is a runtime refusal");
|
||||
let campaign_stderr = String::from_utf8_lossy(&campaign_show.stderr);
|
||||
assert!(
|
||||
campaign_stderr.contains("drop the 'content:' prefix"),
|
||||
"campaign show must hint the content: authoring mistake: {campaign_stderr}"
|
||||
);
|
||||
|
||||
let process_show = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["process", "show", "content:deadbeef"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura process show");
|
||||
assert_eq!(process_show.status.code(), Some(1), "unresolvable id is a runtime refusal");
|
||||
let process_stderr = String::from_utf8_lossy(&process_show.stderr);
|
||||
assert!(
|
||||
process_stderr.contains("drop the 'content:' prefix"),
|
||||
"process show must hint the content: authoring mistake: {process_stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#300): `show` is scoped to its own document-kind store — a
|
||||
/// content id that IS a real, registered document in one store (here: a
|
||||
/// minimal sweep-only process) does not resolve through the OTHER verb
|
||||
/// (`campaign show`). `show_process`/`show_campaign` are two near-identical
|
||||
/// functions written in the same change (both `registry.get_*(id) -> Option
|
||||
/// -> print! the bytes or format! a not-found`); this pins that they read
|
||||
/// from their own store method and neither accidentally shares the other's
|
||||
/// namespace or falls back to a generic content-blob lookup.
|
||||
#[test]
|
||||
fn show_is_scoped_to_its_own_document_store() {
|
||||
let (cwd, _g) = fresh_project();
|
||||
const SWEEP_ONLY_PROCESS: &str = r#"{"format_version":1,"kind":"process","name":"knob-sweep-only","pipeline":[{"block":"std::sweep","metric":"sqn_normalized","select":"argmax"}]}"#;
|
||||
std::fs::write(cwd.join("knob.process.json"), SWEEP_ONLY_PROCESS).expect("write process doc");
|
||||
|
||||
let register = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["process", "register", "knob.process.json"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura process register");
|
||||
assert_eq!(
|
||||
register.status.code(),
|
||||
Some(0),
|
||||
"process register must succeed: {}",
|
||||
String::from_utf8_lossy(®ister.stderr)
|
||||
);
|
||||
let register_out = String::from_utf8_lossy(®ister.stdout).into_owned();
|
||||
let pid = register_out
|
||||
.lines()
|
||||
.find(|l| l.starts_with("registered process "))
|
||||
.expect("register line")
|
||||
.trim_start_matches("registered process ")
|
||||
.split(' ')
|
||||
.next()
|
||||
.expect("process id")
|
||||
.to_string();
|
||||
|
||||
let process_show = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["process", "show", &pid])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura process show");
|
||||
assert_eq!(
|
||||
process_show.status.code(),
|
||||
Some(0),
|
||||
"process show must find the just-registered process: {}",
|
||||
String::from_utf8_lossy(&process_show.stderr)
|
||||
);
|
||||
|
||||
let campaign_show = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["campaign", "show", &pid])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura campaign show");
|
||||
assert_eq!(
|
||||
campaign_show.status.code(),
|
||||
Some(1),
|
||||
"a process id must NOT resolve through campaign show: {}",
|
||||
String::from_utf8_lossy(&campaign_show.stdout)
|
||||
);
|
||||
let campaign_stderr = String::from_utf8_lossy(&campaign_show.stderr);
|
||||
assert!(
|
||||
campaign_stderr.contains(&format!("campaign {pid} not found in the project store")),
|
||||
"campaign show stderr: {campaign_stderr}"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user