refactor(salience_gate): Phase B language-advantage pass — dichotomy wave
Genuine Rust-side advantages over the Python oracle shape (parity pinned
by the frozen battery — 12/12, replay 119/119, shared verify
byte-identical):
* lower_for_match: ASCII-anchored Cow — already-lowercase gate texts (the
common case) match with ZERO allocation; non-ASCII/uppercase takes the
full Python-parity to_lowercase() path so Unicode case mappings (İ, ẛ)
are never skipped (borrow predicate is ASCII-only BY CONSTRUCTION).
* std-only hot-path bench in branch_battery: evaluate_decision over the
56-envelope frozen corpus + word_boundary_contains micro (evidence:
verification/salience-gate-phaseb-cargotest.log).
* #[must_use] on the pure surface (evaluate_decision, evaluate_salience,
evaluate_salience_dict, to_dict, handle_hook, _sg_core).
* iterator any() family scans with single-append semantics matching the
oracle's one-condition-per-family guards (feat fix, made idiomatic).
This commit is contained in:
1 parent
07096f2c59
commit
3d158800c7
2 files changed
+70
-8
No files matched your search
@@ -22,8 +22,8 @@ use serde_json::{json, Value};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::{
|
||||
_sg_core, evaluate_salience, evaluate_salience_dict, flag_score, handle_hook, test_salience,
|
||||
SalienceDecision,
|
||||
_sg_core, evaluate_decision, evaluate_salience, evaluate_salience_dict, flag_score,
|
||||
handle_hook, test_salience, word_boundary_contains, SalienceDecision,
|
||||
};
|
||||
|
||||
const ENVELOPE: &str = include_str!("frozen_sg_truth.json");
|
||||
@@ -212,3 +212,45 @@ fn to_dict_projection_shape() {
|
||||
assert_eq!(m["minimum_tier"], json!("T0"));
|
||||
assert_eq!(m["witness_needed"], json!(false));
|
||||
}
|
||||
|
||||
/// Phase B bench (std-only: no criterion dep) — hot-path cost of the full
|
||||
/// kernel evaluation over the frozen corpus, plus the word-boundary scan
|
||||
/// micro. Numbers land in the landing evidence log.
|
||||
#[test]
|
||||
fn evaluate_hot_path_bench() {
|
||||
use std::time::Instant;
|
||||
|
||||
let cases: Vec<Value> = envelope_cases()
|
||||
.iter()
|
||||
.filter(|c| c["fn"] == "evaluate_salience_dict")
|
||||
.map(|c| c["args"][0].clone())
|
||||
.collect();
|
||||
assert!(!cases.is_empty(), "corpus must be non-empty");
|
||||
|
||||
let rounds = 1000;
|
||||
let start = Instant::now();
|
||||
let mut sink = 0.0f64;
|
||||
for _ in 0..rounds {
|
||||
for env in &cases {
|
||||
sink += evaluate_decision(env).score;
|
||||
}
|
||||
}
|
||||
let per_call = start.elapsed().as_secs_f64() * 1e9 / (rounds * cases.len()) as f64;
|
||||
|
||||
let text = "the tests still fail after two attempts but maybe a new plan again";
|
||||
let start = Instant::now();
|
||||
let mut hits = 0usize;
|
||||
for _ in 0..100_000 {
|
||||
if word_boundary_contains(text, "still") || word_boundary_contains(text, "attempts") {
|
||||
hits += 1;
|
||||
}
|
||||
}
|
||||
let per_scan = start.elapsed().as_secs_f64() * 1e9 / 100_000.0;
|
||||
|
||||
assert!(sink.is_finite() && hits > 0);
|
||||
println!(
|
||||
"bench evaluate_decision: {per_call:.1} ns/call over {} frozen envelopes; \
|
||||
word_boundary_contains: {per_scan:.1} ns/scan",
|
||||
cases.len()
|
||||
);
|
||||
}
|
||||
@@ -50,6 +50,7 @@ impl Default for SalienceDecision {
|
||||
|
||||
impl SalienceDecision {
|
||||
/// Python SalienceDecision.to_dict parity: verbatim field projection.
|
||||
#[must_use]
|
||||
pub fn to_dict(&self) -> BTreeMap<String, Value> {
|
||||
let mut out = BTreeMap::new();
|
||||
out.insert("score".to_string(), serde_json::json!(self.score));
|
||||
@@ -111,9 +112,23 @@ fn add_flag(d: &mut SalienceDecision, flag: &str, code: &str) {
|
||||
d.rationale_codes.push(code.to_string());
|
||||
}
|
||||
|
||||
/// ASCII-anchored lowercase avoidance: keyword matching only needs the
|
||||
/// lowercased text; already-ASCII-lowercase strings (the common case) get
|
||||
/// matched with zero allocation. Non-ASCII (or any uppercase) takes the
|
||||
/// full Python-parity `to_lowercase()` path — Unicode case mappings (e.g.
|
||||
/// 'İ') must never be skipped, so the borrow predicate is ASCII-only.
|
||||
fn lower_for_match(s: &str) -> std::borrow::Cow<'_, str> {
|
||||
if s.is_ascii() && !s.bytes().any(|b| b.is_ascii_uppercase()) {
|
||||
std::borrow::Cow::Borrowed(s)
|
||||
} else {
|
||||
std::borrow::Cow::Owned(s.to_lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed kernel evaluation — mirrors the Python facade's floor exactly:
|
||||
/// authority → urgency → failure → retry → contradiction → uncertainty →
|
||||
/// novel, flags/rationale append in that evaluation order.
|
||||
#[must_use]
|
||||
pub fn evaluate_decision(envelope: &Value) -> SalienceDecision {
|
||||
let mut d = SalienceDecision::default();
|
||||
|
||||
@@ -125,12 +140,13 @@ pub fn evaluate_decision(envelope: &Value) -> SalienceDecision {
|
||||
.get("urgency")
|
||||
.and_then(|u| u.as_str())
|
||||
.unwrap_or("normal");
|
||||
let text = envelope
|
||||
.get("payload")
|
||||
.and_then(|p| p.get("text"))
|
||||
.and_then(|t| t.as_str())
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
let text = lower_for_match(
|
||||
envelope
|
||||
.get("payload")
|
||||
.and_then(|p| p.get("text"))
|
||||
.and_then(|t| t.as_str())
|
||||
.unwrap_or(""),
|
||||
);
|
||||
|
||||
match authority {
|
||||
"emergency" => add_flag(&mut d, "HIGH_RISK_IRREVERSIBLE", "emergency_authority"),
|
||||
@@ -215,6 +231,7 @@ pub fn evaluate_decision(envelope: &Value) -> SalienceDecision {
|
||||
d
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn evaluate_salience(envelope: &Value) -> BTreeMap<String, Value> {
|
||||
evaluate_decision(envelope).to_dict()
|
||||
}
|
||||
@@ -223,6 +240,7 @@ pub fn evaluate_salience(envelope: &Value) -> BTreeMap<String, Value> {
|
||||
/// wrapper the shared freeze harness freezes: the dataclass-returning
|
||||
/// evaluate_salience is not JSON-serializable). Pure to_dict composition —
|
||||
/// zero logic drift from the oracle floor.
|
||||
#[must_use]
|
||||
pub fn evaluate_salience_dict(envelope: &Value) -> Value {
|
||||
serde_json::json!(evaluate_salience(envelope))
|
||||
}
|
||||
@@ -231,6 +249,7 @@ pub fn evaluate_salience_dict(envelope: &Value) -> Value {
|
||||
/// decision only (from the kernel's side the core always exists; the loader
|
||||
/// module the Python probe returns is unreachable from Rust by design).
|
||||
/// Some ↔ probe answered the loader; None ↔ python pin honored / fallback.
|
||||
#[must_use]
|
||||
pub fn _sg_core(pin: Option<&str>) -> Option<Value> {
|
||||
if pin == Some("python") {
|
||||
None
|
||||
@@ -241,6 +260,7 @@ pub fn _sg_core(pin: Option<&str>) -> Option<Value> {
|
||||
|
||||
/// handle_hook — Python handle_hook parity: the plugin hook handler for
|
||||
/// incoming events. The oracle accepts `data` and ignores it.
|
||||
#[must_use]
|
||||
pub fn handle_hook(event: &Value, _data: Option<&Value>) -> Value {
|
||||
serde_json::json!({"status": "ok", "event": event, "handler": "salience_gate"})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user