diff --git a/CHANGELOG.md b/CHANGELOG.md index 99956fd..f77eb7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -167,6 +167,9 @@ Semantic Versioning after its first public release. ### Changed +- Clarify that `dry-run` renders a planning and mount-review surface rather + than a self-contained replay bundle; copied container arguments require + independently validated, caller-owned writable mount sources. - Plans with no artifact contracts now finalize with an empty artifact manifest without requiring or creating a managed `artifacts` directory; plans with declared contracts retain the existing fail-closed observation checks. diff --git a/docs/LOCAL_RUN.md b/docs/LOCAL_RUN.md index 5dd7bc8..43aad84 100644 --- a/docs/LOCAL_RUN.md +++ b/docs/LOCAL_RUN.md @@ -22,8 +22,14 @@ values. Use `--json` to print the canonical receipt. Raw stdout and stderr remain local bounded process state and are not emitted by default or stored in the receipt; only their canonical digest, truncation state, exit status, and duration are -recorded. Use `dry-run` to inspect the exact container argv and reproduce a -failing command deliberately when deeper local diagnostics are required. +recorded. `dry-run` is a planning and mount-review surface: it inspects the +exact container argv, executes no command, and does not create cache +directories. It renders final `entries/sha256-/data` sources, while a live +`run` may prepare private per-generation staging sources. Do not directly +replay the rendered Docker argv. Before a diagnostic invocation, every host +source must be independently proven to exist, and the diagnostic owns its +writable lifecycle. Such an invocation is diagnostic, not qualification +evidence. Heavy commands use a default-on host-wide single-slot admission queue shared by independent repositories, worktrees, agent activities, and cache roots. Override diff --git a/tests/cache_pin_contract.rs b/tests/cache_pin_contract.rs index 4970ab8..08a893f 100644 --- a/tests/cache_pin_contract.rs +++ b/tests/cache_pin_contract.rs @@ -68,3 +68,27 @@ fn cache_pin_documentation_contract() { assert!(text.contains(required), "{path} missing {required}"); } } + +#[test] +fn local_run_documentation_does_not_present_dry_run_argv_as_a_replay_bundle() { + let root = Path::new(env!("CARGO_MANIFEST_DIR")); + let local_run = + fs::read_to_string(root.join("docs/LOCAL_RUN.md")).expect("read local run contract"); + let normalized = local_run.split_whitespace().collect::>().join(" "); + + for required in [ + "planning and mount-review surface", + "does not create cache directories", + "entries/sha256-/data", + "private per-generation staging sources", + "must be independently proven to exist", + "owns its writable lifecycle", + ] { + assert!(normalized.contains(required), "missing {required}"); + } + + assert!( + !normalized.contains("reproduce a failing command deliberately"), + "dry-run argv must not be presented as a self-contained replay bundle" + ); +}