feat(cli): the scaffold ships one starter blueprint
Task 6 of the bound-override cycle: SIGNAL_OPEN_JSON_STD and its write retire — aura new scaffolds signal.json alone, and the project CLAUDE.md quickstart advertises run AND sweep against that one file (the sweep line overrides the bound fast.length; --list-axes shows open + bound axes). The data-only e2e loop proves it end to end: scaffold, run the closed starter, sweep it via an override axis, and assert signal.json is the only starter blueprint. The authoring-guide's starter-pair sentence is retargeted; the last signal_open reference outside the unrelated #196 fixture string is gone. refs #246
This commit is contained in:
@@ -79,11 +79,15 @@ fn new_scaffolds_and_runs_the_data_only_loop() {
|
||||
"Aura.toml",
|
||||
".gitignore",
|
||||
"blueprints/signal.json",
|
||||
"blueprints/signal_open.json",
|
||||
"CLAUDE.md",
|
||||
] {
|
||||
assert!(proj.join(f).is_file(), "missing scaffolded file {f}");
|
||||
}
|
||||
assert_eq!(
|
||||
std::fs::read_dir(proj.join("blueprints")).unwrap().count(),
|
||||
1,
|
||||
"blueprints/signal.json must be the ONLY starter blueprint (#246)"
|
||||
);
|
||||
assert!(!proj.join("Cargo.toml").exists(), "data-only project must have no Cargo.toml");
|
||||
assert!(!proj.join("src/lib.rs").exists(), "data-only project must have no src/lib.rs");
|
||||
|
||||
@@ -108,15 +112,11 @@ fn new_scaffolds_and_runs_the_data_only_loop() {
|
||||
}
|
||||
|
||||
/// #241 headline: a fresh data-only project sweeps a dissolved verb (#218's
|
||||
/// gate) with zero build step — using the scaffolded open starter blueprint
|
||||
/// and the exact axis the scaffolded CLAUDE.md advertises.
|
||||
/// gate) with zero build step — using the scaffolded (closed) starter
|
||||
/// blueprint and the exact axis the scaffolded CLAUDE.md advertises.
|
||||
///
|
||||
/// Why a second starter blueprint exists at all: `aura run` requires a
|
||||
/// CLOSED blueprint (every param bound) while `aura sweep` requires OPEN
|
||||
/// knobs ("this blueprint is fully bound; nothing to sweep"), so one file
|
||||
/// cannot serve both quickstart lines. `blueprints/signal.json` stays closed
|
||||
/// for the run headline above; `blueprints/signal_open.json` leaves
|
||||
/// `fast.length` free for this one.
|
||||
/// #246: the closed starter IS the sweep target — a bound param is a default
|
||||
/// an axis overrides.
|
||||
#[test]
|
||||
fn data_only_project_sweeps_without_any_build() {
|
||||
let base = tmp("sweep");
|
||||
@@ -124,7 +124,7 @@ fn data_only_project_sweeps_without_any_build() {
|
||||
assert!(new.status.success(), "{}", String::from_utf8_lossy(&new.stderr));
|
||||
let proj = base.join("scratch");
|
||||
let out = aura(
|
||||
&["sweep", "blueprints/signal_open.json", "--axis", "scratch_signal_open.fast.length=2,4"],
|
||||
&["sweep", "blueprints/signal.json", "--axis", "scratch_signal.fast.length=2,4"],
|
||||
&proj,
|
||||
);
|
||||
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
|
||||
@@ -132,6 +132,36 @@ fn data_only_project_sweeps_without_any_build() {
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
|
||||
/// Property (#246): the scaffold's sweep quickstart genuinely re-opens the
|
||||
/// bound `fast.length` param — the persisted family has exactly one member
|
||||
/// per axis value, not a single collapsed member. A silent regression where
|
||||
/// the bound-param override is dropped (member always built from the
|
||||
/// blueprint's own default) would still exit 0 and still create a `runs`
|
||||
/// store, so `data_only_project_sweeps_without_any_build` above cannot catch
|
||||
/// it; only the member count can.
|
||||
#[test]
|
||||
fn data_only_project_sweep_over_the_starter_opens_one_member_per_axis_value() {
|
||||
let base = tmp("sweep-members");
|
||||
let new = aura(&["new", "scratch"], &base);
|
||||
assert!(new.status.success(), "{}", String::from_utf8_lossy(&new.stderr));
|
||||
let proj = base.join("scratch");
|
||||
let out = aura(
|
||||
&["sweep", "blueprints/signal.json", "--axis", "scratch_signal.fast.length=2,4,8"],
|
||||
&proj,
|
||||
);
|
||||
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
|
||||
let fams = aura(&["runs", "families"], &proj);
|
||||
assert!(fams.status.success(), "runs families exit: {:?}", fams.status);
|
||||
let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned();
|
||||
assert_eq!(fams_out.lines().count(), 1, "exactly one family persisted: {fams_out}");
|
||||
assert!(
|
||||
fams_out.contains("\"members\":3"),
|
||||
"the three-value bound-param axis grid must yield three members, one per \
|
||||
value (2,4,8) — not a collapsed default-only sweep: {fams_out}"
|
||||
);
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_refuses_an_existing_destination() {
|
||||
let base = tmp("exists");
|
||||
@@ -186,11 +216,15 @@ fn new_inside_a_work_tree_skips_the_nested_git() {
|
||||
"Aura.toml",
|
||||
".gitignore",
|
||||
"blueprints/signal.json",
|
||||
"blueprints/signal_open.json",
|
||||
"CLAUDE.md",
|
||||
] {
|
||||
assert!(proj.join(f).is_file(), "missing scaffolded file {f}");
|
||||
}
|
||||
assert_eq!(
|
||||
std::fs::read_dir(proj.join("blueprints")).unwrap().count(),
|
||||
1,
|
||||
"blueprints/signal.json must be the ONLY starter blueprint (#246)"
|
||||
);
|
||||
// ...but without a nested repo of its own.
|
||||
assert!(
|
||||
!proj.join(".git").exists(),
|
||||
|
||||
Reference in New Issue
Block a user