Record native keyboard entry and source-budget failure verification
This commit is contained in:
@@ -16,6 +16,7 @@ harness/scenario-selection-evidence-20260924/** -text whitespace=cr-at-eol
|
||||
harness/scenario-group-spacing-evidence-20260924/** -text whitespace=cr-at-eol
|
||||
harness/scenario-reorder-interaction-evidence-20260924/** -text whitespace=cr-at-eol
|
||||
harness/scenario-group-removal-evidence-20260925/** -text whitespace=cr-at-eol
|
||||
harness/scenario-removal-followup-evidence-20260925/** -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.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Scenario aggregate removal: Windows implementation and verification
|
||||
|
||||
Later [keyboard and real source-budget-failure verification](scenario-removal-followup-20260925.md) covers focused native Delete/Shift+Delete and the bounded App source-update error path. The original observations and archives below remain unchanged.
|
||||
|
||||
Implemented and tested at `43340b94a13f192c3b5d3c3112dbe87246790a44` on `codex/scenario-multiselect-9e8r6t-20260924`, following the [removal policy](scenario-group-removal-policy-20260925.md) and [multiple-selection contract](multiselect-contract-20260923.md). This is branch-specific evidence, outside the delivered issue-37 prerelease. Default-branch integration, a new release and all issue-29 acceptance are not asserted.
|
||||
|
||||
Scenario groups now have one captured local removal operation through the header, contextual menu and mounted canvas Delete/Shift+Delete route. The review shows selected and additional removals separately from changed surviving records, searches full labels/aliases and pages the complete impact. One explicit confirmation executes one Document transaction. Native Windows checks establish cancellation, mixed participant/message removal, exact saved source, one Undo/Redo, compact review expiry/recovery and fresh-process reopening for the fixture below.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Scenario removal: keyboard entry and real source-budget failure
|
||||
|
||||
This supplements the [original bounded Windows implementation evidence](scenario-group-removal-app-20260925.md), preserving that report and its frozen archives. Native behavior uses the same production source `43340b94a13f192c3b5d3c3112dbe87246790a44`; test-only commit `2aaea8a3a0b9522548b4418d9609f75d90c24345` adds a genuine failure-path evaluator without changing production behavior. It does not establish integration, release acceptance or issue-29 completion.
|
||||
|
||||
## Native focused entry
|
||||
|
||||
A fresh Windows process used the same verified executable, DLL and 4,389-byte fixture as the original run. Contextual Add selected the Intake participant and first message. F6 followed by Delete opened the complete two-object review: 2 records removed, 10 updated, 12 matches across two pages, with both selected aliases visible. A separate second Delete left the review open; Escape retained selection. Save matched every original source byte.
|
||||
|
||||
After Save, F6 followed by Shift+Delete opened the same complete review. Enter did not implicitly confirm; Escape again retained both objects, and Save again matched the baseline exactly. Normal process exit was 0 with empty stdout/stderr and unchanged immutable inputs. The follow-up does not re-exercise destructive confirmation/history/reopen; those remain covered by the original independent native evidence.
|
||||
|
||||
The [portable native evidence](scenario-removal-followup-evidence-20260925/native/manifest.json) contains 24 capture records, original images, two saved snapshots, lifecycle/build/source receipts, attributed action observations and independent review. Archive: 2,339,112 bytes, SHA256 `8be1f0d9d584edc889fb44076c1ef18675afd502bb8f68cb1c8fa974c7f48a4f`, 127 members. Its CLI preflight is an exact reused copy of the previously accepted attempt, not a new execution. Four binary copies are pinned but omitted; this is evidence, not a runnable distribution. Parent standalone verification passes.
|
||||
|
||||
The causal key sequence is recorded from the parent's actual Computer Use calls; screenshots alone do not prove keystrokes. The independent reviewer inspected screenshots and rehashed originals but did not witness input injection. Functional keyboard focus is established by the resulting review, not internal UIA focus inspection. A second separate Delete press is not OS autorepeat evidence. Native Shift-click remains unverified because the supported automation API cannot hold a modifier through a subsequent mouse click; contextual Add is the observed alternative. Keyboard-only confirmation and broad accessibility remain outside this follow-up.
|
||||
|
||||
## Real failure evaluator
|
||||
|
||||
Removing model elements need not shrink preserved source. A surviving message's explicit `sender @a; receiver @b;` becomes `sender none; receiver none;` when its two participants are removed. Many short references can therefore outweigh the deleted declarations. This correction to the earlier assumption enables a real public-interface test without production hooks or private limit mutation.
|
||||
|
||||
The added test constructs 256 asynchronous messages and two local participants in a valid advisory-incomplete authoring model. It first runs the actual prepared removal in an unrestricted Document, requires the exact independently constructed expected model, and asserts net final source growth. It then constructs the same original bundle under a source-byte budget equal to its baseline size. A real Document execution must produce the `source_limit` diagnostic before the test exercises the actual App Begin/Confirm route.
|
||||
|
||||
Derived/Saved representations each run with no history and with a genuine earlier Rename followed by Undo. Failure must preserve the accepted snapshot identity, full source/model, revision, clean/save state, selection order/primary/generation, representation/visit, camera/navigation, source editor and workspace. The review closes and retires its input epoch; replayed confirmation is inert. Existing Redo must still restore the exact renamed model/source, and one Undo restores the original clean state. This tests in-memory source preparation failure, not filesystem write failure. It does not establish arbitrary history-depth or camera stress coverage.
|
||||
|
||||
Runtime verification passed at the exact test-only commit: `removal-app-source-failure-1` ran one test containing all four App cases, with 1 passed / 0 failed / 0 ignored and 1,630 filtered, finishing the test in 7.10 seconds after compilation. The original report's bounded removal source-budget-failure gap is now covered; this does not extend to disk I/O errors. The independent investigation found no corresponding public failure trigger for byte-neutral same-parent reorder, whose separate coverage limit remains unchanged.
|
||||
|
||||
Formatting and strict desktop all-target Clippy (`cargo clippy -p oa-desktop --all-targets --message-format=json -- -D warnings`) also pass. Each of the three closed gates captures 823 unchanged inputs with an exact committed-source proof, map SHA256 `5e77ee22dde7a1223d0e679bd877fe403fbd24ed1cfa9c7615af51cf288fb92c`. Only test code changed, so the earlier production build/native evidence is reused rather than relabeled as a new binary or full-suite run. [Independent test review](scenario-removal-followup-evidence-20260925/test-review.md), [native review](scenario-removal-followup-evidence-20260925/native-review.md), [attributed native actions](scenario-removal-followup-evidence-20260925/native-actions.md).
|
||||
|
||||
The [portable checks](scenario-removal-followup-evidence-20260925/checks/README.md) retain all three follow-up attempts, raw logs, source capsule, measured helpers and exact source proofs. Checks archive: 3,279,375 bytes, SHA256 `320a94e58e7a1c75bae3108b910f1f9b319177b5702d787434a179fb92a2b9b0`. All three passed; the original increment's failed historical attempts remain in its separate unchanged archive. Standalone offline verification passes for both follow-up packages; a direct native archive audit also compared all 126 original members and four omitted binary pins, plus semantic equality of its generated review config.
|
||||
|
||||
## Reproduction
|
||||
|
||||
Use the project Windows GNU/LLVM Rust 1.98.1 environment, one Cargo job and incremental compilation disabled. Test command:
|
||||
|
||||
```text
|
||||
cargo test -p oa-desktop scenario_group_removal_source_write_failure --message-format=json -- --nocapture
|
||||
```
|
||||
|
||||
Native archive consistency (Python 3.11+, standard library):
|
||||
|
||||
```text
|
||||
python -B harness/scenario-removal-followup-evidence-20260925/native/verify.py harness/scenario-removal-followup-evidence-20260925/native
|
||||
python -B harness/scenario-removal-followup-evidence-20260925/checks/verify.py
|
||||
```
|
||||
|
||||
Gitea server storage rejected the original evidence push and issue comment during this checkpoint; these new records are local-only until a successful push and fresh byte readback. Live publication status belongs to issue 29 when writes recover. Linux builds/checks remain user-paused.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Scenario aggregate removal: captured checks
|
||||
|
||||
Create-only packaging for explicitly named, closed `removal-app-*` runs under `CHECKOUT/.local/scenario-group-gates`. Python 3.11+ standard library; no Cargo, Git, network, source execution or native process is used. Adapted from the reviewed semantic-reorder checks tools; input capsules remain deduplicated by SHA-256, with every original logical run path retained.
|
||||
|
||||
```text
|
||||
python -B prepare.py --root CHECKOUT --runs removal-app-scenario-1 removal-app-scenario-2 --check-only
|
||||
python -B prepare.py --root CHECKOUT --runs CLOSED_RUN_NAMES --output NEW_DIRECTORY
|
||||
python -B NEW_DIRECTORY/verify.py
|
||||
```
|
||||
|
||||
Replace `CLOSED_RUN_NAMES` with the explicit complete set intended for delivery, including failed attempts. The tool does not silently discover or select runs. It refuses a pre-existing output directory, active/incomplete runs, changed-input runs and runs missing an exact source-proof receipt. It validates all selected data in memory before creating output. The supplied two-run example is historical demonstration, not a complete final delivery inventory. Do not package an active run.
|
||||
|
||||
Output contains `evidence.zip`, `manifest.json`, `files.json`, the packager, standalone verifier and this README. Raw stdout/stderr, started/process/result receipts, input maps/ZIPs and source proofs retain exact bytes. Measured capture helpers and the source comparator are included as data. Supply repeatable `--helper PATH` or `--comparator PATH` when a historical measured helper is no longer at its default checkout path.
|
||||
|
||||
Verification checks outer file and ZIP inventories/hashes, safe member paths, exact nested input ZIP membership and bytes against its map, dynamic input counts (currently 823), unchanged closed-run receipts, command/time/process consistency, executable Cargo-record relationships, proof result/map/count/name bindings and comparator provenance. The existing capture contract includes all 71 tracked patched-widget vendor files. Every recorded exit code is printed; failures remain failures regardless of package consistency PASS.
|
||||
|
||||
Captured `head` and the proof's named commit retain distinct meanings. The offline verifier validates the proof's metadata and pinned comparator, but does not execute it, read Git objects, establish that captured source matches Git, or rehash omitted executable bytes. The archived source bytes are the independently hash-checkable input witness. Source attribution ultimately relies on the separately performed source comparison. Neither successful packaging nor a source proof establishes test success, hermetic builds, native usability, integration, release acceptance or issue completion.
|
||||
|
||||
Maintainer selfcheck (only closed runs; fresh scratch output beneath this tools directory):
|
||||
|
||||
```text
|
||||
python -B selfcheck.py --root CHECKOUT --runs removal-app-scenario-1 removal-app-scenario-2 --output TOOLS_DIRECTORY/selfcheck-package-1
|
||||
```
|
||||
|
||||
It verifies exact original raw equality, refuses existing output, and mutates only in-memory copies to test corruption rejection. It never edits the original captures or executes archived helpers. Run the standalone verifier again after copying a final package to its delivery location. Published outer pins provide integrity, not an external signature or trust anchor.
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"prepare.py": {
|
||||
"bytes": 6320,
|
||||
"sha256": "c643f42840df075e89a3fc0dc912baa42c51e9ceaa507fd1c96b751dca92ac1a"
|
||||
},
|
||||
"verify.py": {
|
||||
"bytes": 11525,
|
||||
"sha256": "35470ea415156e9001c8ab2fb7d6b108179382d0456490e2caffc242c4f17643"
|
||||
},
|
||||
"README.md": {
|
||||
"bytes": 3306,
|
||||
"sha256": "ae5b9595d3e9063ed9a8f4fcc9c16e27708d0e3e3989617de560d75b71a2143c"
|
||||
},
|
||||
"manifest.json": {
|
||||
"bytes": 9888,
|
||||
"sha256": "c65ddc451a87b18467e6a1529d6fd1b89d6b1c2b71c253a56bcf493ee3bc0b83"
|
||||
},
|
||||
"evidence.zip": {
|
||||
"bytes": 3279375,
|
||||
"sha256": "320a94e58e7a1c75bae3108b910f1f9b319177b5702d787434a179fb92a2b9b0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"kind": "scenario-aggregate-removal-cargo-gates",
|
||||
"runs": {
|
||||
"removal-app-source-failure-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"scenario_group_removal_source_write_failure",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"--nocapture"
|
||||
],
|
||||
"head": "2aaea8a3a0b9522548b4418d9609f75d90c24345",
|
||||
"exitCode": 0,
|
||||
"inputCount": 823,
|
||||
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
|
||||
"sourceProof": {
|
||||
"commit": "2aaea8a3a0b9522548b4418d9609f75d90c24345",
|
||||
"pin": {
|
||||
"bytes": 607,
|
||||
"sha256": "0c692afdd177acbef6890a11a5e656771d7ca803f268fb93e3df0796991c8b01"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"removal-app-followup-format-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"fmt",
|
||||
"--all",
|
||||
"--",
|
||||
"--check"
|
||||
],
|
||||
"head": "2aaea8a3a0b9522548b4418d9609f75d90c24345",
|
||||
"exitCode": 0,
|
||||
"inputCount": 823,
|
||||
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
|
||||
"sourceProof": {
|
||||
"commit": "2aaea8a3a0b9522548b4418d9609f75d90c24345",
|
||||
"pin": {
|
||||
"bytes": 608,
|
||||
"sha256": "6603622dca0f46575c890b7b3478eaaa9fa6a665c66058d0aa70e15fbb7ec6b6"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"removal-app-followup-clippy-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--all-targets",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings"
|
||||
],
|
||||
"head": "2aaea8a3a0b9522548b4418d9609f75d90c24345",
|
||||
"exitCode": 0,
|
||||
"inputCount": 823,
|
||||
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
|
||||
"sourceProof": {
|
||||
"commit": "2aaea8a3a0b9522548b4418d9609f75d90c24345",
|
||||
"pin": {
|
||||
"bytes": 608,
|
||||
"sha256": "2bbfe51adcc832deec9e000eff579740909002758149ee2f0c3c1931f5346201"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
}
|
||||
},
|
||||
"captureHelpers": {
|
||||
"gate.py": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
|
||||
"source-proof.py": "helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py"
|
||||
},
|
||||
"logicalFiles": {
|
||||
"cargo/removal-app-followup-clippy-1/inputs.json": "capsules/5e77ee22dde7a1223d0e679bd877fe403fbd24ed1cfa9c7615af51cf288fb92c.json",
|
||||
"cargo/removal-app-followup-clippy-1/inputs.zip": "capsules/62839623a185599a680952d83d652b677e00e670232e7d8e7887ea2a5bac6649.zip",
|
||||
"cargo/removal-app-followup-clippy-1/process.json": "cargo/removal-app-followup-clippy-1/process.json",
|
||||
"cargo/removal-app-followup-clippy-1/result.json": "cargo/removal-app-followup-clippy-1/result.json",
|
||||
"cargo/removal-app-followup-clippy-1/source-proof.json": "cargo/removal-app-followup-clippy-1/source-proof.json",
|
||||
"cargo/removal-app-followup-clippy-1/started.json": "cargo/removal-app-followup-clippy-1/started.json",
|
||||
"cargo/removal-app-followup-clippy-1/stderr.log": "cargo/removal-app-followup-clippy-1/stderr.log",
|
||||
"cargo/removal-app-followup-clippy-1/stdout.log": "cargo/removal-app-followup-clippy-1/stdout.log",
|
||||
"cargo/removal-app-followup-format-1/inputs.json": "capsules/5e77ee22dde7a1223d0e679bd877fe403fbd24ed1cfa9c7615af51cf288fb92c.json",
|
||||
"cargo/removal-app-followup-format-1/inputs.zip": "capsules/62839623a185599a680952d83d652b677e00e670232e7d8e7887ea2a5bac6649.zip",
|
||||
"cargo/removal-app-followup-format-1/process.json": "cargo/removal-app-followup-format-1/process.json",
|
||||
"cargo/removal-app-followup-format-1/result.json": "cargo/removal-app-followup-format-1/result.json",
|
||||
"cargo/removal-app-followup-format-1/source-proof.json": "cargo/removal-app-followup-format-1/source-proof.json",
|
||||
"cargo/removal-app-followup-format-1/started.json": "cargo/removal-app-followup-format-1/started.json",
|
||||
"cargo/removal-app-followup-format-1/stderr.log": "cargo/removal-app-followup-format-1/stderr.log",
|
||||
"cargo/removal-app-followup-format-1/stdout.log": "cargo/removal-app-followup-format-1/stdout.log",
|
||||
"cargo/removal-app-source-failure-1/inputs.json": "capsules/5e77ee22dde7a1223d0e679bd877fe403fbd24ed1cfa9c7615af51cf288fb92c.json",
|
||||
"cargo/removal-app-source-failure-1/inputs.zip": "capsules/62839623a185599a680952d83d652b677e00e670232e7d8e7887ea2a5bac6649.zip",
|
||||
"cargo/removal-app-source-failure-1/process.json": "cargo/removal-app-source-failure-1/process.json",
|
||||
"cargo/removal-app-source-failure-1/result.json": "cargo/removal-app-source-failure-1/result.json",
|
||||
"cargo/removal-app-source-failure-1/source-proof.json": "cargo/removal-app-source-failure-1/source-proof.json",
|
||||
"cargo/removal-app-source-failure-1/started.json": "cargo/removal-app-source-failure-1/started.json",
|
||||
"cargo/removal-app-source-failure-1/stderr.log": "cargo/removal-app-source-failure-1/stderr.log",
|
||||
"cargo/removal-app-source-failure-1/stdout.log": "cargo/removal-app-source-failure-1/stdout.log",
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py",
|
||||
"helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
|
||||
"helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py": "helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py"
|
||||
},
|
||||
"members": {
|
||||
"capsules/5e77ee22dde7a1223d0e679bd877fe403fbd24ed1cfa9c7615af51cf288fb92c.json": {
|
||||
"bytes": 138269,
|
||||
"sha256": "5e77ee22dde7a1223d0e679bd877fe403fbd24ed1cfa9c7615af51cf288fb92c"
|
||||
},
|
||||
"capsules/62839623a185599a680952d83d652b677e00e670232e7d8e7887ea2a5bac6649.zip": {
|
||||
"bytes": 3291001,
|
||||
"sha256": "62839623a185599a680952d83d652b677e00e670232e7d8e7887ea2a5bac6649"
|
||||
},
|
||||
"cargo/removal-app-followup-clippy-1/process.json": {
|
||||
"bytes": 179,
|
||||
"sha256": "dac42a2700d40baf0eea1b244dad074bab0597732beb3b1212c6c2549b31d1cd"
|
||||
},
|
||||
"cargo/removal-app-followup-clippy-1/result.json": {
|
||||
"bytes": 1958,
|
||||
"sha256": "227e9e735b1f5b523bbe55e873c449820d705323f540dd188c8b10d0e154396a"
|
||||
},
|
||||
"cargo/removal-app-followup-clippy-1/source-proof.json": {
|
||||
"bytes": 608,
|
||||
"sha256": "2bbfe51adcc832deec9e000eff579740909002758149ee2f0c3c1931f5346201"
|
||||
},
|
||||
"cargo/removal-app-followup-clippy-1/started.json": {
|
||||
"bytes": 1563,
|
||||
"sha256": "e19225955fa304f39a40f5e38cc6b403418d1db1fabfae9e68f859fd1fa3af84"
|
||||
},
|
||||
"cargo/removal-app-followup-clippy-1/stderr.log": {
|
||||
"bytes": 134,
|
||||
"sha256": "aa551fda3119b3d6f5836a46546fa13eaf090b885a97cfbfed689883ed9cc569"
|
||||
},
|
||||
"cargo/removal-app-followup-clippy-1/stdout.log": {
|
||||
"bytes": 269430,
|
||||
"sha256": "fde8510c039ad0f2ca28c177eb534203097a735aa63f5e5a870023f1490dec86"
|
||||
},
|
||||
"cargo/removal-app-followup-format-1/process.json": {
|
||||
"bytes": 116,
|
||||
"sha256": "c339c7dbe2acc7718a812c810960a600595002556e67014a99cc382779da3520"
|
||||
},
|
||||
"cargo/removal-app-followup-format-1/result.json": {
|
||||
"bytes": 1869,
|
||||
"sha256": "52ec36f6e5379ae86ed87ae836e0d1571e472d55b6a11860f0a02f48d38c1768"
|
||||
},
|
||||
"cargo/removal-app-followup-format-1/source-proof.json": {
|
||||
"bytes": 608,
|
||||
"sha256": "6603622dca0f46575c890b7b3478eaaa9fa6a665c66058d0aa70e15fbb7ec6b6"
|
||||
},
|
||||
"cargo/removal-app-followup-format-1/started.json": {
|
||||
"bytes": 1480,
|
||||
"sha256": "f8de288f1bc1e0a578cfd564cd37f205ad73834231ad9c3cdceb627cd970c1bc"
|
||||
},
|
||||
"cargo/removal-app-followup-format-1/stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/removal-app-followup-format-1/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/removal-app-source-failure-1/process.json": {
|
||||
"bytes": 204,
|
||||
"sha256": "ba8d71c3f5efed6bfd5f9940b56b84b3f51c3b22395aee3da8afe1a78ae917df"
|
||||
},
|
||||
"cargo/removal-app-source-failure-1/result.json": {
|
||||
"bytes": 3309,
|
||||
"sha256": "f92b9dd1b8eab7748cade9a574ddadbb276b9b3eacef2aa38ff79a07e92c1c92"
|
||||
},
|
||||
"cargo/removal-app-source-failure-1/source-proof.json": {
|
||||
"bytes": 607,
|
||||
"sha256": "0c692afdd177acbef6890a11a5e656771d7ca803f268fb93e3df0796991c8b01"
|
||||
},
|
||||
"cargo/removal-app-source-failure-1/started.json": {
|
||||
"bytes": 1583,
|
||||
"sha256": "56d69a876c563b609ec8e4bdaa3fa12bf003b8908400f492cdc5c247f99f5a5f"
|
||||
},
|
||||
"cargo/removal-app-source-failure-1/stderr.log": {
|
||||
"bytes": 284,
|
||||
"sha256": "6fe5cc7e5a05c5a89efb7cac80344768f8b16c60f0d612e962a568afa02df2e4"
|
||||
},
|
||||
"cargo/removal-app-source-failure-1/stdout.log": {
|
||||
"bytes": 293291,
|
||||
"sha256": "761f91057e4c94a748717fad59e3dfd21a4dc09d1fb91014699dffad24ccafcf"
|
||||
},
|
||||
"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 == RAW_FILES | {"source-proof.json"}, "exact closed raw files and source proof 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-aggregate-removal-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,195 @@
|
||||
"""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"removal-app-[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-aggregate-removal-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():
|
||||
need("sourceProof" in spec, run + ": source proof required")
|
||||
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,25 @@
|
||||
# Parent action observations (not an instrumented input log)
|
||||
|
||||
Recorded by Codex-9e8r6t /root from the actual Computer Use calls in this task. Original capture JSON records capture time/window/images, not injected keys. The following explicitly attributed transcript supplies the causal action record; screenshot labels alone are not proof of keystrokes. Calls use the latest returned `groupState.window`, and coordinate actions use `groupState.screenshots.at(-1).id`. Every action was followed immediately by the named captured state.
|
||||
|
||||
01 launched capture. 02 click accessibility Maximize index5. 03 `sky.press_key({window: groupState.window, key: 'Control_L+o'})`; fresh list_windows selects returned Open dialog. 04 picker capture. 05 `Alt_L+n` focuses filename (UIA still misreports search; visible caret is filename). 06 type_text literal `C:\ar-scenario-9e8r6t\.local\scenario-removal-keyboard-native\run-1\working.arcadia`, visibly confirmed. 07 Return opens model, capture original returned main window. 08 click143,486 selects Main removal sequence. 09 click332,111 Behavior & scenarios. 10 click891,257 opens layout chooser. 11 click325,308 selects Saved A. 12 click600,341 selects Intake participant. 13 right-click654,401 first message. 14 click759,440 Add to selection. Screenshot shows count2, Intake+first message highlighted, primary alias a.
|
||||
|
||||
| After capture | Actual subsequent sky.press_key key | Result capture |
|
||||
| --- | --- | --- |
|
||||
| 14-two-selected | F6 | 15-canvas-keyboard-focus |
|
||||
| 15-canvas-keyboard-focus | Delete | 16-delete-result |
|
||||
| 16-delete-result | Delete | 17-second-delete-keeps-review |
|
||||
| 17-second-delete-keeps-review | Escape | 18-escape-retains-selection |
|
||||
| 18-escape-retains-selection | Control_L+s | 19-save-after-delete-cancel |
|
||||
| 19-save-after-delete-cancel | F6 | 20-focus-before-shift-delete |
|
||||
| 20-focus-before-shift-delete | Shift_L+Delete | 21-shift-delete-review |
|
||||
| 21-shift-delete-review | Return | 22-enter-keeps-review |
|
||||
| 22-enter-keeps-review | Escape | 23-shift-delete-cancel |
|
||||
| 23-shift-delete-cancel | Control_L+s | 24-save-after-shift-delete-cancel |
|
||||
| 24-save-after-shift-delete-cancel | Alt_L+F4 | closed process receipt |
|
||||
|
||||
Capture16 and21 show `Remove 2 Scenario objects?`, `2 records removed · 10 records updated`, the selected Intake @intake_lane and first message @a, Page1of2/12matches. Capture17/22 show the same open review. Capture18/23 show retained count2 and primary alias a. Both post-Save snapshots equal the original4389-byte baseline, SHA25670c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a. Parent did not confirm removal in this follow-up; prior frozen native runs establish explicit-confirm success/history/reopen separately.
|
||||
|
||||
The second Delete is a separate keypress, not evidence of OS autorepeat. F6 input followed by successful Delete review is functional focus evidence, not UIA introspection of inner focus. This does not establish Shift-click, keyboard-only confirmation or accessibility-tree completeness.
|
||||
|
||||
Initial setup tried eval of the capture-helper text; node_repl refused string code generation before evaluating that helper. The helper function was then declared directly in node_repl, preserving equivalent capture behavior. No input was issued by the failed eval. Original prepare-at-records-HEAD assertion failure is described in README; neither setup failure is a product failure.
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"status": "PASS original bytes equal archived bytes",
|
||||
"direct_files": 126,
|
||||
"generated_config_semantic_equal": true,
|
||||
"omitted_binary_pins_checked": 4,
|
||||
"archive": {
|
||||
"bytes": 2339112,
|
||||
"sha256": "8be1f0d9d584edc889fb44076c1ef18675afd502bb8f68cb1c8fa974c7f48a4f"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# Independent native keyboard follow-up audit
|
||||
|
||||
Reviewer: `/root/keyboard_evidence_review`, 2026-09-25 UTC. Read-only inspection of the closed run, helper sources and original archive; the only write is this review. No UI automation, Cargo, Gitea or product execution. Original screenshots 14–24 were inspected with `view_image`, especially 16 and 21. This reviewer did not operate the application or independently witness injected keys.
|
||||
|
||||
**Finding: the bounded parent-observed keyboard sequence is supported, with no blocking inconsistency.** `action-observations.md` supplies the explicitly parent-attributed actual sky calls: F6 then Delete; a separate second Delete; Escape and Save; F6 then Shift_L+Delete; Return; Escape and Save. That record supplies causality; screenshot filenames and capture JSON alone do not prove injected keys.
|
||||
|
||||
- Captures 14/15 show two selected objects, Intake participant and first message, with primary alias `a`. Capture 16 shows “Remove 2 Scenario objects?”, “2 records removed · 10 records updated”, 12 matches over two pages, and both removed selected objects `@intake_lane` and `@a`. Capture 21 shows the same whole-group review after the attributed Shift+Delete action.
|
||||
- Captures 16/17 and 21/22 remain visually identical with review open. In the recorded workflow, a separate second Delete and Return respectively did not implicitly confirm. Captures 18/23 show the modal dismissed, count two retained, Intake/message selection retained, and primary alias `a`. Captures 19/24 show saved status.
|
||||
- Independently rehashed all 12 prepared immutable entries, the started receipt's prepared-file pin, the final working source and both snapshot sources: zero mismatches. Both saved snapshots and final source are exactly 4,389 bytes, SHA256 `70c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a`, matching the original baseline. Both evaluator receipts report exit 0/PASS. `finished.json` reports exit 0 and immutableUnchanged true; actual stdout/stderr are both empty.
|
||||
- Build/source proof pins agree on source `43340b94a13f192c3b5d3c3112dbe87246790a44` and 823 inputs. The copied executable is 120,067,072 bytes, SHA256 `e118e7f0a2ff53ac9c22a73d07555929d11e1ad917d430a7ae88d469aaa91155`; DLL is 190,976 bytes, SHA256 `f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4`. Started identity points to this run's executable, with fresh cwd and system-only PATH.
|
||||
- Compared original and follow-up lifecycle helpers: the only change splits exact HEAD/build-source equality into exact receipt/proof/source equality plus source-ancestor-of-current-HEAD verification. Exact currently enumerated input-map equality remains mandatory before prepare and launch. This is a justified records-descendant allowance, not permission to accept changed source. Current HEAD at review is `f0e8746950b3f85d4ab64e9806137e51411465ca`. Concurrent parent edits mean this audit does not rerun present-working-tree source binding as a historical preparation claim.
|
||||
- Original frozen native archive independently hashes to its documented `9eb28f970d256160238234279fa91ad60e404e8b11d8d7e6d55178999919e2b1`; size remains 6,942,333 bytes. Git reports no working diff in that archive directory.
|
||||
|
||||
## Limits and missing evidence
|
||||
|
||||
No claim-critical result evidence is missing for the narrowly attributed workflow above. A self-contained instrumented input log or raw tool transcript is absent: causal key attribution relies on the parent's explicit observations, not independent input instrumentation. Preserve that distinction in publication. Capture JSON exposes window chrome, not internal focus; successful entry after attributed F6 is functional focus evidence only.
|
||||
|
||||
This follow-up does not exercise native Shift-click (contextual Add created the group), OS autorepeat (the second Delete is a separate press), keyboard-only confirmation, actual removal/history/reopen, other scenarios/platforms, or all second-page contents. Supported sky API limitations were supplied by the parent, not independently revalidated here. The screenshots establish the review totals and selected identities, not all 12 affected details. Original frozen native evidence remains the separate source for earlier explicit-confirm/history/reopen claims. Preparation/eval setup failures are disclosed by the parent and are not product failures. This review is bounded acceptance evidence, not release or integration acceptance.
|
||||
@@ -0,0 +1,556 @@
|
||||
{
|
||||
"kind": "scenario-aggregate-removal-native-evidence",
|
||||
"archive": {
|
||||
"bytes": 2339112,
|
||||
"sha256": "8be1f0d9d584edc889fb44076c1ef18675afd502bb8f68cb1c8fa974c7f48a4f"
|
||||
},
|
||||
"members": {
|
||||
"fixtures/baseline.arcadia": {
|
||||
"bytes": 4389,
|
||||
"sha256": "70c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a"
|
||||
},
|
||||
"fixtures/expected-removed.arcadia": {
|
||||
"bytes": 3856,
|
||||
"sha256": "1cb04cbab8428fc672293f29783fffdeecaf6ec6d81d5439501c32e866c8264f"
|
||||
},
|
||||
"fixtures/fixture-manifest.json": {
|
||||
"bytes": 882,
|
||||
"sha256": "2c7b8996b0bcfadfce727e013c4520ecf41e6b6d3ba1e874057a16df2a4a43b0"
|
||||
},
|
||||
"helpers/README.md": {
|
||||
"bytes": 2611,
|
||||
"sha256": "044597ed840484493bf8a400ca5624dce83d569e61821a218cca6f8fa389cceb"
|
||||
},
|
||||
"helpers/action-observations.md": {
|
||||
"bytes": 3397,
|
||||
"sha256": "a9670feebc8a1df386ecc65230e1105dbc1113e9084d9f29a1862acc98995c44"
|
||||
},
|
||||
"helpers/capture.js": {
|
||||
"bytes": 1597,
|
||||
"sha256": "9035c150f9843bc0a973c77255c1a0ad229bc5e579ef044757f634a16836eff0"
|
||||
},
|
||||
"helpers/cli-preflight.py": {
|
||||
"bytes": 5661,
|
||||
"sha256": "f7f6ceb2686fc5d17929f842952596919f08b7212a25a209cd835989670255d3"
|
||||
},
|
||||
"helpers/independent-review.md": {
|
||||
"bytes": 4447,
|
||||
"sha256": "6209f5dcee4b9596d9c4285dd746bfd74a8e82d3acf95846c507fe72ed9faea1"
|
||||
},
|
||||
"helpers/native-evidence-package.py": {
|
||||
"bytes": 6013,
|
||||
"sha256": "0dea3b02bd4390cc36968f4e3b7a929774b30e170a0d5b3fbd240d910019a8df"
|
||||
},
|
||||
"helpers/native-evidence-verify.py": {
|
||||
"bytes": 19313,
|
||||
"sha256": "03b6b941580ca0c5149f7751e9aa3a8ca2e58aad98d589626029905de04c51a2"
|
||||
},
|
||||
"helpers/native-harness.py": {
|
||||
"bytes": 13292,
|
||||
"sha256": "9844080f8878237116b3b93973979269a8b3d5f0c1400eb449fb7d0ca9587647"
|
||||
},
|
||||
"helpers/verify-captured.py": {
|
||||
"bytes": 2019,
|
||||
"sha256": "32078fe720f00f6c123daa3d7d0cbbd4d296211d3610eb33dec4f74c5be2f573"
|
||||
},
|
||||
"preflight/fixture-preflight-1/baseline.arcadia.stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"preflight/fixture-preflight-1/baseline.arcadia.stdout.json": {
|
||||
"bytes": 6752,
|
||||
"sha256": "3bee31a8bd9109974b6e38ef5817d198027ad37f5a204710f52aabc68521a842"
|
||||
},
|
||||
"preflight/fixture-preflight-1/evidence/cli-preflight.py": {
|
||||
"bytes": 5661,
|
||||
"sha256": "f7f6ceb2686fc5d17929f842952596919f08b7212a25a209cd835989670255d3"
|
||||
},
|
||||
"preflight/fixture-preflight-1/evidence/fixture-manifest.json": {
|
||||
"bytes": 882,
|
||||
"sha256": "2c7b8996b0bcfadfce727e013c4520ecf41e6b6d3ba1e874057a16df2a4a43b0"
|
||||
},
|
||||
"preflight/fixture-preflight-1/evidence/inputs.json": {
|
||||
"bytes": 138269,
|
||||
"sha256": "839b041088be0638c55cdcc4da18d6cf9b69eecee3c00d48734759166ed06a0f"
|
||||
},
|
||||
"preflight/fixture-preflight-1/evidence/native-harness.py": {
|
||||
"bytes": 13148,
|
||||
"sha256": "7db4a634633e50a94f7f9b20370688955e7f00c137a7036d9c2ab7a8bea59004"
|
||||
},
|
||||
"preflight/fixture-preflight-1/evidence/result.json": {
|
||||
"bytes": 4454,
|
||||
"sha256": "539b7536236430b955e2657dd1e5b9ff905c029e5d1d10c2ac194d7d7847464a"
|
||||
},
|
||||
"preflight/fixture-preflight-1/evidence/source-proof.json": {
|
||||
"bytes": 598,
|
||||
"sha256": "b548459a93f6510422eb8dd8542c9eadfc92604c137bf73b3ffaca4c8d9edffe"
|
||||
},
|
||||
"preflight/fixture-preflight-1/evidence/verify-captured.py": {
|
||||
"bytes": 2019,
|
||||
"sha256": "32078fe720f00f6c123daa3d7d0cbbd4d296211d3610eb33dec4f74c5be2f573"
|
||||
},
|
||||
"preflight/fixture-preflight-1/expected-removed.arcadia.stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"preflight/fixture-preflight-1/expected-removed.arcadia.stdout.json": {
|
||||
"bytes": 9306,
|
||||
"sha256": "986d89f8098db95d42dee75fea11f407aef314e90f0ca020201365503f10b73d"
|
||||
},
|
||||
"preflight/fixture-preflight-1/fixtures/baseline.arcadia": {
|
||||
"bytes": 4389,
|
||||
"sha256": "70c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a"
|
||||
},
|
||||
"preflight/fixture-preflight-1/fixtures/expected-removed.arcadia": {
|
||||
"bytes": 3856,
|
||||
"sha256": "1cb04cbab8428fc672293f29783fffdeecaf6ec6d81d5439501c32e866c8264f"
|
||||
},
|
||||
"preflight/fixture-preflight-1/result.json": {
|
||||
"bytes": 3338,
|
||||
"sha256": "6f155edb913549cdb29834839433290bffaff0c86205735a1fb7d7591ad3f855"
|
||||
},
|
||||
"preflight/fixture-preflight-1/started.json": {
|
||||
"bytes": 1800,
|
||||
"sha256": "d710a8539aacc6c48c335177376088617ad07cf7856e07743f25950e0272ccb6"
|
||||
},
|
||||
"preparation/review-config.json": {
|
||||
"bytes": 162,
|
||||
"sha256": "b7d10d9ba274ab8cfe3714ce02b7353771106cce8b327d45b8c30dfbd8df4069"
|
||||
},
|
||||
"review-config.json": {
|
||||
"bytes": 245,
|
||||
"sha256": "83a99dfa73ce606c187edcfe297dea2e95fcebc05a130c2961690d40939f709c"
|
||||
},
|
||||
"runs/run-1/01-launched-0.jpg": {
|
||||
"bytes": 100946,
|
||||
"sha256": "8ea5bfcab898c4a24da1e989f5d36fca13d2d1755f876bebb69a793fb0556914"
|
||||
},
|
||||
"runs/run-1/01-launched.json": {
|
||||
"bytes": 920,
|
||||
"sha256": "23f5831105098801141bfa95f580ebb1dc02a7590312881c9b2b00f0c3f8cff0"
|
||||
},
|
||||
"runs/run-1/02-maximized-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/02-maximized-1.jpg": {
|
||||
"bytes": 107512,
|
||||
"sha256": "d098e89b4d0c87b8068e3dfd09e7e68a16d2667544766ecbbfa3b8f36fe85bc3"
|
||||
},
|
||||
"runs/run-1/02-maximized.json": {
|
||||
"bytes": 1036,
|
||||
"sha256": "86828ffa6f2ebb2228d93e86612240bc8fbf5b51f858eb181b7c4b45e5bd3225"
|
||||
},
|
||||
"runs/run-1/03-open-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/03-open-1.jpg": {
|
||||
"bytes": 105042,
|
||||
"sha256": "83c4a8c80dfb8170bf5e577327f1c330e2b3f6c5d6847d757deed94201d33d96"
|
||||
},
|
||||
"runs/run-1/03-open.json": {
|
||||
"bytes": 1025,
|
||||
"sha256": "a83b4d548c43ad870565998296b1c4e7c1f427b3cd31399af3f14f6ebaff6292"
|
||||
},
|
||||
"runs/run-1/04-picker-0.jpg": {
|
||||
"bytes": 696,
|
||||
"sha256": "1c553708713a77fe378f38300c5e1888c4bb165058359b8b5739b84f7d1f97e7"
|
||||
},
|
||||
"runs/run-1/04-picker-1.jpg": {
|
||||
"bytes": 99912,
|
||||
"sha256": "8c4736e76a6ae14e95eb3cd407628e38c9d51ece6146bd7276c2242dd0d35c43"
|
||||
},
|
||||
"runs/run-1/04-picker.json": {
|
||||
"bytes": 10057,
|
||||
"sha256": "0e7279d256a64696fb26c4dd8aeebcca3506a41df7f756242b0545252b0f4e90"
|
||||
},
|
||||
"runs/run-1/05-filename-focus-0.jpg": {
|
||||
"bytes": 696,
|
||||
"sha256": "1c553708713a77fe378f38300c5e1888c4bb165058359b8b5739b84f7d1f97e7"
|
||||
},
|
||||
"runs/run-1/05-filename-focus-1.jpg": {
|
||||
"bytes": 99912,
|
||||
"sha256": "8c4736e76a6ae14e95eb3cd407628e38c9d51ece6146bd7276c2242dd0d35c43"
|
||||
},
|
||||
"runs/run-1/05-filename-focus.json": {
|
||||
"bytes": 10073,
|
||||
"sha256": "8337b147f68e3fbac37753d538022ecd837bcdd1336b0835ec48b750ddc18586"
|
||||
},
|
||||
"runs/run-1/06-fixture-path-0.jpg": {
|
||||
"bytes": 696,
|
||||
"sha256": "1c553708713a77fe378f38300c5e1888c4bb165058359b8b5739b84f7d1f97e7"
|
||||
},
|
||||
"runs/run-1/06-fixture-path-1.jpg": {
|
||||
"bytes": 103191,
|
||||
"sha256": "749b94ec86edf09f02c85a4cb4a5ddb2ff0c34d14da3a41c2601aa857aa50cd3"
|
||||
},
|
||||
"runs/run-1/06-fixture-path.json": {
|
||||
"bytes": 10069,
|
||||
"sha256": "6cda81ab24888d1547d191f808b47e8fe56ac5bb7959770e677bd22a8484fafc"
|
||||
},
|
||||
"runs/run-1/07-fixture-opened-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/07-fixture-opened-1.jpg": {
|
||||
"bytes": 94519,
|
||||
"sha256": "a6afad248434da8a7b5f6363026aa8ff19a2a35714dfe7a8a7f2daced2600538"
|
||||
},
|
||||
"runs/run-1/07-fixture-opened.json": {
|
||||
"bytes": 1045,
|
||||
"sha256": "ef42d1d9c5f25e4f6abeb167b12dddfba7205e5ab6b7227e4e6526b3a6865c97"
|
||||
},
|
||||
"runs/run-1/08-scenario-selected-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/08-scenario-selected-1.jpg": {
|
||||
"bytes": 103722,
|
||||
"sha256": "9f8e87f4ee4c1a32344ab686f6a4142a7343386c7b30518a60d4deb2806d70ba"
|
||||
},
|
||||
"runs/run-1/08-scenario-selected.json": {
|
||||
"bytes": 1111,
|
||||
"sha256": "be6371c6ba42ae7d9972b7ec2ba0d1c09fd06b5ee82f93a0d47b5cfab5268121"
|
||||
},
|
||||
"runs/run-1/09-behavior-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/09-behavior-1.jpg": {
|
||||
"bytes": 129743,
|
||||
"sha256": "f6c082cb1b45e2a9397c14fc1f398b2401085124caf8fe6b7e39d524cf14503e"
|
||||
},
|
||||
"runs/run-1/09-behavior.json": {
|
||||
"bytes": 1093,
|
||||
"sha256": "38828818830cb6308a6f56126b38649a60b886f685bb81639552fd2f3fdc62b0"
|
||||
},
|
||||
"runs/run-1/10-arrangements-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/10-arrangements-1.jpg": {
|
||||
"bytes": 130548,
|
||||
"sha256": "6bcb9af93e4f9ef8e21bb819584ce2d7add91e30f489ec34cb7c933393cc1053"
|
||||
},
|
||||
"runs/run-1/10-arrangements.json": {
|
||||
"bytes": 1101,
|
||||
"sha256": "5262b16ab4f4dd49178bddaeb9939760bc824eed827f7af39dbdf9ae881b1195"
|
||||
},
|
||||
"runs/run-1/11-saved-a-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/11-saved-a-1.jpg": {
|
||||
"bytes": 123159,
|
||||
"sha256": "264d8313b5c338ecddb6082fe11722f0aef1480f1f1e490d0f8a67850b4f9a02"
|
||||
},
|
||||
"runs/run-1/11-saved-a.json": {
|
||||
"bytes": 1091,
|
||||
"sha256": "c17382c3a3b45c1396e2f21bc9b6a86bbe31f29ae5008ba1f77f6ae0fd11ee39"
|
||||
},
|
||||
"runs/run-1/12-intake-selected-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/12-intake-selected-1.jpg": {
|
||||
"bytes": 125392,
|
||||
"sha256": "75cd15ea3dc7e33882b53a1f09eddff920c11d91c9582ed717d34769a5b6f510"
|
||||
},
|
||||
"runs/run-1/12-intake-selected.json": {
|
||||
"bytes": 1107,
|
||||
"sha256": "3bf0c88287a03388b771a07249ee6d537d14b6fc62f5e4a7e928a0d79e477af5"
|
||||
},
|
||||
"runs/run-1/13-message-context-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/13-message-context-1.jpg": {
|
||||
"bytes": 131963,
|
||||
"sha256": "8df5c7808a926c3110f22a6b576a677529b5869cfde147e535797a51e5828172"
|
||||
},
|
||||
"runs/run-1/13-message-context.json": {
|
||||
"bytes": 1107,
|
||||
"sha256": "7b40a9c685d2e8adbd9e951d7a3ead789a9ea193c198380112a24b7c3d747d7e"
|
||||
},
|
||||
"runs/run-1/14-two-selected-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/14-two-selected-1.jpg": {
|
||||
"bytes": 132626,
|
||||
"sha256": "ddd7070d5e5200bf6326ab5a53ab22dcd22fce6b802f37db766ecc89b97c4243"
|
||||
},
|
||||
"runs/run-1/14-two-selected.json": {
|
||||
"bytes": 1101,
|
||||
"sha256": "9dddac5f7292e6305d50d163a39adba0d848ac2623002d22918867d71c41d5a9"
|
||||
},
|
||||
"runs/run-1/15-canvas-keyboard-focus-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/15-canvas-keyboard-focus-1.jpg": {
|
||||
"bytes": 133822,
|
||||
"sha256": "e8613515eabeedf82872843e07d2bca291d6b4545764ac4bc9b604ab7c3abcbb"
|
||||
},
|
||||
"runs/run-1/15-canvas-keyboard-focus.json": {
|
||||
"bytes": 1119,
|
||||
"sha256": "df9b79dc82c7477dcdb08f335db66ab665b1290d8b8c2aeb75027775d30f6755"
|
||||
},
|
||||
"runs/run-1/16-delete-result-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/16-delete-result-1.jpg": {
|
||||
"bytes": 147740,
|
||||
"sha256": "af97251bf3601fae008bbb6ed5432b22a8c8ad774f9bda7d0dc8f28768493cf7"
|
||||
},
|
||||
"runs/run-1/16-delete-result.json": {
|
||||
"bytes": 1103,
|
||||
"sha256": "aa7abd533ebe076ce0b2bc0e2a15c8864507989542baf6f6eb5ae327586ef4f1"
|
||||
},
|
||||
"runs/run-1/17-second-delete-keeps-review-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/17-second-delete-keeps-review-1.jpg": {
|
||||
"bytes": 147740,
|
||||
"sha256": "af97251bf3601fae008bbb6ed5432b22a8c8ad774f9bda7d0dc8f28768493cf7"
|
||||
},
|
||||
"runs/run-1/17-second-delete-keeps-review.json": {
|
||||
"bytes": 1129,
|
||||
"sha256": "17b17072c0ce80433afd08083f8e71d636ed1afdcf55704e1780448842bbb464"
|
||||
},
|
||||
"runs/run-1/18-escape-retains-selection-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/18-escape-retains-selection-1.jpg": {
|
||||
"bytes": 132619,
|
||||
"sha256": "ed0e7a4bf6987bbb763af626a4fae3a5a4ad8f49ad5d4c0b71c281cd5b09aded"
|
||||
},
|
||||
"runs/run-1/18-escape-retains-selection.json": {
|
||||
"bytes": 1125,
|
||||
"sha256": "a53be9fce65fbb9363c464ac5ecb53f66fcb25fe38035cd54c03e1141c4dcdc1"
|
||||
},
|
||||
"runs/run-1/19-save-after-delete-cancel-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/19-save-after-delete-cancel-1.jpg": {
|
||||
"bytes": 130993,
|
||||
"sha256": "7bb2aceef0d0c06c3e13cdabae8d86b04db3ce8b08fd0e1c4adec1f6b0eb70d2"
|
||||
},
|
||||
"runs/run-1/19-save-after-delete-cancel.json": {
|
||||
"bytes": 1125,
|
||||
"sha256": "a26e873dfe3125daed16ecd0bf56ba9267c24991e1b8726c62666cb7f4f1e7fc"
|
||||
},
|
||||
"runs/run-1/20-focus-before-shift-delete-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/20-focus-before-shift-delete-1.jpg": {
|
||||
"bytes": 132197,
|
||||
"sha256": "24c3db0a96d42931638facbba83413cc686881c00f5939d36817778fc6b2a3d1"
|
||||
},
|
||||
"runs/run-1/20-focus-before-shift-delete.json": {
|
||||
"bytes": 1127,
|
||||
"sha256": "f77ea5325e33dab21f0bbd240f89ba2666d7821fa2be92823d3a45d409e38994"
|
||||
},
|
||||
"runs/run-1/21-shift-delete-review-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/21-shift-delete-review-1.jpg": {
|
||||
"bytes": 146260,
|
||||
"sha256": "a0c404c10300716c49daa92db0d8934a707b62f3f25a280473bfef2624a65fc4"
|
||||
},
|
||||
"runs/run-1/21-shift-delete-review.json": {
|
||||
"bytes": 1115,
|
||||
"sha256": "5aeaf4ccedbec8c676edcf0e494b14c5c01b27704bb8805703f4d1cba75a3ad3"
|
||||
},
|
||||
"runs/run-1/22-enter-keeps-review-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/22-enter-keeps-review-1.jpg": {
|
||||
"bytes": 146260,
|
||||
"sha256": "a0c404c10300716c49daa92db0d8934a707b62f3f25a280473bfef2624a65fc4"
|
||||
},
|
||||
"runs/run-1/22-enter-keeps-review.json": {
|
||||
"bytes": 1113,
|
||||
"sha256": "b8c3256277d0eda8614af1e2a3c6ceb321063aa6f72f9830248751de542a4d34"
|
||||
},
|
||||
"runs/run-1/23-shift-delete-cancel-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/23-shift-delete-cancel-1.jpg": {
|
||||
"bytes": 130993,
|
||||
"sha256": "7bb2aceef0d0c06c3e13cdabae8d86b04db3ce8b08fd0e1c4adec1f6b0eb70d2"
|
||||
},
|
||||
"runs/run-1/23-shift-delete-cancel.json": {
|
||||
"bytes": 1115,
|
||||
"sha256": "03b399c923c93e6bd65607ff67e18f9a72d35d6d5514ad3a73c08ad7b52ee1d4"
|
||||
},
|
||||
"runs/run-1/24-save-after-shift-delete-cancel-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"runs/run-1/24-save-after-shift-delete-cancel-1.jpg": {
|
||||
"bytes": 130993,
|
||||
"sha256": "7bb2aceef0d0c06c3e13cdabae8d86b04db3ce8b08fd0e1c4adec1f6b0eb70d2"
|
||||
},
|
||||
"runs/run-1/24-save-after-shift-delete-cancel.json": {
|
||||
"bytes": 1137,
|
||||
"sha256": "b1176f6a61b511b926dc12ba99855e5cb079efdb0748d67d1a81c68ddf41bbdd"
|
||||
},
|
||||
"runs/run-1/app.stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"runs/run-1/app.stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"runs/run-1/baseline.arcadia": {
|
||||
"bytes": 4389,
|
||||
"sha256": "70c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a"
|
||||
},
|
||||
"runs/run-1/cwd/%SystemDrive%/ProgramData/Microsoft/Windows/Caches/cversions.2.db": {
|
||||
"bytes": 16384,
|
||||
"sha256": "de800ec458c404e412158787a7bc6660fe54a2bc3b6903b31d07a661325056f0"
|
||||
},
|
||||
"runs/run-1/cwd/%SystemDrive%/ProgramData/Microsoft/Windows/Caches/{6AF0698E-D558-4F6E-9B3C-3716689AF493}.2.ver0x0000000000000001.db": {
|
||||
"bytes": 307760,
|
||||
"sha256": "8c39c0f94044df6360f160a936cdebdd957dacf8da660cca02951181775ed226"
|
||||
},
|
||||
"runs/run-1/cwd/%SystemDrive%/ProgramData/Microsoft/Windows/Caches/{DDF571F2-BE98-426D-8288-1A9A39C3FDA2}.2.ver0x0000000000000001.db": {
|
||||
"bytes": 660376,
|
||||
"sha256": "2b67a249b6048fe0d0ccf1d8623de840b17bc8579c75fcb8f2689f6c19115c31"
|
||||
},
|
||||
"runs/run-1/evidence/baseline.arcadia": {
|
||||
"bytes": 4389,
|
||||
"sha256": "70c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a"
|
||||
},
|
||||
"runs/run-1/evidence/capture.js": {
|
||||
"bytes": 1597,
|
||||
"sha256": "9035c150f9843bc0a973c77255c1a0ad229bc5e579ef044757f634a16836eff0"
|
||||
},
|
||||
"runs/run-1/evidence/expected-removed.arcadia": {
|
||||
"bytes": 3856,
|
||||
"sha256": "1cb04cbab8428fc672293f29783fffdeecaf6ec6d81d5439501c32e866c8264f"
|
||||
},
|
||||
"runs/run-1/evidence/fixture-manifest.json": {
|
||||
"bytes": 882,
|
||||
"sha256": "2c7b8996b0bcfadfce727e013c4520ecf41e6b6d3ba1e874057a16df2a4a43b0"
|
||||
},
|
||||
"runs/run-1/evidence/inputs.json": {
|
||||
"bytes": 138269,
|
||||
"sha256": "839b041088be0638c55cdcc4da18d6cf9b69eecee3c00d48734759166ed06a0f"
|
||||
},
|
||||
"runs/run-1/evidence/native-harness.py": {
|
||||
"bytes": 13292,
|
||||
"sha256": "9844080f8878237116b3b93973979269a8b3d5f0c1400eb449fb7d0ca9587647"
|
||||
},
|
||||
"runs/run-1/evidence/result.json": {
|
||||
"bytes": 4454,
|
||||
"sha256": "539b7536236430b955e2657dd1e5b9ff905c029e5d1d10c2ac194d7d7847464a"
|
||||
},
|
||||
"runs/run-1/evidence/source-proof.json": {
|
||||
"bytes": 598,
|
||||
"sha256": "b548459a93f6510422eb8dd8542c9eadfc92604c137bf73b3ffaca4c8d9edffe"
|
||||
},
|
||||
"runs/run-1/evidence/verify-captured.py": {
|
||||
"bytes": 2019,
|
||||
"sha256": "32078fe720f00f6c123daa3d7d0cbbd4d296211d3610eb33dec4f74c5be2f573"
|
||||
},
|
||||
"runs/run-1/finished.json": {
|
||||
"bytes": 452,
|
||||
"sha256": "b7436b5031700f64892940a398b7c9dab7e6a33f0bc4499b098c6d3fffb9aa00"
|
||||
},
|
||||
"runs/run-1/prepared.json": {
|
||||
"bytes": 3792,
|
||||
"sha256": "eec91b06f86810a0d5a850985b0d1a214f7c647807dcb63d5baf6bcff173ddef"
|
||||
},
|
||||
"runs/run-1/snapshots/01-delete-cancel.arcadia": {
|
||||
"bytes": 4389,
|
||||
"sha256": "70c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a"
|
||||
},
|
||||
"runs/run-1/snapshots/01-delete-cancel.evaluator.stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"runs/run-1/snapshots/01-delete-cancel.evaluator.stdout.json": {
|
||||
"bytes": 359,
|
||||
"sha256": "0c2ad922d64f654e062b17890920fbbe43bc46d9323f52fc442fcba8ab56e037"
|
||||
},
|
||||
"runs/run-1/snapshots/01-delete-cancel.json": {
|
||||
"bytes": 802,
|
||||
"sha256": "c49bbaf6ffd99b3b26e97ac3ccdc8434af7d269ba58f0e351598f03e1c903dbb"
|
||||
},
|
||||
"runs/run-1/snapshots/02-shift-delete-cancel.arcadia": {
|
||||
"bytes": 4389,
|
||||
"sha256": "70c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a"
|
||||
},
|
||||
"runs/run-1/snapshots/02-shift-delete-cancel.evaluator.stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"runs/run-1/snapshots/02-shift-delete-cancel.evaluator.stdout.json": {
|
||||
"bytes": 365,
|
||||
"sha256": "8ca35401f43eb27df14a1d0f9276806651e46d1540cb53032b6eab0956fd5fc5"
|
||||
},
|
||||
"runs/run-1/snapshots/02-shift-delete-cancel.json": {
|
||||
"bytes": 808,
|
||||
"sha256": "c3486f9d863d0870141696b17aa6c668b551cd633e0db9aed8c17ec6643dd96b"
|
||||
},
|
||||
"runs/run-1/started.json": {
|
||||
"bytes": 767,
|
||||
"sha256": "9106e41173554f31d87aa324f19e2c4845d68dca1169827f0f013cfdf754965d"
|
||||
},
|
||||
"runs/run-1/working.arcadia": {
|
||||
"bytes": 4389,
|
||||
"sha256": "70c32355b82393c03ef87b24862863e0a06c4dcb00cf2c0a84a7cfd2da687a8a"
|
||||
}
|
||||
},
|
||||
"excluded_binary_bytes": {
|
||||
"preflight/fixture-preflight-1/bin/arcadia.exe": {
|
||||
"bytes": 20719104,
|
||||
"sha256": "2044a6057b565069bcfe457e644ed3d527f3607ca9c0f8313d036d188d73796c"
|
||||
},
|
||||
"preflight/fixture-preflight-1/bin/libunwind.dll": {
|
||||
"bytes": 190976,
|
||||
"sha256": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4"
|
||||
},
|
||||
"runs/run-1/bin/arcadia-studio.exe": {
|
||||
"bytes": 120067072,
|
||||
"sha256": "e118e7f0a2ff53ac9c22a73d07555929d11e1ad917d430a7ae88d469aaa91155"
|
||||
},
|
||||
"runs/run-1/bin/libunwind.dll": {
|
||||
"bytes": 190976,
|
||||
"sha256": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4"
|
||||
}
|
||||
},
|
||||
"runs": [
|
||||
{
|
||||
"name": "run-1",
|
||||
"accepted": true,
|
||||
"source_phases": {
|
||||
"working.arcadia": "baseline"
|
||||
}
|
||||
}
|
||||
],
|
||||
"preflights": [
|
||||
{
|
||||
"name": "fixture-preflight-1",
|
||||
"accepted": true
|
||||
}
|
||||
],
|
||||
"limits": [
|
||||
"Executable/DLL bytes omitted; original readback hashes remain. Offline verification cannot rehash omitted binaries.",
|
||||
"accepted is an explicit reviewer designation, never inferred from screenshots or a passing checker.",
|
||||
"All local run/preflight attempts must be declared and closed, including failures.",
|
||||
"No GUI usability, direct history-depth introspection, remote publication or issue completion claim."
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,282 @@
|
||||
"""Offline archive/source/receipt checks. Never executes archived helpers or native binaries."""
|
||||
from pathlib import Path, PurePosixPath, PureWindowsPath
|
||||
import argparse
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import zipfile
|
||||
|
||||
KIND = 'scenario-aggregate-removal-native-evidence'
|
||||
PHASES = {'baseline': 'baseline.arcadia', 'removed': 'expected-removed.arcadia'}
|
||||
FINAL_SOURCE = '43340b94a13f192c3b5d3c3112dbe87246790a44'
|
||||
|
||||
|
||||
def pin(data):
|
||||
return {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def safe_name(name):
|
||||
p = PurePosixPath(name)
|
||||
assert name and not p.is_absolute() and str(p) == name, 'Unsafe member: ' + name
|
||||
assert '\\' not in name and ':' not in name and not any(x in ('.', '..', '') for x in name.split('/')), 'Unsafe member: ' + name
|
||||
|
||||
|
||||
def check_oracles(files, fixture, prefix='fixtures/'):
|
||||
assert fixture['fixture_revision'] == 'removal-v1-source-derived'
|
||||
assert fixture['schema'] == 7 and fixture['phases'] == PHASES
|
||||
assert fixture['selection'] == fixture['expected_removed'] == ['intake_lane', 'a']
|
||||
assert fixture['expected_changed'] == ['review_flow', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'saved_a', 'saved_b']
|
||||
assert fixture['preserved_engineering_definitions'] == ['intake', 'review', 'archive']
|
||||
assert set(fixture['files']) == set(PHASES.values()), 'Wrong fixture inventory'
|
||||
for name, expected in fixture['files'].items():
|
||||
assert pin(files[prefix + name]) == expected, 'Fixture pin mismatch: ' + name
|
||||
baseline = files[prefix + 'baseline.arcadia'].decode('utf-8')
|
||||
assert baseline.count('steps [@a,@b,@c,@d,@e,@f,@g,@h];') == 1
|
||||
assert baseline.count('oa_occurrence ') == 8 and baseline.count('oa_lifeline ') == 3
|
||||
assert baseline.count('diagram saved_') == 2 and baseline.count('oa_activity ') == 3
|
||||
for alias in fixture['preserved_engineering_definitions']:
|
||||
assert baseline.count('oa_activity ' + alias + ' ') == 1
|
||||
# The known fixture has one complete declaration per line. No parser or
|
||||
# archived preparation helper is executed; only its explicit intended delta
|
||||
# is admitted, including the source writer's retained token whitespace.
|
||||
def trivia(text):
|
||||
return ''.join(token for token in re.findall(r'"(?:\\.|[^"\\])*"|\s+|[^\s"]+', text) if token.isspace())
|
||||
expected = baseline.replace('steps [@a,@b', 'steps [@b')
|
||||
for marker in ('oa_lifeline intake_lane ', 'oa_occurrence a '):
|
||||
rows = [line for line in baseline.splitlines() if marker in line]
|
||||
assert len(rows) == 1
|
||||
declaration = rows[0]
|
||||
assert expected.count(declaration) == 1
|
||||
expected = expected.replace(declaration, '\n\n' + trivia(declaration) + '\n\n')
|
||||
assert expected.count('sender @intake_lane;') == 7
|
||||
expected = expected.replace('sender @intake_lane;', 'sender none;')
|
||||
for offset in (0, 20):
|
||||
for field, item in (
|
||||
('lanes', f'{{lifeline @intake_lane;x {140 + offset};}}'),
|
||||
('messages', f'{{address{{references[];occurrence @a;}};send_y {160 + offset};}}'),
|
||||
):
|
||||
rows = [line for line in expected.splitlines() if f'{field} [{item},' in line]
|
||||
assert len(rows) == 1
|
||||
old = rows[0]
|
||||
new = old.replace(item + ',', '', 1)
|
||||
new = new[:-2] + trivia(item) + '];'
|
||||
expected = expected.replace(old, new, 1)
|
||||
assert files[prefix + 'expected-removed.arcadia'] == expected.encode('utf-8'), 'Oracle changes more than the specified removal/reference/layout delta'
|
||||
|
||||
|
||||
def check_source(files, actual, phase, prefix='fixtures/'):
|
||||
assert phase in PHASES, 'Unknown source phase'
|
||||
assert actual == files[prefix + PHASES[phase]], 'Exact full source differs from ' + phase
|
||||
|
||||
|
||||
def verify(root):
|
||||
manifest = json.loads((root / 'manifest.json').read_text(encoding='utf-8'))
|
||||
assert manifest['kind'] == KIND, 'Wrong package kind'
|
||||
assert pin((root / 'native-evidence.zip').read_bytes()) == manifest['archive'], 'Archive pin mismatch'
|
||||
with zipfile.ZipFile(root / 'native-evidence.zip') as archive:
|
||||
names = archive.namelist()
|
||||
assert len(names) == len(set(names)) and set(names) == set(manifest['members']), 'Archive member inventory mismatch'
|
||||
for name in names:
|
||||
safe_name(name)
|
||||
assert ((archive.getinfo(name).external_attr >> 16) & 0o170000) != 0o120000, 'Symlink member'
|
||||
files = {name: archive.read(name) for name in names}
|
||||
for name, data in files.items():
|
||||
assert pin(data) == manifest['members'][name], 'Member pin mismatch: ' + name
|
||||
assert PurePosixPath(name).suffix.lower() not in ('.exe', '.dll'), 'Unexpected archived binary'
|
||||
for name in manifest['excluded_binary_bytes']:
|
||||
safe_name(name)
|
||||
assert name not in files and PurePosixPath(name).suffix.lower() in ('.exe', '.dll'), 'Invalid omitted binary entry'
|
||||
assert (root / 'verify.py').read_bytes() == files['helpers/native-evidence-verify.py'], 'External verifier differs from pinned helper'
|
||||
|
||||
def js(name):
|
||||
return json.loads(files[name].decode('utf-8-sig'))
|
||||
|
||||
def present_pin(name):
|
||||
return pin(files[name]) if name in files else manifest['excluded_binary_bytes'].get(name)
|
||||
|
||||
def build_binding(prefix, source):
|
||||
receipt = js(prefix + 'result.json')
|
||||
proof = js(prefix + 'source-proof.json')
|
||||
inputs = js(prefix + 'inputs.json')
|
||||
assert receipt['exitCode'] == 0 and receipt['inputsUnchanged'] is True, 'Build not successful/unchanged'
|
||||
assert proof['status'].startswith('PASS') and proof['result'] == pin(files[prefix + 'result.json']), 'Build proof result binding'
|
||||
assert proof['inputMap'] == receipt['inputs'] == pin(files[prefix + 'inputs.json']), 'Build input-map binding'
|
||||
assert proof['inputCount'] == receipt['inputCount'] == len(inputs), 'Build input count'
|
||||
assert proof['commit'] == receipt['head'] == source and re.fullmatch('[0-9a-f]{40}', source), 'Build source identity'
|
||||
return receipt
|
||||
|
||||
fixture = js('fixtures/fixture-manifest.json')
|
||||
check_oracles(files, fixture)
|
||||
for name in files:
|
||||
if name.startswith('oracle-revisions/') and name.endswith('/fixture-manifest.json'):
|
||||
check_oracles(files, js(name), name.removesuffix('fixture-manifest.json'))
|
||||
config = js('review-config.json')
|
||||
assert config['runs'] == manifest['runs'] and config['preflights'] == manifest['preflights']
|
||||
for entries, prefix, pattern in [(config['runs'], 'runs', r'run-[0-9]+'), (config['preflights'], 'preflight', r'fixture-preflight-[0-9]+')]:
|
||||
assert entries and len({x['name'] for x in entries}) == len(entries)
|
||||
assert all(re.fullmatch(pattern, x['name']) and type(x['accepted']) is bool for x in entries)
|
||||
assert {name.split('/')[1] for name in files if name.startswith(prefix + '/')} == {x['name'] for x in entries}, 'Undeclared archived attempts'
|
||||
|
||||
reports, preflights = [], []
|
||||
accepted_cli_bindings = set()
|
||||
for entry in config['preflights']:
|
||||
prefix = 'preflight/' + entry['name'] + '/'
|
||||
problems = []
|
||||
try:
|
||||
current = js(prefix + 'result.json')
|
||||
started = js(prefix + 'started.json')
|
||||
fixture_name = prefix + 'evidence/fixture-manifest.json'
|
||||
current_fixture = js(fixture_name)
|
||||
check_oracles(files, current_fixture, prefix + 'fixtures/')
|
||||
revision = current_fixture.get('fixture_revision', 'removal-v1-source-derived')
|
||||
if 'fixture_revision' in current:
|
||||
assert current['fixture_revision'] == revision and current['fixture_manifest'] == pin(files[fixture_name]), 'Preflight fixture revision binding'
|
||||
assert current['fixture_comparator'] == pin(files[prefix + 'evidence/verify-captured.py']), 'Preflight comparator pin'
|
||||
assert started['checks'] == [] and all(current[key] == started[key] for key in started if key != 'checks'), 'Preflight started/result changed identity'
|
||||
assert current['status'].startswith('PASS') and current['cliUnchanged'] is True, 'Preflight not passed'
|
||||
receipt = build_binding(prefix + 'evidence/', current['source'])
|
||||
assert current['receipt'] == pin(files[prefix + 'evidence/result.json']) and current['proof'] == pin(files[prefix + 'evidence/source-proof.json']), 'Preflight proof pins'
|
||||
cli = [a for a in receipt['artifacts'] if a['cargo']['target']['name'] == 'arcadia' and not a['cargo']['profile']['test']]
|
||||
assert len(cli) == 1 and cli[0]['readback'] == current['cli'] == present_pin(prefix + 'bin/arcadia.exe'), 'CLI artifact binding'
|
||||
assert len(current['checks']) == len(PHASES) and {x['fixture'] for x in current['checks']} == set(PHASES.values()), 'Incomplete fixture preflight'
|
||||
for check in current['checks']:
|
||||
name = check['fixture']
|
||||
assert check['exitCode'] == 0 and check['json_report'] is True and check['fixtureUnchanged'] is True, 'Fixture check failed: ' + name
|
||||
assert check['stdout'] == pin(files[prefix + name + '.stdout.json']) and check['stderr'] == pin(files[prefix + name + '.stderr.log']), 'CLI output pins'
|
||||
assert pin(files[prefix + 'fixtures/' + name]) == current_fixture['files'][name], 'Preflight fixture differs'
|
||||
raw = js(prefix + name + '.stdout.json')
|
||||
assert raw['operation'] == 'check' and raw['exit_code'] == 0 and raw['written'] is False and not any(x['severity'] == 'error' for x in raw['diagnostics']), 'Raw CLI report rejects fixture'
|
||||
for runtime in current['runtime']:
|
||||
assert present_pin(prefix + 'bin/' + PureWindowsPath(runtime['source']).name) == {k: runtime[k] for k in ('bytes', 'sha256')}, 'CLI runtime pin'
|
||||
except (AssertionError, KeyError, ValueError, TypeError) as error:
|
||||
problems.append(str(error))
|
||||
preflights.append({'preflight': entry['name'], 'reviewer_accepted': entry['accepted'], 'checks_pass': not problems, 'problems': problems})
|
||||
if entry['accepted']:
|
||||
assert current['source'] == FINAL_SOURCE, 'Accepted preflight source differs'
|
||||
assert not problems, json.dumps(preflights[-1])
|
||||
accepted_cli_bindings.add((current['source'], pin(files[fixture_name])['sha256']))
|
||||
|
||||
for entry in config['runs']:
|
||||
prefix = 'runs/' + entry['name'] + '/'
|
||||
problems, captures, sources = [], 0, []
|
||||
# Report the actual discovered count even when an earlier historical
|
||||
# source failure prevents the remaining per-run checks from passing.
|
||||
for name in files:
|
||||
if name.startswith(prefix) and name.endswith('.json'):
|
||||
try:
|
||||
record = js(name)
|
||||
captures += int(isinstance(record, dict) and 'images' in record)
|
||||
except (ValueError, UnicodeError):
|
||||
pass
|
||||
try:
|
||||
prepared, started, finished = (js(prefix + name + '.json') for name in ('prepared', 'started', 'finished'))
|
||||
fixture_name = prefix + 'evidence/fixture-manifest.json'
|
||||
run_fixture = js(fixture_name)
|
||||
check_oracles(files, run_fixture, prefix + 'evidence/')
|
||||
revision = run_fixture.get('fixture_revision', 'removal-v1-source-derived')
|
||||
if 'fixture_revision' in prepared:
|
||||
assert prepared['fixture_revision'] == revision and prepared['fixture_manifest'] == pin(files[fixture_name]), 'Run fixture revision binding'
|
||||
assert started['prepared'] == pin(files[prefix + 'prepared.json']), 'Started/prepared binding'
|
||||
for rel, expected in prepared['immutable'].items():
|
||||
safe_name(rel)
|
||||
assert present_pin(prefix + rel) == expected, 'Immutable pin: ' + rel
|
||||
assert prepared['helper'] == pin(files[prefix + 'evidence/native-harness.py']), 'Native helper binding'
|
||||
for helper in ('capture.js', 'verify-captured.py'):
|
||||
rel = 'evidence/' + helper
|
||||
assert prepared['immutable'].get(rel) == pin(files[prefix + rel]), 'Captured observation/source helper is not immutable: ' + helper
|
||||
for name in [*run_fixture['files'], 'fixture-manifest.json']:
|
||||
assert prepared['immutable'].get('evidence/' + name) == pin(files[prefix + 'evidence/' + name]), 'Run oracle is not immutable: ' + name
|
||||
receipt = build_binding(prefix + 'evidence/', prepared['source'])
|
||||
gui = [a for a in receipt['artifacts'] if a['cargo']['target']['name'] == 'arcadia-studio' and not a['cargo']['profile']['test']]
|
||||
assert len(gui) == 1 and gui[0]['readback'] == prepared['artifact'] == present_pin(prefix + 'bin/arcadia-studio.exe'), 'GUI artifact binding'
|
||||
for runtime in prepared['runtime_sources']:
|
||||
assert present_pin(prefix + 'bin/' + PureWindowsPath(runtime['path']).name) == {k: runtime[k] for k in ('bytes', 'sha256')}, 'GUI runtime pin'
|
||||
assert started['command'] == prepared['command'] and started['cwd'] == prepared['cwd'], 'Launch command/cwd changed'
|
||||
identity = started['identity']
|
||||
assert identity['pid'] == started['pid'] and identity['created_filetime'] > 0 and identity['running'] is True, 'Missing initial live process identity'
|
||||
assert PureWindowsPath(identity['image']) == PureWindowsPath(prepared['command'][0]), 'Wrong process image'
|
||||
assert finished['exitCode'] == 0 and finished['immutableUnchanged'] is True, 'Normal exit/immutability not established'
|
||||
for stream in ('stdout', 'stderr'):
|
||||
assert finished[stream] == pin(files[prefix + 'app.' + stream + '.log']), 'Final log binding'
|
||||
assert finished['working'] == pin(files[prefix + 'working.arcadia']), 'Final source binding'
|
||||
assert prepared.get('initial_pin', started['workingBefore']) == started['workingBefore'], 'Initial source pin changed'
|
||||
origin = entry.get('reopen_from')
|
||||
if prefix + 'reopen-origin.json' in files:
|
||||
old = js(prefix + 'reopen-origin.json')
|
||||
declared = {'run': old['from_run'], 'file': old['from_file']}
|
||||
assert origin is None or origin == declared, 'Contradictory reopen origins'
|
||||
origin = declared
|
||||
assert old['from_pin'] == old['copied_pin'] == started['workingBefore'], 'Reopen-origin pins disagree'
|
||||
if origin:
|
||||
assert origin['run'] != entry['name'] and origin['run'] in {x['name'] for x in config['runs']}, 'Unknown/self reopen origin'
|
||||
safe_name(origin['file'])
|
||||
assert pin(files['runs/' + origin['run'] + '/' + origin['file']]) == started['workingBefore'], 'Reopen source is not the exact captured copy'
|
||||
else:
|
||||
assert started['workingBefore'] == run_fixture['files']['baseline.arcadia'], 'Nonbaseline start needs explicit reopen_from'
|
||||
phases = entry.get('source_phases', {})
|
||||
actual_sources = [name for name in files if name.startswith(prefix) and name.endswith('.arcadia') and '/evidence/' not in name and name != prefix + 'baseline.arcadia']
|
||||
assert set(phases) <= {name[len(prefix):] for name in actual_sources}, 'Phase config references missing source'
|
||||
for name in actual_sources:
|
||||
rel = name[len(prefix):]
|
||||
sidecar = name[:-8] + '.json'
|
||||
phase = phases.get(rel)
|
||||
if sidecar in files and 'phase' in js(sidecar):
|
||||
saved = js(sidecar)
|
||||
assert phase is None or phase == saved['phase'], 'Contradictory snapshot phase'
|
||||
phase = saved['phase']
|
||||
assert saved['pin'] == pin(files[name]) and saved['exitCode'] == 0, 'Snapshot check/pin failed'
|
||||
evaluated = js(name[:-8] + '.evaluator.stdout.json')
|
||||
assert evaluated['status'] == 'PASS' and evaluated['phase'] == phase and evaluated['sha256'] == pin(files[name])['sha256'], 'Raw source evaluator report mismatch'
|
||||
assert not files[name[:-8] + '.evaluator.stderr.log'], 'Source evaluator stderr is nonempty'
|
||||
check_source(files, files[name], phase, prefix + 'evidence/')
|
||||
sources.append({'file': rel, 'phase': phase})
|
||||
start_time = datetime.datetime.fromisoformat(started['started'])
|
||||
finish_time = datetime.datetime.fromisoformat(finished['finished'])
|
||||
assert start_time <= finish_time, 'Reversed process lifetime'
|
||||
for name in files:
|
||||
if not name.startswith(prefix) or not name.endswith('.json'):
|
||||
continue
|
||||
record = js(name)
|
||||
if not isinstance(record, dict) or 'images' not in record:
|
||||
continue
|
||||
assert record['images'], 'Empty capture record'
|
||||
assert record['window']['app'].casefold() == ('process:' + identity['image']).casefold(), 'Capture uses a different executable surface'
|
||||
observed = datetime.datetime.fromisoformat(record['time'])
|
||||
assert start_time <= observed <= finish_time, 'Capture is outside process lifetime'
|
||||
for image in record['images']:
|
||||
safe_name(image['file'])
|
||||
assert prefix + image['file'] in files and image['width'] > 0 and image['height'] > 0, 'Missing/invalid capture image'
|
||||
assert captures > 0, 'No captured native observations'
|
||||
if entry['accepted']:
|
||||
assert prepared['source'] == FINAL_SOURCE, 'Accepted native source differs'
|
||||
assert (prepared['source'], pin(files[fixture_name])['sha256']) in accepted_cli_bindings, 'No accepted current-source/current-oracle CLI fixture preflight'
|
||||
except (AssertionError, KeyError, ValueError, TypeError) as error:
|
||||
problems.append(str(error))
|
||||
reports.append({'run': entry['name'], 'reviewer_accepted': entry['accepted'], 'checks_pass': not problems, 'problems': problems, 'capture_records': captures, 'source_checks': sources})
|
||||
if entry['accepted']:
|
||||
assert not problems, json.dumps(reports[-1], indent=2)
|
||||
return {
|
||||
'status': 'PASS offline archive, exact source and receipt consistency checks',
|
||||
'runs': reports,
|
||||
'preflights': preflights,
|
||||
'accepted_run_count': sum(entry['accepted'] for entry in config['runs']),
|
||||
'limitations': [
|
||||
'Human visual, keyboard, selection and interaction claims are not inferred from screenshots.',
|
||||
'Omitted executable/DLL bytes cannot be rehashed offline.',
|
||||
'Build proof/input map consistency checked; Git blobs are not independently available offline.',
|
||||
'accepted=false failures are retained and reported, never silently promoted.',
|
||||
'No native history-depth introspection, usability acceptance, release or publication assertion.',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('directory', type=Path)
|
||||
args = parser.parse_args()
|
||||
print(json.dumps(verify(args.directory.resolve()), indent=2))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,16 @@
|
||||
# Independent Scenario removal source-failure test review
|
||||
|
||||
Reviewer `/root/keyboard_evidence_review`, 2026-09-25 UTC. Reviewed commit `2aaea8a3a0b9522548b4418d9609f75d90c24345`, its complete 230-line test-only diff, and surrounding App/Document/source-budget/camera implementations. Test source in the checkout matches that commit. Author was a different agent. No Cargo, UI, Gitea or product execution performed by this reviewer; only this review file was written.
|
||||
|
||||
**No blocking finding.** The fixture reaches a real Document source-update refusal through the App confirmation path rather than simulating an error or mutating private Document limits.
|
||||
|
||||
- The original project contains two short-alias local participants and 256 surviving asynchronous messages. Removing both participants clears both endpoints of every message, so replacing short references with explicit `none` can outweigh the removed declarations. The test validates the original and independently built expected candidate. Crucially it first executes the same command without the restrictive budget, asserts the complete expected project, and measures that the final edited bundle is larger than the original. The failure therefore does not depend solely on a speculative intermediate writer expansion.
|
||||
- `Document::from_bundle` is the public budget-bearing constructor. The test sets `ProjectLimits.model.max_source_bytes` to the complete original bundle's byte length; initial compilation succeeds. Bounded execution must return an error containing diagnostic code `source_limit`. This distinguishes source-budget refusal from model invalidity or stale review. App status must equal the actual bounded Document error prefixed with `Nothing removed:`. Production `Document::execute` prepares and source-updates before committing, and App's error branch only changes status after retiring the modal.
|
||||
- Four cases exercise derived/saved representations with and without a genuine available Redo. The earlier rename is executed and undone under the same budget; the future snapshot is captured before removal. Thus Redo preservation is tested with a real history entry, not only a boolean.
|
||||
- After valid review confirmation the test checks the identical compiled snapshot Arc, full model and bundle equality, unchanged revision, clean saved state, no new Undo, retained Redo availability, exact selection targets/primary/generation, stable document/revision/representation/visit context, camera boundary, navigation/owner, editor text, source workspace bundle, and clean drafts. Modal dismissal and canvas input retirement are explicit. Replaying the same confirm token must preserve snapshot, selection, status and epoch. Where present, actual App Redo restores the earlier future model/source exactly and Undo returns to the clean baseline.
|
||||
|
||||
## Boundaries
|
||||
|
||||
This is an in-memory lossless source-update budget failure, not a disk-write/permission/I/O failure. It exercises two selected participant removals; mixed message/participant and other failure causes remain separate evidence. Camera equality covers the established viewport and current camera tuple (tool, shelf, viewport, pan, zoom, fit), but does not separately seed unusual pan/zoom. History starts with no Undo entries and at most one Redo entry; this is strong evidence for transaction refusal and preservation of that future, not arbitrary history-depth coverage. The document snapshot equality is complete, while selected surrounding App state is tested rather than every private UI field. None of these are blockers for the stated bounded claim.
|
||||
|
||||
Runtime follow-up: independently read the now-closed parent-run gate `removal-app-source-failure-1` (no independent execution). `result.json` reports exit 0, inputsUnchanged true and exact commit `2aaea8a3a0b9522548b4418d9609f75d90c24345`; `source-proof.json` reports PASS exact captured inventory/Git blob bytes for all 823 inputs. Independently rehashed result, input map and both raw streams against their recorded pins: all match. Raw stdout names this test and records 1 passed, 0 failed, 0 ignored, 1,630 filtered out, 7.10 seconds; that single test contains all four App cases. Raw stderr contains ordinary compilation/run messages. Environment is Windows GNU/LLVM Rust/Cargo 1.98.1. This closes the targeted runtime gap for the bounded source-budget refusal claim. No full-suite, native failure interaction, release, or integration acceptance is claimed.
|
||||
Reference in New Issue
Block a user