refactor(toolchain): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / rust-surface (push) Canceled after 15s
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 0s
CI Pipeline / lint (push) Canceled after 16s
CI Pipeline / type-check (push) Canceled after 3s
CI Pipeline / unit-tests (push) Canceled after 16s
CI Pipeline / go-surface (push) Canceled after 16s
CI Pipeline / canary (push) Canceled after 16s
Auto-version and Release / release (push) Canceled after 26s
deploy / deploy (push) Canceled after 7s

Resolution lattice becomes a real enum (KeepExisting/ChangeMajor/
InsufficientEvidence) with exhaustive as_str/parse matches at the JSON
boundary; DecisionRecord is a struct with RecordFields input (removes
the 12-arg constructor signature and the too_many_arguments escape
hatch); resolve_toolchain returns Result; borrows stay zero-copy until
the boundary. Oracle-faithful quirk kept: resolution stays a free-form
string on the constructor wire (default "") — only resolver outputs
are lattice-guaranteed. Battery 7 tests / 43 frozen cases + lattice
round-trip GREEN; clippy --all-targets -D warnings 0; live parity
613/613; facade delegation unchanged (RUNE_TC_CORE pin).
This commit is contained in:
Nick committed 2026-10-12 00:37:27 +01:00
1 parent f83396eecd
commit 61de7c5b74
2 files changed
+236 -140

No files matched your search

