fix(cli): aura graph rejects a bad blueprint arg, not a silent sample (#28)

The #28 cycle-close audit found aura graph <file> silently rendered the embedded
sample at exit 0 when the positional named an unreadable blueprint (typo,
nonexistent path, wrong extension): is_blueprint_file returns None both for "no
positional" and for a bad arg, and dispatch_graph's None arm conflated them —
re-introducing the exact #28 "I asked for my graph, got the sample" surprise in
the error path, and diverging from every sibling verb (a bad blueprint arg is a
usage fault, exit 2, per C14). Split the None arm: no positional -> the sample
(unchanged); a positional that is not a readable .json -> a usage error naming
the arg, exit 2, mirroring dispatch_run.

Also close the paired doc drift the audit flagged: the README blueprint-verb
table and the authoring guide now document the aura graph <file> render surface,
absent while graph was the last sample-bound verb.

refs #28
This commit is contained in:
2026-07-10 04:10:02 +02:00
parent 692760d3b0
commit 962b249814
3 changed files with 17 additions and 4 deletions
+4 -2
View File
@@ -43,8 +43,9 @@ Invoke it as `aura <command> …` (examples below use the plain name).
## Running & orchestrating a loaded blueprint
These verbs all take the **blueprint file as their first positional argument**
and drive a family of runs over it. `walkforward`, `mc`, and `generalize` share
These verbs all take the **blueprint file as their first positional argument**.
Most drive a family of runs over it; `graph` renders its structure so a mis-wire
is visible before a run. `walkforward`, `mc`, and `generalize` share
`sweep`'s generic grammar — the blueprint positional plus `--real` and a
repeatable `--axis <name>=<csv>` (`generalize` takes `--real <SYM1,SYM2,…>`, at
least two instruments, with a single value per axis) — see `--help`.
@@ -52,6 +53,7 @@ least two instruments, with a single value per axis) — see `--help`.
| Command | Purpose |
|---|---|
| `aura run <bp.json>` | Run one backtest of a **closed** blueprint and print its report. An open (free-knob) blueprint is refused with a clean error — bind it, or use `sweep`. |
| `aura graph <bp.json>` | **Render** the blueprint's structure as an interactive HTML DAG so a mis-wire is visible before a run. Omit the file to render the built-in sample; a named-but-unreadable file is a usage error. |
| `aura sweep <bp.json> --list-axes` | **Discover** the blueprint's open, sweepable knobs. Prints each as `<name>:<kind>`. Run this first to learn the axis names. |
| `aura sweep <bp.json> --axis <name>=<csv> [--axis …]` | Run a **grid family** over the named axes and persist it. |
| `aura mc <bp.json> --seeds <n>` | Run a **synthetic Monte-Carlo family** of `n` seeded realizations. Wants a **closed** blueprint (the inverse of sweep). |
+9 -1
View File
@@ -2685,7 +2685,7 @@ fn dispatch_graph(a: GraphCmd, env: &project::Env) {
});
print!("{}", render::render_html(&bp));
}
None => {
None if a.blueprint.is_none() => {
let bp = blueprint_from_json(
include_str!("../examples/r_sma_open.json"),
&|t| env.resolve(t),
@@ -2693,6 +2693,14 @@ fn dispatch_graph(a: GraphCmd, env: &project::Env) {
.expect("the shipped r-sma example reloads into a renderable blueprint");
print!("{}", render::render_html(&bp));
}
None => {
eprintln!(
"aura: Usage: aura graph [<blueprint.json>] [build|introspect|register]; \
{} is not a readable .json blueprint",
a.blueprint.as_deref().unwrap_or_default()
);
std::process::exit(2);
}
},
Some(GraphSub::Build) => graph_construct::build_cmd(env),
Some(GraphSub::Introspect(i)) => graph_construct::introspect_cmd(i, env),
+4 -1
View File
@@ -86,7 +86,10 @@ $ aura graph build < smacross.json
"input_roles":[{"name":"price",...,"source":"F64"}],"output":[...]}}
```
Introspection is build-free wherever possible:
The built blueprint renders visually, too: `aura graph blueprint.json` emits an
interactive HTML DAG so a mis-wire is visible before any run (`aura graph` with
no file renders the built-in sample; a named-but-unreadable file is a usage
error). Introspection is build-free wherever possible:
```
$ aura graph introspect --vocabulary # one node type per line