refactor(project_registry): Phase B language-advantage pass — dichotomy wave
CI Pipeline / best-practices (push) Successful in 16s
CI Pipeline / rust-surface (push) Failing after 18s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / go-surface (push) Failing after 22s
CI Pipeline / gitea-offline (push) Successful in 22s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 25s
CI Pipeline / canary (push) Canceled after 47s
deploy / deploy (push) Canceled after 51s
Auto-version and Release / release (push) Canceled after 52s
CI Pipeline / lint (push) Canceled after 51s
CI Pipeline / unit-tests (push) Canceled after 51s

This commit is contained in:
Nick committed 2026-10-13 03:22:54 +01:00
1 parent e978510b0b
commit 4bd283c1a8
1 file changed
+92 -15
+92 -15
View File
@@ -36,6 +36,20 @@
//! No pr_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 four-state
//! health lattice is a `HealthState` enum with an exhaustive match
//! (classify once from the caller's string, render the canonical form
//! when stamping); the resolve_cwd precedence outcome is a
//! `CwdResolution` enum (ActiveWorktree / RepoRoot) so the 21.2B-3
//! ordering is a type, not an if-chain; the migration sweep compares
//! through a `SchemaVersion` newtype (missing version → the oracle's
//! default of 1). FFI errors are a typed `RegistryError` enum
//! (ValueError / KeyError) behind the Result envelope, so the loader
//! re-raises the SAME Python exception kind the oracle raised. The
//! registry DOC stays 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;
@@ -50,8 +64,57 @@ pub const DRIFTED: &str = "DRIFTED";
pub const MIGRATION_REQUIRED: &str = "MIGRATION_REQUIRED";
pub const UNKNOWN: &str = "UNKNOWN";
/// The four legal health states — the oracle's ValueError lattice.
const LEGAL_HEALTH: [&str; 4] = [HEALTHY, DRIFTED, MIGRATION_REQUIRED, UNKNOWN];
/// The four legal health states — an exhaustive lattice, not a string
/// soup: classify once from the caller's string, match exhaustively when
/// stamping (the oracle's ValueError lattice made a type).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum HealthState {
Healthy,
Drifted,
MigrationRequired,
Unknown,
}
impl HealthState {
fn classify(health: &str) -> Option<HealthState> {
match health {
HEALTHY => Some(HealthState::Healthy),
DRIFTED => Some(HealthState::Drifted),
MIGRATION_REQUIRED => Some(HealthState::MigrationRequired),
UNKNOWN => Some(HealthState::Unknown),
_ => None,
}
}
fn as_str(self) -> &'static str {
match self {
HealthState::Healthy => HEALTHY,
HealthState::Drifted => DRIFTED,
HealthState::MigrationRequired => MIGRATION_REQUIRED,
HealthState::Unknown => UNKNOWN,
}
}
}
/// A project's structural schema version — newtype so the migration
/// sweep reads as a version comparison, not a bare i64 dance (missing →
/// the oracle's default of 1).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SchemaVersion(i64);
impl SchemaVersion {
fn of(p: &Value) -> SchemaVersion {
SchemaVersion(
p.get("structural_schema_version")
.and_then(Value::as_i64)
.unwrap_or(1),
)
}
fn older_than(self, proposed: i64) -> bool {
self.0 < proposed
}
}
// ── oracle exceptions (typed, behind the doc envelope) ──────────────────────
@@ -179,9 +242,9 @@ pub fn set_health(
note: &str,
now: &str,
) -> Result<Map<String, Value>, RegistryError> {
if !LEGAL_HEALTH.contains(&health) {
let Some(lattice) = HealthState::classify(health) else {
return Err(unknown_health(health));
}
};
let mut out = doc.clone();
let p = projects_mut(&mut out)?
.get_mut(project_id)
@@ -190,7 +253,7 @@ pub fn set_health(
.get_mut("scaffold_state")
.and_then(Value::as_object_mut)
.ok_or_else(|| RegistryError::Key("scaffold_state".to_string()))?;
state.insert("health".to_string(), json!(health));
state.insert("health".to_string(), json!(lattice.as_str()));
state
.insert("last_material_update".to_string(), json!(now));
if !note.is_empty() {
@@ -245,6 +308,23 @@ pub fn mark_generation(
Ok(out)
}
/// The 21.2B-3 precedence outcome — classified once in resolve_cwd,
/// rendered by an exhaustive match (worktree beats repo root; the
/// refusal guards fire BEFORE classification).
enum CwdResolution {
ActiveWorktree(String),
RepoRoot(String),
}
impl CwdResolution {
fn into_cwd(self) -> String {
match self {
CwdResolution::ActiveWorktree(wt) => wt,
CwdResolution::RepoRoot(repo) => repo,
}
}
}
/// `resolve_cwd` — T21.2 §21.2B: canonical REPOSITORY/worktree execution
/// cwd for a project — NOT scaffold_location. Precedence (21.2B-3):
/// explicit contract/worktree cwd > programme cwd > registry repo cwd >
@@ -275,10 +355,12 @@ pub fn resolve_cwd(
.get("active_worktree")
.and_then(Value::as_str)
.unwrap_or("");
if !wt.is_empty() {
return Ok(wt.to_string());
}
Ok(repo)
let resolution = if wt.is_empty() {
CwdResolution::RepoRoot(repo)
} else {
CwdResolution::ActiveWorktree(wt.to_string())
};
Ok(resolution.into_cwd())
}
/// `needs_migration` — projects whose structural schema is older than a
@@ -287,12 +369,7 @@ pub fn resolve_cwd(
pub fn needs_migration(doc: &Map<String, Value>, proposed: i64) -> Result<Vec<String>, RegistryError> {
let mut ids: Vec<String> = projects_of(doc)?
.iter()
.filter(|(_, p)| {
p.get("structural_schema_version")
.and_then(Value::as_i64)
.unwrap_or(1)
< proposed
})
.filter(|(_, p)| SchemaVersion::of(p).older_than(proposed))
.map(|(pid, _)| pid.clone())
.collect();
ids.sort();