Compare commits

...
Author SHA1 Message Date
Nick ba57eb8177 refactor(construction_commands): Phase B language-advantage pass — dichotomy wave
deploy / deploy (push) Skipped
CI Pipeline / lint (push) Canceled after 0s
CI Pipeline / unit-tests (push) Canceled after 0s
CI Pipeline / go-surface (push) Canceled after 0s
CI Pipeline / rust-surface (push) Canceled after 0s
CI Pipeline / canary (push) Canceled after 0s
CI Pipeline / best-practices (push) Canceled after 0s
CI Pipeline / documentation (push) Canceled after 0s
CI Pipeline / type-check (push) Canceled after 0s
CI Pipeline / gitea-offline (push) Canceled after 0s
CI Pipeline / required-checks (push) Canceled after 0s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Canceled after 0s
Typed CommandType/Op/PhaseClass lattices replace the string-arm chains:
parse IS the unknown-command refusal surface, the (command, phase-class)
tuple match in _validate is exhaustive, and command_to_operation maps
through to_op. Behavior byte-identical to the Phase A parity port —
frozen battery the referee, zero truth changes (sha256 ba9fb44d pre ==
post; replay 54/54; pytest 12/12 both pins; clippy -D warnings clean).
2026-10-14 16:10:12 +01:00
7 changed files with 300 additions and 159 deletions

No files matched your search

