Document verified Scenario selection adapter and native evidence

This commit is contained in:
2026-09-24 14:55:30 -07:00
parent ed55b1781d
commit 3b2bb8ba78
20 changed files with 1920 additions and 0 deletions
+1
View File
@@ -12,6 +12,7 @@
*.png binary
*.jpg binary
*.zip binary
harness/scenario-selection-evidence-20260924/** -text whitespace=cr-at-eol
# Exact source/recovery oracles and archived ledgers retain their measured bytes.
# Keep ordinary text diffs; only disable checkout/check-in newline conversion.
+51
View File
@@ -0,0 +1,51 @@
# Scenario selection adapter
Branch-specific continuation of [the Scenario group planner](scenario-group-planner-20260924.md), tracked in [Gitea #29](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/29). This work is outside the frozen #37 prerelease candidate. It does not complete Scenario group movement or aggregate removal.
## Behavior and boundaries
Scenario selection uses exact rendered targets and the shared insertion-ordered selection with an explicit primary. Its envelope additionally includes the complete Scenario representation and visit, so saved A, saved B, and Automatic remain independent input destinations. Shift-click toggles a member; a stationary plain click on a selected group member selects only that member. A background press clears selection and retains ordinary panning. Contextual actions expose Add, Remove from selection, Inspect, and Select only. The normal workspace shows the selected count and identifies the primary whose properties are inspected.
Selecting a group does not grant permission to apply a singleton operation to its primary. Motion, deletion and index arrows refuse the whole group while the semantic operation adapter is unfinished. Ordinary first-press singleton saved spacing remains available, including a press on a message different from the current singleton. Automatic singleton ordering remains on its existing path. Selection alone does not edit model/source or history; pending edits are not silently applied.
The source boundary preserves existing saved body/header spacing. The open group gesture decision and semantic destination preview are recorded in [the group policy](scenario-group-reorder-policy-20260924.md); this increment does not resolve them by reinterpreting a saved body drag.
## Review and experiments
The first adapter source was `879eb1d5c8792a720433022e49f415d2c58ff72c`. Its Windows Scenario run executed 263 passing tests, one failing test, and one ignored test. All 809 captured inputs remained unchanged and matched that Git commit. The failure was the older mounted deletion test's expectation that a menu snapshot remained valid after a Scenario owner change. The new destination check correctly invalidated it. The corrected test first asserts that real invalidation, then uses a deliberately current unqualified snapshot to isolate the independent captured-owner guard; it retains actual removal, source consistency, and Undo assertions.
Independent review also identified two production issues: selection Clear advanced the camera lifetime and interrupted the pan started by the same blank press; legacy singleton selection reconciliation could change generation without retiring an old spacing Begin. The correction retires spacing through the canvas context on all admitted selection changes, while keeping the camera lifetime during selection-only input. A legacy A/B/A evaluator covers both generic selection and Scenario canvas selection. Mounted tests cover same-press blank panning with singleton/group selection and batched/split events.
Additional mounted evaluators use the actual Properties Name input to test passive Shift across focus, busy rebuilding, release and window loss. A saved spacing evaluator begins with an unrelated singleton, requires Begin on the first press without a replacement-selection packet, checks the complete expected project after the drag, and verifies one Undo/Redo. These are production widget-tree tests; busy rebuilding is not evidence of a child-state remount.
The next run at `a0a70340a625c16b1249d0beb71dca4fdd206636` passed 266 tests and failed two, with one ignored. All new mounted tests and the deletion fixture passed. One old direct reorder assertion still expected selection to preserve its gesture context; the test now requires a new epoch with the same view visit and captures a fresh reorder. The other failure exposed a real regression: unconditional retirement during reconciliation invalidated a newly scheduled reveal. Retirement now occurs at the App action boundary only when its complete context is unchanged and its selection snapshot changed. Navigation that already advanced the context retains its newly scheduled reveal. Independent review was updated to acknowledge the missed reveal path.
Run `selection-scenario-3` was intentionally interrupted because the parent started it before the formatter/commit command finished. `app.rs` changed after input capture; its closed result records exit 1 and `inputsUnchanged: false`, with no executable or executed tests. It is preserved in [the invalid-attempt archive](scenario-selection-evidence-20260924/invalid-attempt-3/manifest.json), excluded from accepted checks, and has no source-proof claim.
Corrective source `ca1f18e6b0d53814f0ff16261f810aef49281c26`: `selection-scenario-4` passed 268 tests, zero failed, one ignored; the complete menu filter passed 14 tests with none failed or ignored. Formatting passed. Each closed run retained unchanged 809-input captures, with an independent comparison to the exact Git blobs.
Strict lint then found a redundant `expect` following an `is_some` check. Final source `ed55b1781d86ec49af5d2588cbd06f7b878d9009` binds the same captured Context/Snapshot directly with `if let`. Independent review found no behavior change in either group press or background Clear. Strict lint, formatting, executable build and the final-source regression repeat all pass with exact input proofs.
UTC changed discontinuously during run 4, also observed by both prerelease peers. The raw timestamps are retained verbatim; its separately measured monotonic duration is 515.96 seconds. Do not calculate build duration or infer inactivity from the wall-clock difference. Source/stream/executable pins and process completion are independent of that clock change.
## Final-source verification
Windows toolchain: Cargo/Rust 1.98.1, `stable-x86_64-pc-windows-gnullvm`, LLVM-MinGW 20260826, clang 23.1; `CARGO_BUILD_JOBS=1`, `CARGO_INCREMENTAL=0`. Linux builds/checks remain excluded by the user's current prerelease instruction.
| Command | Receipt | Result |
| --- | --- | --- |
| `cargo clippy -p oa-desktop --all-targets --message-format=json -- -D warnings` | selection-lint-2 | Pass |
| `cargo fmt --all -- --check` | selection-format-3 | Pass |
| `cargo build -p oa-desktop --bin arcadia-studio --message-format=json` | selection-build-1 | Pass |
| `cargo test -p oa-desktop --bin arcadia-studio --message-format=json scenario -- --test-threads=1` | selection-scenario-5 | 268 passed, 0 failed, 1 ignored |
| `cargo test -p oa-desktop --bin arcadia-studio --message-format=json model_menu -- --test-threads=1` | selection-menu-2 | 14 passed, 0 failed, 0 ignored |
Filters overlap and must not be summed as unique tests. The ignored Scenario timing test is not a pass. These are scoped desktop checks, not a full workspace gate. Each final receipt captured unchanged inputs and was independently matched to all 809 exact Git-visible input blobs at `ed55b178`.
[Portable checks](scenario-selection-evidence-20260924/checks/README.md) contain 12 closed runs, three preserved ordinary failures, 12 source proofs and four distinct input capsules. The invalid changed-input attempt remains separate. Checks ZIP: 12,926,901 bytes, SHA256 `05d040e856d0b37adf44f6a9e3d2789d4d76c026e61e85fdfcab0e7bebca4dd4`. Run `python -B harness/scenario-selection-evidence-20260924/checks/verify.py`. The packaging self-check also passed archive equality, existing-output refusal and 11 corruption rejection cases; its local command output is not an archived raw gate. [Independent final audit](scenario-selection-evidence-20260924/reviews/final-checks.md) repeated all four input/Git comparisons and raw archive comparisons.
[Native Windows exercise](scenario-selection-evidence-20260924/native/README.md) records contextual group selection, primary inspection, whole-group refusal, same-press background clear/pan, stationary singleton collapse, first-press singleton spacing, exact saved-source comparisons and one Undo. Selection-only Save all preserves exact source bytes. The spacing edit changes only the expected saved A ordinate; Undo restores the entire original file. Representation navigation and a clean exit were also observed. Native Shift and other unexercised paths are explicitly separated from mounted test evidence. Run `python -B harness/scenario-selection-evidence-20260924/native/verify.py` for archived byte and source-delta checks.
The [independent native evidence review](scenario-selection-evidence-20260924/reviews/native-checks.md) verified all 99 original archive members, linked the build to the accepted Cargo package, compared the executable and fixture, repeated the full saved-file comparisons, and inspected the pan/collapse/spacing screenshot pairs. It found no material issue within this scope.
This delivers the selection adapter increment for review on its contribution branch. It remains outside frozen #37, does not complete #29, and does not claim integrated or published-release acceptance. Group movement/removal and their user-facing destination design remain next work.
@@ -0,0 +1,14 @@
# Scenario selection captured checks
These tools package explicitly named, completed runs from `CHECKOUT/.local/scenario-group-gates` without executing archived source or running Cargo. Adapted only in input-directory and manifest-kind names from the published prerelease Connection/Route evidence tools at `93d2a6f9af40061bf92cd81ba33d5648d0181cb1`.
```powershell
python -B prepare.py --root CHECKOUT --runs selection-scenario-1 selection-scenario-2 selection-format-1 --output NEW_DIRECTORY
python -B NEW_DIRECTORY/verify.py
```
The manifest is authoritative for the selected runs. Failed runs remain failures: archive consistency is not a test pass. Every raw stream and receipt retains its original bytes. Identical input maps and ZIPs are stored by hash; the logical-file map preserves their original run paths. Historical capture helpers and exact source comparators are included as data, never executed by verification.
Source proofs compare every captured input with a named Git commit; the offline package verifier checks those receipts and their pinned comparator identity but does not repeat the Git comparison. The captured HEAD and the named proof commit retain their respective meanings. The verifier checks archive inventory, hashes, input ZIP contents, process/command/result consistency and executable artifact relationships. It does not establish hermetic builds, native workflow acceptance, UI integration, or release acceptance.
Use `--check-only` instead of `--output` for an in-memory consistency check. Existing output directories refuse. Python 3.11+ standard library is sufficient; `-B` avoids bytecode writes.
@@ -0,0 +1,22 @@
{
"prepare.py": {
"bytes": 6308,
"sha256": "2d500f5d43a49893abb3936f880e61b8adc623531e26f0288983502e386894db"
},
"verify.py": {
"bytes": 11436,
"sha256": "ea8e781003bec21f2ac4af23e21b68ca5639a98eb31505c10b2869f57565e917"
},
"README.md": {
"bytes": 1641,
"sha256": "4c5c582cbdf02c8704b9add656d273c275b10eed87219131e3bba639a4c4bd8a"
},
"manifest.json": {
"bytes": 32714,
"sha256": "113c6c4275c8bccb69086bb52b6ce62b8cc2eb743be9febd107b425c51933429"
},
"evidence.zip": {
"bytes": 12926901,
"sha256": "05d040e856d0b37adf44f6a9e3d2789d4d76c026e61e85fdfcab0e7bebca4dd4"
}
}
@@ -0,0 +1,737 @@
{
"schema": 1,
"kind": "scenario-selection-cargo-gates",
"runs": {
"selection-scenario-1": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--bin",
"arcadia-studio",
"--message-format=json",
"scenario",
"--",
"--test-threads=1"
],
"head": "879eb1d5c8792a720433022e49f415d2c58ff72c",
"exitCode": 101,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "879eb1d5c8792a720433022e49f415d2c58ff72c",
"pin": {
"bytes": 599,
"sha256": "6dd4a2e751a46252063e0cd61fb1a23dbd6aa0639519e19a0e7e2f73b781714d"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-scenario-2": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--bin",
"arcadia-studio",
"--message-format=json",
"scenario",
"--",
"--test-threads=1"
],
"head": "a0a70340a625c16b1249d0beb71dca4fdd206636",
"exitCode": 101,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "a0a70340a625c16b1249d0beb71dca4fdd206636",
"pin": {
"bytes": 599,
"sha256": "164bc88b11dda75d38d4b61f44c8a03089df73dbbf031905bc2cccbf0997ad61"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-format-1": {
"command": [
"cargo",
"fmt",
"--all",
"--",
"--check"
],
"head": "a0a70340a625c16b1249d0beb71dca4fdd206636",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "a0a70340a625c16b1249d0beb71dca4fdd206636",
"pin": {
"bytes": 597,
"sha256": "c2d44e0b2ec9d93676f270404c3bc814bbc006aacb4df53f248fe9e01635c055"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-format-2": {
"command": [
"cargo",
"fmt",
"--all",
"--",
"--check"
],
"head": "ca1f18e6b0d53814f0ff16261f810aef49281c26",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ca1f18e6b0d53814f0ff16261f810aef49281c26",
"pin": {
"bytes": 597,
"sha256": "3add2c994cf44be9756c6843214104c3ae9fd3092d031be816013dfba5b43401"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-scenario-4": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--bin",
"arcadia-studio",
"--message-format=json",
"scenario",
"--",
"--test-threads=1"
],
"head": "ca1f18e6b0d53814f0ff16261f810aef49281c26",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ca1f18e6b0d53814f0ff16261f810aef49281c26",
"pin": {
"bytes": 599,
"sha256": "a524249d9fa4ba6b29086e103780c03e345850b3cd390d92ec924ac6e5899b40"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-menu-1": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--bin",
"arcadia-studio",
"--message-format=json",
"model_menu",
"--",
"--test-threads=1"
],
"head": "ca1f18e6b0d53814f0ff16261f810aef49281c26",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ca1f18e6b0d53814f0ff16261f810aef49281c26",
"pin": {
"bytes": 595,
"sha256": "978da6f95bf74ce0fa7ac871ec582a7312ca87576d7f39f4b5ebec71fa96e9fd"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-lint-1": {
"command": [
"cargo",
"clippy",
"-p",
"oa-desktop",
"--all-targets",
"--message-format=json",
"--",
"-D",
"warnings"
],
"head": "ca1f18e6b0d53814f0ff16261f810aef49281c26",
"exitCode": 101,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ca1f18e6b0d53814f0ff16261f810aef49281c26",
"pin": {
"bytes": 595,
"sha256": "386bc87e04ab7eea93781fbe41a9366843125fc1d08f6e967ad98b9c88b58651"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-lint-2": {
"command": [
"cargo",
"clippy",
"-p",
"oa-desktop",
"--all-targets",
"--message-format=json",
"--",
"-D",
"warnings"
],
"head": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"pin": {
"bytes": 595,
"sha256": "616d9d9f6c7a49220c6cda6497ca30e78f309b753745853e7b43a5ab31dbee47"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-format-3": {
"command": [
"cargo",
"fmt",
"--all",
"--",
"--check"
],
"head": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"pin": {
"bytes": 597,
"sha256": "a867e8f941b4dc5c4651f571a10f341134775c1184b6b2e2cb8c1f8ad9ba410a"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-build-1": {
"command": [
"cargo",
"build",
"-p",
"oa-desktop",
"--bin",
"arcadia-studio",
"--message-format=json"
],
"head": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"pin": {
"bytes": 596,
"sha256": "657eb7e9a252e29764eda37557b81aed551750eaada46018b5f9f097a85cf780"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-scenario-5": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--bin",
"arcadia-studio",
"--message-format=json",
"scenario",
"--",
"--test-threads=1"
],
"head": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"pin": {
"bytes": 599,
"sha256": "5382c0455497058de4dbac8c263c1a23976bdef04d5ad165882d47c4420582fd"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"selection-menu-2": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--bin",
"arcadia-studio",
"--message-format=json",
"model_menu",
"--",
"--test-threads=1"
],
"head": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"exitCode": 0,
"inputCount": 809,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"pin": {
"bytes": 595,
"sha256": "3f8b03bc437717f8b5f185483e83dac4eca29dc9f344a31c7ee30fd9a32ba807"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
}
},
"captureHelpers": {
"gate.py": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"source-proof.py": "helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py"
},
"logicalFiles": {
"cargo/selection-build-1/inputs.json": "capsules/995ccb10de056f3b19370da8b47bc176de9fcc5fdddc5e292fd0c31252776aff.json",
"cargo/selection-build-1/inputs.zip": "capsules/405dd5aca8f8137a130af8b4e35c88b560e6b0f90637befca8c3483514276377.zip",
"cargo/selection-build-1/process.json": "cargo/selection-build-1/process.json",
"cargo/selection-build-1/result.json": "cargo/selection-build-1/result.json",
"cargo/selection-build-1/source-proof.json": "cargo/selection-build-1/source-proof.json",
"cargo/selection-build-1/started.json": "cargo/selection-build-1/started.json",
"cargo/selection-build-1/stderr.log": "cargo/selection-build-1/stderr.log",
"cargo/selection-build-1/stdout.log": "cargo/selection-build-1/stdout.log",
"cargo/selection-format-1/inputs.json": "capsules/36a101249a3038caf64293c4339bc2ac356e6730c8c57bd7b755eff8dbd43865.json",
"cargo/selection-format-1/inputs.zip": "capsules/0ddad5faad0d547e591142b95d4cd94064e621ce648f97cceedc5f27aa2a4275.zip",
"cargo/selection-format-1/process.json": "cargo/selection-format-1/process.json",
"cargo/selection-format-1/result.json": "cargo/selection-format-1/result.json",
"cargo/selection-format-1/source-proof.json": "cargo/selection-format-1/source-proof.json",
"cargo/selection-format-1/started.json": "cargo/selection-format-1/started.json",
"cargo/selection-format-1/stderr.log": "cargo/selection-format-1/stderr.log",
"cargo/selection-format-1/stdout.log": "cargo/selection-format-1/stdout.log",
"cargo/selection-format-2/inputs.json": "capsules/33112027b1b34993570bb3d35d44fa53fc82249f5a711a93f3ef1f6cc52f1cfd.json",
"cargo/selection-format-2/inputs.zip": "capsules/70cd4a304c5f314225283e548d884edc584ebde476c451948c4e230ee65a6a24.zip",
"cargo/selection-format-2/process.json": "cargo/selection-format-2/process.json",
"cargo/selection-format-2/result.json": "cargo/selection-format-2/result.json",
"cargo/selection-format-2/source-proof.json": "cargo/selection-format-2/source-proof.json",
"cargo/selection-format-2/started.json": "cargo/selection-format-2/started.json",
"cargo/selection-format-2/stderr.log": "cargo/selection-format-2/stderr.log",
"cargo/selection-format-2/stdout.log": "cargo/selection-format-2/stdout.log",
"cargo/selection-format-3/inputs.json": "capsules/995ccb10de056f3b19370da8b47bc176de9fcc5fdddc5e292fd0c31252776aff.json",
"cargo/selection-format-3/inputs.zip": "capsules/405dd5aca8f8137a130af8b4e35c88b560e6b0f90637befca8c3483514276377.zip",
"cargo/selection-format-3/process.json": "cargo/selection-format-3/process.json",
"cargo/selection-format-3/result.json": "cargo/selection-format-3/result.json",
"cargo/selection-format-3/source-proof.json": "cargo/selection-format-3/source-proof.json",
"cargo/selection-format-3/started.json": "cargo/selection-format-3/started.json",
"cargo/selection-format-3/stderr.log": "cargo/selection-format-3/stderr.log",
"cargo/selection-format-3/stdout.log": "cargo/selection-format-3/stdout.log",
"cargo/selection-lint-1/inputs.json": "capsules/33112027b1b34993570bb3d35d44fa53fc82249f5a711a93f3ef1f6cc52f1cfd.json",
"cargo/selection-lint-1/inputs.zip": "capsules/70cd4a304c5f314225283e548d884edc584ebde476c451948c4e230ee65a6a24.zip",
"cargo/selection-lint-1/process.json": "cargo/selection-lint-1/process.json",
"cargo/selection-lint-1/result.json": "cargo/selection-lint-1/result.json",
"cargo/selection-lint-1/source-proof.json": "cargo/selection-lint-1/source-proof.json",
"cargo/selection-lint-1/started.json": "cargo/selection-lint-1/started.json",
"cargo/selection-lint-1/stderr.log": "cargo/selection-lint-1/stderr.log",
"cargo/selection-lint-1/stdout.log": "cargo/selection-lint-1/stdout.log",
"cargo/selection-lint-2/inputs.json": "capsules/995ccb10de056f3b19370da8b47bc176de9fcc5fdddc5e292fd0c31252776aff.json",
"cargo/selection-lint-2/inputs.zip": "capsules/405dd5aca8f8137a130af8b4e35c88b560e6b0f90637befca8c3483514276377.zip",
"cargo/selection-lint-2/process.json": "cargo/selection-lint-2/process.json",
"cargo/selection-lint-2/result.json": "cargo/selection-lint-2/result.json",
"cargo/selection-lint-2/source-proof.json": "cargo/selection-lint-2/source-proof.json",
"cargo/selection-lint-2/started.json": "cargo/selection-lint-2/started.json",
"cargo/selection-lint-2/stderr.log": "cargo/selection-lint-2/stderr.log",
"cargo/selection-lint-2/stdout.log": "cargo/selection-lint-2/stdout.log",
"cargo/selection-menu-1/inputs.json": "capsules/33112027b1b34993570bb3d35d44fa53fc82249f5a711a93f3ef1f6cc52f1cfd.json",
"cargo/selection-menu-1/inputs.zip": "capsules/70cd4a304c5f314225283e548d884edc584ebde476c451948c4e230ee65a6a24.zip",
"cargo/selection-menu-1/process.json": "cargo/selection-menu-1/process.json",
"cargo/selection-menu-1/result.json": "cargo/selection-menu-1/result.json",
"cargo/selection-menu-1/source-proof.json": "cargo/selection-menu-1/source-proof.json",
"cargo/selection-menu-1/started.json": "cargo/selection-menu-1/started.json",
"cargo/selection-menu-1/stderr.log": "cargo/selection-menu-1/stderr.log",
"cargo/selection-menu-1/stdout.log": "cargo/selection-menu-1/stdout.log",
"cargo/selection-menu-2/inputs.json": "capsules/995ccb10de056f3b19370da8b47bc176de9fcc5fdddc5e292fd0c31252776aff.json",
"cargo/selection-menu-2/inputs.zip": "capsules/405dd5aca8f8137a130af8b4e35c88b560e6b0f90637befca8c3483514276377.zip",
"cargo/selection-menu-2/process.json": "cargo/selection-menu-2/process.json",
"cargo/selection-menu-2/result.json": "cargo/selection-menu-2/result.json",
"cargo/selection-menu-2/source-proof.json": "cargo/selection-menu-2/source-proof.json",
"cargo/selection-menu-2/started.json": "cargo/selection-menu-2/started.json",
"cargo/selection-menu-2/stderr.log": "cargo/selection-menu-2/stderr.log",
"cargo/selection-menu-2/stdout.log": "cargo/selection-menu-2/stdout.log",
"cargo/selection-scenario-1/inputs.json": "capsules/e9a4588279a66a16ef89136867a2517b279a6f66ea08fedd66322c733d4b6396.json",
"cargo/selection-scenario-1/inputs.zip": "capsules/cc0e1425e15adacc48cf167bbada1eb34ebb651228b0429331d59ab7d3ba05fa.zip",
"cargo/selection-scenario-1/process.json": "cargo/selection-scenario-1/process.json",
"cargo/selection-scenario-1/result.json": "cargo/selection-scenario-1/result.json",
"cargo/selection-scenario-1/source-proof.json": "cargo/selection-scenario-1/source-proof.json",
"cargo/selection-scenario-1/started.json": "cargo/selection-scenario-1/started.json",
"cargo/selection-scenario-1/stderr.log": "cargo/selection-scenario-1/stderr.log",
"cargo/selection-scenario-1/stdout.log": "cargo/selection-scenario-1/stdout.log",
"cargo/selection-scenario-2/inputs.json": "capsules/36a101249a3038caf64293c4339bc2ac356e6730c8c57bd7b755eff8dbd43865.json",
"cargo/selection-scenario-2/inputs.zip": "capsules/0ddad5faad0d547e591142b95d4cd94064e621ce648f97cceedc5f27aa2a4275.zip",
"cargo/selection-scenario-2/process.json": "cargo/selection-scenario-2/process.json",
"cargo/selection-scenario-2/result.json": "cargo/selection-scenario-2/result.json",
"cargo/selection-scenario-2/source-proof.json": "cargo/selection-scenario-2/source-proof.json",
"cargo/selection-scenario-2/started.json": "cargo/selection-scenario-2/started.json",
"cargo/selection-scenario-2/stderr.log": "cargo/selection-scenario-2/stderr.log",
"cargo/selection-scenario-2/stdout.log": "cargo/selection-scenario-2/stdout.log",
"cargo/selection-scenario-4/inputs.json": "capsules/33112027b1b34993570bb3d35d44fa53fc82249f5a711a93f3ef1f6cc52f1cfd.json",
"cargo/selection-scenario-4/inputs.zip": "capsules/70cd4a304c5f314225283e548d884edc584ebde476c451948c4e230ee65a6a24.zip",
"cargo/selection-scenario-4/process.json": "cargo/selection-scenario-4/process.json",
"cargo/selection-scenario-4/result.json": "cargo/selection-scenario-4/result.json",
"cargo/selection-scenario-4/source-proof.json": "cargo/selection-scenario-4/source-proof.json",
"cargo/selection-scenario-4/started.json": "cargo/selection-scenario-4/started.json",
"cargo/selection-scenario-4/stderr.log": "cargo/selection-scenario-4/stderr.log",
"cargo/selection-scenario-4/stdout.log": "cargo/selection-scenario-4/stdout.log",
"cargo/selection-scenario-5/inputs.json": "capsules/995ccb10de056f3b19370da8b47bc176de9fcc5fdddc5e292fd0c31252776aff.json",
"cargo/selection-scenario-5/inputs.zip": "capsules/405dd5aca8f8137a130af8b4e35c88b560e6b0f90637befca8c3483514276377.zip",
"cargo/selection-scenario-5/process.json": "cargo/selection-scenario-5/process.json",
"cargo/selection-scenario-5/result.json": "cargo/selection-scenario-5/result.json",
"cargo/selection-scenario-5/source-proof.json": "cargo/selection-scenario-5/source-proof.json",
"cargo/selection-scenario-5/started.json": "cargo/selection-scenario-5/started.json",
"cargo/selection-scenario-5/stderr.log": "cargo/selection-scenario-5/stderr.log",
"cargo/selection-scenario-5/stdout.log": "cargo/selection-scenario-5/stdout.log",
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py",
"helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py": "helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py"
},
"members": {
"capsules/0ddad5faad0d547e591142b95d4cd94064e621ce648f97cceedc5f27aa2a4275.zip": {
"bytes": 3234298,
"sha256": "0ddad5faad0d547e591142b95d4cd94064e621ce648f97cceedc5f27aa2a4275"
},
"capsules/33112027b1b34993570bb3d35d44fa53fc82249f5a711a93f3ef1f6cc52f1cfd.json": {
"bytes": 135839,
"sha256": "33112027b1b34993570bb3d35d44fa53fc82249f5a711a93f3ef1f6cc52f1cfd"
},
"capsules/36a101249a3038caf64293c4339bc2ac356e6730c8c57bd7b755eff8dbd43865.json": {
"bytes": 135839,
"sha256": "36a101249a3038caf64293c4339bc2ac356e6730c8c57bd7b755eff8dbd43865"
},
"capsules/405dd5aca8f8137a130af8b4e35c88b560e6b0f90637befca8c3483514276377.zip": {
"bytes": 3234337,
"sha256": "405dd5aca8f8137a130af8b4e35c88b560e6b0f90637befca8c3483514276377"
},
"capsules/70cd4a304c5f314225283e548d884edc584ebde476c451948c4e230ee65a6a24.zip": {
"bytes": 3234365,
"sha256": "70cd4a304c5f314225283e548d884edc584ebde476c451948c4e230ee65a6a24"
},
"capsules/995ccb10de056f3b19370da8b47bc176de9fcc5fdddc5e292fd0c31252776aff.json": {
"bytes": 135839,
"sha256": "995ccb10de056f3b19370da8b47bc176de9fcc5fdddc5e292fd0c31252776aff"
},
"capsules/cc0e1425e15adacc48cf167bbada1eb34ebb651228b0429331d59ab7d3ba05fa.zip": {
"bytes": 3231538,
"sha256": "cc0e1425e15adacc48cf167bbada1eb34ebb651228b0429331d59ab7d3ba05fa"
},
"capsules/e9a4588279a66a16ef89136867a2517b279a6f66ea08fedd66322c733d4b6396.json": {
"bytes": 135839,
"sha256": "e9a4588279a66a16ef89136867a2517b279a6f66ea08fedd66322c733d4b6396"
},
"cargo/selection-build-1/process.json": {
"bytes": 164,
"sha256": "5b75f4cf36cadf630959bfcee18e340f337217a371f67fc9cd50fcaae55d62a1"
},
"cargo/selection-build-1/result.json": {
"bytes": 3219,
"sha256": "c7fe2098ad813e50f64f8f5ff45a7459859c74ff10fef68e8ef4cb151d5bfb7e"
},
"cargo/selection-build-1/source-proof.json": {
"bytes": 596,
"sha256": "657eb7e9a252e29764eda37557b81aed551750eaada46018b5f9f097a85cf780"
},
"cargo/selection-build-1/started.json": {
"bytes": 1538,
"sha256": "bc90bae8e10e0aeb2c23e4cf5c918f5b213be0c625ffe8da31884bd720ddca33"
},
"cargo/selection-build-1/stderr.log": {
"bytes": 310,
"sha256": "c5c4c5d1788772cb7b2d1a49370c7acef5996b3234b498cd9cb1a2bdfa1fec2f"
},
"cargo/selection-build-1/stdout.log": {
"bytes": 284131,
"sha256": "312000c0ce68949d4960dd9bb18454a4715fffe45cc2a3f5a5657910703504f7"
},
"cargo/selection-format-1/process.json": {
"bytes": 116,
"sha256": "2dd8cb1910f748c6723d23d5f50985efa6e5cc7e9cbf40e8251fde2b5af0558f"
},
"cargo/selection-format-1/result.json": {
"bytes": 1868,
"sha256": "423432a5475292ad7c7a90b5f72aa7963e3d4e2d7e3854e7c5f5d10e866fa100"
},
"cargo/selection-format-1/source-proof.json": {
"bytes": 597,
"sha256": "c2d44e0b2ec9d93676f270404c3bc814bbc006aacb4df53f248fe9e01635c055"
},
"cargo/selection-format-1/started.json": {
"bytes": 1480,
"sha256": "fa1bdc782ae26f6127b8662568c0eb6c7a1996add06943e2fd751717776ac628"
},
"cargo/selection-format-1/stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/selection-format-1/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/selection-format-2/process.json": {
"bytes": 116,
"sha256": "d2c1cac86c9d5038ca50ed6edd0cc210b77baa869f87242fcca3c5a018e4cc9a"
},
"cargo/selection-format-2/result.json": {
"bytes": 1868,
"sha256": "ed855ccae891d7b0eaa9bb07cfec7183d0145f5618712d820cab7127bc8d6274"
},
"cargo/selection-format-2/source-proof.json": {
"bytes": 597,
"sha256": "3add2c994cf44be9756c6843214104c3ae9fd3092d031be816013dfba5b43401"
},
"cargo/selection-format-2/started.json": {
"bytes": 1480,
"sha256": "f32fdfd7d926eef08a7268663b099f7f917fb7c12cdc1e721245a44a37981ca3"
},
"cargo/selection-format-2/stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/selection-format-2/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/selection-format-3/process.json": {
"bytes": 116,
"sha256": "6b80a98424fa35b2b38bc756318eaba3f54535fb3e56c3fbd140c555a6b785ac"
},
"cargo/selection-format-3/result.json": {
"bytes": 1868,
"sha256": "dd9b3129e1951e8b03c341f40f121b29f002c6cb776baf26808c64fe70f40c63"
},
"cargo/selection-format-3/source-proof.json": {
"bytes": 597,
"sha256": "a867e8f941b4dc5c4651f571a10f341134775c1184b6b2e2cb8c1f8ad9ba410a"
},
"cargo/selection-format-3/started.json": {
"bytes": 1480,
"sha256": "bb644f39da11e1f60172fb3c8c220ceba0a73a085faa9865fa1888225e3b9607"
},
"cargo/selection-format-3/stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/selection-format-3/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/selection-lint-1/process.json": {
"bytes": 178,
"sha256": "465892514aaf5a0857c56a548e2c63836d399536eadac421f88d916882d14b9d"
},
"cargo/selection-lint-1/result.json": {
"bytes": 1960,
"sha256": "90374fd2ae51a586f9039626d15deefae3353adeefbb8920f6ef66b95ed6b86f"
},
"cargo/selection-lint-1/source-proof.json": {
"bytes": 595,
"sha256": "386bc87e04ab7eea93781fbe41a9366843125fc1d08f6e967ad98b9c88b58651"
},
"cargo/selection-lint-1/started.json": {
"bytes": 1563,
"sha256": "664f8e03c3aa0a086c6d9b0004f17a57f5b9d3355f1a34c4117313cb1ca58b65"
},
"cargo/selection-lint-1/stderr.log": {
"bytes": 163,
"sha256": "6cd1a10aa8ce9ee8a5ac74fd1dacd158709abeeb9483b49fc61c272157cbfc6d"
},
"cargo/selection-lint-1/stdout.log": {
"bytes": 270943,
"sha256": "9ab293656c1c7df03508a522240e050efd6d380ef37ff2f6404babe233c4902d"
},
"cargo/selection-lint-2/process.json": {
"bytes": 179,
"sha256": "a93b393302c10f24887d17084c32d71d1b5c30ef7777dd20466815b0f912dfef"
},
"cargo/selection-lint-2/result.json": {
"bytes": 1958,
"sha256": "9f7397469f4ac8287d3ea10b418a73020eef350b670c4645c116246af2b779f3"
},
"cargo/selection-lint-2/source-proof.json": {
"bytes": 595,
"sha256": "616d9d9f6c7a49220c6cda6497ca30e78f309b753745853e7b43a5ab31dbee47"
},
"cargo/selection-lint-2/started.json": {
"bytes": 1563,
"sha256": "16ecb83d83f6361cf5d9d0316864378940920e6cfd32ab9a4c80ea474bef5e34"
},
"cargo/selection-lint-2/stderr.log": {
"bytes": 134,
"sha256": "aa99195a7582f495f50056e1db02db3e44240bdb140c98326308259eb0eca959"
},
"cargo/selection-lint-2/stdout.log": {
"bytes": 269431,
"sha256": "172cb2a751dbce271060ee14b6ec5c1245e417bb209a03e4e7569c56526ae9f9"
},
"cargo/selection-menu-1/process.json": {
"bytes": 203,
"sha256": "6039ae1765c928d56b53376ae5853a739e5c96bee5dd1b81f2e1f21ce19c7aba"
},
"cargo/selection-menu-1/result.json": {
"bytes": 3317,
"sha256": "07a2826b348bfc1363f75dd3cf2ec02bf9a197ca435e0e110e263513d58e5b7a"
},
"cargo/selection-menu-1/source-proof.json": {
"bytes": 595,
"sha256": "978da6f95bf74ce0fa7ac871ec582a7312ca87576d7f39f4b5ebec71fa96e9fd"
},
"cargo/selection-menu-1/started.json": {
"bytes": 1592,
"sha256": "f5624c379b683f850c43612586122a3f40d4328e4cd5b0e5c577c3bc75108736"
},
"cargo/selection-menu-1/stderr.log": {
"bytes": 210,
"sha256": "e5728659305abd888a0fef144908828ff52f0d748a3baacfdb7cb2d86192e078"
},
"cargo/selection-menu-1/stdout.log": {
"bytes": 294645,
"sha256": "e9f845ead540fa8160e85a03bdc805f814d3c7a9dc7beb9c5a1f3978065c4c94"
},
"cargo/selection-menu-2/process.json": {
"bytes": 203,
"sha256": "06a711647a26d589e6c30a13e83be0ac6f51a234cc16e7595df85f805411e324"
},
"cargo/selection-menu-2/result.json": {
"bytes": 3315,
"sha256": "779fde518b19d5f85a33158e6629b0c0a08b6f1520fde350043d9d237518dca5"
},
"cargo/selection-menu-2/source-proof.json": {
"bytes": 595,
"sha256": "3f8b03bc437717f8b5f185483e83dac4eca29dc9f344a31c7ee30fd9a32ba807"
},
"cargo/selection-menu-2/started.json": {
"bytes": 1592,
"sha256": "8d037d2319a470520f9d7755f467d865bcf9b7647956672e06b264512a474f21"
},
"cargo/selection-menu-2/stderr.log": {
"bytes": 210,
"sha256": "ecb7980dd03e7f4ba9c56cf9e199abd6031a88e36e236b1ac99315dc59e19731"
},
"cargo/selection-menu-2/stdout.log": {
"bytes": 294645,
"sha256": "4ffba3ef252a554a08ab8a6e0920e6f87e580d6548152a708743464f0e57c9fe"
},
"cargo/selection-scenario-1/process.json": {
"bytes": 201,
"sha256": "bff973999bfea9c00def564106659d0b5bbd05f5387818c611edae98f910ab18"
},
"cargo/selection-scenario-1/result.json": {
"bytes": 3317,
"sha256": "ca91df9036f49c43fcfe47cd0515eb6581401630c51d43eec7c0afdea088c4c6"
},
"cargo/selection-scenario-1/source-proof.json": {
"bytes": 599,
"sha256": "6dd4a2e751a46252063e0cd61fb1a23dbd6aa0639519e19a0e7e2f73b781714d"
},
"cargo/selection-scenario-1/started.json": {
"bytes": 1590,
"sha256": "a8bc241061e0eb80078eeef41720ef9b4888904b53068ac6429230b1b248fa51"
},
"cargo/selection-scenario-1/stderr.log": {
"bytes": 355,
"sha256": "bc821b72e52604d99572f3fcbe63dd7dd9a59c93bfcb011121a729501c161e4e"
},
"cargo/selection-scenario-1/stdout.log": {
"bytes": 326826,
"sha256": "467ba77948bdbfc429be7416955a8864dc660a1c9754d20ec39c0f7d60dd435d"
},
"cargo/selection-scenario-2/process.json": {
"bytes": 201,
"sha256": "3132573dd21a97b6b78e06147a8cbb1d6999028d9c60ae932442e3bb462fc7ba"
},
"cargo/selection-scenario-2/result.json": {
"bytes": 3317,
"sha256": "03b0d2b7039699e348d41c3969e7ff575e37c8d118dcc53867a266edb9d76f0e"
},
"cargo/selection-scenario-2/source-proof.json": {
"bytes": 599,
"sha256": "164bc88b11dda75d38d4b61f44c8a03089df73dbbf031905bc2cccbf0997ad61"
},
"cargo/selection-scenario-2/started.json": {
"bytes": 1590,
"sha256": "d297110e2995f50d5540a452f6ab652fba5416459bbb5200605d4d080ec0500c"
},
"cargo/selection-scenario-2/stderr.log": {
"bytes": 355,
"sha256": "08f0e397bd8873bcce61d9df0d66cba853f00a029ea59ac6748a4ebfcbf8f8bf"
},
"cargo/selection-scenario-2/stdout.log": {
"bytes": 328272,
"sha256": "92660586178ff00a0c7bb1c5257d9296b206fe31525e40b6a7909f676868eee8"
},
"cargo/selection-scenario-4/process.json": {
"bytes": 201,
"sha256": "1e16c0737c62b69cca9634dff1449b80d5c04906c35c3cdd00990e6f1608aa23"
},
"cargo/selection-scenario-4/result.json": {
"bytes": 3309,
"sha256": "10a7257534036979f89bea0c176ca81f90e9a0c3d9b6b36b85da79df415d7f03"
},
"cargo/selection-scenario-4/source-proof.json": {
"bytes": 599,
"sha256": "a524249d9fa4ba6b29086e103780c03e345850b3cd390d92ec924ac6e5899b40"
},
"cargo/selection-scenario-4/started.json": {
"bytes": 1590,
"sha256": "89d8089a688d86209de08dbf6c3aaba90493fde7d78542a695e0fc877db10af2"
},
"cargo/selection-scenario-4/stderr.log": {
"bytes": 284,
"sha256": "a7a607c88c6c19b1fc31ed42e51f85b8873fcaaaf3773d540118078ec538c4d0"
},
"cargo/selection-scenario-4/stdout.log": {
"bytes": 326707,
"sha256": "6f9080bbaa82b66722d1d1d5b1dd0541560fbe1a5e344cf601532c50b233246f"
},
"cargo/selection-scenario-5/process.json": {
"bytes": 201,
"sha256": "a8985e970ba4065afc687fbf537e793ab27b8ed07b79f60314690d24a434ae9f"
},
"cargo/selection-scenario-5/result.json": {
"bytes": 3316,
"sha256": "e9230b6d27237ed459ba45eb3e16f24d08501945e42d15b3dd275ac13713f80e"
},
"cargo/selection-scenario-5/source-proof.json": {
"bytes": 599,
"sha256": "5382c0455497058de4dbac8c263c1a23976bdef04d5ad165882d47c4420582fd"
},
"cargo/selection-scenario-5/started.json": {
"bytes": 1590,
"sha256": "b3a5f65569da96c54d31ab57ca13496ed475aae5b486a48731c733ffc72de949"
},
"cargo/selection-scenario-5/stderr.log": {
"bytes": 284,
"sha256": "f81b8cb9f2232b63f1b60c9d7b753c62fc3aa677b1cd44ccf960b9173fee1356"
},
"cargo/selection-scenario-5/stdout.log": {
"bytes": 326707,
"sha256": "60ca908e049dc967c90fed940babbdf1d36701149a32299a0b80a2d239af0422"
},
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": {
"bytes": 10094,
"sha256": "7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a"
},
"helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py": {
"bytes": 3778,
"sha256": "3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2"
},
"helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py": {
"bytes": 1396,
"sha256": "c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1"
}
}
}
@@ -0,0 +1,110 @@
"""Package explicitly selected closed gate captures without running Cargo."""
from pathlib import Path
import argparse
import json
import zipfile
from verify import RAW_FILES, pin, parsed, need, run_name, verify_data, verify_package
HERE = Path(__file__).resolve().parent
def collect(root, runs, helpers, comparators=()):
gates = root / ".local/scenario-group-gates"
candidates = [gates / name for name in ("gate.py", "source-proof.py")] + helpers
helper_bytes = [path.read_bytes() for path in candidates]
comparator_paths = [root / ".local/process-source-evidence-tools/verify.py", *comparators]
comparator_bytes = [path.read_bytes() for path in comparator_paths if path.is_file()]
payload, logical, specs = {}, {}, {}
def add(name, data):
stored = ("capsules/" + pin(data)["sha256"] + Path(name).suffix
if name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"} else name)
need(stored not in payload or payload[stored] == data, "deduplication collision")
payload[stored] = data
logical[name] = stored
helper_catalog = {}
for name, data in zip(("gate.py", "source-proof.py"), helper_bytes):
helper = "helpers/" + pin(data)["sha256"] + ".py"
add(helper, data)
helper_catalog[name] = helper
for run in runs:
folder = gates / run
need(folder.is_dir(), "run directory missing: " + run)
paths = list(folder.iterdir())
names = {path.name for path in paths}
need(all(path.is_file() and not path.is_symlink() for path in paths)
and names in (RAW_FILES, RAW_FILES | {"source-proof.json"}), "exact closed raw files required: " + run)
raw = {path.name: path.read_bytes() for path in paths}
result = parsed(raw["result.json"])
need("finish" in result and result["inputsUnchanged"] is True, "unfinished/changed run refused: " + run)
matches = [data for data in helper_bytes if pin(data) == result["helper"]]
need(bool(matches), "exact historical helper unavailable for " + run + "; supply --helper PATH")
helper = "helpers/" + result["helper"]["sha256"] + ".py"
add(helper, matches[0])
for name, data in sorted(raw.items()):
add("cargo/" + run + "/" + name, data)
specs[run] = {key: result[key] for key in ("command", "head", "exitCode", "inputCount")}
specs[run]["helper"] = helper
if "source-proof.json" in raw:
proof = parsed(raw["source-proof.json"])
matches = [data for data in comparator_bytes if pin(data) == proof["comparator"]]
need(bool(matches), "exact source comparator unavailable for " + run + "; supply --comparator PATH")
comparator = "comparators/" + proof["comparator"]["sha256"] + ".py"
add(comparator, matches[0])
specs[run]["sourceProof"] = {"commit": proof["commit"], "pin": pin(raw["source-proof.json"]), "comparator": comparator}
manifest = {"schema": 1, "kind": "scenario-selection-cargo-gates", "runs": specs,
"captureHelpers": helper_catalog,
"logicalFiles": dict(sorted(logical.items())),
"members": {name: pin(data) for name, data in sorted(payload.items())}}
summary = verify_data(manifest, payload.__getitem__)
return payload, manifest, summary
def write_json(path, value):
with path.open("xb") as output:
output.write((json.dumps(value, indent=2) + "\n").encode("utf-8"))
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", type=Path, required=True, help="checkout holding .local/scenario-group-gates")
parser.add_argument("--runs", nargs="+", required=True, help="explicit run names, including failures to retain")
parser.add_argument("--helper", type=Path, action="append", default=[], help="additional exact historical helper (repeatable)")
parser.add_argument("--comparator", type=Path, action="append", default=[], help="additional exact historical source comparator (repeatable)")
parser.add_argument("--output", type=Path, help="fresh, nonexistent package directory (required unless --check-only)")
parser.add_argument("--check-only", action="store_true", help="validate raw captures in memory without creating an archive")
args = parser.parse_args()
need(all(run_name(run) for run in args.runs) and len(args.runs) == len(set(args.runs)), "unique safe run names required")
need(args.check_only or args.output is not None, "--output is required unless --check-only")
if not args.check_only:
need(not args.output.exists(), "output must be a fresh directory: " + str(args.output))
payload, manifest, summary = collect(args.root.resolve(), args.runs, args.helper, args.comparator)
if args.check_only:
print("PASS raw consistency; no package written:", summary)
return
out = args.output.resolve()
out.mkdir(parents=True, exist_ok=False)
for name in ("evidence.zip", "manifest.json", "files.json"):
need(not (out / name).exists(), "existing package output refused: " + name)
for name in ("prepare.py", "verify.py", "README.md"):
data = (HERE / name).read_bytes()
destination = out / name
if destination.exists():
need(destination.read_bytes() == data, "different existing package tool: " + name)
else:
with destination.open("xb") as output:
output.write(data)
with zipfile.ZipFile(out / "evidence.zip", "x", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
for name, data in sorted(payload.items()):
info = zipfile.ZipInfo(name, date_time=(2026, 9, 23, 0, 0, 0))
info.compress_type = zipfile.ZIP_DEFLATED
info.external_attr = 0o100644 << 16
archive.writestr(info, data)
write_json(out / "manifest.json", manifest)
names = ("prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip")
write_json(out / "files.json", {name: pin((out / name).read_bytes()) for name in names})
need(verify_package(out) == summary, "written package differs from validated captures")
print("PASS package readback:", out, summary)
if __name__ == "__main__":
main()
@@ -0,0 +1,194 @@
"""Offline consistency checks only; never execute or extract archived inputs."""
from pathlib import Path, PurePosixPath
from datetime import datetime
import argparse
import hashlib
import io
import json
import math
import re
import stat
import zipfile
RAW_FILES = {"started.json", "process.json", "result.json", "stdout.log", "stderr.log", "inputs.json", "inputs.zip"}
OUTER_FILES = {"prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip"}
def need(condition, message):
if not condition:
raise ValueError(message)
def pin(data):
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
def valid_pin(value):
return (isinstance(value, dict) and set(value) == {"bytes", "sha256"}
and type(value["bytes"]) is int and value["bytes"] >= 0
and isinstance(value["sha256"], str)
and re.fullmatch(r"[0-9a-f]{64}", value["sha256"]) is not None)
def safe(name):
return (isinstance(name, str) and bool(name) and "\\" not in name and ":" not in name
and all(ord(char) >= 32 for char in name)
and not PurePosixPath(name).is_absolute()
and all(part not in ("", ".", "..") for part in name.split("/")))
def run_name(name):
return isinstance(name, str) and re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", name) is not None
def parsed(data):
def pairs(items):
result = {}
for key, value in items:
need(key not in result, "duplicate JSON key: " + key)
result[key] = value
return result
def constant(value):
raise ValueError("nonfinite JSON constant: " + value)
return json.loads(data, object_pairs_hook=pairs, parse_constant=constant)
def inventory(archive, expected):
names = archive.namelist()
need(len(names) == len(set(names)) and set(names) == set(expected), "exact ZIP membership")
for entry in archive.infolist():
need(safe(entry.filename) and not entry.is_dir(), "unsafe ZIP member: " + entry.filename)
need(not stat.S_ISLNK(entry.external_attr >> 16), "ZIP symlink: " + entry.filename)
need(valid_pin(expected[entry.filename]), "invalid member pin: " + entry.filename)
need(entry.file_size == expected[entry.filename]["bytes"], "ZIP recorded size: " + entry.filename)
def ordered_times(*values):
times = [datetime.fromisoformat(value) for value in values]
return all(value.utcoffset() is not None for value in times) and times == sorted(times)
def verify_data(manifest, read):
"""Shared read-only verifier for packaged bytes or prepare's in-memory payload."""
need(manifest["schema"] == 1 and manifest["kind"] == "scenario-selection-cargo-gates", "manifest schema")
runs, logical, stored = manifest["runs"], manifest["logicalFiles"], manifest["members"]
helpers = manifest["captureHelpers"]
need(isinstance(helpers, dict) and set(helpers) == {"gate.py", "source-proof.py"}, "capture helper names")
need(all(isinstance(name, str) and re.fullmatch(r"helpers/[0-9a-f]{64}\.py", name)
for name in helpers.values()), "capture helper mapping")
need(isinstance(runs, dict) and bool(runs) and all(run_name(run) for run in runs), "explicit run inventory")
need(all(safe(name) for name in logical) and all(safe(name) for name in stored), "unsafe manifest path")
need(set(logical.values()) == set(stored), "unreferenced or absent stored members")
for name, expected in stored.items():
need(valid_pin(expected) and pin(read(name)) == expected, "stored member hash: " + name)
expected_logical = {"cargo/" + run + "/" + name for run in runs for name in RAW_FILES}
expected_logical.update(spec["helper"] for spec in runs.values())
expected_logical.update(helpers.values())
for run, spec in runs.items():
if "sourceProof" in spec:
proof_spec = spec["sourceProof"]
need(set(proof_spec) == {"commit", "pin", "comparator"}, run + ": exact source proof spec")
need(isinstance(proof_spec["commit"], str) and re.fullmatch(r"[0-9a-f]{40}", proof_spec["commit"]), run + ": named full source commit")
need(valid_pin(proof_spec["pin"]), run + ": source proof pin")
need(isinstance(proof_spec["comparator"], str) and re.fullmatch(r"comparators/[0-9a-f]{64}\.py", proof_spec["comparator"]), run + ": comparator path")
expected_logical.update({"cargo/" + run + "/source-proof.json", proof_spec["comparator"]})
need(set(logical) == expected_logical, "exact logical raw/helper inventory")
for name, destination in logical.items():
if name.startswith("cargo/") and name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"}:
expected = "capsules/" + stored[destination]["sha256"] + PurePosixPath(name).suffix
need(destination == expected, "input capsule mapping: " + name)
else:
need(destination == name, "only input maps/archives may be deduplicated: " + name)
def raw(name):
return read(logical[name])
for name, helper in helpers.items():
need(helper == "helpers/" + pin(raw(helper))["sha256"] + ".py", "named helper hash: " + name)
seen_capsules = set()
for run, spec in runs.items():
prefix = "cargo/" + run + "/"
start = parsed(raw(prefix + "started.json"))
process = parsed(raw(prefix + "process.json"))
result = parsed(raw(prefix + "result.json"))
required_start = {"start", "command", "cwd", "head", "scope", "inputs", "archive",
"inputCount", "environment", "helper", "cargo", "rustc"}
need(required_start <= start.keys(), run + ": incomplete start receipt")
need(all(result.get(key) == value for key, value in start.items()), run + ": start/result relationship")
command = result["command"]
need(isinstance(command, list) and command and command[0] == "cargo"
and all(isinstance(arg, str) for arg in command), run + ": Cargo command")
need(command == process["command"] == spec["command"], run + ": command relationship")
need(result["head"] == spec["head"] and re.fullmatch(r"(?:[0-9a-f]{40}|[0-9a-f]{64})", result["head"]), run + ": HEAD")
need(type(result["exitCode"]) is int and result["exitCode"] == spec["exitCode"], run + ": recorded exit")
need(result["inputsUnchanged"] is True, run + ": changed inputs refused")
need(type(result["seconds"]) in (int, float) and math.isfinite(result["seconds"])
and result["seconds"] >= 0, run + ": duration")
need(type(process["pid"]) is int and process["pid"] > 0, run + ": process identity")
need(ordered_times(start["start"], process["observed"], result["finish"]), run + ": completed timestamp order")
for field, filename in [("inputs", "inputs.json"), ("archive", "inputs.zip"), ("stdout", "stdout.log"), ("stderr", "stderr.log")]:
need(result[field] == pin(raw(prefix + filename)), run + ": " + field + " pin")
need(valid_pin(result["helper"]) and result["helper"] == pin(raw(spec["helper"])), run + ": helper pin")
need(spec["helper"] == "helpers/" + result["helper"]["sha256"] + ".py", run + ": helper mapping")
inputs = parsed(raw(prefix + "inputs.json"))
need(isinstance(inputs, dict) and all(safe(name) for name in inputs), run + ": safe input inventory")
need(type(result["inputCount"]) is int and len(inputs) == result["inputCount"] == spec["inputCount"], run + ": input counts")
if "sourceProof" in spec:
proof_spec = spec["sourceProof"]
proof_bytes = raw(prefix + "source-proof.json")
proof = parsed(proof_bytes)
need(pin(proof_bytes) == proof_spec["pin"], run + ": source proof pinned bytes")
need(proof["run"] == run and proof["commit"] == proof_spec["commit"], run + ": proof run/commit identity")
need(proof["status"] == "PASS exact captured input inventory and Git blob bytes", run + ": source proof status")
need(proof["inputCount"] == len(inputs) and proof["inputMap"] == result["inputs"], run + ": proof input relationship")
need(proof["result"] == pin(raw(prefix + "result.json")), run + ": proof result relationship")
need(ordered_times(result["finish"], proof["verified"]), run + ": proof time after closed gate")
comparator = proof_spec["comparator"]
need(valid_pin(proof["comparator"]) and pin(raw(comparator)) == proof["comparator"], run + ": comparator bytes")
need(comparator == "comparators/" + proof["comparator"]["sha256"] + ".py", run + ": comparator identity")
need(sum(name.startswith("crates/vendor/iced_widget/") for name in inputs) == 71, run + ": vendor count")
capsule = (logical[prefix + "inputs.json"], logical[prefix + "inputs.zip"])
if capsule not in seen_capsules:
with zipfile.ZipFile(io.BytesIO(raw(prefix + "inputs.zip"))) as source:
inventory(source, inputs)
for name, expected in inputs.items():
need(pin(source.read(name)) == expected, run + ": source hash " + name)
seen_capsules.add(capsule)
artifacts = []
for line in raw(prefix + "stdout.log").decode("utf-8", errors="replace").splitlines():
try:
value = json.loads(line)
except json.JSONDecodeError:
continue
if isinstance(value, dict) and value.get("reason") == "compiler-artifact" and value.get("executable"):
artifacts.append(value)
need(isinstance(result["artifacts"], list), run + ": artifact records")
need(artifacts == [record["cargo"] for record in result["artifacts"]], run + ": Cargo JSON artifact relationship")
for record in result["artifacts"]:
need(set(record) == {"cargo", "readback"}
and (record["readback"] is None or valid_pin(record["readback"])), run + ": artifact readback pin")
print(run, "recorded exit", result["exitCode"], "inputs", len(inputs), "executable records", len(artifacts))
return {"runs": len(runs), "recordedFailures": sum(spec["exitCode"] != 0 for spec in runs.values()),
"logicalFiles": len(logical), "storedMembers": len(stored), "sourceCapsulePairs": len(seen_capsules),
"captureHelpers": helpers, "sourceProofs": sum("sourceProof" in spec for spec in runs.values())}
def verify_package(root):
files = parsed((root / "files.json").read_bytes())
need(set(files) == OUTER_FILES, "outer file inventory")
for name, expected in files.items():
need(valid_pin(expected) and pin((root / name).read_bytes()) == expected, "outer hash: " + name)
manifest = parsed((root / "manifest.json").read_bytes())
with zipfile.ZipFile(root / "evidence.zip") as archive:
inventory(archive, manifest["members"])
return verify_data(manifest, archive.read)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("package", nargs="?", type=Path, default=Path(__file__).resolve().parent)
args = parser.parse_args()
print("PASS offline consistency:", verify_package(args.package))
print("Historical receipts only; no source/binary execution or native acceptance claim.")
if __name__ == "__main__":
main()
@@ -0,0 +1,43 @@
{
"status": "INVALID interrupted source-change attempt; no verification claim",
"run": "selection-scenario-3",
"reason": "Cargo started while the final formatter/commit command was still running. app.rs bytes changed after capture. Parent deliberately terminated the observed Cargo process tree; no executable/test result was emitted. Raw HEAD identifies only the starting checkout, not the captured or compiled working tree.",
"archive": {
"bytes": 3192626,
"sha256": "9afef85b7e395d811bf99cd3a49909da881403cac3bcfcaea9fdc701f4ee5180"
},
"members": {
"gate.py": {
"bytes": 3778,
"sha256": "3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2"
},
"inputs.json": {
"bytes": 135839,
"sha256": "88df77614a1030033090cc10dac0ab3959dbcfc81a12317150840b2ea1c86f8f"
},
"inputs.zip": {
"bytes": 3234362,
"sha256": "856f066c247803006f410b2f9066358c4b257239b3b8838b8291778eb8d49ecd"
},
"process.json": {
"bytes": 201,
"sha256": "5eb0b49241515d2712aa6c69d17f21fc0a78402aa4a92700196b2b1bb1803ddf"
},
"result.json": {
"bytes": 1985,
"sha256": "511abf94627b13757771fb3488895a7a4a402b229cfa4bdeb02dc4558236aa7e"
},
"started.json": {
"bytes": 1590,
"sha256": "978ad4d80edf791be33362305116327018fce2dec6fa3b83919ce0b3ed7e71b5"
},
"stderr.log": {
"bytes": 73,
"sha256": "9019f4d830dc881adca029fed3b2f22cfd586c4634eca2cbb47350ca66d1aa34"
},
"stdout.log": {
"bytes": 292222,
"sha256": "9c1457380f81b5651d645548c94f720314eea8fd3cceebfd840d9c1f2abc1f67"
}
}
}
@@ -0,0 +1,29 @@
"""Preserve an interrupted changed-source attempt; never produce acceptance proof."""
from pathlib import Path
import hashlib, json, zipfile
root = Path('C:/ar-scenario-9e8r6t')
run = root/'.local/scenario-group-gates/selection-scenario-3'
out = root/'harness/scenario-selection-evidence-20260924/invalid-attempt-3'
result = json.loads((run/'result.json').read_text())
assert result['exitCode'] == 1 and result['inputsUnchanged'] is False
assert result['artifacts'] == [] and 'finish' in result
assert not (run/'source-proof.json').exists()
def pin(data): return {'bytes':len(data), 'sha256':hashlib.sha256(data).hexdigest()}
payload = {p.name:p.read_bytes() for p in run.iterdir() if p.is_file()}
assert set(payload) == {'started.json','process.json','result.json','stdout.log','stderr.log','inputs.json','inputs.zip'}
payload['gate.py'] = (run.parent/'gate.py').read_bytes()
assert pin(payload['gate.py']) == result['helper']
out.mkdir(parents=True, exist_ok=False)
with zipfile.ZipFile(out/'raw.zip','x',zipfile.ZIP_DEFLATED) as archive:
for name, data in sorted(payload.items()): archive.writestr(name,data)
with zipfile.ZipFile(out/'raw.zip') as archive:
assert set(archive.namelist()) == set(payload)
assert all(archive.read(name) == data for name,data in payload.items())
manifest = {'status':'INVALID interrupted source-change attempt; no verification claim',
'run':'selection-scenario-3',
'reason':'Cargo started while the final formatter/commit command was still running. app.rs bytes changed after capture. Parent deliberately terminated the observed Cargo process tree; no executable/test result was emitted. Raw HEAD identifies only the starting checkout, not the captured or compiled working tree.',
'archive':pin((out/'raw.zip').read_bytes()), 'members':{n:pin(d) for n,d in sorted(payload.items())}}
with (out/'manifest.json').open('x',encoding='utf-8',newline='\n') as stream:
json.dump(manifest,stream,indent=2);stream.write('\n')
print(json.dumps(manifest,indent=2))
@@ -0,0 +1,28 @@
# Native Windows selection exercise
Source `ed55b1781d86ec49af5d2588cbd06f7b878d9009`, closed build `selection-build-1`. The launcher checked that receipt, source proof, binary and tracked fixture before copying the executable and opening an isolated fixture through the app. The archive omits the executable; its identity is recorded in `started.json` and the build receipt. This is branch-specific verification, not the #37 candidate or a release package.
Run `python -B verify.py` with Python 3.11+ to verify the archive and exact file comparisons. The verifier does not execute archived helpers or establish visual assertions. `native.zip` contains original screenshot/metadata bytes, launch/close records, empty app logs, fixture snapshots and the original launch/capture helpers. `prepare.py` requires the retained local run and refuses an existing output directory.
## Observations
All numbered files below are under `captures/` inside the archive. Native inputs used the Computer Use skill's `@oai/sky` API.
- 17: selected saved A in a maximized window. 18–21: contextual Add creates a two-occurrence selection; count, both strokes and Properties primary agree.
- 22–23: group menu exposes selection commands and disabled movement/removal. Inspect first occurrence preserves both members and switches primary.
- 24–26: dragging a selected body and pressing Delete refuse the whole group, retain both members and display the unfinished-operation feedback.
- 27: a single blank drag from (1110,730) to (1060,700) clears the group and moves the visible geometry by (-50,-30). No second press was needed.
- 28–31: contextual Add restores the two-member group after panning. 32: a stationary plain click on the first member collapses to that singleton.
- 33: Save all after selection and pan preserves the entire original 6,206-byte file, SHA256 `74cd2c1dd5eed7eb19895a752bb8ff9ce93655f7638a5312c96f56218568ebb3`.
- 34–35: with the first occurrence selected, the first drag of the second body from (675,454) to (675,474) immediately moves it and selects it. Save all changes only saved A's `updated_report` `send_y`, from 252 to 281.8841247558594. The entire expected file is compared, including unchanged semantic order and other representations.
- 36–37: one Ctrl+Z and Save all restore exact baseline bytes.
- 38–44: A → B → Automatic → A displays each correct representation, retains the existing singleton primary, and final Save all preserves baseline. This is not a native group-clearing-on-navigation claim.
- 46: app remains usable after an unsuccessful title-bar close attempt. Alt+F4 then closes normally with exit 0; binary and fixture unchanged; stdout and stderr empty.
## Limits and automation anomalies
Native Shift-click was not exercised because the API cannot hold Shift across a click. Actual modifier/focus/busy/window-loss sequences are covered by the mounted widget tests in the separate Cargo evidence. Native Redo, dirty Properties admission, stale queued events, group navigation and fallback C were not independently exercised here; test coverage must not be described as native coverage.
Early nonmaximized picker targeting selected B while intending A. Maximizing and using the full-window screenshot resolved targeting; no product picker defect is established. One action selected a 16×16 icon screenshot and was rejected for out-of-bounds coordinates before any app action. Early setup observations remain local; accepted captures start at 17. Later indexed title-bar Close minimized the window instead; restoration was observed, then Alt+F4 succeeded. These are automation limitations, not evidence of a product crash. Capture labels record intent; claims use observed state. Capture 45 produced no file because the API reported a minimized window.
The fixture intentionally contains five model checks and an incomplete occurrence. No new checks appeared. Group semantic movement and aggregate removal remain unfinished. Screenshots and successful launch do not imply full usability or release acceptance.
@@ -0,0 +1,420 @@
{
"kind": "scenario-selection-native-observations",
"source": "ed55b1781d86ec49af5d2588cbd06f7b878d9009",
"buildRun": "selection-build-1",
"archive": {
"bytes": 2622962,
"sha256": "391a91b55ab2c4c4d7663026d38a6485bfa5876d1205f40456da508978a69df3"
},
"members": {
"app.stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"app.stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"baseline.arcadia": {
"bytes": 6206,
"sha256": "74cd2c1dd5eed7eb19895a752bb8ff9ce93655f7638a5312c96f56218568ebb3"
},
"build/result.json": {
"bytes": 3219,
"sha256": "c7fe2098ad813e50f64f8f5ff45a7459859c74ff10fef68e8ef4cb151d5bfb7e"
},
"build/source-proof.json": {
"bytes": 596,
"sha256": "657eb7e9a252e29764eda37557b81aed551750eaada46018b5f9f097a85cf780"
},
"captures/17-readable-arrangement-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/17-readable-arrangement-1.jpg": {
"bytes": 120668,
"sha256": "5a22b1fa00dd235ae8230f03a9bf97418fc79e9e3f3ea6b36094eac016cb24a2"
},
"captures/17-readable-arrangement.json": {
"bytes": 1074,
"sha256": "f0e5b906b5908263857f594889cfaf582b78b70c0dd3364db47afe3dcc585206"
},
"captures/18-first-message-menu-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/18-first-message-menu-1.jpg": {
"bytes": 129668,
"sha256": "b7630fd9a4bb01d3cb2aaeda4e30d5ddb01963a531333aa40e51f79b86b6b894"
},
"captures/18-first-message-menu.json": {
"bytes": 1070,
"sha256": "d9085bba0a31de2189ad91cb6507c1ebdf7cbcd63dcd7ba73fd82472b84c6d1c"
},
"captures/19-first-selected-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/19-first-selected-1.jpg": {
"bytes": 138013,
"sha256": "71d84ca1ee0fe180ce9543ad6e5295d30aa6ea90f3971c2f2dfe91a56b1aec5d"
},
"captures/19-first-selected.json": {
"bytes": 1062,
"sha256": "4c576ece65fc8f4e370f9f36f8507ce6e3874bbfdb8b71496956ee95592795e4"
},
"captures/20-second-message-menu-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/20-second-message-menu-1.jpg": {
"bytes": 146775,
"sha256": "5cf7352170591ef638a52528694225e3deb6e0c74770cf7ab9116d5a6c186e36"
},
"captures/20-second-message-menu.json": {
"bytes": 1072,
"sha256": "625b324e3e5a854dda504d974d369b7891dd1ffbf28f21ede6294f4339d7f097"
},
"captures/21-two-selected-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/21-two-selected-1.jpg": {
"bytes": 136537,
"sha256": "9d9f00dc52e824f02ecd982bd8c0f5ca27846ad7a1a5178d133ccb9cb951dfa2"
},
"captures/21-two-selected.json": {
"bytes": 1058,
"sha256": "b4ca5340ab657af45ab11be55531a5188ff2811d88c41d5cc11c8e268dda2687"
},
"captures/22-group-menu-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/22-group-menu-1.jpg": {
"bytes": 144767,
"sha256": "2435dbc861d420448e11d2aa13bf847794080e2d9886a120d125e31b71a918f5"
},
"captures/22-group-menu.json": {
"bytes": 1054,
"sha256": "486b966b29bccc70c22fb2daa6dab3126e76797ccfbc3c775cdeb30ce104c469"
},
"captures/23-inspect-first-in-group-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/23-inspect-first-in-group-1.jpg": {
"bytes": 138250,
"sha256": "0323a8a14c82e6759b6e6de4f63cc6f9fcabbd168fd77ae9dcbbb8de3f97dee8"
},
"captures/23-inspect-first-in-group.json": {
"bytes": 1078,
"sha256": "82c474da28afdbc6de5e66bd2c3ef0cf890a5fd32496c4b7db26283e39d147af"
},
"captures/24-group-motion-refused-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/24-group-motion-refused-1.jpg": {
"bytes": 139665,
"sha256": "e6f8ad6497368f3137ec3f9480d535ee710439f400714de4a2c5458cb0e9ddf8"
},
"captures/24-group-motion-refused.json": {
"bytes": 1074,
"sha256": "85c3901ce04d7e1e0453887340a5c839f95e55bb50d1aa6c76b36acb6ae3c466"
},
"captures/25-group-delete-refused-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/25-group-delete-refused-1.jpg": {
"bytes": 140455,
"sha256": "61dfa2b7973ad2ddfcb9b5001f76dbb18ca160d1e1cadf305bb153be2358bbac"
},
"captures/25-group-delete-refused.json": {
"bytes": 1074,
"sha256": "3718f8ca5587b8b97dfe70e20cc9b2c3597ba8a32a7ce9b47963b114e49f9b82"
},
"captures/26-resumed-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/26-resumed-1.jpg": {
"bytes": 140455,
"sha256": "61dfa2b7973ad2ddfcb9b5001f76dbb18ca160d1e1cadf305bb153be2358bbac"
},
"captures/26-resumed.json": {
"bytes": 1048,
"sha256": "3fc0e99a7100e64f4107929d6caebf8314d2e11e8808df279a4f041cfcd8195c"
},
"captures/27-blank-drag-clears-and-pans-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/27-blank-drag-clears-and-pans-1.jpg": {
"bytes": 136897,
"sha256": "c745bbb5337d69928457760ab8be0889637bb088c2a6b6229208a345e4f9aa77"
},
"captures/27-blank-drag-clears-and-pans.json": {
"bytes": 1086,
"sha256": "93c3b471119bfb33a027a4ce40555588e49da9a9823af33804fcc2e342d184c4"
},
"captures/28-panned-first-menu-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/28-panned-first-menu-1.jpg": {
"bytes": 144931,
"sha256": "b529ad43065aa40b1e18b40fca7572cd46edee9e7b2f64ff354405327cbcaf91"
},
"captures/28-panned-first-menu.json": {
"bytes": 1068,
"sha256": "b8820472eddb6e40ecf229cbaa6a80816d143866f9bdd471b4fbb6c862bd8b00"
},
"captures/29-first-readded-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/29-first-readded-1.jpg": {
"bytes": 137921,
"sha256": "2ac263a8e22189bb800ca4d759e704527bfba8aa9d23a6e75f237b2defacbca1"
},
"captures/29-first-readded.json": {
"bytes": 1060,
"sha256": "77cd489809d6c02d985ae10a12192b4504f9276ecc94663ae40c7556da6416fa"
},
"captures/30-panned-second-menu-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/30-panned-second-menu-1.jpg": {
"bytes": 146872,
"sha256": "ec9edd151261f9c3f6a8369dcded2eb65f7a0c137c9534695cebbad03b29f307"
},
"captures/30-panned-second-menu.json": {
"bytes": 1070,
"sha256": "94c6066bc0188261b3229e27f328373a73ce2f8f2c30a967e16c7a6006ea57f6"
},
"captures/31-panned-group-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/31-panned-group-1.jpg": {
"bytes": 136311,
"sha256": "1b9d1ed25e436c8a3146f393b0d99ebe0cc3efcfe54e2f10afe299fd04851ede"
},
"captures/31-panned-group.json": {
"bytes": 1058,
"sha256": "8aa8db5192254c6e54a983c536a84b43765d42fddbfc931fb1563ee4a1295dd4"
},
"captures/32-stationary-collapse-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/32-stationary-collapse-1.jpg": {
"bytes": 138993,
"sha256": "f19e8000c13ee9817d6ac7098aff08cd28a5ab7699403cc3deb8248b9a592a8a"
},
"captures/32-stationary-collapse.json": {
"bytes": 1072,
"sha256": "d0c17bf202c465ba3b6eea98ca29c00d8715327b70897cb6c4ae8d198597cb8c"
},
"captures/33-save-selection-only-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/33-save-selection-only-1.jpg": {
"bytes": 135136,
"sha256": "18b6b18c4d8dd9b9e758e9fbe6f9977e234241637a0c21ab761b49dd903f018c"
},
"captures/33-save-selection-only.json": {
"bytes": 1072,
"sha256": "17d72be78dff8e0418687febfb4cdaf518b45bcfed9645a888460e8655f081ff"
},
"captures/34-first-press-singleton-spacing-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/34-first-press-singleton-spacing-1.jpg": {
"bytes": 134229,
"sha256": "28e18973a273a212badee48afb84344c4000a7fb3c1ca466cafa3017b8185e84"
},
"captures/34-first-press-singleton-spacing.json": {
"bytes": 1100,
"sha256": "0812bfb9fff105e0a5a26046330d37ad5cea7a177578cd0b653b67c91445c933"
},
"captures/35-save-singleton-spacing-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/35-save-singleton-spacing-1.jpg": {
"bytes": 133495,
"sha256": "4bf6f5475862d553c406235e9e291b21fb8c8ee573444862c56ee3421990066c"
},
"captures/35-save-singleton-spacing.json": {
"bytes": 1080,
"sha256": "846deaf0909ea1778f49b46cae907c228f18e3a534f69b7422d3e91ee5118d1b"
},
"captures/36-undo-singleton-spacing-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/36-undo-singleton-spacing-1.jpg": {
"bytes": 133493,
"sha256": "e92b98855de9bf6ec8b8d5d7bd7c5ba3942831fa6c82505fe464a006fb5ab879"
},
"captures/36-undo-singleton-spacing.json": {
"bytes": 1086,
"sha256": "8530c3fe040f1f92fc23a4d34e463213a95f88dd6ae4cc3cb2db9cebaaa09c0e"
},
"captures/37-save-after-undo-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/37-save-after-undo-1.jpg": {
"bytes": 133395,
"sha256": "2ae10fcb74b0ec07f72f0255990399487ae3271d22649929a32a987a7dc2b0bf"
},
"captures/37-save-after-undo.json": {
"bytes": 1066,
"sha256": "a025a7d44e9fea476c019addeadabbb31d0d9c1f986e70671b615e2441471890"
},
"captures/38-representation-picker-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/38-representation-picker-1.jpg": {
"bytes": 139092,
"sha256": "5e1d4ff9986f8b291324e444a121f13b66696fb0c951f2b8ba88a2a609c2846d"
},
"captures/38-representation-picker.json": {
"bytes": 1076,
"sha256": "b858ba22d06902a2310140d9dce7a4257bbb6e89bf531c3300d337b866a670c6"
},
"captures/39-saved-b-visit-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/39-saved-b-visit-1.jpg": {
"bytes": 124571,
"sha256": "44770404c5bdaf1efadccb00fe8aaf0fda9a30e8ff14c231ee9affc07f74a7da"
},
"captures/39-saved-b-visit.json": {
"bytes": 1060,
"sha256": "d198e8c0000cb63833a0ca3d94fad899f6ead0b3567191d15e8aeb0d93c9a1f9"
},
"captures/40-representation-picker-b-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/40-representation-picker-b-1.jpg": {
"bytes": 132653,
"sha256": "03f4e852f84b5b922333777e752ac5bc7de8f44ddfa5dc159215b377bceb99d2"
},
"captures/40-representation-picker-b.json": {
"bytes": 1080,
"sha256": "bc434ca168cabbedd35e46642b0a1a87cd33f99c27d986c1de310f0b8a5499ab"
},
"captures/41-automatic-visit-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/41-automatic-visit-1.jpg": {
"bytes": 134389,
"sha256": "b00141ca779f279124af5966e0e30f55112f383c48a708c65eac3db4de9b538b"
},
"captures/41-automatic-visit.json": {
"bytes": 1064,
"sha256": "9c5c937319f53c307a2724cbfcee4fbe2f7d089a6bc486d8815799f7716ae2cd"
},
"captures/42-representation-picker-auto-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/42-representation-picker-auto-1.jpg": {
"bytes": 136059,
"sha256": "f291438eb48e95d223ae53ee75a65651bd1ab6594e984a8034b44b1c2d3e4fa4"
},
"captures/42-representation-picker-auto.json": {
"bytes": 1086,
"sha256": "9a7cdb0f481628a4a92fc4633ec81f9fdc3d513a15cc08d21ad4080ddd7f32a4"
},
"captures/43-saved-a-return-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/43-saved-a-return-1.jpg": {
"bytes": 134944,
"sha256": "a999c42f1beeb3bcbbd5b3bf0bb52631d58152af9832ff96548b16c930097c3c"
},
"captures/43-saved-a-return.json": {
"bytes": 1062,
"sha256": "d1f66eda6221a1e317ed71c8f4876fe14f42c5f5bcfbd2995d21c00919231062"
},
"captures/44-final-save-0.jpg": {
"bytes": 722,
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
},
"captures/44-final-save-1.jpg": {
"bytes": 134546,
"sha256": "2898c6facf90501dfe850a4657006fcdd30f1ead52f4bbc435124856e7eb4ef7"
},
"captures/44-final-save.json": {
"bytes": 1054,
"sha256": "5f62cdc7c0499128c7c45e0c331bf04a5e6d55cc6288e3d353461751a66d0b2c"
},
"captures/46-restored-after-close-attempt-0.jpg": {
"bytes": 140599,
"sha256": "2a187a774a35078549554ec7136802bcf865f9a768590cd124b7c33bfef5e2b0"
},
"captures/46-restored-after-close-attempt.json": {
"bytes": 957,
"sha256": "9b66addec1571f37966a68f3ff82f3de3ff15eaace79289d98a153a248316302"
},
"finished.json": {
"bytes": 475,
"sha256": "b09ce7af55e171af4c7fc76d815448735ed7c73a5d384099b96daf24b70e9c76"
},
"helpers/capture.js": {
"bytes": 1635,
"sha256": "ddd7c01844953e009516939eabb947248aeda4340e196bc638e17ef09a7c87f7"
},
"helpers/native.py": {
"bytes": 3115,
"sha256": "4bc51115c6597baa4697264404d0f892d37b52808832486be49c40af2e85b22f"
},
"selection-only-saved.arcadia": {
"bytes": 6206,
"sha256": "74cd2c1dd5eed7eb19895a752bb8ff9ce93655f7638a5312c96f56218568ebb3"
},
"spacing-saved.arcadia": {
"bytes": 6220,
"sha256": "8d6e11953019cebfc1b287a829a357eddeae3b59943766ad96b8f7a1df013cb0"
},
"started.json": {
"bytes": 863,
"sha256": "9692742e67c2c6b7a4bd8676dec17eea17e53b2afa3d6911e567596fd02f240a"
},
"undo-saved.arcadia": {
"bytes": 6206,
"sha256": "74cd2c1dd5eed7eb19895a752bb8ff9ce93655f7638a5312c96f56218568ebb3"
},
"working.arcadia": {
"bytes": 6206,
"sha256": "74cd2c1dd5eed7eb19895a752bb8ff9ce93655f7638a5312c96f56218568ebb3"
}
},
"claims": [
"selection-only Save all preserves exact source",
"first-press singleton drag changes only saved A updated_report send_y 252 to 281.8841247558594",
"one Undo and Save all restore exact baseline",
"final Save all after A/B/Automatic/A preserves exact baseline",
"ordinary Alt+F4 exit 0; empty stdout/stderr"
],
"limits": [
"Native Shift-click not exercised; actual Shift widget tests are in Cargo evidence",
"Group movement/removal remain unavailable",
"Screenshots are observations, not automated visual assertions",
"Not the frozen prerelease candidate or a release package"
]
}
@@ -0,0 +1,52 @@
"""Package closed native observations; original executable stays local."""
from pathlib import Path
import hashlib, json, shutil, sys, zipfile
root = Path(sys.argv[1]).resolve()
run = root / '.local/scenario-selection-native/run-1'
out = root / 'harness/scenario-selection-evidence-20260924/native'
def pin(data): return {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
def read(name): return (run/name).read_bytes()
start = json.loads(read('started.json'))
finish = json.loads(read('finished.json'))
assert finish['exitCode'] == 0 and finish['binaryUnchanged'] and finish['fixtureUnchanged']
assert pin(read('arcadia-studio.exe')) == start['binary']
baseline = read('baseline.arcadia')
assert pin(baseline) == start['fixture']
for name in ['selection-only-saved.arcadia', 'undo-saved.arcadia', 'working.arcadia']:
assert read(name) == baseline, name
old = b'{ address { references []; occurrence @updated_report; }; send_y 252; }'
new = b'{ address { references []; occurrence @updated_report; }; send_y 281.8841247558594; }'
start_a = baseline.index(b' diagram incident_readable ')
end_a = baseline.index(b' diagram incident_wide ', start_a)
saved_a = baseline[start_a:end_a]
assert saved_a.count(old) == 1
expected = baseline[:start_a] + saved_a.replace(old, new, 1) + baseline[end_a:]
assert read('spacing-saved.arcadia') == expected
assert finish['working'] == pin(read('working.arcadia'))
for stream in ['stdout', 'stderr']:
assert pin(read('app.' + stream + '.log')) == finish[stream]
assert read('app.' + stream + '.log') == b''
gate = root / '.local/scenario-group-gates' / start['gate']
assert pin((gate/'result.json').read_bytes()) == start['receipt']
assert pin((gate/'source-proof.json').read_bytes()) == start['proof']
assert pin((run.parent/'native.py').read_bytes()) == start['helper']
files = {name: read(name) for name in ['started.json', 'finished.json', 'baseline.arcadia', 'working.arcadia', 'selection-only-saved.arcadia', 'spacing-saved.arcadia', 'undo-saved.arcadia', 'app.stdout.log', 'app.stderr.log']}
for path in sorted(run.iterdir()):
if path.name[:2].isdigit() and (17 <= int(path.name[:2]) <= 44 or int(path.name[:2]) == 46):
assert path.suffix in ['.json', '.jpg', '.png']
files['captures/' + path.name] = path.read_bytes()
for name in ['native.py', 'capture.js']:
files['helpers/' + name] = (run.parent/name).read_bytes()
for name in ['result.json', 'source-proof.json']:
files['build/' + name] = (gate/name).read_bytes()
out.mkdir(exist_ok=False)
with zipfile.ZipFile(out/'native.zip', 'x', zipfile.ZIP_DEFLATED) as archive:
for name, data in sorted(files.items()): archive.writestr(name, data)
with zipfile.ZipFile(out/'native.zip') as archive:
assert set(archive.namelist()) == set(files)
for name, data in files.items(): assert archive.read(name) == data
manifest = {'kind': 'scenario-selection-native-observations', 'source': start['source'], 'buildRun': start['gate'], 'archive': pin((out/'native.zip').read_bytes()), 'members': {name: pin(data) for name, data in sorted(files.items())}, 'claims': ['selection-only Save all preserves exact source', 'first-press singleton drag changes only saved A updated_report send_y 252 to 281.8841247558594', 'one Undo and Save all restore exact baseline', 'final Save all after A/B/Automatic/A preserves exact baseline', 'ordinary Alt+F4 exit 0; empty stdout/stderr'], 'limits': ['Native Shift-click not exercised; actual Shift widget tests are in Cargo evidence', 'Group movement/removal remain unavailable', 'Screenshots are observations, not automated visual assertions', 'Not the frozen prerelease candidate or a release package']}
(out/'manifest.json').write_text(json.dumps(manifest, indent=2)+'\n', encoding='utf-8', newline='\n')
shutil.copy2(__file__, out/'prepare.py')
print(json.dumps({'output':str(out), 'archive':manifest['archive'], 'members':len(files)}, indent=2))
@@ -0,0 +1,45 @@
"""Verify archived bytes and source-delta claims; does not execute the app."""
from pathlib import Path
import hashlib, json, zipfile
root = Path(__file__).resolve().parent
def pin(data): return {'bytes':len(data), 'sha256':hashlib.sha256(data).hexdigest()}
manifest = json.loads((root/'manifest.json').read_text(encoding='utf-8'))
assert pin((root/'native.zip').read_bytes()) == manifest['archive']
with zipfile.ZipFile(root/'native.zip') as archive:
assert len(archive.namelist()) == len(set(archive.namelist()))
assert set(archive.namelist()) == set(manifest['members'])
files = {name:archive.read(name) for name in archive.namelist()}
for name, data in files.items(): assert pin(data) == manifest['members'][name], name
start = json.loads(files['started.json'])
finish = json.loads(files['finished.json'])
build = json.loads(files['build/result.json'])
proof = json.loads(files['build/source-proof.json'])
assert start['source'] == manifest['source'] == build['head'] == proof['commit']
assert start['gate'] == manifest['buildRun'] == proof['run']
assert pin(files['build/result.json']) == start['receipt'] == proof['result']
assert pin(files['build/source-proof.json']) == start['proof']
assert pin(files['helpers/native.py']) == start['helper']
assert build['exitCode'] == 0 and build['inputsUnchanged'] is True
artifacts = [a for a in build['artifacts'] if a['cargo']['target']['name'] == 'arcadia-studio' and not a['cargo']['profile']['test']]
assert len(artifacts) == 1 and artifacts[0]['readback'] == start['binary']
assert finish['exitCode'] == 0 and finish['binaryUnchanged'] and finish['fixtureUnchanged']
baseline = files['baseline.arcadia']
assert pin(baseline) == start['fixture']
for name in ['selection-only-saved.arcadia', 'undo-saved.arcadia', 'working.arcadia']:
assert files[name] == baseline, name
assert pin(files['working.arcadia']) == finish['working']
begin = baseline.index(b' diagram incident_readable ')
end = baseline.index(b' diagram incident_wide ', begin)
old = b'{ address { references []; occurrence @updated_report; }; send_y 252; }'
new = b'{ address { references []; occurrence @updated_report; }; send_y 281.8841247558594; }'
assert baseline[begin:end].count(old) == 1
assert files['spacing-saved.arcadia'] == baseline[:begin]+baseline[begin:end].replace(old,new,1)+baseline[end:]
for stream in ['stdout','stderr']:
data = files['app.'+stream+'.log']
assert data == b'' and pin(data) == finish[stream]
for name, data in files.items():
if name.startswith('captures/') and name.endswith('.json'):
for item in json.loads(data)['images']:
assert 'captures/'+item['file'] in files
print('PASS native archive integrity, build/source linkage, exact source deltas and clean close; visual interpretation remains manual')
@@ -0,0 +1,38 @@
# Scenario selection evidence tools — preparatory review
2026-09-24. Bounded independent read-only review in `C:/ar-scenario-9e8r6t`. This report is the only written artifact. No Cargo, Git mutation, network, GUI or archived-source execution. Final delivery package verification remains pending.
## Tool comparison
No material defect found in the intended adaptation. A byte/diff comparison of `.local/scenario-selection-evidence-tools` with `.local/scenario-evidence-tools` shows exactly:
- `prepare.py`: one manifest-kind substitution, from `scenario-group-planner-cargo-gates` to `scenario-selection-cargo-gates`.
- `verify.py`: the matching single schema-kind substitution.
- `selfcheck.py`: byte-identical.
- `README.md`: title and example run-name substitutions only.
The tools still consume `.local/scenario-group-gates`, retain explicit run inventories and refuse unfinished/changed captures. No implicit active-directory discovery was added.
## HEAD versus named source proof
The verifier does **not** require captured HEAD to equal a source-proof commit. It correctly binds HEAD across the original started/result receipts and manifest, while binding the independently named proof commit to its proof receipt and manifest specification. The proof separately pins the run name, exact completed result bytes, input map/count, comparator bytes, status and post-completion comparison time.
The source-proof wrapper invokes only the comparator's generic `compare_commit` routine, which compares the complete filtered Git-tree inventory and each captured blob hash. It does not invoke the older comparator module's Process-only package verifier or its 742-input restriction. Therefore the current 809-input Scenario scope is supported without weakening comparison.
A run begun from dirty working bytes can legitimately record an older HEAD while a later proof identifies a commit containing those exact bytes. This requires unchanged capture inputs and successful complete named-commit comparison; neither a commit label nor successful tests alone establishes that relationship. The offline package verifier checks proof consistency, not the Git comparison afresh. The README correctly preserves these meanings.
## Exact preservation and checks performed
The preparer reads raw bytes and stores original receipts, streams and optional proofs without normalization. Only identical input maps/ZIPs are deduplicated by SHA-256; logical paths retain each run. It requires exactly the seven closed raw files, optionally the eighth source-proof receipt, and archives matching historical capture/helper/comparator bytes. The verifier checks source ZIP membership and every input hash, stream/result relationships and recorded Cargo executable-artifact relationships. Existing outputs are refused. These are consistency guarantees, not independent authentication or a hermetic build.
Ran `python -B .local/scenario-selection-evidence-tools/prepare.py --root C:/ar-scenario-9e8r6t --runs selection-scenario-1 selection-scenario-2 --check-only`: PASS, two runs, two recorded failures, two source proofs, 809 inputs each. No package was written and neither failure was converted to a pass.
## Run 3: invalid changed-input attempt, separate preservation required
The parent subsequently reported terminating the verified Cargo process tree after finding that formatting changed `app.rs` during capture/execution. Independently read back the closed `selection-scenario-3/result.json`: captured HEAD `a0a70340a625c16b1249d0beb71dca4fdd206636`, exit 1, `inputsUnchanged: false`, 809 captured inputs, and no executable artifact records. Its start/finish timestamps are `2026-09-24T07:43:10.740963+00:00` and `2026-09-24T07:45:40.989301+00:00`. The raw stdout has no test-run/result summary; stderr only reports desktop compilation. The termination reason is an external parent observation, not a compiler diagnostic inferred from exit 1.
Independently checked the receipt's input-map, input-ZIP and both stream size/hash pins. Compared the captured map with current frozen working files: the only differing captured path is `crates/oa-desktop/src/app.rs`. Invoking prepare in `--check-only` mode on run 3 independently produced the expected rejection, `unfinished/changed run refused: selection-scenario-3`.
Run 3 must stay outside the unchanged-input checks package and source-proof claims. Preserve its unchanged closed raw files separately with an exact ZIP/member manifest and a clearly labelled external explanation of the changed-input termination. Do not rewrite `inputsUnchanged`, HEAD, exit, timestamps or streams; do not call this an ordinary failing test or attach the final commit as its tested source. Its retained raw archive can establish preserved bytes only.
The parent identifies `ca1f18e6b0d53814f0ff16261f810aef49281c26` as the correction frozen after formatting, with run 4 started afterward. This preparatory review does not claim run 4 passed or compared to that source. The final audit must verify closed unchanged receipts, their independent source proofs, exact failure/invalid-attempt separation, original-byte equality and the completed portable package. No implementation, native, integration or release acceptance is established here.
@@ -0,0 +1,45 @@
# Scenario selection — final captured-evidence audit
2026-09-24. Bounded read-only audit of `harness/scenario-selection-evidence-20260924/checks` and the separate invalid-attempt archive in `C:/ar-scenario-9e8r6t`. This report is the only written artifact. No Cargo, native/GUI action, Git mutation, network transfer or archived-source execution. Native verification was still underway when requested; this is an evidence-package review, not final application acceptance.
## Result
No material integrity, source-identity or failure-preservation finding. The portable checks package contains exactly 12 completed unchanged-input runs, three recorded failures, 12 source proofs and four distinct source capsule pairs. Each capture has 809 inputs. `selection-scenario-3` is correctly excluded because its inputs changed and is preserved separately without a source-proof claim.
The current top-level `harness/scenario-selection-20260924.md` still says the final executable build and final-source regression repeat are pending. Those gates are now closed and proven, so their pending clauses should be updated before delivery; native verification must retain its actual pending status. The historical source-review sections explicitly describe evidence available at their review time and need not be rewritten as though they had already inspected later runs.
## Independent checks
1. Compared packaged `prepare.py`, `verify.py` and README bytes with the preparatorily reviewed selection tools: exact equality. Their adaptation remains the intended manifest-kind/title/example changes only.
2. Executed the packaged verifier with bundled Python `-B`: PASS, 12 runs / 3 failures / 12 proofs / 99 logical files / 83 stored members / 4 source capsule pairs. This did not execute Cargo or archived helpers.
3. Compared every packaged logical raw file, including optional proof receipts, byte-for-byte with its retained `.local/scenario-group-gates` original: exact equality. Archive size is **12,926,901 bytes**, SHA-256 **`05d040e856d0b37adf44f6a9e3d2789d4d76c026e61e85fdfcab0e7bebca4dd4`**.
4. Independently read each of the four named Git trees with lazy fetch disabled. Required complete equality of the filtered path inventory, then compared each of 809 source ZIP entries directly with its named Git blob and input size/hash. All four commits passed: `879eb1d5c8792a720433022e49f415d2c58ff72c`, `a0a70340a625c16b1249d0beb71dca4fdd206636`, `ca1f18e6b0d53814f0ff16261f810aef49281c26`, and `ed55b1781d86ec49af5d2588cbd06f7b878d9009`. This independently repeats source comparison rather than trusting only the archived proof/comparator label. All runs sharing each source have the same captured input-map bytes.
5. Read the actual result/stream records for all runs. Captured HEAD and proof source happen to match in all 12 accepted-package runs; neither was substituted for the other.
## Final source and historical results
All five final gates bind to **`ed55b1781d86ec49af5d2588cbd06f7b878d9009`**:
| Run | Observed result |
| --- | --- |
| `selection-lint-2` | Strict desktop all-targets Clippy, exit 0 |
| `selection-format-3` | Cargo fmt check, exit 0 |
| `selection-build-1` | Desktop executable build, exit 0 |
| `selection-scenario-5` | 268 passed, 0 failed, 1 ignored |
| `selection-menu-2` | 14 passed, 0 failed, 0 ignored |
The ignored Scenario case is `saved_spacing_capture_prepare_20_lanes_1000_messages_3_views`, explicitly diagnostic timing with no latency-acceptance threshold. The record does not imply that this ignored timing case ran. Scenario and menu filters may overlap and their counts must not be added into a unique-test total.
The other seven runs remain historical evidence: `selection-scenario-1` has 263 passing / 1 failing / 1 ignored; `selection-scenario-2` has 266 passing / 2 failing / 1 ignored; `selection-lint-1` fails on the redundant `expect` after `is_some`. The earlier format checks and ca1 Scenario/menu runs pass under their own source pins. None of these failures is relabelled as passing merely because archive/source verification succeeds.
The build is a Windows GNU LLVM **debug** desktop build (`cargo build -p oa-desktop --bin arcadia-studio --message-format=json`), not a release package. Its recorded executable is 119,545,344 bytes, SHA-256 `7e0b6e714c89e34baef38bdaf12ecd35456cd39d62cb6b1dcf06e3487b7824bc`. Independently reading the current local executable reproduced that pin. This establishes local build-artifact identity only; it does not prove launch, GUI behavior, external availability or a separate-machine package run.
## Invalid attempt and clock boundaries
Verified `invalid-attempt-3/raw.zip` against its exact eight-member manifest: seven unchanged original raw files plus the original gate helper. Every member equals its retained original and declared hash. The archive is 3,192,626 bytes, SHA-256 `9afef85b7e395d811bf99cd3a49909da881403cac3bcfcaea9fdc701f4ee5180`. The closed result remains exit 1 / `inputsUnchanged: false` / no executable records; there is no source proof. The manifest explicitly attributes deliberate process-tree termination to the parent and does not recast it as a failing test. This archive is outside the 12 unchanged-input runs.
Run 4 retains the discontinuous UTC interval verbatim (`07:46:08` to `21:22:59`) and separately records 515.9615506 monotonic seconds. The top-level record correctly uses the latter duration and warns against inferring runtime/inactivity from UTC. This audit verifies those retained values, not the separately reported observations on other machines.
## Scope of delivery
The top-level record correctly labels this as branch-specific issue **#29** work outside the frozen **#37** prerelease. It explicitly leaves Scenario group movement, semantic destination preview and aggregate removal incomplete. Archive verification, mounted test streams, successful local build and source review do not replace native verification or authorize claiming full group-operation/release acceptance. Publication and downloaded-artifact availability were not audited here. Final native findings and their exact executable/source identity remain a separate delivery step.
@@ -0,0 +1,28 @@
# Independent native evidence review
Reviewed the Scenario selection native package and updated top report on 2026-09-24. No material finding within this bounded evidence audit. This is an audit of the recorded branch-specific exercise, not implementation acceptance or a new native run.
## Integrity and provenance
- Ran `python -B harness/scenario-selection-evidence-20260924/native/verify.py`: PASS for archive integrity, build/source linkage, exact source deltas and clean close. Inspected `prepare.py`, `verify.py`, manifest, README, retained launch/capture helpers, and start/finish records.
- Independently checked the ZIP inventory, member lengths and SHA256 values: 99 members, no duplicates. All 99 members equal their retained original bytes in `.local/scenario-selection-native/run-1`, its helper directory, or the closed `selection-build-1` gate. The archive is 2,622,962 bytes, SHA256 `391a91b55ab2c4c4d7663026d38a6485bfa5876d1205f40456da508978a69df3`.
- Archived build result and source proof are byte-identical to their logical files in the separately verified checks package. They link to `ed55b1781d86ec49af5d2588cbd06f7b878d9009`. The retained copied executable equals the build artifact pin: 119,545,344 bytes, SHA256 `7e0b6e714c89e34baef38bdaf12ecd35456cd39d62cb6b1dcf06e3487b7824bc`. The executable is intentionally not included in the native archive.
- Independently matched the baseline fixture to the tracked fixture bytes in the proven build input capsule. Launch records identify the isolated copied executable; every archived capture metadata record targets that process path and has a timestamp between recorded launch and close. The closed process has exit 0, unchanged executable and fixture, and empty stdout/stderr.
## Exact model/source checks
Independently diffed baseline against the spacing snapshot. Exactly one line changes: saved diagram `incident_readable` (A), occurrence `updated_report`, `send_y` 252 to 281.8841247558594. All other lines remain identical, including saved B and semantic ordering. Selection-only, post-Undo, and final working snapshots each equal the entire 6,206-byte baseline (SHA256 `74cd2c1dd5eed7eb19895a752bb8ff9ce93655f7638a5312c96f56218568ebb3`).
The corrected packager/verifier locates A before the `incident_wide` section and then compares the complete expected file. This avoids the incorrect earlier assumption that the original ordinate appears only once across both representations. The parent reports that earlier packaging attempt failed before output creation; the corrected artifact validates. This packaging correction establishes no application defect.
## Manual visual inspection
Inspected original screenshots 26/27, 31/32, and 34. The 26-to-27 pair shows count two becoming zero, primary Properties returning to Scenario, and visible geometry translating by (-50,-30). The 31-to-32 pair shows count two becoming one, first occurrence primary, and unchanged geometry. Screenshot 34 shows the second occurrence selected and shifted downward, the first unchanged, and spacing-updated feedback. These endpoints agree with the recorded exercise and source comparisons.
Screenshots establish visible endpoints; the precise first-press input sequence is the executing agent's observed native interaction, additionally supported by the separately recorded mounted tests. The standalone verifier correctly does not claim to automate visual interpretation or execute archived helpers.
## Claim boundaries
The native README and updated `harness/scenario-selection-20260924.md` keep observed native behavior separate from mounted-test coverage. Native Shift, Redo, dirty Properties admission, stale queued events, group navigation, and fallback C are explicitly unexercised. Representation navigation is limited to retained singleton primary. Early targeting and minimized-window capture anomalies are disclosed; missing capture 45 is explained, not represented as a successful capture. The fixture's five existing checks are disclosed.
The updated top report accurately carries the final scoped gates and historical failures, keeps changed-input attempt 3 outside accepted checks, and distinguishes this #29 contribution from frozen #37. It does not claim completed group movement/removal, full workspace verification, integrated acceptance, or published-release acceptance. No Cargo, Git mutation, network access, or native input was performed in this review.
@@ -0,0 +1,63 @@
# Scenario selection adapter source review
Reviewed local commit `879eb1d5c8792a720433022e49f415d2c58ff72c` against `47917dd`, 2026-09-24. Scope: exact selection adapter, App admission/reconciliation, singleton spacing bridge, Scene input/paint and contextual menus. Read AGENTS.md, the multiple-selection contract, Scenario group policy, prior adapter critique, changed implementation and new tests. No Cargo, GUI, network or repository mutation. This review does not establish runtime acceptance or completion of issue 29. Group movement and aggregate removal are explicitly unfinished.
## Findings
### P2 — Clearing selection aborts the blank press's newly started pan
`crates/oa-desktop/src/behavior.rs:2262-2268` publishes Clear without capturing the press, explicitly to share it with background panning. The containing `behavior/canvas_workspace/viewport.rs:387-399` consequently starts `state.pan` and publishes Camera::CancelGesture. For a nonempty selection, `app/scenario_multiselect.rs:114-125` advances both canvas_epoch and camera_epoch. The subsequent Viewport diff observes that changed camera scope and clears the newly started pan (`viewport.rs:93-99`; Scenario scope uses camera_epoch at `behavior/scenario/workspace.rs:123`). The camera packet emitted by that same press also carries the previous camera lifetime.
Concrete falsifier: in either Scenario representation, select a visible occurrence, then press blank canvas and drag without releasing. Selection should clear and the existing blank-pan gesture should move the camera. Source predicts selection clears but the first drag cannot continue after redraw; a second blank drag with already empty selection works. Existing Scene-only tests omit the parent Viewport/App/rediff composition and cannot establish this interaction.
Fix: retire stale selection/spacing captures without retiring the camera gesture legitimately initiated by that same background press, or admit clear-and-begin-pan as one coordinated operation. Do not merely capture Clear and remove background panning. Add a composed input regression that includes publication ordering and widget diff, then verify the native gesture.
### P2 — Epoch retirement covers new selection events, not legacy singleton reconciliation
The new spacing bridge relies on generation changes advancing canvas_epoch (`app/scenario_spacing.rs:24-26`), but that happens only in `scenario_selection_event` (`app/scenario_multiselect.rs:114-125`). `reconcile_scenario_multiselect` can independently replace or clear membership (`:35-45`) without retirement. The deliberately retained singleton Scene path can emit legacy CanvasEvent::Select; App accepts it at `app.rs:2444-2451` and `select` changes a same-owner selection without incrementing epochs (`:1168-1219`). The outer wrapper advances canvas_epoch on a selection change only if an active spacing gesture is actually canceled (`:1631-1636`); camera_boundary tracks the owner, not exact selected member (`:1709-1723`). Thus with no active spacing, same-owner singleton A-to-B-to-A changes selection generation but can preserve the captured spacing Context and camera_epoch.
Concrete App falsifier: start with saved-view singleton A; capture but do not deliver a valid spacing Begin. Deliver same-context legacy CanvasEvent::Select(B), then Select(A), with no active spacing and no model edits. Check that generation changed while Context/camera_epoch did not. Deliver the old Begin: `scenario_spacing.rs:20-35` admits it because the live set is again singleton and no spacing token is active; its matching Finish can commit. Nonspacing Scenario boxes preserve an actual legacy selection route, so testing only Add/Remove through the new handler is not exhaustive. The new secondary-membership ABA test covers its new event path correctly but misses this path.
Fix: bind spacing Begin to the captured complete selection Snapshot, or make retirement cover every Scenario selection generation change, including legacy reconciliation. Preserve first-press singleton drag behavior while doing so. Add the legacy Select round-trip evaluator and verify that rejecting the old Begin leaves a newer live gesture, model, source, history and camera untouched. This is a remaining bridge/admission hole; the underlying legacy Select path predates this increment.
### P3 — Ordinary workspace still labels only the primary, without the group's count
`behavior/scenario/workspace.rs:348` still displays only `Selected: <primary name>`. Group count appears in the contextual menu, but no count was added to the normal Scenario workspace despite the contract's visible-count requirement (`harness/multiselect-contract-20260923.md:34-35`). Teal membership paint alone does not distinguish a two-item selection from a larger/off-screen group while property edits address only the primary.
Fix: show a compact visible count alongside the primary inspector label whenever the current Scenario snapshot contains a group. Native verification should include one selected item outside the viewport.
## Bounded positive findings
The Scenario-specific envelope uses full representation/visit Context plus complete shared Snapshot; it rejects stale selection packets before shared navigation retirement, and bare generic selection packets are refused while Scenario is active. The representation-specific shared Scope is compatible with existing infrastructure. Actual presented targets, primary handling, passive Shift tracking, deferred group collapse, and whole-group refusal keep selection distinct from movement eligibility.
Menu Open validates before shared side effects, refreshes its context after epoch advancement, and captures selection. Invoke validates token/context/snapshot before dispatch; selection actions avoid ordinary implicit subject selection. No material menu-specific defect was found in this source review.
Preserving the original first-press singleton spacing/reorder path is a reasonable compatibility choice: unselected singleton hits retain their existing gesture, while an unselected hit replaces an already displayed group and coordinate-unavailable fallback remains selectable. Saved body/header spacing keeps precedence, and groups cannot silently apply spacing/reorder/removal to only their primary. That choice needs the bridge fix above rather than being reversed wholesale.
The new tests exercise membership/primary, representation round trips, stale new-event ABA, unsupported/bare packets, group refusal, secondary ABA spacing, pending edits, deferred collapse, threshold/refusal and menu lifetimes. I inspected their fixtures and assertions; I did not execute them. No source/history mutation was found in an admitted selection-only action. Complete native focus, first-press singleton drag, background camera behavior, selection visibility, and later group-operation preview remain separate acceptance work.
## Delta review — local corrective changes after 879eb1d
Read the current uncommitted corrective source on 2026-09-24, without Git commands, Cargo or GUI execution. **All three original findings are addressed at source level; no new material lifetime regression was found in this bounded delta.** The findings above describe the reviewed commit, not the corrected working tree.
- **Blank pan:** the new selection handler now retires canvas_epoch only (`app/scenario_multiselect.rs:129-132`), leaving camera_epoch and camera_boundary_epoch unchanged. Existing Camera admission deliberately permits an older canvas epoch within the same camera lifetime. Therefore the Clear and parent CancelGesture emitted by one blank press remain compatible, while the Viewport no longer resets its pan merely because selection changed. Canvas-bound spacing and selection packets still become stale. This is the right separation of input lifetimes; native/composed verification remains necessary.
- **Legacy selection ABA:** reconciliation now captures a Snapshot against the already-current Context before changing membership and compares it with the resulting Snapshot (`app/scenario_multiselect.rs:24,49-55`). A changed existing selection cancels spacing and advances canvas_epoch. Because both snapshots use the same current Context, a changed revision/epoch alone does not repeatedly trigger retirement. An unchanged reconcile remains a no-op. The before-is-Some condition excludes initialization/scope changes; those lack an older matching selection scope or already change representation/visit, so no additional spacing-admission hole was identified there. The new `legacy_singleton_selection_aba_retires_old_spacing_begin` test covers both CanvasEvent::Select and Message::Select, checks restored exact singleton membership and changed Context, requires old Begin to be inert, then verifies a fresh Begin is accepted. The test is a suitable discriminator for the reported hole; I have not executed it.
- **Count:** the existing navigation row now always displays the exact snapshot count (`behavior/scenario/workspace.rs:201-205`), independently of opening Details. Details additionally distinguishes the primary properties target from group count (`:355-358`), and Select tooltip copy no longer promises currently unavailable group movement. This resolves the source-level omission; clipping/readability and offscreen-member comprehension still require visual evidence.
The corrective retirement remains compatible with the retained first-press singleton route: it happens when legacy selection actually changes, or after spacing Finish has already consumed its gesture, not automatically on a valid spacing Begin. Early exact packet admission and menu validation remain unchanged. No claim is made here about the separately owned mounted tests, their execution, native usability, operation preview, group movement/removal or issue 29 completion.
## Delta review — retirement moved to the outer action boundary
The parent reports that the subsequent `selection-scenario-2` run at the a0a source had 266 passing tests, two failures and one ignored test. It reports all three new mounted regressions, the corrected deletion guard fixture and legacy ABA passing. One failure was an obsolete context-retention expectation; the other demonstrated a real regression: the prior unconditional reconciliation retirement invalidated newly prepared RevealInDiagram work. These execution results are parent-reported, not independently rerun here. **The earlier source-level assessment above missed this reveal-producing action path and is superseded for that fix.**
Read the newest uncommitted corrective source immediately after that report. `reconcile_scenario_multiselect` again performs reconciliation only. `App::update` captures the full Scenario Context and Snapshot before the ordinary action (`app.rs:1382-1384`), then after reconciliation retires spacing and advances canvas_epoch only if the full Context is still identical and the Snapshot changed (`:1650-1659`). No new material defect was found in this bounded source delta.
This condition fits both relevant call paths. A legacy same-owner Select with no other retirement retains its original Context, changes the Snapshot and therefore receives the necessary retirement. An action that already changes document/revision/representation/visit/epoch invalidates older spacing by that Context change; it does not need another epoch bump merely because it also reconciles membership. In particular, diagram usage reveal prepares its new canvas_epoch before recording pending_diagram_reveal (`app/diagram_usages.rs:456-457,491-499`), so the changed Context now prevents the extra bump that stranded its viewport receipt. New exact selection events return through their own admission/retirement handler, and spacing Finish likewise owns its existing retirement; those early paths do not depend on this new tail condition. BehaviorControl delegates admitted actions through update; its existing camera/focus retirement remains separate.
The tail bump still leaves the camera lifetime intact for a same-context selection change, preserving the blank-pan correction. Scope initialization/navigation does not acquire an additional artificial retirement. This source reasoning supports the revised location but is **not a passing result for the new pending fix**. Re-run the revealed failing case and the legacy ABA/new mounted input cases on the exact corrected source; retain the failing a0 evidence as a regression finding, not acceptance. Native usability and group operation preview/movement/removal remain outside this review.
## Final bounded source delta — ca1f18e to ed55b17
Inspected the exact read-only diff from `ca1f18e` to `ed55b1781d86ec49af5d2588cbd06f7b878d9009`: one file, `crates/oa-desktop/src/behavior.rs`, three insertions and six deletions. The change replaces `self.selection.is_some()` followed by repeated `as_ref().expect(...)` / `as_ref()?` with one `if let Some((selection_context, snapshot)) = &self.selection` binding. Both ScenarioGroupPress and blank Clear still use the selection envelope's Context and Snapshot, rather than the separate interaction Context. The None path, hit testing, modifiers, capture status, movement threshold and spacing/reorder fallthrough are unchanged. No behavioral or lifetime defect was found in this bounded source delta.
The parent reports that frozen ca1f18e passed 268 Scenario tests with zero failures and one ignored, plus 14 model_menu tests, against 809 unchanged proven inputs; strict lint then found only the unnecessary unwrap addressed by this commit. I did not independently inspect or execute those gate records. Final lint/format/build/Scenario/menu runs on ed55b17 are still pending at the time of this review. This delta review does not transfer ca1 runtime evidence to an unexecuted final gate or establish native/group-operation acceptance.