+34 -16
View File
@@ -59,21 +59,24 @@ fn constraints_get_parity_battery() {
fn decision_record_parity_battery() {
for case in cases("decision_record") {
let a = &case["input"];
let out = decision_record(
a["selected_language"].clone(),
a.get("runtime"),
a.get("framework"),
a.get("libraries"),
a.get("build_system").and_then(Value::as_str).unwrap_or(""),
a.get("alternatives"),
a.get("decisive_constraints"),
a.get("rationale").and_then(Value::as_str).unwrap_or(""),
a.get("evidence_refs"),
a.get("confidence").and_then(Value::as_f64).unwrap_or(0.0),
a.get("user_approval_required").and_then(Value::as_bool).unwrap_or(false),
a.get("resolution").and_then(Value::as_str).unwrap_or(""),
);
check(case["name"].as_str().unwrap(), out, &case);
let out = decision_record(RecordFields {
selected_language: a["selected_language"].clone(),
runtime: a.get("runtime"),
framework: a.get("framework"),
libraries: a.get("libraries"),
build_system: a.get("build_system").and_then(Value::as_str).unwrap_or(""),
alternatives: a.get("alternatives"),
decisive_constraints: a.get("decisive_constraints"),
rationale: a.get("rationale").and_then(Value::as_str).unwrap_or(""),
evidence_refs: a.get("evidence_refs"),
confidence: a.get("confidence").and_then(Value::as_f64).unwrap_or(0.0),
user_approval_required: a
.get("user_approval_required")
.and_then(Value::as_bool)
.unwrap_or(false),
resolution: a.get("resolution").and_then(Value::as_str).unwrap_or("").to_string(),
});
check(case["name"].as_str().unwrap(), out.to_value(), &case);
}
}
@@ -96,7 +99,22 @@ fn resolve_toolchain_parity_battery() {
a.get("requested_language").and_then(Value::as_str),
a.get("evidence_refs"),
a.get("performance_evidence").filter(|p| !p.is_null()),
);
)
.expect("resolve_toolchain must not refuse a corpus case");
check(case["name"].as_str().unwrap(), out, &case);
}
}
#[test]
fn resolution_lattice_round_trip() {
// every lattice member renders to its oracle string and parses back
for (r, s) in [
(Resolution::KeepExisting, "KEEP_EXISTING"),
(Resolution::ChangeMajor, "CHANGE_MAJOR"),
(Resolution::InsufficientEvidence, "INSUFFICIENT_EVIDENCE"),
] {
assert_eq!(r.as_str(), s);
assert_eq!(Resolution::parse(s), Some(r));
}
assert_eq!(Resolution::parse("MAYBE"), None);
}
+202 -124
View File
@@ -21,6 +21,11 @@
//! NOTE: no tl_free_string export — sibling crates sharing the Python
//! process already export identical free symbols; a duplicate breaks the
//! cdylib link. Callers free through capability_scoring's cs_free_string.
//!
//! Phase B (language-advantage pass): the resolution lattice is a real
//! enum with exhaustive matches at the JSON boundary; the decision
//! record is a struct (no 12-arg signature); the resolver returns
//! Result; borrows stay zero-copy until the boundary.
use serde_json::{json, Map, Value};
use std::ffi::{CStr, CString};
@@ -56,6 +61,37 @@ const HARD_FACTOR_KEYS: &[&str] = &[
"deployment",
];
// ── resolution lattice (exhaustive match at the JSON boundary — the
// wire always carries the oracle's exact strings) ─────────────────────
/// The oracle's `resolution` field values. Not a free-form string: an
/// unknown resolution on the wire is a hard error, never a guess.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Resolution {
KeepExisting,
ChangeMajor,
InsufficientEvidence,
}
impl Resolution {
pub fn as_str(self) -> &'static str {
match self {
Resolution::KeepExisting => "KEEP_EXISTING",
Resolution::ChangeMajor => "CHANGE_MAJOR",
Resolution::InsufficientEvidence => "INSUFFICIENT_EVIDENCE",
}
}
pub fn parse(s: &str) -> Option<Resolution> {
match s {
"KEEP_EXISTING" => Some(Resolution::KeepExisting),
"CHANGE_MAJOR" => Some(Resolution::ChangeMajor),
"INSUFFICIENT_EVIDENCE" => Some(Resolution::InsufficientEvidence),
_ => None,
}
}
}
/// Python truthiness for JSON values (0, "", [], {}, null, False are falsy).
fn is_truthy(v: Option<&Value>) -> bool {
match v {
@@ -98,41 +134,85 @@ fn sorted_strs(v: Option<&Value>) -> Vec<String> {
out
}
/// The oracle's DecisionRecord constructor: durable, inspectable
/// toolchain decision (tranche brief §9). List inputs are normalized
/// (sorted, None → empty); alternatives are carried verbatim.
#[allow(clippy::too_many_arguments)]
pub fn decision_record(
selected_language: Value,
runtime: Option<&Value>,
framework: Option<&Value>,
libraries: Option<&Value>,
build_system: &str,
alternatives: Option<&Value>,
decisive_constraints: Option<&Value>,
rationale: &str,
evidence_refs: Option<&Value>,
confidence: f64,
user_approval_required: bool,
resolution: &str,
) -> Value {
json!({
"selected_language": selected_language,
"runtime": runtime.cloned().unwrap_or(json!("")),
"framework": framework.cloned().unwrap_or(json!("")),
"libraries": sorted_strs(libraries),
"build_system": build_system,
"alternatives": alternatives
/// The oracle's DecisionRecord: durable, inspectable toolchain decision
/// (tranche brief §9). Inputs normalize at construction (lists sorted,
/// None → empty); alternatives are carried verbatim.
#[derive(Clone, PartialEq, Debug)]
pub struct DecisionRecord {
pub selected_language: Value,
pub runtime: Value,
pub framework: Value,
pub libraries: Vec<String>,
pub build_system: String,
pub alternatives: Value,
pub decisive_constraints: Vec<String>,
pub rationale: String,
pub evidence_refs: Vec<String>,
pub confidence: f64,
pub user_approval_required: bool,
/// Oracle keeps this a free-form string (constructor default "") —
/// only resolve_toolchain's outputs are lattice-guaranteed.
pub resolution: String,
}
impl DecisionRecord {
/// Render the oracle's `to_dict()` exactly.
pub fn to_value(&self) -> Value {
json!({
"selected_language": self.selected_language,
"runtime": self.runtime,
"framework": self.framework,
"libraries": self.libraries,
"build_system": self.build_system,
"alternatives": self.alternatives,
"decisive_constraints": self.decisive_constraints,
"rationale": self.rationale,
"evidence_refs": self.evidence_refs,
"confidence": self.confidence,
"user_approval_required": self.user_approval_required,
"resolution": self.resolution,
})
}
}
/// Optional record inputs; `Default` reproduces the oracle's parameter
/// defaults (empty strings/lists, confidence 0.0, no approval flag).
#[derive(Clone, Default)]
pub struct RecordFields<'a> {
pub selected_language: Value,
pub runtime: Option<&'a Value>,
pub framework: Option<&'a Value>,
pub libraries: Option<&'a Value>,
pub build_system: &'a str,
pub alternatives: Option<&'a Value>,
pub decisive_constraints: Option<&'a Value>,
pub rationale: &'a str,
pub evidence_refs: Option<&'a Value>,
pub confidence: f64,
pub user_approval_required: bool,
pub resolution: String,
}
/// The oracle's DecisionRecord(...) constructor.
pub fn decision_record(fields: RecordFields<'_>) -> DecisionRecord {
DecisionRecord {
selected_language: fields.selected_language,
runtime: fields.runtime.cloned().unwrap_or(json!("")),
framework: fields.framework.cloned().unwrap_or(json!("")),
libraries: sorted_strs(fields.libraries),
build_system: fields.build_system.to_string(),
alternatives: fields
.alternatives
.filter(|v| !v.is_null()) // Python: list(alternatives or [])
.cloned()
.unwrap_or(json!([])),
"decisive_constraints": sorted_strs(decisive_constraints),
"rationale": rationale,
"evidence_refs": sorted_strs(evidence_refs),
"confidence": confidence,
"user_approval_required": user_approval_required,
"resolution": resolution,
})
decisive_constraints: sorted_strs(fields.decisive_constraints),
rationale: fields.rationale.to_string(),
evidence_refs: sorted_strs(fields.evidence_refs),
confidence: fields.confidence,
user_approval_required: fields.user_approval_required,
resolution: fields.resolution,
}
}
/// A major toolchain change = introducing/changing a language, runtime or
@@ -158,7 +238,7 @@ pub fn resolve_toolchain(
requested_language: Option<&str>,
evidence_refs: Option<&Value>,
performance_evidence: Option<&Value>,
) -> Value {
) -> Result<Value, String> {
let refs = sorted_strs(evidence_refs);
let existing = known.get("existing_language");
let requested = requested_language.map(|r| Value::String(r.to_string()));
@@ -172,22 +252,17 @@ pub fn resolve_toolchain(
.collect();
let requested_truthy = requested.as_ref().map(|r| is_truthy(Some(r))).unwrap_or(false);
if !is_truthy(existing) && !requested_truthy && hard_factors.len() < 2 {
return decision_record(
json!(""),
None,
None,
None,
"",
None,
Some(&json!(hard_factors)),
"no existing project toolchain recorded and too few \
constraints known to select honestly; refusing to \
invent certainty",
Some(&json!(refs)),
0.0,
false,
"INSUFFICIENT_EVIDENCE",
);
return Ok(decision_record(RecordFields {
selected_language: json!(""),
decisive_constraints: Some(&json!(hard_factors)),
rationale: "no existing project toolchain recorded and too few \
constraints known to select honestly; refusing to \
invent certainty",
evidence_refs: Some(&json!(refs)),
resolution: Resolution::InsufficientEvidence.as_str().to_string(),
..Default::default()
})
.to_value());
}
// DEFAULT: existing toolchain wins
@@ -202,23 +277,24 @@ pub fn resolve_toolchain(
"performance_evidence(rejected: does not override existing toolchain without user approval)"
));
}
return decision_record(
existing.cloned().unwrap_or(json!("")),
known.get("existing_runtime"),
known.get("existing_framework"),
None,
known
return Ok(decision_record(RecordFields {
selected_language: existing.cloned().unwrap_or(json!("")),
runtime: known.get("existing_runtime"),
framework: known.get("existing_framework"),
// the oracle looks this up even though the key can never be
// in the known vocabulary — behavior preserved verbatim
build_system: known
.get("existing_build_system")
.and_then(Value::as_str)
.unwrap_or(""),
None,
Some(&json!(decisive)),
"existing project toolchain wins (§8); no recorded evidence shows significant benefit from change",
Some(&json!(refs)),
if !hard_factors.is_empty() { 0.85 } else { 0.7 },
false,
"KEEP_EXISTING",
);
decisive_constraints: Some(&json!(decisive)),
rationale: "existing project toolchain wins (§8); no recorded evidence shows significant benefit from change",
evidence_refs: Some(&json!(refs)),
confidence: if !hard_factors.is_empty() { 0.85 } else { 0.7 },
resolution: Resolution::KeepExisting.as_str().to_string(),
..Default::default()
})
.to_value());
}
// REQUESTED change away from existing: recommendable, never silent
@@ -231,22 +307,24 @@ pub fn resolve_toolchain(
if significant {
decisive.push(json!("performance_evidence"));
}
return decision_record(
requested.clone().unwrap_or(json!("")),
known.get("existing_runtime"),
None,
None,
"",
Some(&json!([{"language": existing, "rejected_because": "requested change"}])),
Some(&json!(decisive)),
"requested language differs from the project's \
established toolchain; recommendation only — \
user approval required before material change",
Some(&json!(refs)),
if significant { 0.8 } else { 0.5 },
true,
"CHANGE_MAJOR",
);
return Ok(decision_record(RecordFields {
selected_language: requested.clone().unwrap_or(json!("")),
runtime: known.get("existing_runtime"),
alternatives: Some(&json!([{
"language": existing,
"rejected_because": "requested change",
}])),
decisive_constraints: Some(&json!(decisive)),
rationale: "requested language differs from the project's \
established toolchain; recommendation only — \
user approval required before material change",
evidence_refs: Some(&json!(refs)),
confidence: if significant { 0.8 } else { 0.5 },
user_approval_required: true,
resolution: Resolution::ChangeMajor.as_str().to_string(),
..Default::default()
})
.to_value());
}
// Greenfield: we have NO language-selection intelligence (tranche brief
@@ -255,35 +333,28 @@ pub fn resolve_toolchain(
// never a guess, never silently Python. With a request, record it as a
// recommendation requiring approval.
if requested_truthy {
return decision_record(
requested.clone().unwrap_or(json!("")),
None,
None,
None,
"",
None,
Some(&json!(hard_factors)),
"greenfield request recorded as recommendation; user approval required",
Some(&json!(refs)),
0.4,
true,
"CHANGE_MAJOR",
);
return Ok(decision_record(RecordFields {
selected_language: requested.clone().unwrap_or(json!("")),
decisive_constraints: Some(&json!(hard_factors)),
rationale: "greenfield request recorded as recommendation; user approval required",
evidence_refs: Some(&json!(refs)),
confidence: 0.4,
user_approval_required: true,
resolution: Resolution::ChangeMajor.as_str().to_string(),
..Default::default()
})
.to_value());
}
decision_record(
json!(""),
None,
None,
None,
"",
None,
Some(&json!(hard_factors)),
"greenfield: no requested language and no language-selection evidence; refusing to invent a choice",
Some(&json!(refs)),
0.0,
true,
"INSUFFICIENT_EVIDENCE",
)
Ok(decision_record(RecordFields {
selected_language: json!(""),
decisive_constraints: Some(&json!(hard_factors)),
rationale: "greenfield: no requested language and no language-selection evidence; refusing to invent a choice",
evidence_refs: Some(&json!(refs)),
user_approval_required: true,
resolution: Resolution::InsufficientEvidence.as_str().to_string(),
..Default::default()
})
.to_value())
}
// ── FFI (doc-envelope law) ──────────────────────────────────────────────
@@ -345,7 +416,8 @@ pub extern "C" fn tl_constraints_get(arg: *const c_char) -> *mut c_char {
/// tl_decision_record({selected_language, runtime?, framework?,
/// libraries?, build_system?, alternatives?, decisive_constraints?,
/// rationale?, evidence_refs?, confidence?, user_approval_required?,
/// resolution?}) → normalized record dict.
/// resolution?}) → normalized record dict. An unknown resolution string
/// is refused — the wire never carries a guess.
#[no_mangle]
pub extern "C" fn tl_decision_record(arg: *const c_char) -> *mut c_char {
envelope(read_arg(arg).and_then(|a| {
@@ -353,20 +425,26 @@ pub extern "C" fn tl_decision_record(arg: *const c_char) -> *mut c_char {
.get("selected_language")
.cloned()
.ok_or_else(|| "missing selected_language".to_string())?;
Ok(decision_record(
selected,
a.get("runtime"),
a.get("framework"),
a.get("libraries"),
a.get("build_system").and_then(Value::as_str).unwrap_or(""),
a.get("alternatives"),
a.get("decisive_constraints"),
a.get("rationale").and_then(Value::as_str).unwrap_or(""),
a.get("evidence_refs"),
a.get("confidence").and_then(Value::as_f64).unwrap_or(0.0),
a.get("user_approval_required").and_then(Value::as_bool).unwrap_or(false),
a.get("resolution").and_then(Value::as_str).unwrap_or(""),
))
// the oracle's constructor keeps resolution a free-form string
let resolution = a.get("resolution").and_then(Value::as_str).unwrap_or("");
let record = decision_record(RecordFields {
selected_language: selected,
runtime: a.get("runtime"),
framework: a.get("framework"),
libraries: a.get("libraries"),
build_system: a.get("build_system").and_then(Value::as_str).unwrap_or(""),
alternatives: a.get("alternatives"),
decisive_constraints: a.get("decisive_constraints"),
rationale: a.get("rationale").and_then(Value::as_str).unwrap_or(""),
evidence_refs: a.get("evidence_refs"),
confidence: a.get("confidence").and_then(Value::as_f64).unwrap_or(0.0),
user_approval_required: a
.get("user_approval_required")
.and_then(Value::as_bool)
.unwrap_or(false),
resolution: resolution.to_string(),
});
Ok(record.to_value())
}))
}
@@ -377,12 +455,12 @@ pub extern "C" fn tl_decision_record(arg: *const c_char) -> *mut c_char {
pub extern "C" fn tl_resolve_toolchain(arg: *const c_char) -> *mut c_char {
envelope(read_arg(arg).and_then(|a| {
let known = arg_obj(&a, "known")?;
Ok(resolve_toolchain(
resolve_toolchain(
known,
a.get("requested_language").and_then(Value::as_str),
a.get("evidence_refs"),
a.get("performance_evidence").filter(|p| !p.is_null()),
))
)
}))
}