+286 -155
View File
@@ -13,6 +13,17 @@
//! intentionally not part of the vocabulary — a Substrate asking for
//! them gets a refusal, not a half-measure.
//!
//! Phase B language-advantage pass (dichotomy wave): the command
//! vocabulary, its interface-op mapping and the lifecycle phase lattice
//! are typed enums — `CommandType::parse` + exhaustive `match` replace
//! string-arm chains, so a future vocabulary member cannot be handled at
//! one site and forgotten at another (the oracle's defensive
//! "unreachable validator branch" becomes structurally impossible);
//! `PhaseClass` carries RESUME's terminal refusal pair and
//! ACKNOWLEDGE_BLOCKER's blockable set as lattice data instead of
//! string-compare chains. Behavior is byte-identical to the Phase A
//! parity port (the frozen battery is the referee — zero truth changes).
//!
//! FFI doc-envelope law: {"ok":true,"value":...} | {"ok":false,"err":...};
//! NO ccm_free_string export (sibling crates sharing one Python process
//! already export identical free symbols; callers free through
@@ -33,17 +44,107 @@ pub const COMMANDS: [&str; 6] = [
"PROVIDE_CONTEXT",
];
/// The inbound command vocabulary, typed. Parsing IS the
/// unknown-command refusal surface: no string arm can be added without
/// the compiler demanding handling in `to_op`, `_validate` and the
/// dispatch executor table (the oracle's defensive
/// "unreachable validator branch" becomes structurally impossible).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CommandType {
RequestState,
Start,
Resume,
AcknowledgeBlocker,
ProvideApproval,
ProvideContext,
}
impl CommandType {
fn parse(s: &str) -> Option<Self> {
match s {
"REQUEST_STATE" => Some(Self::RequestState),
"START" => Some(Self::Start),
"RESUME" => Some(Self::Resume),
"ACKNOWLEDGE_BLOCKER" => Some(Self::AcknowledgeBlocker),
"PROVIDE_APPROVAL" => Some(Self::ProvideApproval),
"PROVIDE_CONTEXT" => Some(Self::ProvideContext),
_ => None,
}
}
/// Command → the interface operation it asks for (the interface
/// still decides: validation failures return refused, never raise
/// into storage).
fn to_op(self) -> Op {
match self {
Self::RequestState | Self::Resume => Op::Resume, // read-only state
Self::Start | Self::ProvideApproval | Self::AcknowledgeBlocker => Op::Transition,
Self::ProvideContext => Op::AmendScope, // context = scope amendment
}
}
}
/// The interface operation a validated command asks for, typed.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Op {
Resume,
Transition,
AmendScope,
}
impl Op {
fn as_str(self) -> &'static str {
match self {
Self::Resume => "resume",
Self::Transition => "transition",
Self::AmendScope => "amend_scope",
}
}
/// Parse back from the intent envelope's op string (the dispatch
/// executor table matches on this, so an unknown op string stays a
/// "no executor" envelope, never a panic).
fn parse(s: &str) -> Option<Self> {
match s {
"resume" => Some(Self::Resume),
"transition" => Some(Self::Transition),
"amend_scope" => Some(Self::AmendScope),
_ => None,
}
}
}
/// Command → the interface operation it asks for. The interface still
/// decides: validation failures return refused, never raise into storage.
pub fn command_to_operation(command: &str) -> Option<&'static str> {
match command {
"REQUEST_STATE" => Some("resume"),
"START" => Some("transition"),
"RESUME" => Some("resume"),
"ACKNOWLEDGE_BLOCKER" => Some("transition"),
"PROVIDE_APPROVAL" => Some("transition"),
"PROVIDE_CONTEXT" => Some("amend_scope"),
_ => None,
CommandType::parse(command).map(|cmd| cmd.to_op().as_str())
}
/// The lifecycle phase lattice as seen by the validator (Phase B):
/// RESUME's terminal refusal pair (closed/abandoned), the
/// ACKNOWLEDGE_BLOCKER blockable set (failed/building/verifying) and
/// START/PROVIDE_APPROVAL's draft gate are lattice data instead of
/// string-compare chains. Unknown phases (and a missing/null phase)
/// classify to `Other` and fail every privileged gate — the oracle
/// accepts a RESUME on an arbitrary phase string but refuses
/// START/ACKNOWLEDGE_BLOCKER/PROVIDE_APPROVAL on it; the tuple match in
/// `_validate_branches` encodes exactly that, exhaustively.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum PhaseClass {
Draft,
Blockable,
Terminal,
Other,
}
impl PhaseClass {
fn classify(phase: Option<&str>) -> Self {
match phase {
Some("draft") => Self::Draft,
Some("failed" | "building" | "verifying") => Self::Blockable,
Some("closed" | "abandoned") => Self::Terminal,
_ => Self::Other,
}
}
}
@@ -165,41 +266,42 @@ pub fn command(
replayed["idempotent_replay"] = json!(true);
return replayed;
}
if !COMMANDS.contains(&command_type) {
return _refuse(
command_id,
if let Some(cmd) = CommandType::parse(command_type) {
if construction_id.is_empty() {
return _refuse(
command_id,
construction_id,
correlation_id,
"construction_id is required",
now,
);
}
return _validate(
cmd,
construction_id,
correlation_id,
&format!(
"unknown command '{}' (supported: {})",
command_type,
COMMANDS.join(", ")
),
now,
);
}
if construction_id.is_empty() {
return _refuse(
command_id,
construction_id,
correlation_id,
"construction_id is required",
payload,
now,
scaffold_doc,
);
}
_validate(
command_type,
_refuse(
command_id,
construction_id,
correlation_id,
command_id,
payload,
&format!(
"unknown command '{}' (supported: {})",
command_type,
COMMANDS.join(", ")
),
now,
scaffold_doc,
)
}
#[allow(clippy::too_many_arguments)]
fn _validate(
command_type: &str,
cmd: CommandType,
construction_id: &str,
correlation_id: &str,
command_id: &str,
@@ -240,7 +342,7 @@ fn _validate(
};
// Python get("phase", "") only falls back to "" for a MISSING key;
// a present null renders "None" in the refusal messages.
let phase_py: Option<&str> = contract.get("phase").and_then(Value::as_str);
let phase = PhaseClass::classify(contract.get("phase").and_then(Value::as_str));
let phase_render = match contract.get("phase") {
Some(v) => py_render(v),
None => String::new(),
@@ -251,107 +353,121 @@ fn _validate(
} else {
payload
};
_validate_branches(
cmd,
construction_id,
correlation_id,
command_id,
payload,
now,
phase,
&phase_render,
)
}
match command_type {
"REQUEST_STATE" => _accept(
#[allow(clippy::too_many_arguments)]
fn _validate_branches(
cmd: CommandType,
construction_id: &str,
correlation_id: &str,
command_id: &str,
payload: &Value,
now: &str,
phase: PhaseClass,
phase_render: &str,
) -> Value {
// Exhaustive over the (command, phase-class) lattice: adding a
// CommandType or PhaseClass variant breaks compilation here, so a
// future vocabulary member cannot be handled at one site and
// forgotten at another (the oracle's defensive "unreachable
// validator branch" is structurally impossible).
match (cmd, phase) {
(CommandType::RequestState, _) => _accept(
command_id,
construction_id,
correlation_id,
"resume",
Op::Resume.as_str(),
json!({"contract_id": construction_id}),
now,
),
"RESUME" => {
if phase_py == Some("closed") || phase_py == Some("abandoned") {
_refuse(
// RESUME refuses only on a terminal construction; any other
// phase string (even an unknown one) re-reads canonical state.
(CommandType::Resume, PhaseClass::Terminal) => _refuse(
command_id,
construction_id,
correlation_id,
&format!("construction is {phase_render} — resume is invalid"),
now,
),
(CommandType::Resume, _) => _accept(
command_id,
construction_id,
correlation_id,
Op::Resume.as_str(),
json!({"contract_id": construction_id}),
now,
),
(CommandType::Start, PhaseClass::Draft) => _accept(
command_id,
construction_id,
correlation_id,
Op::Transition.as_str(),
json!({"contract_id": construction_id, "phase": "approved"}),
now,
),
(CommandType::Start, _) => _refuse(
command_id,
construction_id,
correlation_id,
&format!("START requires draft phase (current: {phase_render})"),
now,
),
(CommandType::ProvideApproval, PhaseClass::Draft) => {
match payload.get("approved_by").and_then(Value::as_str) {
Some("user") => _accept(
command_id,
construction_id,
correlation_id,
&format!("construction is {phase_render} — resume is invalid"),
now,
)
} else {
_accept(
command_id,
construction_id,
correlation_id,
"resume",
json!({"contract_id": construction_id}),
now,
)
}
}
"START" => {
if phase_py != Some("draft") {
_refuse(
command_id,
construction_id,
correlation_id,
&format!("START requires draft phase (current: {phase_render})"),
now,
)
} else {
_accept(
command_id,
construction_id,
correlation_id,
"transition",
json!({"contract_id": construction_id, "phase": "approved"}),
now,
)
}
}
"PROVIDE_APPROVAL" => {
if phase_py != Some("draft") {
_refuse(
command_id,
construction_id,
correlation_id,
&format!("approval applies to draft (current: {phase_render})"),
now,
)
} else if payload.get("approved_by").and_then(Value::as_str) != Some("user") {
// lifecycle rule: approval provenance must be the user —
// Substrate cannot approve on the user's behalf
_refuse(
command_id,
construction_id,
correlation_id,
"approval provenance must be user (Substrate cannot approve for the user)",
now,
)
} else {
_accept(
command_id,
construction_id,
correlation_id,
"transition",
Op::Transition.as_str(),
json!({"contract_id": construction_id, "phase": "approved", "approved_by": "user"}),
now,
)
),
_ => {
// lifecycle rule: approval provenance must be the user —
// Substrate cannot approve on the user's behalf
_refuse(
command_id,
construction_id,
correlation_id,
"approval provenance must be user (Substrate cannot approve for the user)",
now,
)
}
}
}
"ACKNOWLEDGE_BLOCKER" => {
if !matches!(phase_py, Some("failed") | Some("building") | Some("verifying")) {
_refuse(
command_id,
construction_id,
correlation_id,
&format!("no blockable phase (current: {phase_render})"),
now,
)
} else {
_accept(
command_id,
construction_id,
correlation_id,
"transition",
json!({"contract_id": construction_id, "phase": "building"}),
now,
)
}
}
"PROVIDE_CONTEXT" => {
(CommandType::ProvideApproval, _) => _refuse(
command_id,
construction_id,
correlation_id,
&format!("approval applies to draft (current: {phase_render})"),
now,
),
(CommandType::AcknowledgeBlocker, PhaseClass::Blockable) => _accept(
command_id,
construction_id,
correlation_id,
Op::Transition.as_str(),
json!({"contract_id": construction_id, "phase": "building"}),
now,
),
(CommandType::AcknowledgeBlocker, _) => _refuse(
command_id,
construction_id,
correlation_id,
&format!("no blockable phase (current: {phase_render})"),
now,
),
(CommandType::ProvideContext, _) => {
let paths = python_or_empty_list(payload.get("add_paths"));
if !python_truthy(Some(&paths)) {
_refuse(
@@ -374,7 +490,7 @@ fn _validate(
command_id,
construction_id,
correlation_id,
"amend_scope",
Op::AmendScope.as_str(),
json!({
"contract_id": construction_id,
"add_paths": paths,
@@ -385,13 +501,6 @@ fn _validate(
)
}
}
_ => _refuse(
command_id,
construction_id,
correlation_id,
"unreachable validator branch",
now,
),
}
}
@@ -484,25 +593,28 @@ pub fn dispatch(
},
});
}
let op = intent.get("op").and_then(Value::as_str).unwrap_or("");
let op_str = intent.get("op").and_then(Value::as_str).unwrap_or("");
let args = intent.get("args").cloned().unwrap_or(json!({}));
if !matches!(op, "resume" | "transition" | "amend_scope") {
return json!({
"kind": "final",
"record": true,
"intent": intent,
"envelope": {
"accepted": true,
"executed": false,
"success": false,
"error": format!("no executor for op '{}'", op),
"command_type": command_type,
"construction_id": construction_id,
},
});
}
let op = match Op::parse(op_str) {
Some(op) => op,
None => {
return json!({
"kind": "final",
"record": true,
"intent": intent,
"envelope": {
"accepted": true,
"executed": false,
"success": false,
"error": format!("no executor for op '{}'", op_str),
"command_type": command_type,
"construction_id": construction_id,
},
})
}
};
let evidence_ref = match op {
"transition" => {
Op::Transition => {
let suffix = match args.get("approved_by") {
Some(v) if python_truthy(Some(v)) => format!(" approved_by:{}", py_render(v)),
_ => String::new(),
@@ -512,12 +624,12 @@ pub fn dispatch(
intent.get("command_id").and_then(Value::as_str).unwrap_or("")
)
}
_ => String::new(),
Op::Resume | Op::AmendScope => String::new(),
};
json!({
"kind": "exec",
"record": true,
"op": op,
"op": op.as_str(),
"args": args,
"evidence_ref": evidence_ref,
"intent": intent,
@@ -641,9 +753,6 @@ pub extern "C" fn ccm_dispatch_finish(arg: *const c_char) -> *mut c_char {
}))
}
#[cfg(test)]
mod branch_battery;
#[cfg(test)]
mod tests {
use super::*;
@@ -688,13 +797,32 @@ mod tests {
#[test]
fn command_vocabulary_maps_to_interface_ops() {
assert_eq!(command_to_operation("REQUEST_STATE"), Some("resume"));
assert_eq!(command_to_operation("START"), Some("transition"));
assert_eq!(command_to_operation("RESUME"), Some("resume"));
assert_eq!(command_to_operation("ACKNOWLEDGE_BLOCKER"), Some("transition"));
assert_eq!(command_to_operation("PROVIDE_APPROVAL"), Some("transition"));
assert_eq!(command_to_operation("PROVIDE_CONTEXT"), Some("amend_scope"));
// the full lattices, exhausted through the typed enums
for (name, cmd, op) in [
("REQUEST_STATE", CommandType::RequestState, Op::Resume),
("START", CommandType::Start, Op::Transition),
("RESUME", CommandType::Resume, Op::Resume),
("ACKNOWLEDGE_BLOCKER", CommandType::AcknowledgeBlocker, Op::Transition),
("PROVIDE_APPROVAL", CommandType::ProvideApproval, Op::Transition),
("PROVIDE_CONTEXT", CommandType::ProvideContext, Op::AmendScope),
] {
assert_eq!(CommandType::parse(name), Some(cmd));
assert_eq!(cmd.to_op(), op);
assert_eq!(command_to_operation(name), Some(op.as_str()));
}
assert_eq!(command_to_operation("PAUSE"), None);
// the RESUME terminal pair and the ACK blockable set live in
// the phase lattice, not in string-compare chains
assert_eq!(PhaseClass::classify(Some("draft")), PhaseClass::Draft);
for phase in ["failed", "building", "verifying"] {
assert_eq!(PhaseClass::classify(Some(phase)), PhaseClass::Blockable);
}
for phase in ["closed", "abandoned"] {
assert_eq!(PhaseClass::classify(Some(phase)), PhaseClass::Terminal);
}
for phase in [None, Some(""), Some("weird"), Some("DRAFT")] {
assert_eq!(PhaseClass::classify(phase), PhaseClass::Other);
}
}
#[test]
@@ -738,4 +866,7 @@ mod tests {
assert_eq!(err.chars().count(), 200);
assert!(err.starts_with("serving unavailable: "));
}
}
}
#[cfg(test)]
mod branch_battery;
+1 -1
View File
@@ -1 +1 @@
exit=0 result=cargo test 11 passed 0 failed
exit=0 result=phase B cargo test 11 passed 0 failed (battery verbatim unchanged)
+1
View File
@@ -0,0 +1 @@
exit=0 result=phase B clippy --all-targets -D warnings clean
+1 -1
View File
@@ -1 +1 @@
exit=0 result=replay: 54/54 (kernel via ccm_loader + facade dispatch_exec)
exit=0 result=phase B replay: 54/54 kernel+facade parity
+1 -1
View File
@@ -1 +1 @@
exit=0 result=pytest test_substrate_seam 12 passed (RUNE_CONSTRUCTION_COMMANDS_CORE auto/rust)
exit=0 result=phase B pytest live delegation 12 passed
+1 -1
View File
@@ -1 +1 @@
exit=0 result=pytest test_substrate_seam 12 passed (RUNE_CONSTRUCTION_COMMANDS_CORE=python)
exit=0 result=phase B pytest python pin 12 passed
@@ -4,6 +4,15 @@ Worktree: /mnt/workdrive/rune-wave-cx2 · Oracle: src/rune/construction_commands
(268L, 5 fns, 1/5 before) · Facade+kernel port; facade = oracle's exact 5-fn surface
(393L), Rust kernel = pure decision surface.
Two commits: **Phase A** parity port (feat(construction_commands): port to
FULL Rust) + **Phase B** language-advantage pass (refactor(construction_commands):
Phase B — typed `CommandType`/`Op`/`PhaseClass` lattices; exhaustive
`(command, phase-class)` tuple match replaces the string-arm chains, so a
future vocabulary member cannot be handled at one site and forgotten at
another — the oracle's defensive "unreachable validator branch" becomes
structurally impossible. Behavior byte-identical: the frozen battery is
the referee, ZERO truth changes across the pass).
## Ladder evidence (all run in this checkout)
| Layer | Command | Evidence | Result |