From 935e71a577f7f77e163540cb94331a0660116b33 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 00:18:53 +0000 Subject: [PATCH 1/4] jc-batch-probe: refuse selections that name a resident plane A Rel::Plane(i) selection was accepted and then failed inside the fold, because the probe passes no resident masks. Both paths now refuse it up front with BatchError::ResidentPlane; resident planes are U2's let-planes, and Cost does not count plane reads. Codex review on #1643. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VncKoyG6JkpsoqhqTGTv3f --- crates/jc-batch-probe/src/lib.rs | 25 ++++++++++++++++++++++--- crates/jc-batch-probe/tests/batch.rs | 23 +++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/crates/jc-batch-probe/src/lib.rs b/crates/jc-batch-probe/src/lib.rs index e08f5541d..b406348f6 100644 --- a/crates/jc-batch-probe/src/lib.rs +++ b/crates/jc-batch-probe/src/lib.rs @@ -143,6 +143,10 @@ pub enum BatchError { /// The selection was decided from its description alone (all rows or /// none). The probe runs only selections that need a pass. SymbolicSelection { request: usize }, + /// The selection names a resident mask plane (`Rel::Plane`). A batch + /// reads only the table's lanes: resident planes are U2's let-planes, and + /// [`Cost`] does not count plane reads. + ResidentPlane { request: usize }, /// relalg refused the selection. Relalg(RelError), /// mask-risc refused a pass. @@ -211,8 +215,23 @@ fn selection_lanes(rel: &Rel, out: &mut Vec) { } } -fn check_versions(t: &Table<'_>, reqs: &[Request]) -> Result<(), BatchError> { +/// Whether a selection names a resident mask plane anywhere. +fn names_plane(rel: &Rel) -> bool { + match rel { + Rel::Plane(_) => true, + Rel::All | Rel::Empty | Rel::Atom(_) => false, + Rel::And(xs) | Rel::Or(xs) => xs.iter().any(names_plane), + Rel::Not(x) => names_plane(x), + } +} + +/// Refuses a request for another version, or one whose selection names a +/// resident plane, before anything runs. +fn check_requests(t: &Table<'_>, reqs: &[Request]) -> Result<(), BatchError> { for (i, r) in reqs.iter().enumerate() { + if names_plane(&r.select) { + return Err(BatchError::ResidentPlane { request: i }); + } if r.version != t.version { return Err(BatchError::VersionMismatch { request: i, @@ -412,7 +431,7 @@ pub fn answer_separately( t: &Table<'_>, reqs: &[Request], ) -> Result<(Vec, Cost), BatchError> { - check_versions(t, reqs)?; + check_requests(t, reqs)?; let mut cost = Cost::default(); let mut out = Vec::with_capacity(reqs.len()); for (i, r) in reqs.iter().enumerate() { @@ -455,7 +474,7 @@ pub fn answer_batched( t: &Table<'_>, reqs: &[Request], ) -> Result<(Vec, Cost, Reduction), BatchError> { - check_versions(t, reqs)?; + check_requests(t, reqs)?; let mut cost = Cost::default(); let mut red = Reduction::default(); diff --git a/crates/jc-batch-probe/tests/batch.rs b/crates/jc-batch-probe/tests/batch.rs index fc4129a0e..3a46e0dd3 100644 --- a/crates/jc-batch-probe/tests/batch.rs +++ b/crates/jc-batch-probe/tests/batch.rs @@ -430,3 +430,26 @@ fn a_lane_only_univariate_requests_need_gets_one_pass() { assert_eq!((red.cross_passes, red.power_passes), (1, 1)); assert_eq!(cost.sum_passes, 2); } + +/// FAILS IF: a selection that names a resident mask plane, alone or inside a +/// conjunction, is run instead of refused. Neither path has resident planes, +/// so running it could only fail later inside the fold. +#[test] +fn a_selection_naming_a_resident_plane_is_refused() { + let fx = Fixture::new(500, 0x91A7); + let lanes = fx.lanes(); + let t = fx.table(&lanes); + for (k, select) in [ + Rel::Plane(0), + Rel::And(vec![low(), Rel::Not(Box::new(Rel::Plane(1)))]), + ] + .into_iter() + .enumerate() + { + let mut reqs = requests(); + reqs[5 + k].select = select; + let want = Err(BatchError::ResidentPlane { request: 5 + k }); + assert_eq!(answer_batched(&t, &reqs).map(|_| ()), want); + assert_eq!(answer_separately(&t, &reqs).map(|_| ()), want); + } +} From 3321f571013d898882a92214ae1f53185e0628b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 00:19:06 +0000 Subject: [PATCH 2/4] board: U1 entry records the resident-plane refusal and its disables Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VncKoyG6JkpsoqhqTGTv3f --- .claude/board/entries/2026-10-20-u1-jc-batch.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.claude/board/entries/2026-10-20-u1-jc-batch.md b/.claude/board/entries/2026-10-20-u1-jc-batch.md index bc4770185..249d9ebb7 100644 --- a/.claude/board/entries/2026-10-20-u1-jc-batch.md +++ b/.claude/board/entries/2026-10-20-u1-jc-batch.md @@ -11,6 +11,7 @@ - A reversed pair swaps the sides. - Statistics: mean, variance, one-way ANOVA, covariance, Pearson, R², and the simple regression line in both directions. - Both paths refuse a request built for another version (`BatchError::VersionMismatch`). +- Both paths refuse a selection that names a resident mask plane (`BatchError::ResidentPlane`). Resident planes are U2. - `Cost` is counted from the programs that actually run: programs, selection passes, sum passes, rows traversed, values read, mask bytes. - `examples/bench.rs` (`BENCH_N`, `BENCH_REPS`). @@ -38,13 +39,15 @@ The time ratio is below the values-read ratio because a cross pass folds five su - At `i32::MIN`/`i32::MAX` values, `Σx²` passes `u64` and `Σxy` passes `i64`; the test asserts both. Mean and variance still equal the exact `i128` values. - At an offset of 10⁹, the naive one-pass f64 Pearson is off by more than 10⁻³. The exact path is within 10⁻¹² of the two-pass value. -**Tests and disables.** 8 tests pass. Six disable runs each turned red, with every disable patch's anchor asserted: +**Tests and disables.** 9 tests pass. Eight disable runs each turned red, with every disable patch's anchor asserted: - swap the reversed pair; - read the `y` side as `x`; - drop the pair dedup; - skip `normalize`; - merge only the first group; - drop the version check. +- drop the resident-plane refusal (`Rel::Plane` selections are refused, Codex on #1643); +- stop that check at `Not`. ## OPEN From 7704c80e89d1c583db727382d604896707a84275 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 00:39:47 +0000 Subject: [PATCH 3/4] jc-batch-probe: check versions over the whole batch before resident planes The single in-order pass reported ResidentPlane for a batch whose plane came before a stale request, breaking the documented VersionMismatch precedence. Codex review on #1645. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VncKoyG6JkpsoqhqTGTv3f --- crates/jc-batch-probe/src/lib.rs | 10 ++++++---- crates/jc-batch-probe/tests/batch.rs | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/crates/jc-batch-probe/src/lib.rs b/crates/jc-batch-probe/src/lib.rs index b406348f6..be12a3cfb 100644 --- a/crates/jc-batch-probe/src/lib.rs +++ b/crates/jc-batch-probe/src/lib.rs @@ -228,10 +228,9 @@ fn names_plane(rel: &Rel) -> bool { /// Refuses a request for another version, or one whose selection names a /// resident plane, before anything runs. fn check_requests(t: &Table<'_>, reqs: &[Request]) -> Result<(), BatchError> { + // Versions first, over the whole batch: a batch with a stale request is + // refused as `VersionMismatch` wherever a resident plane appears. for (i, r) in reqs.iter().enumerate() { - if names_plane(&r.select) { - return Err(BatchError::ResidentPlane { request: i }); - } if r.version != t.version { return Err(BatchError::VersionMismatch { request: i, @@ -240,7 +239,10 @@ fn check_requests(t: &Table<'_>, reqs: &[Request]) -> Result<(), BatchError> { }); } } - Ok(()) + match reqs.iter().position(|r| names_plane(&r.select)) { + Some(i) => Err(BatchError::ResidentPlane { request: i }), + None => Ok(()), + } } /// Evaluates `select` into a mask (one relalg program). diff --git a/crates/jc-batch-probe/tests/batch.rs b/crates/jc-batch-probe/tests/batch.rs index 3a46e0dd3..7fc8571b3 100644 --- a/crates/jc-batch-probe/tests/batch.rs +++ b/crates/jc-batch-probe/tests/batch.rs @@ -453,3 +453,23 @@ fn a_selection_naming_a_resident_plane_is_refused() { assert_eq!(answer_separately(&t, &reqs).map(|_| ()), want); } } + +/// FAILS IF: a resident-plane refusal takes precedence over a version +/// mismatch. The plane comes first (request 2) and the stale request later +/// (request 7), so a single in-order pass would report the plane. +#[test] +fn a_version_mismatch_wins_over_a_resident_plane_anywhere_in_the_batch() { + let fx = Fixture::new(500, 0x0DE7); + let lanes = fx.lanes(); + let t = fx.table(&lanes); + let mut reqs = requests(); + reqs[2].select = Rel::Plane(0); + reqs[7].version = VERSION + 1; + let want = Err(BatchError::VersionMismatch { + request: 7, + request_version: VERSION + 1, + table_version: VERSION, + }); + assert_eq!(answer_batched(&t, &reqs).map(|_| ()), want); + assert_eq!(answer_separately(&t, &reqs).map(|_| ()), want); +} From f0d34f917cccae9b08bb5964e87c03fbd581937f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 00:39:56 +0000 Subject: [PATCH 4/4] board: U1 entry records the version-before-plane precedence Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VncKoyG6JkpsoqhqTGTv3f --- .claude/board/entries/2026-10-20-u1-jc-batch.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.claude/board/entries/2026-10-20-u1-jc-batch.md b/.claude/board/entries/2026-10-20-u1-jc-batch.md index 249d9ebb7..cb4760d32 100644 --- a/.claude/board/entries/2026-10-20-u1-jc-batch.md +++ b/.claude/board/entries/2026-10-20-u1-jc-batch.md @@ -39,7 +39,7 @@ The time ratio is below the values-read ratio because a cross pass folds five su - At `i32::MIN`/`i32::MAX` values, `Σx²` passes `u64` and `Σxy` passes `i64`; the test asserts both. Mean and variance still equal the exact `i128` values. - At an offset of 10⁹, the naive one-pass f64 Pearson is off by more than 10⁻³. The exact path is within 10⁻¹² of the two-pass value. -**Tests and disables.** 9 tests pass. Eight disable runs each turned red, with every disable patch's anchor asserted: +**Tests and disables.** 10 tests pass. Nine disable runs each turned red, with every disable patch's anchor asserted: - swap the reversed pair; - read the `y` side as `x`; - drop the pair dedup; @@ -48,6 +48,7 @@ The time ratio is below the values-read ratio because a cross pass folds five su - drop the version check. - drop the resident-plane refusal (`Rel::Plane` selections are refused, Codex on #1643); - stop that check at `Not`. +- check planes before versions in one pass (a version mismatch must win wherever a plane appears, Codex on #1645). ## OPEN