From b7fcdb85aa5f51b14788dba71f3421a527b80ef4 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 7 Oct 2026 06:13:31 -0700 Subject: [PATCH 1/2] fix: report bounded typed Action outcomes in runner errors --- CHANGELOG.md | 2 ++ .../application-contract-hosting.md | 2 ++ xtask/src/run_edict_operation.rs | 26 ++++++++++++-- xtask/tests/run_edict_operation.rs | 34 +++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83a753c1b..007c243cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ ### Fixed +- The generic operation runner names typed obstruction kinds, footprint conflicts, and missing outcomes in bounded errors. It omits raw outcome records and invocation data on both Action error paths. + - `xtask run-edict-operation` runs outside Git and resolves relative artifact and WAL paths from the caller directory. Repository maintenance commands retain their Git-root behavior. - `WorldlineState::state_root` documentation now states its reachable-state boundary. Detached create-if-absent writes remain bound by patch and commit identities. Hash bytes are unchanged. diff --git a/docs/architecture/application-contract-hosting.md b/docs/architecture/application-contract-hosting.md index f9058ffba..1f8e7f26f 100644 --- a/docs/architecture/application-contract-hosting.md +++ b/docs/architecture/application-contract-hosting.md @@ -165,6 +165,8 @@ tests, but it is not the application lifecycle. The create-if-absent profile creates a node and its alpha attachment. It does not create a skeleton edge from the lane root. A detached cell therefore remains outside the reachable-state root hash. Equal roots do not prove equal stores or the absence of a detached write. The retained tick patch and commit identity bind that write; duplicate checks also compare the typed target-value digest. This preserves the [Merkle commit law](../spec/merkle-commit.md). +The generic operation runner distinguishes missing Action outcomes, typed obstructions, and footprint conflicts in its error messages. Both the first Action and unexpected duplicate outcomes use bounded categories and omit invocation data. This diagnostic boundary does not change retained obstruction encoding. + The external-provider schema additionally admits one exact zero-choice `compiler-produced-bounded-pure/v1` target configuration. It contains no application operation, target-specific budget override, or mutation authority. diff --git a/xtask/src/run_edict_operation.rs b/xtask/src/run_edict_operation.rs index 1044b0d9f..ee483221f 100644 --- a/xtask/src/run_edict_operation.rs +++ b/xtask/src/run_edict_operation.rs @@ -381,7 +381,10 @@ pub fn run(config: RunEdictOperationConfig) -> Result { .echo_operation_action_outcome_v1(&first_submission_id) { Some(EchoOperationActionOutcomeV1::Committed(receipt)) => receipt, - _ => bail!("scheduler did not publish a committed typed Action outcome"), + outcome => bail!( + "scheduler did not publish a committed typed Action outcome: {}", + action_outcome_summary(outcome) + ), }; tick_commit_id = hex::encode(committed_receipt.commit_id()); receipt_digest = hex::encode(committed_receipt.digest()); @@ -526,7 +529,10 @@ pub fn run(config: RunEdictOperationConfig) -> Result { { package.obstruction_coordinate.clone() } - outcome => bail!("duplicate Action produced unexpected outcome: {outcome:?}"), + outcome => bail!( + "duplicate Action produced unexpected outcome: {}", + action_outcome_summary(outcome) + ), }; duplicate = duplicate_report( duplicate_obstruction, @@ -958,6 +964,22 @@ fn validate_package_configuration( Ok(()) } +fn action_outcome_summary(outcome: Option<&EchoOperationActionOutcomeV1>) -> String { + match outcome { + Some(EchoOperationActionOutcomeV1::Committed(_)) => "committed".to_owned(), + Some(EchoOperationActionOutcomeV1::Obstructed(obstruction)) => { + format!("obstructed: {:?}", obstruction.kind()) + } + Some(EchoOperationActionOutcomeV1::RejectedFootprintConflict(conflict)) => { + format!( + "footprint conflict ({} blockers)", + conflict.blocked_by().len() + ) + } + None => "missing typed Action outcome".to_owned(), + } +} + fn parse_input(bytes: &[u8], configuration: &TargetConfiguration) -> Result { if configuration.node_key_field == configuration.replacement_field || configuration.node_key_field == "basis" diff --git a/xtask/tests/run_edict_operation.rs b/xtask/tests/run_edict_operation.rs index 4d6e565b2..79bab211c 100644 --- a/xtask/tests/run_edict_operation.rs +++ b/xtask/tests/run_edict_operation.rs @@ -410,6 +410,40 @@ fn malformed_input_and_nonempty_wal_fail_closed() { ); } +#[test] +fn result_projection_obstruction_reports_a_bounded_kind() { + let run_dir = TempRunDir::new(); + let input_path = run_dir.path().join("wide-projection-input.json"); + let private_key = "private-input-marker".repeat(256); + fs::write( + &input_path, + serde_json::to_vec(&serde_json::json!({ + "basis": "u0", + "key": private_key, + "value": "small", + })) + .expect("wide fixture is JSON"), + ) + .expect("wide fixture is writable"); + let output = runner_command( + &fixture_path("executable-operation-package.cbor"), + &fixture_path("verification-report.cbor"), + &input_path, + &run_dir.path().join("wal"), + ) + .output() + .expect("the obstructed runner starts"); + assert_rejected(&output, "ResultProjectionInvalid"); + assert!( + output.stderr.len() <= 256, + "outcome error must stay bounded" + ); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(!stderr.contains("private-input-marker")); + assert!(!stderr.contains("invocation_admission")); + assert!(!stderr.contains("EchoOperationObstructionV1 {")); +} + #[test] fn replacement_bound_is_enforced_before_runtime_submission() { let run_dir = TempRunDir::new(); From 08a2416315bc014b65fc605839a5984b33c269a7 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 7 Oct 2026 06:40:31 -0700 Subject: [PATCH 2/2] test: isolate diagnostic summary bounds from backtrace settings --- CHANGELOG.md | 2 +- docs/architecture/application-contract-hosting.md | 2 +- xtask/tests/run_edict_operation.rs | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 007c243cb..571990e85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ ### Fixed -- The generic operation runner names typed obstruction kinds, footprint conflicts, and missing outcomes in bounded errors. It omits raw outcome records and invocation data on both Action error paths. +- The generic operation runner names typed obstruction kinds, footprint conflicts, and missing outcomes in bounded summaries. It omits raw outcome records and invocation data on both Action error paths. - `xtask run-edict-operation` runs outside Git and resolves relative artifact and WAL paths from the caller directory. Repository maintenance commands retain their Git-root behavior. diff --git a/docs/architecture/application-contract-hosting.md b/docs/architecture/application-contract-hosting.md index 1f8e7f26f..f272e5552 100644 --- a/docs/architecture/application-contract-hosting.md +++ b/docs/architecture/application-contract-hosting.md @@ -165,7 +165,7 @@ tests, but it is not the application lifecycle. The create-if-absent profile creates a node and its alpha attachment. It does not create a skeleton edge from the lane root. A detached cell therefore remains outside the reachable-state root hash. Equal roots do not prove equal stores or the absence of a detached write. The retained tick patch and commit identity bind that write; duplicate checks also compare the typed target-value digest. This preserves the [Merkle commit law](../spec/merkle-commit.md). -The generic operation runner distinguishes missing Action outcomes, typed obstructions, and footprint conflicts in its error messages. Both the first Action and unexpected duplicate outcomes use bounded categories and omit invocation data. This diagnostic boundary does not change retained obstruction encoding. +The generic operation runner distinguishes missing Action outcomes, typed obstructions, and footprint conflicts in its error messages. Both the first Action and unexpected duplicate outcomes use bounded summaries and omit invocation data. Opted-in Rust backtraces remain separate diagnostic output. This diagnostic boundary does not change retained obstruction encoding. The external-provider schema additionally admits one exact zero-choice `compiler-produced-bounded-pure/v1` target configuration. It contains no diff --git a/xtask/tests/run_edict_operation.rs b/xtask/tests/run_edict_operation.rs index 79bab211c..2e0c2a450 100644 --- a/xtask/tests/run_edict_operation.rs +++ b/xtask/tests/run_edict_operation.rs @@ -103,6 +103,8 @@ fn runner_command_with_closure( ) -> Command { let mut command = Command::new(env!("CARGO_BIN_EXE_xtask")); command + .env_remove("RUST_BACKTRACE") + .env_remove("RUST_LIB_BACKTRACE") .arg("run-edict-operation") .arg("--package") .arg(package)