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:
1 parent
d77d1bce93
commit
558dd2cbb0
1 file changed
+24
-26
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user