tidy: fieldtest follow-ups — domain prose for the zero bound, gate-emptied exit semantics documented

The two fieldtest findings, resolved:

- friction: --parallel-instruments 0 now refuses in domain terms ("must be
  a whole number of at least 1 — it bounds how many distinct instruments
  are resident in parallel") via a custom clap value-parser, instead of
  leaking Rust's NonZeroUsize wording; the zero-bound test pins the prose.
- spec_gap: the authoring guide's exit-code contract now states that a
  gate-emptied cell (a std::gate leaving no survivors) is a successful
  cell — exit 0 with an informational aura: note and a truncated
  realization — and that exit 3 is reserved for cells that could not be
  evaluated. Records observed behaviour; no code change.

Gates re-verified: workspace suite green, clippy -D warnings clean.

refs #277
This commit is contained in:
2026-07-16 15:34:57 +02:00
parent 9add426a93
commit bfb8648925
3 changed files with 25 additions and 2 deletions
+16 -1
View File
@@ -60,6 +60,17 @@ impl DocIntrospectCmd {
}
}
/// Parse `--parallel-instruments` in domain terms: clap's built-in
/// `NonZeroUsize` parser would leak the Rust type name ("number would be
/// zero for non-zero type") at exactly the moment a user needs guidance.
fn parse_parallel_instruments(s: &str) -> Result<std::num::NonZeroUsize, String> {
s.parse::<usize>().ok().and_then(std::num::NonZeroUsize::new).ok_or_else(|| {
"must be a whole number of at least 1 — it bounds how many distinct \
instruments are resident in parallel"
.to_string()
})
}
/// Exactly one introspect mode (the graph-introspect guard idiom: usage
/// error on stderr, exit 2).
fn guard_one_mode(cmd: &DocIntrospectCmd, family: &str) {
@@ -320,7 +331,11 @@ pub enum CampaignSub {
target: String,
/// Bound on distinct instruments resident in parallel (the RAM
/// lever; 1 reproduces the sequential loop's footprint).
#[arg(long, default_value_t = aura_campaign::DEFAULT_PARALLEL_INSTRUMENTS)]
#[arg(
long,
default_value_t = aura_campaign::DEFAULT_PARALLEL_INSTRUMENTS,
value_parser = parse_parallel_instruments,
)]
parallel_instruments: std::num::NonZeroUsize,
},
/// List stored campaign realizations, or dump one campaign's records
+3
View File
@@ -4083,4 +4083,7 @@ fn campaign_over_a_gapped_archive_records_the_uncovered_cell_and_continues() {
fn campaign_run_rejects_a_zero_parallel_instruments_bound() {
let (out, code) = run_code(&["campaign", "run", "unused", "--parallel-instruments", "0"]);
assert_eq!(code, Some(2), "clap usage error expected, got: {out}");
// Domain prose, not clap's raw NonZeroUsize wording ("number would be
// zero for non-zero type") — the fieldtest friction finding.
assert!(out.contains("at least 1"), "domain guidance expected, got: {out}");
}
+6 -1
View File
@@ -609,4 +609,9 @@ Exit codes: a clean run exits 0; a usage error exits 2; a refusal before any
cell runs (invalid document, missing project, unresolvable strategy) exits 1;
a run that **completes with one or more failed cells** exits 3 — the run record
and every healthy cell persist, and the failed cells are named on stderr
(#272).
(#272). A **gate-emptied cell** — a `std::gate` stage that filters out every
member — is a *successful* cell, not a failed one: the gate legitimately
answered "no survivors", so the run still exits 0, the cell's realization is
recorded as truncated at that stage, and an informational `aura:`-prefixed
note names the cell on stderr. Exit 3 is reserved for cells that could not be
evaluated (faults), never for an empty-but-valid result.