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:
@@ -221,21 +221,16 @@ const GITIGNORE_PROJECT: &str = "/runs\n";
|
||||
|
||||
const SIGNAL_JSON_STD: &str = r#"{"format_version":1,"blueprint":{"name":"__NAME_SNAKE___signal","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}"#;
|
||||
|
||||
/// The open sibling of `SIGNAL_JSON_STD`: `fast.length` is a free knob, so
|
||||
/// the scaffolded sweep quickstart works out of the box (`run` requires a
|
||||
/// closed blueprint; `sweep` requires open knobs — one file cannot serve
|
||||
/// both, #241).
|
||||
const SIGNAL_OPEN_JSON_STD: &str = r#"{"format_version":1,"blueprint":{"name":"__NAME_SNAKE___signal_open","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}"#;
|
||||
|
||||
const CLAUDE_MD_PROJECT: &str = r#"# __NAME__ — an aura research project (data-only)
|
||||
|
||||
This directory is an aura project: blueprints + research documents over the
|
||||
std vocabulary, anchored by `Aura.toml`. There is no crate and no build step.
|
||||
|
||||
- Run: `aura run blueprints/signal.json` (a *closed* blueprint — every
|
||||
param bound)
|
||||
- Sweep: `aura sweep blueprints/signal_open.json --axis __NAME_SNAKE___signal_open.fast.length=2,4,8`
|
||||
(a sweep varies *open* knobs; `aura sweep <bp> --list-axes` lists them)
|
||||
- Run: `aura run blueprints/signal.json` (the starter is closed — all
|
||||
params bound; bound values are defaults — any `--axis` may override them
|
||||
(#246))
|
||||
- Sweep: `aura sweep blueprints/signal.json --axis __NAME_SNAKE___signal.fast.length=2,4,8`
|
||||
(`aura sweep <bp> --list-axes` lists the open + bound-overridable axes)
|
||||
- Native nodes: when the project needs its first project-specific node,
|
||||
`aura nodes new <name>` scaffolds a node crate beside this project and
|
||||
attaches it via `[nodes]` in `Aura.toml` (build it with `cargo build`).
|
||||
@@ -277,7 +272,6 @@ pub fn scaffold_project(spec: &ProjectScaffoldSpec) -> Result<(), String> {
|
||||
write("Aura.toml", AURA_TOML.to_string())?;
|
||||
write(".gitignore", GITIGNORE_PROJECT.to_string())?;
|
||||
write("blueprints/signal.json", render_project(SIGNAL_JSON_STD, &spec.name))?;
|
||||
write("blueprints/signal_open.json", render_project(SIGNAL_OPEN_JSON_STD, &spec.name))?;
|
||||
write("CLAUDE.md", render_project(CLAUDE_MD_PROJECT, &spec.name))?;
|
||||
if inside_git_work_tree(&spec.target_dir) {
|
||||
return Ok(());
|
||||
@@ -445,16 +439,17 @@ mod tests {
|
||||
assert!(cargo.contains("/crates/aura-core"));
|
||||
|
||||
// Project-template set (data-only tier, via `render_project`).
|
||||
for t in [GITIGNORE_PROJECT, SIGNAL_JSON_STD, SIGNAL_OPEN_JSON_STD, CLAUDE_MD_PROJECT] {
|
||||
for t in [GITIGNORE_PROJECT, SIGNAL_JSON_STD, CLAUDE_MD_PROJECT] {
|
||||
let out = render_project(t, "demo-lab");
|
||||
assert!(!out.contains("__"), "unsubstituted token in: {out}");
|
||||
}
|
||||
let claude = render_project(CLAUDE_MD_PROJECT, "demo-lab");
|
||||
assert!(claude.contains("demo-lab"));
|
||||
// The CLAUDE.md sweep quickstart targets the open sibling with the
|
||||
// rendered blueprint-name axis prefix (a bound starter cannot sweep).
|
||||
assert!(claude.contains("blueprints/signal_open.json"));
|
||||
assert!(claude.contains("demo_lab_signal_open.fast.length"));
|
||||
// The CLAUDE.md sweep quickstart targets the closed starter itself
|
||||
// with the rendered blueprint-name axis prefix (#246: a bound param
|
||||
// is a default an axis overrides).
|
||||
assert!(claude.contains("blueprints/signal.json"));
|
||||
assert!(claude.contains("demo_lab_signal.fast.length"));
|
||||
}
|
||||
|
||||
/// Property (#183, fieldtest F2 gap): the scaffold's own starter node teaches
|
||||
@@ -478,23 +473,17 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// #241: the data-only starter blueprints reference std vocabulary only —
|
||||
/// #241: the data-only starter blueprint references std vocabulary only —
|
||||
/// no project-namespaced (`::`) type id survives in the rendered JSON.
|
||||
/// The closed one binds every param (runnable); the open sibling leaves
|
||||
/// `fast.length` free (sweepable).
|
||||
/// It binds every param (runnable, #246: bound values are defaults an
|
||||
/// `--axis` may override, so this closed starter is also the sweep
|
||||
/// target).
|
||||
#[test]
|
||||
fn data_only_starter_blueprint_is_std_only() {
|
||||
let json = render_project(SIGNAL_JSON_STD, "demo-lab");
|
||||
assert!(!json.contains("__"), "unsubstituted token: {json}");
|
||||
assert!(!json.contains("::"), "project-namespaced id in std-only blueprint: {json}");
|
||||
assert!(json.contains("\"name\":\"demo_lab_signal\""));
|
||||
let open = render_project(SIGNAL_OPEN_JSON_STD, "demo-lab");
|
||||
assert!(!open.contains("::"), "project-namespaced id in std-only blueprint: {open}");
|
||||
assert!(open.contains("\"name\":\"demo_lab_signal_open\""));
|
||||
assert!(
|
||||
open.contains(r#"{"primitive":{"type":"SMA","name":"fast"}}"#),
|
||||
"the open sibling must leave fast.length unbound: {open}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user