From 88b216201a463455b1b3ff430a8284e7b9396684 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 17:18:00 +0000 Subject: [PATCH] pillar11 W5: derive the trigger's in-tree maximum instead of retyping it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompted by a defect the parallel MedCare-rs session hit and fixed in #606: a bake read its own previous image back in, so a stale artifact silently fed the next measurement (817,295 vs the clean-room 762,041). The same shape was sitting in this trigger check. `w5_trigger_check` hardcoded `longest_in_tree = 4609` with a comment saying where it came from. The value is real — 3 segments x 1536 pts/segment + 1 — but it lives in `hambly_lyons::PER_SEG`, a private constant in another module. Raise that resolution and the trigger keeps reporting 4609, and a trigger reporting a stale value is worse than no trigger: it answers "not fired" with confidence, which is exactly the question it exists to answer. Now derived. Each battery exports the longest path it constructs (`hambly_lyons::LONGEST_PATH_POINTS` = 3*PER_SEG+1, `solver_order::LONGEST_PATH_POINTS` = M+1) and the check takes their max, so raising a resolution anywhere re-arms it automatically. The attribution label is derived from the same comparison rather than asserted, so it cannot name the wrong leg once the other one wins. Output is unchanged where it should be — 4609, memory half fires at ~11585, TRIGGER FIRED: false. The old constant was correct; it just had no way to stay correct. Also corrected, a measurement of my own: the "clippy error count 38" reported on the W2/W3 commits was a noisy instrument. `grep -c '^error'` also counts the "could not compile ... due to N previous errors" summary lines, which vary with how far each target gets. The set of LOCATED errors is the real measure and it is byte-identical before and after this change (36 entries, empty diff both directions) — this adds no lint and fixes none. Tests 124 + 13 green; default zero-dep build unchanged (3 pre-existing dead-code warnings in ewa_sandwich/pflug, present in the unchanged set). Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01KCGhDYoQBXs3poaR7sFuqp --- crates/jc/examples/w5_trigger_check.rs | 19 ++++++++++++++----- crates/jc/src/hambly_lyons.rs | 11 +++++++++++ crates/jc/src/solver_order.rs | 8 ++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/crates/jc/examples/w5_trigger_check.rs b/crates/jc/examples/w5_trigger_check.rs index ec95ff95b..01dc8268f 100644 --- a/crates/jc/examples/w5_trigger_check.rs +++ b/crates/jc/examples/w5_trigger_check.rs @@ -68,11 +68,20 @@ fn main() { ); // The longest path this workspace's own certification legs construct. - let longest_in_tree = 4609usize; // W2 triangle at 1536 pts/segment, 3 segments - println!( - "\n longest path constructed anywhere in-tree today: {longest_in_tree} \ - (the W2 depth-infinity converse leg)" - ); + // DERIVED, never retyped. Each battery exports the length of the longest + // path it builds, so raising a resolution anywhere re-arms this check + // automatically. The previous revision hardcoded 4609 with a comment + // explaining where it came from — which is exactly how a measurement + // that later moves keeps reporting its old value, and a trigger that + // reports a stale value is worse than no trigger. + let longest_in_tree = + jc::hambly_lyons::LONGEST_PATH_POINTS.max(jc::solver_order::LONGEST_PATH_POINTS); + let owner = if jc::hambly_lyons::LONGEST_PATH_POINTS >= jc::solver_order::LONGEST_PATH_POINTS { + "the W2 depth-infinity converse leg" + } else { + "the W3 solver-order battery" + }; + println!("\n longest path constructed anywhere in-tree today: {longest_in_tree} ({owner})"); let fired = longest_in_tree as f64 >= mem_len.min(time_len); println!(" TRIGGER FIRED: {fired}"); if !fired { diff --git a/crates/jc/src/hambly_lyons.rs b/crates/jc/src/hambly_lyons.rs index 90773288b..2f399c271 100644 --- a/crates/jc/src/hambly_lyons.rs +++ b/crates/jc/src/hambly_lyons.rs @@ -146,6 +146,12 @@ mod active { // sweeps: `examples/w2_refinement_sweep.rs` (convergence) and // `examples/w2_area_edge.rs` (the converse law and its edge). const PER_SEG: usize = 1536; + /// Points in the longest path this leg constructs: the converse triangle + /// is three resampled segments plus the closing point. Exported so the W5 + /// trigger check DERIVES the in-tree maximum instead of restating it — + /// a measured value retyped into a second file is one that goes stale + /// silently, and the trigger's whole job is to notice when it has not. + pub const LONGEST_PATH_POINTS: usize = 3 * PER_SEG + 1; /// Forward pairs. The forward statistic is population-stable — the /// refinement sweep measured max deviation 5.005e-6 at both 12 and 25 /// pairs — so a small sample is a faithful one here. @@ -415,6 +421,11 @@ mod active { } } +/// Points in the longest path this module constructs — see the constant's +/// own doc in the gated implementation. +#[cfg(feature = "hambly-lyons")] +pub use active::LONGEST_PATH_POINTS; + #[cfg(feature = "hambly-lyons")] pub fn prove() -> PillarResult { active::prove() diff --git a/crates/jc/src/solver_order.rs b/crates/jc/src/solver_order.rs index b7c7cbae9..134ff8eba 100644 --- a/crates/jc/src/solver_order.rs +++ b/crates/jc/src/solver_order.rs @@ -49,6 +49,9 @@ mod active { type P2 = [f64; 2]; const M: usize = 2048; + /// Points in the longest path this battery constructs (see the sibling + /// constant in `hambly_lyons` for why it is exported rather than retyped). + pub const LONGEST_PATH_POINTS: usize = M + 1; /// Super-period windows for these fixtures (period ~ 2π·M/ω micro-steps). const WINDOWS: [usize; 3] = [32, 48, 64]; /// Area-register bit depths for the carrier-fidelity leg. @@ -276,6 +279,11 @@ mod active { } } +/// Points in the longest path this module constructs — see the constant's +/// own doc in the gated implementation. +#[cfg(feature = "hambly-lyons")] +pub use active::LONGEST_PATH_POINTS; + #[cfg(feature = "hambly-lyons")] pub fn prove() -> PillarResult { active::prove()