From afa5a8ffd9107e5d718c81b8942a70ce2b5c0d6f Mon Sep 17 00:00:00 2001 From: Jens Holdgaard Pedersen Date: Sun, 12 Jul 2026 00:35:11 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(bench):=20rfc=200031=20=E2=80=94=20flo?= =?UTF-8?q?or-direction=20gate=20for=20l6/l7=20reporting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L6 (broad scan) and L7 (ingest) are FLOOR/parity classes in RFC 0031 §2 — Ourios may be worse by at most F_L6/F_L7 — but the indicative report pushed the time-window pairs through bytes_must_win, the wrong direction (run #7 printed advantage 0.02 as a must-win fail instead of answering "is Ourios within 3x of Loki?"). Adds bytes_within_floor(ourios, loki, factor): pass iff ourios <= factor x loki, mirroring bytes_must_win's honesty guards (factor==0 and zero measurements are Invalid, never a pass). The overflow arm differs deliberately: an overflowing factor x loki budget would be a mathematically true pass on an implausible measurement, so checked_mul fails it closed as Invalid rather than letting a saturated budget pass everything. advantage keeps the loki/ourios orientation so both gates' tables read the same way; only the pass rule differs. The indicative report gains a GateKind on PairSpec: the severity pair stays must-win (m_l2), the time-window slices report via the floor gate (f_l6), and the gate lines name the direction. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y --- crates/ourios-bench/src/lgates.rs | 147 ++++++++++++++++-- crates/ourios-bench/src/lib.rs | 4 +- .../ourios-bench/tests/rfc0031_comparative.rs | 45 +++++- 3 files changed, 178 insertions(+), 18 deletions(-) diff --git a/crates/ourios-bench/src/lgates.rs b/crates/ourios-bench/src/lgates.rs index eb526c42a..bdb14f7da 100644 --- a/crates/ourios-bench/src/lgates.rs +++ b/crates/ourios-bench/src/lgates.rs @@ -1,8 +1,9 @@ //! RFC 0031 L-gate math — the comparative pass/fail rules. //! //! Pure ratio logic over the §3.6 measurements (no IO): the bytes-read -//! **must-win** rule for the L1–L4 classes, with the §7 calibration -//! values carried as configuration. Mirrors the a1/c1/c2 gate-math +//! **must-win** rule for the L1–L4 classes and the **floor** rule for +//! the L6/L7 family, with the §7 calibration values carried as +//! configuration. Mirrors the a1/c1/c2 gate-math //! pattern: the arithmetic is unit-tested here, the measurements arrive //! from the comparative harness (`ourios_query_answer` / //! `parse_loki_bytes_processed`), and the wiring into the §3.6 results @@ -45,28 +46,33 @@ impl Default for ComparativeMargins { } } -/// Outcome of one bytes-read must-win gate (RFC 0031 §5, RFC0031.2–.5): -/// pass iff `ourios_bytes × margin ≤ loki_bytes`. +/// Outcome of one bytes-read gate (RFC 0031 §5): the must-win rule +/// ([`bytes_must_win`], RFC0031.2–.5) or the floor rule +/// ([`bytes_within_floor`], the RFC0031.7–.8 direction). /// /// A zero byte-count on **either** side is [`Invalid`](Self::Invalid), -/// never a pass: a must-win gate only passes on a *demonstrated* win over -/// valid measurements, and a stray zero (a broken channel, an empty -/// result) would otherwise fake an infinite advantage — the same honesty -/// rule that makes a missing Loki stats block an error rather than a 0. +/// never a pass: a gate only decides over valid measurements, and a +/// stray zero (a broken channel, an empty result) would otherwise fake +/// an infinite advantage — the same honesty rule that makes a missing +/// Loki stats block an error rather than a 0. #[derive(Debug, Clone, PartialEq)] #[non_exhaustive] pub enum BytesGateOutcome { /// Both measurements valid; the gate is decided. Decided { - /// `ourios_bytes × margin ≤ loki_bytes`. + /// The evaluating gate's rule held (see [`bytes_must_win`] / + /// [`bytes_within_floor`] for the two rules). pass: bool, /// The headline ratio `loki_bytes / ourios_bytes`: values above /// `1.0` mean Ourios read that many times fewer bytes, below - /// `1.0` that it read more; `≥ margin` ⇒ `pass`. + /// `1.0` that it read more. Same orientation for both gates; + /// must-win passes at `≥ margin`, the floor gate at + /// `≥ 1/factor`. advantage: f64, }, /// The comparison was meaningless: a zero byte-count on either side, - /// or a zero `margin` (which would pass unconditionally). + /// a zero `margin`/`factor` (a misconfigured gate), or a + /// measurement so large the floor budget overflows. Invalid { /// What made it meaningless: which side(s) reported zero, or /// that the margin itself was zero. @@ -118,6 +124,61 @@ pub fn bytes_must_win(ourios_bytes: u64, loki_bytes: u64, margin: u64) -> BytesG BytesGateOutcome::Decided { pass, advantage } } +/// Evaluate one bytes-read **floor** gate (RFC 0031 §2's L6/L7 +/// dispositions, scenarios RFC0031.7–.8, factors `F_L6`/`F_L7` from §7): +/// Ourios is allowed to be *worse* than Loki here, but only within the +/// committed factor — pass iff `ourios_bytes ≤ factor × loki_bytes`. +/// The inverse question of [`bytes_must_win`]: broad scans (L6) and +/// ingest (L7) are bounded-loss classes, not wins to demonstrate. +/// +/// The reported `advantage` keeps [`bytes_must_win`]'s orientation +/// (`loki_bytes / ourios_bytes`, above `1.0` means Ourios read fewer +/// bytes) so both gates' tables read the same way; only the pass rule +/// differs — the floor passes at `advantage ≥ 1/factor`. +/// +/// Same honesty guards as [`bytes_must_win`], with one twist on the +/// overflow arm: `factor × loki_bytes` overflowing would be a +/// *mathematically true* pass (the budget exceeds anything a `u64` can +/// measure) — but an exabyte-scale Loki figure is a broken measurement, +/// so it fails closed as [`Invalid`](BytesGateOutcome::Invalid) rather +/// than passing on garbage. +#[must_use] +pub fn bytes_within_floor(ourios_bytes: u64, loki_bytes: u64, factor: u64) -> BytesGateOutcome { + // factor == 0 would make `ourios ≤ 0` fail unconditionally — a + // misconfiguration must be loud, not a silent permanent fail. + if factor == 0 { + return BytesGateOutcome::Invalid { + reason: "factor is 0 — a floor gate with no budget fails everything, \ + which demonstrates nothing" + .to_string(), + }; + } + if ourios_bytes == 0 || loki_bytes == 0 { + return BytesGateOutcome::Invalid { + reason: format!( + "zero byte-count (ourios={ourios_bytes}, loki={loki_bytes}) — a floor \ + gate needs both measurements non-zero to demonstrate anything" + ), + }; + } + // checked_mul, not saturating: a saturated budget of u64::MAX would + // pass ANY ourios figure — the same false-pass trap as must-win's, + // reached from the other side. Overflow ⇒ Invalid, never a pass. + let Some(budget) = loki_bytes.checked_mul(factor) else { + return BytesGateOutcome::Invalid { + reason: format!( + "loki_bytes × factor overflows u64 (loki={loki_bytes}, factor={factor}) \ + — a budget past u64::MAX is a broken measurement, not a pass" + ), + }; + }; + let pass = ourios_bytes <= budget; + #[allow(clippy::cast_precision_loss)] // reporting ratio only; the pass + // decision above is exact integer math. + let advantage = loki_bytes as f64 / ourios_bytes as f64; + BytesGateOutcome::Decided { pass, advantage } +} + #[cfg(test)] mod tests { use super::*; @@ -196,4 +257,68 @@ mod tests { assert!(bytes_must_win(100, 1_000, m.m_l1).passed()); assert!(!bytes_must_win(100, 1_000, 20).passed()); } + + #[test] + fn floor_passes_at_and_below_the_factor_boundary() { + // Exactly at the boundary: ourios == 3×loki ⇒ pass. + let at = bytes_within_floor(300, 100, 3); + assert!(at.passed(), "{at:?}"); + // Ourios reading FEWER bytes trivially satisfies the floor; the + // advantage keeps the must-win loki/ourios orientation. + let BytesGateOutcome::Decided { pass, advantage } = bytes_within_floor(10, 1_000, 3) else { + panic!("expected decided"); + }; + assert!(pass); + assert!((advantage - 100.0).abs() < f64::EPSILON, "{advantage}"); + } + + #[test] + fn floor_fails_just_above_the_boundary() { + // One byte over the budget: still a decided (reportable) outcome. + let BytesGateOutcome::Decided { pass, advantage } = bytes_within_floor(301, 100, 3) else { + panic!("expected decided"); + }; + assert!(!pass); + assert!(advantage < 1.0, "{advantage}"); + } + + #[test] + fn floor_zero_on_either_side_is_invalid_never_a_pass() { + assert!(!bytes_within_floor(0, 1_000, 3).passed()); + assert!(!bytes_within_floor(100, 0, 3).passed()); + assert!(!bytes_within_floor(0, 0, 3).passed()); + assert!(matches!( + bytes_within_floor(0, 1_000, 3), + BytesGateOutcome::Invalid { .. } + )); + // A zero FACTOR would fail unconditionally — a misconfiguration + // must be Invalid, not a silent permanent fail. + assert!(matches!( + bytes_within_floor(100, 1_000, 0), + BytesGateOutcome::Invalid { .. } + )); + } + + #[test] + fn floor_overflowing_budget_is_invalid_never_a_pass() { + // loki×factor overflows: the bound would hold mathematically for + // ANY ourios figure (a saturated budget of u64::MAX passes + // everything), but only because the measurement is implausible — + // checked_mul must refuse, not pass. + let trap = bytes_within_floor(u64::MAX / 2, u64::MAX, 3); + assert!(!trap.passed(), "{trap:?}"); + assert!(matches!(trap, BytesGateOutcome::Invalid { .. })); + // Sanity: a huge-but-non-overflowing budget still decides exactly, + // on both sides of the bound. + assert!(bytes_within_floor(u64::MAX / 2, u64::MAX / 4, 3).passed()); + assert!(!bytes_within_floor(u64::MAX / 2, u64::MAX / 8, 3).passed()); + } + + #[test] + fn floor_factors_flow_into_the_decision() { + // The same measurements decide differently under F_L6 vs F_L7. + let m = ComparativeMargins::default(); + assert!(bytes_within_floor(250, 100, m.f_l6).passed()); + assert!(!bytes_within_floor(250, 100, m.f_l7).passed()); + } } diff --git a/crates/ourios-bench/src/lib.rs b/crates/ourios-bench/src/lib.rs index 5e7fe97da..9cf19784c 100644 --- a/crates/ourios-bench/src/lib.rs +++ b/crates/ourios-bench/src/lib.rs @@ -27,7 +27,7 @@ //! `calibrate` + `reference` + `store` (RFC 0024 / the B1/B2 query //! stores), and the RFC 0031 comparative harness (`comparative` — the //! Loki equivalence check + measurement channel — and `lgates`, the -//! comparative must-win gate math). +//! comparative must-win / floor gate math). #![deny(unsafe_code)] @@ -54,7 +54,7 @@ pub use comparative::{ parse_loki_bytes_processed, parse_loki_fetched_bytes, parse_loki_streams, }; pub use corpus::TxtSeverity; -pub use lgates::{BytesGateOutcome, ComparativeMargins, bytes_must_win}; +pub use lgates::{BytesGateOutcome, ComparativeMargins, bytes_must_win, bytes_within_floor}; pub use reference::ReferenceCorpus; pub use report::{update_status_section, write_results_json}; pub use store::{B1Store, BuiltStore, build_b1_store, build_comparative_store, build_query_store}; diff --git a/crates/ourios-bench/tests/rfc0031_comparative.rs b/crates/ourios-bench/tests/rfc0031_comparative.rs index 32c4d5bb2..7fbd0470c 100644 --- a/crates/ourios-bench/tests/rfc0031_comparative.rs +++ b/crates/ourios-bench/tests/rfc0031_comparative.rs @@ -917,6 +917,31 @@ async fn push_corpus_to_loki(http: &reqwest::Client, base: &str, corpus_dir: &st eprintln!("loki ingest complete: {batched} LogsData lines in {pushed} requests"); } +/// Which direction a pair's bytes gate asks its question in (RFC 0031 +/// §2 dispositions): the L1–L4 classes must WIN by the margin, the +/// L6/L7 family must merely stay WITHIN the floor factor. +#[derive(Clone, Copy)] +enum GateKind { + MustWin, + Floor, +} + +impl GateKind { + fn evaluate(self, ourios: u64, loki: u64, margin: u64) -> ourios_bench::BytesGateOutcome { + match self { + Self::MustWin => ourios_bench::bytes_must_win(ourios, loki, margin), + Self::Floor => ourios_bench::bytes_within_floor(ourios, loki, margin), + } + } + + fn margin_label(self) -> &'static str { + match self { + Self::MustWin => "must-win margin", + Self::Floor => "floor factor", + } + } +} + /// One measured query of the indicative run: the equivalent DSL/`LogQL` /// question, the window both systems answer it over, and the row count /// both must return exactly. @@ -925,6 +950,9 @@ struct PairSpec { /// The pair's §7 margin for the reported gate (`m_l2` for the /// severity pair, `f_l6` for the broad time-window slices). margin: u64, + /// Direction the gate is reported under: must-win for the severity + /// pair, floor for the time-window slices. + gate: GateKind, dsl: String, logql: String, /// Loki `query_range` window (nanoseconds, `[start, end)` by the @@ -966,6 +994,7 @@ fn build_pair_specs( pair.service, pair.threshold, pair.text ), margin: margins.m_l2, + gate: GateKind::MustWin, dsl: format!( "service == \"{}\" and severity >= {} | limit 5000", pair.service, pair.threshold @@ -998,6 +1027,7 @@ fn build_pair_specs( pair.service ), margin: margins.f_l6, + gate: GateKind::Floor, dsl: format!("service == \"{}\" | limit 5000", pair.service), logql: format!("{{service_name=\"{}\"}}", pair.service), start, @@ -1172,9 +1202,12 @@ fn print_indicative_report( println!("corpus: {} ({total_records} records)", corpus_dir.display()); for ((spec, ours), (_, loki_processed, loki_fetched)) in specs.iter().zip(ourios).zip(loki) { let loki_storage = loki_fetched.compressed_bytes + loki_fetched.head_chunk_bytes; - let gate_storage = ourios_bench::bytes_must_win(ours.bytes_read, loki_storage, spec.margin); - let gate_processed = - ourios_bench::bytes_must_win(ours.bytes_read, *loki_processed, spec.margin); + let gate_storage = spec + .gate + .evaluate(ours.bytes_read, loki_storage, spec.margin); + let gate_processed = spec + .gate + .evaluate(ours.bytes_read, *loki_processed, spec.margin); println!("--- pair [{}] rows={} ---", spec.label, spec.expected_rows); println!("dsl: {}", spec.dsl); println!( @@ -1188,11 +1221,13 @@ fn print_indicative_report( ); println!("loki totalBytesProcessed (decompressed) = {loki_processed}"); println!( - "gate vs storage-side (PRIMARY, margin {}): {gate_storage:?}", + "gate vs storage-side (PRIMARY, {} {}): {gate_storage:?}", + spec.gate.margin_label(), spec.margin ); println!( - "gate vs bytes-processed (context, margin {}): {gate_processed:?}", + "gate vs bytes-processed (context, {} {}): {gate_processed:?}", + spec.gate.margin_label(), spec.margin ); } From 492b73afb223c0aa0ba742c5a3f8c09f1d027549 Mon Sep 17 00:00:00 2001 From: Jens Holdgaard Pedersen Date: Sun, 12 Jul 2026 00:44:03 +0200 Subject: [PATCH 2/2] =?UTF-8?q?docs(bench):=20rfc=200031=20=E2=80=94=20rea?= =?UTF-8?q?son-field=20doc=20covers=20all=20invalid=20arms,=20param=20rena?= =?UTF-8?q?me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y --- crates/ourios-bench/src/lgates.rs | 5 +++-- crates/ourios-bench/tests/rfc0031_comparative.rs | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/ourios-bench/src/lgates.rs b/crates/ourios-bench/src/lgates.rs index bdb14f7da..533623cf1 100644 --- a/crates/ourios-bench/src/lgates.rs +++ b/crates/ourios-bench/src/lgates.rs @@ -74,8 +74,9 @@ pub enum BytesGateOutcome { /// a zero `margin`/`factor` (a misconfigured gate), or a /// measurement so large the floor budget overflows. Invalid { - /// What made it meaningless: which side(s) reported zero, or - /// that the margin itself was zero. + /// What made it meaningless: which side(s) reported zero, that + /// the margin/factor itself was zero, or that the floor budget + /// overflowed. reason: String, }, } diff --git a/crates/ourios-bench/tests/rfc0031_comparative.rs b/crates/ourios-bench/tests/rfc0031_comparative.rs index 7fbd0470c..d83b6b884 100644 --- a/crates/ourios-bench/tests/rfc0031_comparative.rs +++ b/crates/ourios-bench/tests/rfc0031_comparative.rs @@ -927,10 +927,10 @@ enum GateKind { } impl GateKind { - fn evaluate(self, ourios: u64, loki: u64, margin: u64) -> ourios_bench::BytesGateOutcome { + fn evaluate(self, ourios: u64, loki: u64, calibration: u64) -> ourios_bench::BytesGateOutcome { match self { - Self::MustWin => ourios_bench::bytes_must_win(ourios, loki, margin), - Self::Floor => ourios_bench::bytes_within_floor(ourios, loki, margin), + Self::MustWin => ourios_bench::bytes_must_win(ourios, loki, calibration), + Self::Floor => ourios_bench::bytes_within_floor(ourios, loki, calibration), } }