Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

### Fixed

- 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.

- `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.
Expand Down
2 changes: 2 additions & 0 deletions docs/architecture/application-contract-hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
application operation, target-specific budget override, or mutation authority.
Expand Down
26 changes: 24 additions & 2 deletions xtask/src/run_edict_operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,10 @@ pub fn run(config: RunEdictOperationConfig) -> Result<RunEdictOperationReport> {
.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());
Expand Down Expand Up @@ -526,7 +529,10 @@ pub fn run(config: RunEdictOperationConfig) -> Result<RunEdictOperationReport> {
{
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,
Expand Down Expand Up @@ -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<OperationInput> {
if configuration.node_key_field == configuration.replacement_field
|| configuration.node_key_field == "basis"
Expand Down
36 changes: 36 additions & 0 deletions xtask/tests/run_edict_operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -410,6 +412,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"
Comment thread
flyingrobots marked this conversation as resolved.
);
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();
Expand Down
Loading