refactor(drift_core): Phase B language-advantage pass — dichotomy wave

One generic py_round(x, digits) replaces the py_round2/py_round4 twins
(same round_ties_even-at-scale law, one definition), and fail_dict()
carries promote_entry's three failure shapes. Behavior unchanged:
battery 3/3, live parity 30+600+11 failures=0, clippy -D warnings 0.
This commit is contained in:
Nick committed 2026-10-14 05:43:15 +01:00
1 parent d77d1bce93
commit 558dd2cbb0
1 file changed
+24 -26
+24 -26
View File
@@ -153,16 +153,12 @@ fn write_v(out: &mut String, v: &V) {
// ── Python-semantics helpers ────────────────────────────────────────────────
/// Python round(x, 2) — banker's rounding on the exact binary double at the
/// 1e2 scale (same law as memory_scoring's py_round4 at 1e4, proved there).
fn py_round2(x: f64) -> f64 {
(x * 100.0).round_ties_even() / 100.0
}
/// Python round(x, 4) — the 1e4 twin (memory_scoring keeps its own private
/// copy; this crate mirrors it rather than reaching into privates).
fn py_round4(x: f64) -> f64 {
(x * 10000.0).round_ties_even() / 10000.0
/// Python round(x, digits) — banker's rounding on the exact binary double
/// at the 10^digits scale (same law as memory_scoring's py_round4 at 1e4,
/// proved there; one generic serves every scale this module rounds to).
fn py_round(x: f64, digits: i32) -> f64 {
let scale = 10f64.powi(digits);
(x * scale).round_ties_even() / scale
}
fn as_str(v: Option<&Json>) -> Option<&str> {
@@ -271,32 +267,31 @@ pub fn pre_commit_gate_entry(entry: &Json) -> V {
// ── promotion lifecycle ─────────────────────────────────────────────────────
/// The oracle's failure shape: {"success": false, "reason": ...}.
fn fail_dict(reason: impl Into<String>) -> V {
V::Obj(vec![
("success".into(), V::Bool(false)),
("reason".into(), V::Str(reason.into())),
])
}
/// Promote an entry through the lifecycle (decision kernel): the facade
/// SELECTs the row and passes it here; on success the returned new_status
/// (and its derived promotion_eligible flag) are applied by the facade's
/// UPDATE. Key order mirrors the oracle's insertion order.
pub fn promote_entry(entry: Option<&Json>, entry_id: i64, now_iso: &str) -> V {
let Some(entry) = entry else {
return V::Obj(vec![
("success".into(), V::Bool(false)),
("reason".into(), V::Str("entry not found".into())),
]);
return fail_dict("entry not found");
};
let current_status = as_str(entry.get("rock_status"))
.unwrap_or("washable")
.to_string();
if current_status == "rock" {
return V::Obj(vec![
("success".into(), V::Bool(false)),
("reason".into(), V::Str("already rock".into())),
]);
return fail_dict("already rock");
}
let (eligible, reason) = check_promotion_eligibility(entry, now_iso);
if !eligible {
return V::Obj(vec![
("success".into(), V::Bool(false)),
("reason".into(), V::Str(reason)),
]);
return fail_dict(reason);
}
let new_status = match current_status.as_str() {
"washable" => "established",
@@ -360,10 +355,10 @@ pub fn compute_drift_velocity(changes: i64, promotions: i64, days: i64) -> V {
let velocity = changes as f64 / window as f64;
let promotion_rate = promotions as f64 / window as f64;
V::Obj(vec![
("changes_per_day".into(), V::F64(py_round2(velocity))),
("changes_per_day".into(), V::F64(py_round(velocity, 2))),
(
"promotions_per_day".into(),
V::F64(py_round2(promotion_rate)),
V::F64(py_round(promotion_rate, 2)),
),
("total_changes".into(), V::Int(changes)),
("total_promotions".into(), V::Int(promotions)),
@@ -385,8 +380,11 @@ pub fn compute_drift_direction(total: i64, established: i64, rock: i64) -> V {
("total_entries".into(), V::Int(total)),
("established_count".into(), V::Int(established)),
("rock_count".into(), V::Int(rock)),
("stability_ratio".into(), V::F64(py_round4(stability_ratio))),
("rock_ratio".into(), V::F64(py_round4(rock_ratio))),
(
"stability_ratio".into(),
V::F64(py_round(stability_ratio, 4)),
),
("rock_ratio".into(), V::F64(py_round(rock_ratio, 4))),
(
"direction".into(),
V::Str(