Record Process removal Windows and native verification

This commit is contained in:
2026-09-24 20:56:06 -07:00
parent 572e96ba03
commit 7323a80e94
19 changed files with 3467 additions and 0 deletions
+3
View File
@@ -95,3 +95,6 @@ harness/experiments/analysis-source-s2a-windows-20260913/** -text whitespace=-bl
# Preserve the pinned Iced dependency bytes and upstream manifest.
crates/vendor/iced_widget/** -text whitespace=cr-at-eol
# Preserve exact Process removal review and portable evidence bytes.
harness/process-group-removal-evidence-20260925/** -text whitespace=cr-at-eol
@@ -0,0 +1,91 @@
# Process aggregate removal — App implementation and verification, 2026-09-25 UTC
Local-only contribution for issue 29. Source `572e96ba03522cfb822b13fe4abb50e857a5de48` builds and runs on Windows. Process/FunctionalChain aggregate removal has source review, filtered regression checks and a native OA model/source/history/reopen receipt. Compact-window acceptance remains open. No integration, release or remote publication is claimed.
## Behavior and reviewed corrections
The [removal policy](process-group-removal-policy-20260925.md) defines this scope. Process/FunctionalChain removal captures the complete typed group of listed local contributions and maintained links, admits every original member, and evaluates one Batch. Occurrence cascade coverage suppresses redundant selected-link commands without hiding those selections from review. Qualified previews cause whole-set refusal; local ContextualLinks use explicit owner even with qualified endpoints. Actual before/after candidates determine removed identities and changed surviving records, including embedded placements. Referenced definitions, other uses and opposite-layer counterparts are preserved under existing core cascade rules.
Review separately reports stored impact, local listed order, expanded indexed order, contextual-analysis changes and surviving caller definitions. Caller impact includes ordinary explicit-to-explicit topology changes, and warns that reused entry/exit connectivity and diagnostics may change without creating stored identities. It does not claim complete expanded graph analysis or count repeated reference instances as separate caller definitions.
Source review found and corrected:
- P1: interpretation-only caller closure missed changed explicit topology. `affected_uses` now derives from actual changed definition topology, independently of interpretation deltas; literal OA/SA direct, repeated and transitive caller discriminators were added.
- P2: empty local-order wording falsely denied surviving unlisted contributions. Review now says “Empty order” / “One item in this order,” retaining incomplete-flow qualifications and separate dimensions when they differ.
- A same-input-batch queued Delete could precede a later private gesture while App admission lacked that evidence. Synchronous shared input generation and a gesture lease now revoke stale captures; mounted production-tree and lease lifecycle discriminators were added. The bounded source review found the specific race addressed, without claiming every viewport gesture is represented.
- Independent planner review prompted literal tests for covered plus uncovered selected links and global attachment/trace/placement/diagram bookkeeping. Final source review at `fe637dcd277b17653139ffa234e601b29775c565` found no new actionable production finding in its bounded scope. Its later menu-correction addendum likewise found no actionable issue. These reviews did not execute runtime/native gates.
Exact historical review notes: [policy critique](process-group-removal-evidence-20260925/policy-critique.md), [planner](process-group-removal-evidence-20260925/planner-review.md), [adapter](process-group-removal-evidence-20260925/adapter-review.md), and [final review/addendum](process-group-removal-evidence-20260925/final-review.md). Their original source/runtime boundaries remain intact; source-only conclusions are not retroactively represented as executed tests.
## Closed failed gate history
Both runs used Windows `stable-x86_64-pc-windows-gnullvm`, Cargo/rustc 1.98.1, one build job, incremental compilation disabled, and:
```text
cargo test -p oa-desktop --message-format=json -- process_ topology scenario_removal removal --test-threads=1
```
The identified local closed receipts for `process-removal-tests-1` and `process-removal-tests-2` were inspected with their exact source proofs and logs. Raw evidence publication is pending; these local files are not a peer-accessible handoff. Both closed with exit 101 and unchanged captured inputs. Each separate source proof passed exact captured inventory/Git-blob comparison; that proof does not change the failed test outcome. Neither command is the full suite (1469 tests filtered out).
1. **Gate 1**, source `fe637dcd277b17653139ffa234e601b29775c565`, finished 03:12:51 UTC: **185 passed, 2 failed**. The existing qualified-definition navigation regression exposed a real menu omission. Correction `f20497b29bf637619bc35ffe41274fc0f66c387b` restores qualified navigation before the aggregate menu early return, preserving exact endpoint/context and whole-group removal. The other failure was the newly authored v1 lexical oracle: complete semantic Project equality passed, then exact Document source equality failed. Independent source-policy investigation derived retained declaration trivia (22/20/16 spaces and existing annotation LF boundaries) and unformatted surviving `[@e1,@e2]`. V2 was derived from the checksum-pinned baseline before separate comparison with observed output; it matched 2258 bytes, SHA256 `b4a2b07ad4c796f555c61945ca42b1c221b454b35488c60fefc86f96f7aa71ba`. V1 and failed evidence remain preserved. This was an incorrect lexical assumption, not evidence of a semantic deletion defect or a new writer regression. See the [exact source-oracle investigation](process-group-removal-evidence-20260925/source-oracle-investigation.md).
2. **Gate 2**, source `f20497b29bf637619bc35ffe41274fc0f66c387b`, finished 03:20:16 UTC: **186 passed, 1 failed**. Qualified-navigation regression passed. In the literal test, complete Project and exact Document/v2 source assertions passed; the later `app.source_workspace.bundle()` equality failed. This is distinct from v1: the fixture helper replaced Document after `open_project` but retained its generated split workspace. `sync` preserves discovered files; it is not a new-document initializer. Commit `d93c0a801249f361d95721fb0fc239a95159af5c` initializes `Workspace::new(app.document.bundle(), true)` before sync and asserts baseline workspace equality. No production writer change or weakened source equality was needed. Undo/Redo assertions later in this failing test were not reached.
## Closed correction and final-source gates
All gates below closed with unchanged captured inputs and a separate PASS proving exact input inventory/Git-blob bytes. The result/input-map hashes were checked against those proofs. A source-proof PASS means source binding; it does not turn a failed gate into a pass.
| Gate | Identified source | Result |
| --- | --- | --- |
| tests-3 | d93c0a801249f361d95721fb0fc239a95159af5c | 187 passed, 0 failed; 1469 filtered out |
| clippy-1 | d93c0a801249f361d95721fb0fc239a95159af5c | Exit 101: two style lints under warnings-as-errors |
| tests-4 | 572e96ba03522cfb822b13fe4abb50e857a5de48 | 27 passed, 0 failed; 1629 filtered out |
| format-3 | 572e96ba03522cfb822b13fe4abb50e857a5de48 | PASS, exit 0 |
| clippy-2 | 572e96ba03522cfb822b13fe4abb50e857a5de48 | PASS, exit 0 |
| build-1 | 572e96ba03522cfb822b13fe4abb50e857a5de48 | Studio and CLI PASS, exit 0 |
Gate names have the prefix `process-removal-`. Tests-3 used the broad filtered test command above and passed the literal model/source/one-Undo/Redo case after the fixture initialization correction. Clippy-1 identified manual parity and manual saturating arithmetic in the input lifetime helper. Commit `572e96ba03522cfb822b13fe4abb50e857a5de48` replaces `generation % 2 == 0` with `generation.is_multiple_of(2)`, and `checked_add(2).unwrap_or(u64::MAX)` with `saturating_add(2)`; these retain the same unsigned-integer and terminal-overflow semantics. Tests-4 reran the affected Process removal, input generation and gesture coverage on that final source. The broad 187-test result belongs to d93c0a80, not a rerun of all 187 on 572e96ba.
Final-source commands:
```text
cargo test -p oa-desktop --message-format=json -- process_removal removal_input group_gesture --test-threads=1
cargo fmt --all --check
cargo clippy -p oa-desktop --all-targets -- -D warnings
cargo build -p oa-desktop -p oa-cli --message-format=json
```
Runtime results are filtered Windows evidence, not a full-suite or Linux claim. Both passing test runs include the literal aggregate removal model/source history oracle and mounted queued-Delete/camera-boundary discriminator.
Receipt SHA-256 anchors (result.json; raw package publication pending):
| Gate | SHA-256 |
| --- | --- |
| tests-3 | 82709fef9b1aabdf2fcc07a7cb9300d16474bc62370d7157e6bb6270cd8264f8 |
| tests-4 | 8513d24b9f05645908f4a475fcc90b5887362473f8046282bfd8af6a24c61dd5 |
| format-3 | 7ec891fa7a881b4f04fc3f58047a15f3972b397b7d2595e4a0bb04a9f658d9a3 |
| clippy-1 | 35e1afdd586853dbd992b3b944045e479297bf6dc2bf33db5186be0a51c3e41f |
| clippy-2 | 12f3352607a1608fcd1242b48336ae877b81677df38d96d17bbe9dc6ed3ff49b |
## Native verification and portable records
The closed build has 832 unchanged captured inputs and an exact Git-blob source proof. Its copied Studio binary and explicitly pinned libunwind.dll launch with only Windows system directories on PATH. Current CLI preflight accepts both oracle-v2 files with warning-only model diagnostics. Run 1 removes Action @a, Control @c (both named Review gate) and their selected authored link @approved. The review distinguishes them by alias and exposes all ten effects through scrolling and two pages: three selected removals, one updated owner, before/after implicit-order consequences, the two remaining exchange positions and direct/transitive caller definitions. Search finds @outer; Enter does not confirm; empty search supplies recovery guidance. Cancel and Escape retain the full group and link primary. Header, context menu over an unselected object, focused Delete and focused Shift+Delete all open the complete review.
After restoring/reactivating the window, the captured review is expired and Remove is disabled; that native sequence does not isolate resizing from focus changes. Explicit confirmation of a fresh review leaves two exchange cards and their implicit connection. Save produces exactly 2258 oracle bytes; one Undo and Save restores all 2609 baseline bytes; one Redo and Save reproduces all 2258 removed bytes. Run 2 starts a fresh process from the saved Redo file, displays the same two exchanges/implicit connection and saves identical bytes. Both processes exit normally with empty logs and unchanged executable, runtime and fixture pins. These exact bytes preserve underlying work/exchanges, caller definitions/references and SA sentinels.
The [native action record](process-group-removal-evidence-20260925/native-actions.md) identifies capture labels, actual keys, source snapshots and automation failures. Captures are observations, not an instrumented input log. Membership used context Add, not native Shift-click. After Escape, Shift+Delete required F6 to restore canvas focus. A restored-window capture initially failed as minimized and recovered through fresh window selection/reactivation; a later out-of-bounds positioning drag was rejected before capture. These are recorded separately from application behavior. True compact 1100px layout is unverified; the restored view was partly clipped by the desktop. Maximum-window review reachability is established at 1605x872.
Portable [Cargo records and offline verifier](process-group-removal-evidence-20260925/checks/README.md) preserve all ten attempts, including three failures, with four distinct source input capsules. [Native archive](process-group-removal-evidence-20260925/native/native-evidence.zip) and [standalone verifier](process-group-removal-evidence-20260925/native/verify.py) preserve original v1, separately derived v2, 53 raw capture records, exact saved snapshots and closed process receipts. Both standalone verifiers pass. Native ZIP: 5,617,713 bytes, SHA256 `f77fd3abe7ecc5838395b94a95ba935c8dc5c10a36c8e3a2c1ec4a1da1521b84`, 327 members, six omitted executable/DLL payloads represented by pins. These evidence archives are not runnable releases.
Cargo ZIP: 13,162,535 bytes, SHA256 `13aeacd8b1326ec5d57d49a13e8fd0c2e8611f85da1244cbcb1362edcb6fd4d5`. The [independent native review](process-group-removal-evidence-20260925/native-review.md) found no blocking issue within the bounded flow, recomputed both runs' immutable file pins and checked all saved-source results. It preserves the distinction between parent-reported actions and captured states, and leaves compact layout and accessibility unaccepted. Exact source comparison and archive integrity do not substitute for this visual review. Recheck records offline with Python 3.11+:
```text
python -B harness/process-group-removal-evidence-20260925/checks/verify.py
python -B harness/process-group-removal-evidence-20260925/native/verify.py harness/process-group-removal-evidence-20260925/native
```
## Remaining scope and publication
This native receipt covers OA derived layout and the bounded fixture. SA/qualified paths, cascades, stale input races and command-failure preservation have separate filtered-test evidence. Actual Process source-budget failure, native Shift-click, compact layout, comprehensive accessibility, full-suite and Linux acceptance are unclaimed. Preserve these limits when integrating; no issue closure is justified by this branch alone.
Fresh guarded push was rejected with `remote unpack failed: unable to create temporary object directory`; a Gitea MCP issue-comment write also failed. Read operations work, main AGENTS remains blob e76647f49913b314e1543c4464f6199533909bae, and the remote contribution branch remains 43340b94. WSL DNS still timed out after ten seconds; Linux builds remain explicitly user-paused. These are observed failed attempts, not a newly verified disk-full root cause. Pending Scenario follow-up and this Process contribution remain local-only. Reconcile and publish the buffered issue-29 handoff when writes recover; do not overwrite remote history. Historical local paths inside copied reviews identify their original context, not peer-accessible deliverables.
@@ -0,0 +1,112 @@
# Independent Process removal adapter source review
Read-only inspection of uncommitted production adapter surfaces on base
`c62622cce71e06f0beae49bc3b4fe8e890eba951`. Inspected `app/process_removal.rs`,
its `view.rs`, and the diffs/context in `app.rs`, `app/diagram_removal.rs`,
`app/escape.rs`, `model_menu.rs`, `views.rs`, `behavior/topology/view.rs` and
`behavior/topology/direct_selection.rs`. Adapter tests were still being authored;
no Cargo/UI execution and no production edits. This is not native acceptance.
## P2: local empty-order wording can deny surviving unlisted contributions
At inspection, `matching_rows` rendered `Some([])` as “No remaining
contributions; no ordered pair” for both order dimensions; `Some([E])` rendered
“One contribution; no ordered pair.” This is false when describing local listed
order in the supported owned-but-unlisted case.
Literal discriminator: owner P lists Action A and owns unlisted Action U. Remove
A. U survives; local order changes from explicit to `Some([])`, expanded order
stays explicit. The review must not tell the user that no contributions remain.
Use “No listed contributions” for the local dimension, “No indexed
contributions” for the expanded dimension, or dimension-neutral “Empty order.”
Similarly qualify “One contribution.” This is a wording defect introduced by
the new review, not a request to repair preexisting desktop/core semantics.
Reported to parent for the adapter owner.
## P2 candidate: same input-batch newer gesture is invisible to App admission
`process_removal_enabled` checks modal/draft/tool state but has no Process
gesture token. `direct_selection::selection_input` correctly captures Delete
when `Input.group` already exists, and blocks ordinary press/source gestures.
However starting a group press sets only private canvas `Input.group` and
publishes a redraw, leaving the App context and selection generation unchanged.
A source-supported event ordering to test, instead of arbitrary packet replay:
1. Production tree has local A/B selected and a cursor over selected A.
2. The same UI event batch receives Delete, then LeftPressed over A.
3. Delete publishes a captured removal Begin. LeftPressed starts private
`Input.group` with the same context/snapshot and publishes redraw only.
4. App consumes Begin after the UI batch; admission has no evidence of that
newer gesture and opens review, increments epoch and retires the gesture.
Source evidence: `direct_selection.rs:209–229` publishes Begin;
`:316–334` creates private GroupPress without an App gesture message;
`app/process_removal.rs:51–92` admits using App state alone.
`app/escape.rs` explicitly documents Iced's batching behavior, and existing
production `Mounted.batch` topology tests exercise multiple input events before
App messages are applied. This is a concrete plausible production-event path,
but this reviewer did not execute it. Parent was asked to run the literal batch
discriminator before treating it as a reproduced defect.
This can violate the policy's “active gestures refuse admission” promise, but
does not silently delete: an explicit current review confirmation is still
required. It therefore does not justify P1/data-loss severity from current
evidence. The successful admission represents an earlier Delete followed by a
later gesture in one batch; the project should either test/enforce the promised
lifetime ordering or state a narrower supported guarantee. Do not claim the App
proves absence of every private canvas gesture.
## Verified source properties
- Whole-group admission happens before shared source/canvas boundaries.
`process_removal_matches` validates exact topology context, selection
generation/membership/primary and actual graph target visibility. The planner
subsequently refuses mixed local/qualified content as a whole.
- Header and focused Delete/Shift+Delete capture complete snapshots. Keyboard
repeat and unsupported modifiers do not issue deletion. Active GroupPress
captures keyboard input before Delete can reach removal.
- Process context-menu requests retain selection. Menu invocation accepts the
new group message before ordinary clicked-object `select`, so a menu opened
over an unselected object does not silently replace the group. Menu validity
now also captures the Process selection snapshot.
- Legacy `Message::Delete` and singleton TopologyCanvas Remove packets are
refused while a group exists before shared state is disturbed. Qualified
singleton previews remain read-only. A direct arbitrary Command injection is
not an established production removal entry point and was not presented as a
user-reachable bypass.
- Query/Page/Cancel packets are token scoped; ConfirmDelete is checked before
forwarding. Unstamped CancelDelete/Escape cannot close a later Process review.
Root Escape emits the captured current review's token before focused search
receives it. Search has no Enter-submit confirmation handler.
- Confirmation checks current document/revision/owner/epoch, exact selection,
actual visibility, primary inspector identity, pending source/property/
behavior drafts, source rename and Select tool. It executes one captured
Document command. Failed source preparation remains the Document layer's
responsibility; this source review does not independently establish its
runtime preservation behavior.
- Initial enabled gates include hosted/workflow/file/pending/menu/usages/
reference-input states via `keyboard_owner_enabled`. Existing modal dispatch
prevents ordinary new modal actions while review is open. Confirm's local
guard does not re-enumerate every other modal, but no concrete production path
establishing an intervening competing modal was found. Do not promote a
manually mutated App test alone into a claimed user-reachable failure.
- Review distinguishes actual removed/updated records, local/expanded order,
contextual-analysis changes and caller definitions. Affected-caller rows merge
the prior P1 correction with interpretation caller closure. Repeated reference
instances are not falsely counted as separate caller rows. The explanatory
text acknowledges possible reused entry/exit changes and disclaims complete
expanded graph analysis.
- All impact rows are searchable/pageable with full names and aliases. Long
text wraps. Footer is separate from the scrolling body. These structural
choices support usability but do not establish native layout/focus behavior.
## Remaining acceptance work
Resolve or narrow the two items above. Run App and actual production-tree tests
for stale/ABA snapshots, menu selection preservation, wrong/old tokens,
source-write failure, exact cancel/Undo/Redo and keyboard focus. Inspect compact
native modal layout, every impact category and search/page access, including a
long implicit order and multiple caller definitions. Validate save/reopen on the
identified tested source. No additional P1 production defect was found in this
bounded review.
@@ -0,0 +1,27 @@
# Process aggregate removal: captured checks
Python 3.11+ offline packaging/verification for explicitly named closed
`process-removal-*` runs under `CHECKOUT/.local/scenario-group-gates`.
These tools reuse the Scenario removal verifier unchanged except for the
manifest kind and run-name prefix. They do not run Cargo, Git, network requests
or archived code. Input capsules are deduplicated by hash; every original
logical path and failed attempt remains in the manifest.
```text
python -B prepare.py --root CHECKOUT --runs CLOSED_RUN_NAMES --output NEW_DIRECTORY
python -B NEW_DIRECTORY/verify.py
```
Supply the complete explicit attempt inventory, including failures. Existing
output directories, incomplete runs, changed-input runs and missing source
proofs are refused. Verification checks exact outer/raw/nested ZIP inventories
and hashes, safe paths, command/process/time consistency, dynamic input counts,
unchanged captures, executable Cargo-record relationships, source-proof bindings
and pinned comparator provenance. All 71 tracked patched-widget files must be
present. Failed exits remain failures even when archive verification passes.
An offline proof check does not execute the comparator or read Git objects.
The parent separately compares all captured inputs to the named Git commit.
Executables are omitted; only their pins and Cargo metadata are retained.
Neither package integrity nor compilation establishes native usability,
integration, release acceptance or completion of the application.
@@ -0,0 +1,22 @@
{
"prepare.py": {
"bytes": 6319,
"sha256": "3d82c4784109d41feb969c94e406358e42c19d08e56952b04df768fef2a3d3f0"
},
"verify.py": {
"bytes": 11528,
"sha256": "d9fd0f14d5a034acf82830b0bf66577be72e9002693f5b7397b3fefdf921a047"
},
"README.md": {
"bytes": 1517,
"sha256": "d1d3087d55d54ad9afa8d451166ddbae61c846396c8d81381c0f27a5f39e6d3c"
},
"manifest.json": {
"bytes": 28845,
"sha256": "91455965138d6dfa19d8359da6114ed7ece995390d8026b1a944cd473058c34c"
},
"evidence.zip": {
"bytes": 13162535,
"sha256": "13aeacd8b1326ec5d57d49a13e8fd0c2e8611f85da1244cbcb1362edcb6fd4d5"
}
}
@@ -0,0 +1,621 @@
{
"schema": 1,
"kind": "process-aggregate-removal-cargo-gates",
"runs": {
"process-removal-tests-1": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--message-format=json",
"--",
"process_",
"topology",
"scenario_removal",
"removal",
"--test-threads=1"
],
"head": "fe637dcd277b17653139ffa234e601b29775c565",
"exitCode": 101,
"inputCount": 831,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "fe637dcd277b17653139ffa234e601b29775c565",
"pin": {
"bytes": 602,
"sha256": "4074db859ff385c14941ba8e45530a7e748e0db45101339d881937663c0ef512"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-tests-2": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--message-format=json",
"--",
"process_",
"topology",
"scenario_removal",
"removal",
"--test-threads=1"
],
"head": "f20497b29bf637619bc35ffe41274fc0f66c387b",
"exitCode": 101,
"inputCount": 832,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "f20497b29bf637619bc35ffe41274fc0f66c387b",
"pin": {
"bytes": 602,
"sha256": "4ab508f91efc34fde8cc1fc99fcc174d22f9eaf141730f608582b0067007eb56"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-tests-3": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--message-format=json",
"--",
"process_",
"topology",
"scenario_removal",
"removal",
"--test-threads=1"
],
"head": "d93c0a801249f361d95721fb0fc239a95159af5c",
"exitCode": 0,
"inputCount": 832,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "d93c0a801249f361d95721fb0fc239a95159af5c",
"pin": {
"bytes": 602,
"sha256": "0407b22338c2d7bf725c8fe7098515a484cf67067578b4bf4992fa0a49bd3f93"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-tests-4": {
"command": [
"cargo",
"test",
"-p",
"oa-desktop",
"--message-format=json",
"--",
"process_removal",
"removal_input",
"group_gesture",
"--test-threads=1"
],
"head": "572e96ba03522cfb822b13fe4abb50e857a5de48",
"exitCode": 0,
"inputCount": 832,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "572e96ba03522cfb822b13fe4abb50e857a5de48",
"pin": {
"bytes": 602,
"sha256": "ff045bf753ec4e09abc980018dd90f38338889dcd0877200364f5460418df5c6"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-format-1": {
"command": [
"cargo",
"fmt",
"--all",
"--",
"--check"
],
"head": "fe637dcd277b17653139ffa234e601b29775c565",
"exitCode": 0,
"inputCount": 831,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "fe637dcd277b17653139ffa234e601b29775c565",
"pin": {
"bytes": 603,
"sha256": "d36d50d20b92c49c0867d1468ed9d8062e23aa8cda9a995ad8bd91938e337335"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-format-2": {
"command": [
"cargo",
"fmt",
"--all",
"--",
"--check"
],
"head": "f20497b29bf637619bc35ffe41274fc0f66c387b",
"exitCode": 0,
"inputCount": 832,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "f20497b29bf637619bc35ffe41274fc0f66c387b",
"pin": {
"bytes": 603,
"sha256": "6f6a756569f3877223932253db0cea5380d646dd221f467c69b3572289d9a2fc"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-format-3": {
"command": [
"cargo",
"fmt",
"--all",
"--check"
],
"head": "572e96ba03522cfb822b13fe4abb50e857a5de48",
"exitCode": 0,
"inputCount": 832,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "572e96ba03522cfb822b13fe4abb50e857a5de48",
"pin": {
"bytes": 603,
"sha256": "05d86dd797bf3c1a0d9c8b0c00a49294009ead98cb9e9c3e258d0cb59e9331b1"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-clippy-1": {
"command": [
"cargo",
"clippy",
"-p",
"oa-desktop",
"--all-targets",
"--",
"-D",
"warnings"
],
"head": "d93c0a801249f361d95721fb0fc239a95159af5c",
"exitCode": 101,
"inputCount": 832,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "d93c0a801249f361d95721fb0fc239a95159af5c",
"pin": {
"bytes": 603,
"sha256": "a758a8df8dbc2aa86b907856b69ca74f8219a9b462a83d690f06929ba3cc1797"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-clippy-2": {
"command": [
"cargo",
"clippy",
"-p",
"oa-desktop",
"--all-targets",
"--",
"-D",
"warnings"
],
"head": "572e96ba03522cfb822b13fe4abb50e857a5de48",
"exitCode": 0,
"inputCount": 832,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "572e96ba03522cfb822b13fe4abb50e857a5de48",
"pin": {
"bytes": 603,
"sha256": "d27fd807235a480aa0fe76f3ceefddbdc1756cc1782727829be99f86b00d7550"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
},
"process-removal-build-1": {
"command": [
"cargo",
"build",
"-p",
"oa-desktop",
"-p",
"oa-cli",
"--message-format=json"
],
"head": "572e96ba03522cfb822b13fe4abb50e857a5de48",
"exitCode": 0,
"inputCount": 832,
"helper": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"sourceProof": {
"commit": "572e96ba03522cfb822b13fe4abb50e857a5de48",
"pin": {
"bytes": 602,
"sha256": "3c40d37ad31dc3f23e2c3f57fa0ab780f8b7891f491f9f5382948a625e8ecf84"
},
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
}
}
},
"captureHelpers": {
"gate.py": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"source-proof.py": "helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py"
},
"logicalFiles": {
"cargo/process-removal-build-1/inputs.json": "capsules/df764fd132235169050a761757a2761dcc36cdf08fb812edbfd3e18af50a5dfb.json",
"cargo/process-removal-build-1/inputs.zip": "capsules/dec4a591276d0920e0b7940c9785a2fb9bf92af71a4c206ef3d692db7798f45b.zip",
"cargo/process-removal-build-1/process.json": "cargo/process-removal-build-1/process.json",
"cargo/process-removal-build-1/result.json": "cargo/process-removal-build-1/result.json",
"cargo/process-removal-build-1/source-proof.json": "cargo/process-removal-build-1/source-proof.json",
"cargo/process-removal-build-1/started.json": "cargo/process-removal-build-1/started.json",
"cargo/process-removal-build-1/stderr.log": "cargo/process-removal-build-1/stderr.log",
"cargo/process-removal-build-1/stdout.log": "cargo/process-removal-build-1/stdout.log",
"cargo/process-removal-clippy-1/inputs.json": "capsules/f199e1a7a58f7d9093a2cb9da288120c35bd87c650bb971ecad3c9d964d1c76a.json",
"cargo/process-removal-clippy-1/inputs.zip": "capsules/b2504689cfe1a461949673ba5ed67323571aa70ee7965230f980c20cafd317ba.zip",
"cargo/process-removal-clippy-1/process.json": "cargo/process-removal-clippy-1/process.json",
"cargo/process-removal-clippy-1/result.json": "cargo/process-removal-clippy-1/result.json",
"cargo/process-removal-clippy-1/source-proof.json": "cargo/process-removal-clippy-1/source-proof.json",
"cargo/process-removal-clippy-1/started.json": "cargo/process-removal-clippy-1/started.json",
"cargo/process-removal-clippy-1/stderr.log": "cargo/process-removal-clippy-1/stderr.log",
"cargo/process-removal-clippy-1/stdout.log": "cargo/process-removal-clippy-1/stdout.log",
"cargo/process-removal-clippy-2/inputs.json": "capsules/df764fd132235169050a761757a2761dcc36cdf08fb812edbfd3e18af50a5dfb.json",
"cargo/process-removal-clippy-2/inputs.zip": "capsules/dec4a591276d0920e0b7940c9785a2fb9bf92af71a4c206ef3d692db7798f45b.zip",
"cargo/process-removal-clippy-2/process.json": "cargo/process-removal-clippy-2/process.json",
"cargo/process-removal-clippy-2/result.json": "cargo/process-removal-clippy-2/result.json",
"cargo/process-removal-clippy-2/source-proof.json": "cargo/process-removal-clippy-2/source-proof.json",
"cargo/process-removal-clippy-2/started.json": "cargo/process-removal-clippy-2/started.json",
"cargo/process-removal-clippy-2/stderr.log": "cargo/process-removal-clippy-2/stderr.log",
"cargo/process-removal-clippy-2/stdout.log": "cargo/process-removal-clippy-2/stdout.log",
"cargo/process-removal-format-1/inputs.json": "capsules/b09d4d557af5882c4d23c546394da9f1ff7aa0d6ac3de6f509e923d96c48f929.json",
"cargo/process-removal-format-1/inputs.zip": "capsules/6e49c576b61f844341d6e84e4da44eea24045f6064467a9cf060646289a706ea.zip",
"cargo/process-removal-format-1/process.json": "cargo/process-removal-format-1/process.json",
"cargo/process-removal-format-1/result.json": "cargo/process-removal-format-1/result.json",
"cargo/process-removal-format-1/source-proof.json": "cargo/process-removal-format-1/source-proof.json",
"cargo/process-removal-format-1/started.json": "cargo/process-removal-format-1/started.json",
"cargo/process-removal-format-1/stderr.log": "cargo/process-removal-format-1/stderr.log",
"cargo/process-removal-format-1/stdout.log": "cargo/process-removal-format-1/stdout.log",
"cargo/process-removal-format-2/inputs.json": "capsules/46a4532cb0bccafe9260df88d02781b29bf979c3277a45cad261852a2cfaf998.json",
"cargo/process-removal-format-2/inputs.zip": "capsules/34d335f010637739cbb763f1436f4c640a2c472051c30e2a5a6beb39a2ed7e78.zip",
"cargo/process-removal-format-2/process.json": "cargo/process-removal-format-2/process.json",
"cargo/process-removal-format-2/result.json": "cargo/process-removal-format-2/result.json",
"cargo/process-removal-format-2/source-proof.json": "cargo/process-removal-format-2/source-proof.json",
"cargo/process-removal-format-2/started.json": "cargo/process-removal-format-2/started.json",
"cargo/process-removal-format-2/stderr.log": "cargo/process-removal-format-2/stderr.log",
"cargo/process-removal-format-2/stdout.log": "cargo/process-removal-format-2/stdout.log",
"cargo/process-removal-format-3/inputs.json": "capsules/df764fd132235169050a761757a2761dcc36cdf08fb812edbfd3e18af50a5dfb.json",
"cargo/process-removal-format-3/inputs.zip": "capsules/dec4a591276d0920e0b7940c9785a2fb9bf92af71a4c206ef3d692db7798f45b.zip",
"cargo/process-removal-format-3/process.json": "cargo/process-removal-format-3/process.json",
"cargo/process-removal-format-3/result.json": "cargo/process-removal-format-3/result.json",
"cargo/process-removal-format-3/source-proof.json": "cargo/process-removal-format-3/source-proof.json",
"cargo/process-removal-format-3/started.json": "cargo/process-removal-format-3/started.json",
"cargo/process-removal-format-3/stderr.log": "cargo/process-removal-format-3/stderr.log",
"cargo/process-removal-format-3/stdout.log": "cargo/process-removal-format-3/stdout.log",
"cargo/process-removal-tests-1/inputs.json": "capsules/b09d4d557af5882c4d23c546394da9f1ff7aa0d6ac3de6f509e923d96c48f929.json",
"cargo/process-removal-tests-1/inputs.zip": "capsules/6e49c576b61f844341d6e84e4da44eea24045f6064467a9cf060646289a706ea.zip",
"cargo/process-removal-tests-1/process.json": "cargo/process-removal-tests-1/process.json",
"cargo/process-removal-tests-1/result.json": "cargo/process-removal-tests-1/result.json",
"cargo/process-removal-tests-1/source-proof.json": "cargo/process-removal-tests-1/source-proof.json",
"cargo/process-removal-tests-1/started.json": "cargo/process-removal-tests-1/started.json",
"cargo/process-removal-tests-1/stderr.log": "cargo/process-removal-tests-1/stderr.log",
"cargo/process-removal-tests-1/stdout.log": "cargo/process-removal-tests-1/stdout.log",
"cargo/process-removal-tests-2/inputs.json": "capsules/46a4532cb0bccafe9260df88d02781b29bf979c3277a45cad261852a2cfaf998.json",
"cargo/process-removal-tests-2/inputs.zip": "capsules/34d335f010637739cbb763f1436f4c640a2c472051c30e2a5a6beb39a2ed7e78.zip",
"cargo/process-removal-tests-2/process.json": "cargo/process-removal-tests-2/process.json",
"cargo/process-removal-tests-2/result.json": "cargo/process-removal-tests-2/result.json",
"cargo/process-removal-tests-2/source-proof.json": "cargo/process-removal-tests-2/source-proof.json",
"cargo/process-removal-tests-2/started.json": "cargo/process-removal-tests-2/started.json",
"cargo/process-removal-tests-2/stderr.log": "cargo/process-removal-tests-2/stderr.log",
"cargo/process-removal-tests-2/stdout.log": "cargo/process-removal-tests-2/stdout.log",
"cargo/process-removal-tests-3/inputs.json": "capsules/f199e1a7a58f7d9093a2cb9da288120c35bd87c650bb971ecad3c9d964d1c76a.json",
"cargo/process-removal-tests-3/inputs.zip": "capsules/b2504689cfe1a461949673ba5ed67323571aa70ee7965230f980c20cafd317ba.zip",
"cargo/process-removal-tests-3/process.json": "cargo/process-removal-tests-3/process.json",
"cargo/process-removal-tests-3/result.json": "cargo/process-removal-tests-3/result.json",
"cargo/process-removal-tests-3/source-proof.json": "cargo/process-removal-tests-3/source-proof.json",
"cargo/process-removal-tests-3/started.json": "cargo/process-removal-tests-3/started.json",
"cargo/process-removal-tests-3/stderr.log": "cargo/process-removal-tests-3/stderr.log",
"cargo/process-removal-tests-3/stdout.log": "cargo/process-removal-tests-3/stdout.log",
"cargo/process-removal-tests-4/inputs.json": "capsules/df764fd132235169050a761757a2761dcc36cdf08fb812edbfd3e18af50a5dfb.json",
"cargo/process-removal-tests-4/inputs.zip": "capsules/dec4a591276d0920e0b7940c9785a2fb9bf92af71a4c206ef3d692db7798f45b.zip",
"cargo/process-removal-tests-4/process.json": "cargo/process-removal-tests-4/process.json",
"cargo/process-removal-tests-4/result.json": "cargo/process-removal-tests-4/result.json",
"cargo/process-removal-tests-4/source-proof.json": "cargo/process-removal-tests-4/source-proof.json",
"cargo/process-removal-tests-4/started.json": "cargo/process-removal-tests-4/started.json",
"cargo/process-removal-tests-4/stderr.log": "cargo/process-removal-tests-4/stderr.log",
"cargo/process-removal-tests-4/stdout.log": "cargo/process-removal-tests-4/stdout.log",
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py",
"helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py": "helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py",
"helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py": "helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py"
},
"members": {
"capsules/34d335f010637739cbb763f1436f4c640a2c472051c30e2a5a6beb39a2ed7e78.zip": {
"bytes": 3317469,
"sha256": "34d335f010637739cbb763f1436f4c640a2c472051c30e2a5a6beb39a2ed7e78"
},
"capsules/46a4532cb0bccafe9260df88d02781b29bf979c3277a45cad261852a2cfaf998.json": {
"bytes": 139818,
"sha256": "46a4532cb0bccafe9260df88d02781b29bf979c3277a45cad261852a2cfaf998"
},
"capsules/6e49c576b61f844341d6e84e4da44eea24045f6064467a9cf060646289a706ea.zip": {
"bytes": 3316503,
"sha256": "6e49c576b61f844341d6e84e4da44eea24045f6064467a9cf060646289a706ea"
},
"capsules/b09d4d557af5882c4d23c546394da9f1ff7aa0d6ac3de6f509e923d96c48f929.json": {
"bytes": 139641,
"sha256": "b09d4d557af5882c4d23c546394da9f1ff7aa0d6ac3de6f509e923d96c48f929"
},
"capsules/b2504689cfe1a461949673ba5ed67323571aa70ee7965230f980c20cafd317ba.zip": {
"bytes": 3317568,
"sha256": "b2504689cfe1a461949673ba5ed67323571aa70ee7965230f980c20cafd317ba"
},
"capsules/dec4a591276d0920e0b7940c9785a2fb9bf92af71a4c206ef3d692db7798f45b.zip": {
"bytes": 3317564,
"sha256": "dec4a591276d0920e0b7940c9785a2fb9bf92af71a4c206ef3d692db7798f45b"
},
"capsules/df764fd132235169050a761757a2761dcc36cdf08fb812edbfd3e18af50a5dfb.json": {
"bytes": 139818,
"sha256": "df764fd132235169050a761757a2761dcc36cdf08fb812edbfd3e18af50a5dfb"
},
"capsules/f199e1a7a58f7d9093a2cb9da288120c35bd87c650bb971ecad3c9d964d1c76a.json": {
"bytes": 139818,
"sha256": "f199e1a7a58f7d9093a2cb9da288120c35bd87c650bb971ecad3c9d964d1c76a"
},
"cargo/process-removal-build-1/process.json": {
"bytes": 153,
"sha256": "dc004e0235427ba30d729f43829567c953fc8813805493d88aa8026735f38855"
},
"cargo/process-removal-build-1/result.json": {
"bytes": 4454,
"sha256": "5699e510ec2541de2b2f484da17fd99685182baf80a0046e5ad3f651d54749db"
},
"cargo/process-removal-build-1/source-proof.json": {
"bytes": 602,
"sha256": "3c40d37ad31dc3f23e2c3f57fa0ab780f8b7891f491f9f5382948a625e8ecf84"
},
"cargo/process-removal-build-1/started.json": {
"bytes": 1527,
"sha256": "4db0bbcf6a8bc0123b383f2652490b32398c8aaa67c01040fa8d0ac04db2b531"
},
"cargo/process-removal-build-1/stderr.log": {
"bytes": 134,
"sha256": "653b17b1fe092ae2a63bd8de1567a002926ba1e5490fc7cc6e2def5bc194f8a5"
},
"cargo/process-removal-build-1/stdout.log": {
"bytes": 285562,
"sha256": "02fd17e99b96025280cab7ce7cfe19bfcdc406530bbaa31d4277744b7f8ee219"
},
"cargo/process-removal-clippy-1/process.json": {
"bytes": 154,
"sha256": "e41df8b0608174388c261d28f7c3ee3cb20137ec33f5f2caa1ce6df9917a24ba"
},
"cargo/process-removal-clippy-1/result.json": {
"bytes": 1927,
"sha256": "35e1afdd586853dbd992b3b944045e479297bf6dc2bf33db5186be0a51c3e41f"
},
"cargo/process-removal-clippy-1/source-proof.json": {
"bytes": 603,
"sha256": "a758a8df8dbc2aa86b907856b69ca74f8219a9b462a83d690f06929ba3cc1797"
},
"cargo/process-removal-clippy-1/started.json": {
"bytes": 1533,
"sha256": "af25bb05f73bb02e64ab8f367aa33bcfefce11d23cafa4f2a0f0f82181a60c2b"
},
"cargo/process-removal-clippy-1/stderr.log": {
"bytes": 1354,
"sha256": "ab0b2ce019708e42eb13591a678c0c53a03805326568f07bf7a31849993031b7"
},
"cargo/process-removal-clippy-1/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/process-removal-clippy-2/process.json": {
"bytes": 154,
"sha256": "012134abc527a95c97027e1b969d50bff5e5b2fc1017521ec9999e8aea772543"
},
"cargo/process-removal-clippy-2/result.json": {
"bytes": 1923,
"sha256": "12f3352607a1608fcd1242b48336ae877b81677df38d96d17bbe9dc6ed3ff49b"
},
"cargo/process-removal-clippy-2/source-proof.json": {
"bytes": 603,
"sha256": "d27fd807235a480aa0fe76f3ceefddbdc1756cc1782727829be99f86b00d7550"
},
"cargo/process-removal-clippy-2/started.json": {
"bytes": 1533,
"sha256": "2e54d3d7bb6f1dca1ba771644dfd52d7c2557da4058fd9ce46f5758747e0ca20"
},
"cargo/process-removal-clippy-2/stderr.log": {
"bytes": 134,
"sha256": "fe9cc79b9f5e01c55fac8b73f57930aeb698f3a311a8e8eaab9dd9e5f45904ac"
},
"cargo/process-removal-clippy-2/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/process-removal-format-1/process.json": {
"bytes": 116,
"sha256": "6c78f04e1dacf3c96dbc71e106c9fa634a1f5f7d74d63659a52c71a51ef92956"
},
"cargo/process-removal-format-1/result.json": {
"bytes": 1868,
"sha256": "27181d72da07441a265453b2a76228ee92b77dfdd81995affbc874b61f10e26b"
},
"cargo/process-removal-format-1/source-proof.json": {
"bytes": 603,
"sha256": "d36d50d20b92c49c0867d1468ed9d8062e23aa8cda9a995ad8bd91938e337335"
},
"cargo/process-removal-format-1/started.json": {
"bytes": 1480,
"sha256": "30398f79fb193f3348222f7561c6b39a873ba4f832a71e82f3494ea8a5b357e0"
},
"cargo/process-removal-format-1/stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/process-removal-format-1/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/process-removal-format-2/process.json": {
"bytes": 116,
"sha256": "7f289173389898a25c8313c5a91e1f66ce6249688aa7241dac0b171a78b85eaf"
},
"cargo/process-removal-format-2/result.json": {
"bytes": 1868,
"sha256": "dce00e380220b98c5ff51eb63a4a8ef71b4b3f936db08160ecef40f26169acdc"
},
"cargo/process-removal-format-2/source-proof.json": {
"bytes": 603,
"sha256": "6f6a756569f3877223932253db0cea5380d646dd221f467c69b3572289d9a2fc"
},
"cargo/process-removal-format-2/started.json": {
"bytes": 1480,
"sha256": "2892f3c304419b97ff95c9a554e6cee623751eda9764b98466e474e1fb80c034"
},
"cargo/process-removal-format-2/stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/process-removal-format-2/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/process-removal-format-3/process.json": {
"bytes": 110,
"sha256": "17035416a3896914c8b79e0371362503ffa0ab21e8f4bc4e922cc21b32e15a79"
},
"cargo/process-removal-format-3/result.json": {
"bytes": 1858,
"sha256": "7ec891fa7a881b4f04fc3f58047a15f3972b397b7d2595e4a0bb04a9f658d9a3"
},
"cargo/process-removal-format-3/source-proof.json": {
"bytes": 603,
"sha256": "05d86dd797bf3c1a0d9c8b0c00a49294009ead98cb9e9c3e258d0cb59e9331b1"
},
"cargo/process-removal-format-3/started.json": {
"bytes": 1470,
"sha256": "6dae3900f3e53d89878ee0a9b89552bec60f351f56aa88505bbdba0a33348af8"
},
"cargo/process-removal-format-3/stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/process-removal-format-3/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"cargo/process-removal-tests-1/process.json": {
"bytes": 217,
"sha256": "007c489991c9d8ab793dbaa530aa96020e991b2428188e06a6e3ff4b7c369bc9"
},
"cargo/process-removal-tests-1/result.json": {
"bytes": 4695,
"sha256": "f95efe6a49ecd21625cb93fc13a3c759fc5703ed41d16689a075964464b443b9"
},
"cargo/process-removal-tests-1/source-proof.json": {
"bytes": 602,
"sha256": "4074db859ff385c14941ba8e45530a7e748e0db45101339d881937663c0ef512"
},
"cargo/process-removal-tests-1/started.json": {
"bytes": 1611,
"sha256": "ab8ca8306f1ea1a909cb7c049d0503646b738570840dd800865c89d2a0b4d434"
},
"cargo/process-removal-tests-1/stderr.log": {
"bytes": 355,
"sha256": "8106f2b580d08ae4991b1bae18a158f7cc7cf0b6d27e62114492553f69fd6f83"
},
"cargo/process-removal-tests-1/stdout.log": {
"bytes": 322947,
"sha256": "aeb571241a21c246ac94ae64f698b478ded69988b9f811c806bb689bb8af6dc9"
},
"cargo/process-removal-tests-2/process.json": {
"bytes": 217,
"sha256": "2884ec9947c403f3185f7963b2313500f79409cde575f5279827b8cc4227a868"
},
"cargo/process-removal-tests-2/result.json": {
"bytes": 4693,
"sha256": "ebf26a86bf954f0e0f6819ff1f6dc8cb261b02d0c73cde548827ce5bbcc0812a"
},
"cargo/process-removal-tests-2/source-proof.json": {
"bytes": 602,
"sha256": "4ab508f91efc34fde8cc1fc99fcc174d22f9eaf141730f608582b0067007eb56"
},
"cargo/process-removal-tests-2/started.json": {
"bytes": 1611,
"sha256": "6e5e873ac87130f4e352fda135d31ade35d248eb7e9c0d826a92346a57264e1c"
},
"cargo/process-removal-tests-2/stderr.log": {
"bytes": 355,
"sha256": "fa12c89f816b914e7b32627e498d2913f175cec41e5c3b100f815e32f471e9a4"
},
"cargo/process-removal-tests-2/stdout.log": {
"bytes": 325669,
"sha256": "c557894a0c8dfeae7ca91191a97df758638f0cdb32b0bb8e871fa8d06c48177f"
},
"cargo/process-removal-tests-3/process.json": {
"bytes": 217,
"sha256": "cb98374a36dc5591268b0420213399e5bc4607b9877a4707ad265daa08ad2025"
},
"cargo/process-removal-tests-3/result.json": {
"bytes": 4692,
"sha256": "82709fef9b1aabdf2fcc07a7cb9300d16474bc62370d7157e6bb6270cd8264f8"
},
"cargo/process-removal-tests-3/source-proof.json": {
"bytes": 602,
"sha256": "0407b22338c2d7bf725c8fe7098515a484cf67067578b4bf4992fa0a49bd3f93"
},
"cargo/process-removal-tests-3/started.json": {
"bytes": 1611,
"sha256": "bf71f073afc1fcc56df6c6f51841d75cd189c8576602b8e43ecb4e7470593c86"
},
"cargo/process-removal-tests-3/stderr.log": {
"bytes": 284,
"sha256": "ae9e8161a2438edc48c8667594310c21c3103583ce9c289efabb6f2e031574af"
},
"cargo/process-removal-tests-3/stdout.log": {
"bytes": 317009,
"sha256": "ace568a568723a784e0736f0b2d9287401f7fbe6a9b5fc025dc047205406f22a"
},
"cargo/process-removal-tests-4/process.json": {
"bytes": 215,
"sha256": "07992dcb5311d345837f37093dc9eb1d699d1ede9f812496a7f964d2c0dc363d"
},
"cargo/process-removal-tests-4/result.json": {
"bytes": 4685,
"sha256": "8513d24b9f05645908f4a475fcc90b5887362473f8046282bfd8af6a24c61dd5"
},
"cargo/process-removal-tests-4/source-proof.json": {
"bytes": 602,
"sha256": "ff045bf753ec4e09abc980018dd90f38338889dcd0877200364f5460418df5c6"
},
"cargo/process-removal-tests-4/started.json": {
"bytes": 1604,
"sha256": "1c408cdce0995388fcc2ba6d17ab2786daef461a656384684b02777e3b5d887d"
},
"cargo/process-removal-tests-4/stderr.log": {
"bytes": 284,
"sha256": "ed791ebad3050c571f427686faacb85afa112e958c6a190b272881f0fa661fc9"
},
"cargo/process-removal-tests-4/stdout.log": {
"bytes": 297004,
"sha256": "39100d92c647f02ca6c172b0a859e9ee39caaafca9fa60426fd628bceeea633b"
},
"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": "process-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"process-removal-[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"] == "process-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,122 @@
# Final bounded Process removal source review
Reviewed local commit `fe637dcd277b17653139ffa234e601b29775c565` on
2026-09-25 UTC. `git status --porcelain` was empty before this ignored local
review note. No production edits, Cargo, UI or Gitea operations were performed;
the parent's frozen-input verification gate was running independently.
**Result:** no new actionable production finding in the requested final scope.
The previous P1, P2 wording and same-batch gesture findings are addressed in
source; the two requested planner coverage gaps now have independent literal
test cases. Passing runtime/native gates remain the parent's separate evidence.
## Prior finding disposition
| Prior item | Final source assessment |
| --- | --- |
| P1 interpretation-only caller closure omitted explicit-to-explicit effects | Addressed. `Plan.affected_uses` remains independent of interpretation deltas, seeded from actual removed/changed definition topology. The OA/SA repeated/direct/transitive caller test is at `behavior/topology/group_removal/tests.rs:680`. |
| P2 “No remaining contributions” denied surviving unlisted contributions | Addressed. `app/process_removal/view.rs:89–93` now says “Empty order” / “One item in this order,” with no false global contribution count. Empty order explicitly permits incomplete contextual flow. |
| P2 candidate queued Delete admitted after a later private group press | Addressed in source with synchronous shared input generation and an active gesture lease; production-tree discriminator now exists at `app/process_removal/tests.rs:536`. Runtime result not independently observed by this reviewer. |
| Coverage: selected occurrence + covered selected link + uncovered selected link | Added at `behavior/topology/group_removal/tests.rs:707`, both layers. Literal removed IDs, changed IDs, exact two Delete commands, reverse selection order and independent final Project/source history oracle. |
| Coverage: global attachment/trace/placement/diagram impact | Added at `behavior/topology/group_removal/tests.rs:738`, both layers. Literal removed and changed sets include two placements, cascade-owned evidence, trace and surviving constraint/diagrams; counterpart, unrelated view, retained trace/artifact/placement remain exact. Full expected Project/source history oracle is independently constructed. |
## Input lifetime correction
`behavior/topology/removal_input.rs` supplies a shared `Arc<AtomicU64>` lifetime:
even generations admit idle capture; an acquired group gesture owns an odd
generation. A lease advances the generation on Drop, including ordinary
release, cancellation and widget-state destruction. Checked arithmetic leaves
overflow terminal/unavailable rather than revalidating old packets. Compare-
exchange prevents a stale lease from resetting an unrelated generation.
The production topology view supplies the State's lifetime to the Graph.
`direct_selection.rs` revokes idle captures synchronously on canvas presses and
Unfocused/Resized boundaries. A selected-card GroupPress holds the lease. The
shared viewport forwards synthetic Unfocused when camera ownership begins, so
the same native batch can revoke an earlier Delete even though App has not yet
consumed its queued Begin message.
`app/process_removal.rs:43–58` captures/requires an idle generation;
`:90` compares it before any source/canvas boundary; `:185` compares it again at
confirmation. This closes the specific prior race without substituting an App
message-order check for synchronous widget state. Full typed selection/context
admission remains present alongside the generation check.
The new mounted test at `app/process_removal/tests.rs:536` uses a real queued
Delete, cursor move and selected-card press in one production input batch. Its
assertions require no modal, unchanged canvas epoch, exact selection and shared
Document snapshot, disabled fresh capture during the live gesture, and a fresh
review after cancellation. The test at `:606` exercises Delete followed by
middle/background-left camera pan start. Direct lifecycle tests at
`behavior/topology/direct_selection_tests.rs:458` cover release, Escape,
unfocus and Input destruction, requiring idle recovery with a different
generation. The primitive lease test also checks concurrent acquisition refusal
and explicit idle revocation.
These are meaningful discriminators for the reported paths. They are not a
claim that the generation tracks every internal viewport gesture: existing
viewport keyboard/capture rules still own camera input. No additional concrete
production bypass was found in that composition.
## Review presentation correction
Identical local/expanded order dimensions are coalesced only when BOTH before
and after vectors match (`app/process_removal/view.rs:62–80`). A divergence in
either snapshot still produces separately labeled dimensions, preserving the
owned-unlisted discriminator. Contextual-analysis changes remain independent.
Affected caller definitions are deduplicated for the header count; rows retain
definition-to-caller relationships, without claiming reference-instance counts.
The text continues to disclose possible reused entry/exit changes and the lack
of complete expanded-graph analysis.
## Precise verification boundary
This review establishes source consistency, test-oracle suitability and old
finding disposition for the identified commit only. I did not execute any new
or existing test, inspect the parent's gate output, run the native UI, verify
compact layout/focus with real input, test packaged launch/save/reopen, or
confirm remote publication/integration. In particular, the mounted race tests'
presence and correct assertions do not by themselves establish their passing
result. Parent gate/native evidence must remain attached to the tested commit;
later changes invalidate only the affected portion of this source review.
## Addendum: qualified-definition menu restoration after runtime gate
The parent subsequently reported 185 passing and two failing tests for
`fe637dcd277b17653139ffa234e601b29775c565`: an existing qualified-definition menu
regression and a literal source-oracle whitespace mismatch. Those are parent-
reported runtime results; I did not run or independently inspect their logs.
I reviewed the uncommitted `crates/oa-desktop/src/model_menu.rs` correction
relative to that commit. Inspected file SHA256:
`8BC2C74EA83EAEC8BD342AF5600FBF8E9963BED7290DC094E8B4FC7ABFC0B70D`.
At inspection this was the only tracked modification. This addendum binds to
that file content, not a claim that all current files equal the prior HEAD.
**No actionable finding in the correction.** The qualified navigation entry is
now constructed before the aggregate-selection early return, so it remains
available for a clicked selected or unselected preview while preserving group
removal. Its message uses the captured request's Process context and exact
qualified endpoint; it does not read the primary selection or strip reference
paths. Preview-link navigation retains the prior source-endpoint convention.
The group branch still returns before singleton mutation entries are built.
Outside the group branch, a qualified target still returns before those entries.
The group Remove message continues to capture the entire current selection;
adding navigation does not bypass the planner's qualified-member refusal or
turn deletion into a clicked-object action. Selection-changing menu entries
remain explicit.
Existing menu token/context/exact-selection validity checks apply unchanged.
App menu invocation routes TopologyCanvas navigation before ordinary clicked-
object selection. `app/process_canvas.rs:125` resolves the complete endpoint,
remembers that exact invocation and opens its defining sequence; it retains
pending-edit admission. No singleton destructive bypass was introduced by the
entry relocation.
The previously passing source-only review did not catch the menu regression;
the runtime gate supplied necessary additional evidence. The correction still
requires its relevant regression rerun. The independent source-oracle whitespace
repair is outside this addendum's inspected scope and is not treated as a
production defect or as verified here. No Cargo/UI/Gitea or production edits
were performed for this addendum.
@@ -0,0 +1,67 @@
# Parent native observations, 2026-09-25 UTC
Source `572e96ba03522cfb822b13fe4abb50e857a5de48`, Windows gnullvm. These
observations describe Computer Use actions; capture JSON is window/image state,
not an instrumented input log. Both runs use independently derived oracle-v2.
## Run 1
PID 6524. Prepared immutable executable/DLL copies, normal launch and Alt+F4
close; final receipt exit 0, empty stdout/stderr, all immutable pins unchanged.
Opened only this run's working.arcadia through the native file picker.
- Captures 01–11: launch/open/navigation. Attempts 07/08 did not select the
Process; after maximizing, 10 selected it and 11 opened Behavior. Use the
largest returned screenshot for canvas coordinates; captures may also contain
a tiny window-icon region. Native accessibility exposes the top-level window,
not the Iced widget tree, so content/focus judgments use visible screenshots.
- 12–16: selected local Action @a; context Add selected Control @c, then authored
link @approved. Count 3, both same-name cards outlined, link primary with alias
approved in the inspector. Context Add is demonstrated, not native Shift-click.
- 17–19: header removal review shows three selected removals, one preserved
updated Process, ten effects across two pages. Scrolling page 1 exposes the
explicit-to-implicit order change. Page 2 names @e1 then @e2 and both caller
definitions @caller/@outer, separately from stored-record counts. Full names,
aliases, explanatory scope and fixed footer are reachable at 1605x872.
- 20–23: focus search, type @outer, see one transitive caller. Enter leaves the
review open. Appending -no-match shows zero results and recovery guidance.
- 24: Cancel retains count 3 and link primary. 25 F6, 26 Delete opens full
review; 27 Escape retains group. 28 Shift+Delete immediately after Escape does
not open review. 29 F6 followed by 30 Shift+Delete opens full review. Canvas
focus is required; do not infer success from capture 28's filename.
- Attempt 31: titlebar Restore action returned a minimized-window capture error.
Fresh window listing/reactivation recovered a restored 1442x790 view in 32;
the review visibly expired and Remove was disabled. 33 Escape cancelled it.
Attempt 34 to reposition the window was rejected by the automation API because
its destination was outside window bounds. No 31/34 capture exists. 35 refreshed
unchanged state; 36 maximized again. These failures are not native crash evidence.
A genuinely compact 1100px review was NOT established; restored view was partly
clipped by the visible desktop. Do not claim compact-layout acceptance.
- 37 Save all after cancellation: captured cancelled-baseline is exactly 2609
bytes, baseline SHA256 9965f4024584d64ac81442724aeb77b018b0cfae94e50bd412c5dcbbac49f6bf.
- 38 right-clicked unselected @e1: existing group count 3 and link primary remain.
39 context Remove opens same complete group. 40 explicit Remove commits once:
two exchange cards remain with an implicit arrow, owner selected, group empty.
- 41 Save all: removed snapshot is exactly 2258 expected bytes, SHA256
b4a2b07ad4c796f555c61945ca42b1c221b454b35488c60fefc86f96f7aa71ba.
- 42 one Ctrl+Z restores four cards and authored link. 43 Save all plus undo
snapshot equals all original 2609 bytes. 44 one Ctrl+Y restores removed model;
45 Save all plus redo snapshot equals all expected 2258 bytes. Normal close.
## Run 2
PID 7248. New process and isolated working file copied from run-1/snapshots/redo.arcadia;
prepared receipt records the origin path and exact pin. 01–07 fresh launch,
maximize, native file picker and open run-2/working.arcadia. 08 select Assessment,
09 Behavior displays two surviving exchanges and implicit order. 10 Save all;
reopened snapshot equals all 2258 expected bytes. Normal Alt+F4 close; exit 0,
empty logs and unchanged immutable pins. No history carried between processes.
## Scope
Native evidence is OA, derived layout, this bounded fixture. SA/planner variants,
stale event races and command failure are covered separately by recorded tests.
No native Shift-click, compact 1100px acceptance, comprehensive accessibility,
actual Process source-budget failure, full suite, Linux, integration or release
is claimed. Source oracle equality preserves reusable definitions, caller
references and SA sentinels; it is distinct from screenshot/gesture evidence.
@@ -0,0 +1,124 @@
{
"status": "PASS",
"scope": "Independent byte-level archive-to-original mapping and recursive run/preflight inventory; no native execution or Git/Cargo operations",
"archive": {
"bytes": 5617713,
"sha256": "f77fd3abe7ecc5838395b94a95ba935c8dc5c10a36c8e3a2c1ec4a1da1521b84"
},
"members": 327,
"mapped_original_files": 326,
"normalized_config": 1,
"member_groups": {
"fixtures": 3,
"gates": 70,
"helpers": 11,
"oracle-revisions": 5,
"preflight": 15,
"preparation": 6,
"normalized_config": 1,
"review-notes": 8,
"runs": 208
},
"attempts": {
"run-1": {
"archived_files": 156,
"capture_records": 43,
"excluded_binaries": 2
},
"run-2": {
"archived_files": 52,
"capture_records": 10,
"excluded_binaries": 2
},
"fixture-preflight-1": {
"archived_files": 15,
"excluded_binaries": 2
}
},
"captures": 53,
"omitted_binaries": 6,
"errors": [],
"report_review": {
"material_overclaims": [],
"limits_preserved": [
"OA bounded fixture",
"1100px compact layout open",
"Shift-click not exercised",
"capture helper not input logger",
"focused Shift+Delete required F6 after Escape",
"no full accessibility/full suite/Linux/integration/release/publication claim"
],
"accepted_true_interpretation": "Bounded recorded native run/preflight acceptance, not compact-layout or complete-application acceptance",
"observation": "Main report says resizing expires review; raw sequence demonstrates review expired following Restore/minimized capture recovery/reactivation, not an isolated controlled resize experiment."
},
"standalone_verifiers": [
{
"kind": "native",
"exit_code": 0,
"stderr": "",
"status": "PASS offline archive, exact source and receipt consistency checks",
"runs": [
{
"run": "run-1",
"reviewer_accepted": true,
"checks_pass": true,
"problems": [],
"capture_records": 43,
"source_checks": [
{
"file": "snapshots/cancelled-baseline.arcadia",
"phase": "baseline"
},
{
"file": "snapshots/redo.arcadia",
"phase": "removed"
},
{
"file": "snapshots/removed.arcadia",
"phase": "removed"
},
{
"file": "snapshots/undo.arcadia",
"phase": "baseline"
},
{
"file": "working.arcadia",
"phase": "removed"
}
]
},
{
"run": "run-2",
"reviewer_accepted": true,
"checks_pass": true,
"problems": [],
"capture_records": 10,
"source_checks": [
{
"file": "snapshots/reopened.arcadia",
"phase": "removed"
},
{
"file": "working.arcadia",
"phase": "removed"
}
]
}
],
"preflights": [
{
"preflight": "fixture-preflight-1",
"reviewer_accepted": true,
"checks_pass": true,
"problems": []
}
]
},
{
"kind": "checks",
"exit_code": 0,
"stderr": "",
"stdout": "process-removal-tests-1 recorded exit 101 inputs 831 executable records 2\nprocess-removal-tests-2 recorded exit 101 inputs 832 executable records 2\nprocess-removal-tests-3 recorded exit 0 inputs 832 executable records 2\nprocess-removal-tests-4 recorded exit 0 inputs 832 executable records 2\nprocess-removal-format-1 recorded exit 0 inputs 831 executable records 0\nprocess-removal-format-2 recorded exit 0 inputs 832 executable records 0\nprocess-removal-format-3 recorded exit 0 inputs 832 executable records 0\nprocess-removal-clippy-1 recorded exit 101 inputs 832 executable records 0\nprocess-removal-clippy-2 recorded exit 0 inputs 832 executable records 0\nprocess-removal-build-1 recorded exit 0 inputs 832 executable records 2\nPASS offline consistency: {'runs': 10, 'recordedFailures': 3, 'logicalFiles': 83, 'storedMembers': 71, 'sourceCapsulePairs': 4, 'captureHelpers': {'gate.py': 'helpers/3e4cd38fd3807a4010a5a7102354f9139cab236ca55163c96090e416161e5fd2.py', 'source-proof.py': 'helpers/c86f51d9b8465341dab73d045923ec306b15ebabf7c97b81beac7afe73bd72a1.py'}, 'sourceProofs': 10}\nHistorical receipts only; no source/binary execution or native acceptance claim.\n"
}
]
}
@@ -0,0 +1,54 @@
# Independent bounded native evidence review: Process removal
Reviewed 2026-09-25 UTC. Source identified by the supplied build/native receipts: `572e96ba03522cfb822b13fe4abb50e857a5de48`. This reviewer made no application inputs, source edits, Cargo invocations, Git mutations or Gitea writes. Parent owns execution and publication. Evidence root: `.local/process-removal-native/run-1`; local-only until published by parent.
## Conclusion
No new blocking correctness or readability finding in the inspected OA three-member removal workflow. The evidence supports the complete-group review and removal, visible interpretation/caller consequences, cancellation, expired-review feedback, exact saved-source removal/Undo/Redo and fresh-process reopen. Native compact-width acceptance remains open: the 1605x872 maximized images and restored 1442x790 image do not establish a 1100px window. Accessibility and keyboard-only navigation are also not established by this evidence. The closed run-2 reopen addendum below completes the persistence review without extending these layout/input boundaries.
## Evidence binding and its limits
- Inspected `prepared.json`, `started.json`, `finished.json`, build `evidence/result.json`, `evidence/source-proof.json`, `capture.js`, `verify-captured.py`, fixture files and every saved-source snapshot receipt/evaluator output. Independently recomputed the lengths/hashes of all 12 immutable entries in prepared.json; all match. This includes the executable, runtime DLL, oracle, helper, result and captured input-map/source-proof records.
- The build receipt identifies exit 0, unchanged captured inputs, 832 captured inputs, and desktop artifact SHA-256 `a13190e41bdb2d05ee35f1e6e38d35ce9f7a8a46864d5a73522126c0529489ca`. The native executable matches this artifact. The source-proof receipt reports exact captured inventory/Git blob agreement at the identified source. I checked the receipt bindings; I did not independently repeat the Git-blob comparator.
- Native run-1 starts PID 6524 from its isolated copied binary at 03:35:47 UTC; closes at 03:45:29 UTC with exit 0, unchanged immutable inputs and empty stdout/stderr. These are crash/log checks for this run, not assertions about all application behavior.
- Capture JSON records window identity, time, accessibility snapshot and image filenames/dimensions. The capture helper does not record each input. Consequently screenshots prove visible states; descriptions of clicks/keys and the single Undo/Redo count are parent-reported execution observations, corroborated by before/after states and source snapshots. Labels alone are not instrumented input evidence.
## Directly inspected images and workflow findings
Images inspected with view_image: 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 30, 32, 38, 39, 40, 42 and 44 (the full-size `-1.jpg` variant when present, otherwise `-0.jpg`).
1. Captures 17/18/19 expose all ten effects across two pages and page-one scrolling: three selected removals (same-name Action/Control distinguished by @a/@c, plus @approved), one preserved/updated owner, explicit-before and implicit-after order, the two surviving ordered positions @e1/@e2, and caller definitions @caller/@outer. Full names and aliases are readable. The wording separates three removed records from one updated record and two affected caller definitions. It warns that reused topology, entry/exit connections and diagnostics may change and disclaims complete caller graph computation. This correctly avoids presenting the two caller definitions as a reference-instance count: @caller contains two references in the fixture. The transitive @outer row says it uses @assessment; the policy intentionally includes transitive use.
2. Page-one content scrolls while search, pagination, Cancel and destructive action stay available. Capture 18 reveals the otherwise lower after-order result; capture 19 exposes both ordered positions and both caller rows. No hidden part of this fixture's reported ten-effect set remains uninspected. This does not establish reachability for arbitrarily long names or much larger effect sets.
3. Capture 21 finds exactly @outer with full name and alias. Capture 22 retains that review and search after parent-reported Enter. Capture 23 shows zero matches and helpful recovery wording. The selection count and destructive action remain independent of the filter, as expected for reviewing the captured group.
4. Capture 24 after parent-reported Cancel and 27 after Escape retain three selected members, both same-name cards and the maintained arrow at the same visible positions; status says model/selection unchanged. A later saved snapshot is byte-identical to baseline. Capture 26 shows the whole group review after focused Delete; 30 shows it after focused Shift+Delete. Capture 28 explicitly does NOT show a review, despite its filename: parent reports that Shift+Delete immediately after Escape was ineffective until F6 restored canvas focus (29), then Shift+Delete opened 30. This supports the focused shortcut contract; do not claim automatic focus restoration after cancellation.
5. Capture 32 shows the restored window with the stale review warning, disabled removal button and available Cancel. Text and controls are readable. Its lower edge is clipped by the available capture/screen, so it cannot count as a fully visible compact-layout pass. Parent reports missing capture 31 followed a UIA restore action that yielded a minimized-window capture error; 32 was recovered through fresh enumeration/reactivation. Missing 34 followed a tool refusal to drag outside screen bounds; parent reports 35 unchanged. Neither absence is a native crash finding or evidence of successful resize.
6. Capture 38 shows the menu over unselected Receive report while the original group remains three selected; its action is explicitly Remove selected contributions / links. Capture 39 then reviews @a/@c/@approved, not Receive report. This is a meaningful non-primary/context-subject discriminator.
7. Capture 40 shows zero selection and owner inspection after confirmation. Only Receive report and Forward report remain, joined by the expected implicit arrow, and status reports three selected contributions/links removed in one change with referenced definitions preserved. Capture 42 restores the two removed cards and maintained arrow and reports model change undone. Capture 44 returns to the two exchange cards and reports model change redone. The status/canvas observations agree with the byte-exact snapshots below.
## Exact source and history evidence
Independently recomputed all snapshot SHA-256 hashes in addition to inspecting the four evaluator results (each PASS, exit 0). Full fixture source inspection confirms that the v2 expected file deletes @a, @c and @approved, changes assessment steps to [@e1,@e2], and retains all work/exchanges, both repeated caller references, transitive caller and opposite-layer sentinel records, with preserved writer trivia.
| Saved state | Bytes | SHA-256 | Expected |
| --- | --- | --- | --- |
| cancelled-baseline | 2609 | 9965f4024584d64ac81442724aeb77b018b0cfae94e50bd412c5dcbbac49f6bf | Complete baseline |
| removed | 2258 | b4a2b07ad4c796f555c61945ca42b1c221b454b35488c60fefc86f96f7aa71ba | Complete v2 removal oracle |
| undo | 2609 | 9965f4024584d64ac81442724aeb77b018b0cfae94e50bd412c5dcbbac49f6bf | Complete baseline |
| redo / final working file | 2258 | b4a2b07ad4c796f555c61945ca42b1c221b454b35488c60fefc86f96f7aa71ba | Complete v2 removal oracle |
The saved-source comparisons establish exact full-file persistence at these checkpoints. Together with parent-reported single Undo/Redo actions and screenshots, they support the one-change history workflow. The source comparator itself does not introspect parser state, transaction count or history stack. This native case does not independently exercise SA removal, qualified-selection refusal, every ContextualLink purpose, a write failure, or every semantic discriminator in the broader automated suite.
## Residual acceptance gaps and small observations
- Native 1100px compact layout and complete modal visibility at that width remain untested; do not close that gate based on restored 1442px capture.
- Parent created membership with context-menu Add, so native Shift+click selection is not established by this run. The failed early selection attempts 07/08 should not be counted as successful; parent reports selection took after maximize at 10.
- Accessibility snapshots expose only the window/system chrome, not application controls; no screen-reader semantics or tab-order acceptance follows. Search caret and focused shortcuts provide only the specific focus evidence above.
- No keyboard-only review completion, native failure recovery, or native SA group-removal evidence is included. Existing automated/source-review results must retain their distinct scopes.
- The visible plural agreement strings (for example "1 records updated", "1 definitions", "1 adjacent ordered pairs") are minor copy defects, not a correctness or reachability blocker. No source change is proposed as part of this bounded review.
- Packaged launch, integration and publication require their own closed records. Run-2 is a fresh native process using an isolated copied executable/runtime; it does not establish execution from the eventual distribution archive.
## Closed run-2 reopen addendum
After parent reported closure, inspected run-2 prepared/started/finished records, snapshot receipt/evaluator result, and actual full-size captures `09-reopened-implicit-order-1.jpg` and `10-reopened-saved-1.jpg`. Independently recomputed every prepared immutable file length/hash: all 12 match. Both final working file and independent reopened snapshot hash to the exact 2258-byte v2 removed oracle (`b4a2b07ad4c796f555c61945ca42b1c221b454b35488c60fefc86f96f7aa71ba`). The snapshot evaluator passed with exit 0.
The prepared record names run-1/snapshots/redo.arcadia as initial source with its matching removed-source pin. Run-2 launched a new process, PID 7248, at 03:45:38 UTC from its separately copied binary and isolated cwd. Capture 09 displays 01 Assessment with zero selection, only Receive report and Forward report joined by the implicit arrow, owner inspection, and the source-opened status. Capture 10 displays all changes saved / all source files saved. The full file remains byte-identical after this reopen/save. The process closed at 03:47:52 UTC, exit 0, empty stdout/stderr and immutableUnchanged true; parent reports normal Alt+F4. These observations support fresh-process load/render/save preservation for the actual removed native result. No parser/history-stack introspection or broad model completeness claim is inferred from them.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,300 @@
"""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 = 'process-aggregate-removal-native-evidence'
PHASES = {'baseline': 'baseline.arcadia', 'removed': 'expected-removed.arcadia'}
V2 = 'process-removal-v2-preserved-trivia'
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/'):
revision = fixture['fixture_revision']
assert revision in ('process-removal-v1-literal', V2)
assert fixture['selection'] == fixture['removed'] == ['a', 'c', 'approved']
assert fixture['changed'] == ['assessment']
assert fixture['caller_definitions'] == ['caller', 'outer']
assert fixture['remaining_implicit_order'] == ['e1', 'e2']
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']
assert pin(baseline) == {'bytes': 2609, 'sha256': '9965f4024584d64ac81442724aeb77b018b0cfae94e50bd412c5dcbbac49f6bf'}
actual = files[prefix + 'expected-removed.arcadia']
if revision != V2:
# Preserve the original incorrect oracle as history, never acceptance.
assert pin(actual) == {'bytes': 2186, 'sha256': '117b550f0c10753f53febb3ca6beb2c8088688ffa707f21eac654573e4a3d29c'}
return
text = baseline.decode('utf-8')
assert '\r' not in text
pattern = r'(?m)^ (?:oa_occurrence (?:a|c) |sequence_link approved )[^\n]*'
# Independent fixture-specific lexical rules; never execute archived code.
def trivia(line):
return re.sub(r'\S', '', re.sub(r'"(?:\\.|[^"\\])*"', '', line))
rows = re.findall(pattern, text)
assert [trivia(row) for row in rows] == [' ' * 22, ' ' * 20, ' ' * 16]
expected = re.sub(pattern, lambda m: '\n\n' + trivia(m.group()) + '\n\n', text)
assert expected.count('steps [@a,@e1,@c,@e2]') == 1
expected = expected.replace('steps [@a,@e1,@c,@e2]', 'steps [@e1,@e2]')
assert actual == expected.encode('utf-8'), 'V2 differs from independently specified delta'
assert pin(actual) == {'bytes': 2258, 'sha256': 'b4a2b07ad4c796f555c61945ca42b1c221b454b35488c60fefc86f96f7aa71ba'}
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'))
derivation = 'oracle-revisions/oracle-v2/derive-source-oracle-v2.ps1'
assert pin(files[derivation]) == js('oracle-revisions/oracle-v2/fixture-manifest.json')['derivation']
config = js('review-config.json')
final_source = config['final_source']
assert re.fullmatch('[0-9a-f]{40}', final_source)
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'
gates = config['gates']
assert len(gates) == len(set(gates)) and {'process-removal-tests-1', 'process-removal-tests-2'} <= set(gates)
assert all(re.fullmatch(r'process-removal-(?:tests|format|clippy|build)-[0-9]+', g) for g in gates)
assert {n.split('/')[1] for n in files if n.startswith('gates/')} == set(gates)
gate_reports = []
for gate in gates:
prefix = 'gates/' + gate + '/'
result, proof, inputs = (js(prefix + f) for f in ('result.json', 'source-proof.json', 'inputs.json'))
assert result['inputsUnchanged'] is True and 'finish' in result
assert proof['status'].startswith('PASS') and proof['result'] == pin(files[prefix + 'result.json'])
assert proof['inputMap'] == result['inputs'] == pin(files[prefix + 'inputs.json'])
assert proof['inputCount'] == result['inputCount'] == len(inputs)
assert proof['commit'] == result['head'] and re.fullmatch('[0-9a-f]{40}', result['head'])
for stream in ('stdout', 'stderr'):
assert result[stream] == pin(files[prefix + stream + '.log'])
gate_reports.append({'gate': gate, 'source': result['head'], 'exitCode': result['exitCode']})
# The original failed gates must remain failed historical facts.
for gate, source in [('process-removal-tests-1', 'fe637dcd277b17653139ffa234e601b29775c565'),
('process-removal-tests-2', 'f20497b29bf637619bc35ffe41274fc0f66c387b')]:
receipt = js('gates/' + gate + '/result.json')
assert receipt['head'] == source and receipt['exitCode'] == 101
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_fixture['fixture_revision'] == V2, 'Historical v1 cannot be 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 run_fixture['fixture_revision'] == V2, 'Historical v1 cannot be 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',
'gates': gate_reports,
'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,93 @@
# Independent Process removal planner review
Read-only source/test review against the amended Process group-removal policy.
Base `c62622cce71e06f0beae49bc3b4fe8e890eba951`; inspected uncommitted files:
- `crates/oa-desktop/src/behavior/topology/group_removal.rs`, SHA256
`4F125AB9F5EFEE7B92700E2B23E5B805A16BF2529F662D5C4413368752960C79`.
- `crates/oa-desktop/src/behavior/topology/group_removal/tests.rs`, SHA256
`3E6E56A45738F5E377C0659C58AD782ECDA408657439B806DAEFD396E06A46D5`.
No production defect found in this bounded inspection. No Cargo/UI execution;
this does not establish passing tests, App admission, native usability or release
acceptance. Prior policy critique is `process-removal-policy-critique-20260925.md`.
## Prior P1 correction verified in source
`prepare:213–217` derives affected definition owners from actual removed and
changed IDs using both project versions, then traverses unioned before/after
reuse dependencies. `definition_owner:106–124` handles definition records,
direct contributions and both maintained relation kinds with explicit owner for
ContextualLink. It does not require interpretation changes. The test at `:372`
uses Actions A/B/C, A→B and B→C, repeated direct references and a transitive
caller; deleting A or A→B yields literal {caller,outer} with unchanged
interpretation facts and unchanged caller records. This addresses the reported
explicit-to-explicit false negative.
## Admission and transaction assessment
- `prepare:127–178` validates the initial structural model, every owner-listed
contribution and every original typed member. Correct owner, body, layer,
membership and uniqueness are enforced. Qualified occurrences/preview links,
unlisted selected cards, wrong target classes and foreign maintained links
refuse before mutation/normalization.
- SequenceLink endpoints must both be validated listed contributions;
ContextualLink admission uses its explicit owner. Initial core validation
supplies remaining endpoint/purpose constraints and permits warning-only
incomplete models.
- `:181–187` unions occurrence cascades, omits only covered relation commands and
evaluates one Batch. Sorting admitted occurrence roots before uncovered links
avoids UUID/click-order dependency. This is valid for the admitted contribution
and maintained-link domain; no mutual occurrence deletion dependency exists.
- `:188–206` derives actual identity differences, rejects new IDs and verifies
every original selection was removed. `Project::identities` includes embedded
placements; complete surviving record comparisons include diagram records and
embedded nodes, artifacts and traces. The implementation covers every mutable
record map in the current Project schema.
## Semantic impact assessment
`interpretations:43–73` correctly separates desktop listed-step interpretation,
core indexed-contribution/no-maintained-relation interpretation, and transitive
ContextualLink presence. All surviving definitions are compared, independently
of record deltas. Owned-unlisted contributions affect the core predicate and
caller edges. Empty `Some([])` remains a classifier, not proof of a valid flow;
the policy's user-visible wording condition remains applicable.
`callers:85–98` terminates with a seen set, deduplicates definition owners and
filters to surviving definitions. It does not enumerate repeated instance paths
or assert complete expanded connectivity. Both snapshots contribute reuse
edges. Global dependent link removal seeds its explicit defining owner even
when that owner's own record is unchanged.
## Test evidence and remaining gaps
The tests construct literal removal and surviving-change identity sets, literal
interpretation vectors/flags/caller sets, and independently clone/prune the
expected model (`expected_without:68`). They do not use the planner candidate as
the expected result. `assert_history:76` compares the complete Project, reopens
the emitted source bundle and verifies exact bundle/model Undo and Redo. OA and
SA execute each scenario. This is meaningful source-level evaluator design.
Two useful remaining verification gaps were sent to the parent; they are not
demonstrated production failures:
1. Mixed covered and uncovered selected relations in the same group. Current
mixed occurrence/link tests cover every selected relation via occurrence
roots; uncovered selected links are otherwise tested alone. Add A,B,C,D with
L1=A→B and independent L2=C→D; select A,L1,L2; literal result removes exactly
{A,L1,L2}, preserves B,C,D and runs exactly Delete(A),Delete(L2), regardless
of click/UUID order. This discriminates accidentally dropping uncovered
relations when occurrence roots exist.
2. Aggregate identity bookkeeping outside process records. Existing fixtures
contain no artifacts, traces, opposite-layer counterpart or embedded diagram
placements. Add a valid attachment/trace/placement case and compare literal
removed IDs, changed surviving diagram/owner IDs, and complete preserved
counterpart/source model. Core cascade behavior has existing independent
tests, but this new planner's review accounting still needs direct evidence.
Other bounded limitations: fixed adversarial UUID ordering is not explicitly
constructed; source inspection proves occurrence-before-link partitioning, while
tests reverse click order. App/source-write-failure/visibility/lifetime tests
belong to the adapter review. Large reuse performance and incomplete expanded
analysis are not runtime-tested here.
@@ -0,0 +1,109 @@
# Independent Process removal policy critique
Local-only review for parent session issue 29. Base inspected:
`c62622cce71e06f0beae49bc3b4fe8e890eba951`; policy and concurrent planner work were
uncommitted. Read-only inspection apart from this note; no Cargo, UI, Gitea or
production edits. This is policy/source review, not runtime acceptance.
## P1: interpretation-only caller closure misses ordinary topology changes
**Status:** reported promptly; parent accepted and instructed planner/App to add
`affected_uses` independently of interpretation changes. Concurrent planner
source now contains that collection, but this review does not verify the final
implementation or tests.
The initial policy computed caller closure only for changed `local_order`,
`expanded_order` or transitive contextual-presence flags. Those facts detect
implicit-order changes and diagnostic mode changes, but do not cover ordinary
explicit-to-explicit topology changes. A caller's record can remain unchanged
while its reused graph changes substantially.
Literal discriminator, separately instantiated for OA and SA:
- P owns Action A, Action B, Action C and links L1=A→B, L2=B→C.
- Q owns R1(P) and R2(P); T owns RT(Q).
- Remove L1. P's `steps` is still [A,B,C], both implicit-order facts remain
`None`, contextual presence remains false. Removed IDs are exactly {L1}; L2,
all actions, both Q references, T's reference, and all work definitions survive.
- Nevertheless P's intrinsic entries change from {A} to {A,B}; expanding either
Q reference adds synthetic connectivity from that reference's entry to B.
Expected affected caller definition set for P is exactly {Q,T}.
- Removing A instead also changes Q/T while the three interpretation facts can
remain equal. Removing one ContextualLink while another remains yields the
same kind of false negative for a contextual-presence-only detector.
Source: `crates/oa-core/src/topology_context/analysis.rs:262–298` expands callee
entries/exits into synthetic edges; `:412–425` derives intrinsic boundaries from
remaining precedence. `docs/language.rst:632–640` separates synthetic connectivity
from authored degree and forbids complete-looking results from incomplete
analysis. The multiple-selection contract requires aggregate removal impact.
Bounded correction: derive changed definition owners from actual removed/changed
contribution and maintained-link records in both candidates; find surviving
caller definitions using before/after reverse reuse closure. Keep interpretation
facts as additional, more precise effects. Label the former “uses changed
definition”; explain that reused topology, entry/exit connectivity and
diagnostics can change. No full expanded graph is needed to establish this
conservative affected set. Do not equate “no newly created stored identity” with
“no new effective connectivity.”
## P2 wording conditions: empty definitions and repeated uses
These are review conditions, not verified defects in the unfinished App.
1. Empty expanded definition: `Builder::expand` returns an incomplete-analysis
notice immediately when children are empty (`analysis.rs:212–221`), before
the exchange-only discriminator at `:384`. Representing an empty classifier
as `Some([])` is useful to distinguish it from explicit mode, but does not
establish a valid implicit flow or any entry/exit. Literal discriminator:
P has only A; remove A; review says no remaining contributions/no ordered
pair, and does not promise connected or executable flow. Core incomplete
diagnostics remain authoritative. The policy already disclaims full graph
analysis; user-visible wording must carry that distinction.
2. Caller-owner closure is not a use-instance count. In the P/Q/T discriminator
above, Q is one caller definition containing two references to P. A map
containing Q once must not say “one use.” “Caller definitions” or “uses changed
definition” is accurate. No Cartesian expansion is necessary.
## Accepted boundaries and why
- Comparing both real order predicates is preferable to refusing every model
where listed desktop and indexed core contributions differ. The inconsistency
predates this operation: desktop `behavior.rs:1097–1123` looks at listed steps
and SequenceLinks; core `topology_context.rs:97–145` includes unlisted owned
contributions and ContextualLinks. Reporting both actual facts is sufficient
for this bounded operation, provided it claims neither full graph preservation
nor complete expanded analysis. Selected unlisted cards remain ineligible.
- The actual final candidate and all changed surviving records are the correct
authority for global stored impact. `references.rs:325–427` propagates every
typed ContextualLink dependency, descendants, traces, attachments and embedded
placements; `:459` onwards clears/prunes surviving records. A caller-owned
ContextualLink must be identified by explicit owner, not leaf parent.
- Occurrence-root cascade union plus uncovered selected relation commands is
valid for the admitted kinds. These occurrences do not own one another;
maintained Process relations cannot delete their contribution endpoints or
another admitted maintained relation. Selected links covered by occurrence
cascade remain selected review subjects. Control-flow cycles do not introduce
deletion-dependency cycles. Generalizing admitted kinds requires revisiting
this argument. The final Batch remains authoritative for compound effects.
- ContextualLink Exchange/CommonWork removal is assembly impact, not necessarily
precedence removal. Underlying work/exchange definitions survive. Qualified
local ContextualLink endpoints do not make the caller-owned relation read-only;
qualified preview occurrences/links are a different target class.
- Capturing document/revision/owner/epoch plus selection membership, primary and
generation is the right lifetime boundary. `diagram_selection.rs:169–175`
explicitly handles remove/re-add and primary-change/restore ABA. Final
admission must use rendered visibility and the current tool/draft/gesture
gates, not just UUID existence. Token scoping prevents old modal packets from
mutating the next review. This policy is sufficient; unfinished App execution
was not independently verified here.
## Remaining verification focus
Literal tests should include the explicit-to-explicit caller case above,
repeated-reference owner counting, empty definitions, mixed occurrence/link
cascade normalization in reverse click/UUID order, and a secondary-selection
ABA with the primary restored. Native review should inspect truthful meaning,
all impact pages/search, input focus and Enter behavior, stale packets, Cancel,
source-write failure and exact Undo/Redo/save/reopen. This critique does not
substitute for those checks.
@@ -0,0 +1,42 @@
# Process removal source-oracle investigation — 2026-09-25
Local-only, bounded investigation at source `fe637dcd277b17653139ffa234e601b29775c565`. Parent owns Process removal and oracle integration. No production code, tests, or versioned fixtures edited; no Cargo, build, UI or Gitea operation. Another worker's `model_menu.rs` edit was observed and left untouched.
## Conclusion
The full semantic Project comparison passed before the lexical assertion failed. The three selected/dependent declarations (Action `a`, Control `c`, SequenceLink `approved`) are removed, Process steps retain `e1,e2`, reused definitions and unrelated OA/SA records agree with the independently authored model oracle. The failed assertion is an incorrect **lossless-source oracle**, not evidence of a semantic deletion defect or a newly introduced writer regression.
The source contract explicitly says removed declarations erase syntax tokens while retaining their comments **and whitespace** as enclosing annotations (`docs/language.rst:1987-1989`). It also requires deletion annotations be separated from adjacent declarations with blank lines (`:1963-1964`) and preserves surviving reference-list chunks and their separator-adjacent trivia (`:1993-2000`). Canonical export is a different operation (`:1926-1929`); the mutation path must not silently reformat source (`:1969-1973`). `harness/release-source-project.md:43-45` and `harness/DECISIONS.md:87,93` corroborate this accepted policy.
This does not establish that blank space-only annotations are the ideal future editor UX. They follow an explicit existing contract and pre-existing implementation; changing them would require a separately scoped source-policy decision and source/core validation. Silently removing them solely to satisfy a newly authored Process oracle would change the common writer contract. No production formatting fix is justified within this aggregate-removal acceptance task.
## Independent derivation
Input `harness/fixtures/process-removal-baseline-20260925.arcadia`:
- Source commit: `fe637dcd277b17653139ffa234e601b29775c565`.
- Git blob: `9570e150d160bea87dc70b5dacedc692eb3708b4`.
- SHA-256: `9965f4024584d64ac81442724aeb77b018b0cfae94e50bd412c5dcbbac49f6bf`.
- Original UTF-8/LF bytes are required; derivation refuses changed input or CR characters.
1. Original steps are `[@a,@e1,@c,@e2]`. Surviving entries have no leading/trailing trivia and the removed entries have no trivia. Retain `@e1` and `@e2`, emit their one comma, and invent no whitespace: **`[@e1,@e2]`**. `source_project.rs:1516-1651` implements unique-reference identity matching and chunk preservation; this is not canonical `, ` formatting.
2. For each removed single-line declaration, quoted strings are indivisible syntax tokens. Their interior spaces disappear with those strings. Every whitespace byte outside strings survives, including the four-space indentation. Independent token-boundary counting yields **22 spaces for Action `a`, 20 for Control `c`, 16 for link `approved`**.
3. `source_structure.rs:236-271` creates deleted-declaration pieces through `trivia`; `source_project.rs:1127-1139` retains only Whitespace/Comment tokens. `attached_span` at `source_structure.rs:435` includes the line indentation and leaves the ordinary following newline in the enclosing gap when there is no trailing comment.
4. Existing concrete structural policy (`source_structure.rs:397-404`) surrounds each orphaned annotation with **two LF characters on each side**. The untouched original gap has one LF on either side. Consequently the exact post-predecessor sequence is `"\n\n\n" + " " * N + "\n\n\n"`, followed by the next declaration's unchanged four-space indentation. These are three LF characters per side, not three full empty lines per side.
5. All other baseline bytes remain unchanged. The specification requires whitespace retention and blank-line separation; the exact two-LF inserted boundary is the established writer policy, not a claim that every conforming future writer must always choose that count.
The common structural file's latest historical change is `4e8575bd1127d132b8170b408163ef234b4d307e` (`feat: preserve nested source projects across desktop and REST`), predating this Process change. Existing tests `crates/oa-core/tests/source_project.rs:1065` (`removed_list_items_and_declarations_keep_comments_as_enclosing_annotations`) and `crates/oa-core/tests/source_nested_updates.rs:187` (`deleting_owner_retains_annotations_and_extracts_independently_moved_descendant`) verify comment retention, semantic equality and annotation ownership. Those tests support the policy but do not themselves prescribe the exact 22/20/16 whitespace-only output; the above input-specific derivation does.
## Created artifacts and separate observation check
- `.local/process-removal-native/derive-source-oracle-v2.ps1`: create-only fixture-specific derivation script. It reads only the checksum-pinned baseline, applies the independently stated lexical rules, and refuses to overwrite an existing output. It does **not** read production writer code, expected-v1, failure logs or actual output. Script SHA-256: `38979a4b233b7d28a6392070c72298fb6c17660b2a2c8dc2a3bff74c3c816fae`.
- `.local/process-removal-native/expected-removed-v2.arcadia`: independently derived candidate, **2258 bytes**, SHA-256 `b4a2b07ad4c796f555c61945ca42b1c221b454b35488c60fefc86f96f7aa71ba`.
- Reproduce into a new destination: `& .local/process-removal-native/derive-source-oracle-v2.ps1 -OutputPath <new-output.arcadia>`. The script refuses changed baseline and an existing destination.
Only **after creating** the candidate, I separately extracted the sole `left: SourceBundle` project's JSON-compatible ASCII debug string from the failed log using an anchored shape check and `ConvertFrom-Json`, then encoded UTF-8 and compared byte-for-byte. **MATCH**, 2258 derived bytes and 2258 observed bytes. This observation corroborates the rule derivation; actual output was not an input to the derivation script. Failure log `.local/scenario-group-gates/process-removal-tests-1/stdout.log` SHA-256: `aeb571241a21c246ac94ae64f698b478ded69988b9f811c806bb689bb8af6dc9`.
The original versioned expected-v1 remains untouched (SHA-256 `117b550f0c10753f53febb3ca6beb2c8088688ffa707f21eac654573e4a3d29c`). The original failed run remains a failure. No new test execution or native acceptance is claimed.
## Concrete next action
Parent should preserve v1/failure evidence, introduce a new versioned v2 literal fixture from the pinned independent candidate, and point the full-source comparison and separately versioned native oracle/manifest to v2. Keep the full Project comparison, exact-source assertion and Undo/Redo checks. Explain the lexical correction and explicitly preserve significant space-only lines against editor trimming; a narrowly scoped Git whitespace attribute may be needed for the fixture, consistent with other exact source oracles. Re-run the relevant failed gate on the identified new source and separately review the menu correction. Do not canonicalize the baseline or weaken exact-source equality to semantic-only equality.