refactor(testing_mode): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 8s
CI Pipeline / rust-surface (push) Failing after 15s
CI Pipeline / go-surface (push) Failing after 20s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / gitea-offline (push) Canceled after 23s
CI Pipeline / type-check (push) Canceled after 27s
CI Pipeline / unit-tests (push) Canceled after 39s
CI Pipeline / lint (push) Canceled after 39s
Auto-version and Release / release (push) Canceled after 41s
CI Pipeline / canary (push) Canceled after 41s
deploy / deploy (push) Canceled after 25s

This commit is contained in:
Nick committed 2026-10-12 02:55:56 +01:00
1 parent 626359e693
commit 3c70f3bd14
1 file changed
+135 -40
+135 -40
View File
@@ -37,12 +37,93 @@
//! No tm_free_string export — sibling crates sharing the Python process
//! already export identical free symbols; callers free through
//! capability_scoring's cs_free_string.
//!
//! Language-advantage pass (Phase B, battery-pinned): the paused gate is
//! a `PauseState` enum with an exhaustive match (classify once from the
//! stored flag); the closure verdict is a `ClosureVerdict` enum
//! (retest ∧ user-confirmed, exhaustive); aggregate rows classify into
//! an `AggregateRow` enum (NoLedger / WithSummary) before rendering;
//! next_item hands back the selected queue entry by REFERENCE (zero-copy
//! borrow of the facade's outstanding list — the clone happens once, at
//! the FFI edge, only for the chosen item); FFI errors are a typed
//! `FfiError` enum behind the Result envelope. The STORED session state
//! and ledger records stay pass-through Values — the oracle forwards any
//! caller shape verbatim, so a closed stored lattice would be a parity
//! break, not an advantage. Battery green throughout.
use serde_json::{json, Map, Value};
use std::fmt;
use std::os::raw::c_char;
pub const SESSION_SCHEMA_VERSION: i64 = 1;
// ── decision lattices (enums, not string soup) ──────────────────────────────
/// The 19N pause gate — classified once from the stored `paused` flag
/// (Python truthiness), matched exhaustively in next_item.
#[derive(Clone, Copy, PartialEq, Eq)]
enum PauseState {
Paused,
Active,
}
impl PauseState {
fn classify(state: &Map<String, Value>) -> PauseState {
if py_truthy(state.get("paused").unwrap_or(&Value::Null)) {
PauseState::Paused
} else {
PauseState::Active
}
}
}
/// The 19O closure verdict — an observation closes only when the
/// behaviour was retested AND user-confirmed (code changed ≠ closure;
/// automated green ≠ closure).
#[derive(Clone, Copy, PartialEq, Eq)]
enum ClosureVerdict {
Closed,
Open,
}
impl From<ClosureVerdict> for bool {
fn from(v: ClosureVerdict) -> bool {
matches!(v, ClosureVerdict::Closed)
}
}
/// Aggregate row classification — decided per registry entry before
/// rendering, matched exhaustively.
enum AggregateRow {
NoLedger(Value),
WithSummary(Value, Value),
}
impl AggregateRow {
fn classify(entry: &Value) -> AggregateRow {
let project = entry.get("project_id").cloned().unwrap_or(Value::Null);
match entry.get("summary") {
None | Some(Value::Null) => AggregateRow::NoLedger(project),
Some(s) => AggregateRow::WithSummary(project, s.clone()),
}
}
fn render(self) -> Value {
match self {
AggregateRow::NoLedger(project) => json!({
"project": project,
"outstanding": Value::Null,
"note": "no behaviour ledger yet",
}),
AggregateRow::WithSummary(project, s) => json!({
"project": project,
"outstanding": s.get("outstanding").cloned().unwrap_or(Value::Null),
"states": s.get("states").cloned().unwrap_or(Value::Null),
}),
}
}
}
// ── Python scalar semantics ─────────────────────────────────────────────────
/// Python truthiness over a JSON value.
@@ -116,25 +197,32 @@ pub fn default_state(project: &Value, scope_component: &Value, now: &str) -> Val
})
}
/// The dequeued-item sentinel for paused/empty queues — a stable static
/// so the borrowed return never points into a temporary.
static NULL_ITEM: Value = Value::Null;
/// `next_item` — 19L next eligible item. Paused sessions dequeue
/// nothing (state untouched); otherwise the first outstanding item is
/// selected and its behaviour_id becomes current_item.
pub fn next_item(
/// selected and its behaviour_id becomes current_item. The selected
/// item is handed back by reference — zero-copy off the facade's list.
pub fn next_item<'a>(
state: &Map<String, Value>,
outstanding: &[Value],
) -> (Map<String, Value>, Value) {
if py_truthy(state.get("paused").unwrap_or(&Value::Null)) {
return (state.clone(), Value::Null);
outstanding: &'a [Value],
) -> (Map<String, Value>, &'a Value) {
match PauseState::classify(state) {
PauseState::Paused => (state.clone(), &NULL_ITEM),
PauseState::Active => {
let mut out = state.clone();
let item = outstanding.first().unwrap_or(&NULL_ITEM);
let current = item
.as_object()
.and_then(|o| o.get("behaviour_id"))
.cloned()
.unwrap_or(Value::Null);
out.insert("current_item".to_string(), current);
(out, item)
}
}
let mut out = state.clone();
let item = outstanding.first().cloned().unwrap_or(Value::Null);
let current = item
.as_object()
.and_then(|o| o.get("behaviour_id"))
.cloned()
.unwrap_or(Value::Null);
out.insert("current_item".to_string(), current);
(out, item)
}
/// `record` — append the completed-session bookkeeping entry (the
@@ -240,11 +328,15 @@ pub fn close_observation(
for rec in records {
let mut rec = rec.clone();
if rec.get("observation_id") == Some(observation_id) {
let verdict = match (behaviour_retest_passed, user_confirmed) {
(true, true) => ClosureVerdict::Closed,
_ => ClosureVerdict::Open,
};
rec["closure"] = json!({
"behaviour_retest_passed": behaviour_retest_passed,
"user_confirmed": user_confirmed,
"contract_ref": contract_ref,
"closed": behaviour_retest_passed && user_confirmed,
"closed": bool::from(verdict),
"at": now,
});
result = rec.clone();
@@ -289,43 +381,46 @@ fn truncate_chars(s: &str, n: usize) -> String {
pub fn aggregate(entries: &[Value]) -> Vec<Value> {
entries
.iter()
.map(|entry| {
let project = entry
.get("project_id")
.cloned()
.unwrap_or(Value::Null);
match entry.get("summary") {
None | Some(Value::Null) => json!({
"project": project,
"outstanding": Value::Null,
"note": "no behaviour ledger yet",
}),
Some(s) => json!({
"project": project,
"outstanding": s.get("outstanding").cloned().unwrap_or(Value::Null),
"states": s.get("states").cloned().unwrap_or(Value::Null),
}),
}
})
.map(AggregateRow::classify)
.map(AggregateRow::render)
.collect()
}
// ── FFI (doc-envelope law) ──────────────────────────────────────────────────
fn read_arg(ptr: *const c_char) -> Result<Value, String> {
/// Typed FFI failure lattice behind the doc-envelope Result — the
/// caller-visible `err` string is unchanged (the enum only shapes the
/// Rust side; parity is pinned by the battery).
enum FfiError {
NullPtr,
Utf8(std::str::Utf8Error),
Json(serde_json::Error),
}
impl fmt::Display for FfiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FfiError::NullPtr => write!(f, "null argument pointer"),
FfiError::Utf8(e) => write!(f, "{e}"),
FfiError::Json(e) => write!(f, "{e}"),
}
}
}
fn read_arg(ptr: *const c_char) -> Result<Value, FfiError> {
if ptr.is_null() {
return Err("null argument pointer".to_string());
return Err(FfiError::NullPtr);
}
let s = unsafe { std::ffi::CStr::from_ptr(ptr) }
.to_str()
.map_err(|e| e.to_string())?;
serde_json::from_str(s).map_err(|e| e.to_string())
.map_err(FfiError::Utf8)?;
serde_json::from_str(s).map_err(FfiError::Json)
}
fn envelope(r: Result<Value, String>) -> *mut c_char {
fn envelope(r: Result<Value, FfiError>) -> *mut c_char {
let v = match r {
Ok(x) => json!({"ok": true, "value": x}),
Err(e) => json!({"ok": false, "err": e}),
Err(e) => json!({"ok": false, "err": e.to_string()}),
};
std::ffi::CString::new(serde_json::to_string(&v).unwrap_or_default())
.map(|c| c.into_raw())