audit: cycle-close tidy for #277 — preflight duplicate refusal, zero-bound pin, C1 realization note
Resolutions for the architect's four drift items (all fix/document, none ratified away): - [medium] execute enforced family-name uniqueness only via the CLI's validate tier: preflight now refuses duplicate campaign instruments itself (defense in depth for direct callers), RED-first (execute_refuses_duplicate_instruments). - [medium] the parallel cell loop's C1 relationship lived only in the git-ignored spec: C1 gains a realization note (docs/design/INDEX.md) recording the chunked instrument-major schedule, the structural residency bound, and the two scheduling-dependent fatal-path carve-outs (fault attribution among completed cells; already-written family lines) — both inert and outside the success-path bit-identity. - [low] the fatal-path orphan-line honesty is part of that note. - [low] the --parallel-instruments zero-reject acceptance criterion had no protecting test: campaign_run_rejects_a_zero_parallel_instruments_bound pins clap's NonZeroUsize usage error (exit 2). Gates re-verified: workspace suite green, clippy -D warnings clean. refs #277
This commit is contained in:
@@ -237,6 +237,21 @@ pub fn preflight(process: &ProcessDoc, campaign: &CampaignDoc) -> Result<(), Exe
|
||||
StageBlock::Generalize { .. } => "std::generalize",
|
||||
}
|
||||
}
|
||||
// Duplicate instruments enumerate identical cells whose registry family
|
||||
// names collide (the name embeds the raw instrument string), racing one
|
||||
// name's run-index assignment under the parallel cell loop — refused here
|
||||
// as defense in depth beside the CLI's validate tier (#277 audit tidy).
|
||||
let mut seen_instruments = std::collections::BTreeSet::new();
|
||||
for instrument in &campaign.data.instruments {
|
||||
if !seen_instruments.insert(instrument.as_str()) {
|
||||
return Err(ExecFault::PipelineShape {
|
||||
detail: format!(
|
||||
"campaign.data.instruments lists \"{instrument}\" more than once — \
|
||||
duplicate instruments collide on registry family names"
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
// shape: `std::sweep (std::gate)* (std::walk_forward)? (std::monte_carlo)?
|
||||
// (std::generalize)?` — a monotone rank walk over adjacent pairs captures
|
||||
// every violation: a rank drop is an out-of-order stage (an annotator
|
||||
|
||||
@@ -1177,3 +1177,25 @@ fn member_faults_stay_contained_in_the_parallel_loop() {
|
||||
assert!(out.record.cells.iter().all(|c| c.fault.is_some()));
|
||||
assert_eq!(reg.load_campaign_runs().expect("load runs").len(), 1);
|
||||
}
|
||||
|
||||
/// Defense in depth (audit tidy, #277): `execute` itself refuses duplicate
|
||||
/// instruments via `preflight`, independent of the CLI's validate tier — a
|
||||
/// direct caller skipping `validate_campaign` must not reach the cell loop
|
||||
/// with colliding registry family names.
|
||||
#[test]
|
||||
fn execute_refuses_duplicate_instruments() {
|
||||
let reg = temp_registry("duplicate_instruments");
|
||||
let doc = campaign(&["AAA", "AAA"]);
|
||||
let proc_doc = process(vec![sweep_stage(false)]);
|
||||
let err = execute(
|
||||
CAMPAIGN_ID,
|
||||
&doc,
|
||||
&proc_doc,
|
||||
&strategies(),
|
||||
&FakeRunner::clean(),
|
||||
®,
|
||||
DEFAULT_PARALLEL_INSTRUMENTS,
|
||||
)
|
||||
.expect_err("duplicate instruments collide on family names");
|
||||
assert!(matches!(err, ExecFault::PipelineShape { .. }));
|
||||
}
|
||||
|
||||
@@ -4075,3 +4075,12 @@ fn campaign_over_a_gapped_archive_records_the_uncovered_cell_and_continues() {
|
||||
"the campaign-run summary names the one failed cell: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Acceptance pin (#277): `--parallel-instruments` rejects 0 at parse time —
|
||||
/// the NonZeroUsize carrier makes a zero residency bound unrepresentable, so
|
||||
/// clap refuses it as a usage error (exit 2) before any target resolution.
|
||||
#[test]
|
||||
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}");
|
||||
}
|
||||
|
||||
@@ -125,6 +125,25 @@ same cell re-run under `aura generalize`) may differ by floating-point reassocia
|
||||
operation order (IEEE-754 non-associativity). This is not a C1 violation: C1 governs
|
||||
the determinism of a single run, not the cross-command bit-identity of a re-derived
|
||||
statistic.
|
||||
**Realization note (2026-07-16, #277).** Cross-sim parallelism now also spans
|
||||
campaign cells: the executor flattens the cell matrix, groups it by instrument
|
||||
ordinal, and walks sequential chunks of K instrument groups
|
||||
(`--parallel-instruments`, default 4 — a structural bound on distinct resident
|
||||
instruments, the RAM lever for the external data-server's per-reference file
|
||||
retention); within a chunk, cells run concurrently on the process-global rayon
|
||||
pool shared with the member/window fan-out. Results are collected into
|
||||
document-order slots, so outputs stay byte-identical across worker counts and
|
||||
bounds. Two deliberate scheduling-dependent carve-outs, both outside this
|
||||
contract's per-run bit-identity (which governs successful runs): on the
|
||||
run-fatal path (non-containable faults, e.g. a dead registry store) the
|
||||
propagated fault is the lowest document-order fault among the cells that
|
||||
completed before the abort flag latched, and the set of per-cell family lines
|
||||
already written by then is scheduling-dependent — inert, because no
|
||||
campaign-run record is written on that path and store reads are name-keyed,
|
||||
never line-ordered. Duplicate campaign instruments are refused at both the
|
||||
validate tier and the executor's preflight: the per-cell family name embeds
|
||||
the raw instrument string, so uniqueness is what keeps concurrent appends from
|
||||
racing one name's run-index assignment.
|
||||
**Forbids.** Concurrency *within* a single sim; any nondeterministic input that
|
||||
is not captured as an explicit input (see C11, C12).
|
||||
**Why.** Real money rides on backtest results; reproducibility and an audit
|
||||
|
||||
Reference in New Issue
Block a user