docs: publish prerelease admission reviews and initial gate evidence
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# Allocation integration seams for prerelease #37
|
||||
|
||||
Read-only comparison of actual fetched commits: group/App source `495464e84b5bae8ddad93e9fc69ef353e7f1c884` and drawing/Traceability tip `301126f6098286bf440c1bd1b1ed685753984723`; common ancestor `b8685416f56e53e30bd11ecf93a54cea10ac8458`. Local HEAD was reported by the parent as `57aec979f640ba1b8aa6fcbb7d9e26b29cf51596`; this review uses the explicit source commits, not uncommitted files. Existing untracked human-check documents were left untouched. No merge, index/ref/object mutation, build, GUI or Gitea action occurred. A stdout-only three-way merge of temporary file copies was used to identify text conflicts. This is not acceptance of a merged release candidate; the later union tip was not available to this review.
|
||||
|
||||
## One confirmed textual conflict
|
||||
|
||||
The branches modify five common code files. A three-way text check found **one conflicting hunk in `app.rs`, `App::update_inner`, `Message::Menu(Event::Invoke)`** (group line2412; drawing line2294). `architecture_authoring/tests.rs`, `guide.rs`, `model_menu.rs` and `views.rs` merge textually. Peer changes no `diagram.rs` canvas code.
|
||||
|
||||
Owner956 must compose the Menu hunk in this order after the existing menu token/context and pending-edit guards:
|
||||
|
||||
1. Preserve the group's early dispatch for `Message::DiagramSelection` and captured Process `TopologyCanvas` actions. Calling `select(menu.request.object)` first would collapse/rebind exact typed membership or navigate away from a qualified Process use before the intended selection action.
|
||||
2. For the remaining actions, preserve drawing's narrowly scoped `show_in_mixed_view` branch (drawing `app.rs:2319`): Traceability `PlaceSelected` sets selected/clears attachment/refreshes draft without ordinary cross-layer navigation; otherwise use `select`. Dropping it makes Show in this diagram operate on the preferred same-layer view instead of the validated captured mixed view.
|
||||
3. Preserve group's State-vs-architecture placement routing (`behavior.diagram.placement` with `selected_node=None` on Behavior canvas; otherwise `selected_node=request.placement`) and `reconcile_multiselect`, followed by the existing bend/action dispatch.
|
||||
|
||||
Choosing either conflict side wholesale loses a verified invariant. The combined behavior is directly exercised by existing group/context-menu/State tests and drawing's `model_menu_traceability_tests` plus `traceability_navigation_tests`; neither branch's separate pass proves that composition.
|
||||
|
||||
## Concrete lifecycle gap to check on the union
|
||||
|
||||
Both input families share `App::update`. At group `app.rs:1442`, `allocation_before(&message)` runs before `edit_connection`; `app/allocation_drag.rs:686` retires Allocation state for every `Message::Connection`, and `State::retire` at608 clears active token, preview and Arrange arm while advancing its epoch. Only later does `App::edit_connection` (group4611; drawing4481) reject a stale `Event::Endpoint(captured, id)` or an absent drawing session. There is no Connection envelope among the earlier stale-admission guards (`source_input_envelope` accepts unrelated messages).
|
||||
|
||||
Consequently, a delayed endpoint from a canceled drawing session can retire a newer group drag/Arrange arm even though it cannot create a relation. This is a source-demonstrated ordering risk already present in the compared branches, **not a merge-marker conflict or a newly demonstrated native failure**. Drawing's `allocation_cancel_navigation_stale_context_and_pending_edits_preserve_model` preserves a newer drawing session under old endpoints; group's `navigation_union::assert_stale_noop` preserves newer group ownership for other stale packet families. Neither inspected test covers this cross-family case. The focused integration check is: cancel drawing, establish a fresh live Allocation group press/Arrange state, deliver the old Endpoint, and require unchanged token/epoch/arm/preview/Document. If fixed, reject stale/non-owned drawing endpoint packets before shared retirement while still letting a current intentional tool change retire old drag ownership.
|
||||
|
||||
## Compatible seams that must not be conflated
|
||||
|
||||
Canvas dispatch already separates modes: `diagram/multiselect.rs::group_input` (group85, guard158) declines new group input while a connection session exists; `allocation_input_owner` (636) also requires no connection session. `DiagramCanvas::update` then routes the physical endpoint click to Connection before normal Allocation movement. Keep these guards; no replacement canvas architecture is needed to add the tool.
|
||||
|
||||
Drawing's `connection_tool::Tool::{can_connect,command,available_in}` (peer76/126/185) creates **one new Allocation relation for compatible unallocated leaf work**, preserves authored placements and view filters, and requires projections containing both performer/work categories. Group assignment deliberately retargets existing Allocation relation bodies while moving the selected closure and pins. Do not substitute either operation's planner for the other. The palette's new `available_in` call replaces an exhaustive four-tool match; retain it with the new enum variant.
|
||||
|
||||
Drawing's mixed Traceability endpoint branch in `edit_connection` intentionally bypasses ordinary `select` for the visible opposite-layer performer, and temporarily aligns `self.layer` to the captured diagram before execute, restoring it on failure. Keep that captured-view behavior together with group's outer multiselect reconciliation. The existing `allocation_mixed_view_keeps_captured_canvas_for_opposite_layer_pairs` tests exact view/camera/source history and rejects opposite-owner-layer targets; preserve those assertions on the actual union. Actual group/native results remain evidence for495464e, not automatic acceptance of drawing or the merged candidate.
|
||||
|
||||
## Parent readback of the selected candidate
|
||||
|
||||
The release owner subsequently published candidate `6ae75fb2ccaae16c29004444815ddeaeab2e75ac`. Full-branch transport stalled, so this session retrieved named files through Gitea's contents API at that explicit commit, verifying returned size and Git blob SHA-1 before inspection. This is scoped source inspection, not a local candidate checkout, combined compilation or package acceptance.
|
||||
|
||||
Candidate `app.rs` blob `646b0415a156b3a4e8074847c5b2dadfea24b27c` preserves the required Menu::Invoke composition: exact group/Process early dispatch, captured mixed-view placement, State-versus-Architecture placement assignment, and final selection reconciliation. Candidate `app/allocation_drag.rs` blob `b6c329496c1c935defee6f6cd85c281c3556e4a2` is byte-identical to the accepted495464e adapter. The old Endpoint/Restart admission still occurs after shared retirement in this candidate, confirming the identified gap applies to the selected integration source.
|
||||
|
||||
The narrow contribution adds early admission for both captured drawing packet types. A packet must match the current route context and an extant session with that exact context before shared state changes. Choose/Begin remain intentional tool boundaries, and a current Restart retains its existing reset behavior. The regression distinguishes missing session, wrong captured context, wrong live context while captured/session agree, and wrong retained session while captured/live agree. OA/SA canceled-drawing-to-Arrange tests require unchanged ownership, preview, selection and exact source, then complete the surviving action with literal model/history expectations. A positive drawing path verifies current Choose/Begin, Restart and a completed connection. The [admission report](prerelease37-connection-admission-20260924.md) distinguishes this contribution's validation from combined-source acceptance and records the subsequently identified Route context/selected-edge correction.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Captured Connection and Route admission — prerelease 37
|
||||
|
||||
Branch contribution by Codex-9e8r6t; release coordination is [issue 37](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/37). This is not acceptance of the combined prerelease or its packaged executable. Linux builds are excluded from this prerelease by the user's current instruction.
|
||||
|
||||
## Behavior and source
|
||||
|
||||
Source `72b1bb7aa723128f57b407f4dc55e55c3bc34818` adds early admission for captured Connection Endpoint/Restart events before shared Allocation retirement. A captured drawing event must match both the live route context and an extant drawing session. An old event therefore cannot retire a newer Arrange arm, active group press or preview. Current Choose/Begin and Restart retain their intentional tool-boundary behavior.
|
||||
|
||||
Successor source `6f247af795b7bec1818c588b2dabf203f8f68d8a` also admits captured Route Commit/CommitAttachment before retirement. Their captured context and changed edge must match the current context and selected edge. There is no invented route-session requirement. Selecting a placement group does not necessarily advance the canvas context, so context equality alone is insufficient. Stale route commits now preserve existing feedback instead of reaching the former late cancellation diagnostic. Current route edits and unscoped route commands retain their existing handlers.
|
||||
|
||||
The three Connection and six Route regression cases protect ownership, selection, preview/Document identity, complete model/source state, and exact Undo/Redo history. Positive cases prove current drawing and route edits remain usable. Context and selected-edge predicates have separate discriminating cases. The retained group movement uses literal expected placements and saved pin `(1100, 400)`, independently of the movement planner.
|
||||
|
||||
## Initial Connection validation
|
||||
|
||||
All successful rows below captured the same 755 inputs and were compared byte-for-byte with every relevant Git blob of source `72b1bb7`. Windows GNU LLVM; Rust/Cargo 1.98.1; one build job; incremental compilation disabled. The [portable package](prerelease37-connection-evidence-20260924/checks/README.md) retains exact commands, environment, source capsules, streams and source-proof receipts.
|
||||
|
||||
| Captured run | Result |
|
||||
| --- | --- |
|
||||
| connection-focused-2 | 3 passed, 0 failed |
|
||||
| connection-regressions-1 | 23 passed, 0 failed |
|
||||
| allocation-regressions-1 | 45 passed, 0 failed |
|
||||
| connection-format-2 | `cargo fmt --all -- --check`, exit 0 |
|
||||
| connection-lint-1 | `cargo clippy -p oa-desktop --all-targets -- -D warnings`, exit 0 |
|
||||
|
||||
The first focused/format attempts at `4421730` failed because the nested test module needed an explicit relative path; no tests ran in that attempt. The first focused filter also named the wrong Rust module path. Both failures are preserved. The corrected registration and actual `connection_lifetime` filter passed in the second attempts; independent review also added a test isolating the live-context predicate.
|
||||
|
||||
Initial archive SHA-256: `30087db61aa4e045a522cf9db56d6b85b595012ebb6a1bb1d03bfb8bb213bd5c` (6,087,017 bytes). Standalone verification and selfcheck passed: seven runs, two retained failures, five source proofs, original-byte equality and eleven corruption-rejection checks. Archive consistency is not a fresh test run or an authenticated build.
|
||||
|
||||
## Route successor validation status at source handoff
|
||||
|
||||
The six Route tests and guard have independent source review and were formatted before commit. Recorded route regressions, Allocation regressions, formatting and strict Clippy are running against `6f247af`; their results are not yet claimed here. This source is made available so the release owner can perform the combined checks without waiting for duplicate local code generation. A subsequent evidence update must name the tested source and results before this contribution is treated as locally validated.
|
||||
|
||||
## Reviews and scope
|
||||
|
||||
- [Drawing/Allocation integration seams](prerelease37-allocation-seams-20260924.md): actual branch/candidate inspection, including the required Menu composition.
|
||||
- [Connection guard review](prerelease37-connection-evidence-20260924/reviews/connection-envelope-review.md): initial evaluator finding and addressed readback.
|
||||
- [Route guard review](prerelease37-connection-evidence-20260924/reviews/route-envelope-review.md): same-context selection defect and addressed readback.
|
||||
- [Candidate Text Apply review](prerelease37-connection-evidence-20260924/reviews/candidate-text-review.md): scoped source evidence, not execution.
|
||||
|
||||
This narrow contribution has no fresh GUI build, mounted native reproduction or packaged acceptance claim. Earlier [Allocation native evidence](allocation-group-app-20260924.md) remains bound to `495464e`. The release owner's final integrated source, workspace gates, native tests and separate-machine downloaded-package launch are distinct acceptance steps. Preserved Scenario group work is excluded.
|
||||
@@ -0,0 +1,2 @@
|
||||
# Preserve measured package files and historical review bytes verbatim.
|
||||
* -text whitespace=cr-at-eol
|
||||
@@ -0,0 +1,24 @@
|
||||
# Prerelease 37 Connection admission captured-gate evidence tools
|
||||
|
||||
Adapted from the reviewed Allocation prepare/verify/selfcheck tools. Read-only preparation consumes explicitly selected **closed** runs in CHECKOUT/.local/prerelease37-gates. No Cargo, source execution, checkout, GUI, network or archived helper execution occurs.
|
||||
|
||||
```powershell
|
||||
python -B prepare.py --root CHECKOUT --runs connection-focused-1 connection-format-1 --check-only
|
||||
```
|
||||
|
||||
Keep failed connection-focused-1 and connection-format-1: verification preserves their recorded failures rather than converting archive consistency into test success. The example selects those two historical failures. Later closed Connection admission runs can be explicitly listed with the same interface; the final package manifest is authoritative for its exact inventory. No implicit discovery includes active directories.
|
||||
|
||||
To create a portable package, replace --check-only with `--output NEW_DIRECTORY`. Existing output refuses. The exact seven raw files are required, with only `source-proof.json` accepted as an optional eighth file. No raw byte normalization or receipt rewriting occurs. Maps and nested input ZIPs deduplicate by SHA-256; other logical raw files keep their names. All original helper bytes, failed-run streams and optional proof bytes remain archived. The prerelease37 Connection admission manifest kind distinguishes this evidence from Allocation and Process packages.
|
||||
|
||||
The gate.py and source-proof.py helper catalog is preserved. Every gate's recorded helper must match available bytes; use repeatable `--helper PATH` for older captures. If a proof exists, its exact comparator bytes must be available at the default `.local/process-source-evidence-tools/verify.py` or a repeatable `--comparator PATH`. They are archived under their content hash and never executed.
|
||||
|
||||
An optional source proof is independently pinned in the manifest with its full named commit. Verification binds it to the enclosing run, exact result receipt, captured map/count, completed gate time, status and comparator bytes/hash. Captured HEAD is preserved as historical metadata and is not substituted for the named source commit. This packager checks historical proof consistency; it does **not** repeat Git blob comparison. A source proof on a failed gate is source-identity evidence only, never a passing-test claim.
|
||||
|
||||
```powershell
|
||||
python -B PACKAGE/verify.py
|
||||
python -B selfcheck.py --root CHECKOUT --runs connection-focused-1 connection-format-1 --output TOOLS/NEW_SELFCHECK_DIRECTORY
|
||||
```
|
||||
|
||||
The verifier checks pins, exact ZIP inventory, process/start/result relationships, streams, command/exit identities, source capsules, vendor inventory and Cargo artifact relationships. Selfchecks compare packaged raw bytes to originals, reject existing output, and mutate only in-memory copies to test stream/helper/exit/capsule/proof corruption. The package contains tools, manifest, files.json and evidence.zip; selfcheck is a preparation utility, not packaged runtime input.
|
||||
|
||||
Limits: internal consistency is not external authentication, hermetic build proof, native usability, a new source comparison or production acceptance. Review exact run results and source identity separately. Run with `python -B` to avoid incidental bytecode writes.
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"prepare.py": {
|
||||
"bytes": 6319,
|
||||
"sha256": "ece8199911549266ee8ff6a3cd5636534cc13f15315fdce8838e269071a14383"
|
||||
},
|
||||
"verify.py": {
|
||||
"bytes": 11451,
|
||||
"sha256": "87e29134dd04f38040ed7be0fb36954ef6c5873ff7fe844217ce72fd1aa3f92f"
|
||||
},
|
||||
"README.md": {
|
||||
"bytes": 3287,
|
||||
"sha256": "cfa094bdf820db71002e84a0617ed86f4e2de2e693809f308ff02b6a80a21d07"
|
||||
},
|
||||
"manifest.json": {
|
||||
"bytes": 18809,
|
||||
"sha256": "4b5b371bfe6761b6183932793e37999f5082ebeb13beca7e86e5f5bcd850ea3e"
|
||||
},
|
||||
"evidence.zip": {
|
||||
"bytes": 6087017,
|
||||
"sha256": "30087db61aa4e045a522cf9db56d6b85b595012ebb6a1bb1d03bfb8bb213bd5c"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"kind": "prerelease37-connection-admission-cargo-gates",
|
||||
"runs": {
|
||||
"connection-focused-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"allocation_drag_tests::group::connection_lifetime",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "44217307f9194682d65babf883be87fdb709e4c0",
|
||||
"exitCode": 101,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"connection-format-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"fmt",
|
||||
"--all",
|
||||
"--",
|
||||
"--check"
|
||||
],
|
||||
"head": "44217307f9194682d65babf883be87fdb709e4c0",
|
||||
"exitCode": 1,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py"
|
||||
},
|
||||
"connection-focused-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"connection_lifetime",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 599,
|
||||
"sha256": "9828a732394e336fa44e2fd175ae963dcbd49fb91e40f973d12e2bdd2aef1b49"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"connection-format-2": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"fmt",
|
||||
"--all",
|
||||
"--",
|
||||
"--check"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 598,
|
||||
"sha256": "669373dcd72740739d45b132f633b4ddb05ac50447d8dcfd32ea726d213496fd"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"allocation-regressions-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"app::allocation_drag::tests",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 603,
|
||||
"sha256": "ab424d2c2a8d557dc3c607092061458bcc5035a70aa618f3421610efec29e3ae"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"connection-regressions-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"test",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--bin",
|
||||
"arcadia-studio",
|
||||
"--message-format=json",
|
||||
"connection",
|
||||
"--",
|
||||
"--test-threads=1"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 603,
|
||||
"sha256": "cadd10c80770f167aebd72023846c8991291979ca34139c65109de25c03714cc"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
},
|
||||
"connection-lint-1": {
|
||||
"command": [
|
||||
"cargo",
|
||||
"clippy",
|
||||
"-p",
|
||||
"oa-desktop",
|
||||
"--all-targets",
|
||||
"--message-format=json",
|
||||
"--",
|
||||
"-D",
|
||||
"warnings"
|
||||
],
|
||||
"head": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"exitCode": 0,
|
||||
"inputCount": 755,
|
||||
"helper": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"sourceProof": {
|
||||
"commit": "72b1bb7aa723128f57b407f4dc55e55c3bc34818",
|
||||
"pin": {
|
||||
"bytes": 596,
|
||||
"sha256": "51d5d4915efc0e272062844638f529624319b10ff5885fb330731b99af2377b9"
|
||||
},
|
||||
"comparator": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py"
|
||||
}
|
||||
}
|
||||
},
|
||||
"captureHelpers": {
|
||||
"gate.py": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"source-proof.py": "helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py"
|
||||
},
|
||||
"logicalFiles": {
|
||||
"cargo/allocation-regressions-1/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/allocation-regressions-1/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/allocation-regressions-1/process.json": "cargo/allocation-regressions-1/process.json",
|
||||
"cargo/allocation-regressions-1/result.json": "cargo/allocation-regressions-1/result.json",
|
||||
"cargo/allocation-regressions-1/source-proof.json": "cargo/allocation-regressions-1/source-proof.json",
|
||||
"cargo/allocation-regressions-1/started.json": "cargo/allocation-regressions-1/started.json",
|
||||
"cargo/allocation-regressions-1/stderr.log": "cargo/allocation-regressions-1/stderr.log",
|
||||
"cargo/allocation-regressions-1/stdout.log": "cargo/allocation-regressions-1/stdout.log",
|
||||
"cargo/connection-focused-1/inputs.json": "capsules/331c184edd48b4616a43c353fead9a0aa15b262e8ba0f1d73813bea3451e90bf.json",
|
||||
"cargo/connection-focused-1/inputs.zip": "capsules/f1505cd1396b5662d904db4e13f36d43dbc50bd7243b02973c9d5dd7a8a9799a.zip",
|
||||
"cargo/connection-focused-1/process.json": "cargo/connection-focused-1/process.json",
|
||||
"cargo/connection-focused-1/result.json": "cargo/connection-focused-1/result.json",
|
||||
"cargo/connection-focused-1/started.json": "cargo/connection-focused-1/started.json",
|
||||
"cargo/connection-focused-1/stderr.log": "cargo/connection-focused-1/stderr.log",
|
||||
"cargo/connection-focused-1/stdout.log": "cargo/connection-focused-1/stdout.log",
|
||||
"cargo/connection-focused-2/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/connection-focused-2/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/connection-focused-2/process.json": "cargo/connection-focused-2/process.json",
|
||||
"cargo/connection-focused-2/result.json": "cargo/connection-focused-2/result.json",
|
||||
"cargo/connection-focused-2/source-proof.json": "cargo/connection-focused-2/source-proof.json",
|
||||
"cargo/connection-focused-2/started.json": "cargo/connection-focused-2/started.json",
|
||||
"cargo/connection-focused-2/stderr.log": "cargo/connection-focused-2/stderr.log",
|
||||
"cargo/connection-focused-2/stdout.log": "cargo/connection-focused-2/stdout.log",
|
||||
"cargo/connection-format-1/inputs.json": "capsules/331c184edd48b4616a43c353fead9a0aa15b262e8ba0f1d73813bea3451e90bf.json",
|
||||
"cargo/connection-format-1/inputs.zip": "capsules/f1505cd1396b5662d904db4e13f36d43dbc50bd7243b02973c9d5dd7a8a9799a.zip",
|
||||
"cargo/connection-format-1/process.json": "cargo/connection-format-1/process.json",
|
||||
"cargo/connection-format-1/result.json": "cargo/connection-format-1/result.json",
|
||||
"cargo/connection-format-1/started.json": "cargo/connection-format-1/started.json",
|
||||
"cargo/connection-format-1/stderr.log": "cargo/connection-format-1/stderr.log",
|
||||
"cargo/connection-format-1/stdout.log": "cargo/connection-format-1/stdout.log",
|
||||
"cargo/connection-format-2/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/connection-format-2/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/connection-format-2/process.json": "cargo/connection-format-2/process.json",
|
||||
"cargo/connection-format-2/result.json": "cargo/connection-format-2/result.json",
|
||||
"cargo/connection-format-2/source-proof.json": "cargo/connection-format-2/source-proof.json",
|
||||
"cargo/connection-format-2/started.json": "cargo/connection-format-2/started.json",
|
||||
"cargo/connection-format-2/stderr.log": "cargo/connection-format-2/stderr.log",
|
||||
"cargo/connection-format-2/stdout.log": "cargo/connection-format-2/stdout.log",
|
||||
"cargo/connection-lint-1/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/connection-lint-1/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/connection-lint-1/process.json": "cargo/connection-lint-1/process.json",
|
||||
"cargo/connection-lint-1/result.json": "cargo/connection-lint-1/result.json",
|
||||
"cargo/connection-lint-1/source-proof.json": "cargo/connection-lint-1/source-proof.json",
|
||||
"cargo/connection-lint-1/started.json": "cargo/connection-lint-1/started.json",
|
||||
"cargo/connection-lint-1/stderr.log": "cargo/connection-lint-1/stderr.log",
|
||||
"cargo/connection-lint-1/stdout.log": "cargo/connection-lint-1/stdout.log",
|
||||
"cargo/connection-regressions-1/inputs.json": "capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json",
|
||||
"cargo/connection-regressions-1/inputs.zip": "capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip",
|
||||
"cargo/connection-regressions-1/process.json": "cargo/connection-regressions-1/process.json",
|
||||
"cargo/connection-regressions-1/result.json": "cargo/connection-regressions-1/result.json",
|
||||
"cargo/connection-regressions-1/source-proof.json": "cargo/connection-regressions-1/source-proof.json",
|
||||
"cargo/connection-regressions-1/started.json": "cargo/connection-regressions-1/started.json",
|
||||
"cargo/connection-regressions-1/stderr.log": "cargo/connection-regressions-1/stderr.log",
|
||||
"cargo/connection-regressions-1/stdout.log": "cargo/connection-regressions-1/stdout.log",
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": "comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py",
|
||||
"helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py": "helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py",
|
||||
"helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py": "helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py"
|
||||
},
|
||||
"members": {
|
||||
"capsules/14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989.zip": {
|
||||
"bytes": 3040776,
|
||||
"sha256": "14cd46c42ec90ffb7ff40898fbc06f85f0aed4d75ec164e6250c763c123c5989"
|
||||
},
|
||||
"capsules/331c184edd48b4616a43c353fead9a0aa15b262e8ba0f1d73813bea3451e90bf.json": {
|
||||
"bytes": 126555,
|
||||
"sha256": "331c184edd48b4616a43c353fead9a0aa15b262e8ba0f1d73813bea3451e90bf"
|
||||
},
|
||||
"capsules/a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901.json": {
|
||||
"bytes": 126555,
|
||||
"sha256": "a85e81534e4db5ddf3f677e86ad875f6b1e0ae184aa962bdfae40219a2a48901"
|
||||
},
|
||||
"capsules/f1505cd1396b5662d904db4e13f36d43dbc50bd7243b02973c9d5dd7a8a9799a.zip": {
|
||||
"bytes": 3040660,
|
||||
"sha256": "f1505cd1396b5662d904db4e13f36d43dbc50bd7243b02973c9d5dd7a8a9799a"
|
||||
},
|
||||
"cargo/allocation-regressions-1/process.json": {
|
||||
"bytes": 220,
|
||||
"sha256": "248f4476bc996f5b32ea708308d13805134f95cdec2af5620275b73b6a46fc79"
|
||||
},
|
||||
"cargo/allocation-regressions-1/result.json": {
|
||||
"bytes": 3346,
|
||||
"sha256": "e44aef2b84c8c32f4345f8906a3abc17f7b8322e1acc98b01b20ee17a146972c"
|
||||
},
|
||||
"cargo/allocation-regressions-1/source-proof.json": {
|
||||
"bytes": 603,
|
||||
"sha256": "ab424d2c2a8d557dc3c607092061458bcc5035a70aa618f3421610efec29e3ae"
|
||||
},
|
||||
"cargo/allocation-regressions-1/started.json": {
|
||||
"bytes": 1612,
|
||||
"sha256": "e8d3bf4aca316e1fd070ca0f5219d46313a1dfd4c64b0b4860116e2c419a6bbc"
|
||||
},
|
||||
"cargo/allocation-regressions-1/stderr.log": {
|
||||
"bytes": 210,
|
||||
"sha256": "c39778722d7dd318acd13162c403db30bef8954168ec8ae5705a6699fcc95b46"
|
||||
},
|
||||
"cargo/allocation-regressions-1/stdout.log": {
|
||||
"bytes": 298709,
|
||||
"sha256": "511fde7af8ce47db8b5290c43eeb42863f444fba9f84aa471d5d13286270c54e"
|
||||
},
|
||||
"cargo/connection-focused-1/process.json": {
|
||||
"bytes": 242,
|
||||
"sha256": "8eb05916282ff44629bf72088ec44748ccec510ccd8f64412622bd791cc3d172"
|
||||
},
|
||||
"cargo/connection-focused-1/result.json": {
|
||||
"bytes": 2031,
|
||||
"sha256": "3377951f3175537f7010c069e0d8fa0810a8bb24082ee6416d71b47eba51fda8"
|
||||
},
|
||||
"cargo/connection-focused-1/started.json": {
|
||||
"bytes": 1634,
|
||||
"sha256": "a0427b0557de3bce3fa4371610368b5aba56d7bc77a721d9ee857df14cd61f7a"
|
||||
},
|
||||
"cargo/connection-focused-1/stderr.log": {
|
||||
"bytes": 166,
|
||||
"sha256": "ad39fc56f685f7f3c6a9b4a342abff1f19f9daad28c394673be0e3a2a1cf2523"
|
||||
},
|
||||
"cargo/connection-focused-1/stdout.log": {
|
||||
"bytes": 295416,
|
||||
"sha256": "270c8973742b7564cd8dca1a39f25c24802b53befacdc44679bd261182be3d35"
|
||||
},
|
||||
"cargo/connection-focused-2/process.json": {
|
||||
"bytes": 212,
|
||||
"sha256": "88825e3fb6ec06cfc30390f6c920c8ba138cd1cf318e7640b1cb22d28376bb97"
|
||||
},
|
||||
"cargo/connection-focused-2/result.json": {
|
||||
"bytes": 3338,
|
||||
"sha256": "68939477e4011a0de426adf0c78260ce4eed7d4618de3119b33f27f9257234ef"
|
||||
},
|
||||
"cargo/connection-focused-2/source-proof.json": {
|
||||
"bytes": 599,
|
||||
"sha256": "9828a732394e336fa44e2fd175ae963dcbd49fb91e40f973d12e2bdd2aef1b49"
|
||||
},
|
||||
"cargo/connection-focused-2/started.json": {
|
||||
"bytes": 1604,
|
||||
"sha256": "3ea15b713275aa4088f0ede9222f3e249106025ed3935c761a1e979e0810a1a6"
|
||||
},
|
||||
"cargo/connection-focused-2/stderr.log": {
|
||||
"bytes": 288,
|
||||
"sha256": "fe1a93a60511f726056a2b70353ff3d2ad9d63427b48b2c7355c5485e7a1dfb3"
|
||||
},
|
||||
"cargo/connection-focused-2/stdout.log": {
|
||||
"bytes": 293650,
|
||||
"sha256": "3c446459c6415db2cc1d9554507f278c2ad01aea6eb747525f60ed9e37f2d2e9"
|
||||
},
|
||||
"cargo/connection-format-1/process.json": {
|
||||
"bytes": 116,
|
||||
"sha256": "287fa4c950dfa20b2bd7dfc99914d07decbd7f3089ed6756aaa919bb8e26d3b7"
|
||||
},
|
||||
"cargo/connection-format-1/result.json": {
|
||||
"bytes": 1874,
|
||||
"sha256": "18ed79a8f6388bb93f199f79d77ddf3da7708054e0f41fcb9e44030911b8a087"
|
||||
},
|
||||
"cargo/connection-format-1/started.json": {
|
||||
"bytes": 1483,
|
||||
"sha256": "87bbb6011b68269fdab0472da3cfa074ccb561e63f1c9efa83ca81060f5b48dc"
|
||||
},
|
||||
"cargo/connection-format-1/stderr.log": {
|
||||
"bytes": 181,
|
||||
"sha256": "e6be01db877bb8d308543844eb96ef6c620e1b730b0313c4ab40dbd6fb859a4a"
|
||||
},
|
||||
"cargo/connection-format-1/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/connection-format-2/process.json": {
|
||||
"bytes": 116,
|
||||
"sha256": "55410bf9cc4f4b446b6aafd0782f234e756b3202952294f1ad52929de11f0646"
|
||||
},
|
||||
"cargo/connection-format-2/result.json": {
|
||||
"bytes": 1871,
|
||||
"sha256": "4f261156169ee53703ff784736792cf7ea593fb3f4f2004917aa0ad751d2e75e"
|
||||
},
|
||||
"cargo/connection-format-2/source-proof.json": {
|
||||
"bytes": 598,
|
||||
"sha256": "669373dcd72740739d45b132f633b4ddb05ac50447d8dcfd32ea726d213496fd"
|
||||
},
|
||||
"cargo/connection-format-2/started.json": {
|
||||
"bytes": 1483,
|
||||
"sha256": "6fad517a834063db7b5774bfb216f888ab76d46155fbbcc34504d7aad2188ae4"
|
||||
},
|
||||
"cargo/connection-format-2/stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/connection-format-2/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"cargo/connection-lint-1/process.json": {
|
||||
"bytes": 179,
|
||||
"sha256": "ac079286f4df14c1797fab2030004b75653392b8078839d84e383158ecee3b22"
|
||||
},
|
||||
"cargo/connection-lint-1/result.json": {
|
||||
"bytes": 1962,
|
||||
"sha256": "ec759bb78dbadb99893c728dc87dac8c4f2f3b3b6548378e570b694a950563d1"
|
||||
},
|
||||
"cargo/connection-lint-1/source-proof.json": {
|
||||
"bytes": 596,
|
||||
"sha256": "51d5d4915efc0e272062844638f529624319b10ff5885fb330731b99af2377b9"
|
||||
},
|
||||
"cargo/connection-lint-1/started.json": {
|
||||
"bytes": 1566,
|
||||
"sha256": "f3863de8411828d8db60a87a31539b2966e62b2c0aee92ada8e175595627e914"
|
||||
},
|
||||
"cargo/connection-lint-1/stderr.log": {
|
||||
"bytes": 137,
|
||||
"sha256": "3a455f3ba071f27a9429f649c9cb75fce90d0b054054388470b73aadc557a18d"
|
||||
},
|
||||
"cargo/connection-lint-1/stdout.log": {
|
||||
"bytes": 269521,
|
||||
"sha256": "d316d47efa31ef26264566ce7dbb29f008adc7eb0cd4035319c42c9d066e862e"
|
||||
},
|
||||
"cargo/connection-regressions-1/process.json": {
|
||||
"bytes": 203,
|
||||
"sha256": "44e467beac6aa66ef68330d6e550fdf1df4273efbb5224f7eaf3c78a4c5ee7d9"
|
||||
},
|
||||
"cargo/connection-regressions-1/result.json": {
|
||||
"bytes": 3328,
|
||||
"sha256": "7a8de66cdcca94a5884046f4342b9267250746cec53b3ef2a63368477e082a09"
|
||||
},
|
||||
"cargo/connection-regressions-1/source-proof.json": {
|
||||
"bytes": 603,
|
||||
"sha256": "cadd10c80770f167aebd72023846c8991291979ca34139c65109de25c03714cc"
|
||||
},
|
||||
"cargo/connection-regressions-1/started.json": {
|
||||
"bytes": 1595,
|
||||
"sha256": "861f1a81ef5da8af2afe68cf2115a3ba6fb0403bdb48e4d82870e0f3841400e5"
|
||||
},
|
||||
"cargo/connection-regressions-1/stderr.log": {
|
||||
"bytes": 210,
|
||||
"sha256": "8e1c02b183f8e9812aba2413bde57010fb6eb9e6756dfaa04cad275028710a54"
|
||||
},
|
||||
"cargo/connection-regressions-1/stdout.log": {
|
||||
"bytes": 295748,
|
||||
"sha256": "953e35f1d65016e036219ea6114893c013206522f96d44b6df44de50ed35caf1"
|
||||
},
|
||||
"comparators/7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a.py": {
|
||||
"bytes": 10094,
|
||||
"sha256": "7805832d5f563ab2d7a50dd3de700792b5d6f459c68f7b1d8bcfcc9800fa0c3a"
|
||||
},
|
||||
"helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py": {
|
||||
"bytes": 3827,
|
||||
"sha256": "1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20"
|
||||
},
|
||||
"helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py": {
|
||||
"bytes": 1399,
|
||||
"sha256": "58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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/prerelease37-gates"
|
||||
candidates = [gates / name for name in ("gate.py", "source-proof.py")] + helpers
|
||||
helper_bytes = [path.read_bytes() for path in candidates]
|
||||
comparator_paths = [root / ".local/process-source-evidence-tools/verify.py", *comparators]
|
||||
comparator_bytes = [path.read_bytes() for path in comparator_paths if path.is_file()]
|
||||
payload, logical, specs = {}, {}, {}
|
||||
def add(name, data):
|
||||
stored = ("capsules/" + pin(data)["sha256"] + Path(name).suffix
|
||||
if name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"} else name)
|
||||
need(stored not in payload or payload[stored] == data, "deduplication collision")
|
||||
payload[stored] = data
|
||||
logical[name] = stored
|
||||
helper_catalog = {}
|
||||
for name, data in zip(("gate.py", "source-proof.py"), helper_bytes):
|
||||
helper = "helpers/" + pin(data)["sha256"] + ".py"
|
||||
add(helper, data)
|
||||
helper_catalog[name] = helper
|
||||
for run in runs:
|
||||
folder = gates / run
|
||||
need(folder.is_dir(), "run directory missing: " + run)
|
||||
paths = list(folder.iterdir())
|
||||
names = {path.name for path in paths}
|
||||
need(all(path.is_file() and not path.is_symlink() for path in paths)
|
||||
and names in (RAW_FILES, RAW_FILES | {"source-proof.json"}), "exact closed raw files required: " + run)
|
||||
raw = {path.name: path.read_bytes() for path in paths}
|
||||
result = parsed(raw["result.json"])
|
||||
need("finish" in result and result["inputsUnchanged"] is True, "unfinished/changed run refused: " + run)
|
||||
matches = [data for data in helper_bytes if pin(data) == result["helper"]]
|
||||
need(bool(matches), "exact historical helper unavailable for " + run + "; supply --helper PATH")
|
||||
helper = "helpers/" + result["helper"]["sha256"] + ".py"
|
||||
add(helper, matches[0])
|
||||
for name, data in sorted(raw.items()):
|
||||
add("cargo/" + run + "/" + name, data)
|
||||
specs[run] = {key: result[key] for key in ("command", "head", "exitCode", "inputCount")}
|
||||
specs[run]["helper"] = helper
|
||||
if "source-proof.json" in raw:
|
||||
proof = parsed(raw["source-proof.json"])
|
||||
matches = [data for data in comparator_bytes if pin(data) == proof["comparator"]]
|
||||
need(bool(matches), "exact source comparator unavailable for " + run + "; supply --comparator PATH")
|
||||
comparator = "comparators/" + proof["comparator"]["sha256"] + ".py"
|
||||
add(comparator, matches[0])
|
||||
specs[run]["sourceProof"] = {"commit": proof["commit"], "pin": pin(raw["source-proof.json"]), "comparator": comparator}
|
||||
manifest = {"schema": 1, "kind": "prerelease37-connection-admission-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/prerelease37-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,38 @@
|
||||
Command: C:\Users\User\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe -B .local/prerelease37-evidence-tools/selfcheck.py --root . --runs connection-focused-1 connection-format-1 connection-focused-2 connection-format-2 allocation-regressions-1 connection-regressions-1 connection-lint-1 --output .local/prerelease37-evidence-tools/selfcheck-initial-publication
|
||||
Exit: 0
|
||||
|
||||
STDOUT
|
||||
connection-focused-1 recorded exit 101 inputs 755 executable records 0
|
||||
connection-format-1 recorded exit 1 inputs 755 executable records 0
|
||||
connection-focused-2 recorded exit 0 inputs 755 executable records 1
|
||||
connection-format-2 recorded exit 0 inputs 755 executable records 0
|
||||
allocation-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-lint-1 recorded exit 0 inputs 755 executable records 0
|
||||
connection-focused-1 recorded exit 101 inputs 755 executable records 0
|
||||
connection-format-1 recorded exit 1 inputs 755 executable records 0
|
||||
connection-focused-2 recorded exit 0 inputs 755 executable records 1
|
||||
connection-format-2 recorded exit 0 inputs 755 executable records 0
|
||||
allocation-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-lint-1 recorded exit 0 inputs 755 executable records 0
|
||||
PASS package readback: C:\ar-multiselect-9e8r6t\.local\prerelease37-evidence-tools\selfcheck-initial-publication {'runs': 7, 'recordedFailures': 2, 'logicalFiles': 57, 'storedMembers': 47, 'sourceCapsulePairs': 2, 'captureHelpers': {'gate.py': 'helpers/1368375b1fba0dfc50b77ea4f406dba71960a78317ce8f259d4c4a0254863a20.py', 'source-proof.py': 'helpers/58597566ae1cb5c4021ddc7e86635b76d127bdb63b5d3026c427771aae7e2119.py'}, 'sourceProofs': 5}
|
||||
connection-focused-1 recorded exit 101 inputs 755 executable records 0
|
||||
connection-format-1 recorded exit 1 inputs 755 executable records 0
|
||||
connection-focused-2 recorded exit 0 inputs 755 executable records 1
|
||||
connection-format-2 recorded exit 0 inputs 755 executable records 0
|
||||
allocation-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-lint-1 recorded exit 0 inputs 755 executable records 0
|
||||
connection-focused-1 recorded exit 101 inputs 755 executable records 0
|
||||
connection-format-1 recorded exit 1 inputs 755 executable records 0
|
||||
connection-focused-2 recorded exit 0 inputs 755 executable records 1
|
||||
connection-format-2 recorded exit 0 inputs 755 executable records 0
|
||||
allocation-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-regressions-1 recorded exit 0 inputs 755 executable records 1
|
||||
connection-lint-1 recorded exit 0 inputs 755 executable records 0
|
||||
PASS original raw/optional-proof/helper archive equality, existing-output refusal, and corruption rejection: ['raw stream/receipt mismatch', 'helper bytes/name mismatch', 'run/result exit mismatch', 'input archive mapping mismatch', 'missing closed receipt', 'source proof run relationship', 'source proof commit relationship', 'source proof result relationship', 'source proof inputCount relationship', 'missing declared optional source proof', 'source comparator bytes mismatch']
|
||||
Historical captured failures remain failures: 2
|
||||
No Cargo, archived code, or GUI executed.
|
||||
|
||||
STDERR
|
||||
@@ -0,0 +1,194 @@
|
||||
"""Offline consistency checks only; never execute or extract archived inputs."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import stat
|
||||
import zipfile
|
||||
|
||||
RAW_FILES = {"started.json", "process.json", "result.json", "stdout.log", "stderr.log", "inputs.json", "inputs.zip"}
|
||||
OUTER_FILES = {"prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip"}
|
||||
|
||||
|
||||
def need(condition, message):
|
||||
if not condition:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def pin(data):
|
||||
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def valid_pin(value):
|
||||
return (isinstance(value, dict) and set(value) == {"bytes", "sha256"}
|
||||
and type(value["bytes"]) is int and value["bytes"] >= 0
|
||||
and isinstance(value["sha256"], str)
|
||||
and re.fullmatch(r"[0-9a-f]{64}", value["sha256"]) is not None)
|
||||
|
||||
|
||||
def safe(name):
|
||||
return (isinstance(name, str) and bool(name) and "\\" not in name and ":" not in name
|
||||
and all(ord(char) >= 32 for char in name)
|
||||
and not PurePosixPath(name).is_absolute()
|
||||
and all(part not in ("", ".", "..") for part in name.split("/")))
|
||||
|
||||
|
||||
def run_name(name):
|
||||
return isinstance(name, str) and re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", name) is not None
|
||||
|
||||
|
||||
def parsed(data):
|
||||
def pairs(items):
|
||||
result = {}
|
||||
for key, value in items:
|
||||
need(key not in result, "duplicate JSON key: " + key)
|
||||
result[key] = value
|
||||
return result
|
||||
def constant(value):
|
||||
raise ValueError("nonfinite JSON constant: " + value)
|
||||
return json.loads(data, object_pairs_hook=pairs, parse_constant=constant)
|
||||
|
||||
|
||||
def inventory(archive, expected):
|
||||
names = archive.namelist()
|
||||
need(len(names) == len(set(names)) and set(names) == set(expected), "exact ZIP membership")
|
||||
for entry in archive.infolist():
|
||||
need(safe(entry.filename) and not entry.is_dir(), "unsafe ZIP member: " + entry.filename)
|
||||
need(not stat.S_ISLNK(entry.external_attr >> 16), "ZIP symlink: " + entry.filename)
|
||||
need(valid_pin(expected[entry.filename]), "invalid member pin: " + entry.filename)
|
||||
need(entry.file_size == expected[entry.filename]["bytes"], "ZIP recorded size: " + entry.filename)
|
||||
|
||||
|
||||
def ordered_times(*values):
|
||||
times = [datetime.fromisoformat(value) for value in values]
|
||||
return all(value.utcoffset() is not None for value in times) and times == sorted(times)
|
||||
|
||||
|
||||
def verify_data(manifest, read):
|
||||
"""Shared read-only verifier for packaged bytes or prepare's in-memory payload."""
|
||||
need(manifest["schema"] == 1 and manifest["kind"] == "prerelease37-connection-admission-cargo-gates", "manifest schema")
|
||||
runs, logical, stored = manifest["runs"], manifest["logicalFiles"], manifest["members"]
|
||||
helpers = manifest["captureHelpers"]
|
||||
need(isinstance(helpers, dict) and set(helpers) == {"gate.py", "source-proof.py"}, "capture helper names")
|
||||
need(all(isinstance(name, str) and re.fullmatch(r"helpers/[0-9a-f]{64}\.py", name)
|
||||
for name in helpers.values()), "capture helper mapping")
|
||||
need(isinstance(runs, dict) and bool(runs) and all(run_name(run) for run in runs), "explicit run inventory")
|
||||
need(all(safe(name) for name in logical) and all(safe(name) for name in stored), "unsafe manifest path")
|
||||
need(set(logical.values()) == set(stored), "unreferenced or absent stored members")
|
||||
for name, expected in stored.items():
|
||||
need(valid_pin(expected) and pin(read(name)) == expected, "stored member hash: " + name)
|
||||
expected_logical = {"cargo/" + run + "/" + name for run in runs for name in RAW_FILES}
|
||||
expected_logical.update(spec["helper"] for spec in runs.values())
|
||||
expected_logical.update(helpers.values())
|
||||
for run, spec in runs.items():
|
||||
if "sourceProof" in spec:
|
||||
proof_spec = spec["sourceProof"]
|
||||
need(set(proof_spec) == {"commit", "pin", "comparator"}, run + ": exact source proof spec")
|
||||
need(isinstance(proof_spec["commit"], str) and re.fullmatch(r"[0-9a-f]{40}", proof_spec["commit"]), run + ": named full source commit")
|
||||
need(valid_pin(proof_spec["pin"]), run + ": source proof pin")
|
||||
need(isinstance(proof_spec["comparator"], str) and re.fullmatch(r"comparators/[0-9a-f]{64}\.py", proof_spec["comparator"]), run + ": comparator path")
|
||||
expected_logical.update({"cargo/" + run + "/source-proof.json", proof_spec["comparator"]})
|
||||
need(set(logical) == expected_logical, "exact logical raw/helper inventory")
|
||||
for name, destination in logical.items():
|
||||
if name.startswith("cargo/") and name.rsplit("/", 1)[1] in {"inputs.json", "inputs.zip"}:
|
||||
expected = "capsules/" + stored[destination]["sha256"] + PurePosixPath(name).suffix
|
||||
need(destination == expected, "input capsule mapping: " + name)
|
||||
else:
|
||||
need(destination == name, "only input maps/archives may be deduplicated: " + name)
|
||||
def raw(name):
|
||||
return read(logical[name])
|
||||
for name, helper in helpers.items():
|
||||
need(helper == "helpers/" + pin(raw(helper))["sha256"] + ".py", "named helper hash: " + name)
|
||||
seen_capsules = set()
|
||||
for run, spec in runs.items():
|
||||
prefix = "cargo/" + run + "/"
|
||||
start = parsed(raw(prefix + "started.json"))
|
||||
process = parsed(raw(prefix + "process.json"))
|
||||
result = parsed(raw(prefix + "result.json"))
|
||||
required_start = {"start", "command", "cwd", "head", "scope", "inputs", "archive",
|
||||
"inputCount", "environment", "helper", "cargo", "rustc"}
|
||||
need(required_start <= start.keys(), run + ": incomplete start receipt")
|
||||
need(all(result.get(key) == value for key, value in start.items()), run + ": start/result relationship")
|
||||
command = result["command"]
|
||||
need(isinstance(command, list) and command and command[0] == "cargo"
|
||||
and all(isinstance(arg, str) for arg in command), run + ": Cargo command")
|
||||
need(command == process["command"] == spec["command"], run + ": command relationship")
|
||||
need(result["head"] == spec["head"] and re.fullmatch(r"(?:[0-9a-f]{40}|[0-9a-f]{64})", result["head"]), run + ": HEAD")
|
||||
need(type(result["exitCode"]) is int and result["exitCode"] == spec["exitCode"], run + ": recorded exit")
|
||||
need(result["inputsUnchanged"] is True, run + ": changed inputs refused")
|
||||
need(type(result["seconds"]) in (int, float) and math.isfinite(result["seconds"])
|
||||
and result["seconds"] >= 0, run + ": duration")
|
||||
need(type(process["pid"]) is int and process["pid"] > 0, run + ": process identity")
|
||||
need(ordered_times(start["start"], process["observed"], result["finish"]), run + ": completed timestamp order")
|
||||
for field, filename in [("inputs", "inputs.json"), ("archive", "inputs.zip"), ("stdout", "stdout.log"), ("stderr", "stderr.log")]:
|
||||
need(result[field] == pin(raw(prefix + filename)), run + ": " + field + " pin")
|
||||
need(valid_pin(result["helper"]) and result["helper"] == pin(raw(spec["helper"])), run + ": helper pin")
|
||||
need(spec["helper"] == "helpers/" + result["helper"]["sha256"] + ".py", run + ": helper mapping")
|
||||
inputs = parsed(raw(prefix + "inputs.json"))
|
||||
need(isinstance(inputs, dict) and all(safe(name) for name in inputs), run + ": safe input inventory")
|
||||
need(type(result["inputCount"]) is int and len(inputs) == result["inputCount"] == spec["inputCount"], run + ": input counts")
|
||||
if "sourceProof" in spec:
|
||||
proof_spec = spec["sourceProof"]
|
||||
proof_bytes = raw(prefix + "source-proof.json")
|
||||
proof = parsed(proof_bytes)
|
||||
need(pin(proof_bytes) == proof_spec["pin"], run + ": source proof pinned bytes")
|
||||
need(proof["run"] == run and proof["commit"] == proof_spec["commit"], run + ": proof run/commit identity")
|
||||
need(proof["status"] == "PASS exact captured input inventory and Git blob bytes", run + ": source proof status")
|
||||
need(proof["inputCount"] == len(inputs) and proof["inputMap"] == result["inputs"], run + ": proof input relationship")
|
||||
need(proof["result"] == pin(raw(prefix + "result.json")), run + ": proof result relationship")
|
||||
need(ordered_times(result["finish"], proof["verified"]), run + ": proof time after closed gate")
|
||||
comparator = proof_spec["comparator"]
|
||||
need(valid_pin(proof["comparator"]) and pin(raw(comparator)) == proof["comparator"], run + ": comparator bytes")
|
||||
need(comparator == "comparators/" + proof["comparator"]["sha256"] + ".py", run + ": comparator identity")
|
||||
need(sum(name.startswith("crates/vendor/iced_widget/") for name in inputs) == 71, run + ": vendor count")
|
||||
capsule = (logical[prefix + "inputs.json"], logical[prefix + "inputs.zip"])
|
||||
if capsule not in seen_capsules:
|
||||
with zipfile.ZipFile(io.BytesIO(raw(prefix + "inputs.zip"))) as source:
|
||||
inventory(source, inputs)
|
||||
for name, expected in inputs.items():
|
||||
need(pin(source.read(name)) == expected, run + ": source hash " + name)
|
||||
seen_capsules.add(capsule)
|
||||
artifacts = []
|
||||
for line in raw(prefix + "stdout.log").decode("utf-8", errors="replace").splitlines():
|
||||
try:
|
||||
value = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if isinstance(value, dict) and value.get("reason") == "compiler-artifact" and value.get("executable"):
|
||||
artifacts.append(value)
|
||||
need(isinstance(result["artifacts"], list), run + ": artifact records")
|
||||
need(artifacts == [record["cargo"] for record in result["artifacts"]], run + ": Cargo JSON artifact relationship")
|
||||
for record in result["artifacts"]:
|
||||
need(set(record) == {"cargo", "readback"}
|
||||
and (record["readback"] is None or valid_pin(record["readback"])), run + ": artifact readback pin")
|
||||
print(run, "recorded exit", result["exitCode"], "inputs", len(inputs), "executable records", len(artifacts))
|
||||
return {"runs": len(runs), "recordedFailures": sum(spec["exitCode"] != 0 for spec in runs.values()),
|
||||
"logicalFiles": len(logical), "storedMembers": len(stored), "sourceCapsulePairs": len(seen_capsules),
|
||||
"captureHelpers": helpers, "sourceProofs": sum("sourceProof" in spec for spec in runs.values())}
|
||||
|
||||
|
||||
def verify_package(root):
|
||||
files = parsed((root / "files.json").read_bytes())
|
||||
need(set(files) == OUTER_FILES, "outer file inventory")
|
||||
for name, expected in files.items():
|
||||
need(valid_pin(expected) and pin((root / name).read_bytes()) == expected, "outer hash: " + name)
|
||||
manifest = parsed((root / "manifest.json").read_bytes())
|
||||
with zipfile.ZipFile(root / "evidence.zip") as archive:
|
||||
inventory(archive, manifest["members"])
|
||||
return verify_data(manifest, archive.read)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("package", nargs="?", type=Path, default=Path(__file__).resolve().parent)
|
||||
args = parser.parse_args()
|
||||
print("PASS offline consistency:", verify_package(args.package))
|
||||
print("Historical receipts only; no source/binary execution or native acceptance claim.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
[
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "harness/prerelease-integrated-source-20260924.md",
|
||||
"gitBlob": "898bf45550a8c8c449c6b1989cabbf534575a27f",
|
||||
"bytes": 5288,
|
||||
"sha256": "8e6eb6333c47850dff04185972c8c93ce5ddf4b0f2b591bc3d4fc59a6b878e48"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "harness/reviews/prerelease37-integration-20260924.md",
|
||||
"gitBlob": "dc6e6b6036ab587d7e7cec59b8c762ac404efc1b",
|
||||
"bytes": 8866,
|
||||
"sha256": "4f13043fc653d50a18e043438d919cdf32db63454e048a4c61ab3b4a473808ea"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app.rs",
|
||||
"gitBlob": "646b0415a156b3a4e8074847c5b2dadfea24b27c",
|
||||
"bytes": 292471,
|
||||
"sha256": "d2296118db22fc51554273a7ec84421c83793b769b87c65a85a2a6d61c7a16b5"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/allocation_drag.rs",
|
||||
"gitBlob": "b6c329496c1c935defee6f6cd85c281c3556e4a2",
|
||||
"bytes": 42262,
|
||||
"sha256": "26934501d8a95a6d196bdf477eed00c68545c98921744b181613c0b1f9d95c36"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/connection_tool.rs",
|
||||
"gitBlob": "10910961860ebf528edf4b159b59a62c0a500d75",
|
||||
"bytes": 28840,
|
||||
"sha256": "43cb9fb4c0da31a28c2a59ef8bbf5a66989b4ab58cb532a237e9595b34ebe5eb"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/connection_allocation_tests.rs",
|
||||
"gitBlob": "2f8c93e6154db2421b9f39b5e045866875ff7612",
|
||||
"bytes": 20135,
|
||||
"sha256": "33e8b7d238240f40be0fe64e796f8af4d8d3e6945315e07b70bc553fe45ea15b"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/diagram/multiselect.rs",
|
||||
"gitBlob": "5f95b92c50ab4b0e521b801a9f74b9ed223dbaf9",
|
||||
"bytes": 11124,
|
||||
"sha256": "8cb5d7866fa092ecbcf7355ffc41bd060666d271fccd0bdafea68ae442cde78f"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/multiselect_tests.rs",
|
||||
"gitBlob": "0c71dec48ce6ad5e625f941c4b2638bb557bcc72",
|
||||
"bytes": 31304,
|
||||
"sha256": "09eea1403af1e4804dcb3ab863ad110095f451d9b1957dcdbf859fe7dd833abd"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/allocation_drag_tests/navigation_union.rs",
|
||||
"gitBlob": "7bce1ba559289986f9c92376669571b62d490e79",
|
||||
"bytes": 7247,
|
||||
"sha256": "9962cd4e11efb586b55c9a5f78b9df26c4316b6df3a0b01fd3587ee89e4c5900"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/process_multiselect/tests.rs",
|
||||
"gitBlob": "dec1498584c97b38317c6397ed937d1e62512cee",
|
||||
"bytes": 42929,
|
||||
"sha256": "22ead59676c53877d58339e7fe31ba548231610f8c12dcb1f07ef550e0680d90"
|
||||
},
|
||||
{
|
||||
"commit": "6ae75fb2ccaae16c29004444815ddeaeab2e75ac",
|
||||
"path": "crates/oa-desktop/src/app/allocation_drag_tests/group.rs",
|
||||
"gitBlob": "eb894abbfe0db1687999be72bb10a73d26268610",
|
||||
"bytes": 34325,
|
||||
"sha256": "8bf8d57dbc07978934ab46f39058c71061059f232c6868394914a74f18f39c59"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,41 @@
|
||||
# Candidate completed-Text-Apply review
|
||||
|
||||
2026-09-24. Bounded read-only review of fetched files for candidate `6ae75fb2ccaae16c29004444815ddeaeab2e75ac`, under `.local/prerelease37/source-6ae75fb2ccaae16c29004444815ddeaeab2e75ac/`. This is a nine-file partial source retrieval with per-file commit/blob/hash receipts, not a complete checkout. Identity verification was reported by the parent; this review read the receipts and source without performing another fetch. No Cargo, format/test/build gate, Git/Gitea, GUI or production mutation was performed. This report is the only file written.
|
||||
|
||||
## Result
|
||||
|
||||
No new material production defect found in the examined completed-Text-Apply path or dispatch ordering. The ordinary/State test is a meaningful concrete integration evaluator. **The retrieved Allocation `navigation_union.rs` does not contain a completed Text Apply evaluator**, so the two files must not be described as proving that workflow across all group adapters.
|
||||
|
||||
## What the ordinary/State test establishes
|
||||
|
||||
`crates/oa-desktop/src/app/multiselect_tests.rs:197` captures the original group, navigates through the real Text page, selects the declaration's actual source file, edits the source editor using SelectAll/Paste, and dispatches `Message::ApplySource`. It asserts that the dirty edit initially leaves the committed Document unchanged, then asserts a literal full expected Project and exact edited SourceBundle after Apply. The expected change is constructed independently as one element name replacement; unrelated diagrams/routes and opposite-layer records remain part of the complete-model oracle.
|
||||
|
||||
Candidate `app.rs:4027` dispatches to `apply_source`; `app.rs:901` compiles the source workspace, calls `document.apply_compiled`, and synchronizes only after success. Thus this test does not substitute an ordinary model command or manually increment a revision for completed Text Apply.
|
||||
|
||||
The test returns to the ordinary or State surface, recreates the same exact membership and primary, verifies that the fresh snapshot is admitted, and dispatches old Move/Hide/Collapse/Refuse events. It checks complete Project/source/revision, the fresh selection snapshot, legacy inspector/placement identity, camera epochs/geometry, draft state, feedback, and the Allocation Arrange-arm sentinel. The early envelope at candidate `app.rs:1354` handles both DiagramSelection and StateDiagram::Selection before shared Allocation retirement at `app.rs:1535` and the ordinary update-inner side effects. The tests dispatch the shared DiagramSelection form even for State; this is a valid common App path, but is not a mounted State-canvas event test.
|
||||
|
||||
The literal Undo/Redo assertions at `multiselect_tests.rs:329` establish one Text Apply history entry and no hidden second edit from the stale packets. Source/model compilation is checked after Apply. The test deliberately distinguishes a clean completed source transaction from rejection caused merely by a still-dirty source draft.
|
||||
|
||||
This is not an isolated proof that the revision field alone rejects the packet: Page transitions and selection regeneration also make its lifetime stale. That is appropriate to the realistic workflow; the same file separately has field-specific envelope tests at line 438. Do not interpret the new test alone as mutation coverage for every Snapshot equality field. Also, its manually set Arrange arm is a side-effect sentinel, not evidence of retaining a real post-Text Allocation group press or preview.
|
||||
|
||||
## Allocation coverage boundary and next discriminating case
|
||||
|
||||
The fetched `app/allocation_drag_tests/navigation_union.rs` ends with two existing tests: `stale_gesture_packets_preserve_navigation_and_a_new_live_cancel_still_works` at line 104 and `old_chooser_envelopes_preserve_a_current_canvas_gesture` at line 170. They exercise actual captured Allocation packets across navigation/chooser lifetime changes, retain pending reveal/current gesture ownership, and prove that a current Cancel still works. Neither edits source nor calls ApplySource.
|
||||
|
||||
For the requested completed-Text boundary, a distinct evaluator still needs to capture old Allocation input, perform an actual source edit and successful Apply, return to the Allocation view and establish a fresh group owner/preview, then deliver old Preview/Release/Cancel/Refused packets. Require the newer token/preview/selection/model/source/history to remain exact and let the fresh group gesture complete with an independently expected whole Project and one additional history entry. The candidate's own `harness/reviews/prerelease37-integration-20260924.md` already lists this extension as pending; this review does not reopen a claimed completed test.
|
||||
|
||||
Treat old Press separately: candidate `app.rs:1448` intentionally routes a rejected Press to its diagnostic handler before shared boundaries, and `navigation_union.rs:119` expects `The view changed. Start this drag again.` while preserving ownership/navigation. A blanket unchanged-status oracle for old Press would contradict the current explicit contract. Other stale Allocation packets use the early `allocation_event_envelope` gate (`app.rs:1444`, `app/allocation_drag.rs:768`).
|
||||
|
||||
## Limits
|
||||
|
||||
The fetched set does not include all called selection/Document/source-workspace implementation modules or the complete combined test tree. This review therefore assesses the concrete fetched tests and visible dispatch order, supplemented only by the parent's stated retrieval identity. It establishes no combined compilation, passing tests, native behavior, packaged launch, full-source integration or prerelease acceptance. The separately reviewed Endpoint/Restart envelope correction is outside this task and is not re-evaluated here.
|
||||
|
||||
## Parent follow-up: additional identified Text Apply cases
|
||||
|
||||
The release owner identified two additional files in issue37 comment1194. The parent retrieved both at the same exact candidate6ae75fb and independently verified their API path/size and Git blob identities; the accompanying candidate-source-receipts.json now records eleven files. This resolves the earlier retrieval coverage boundary; it was not evidence of missing implementation.
|
||||
|
||||
`app/process_multiselect/tests.rs:148` actually edits and applies the source declaration, compares a literal complete Project and exact SourceBundle, returns to Process, establishes the same membership/primary with a fresh snapshot, and proves the intended current reorder would change the model. Replaying the old reorder must preserve the new state and leave one history entry belonging only to Text Apply. Both OA and SA are exercised.
|
||||
|
||||
`app/allocation_drag_tests/group.rs:194` captures an actual old group press, completes Text Apply with the literal model/source oracle, returns to the Allocation diagram and creates a new actual assignment preview. Old Press and Release must preserve the current press/preview identities, selection, camera, epochs, draft and exact model/source. It then undoes/redoes the Text edit; the current gesture intentionally remains a preview, so this case does not prove completing a post-Text assignment. Its old Press expectation is scoped to a newer active gesture and agrees with the diagnostic distinction above. Both OA and SA are exercised.
|
||||
|
||||
No material defect found in this additional readback. The parent reviewed source only; combined execution remains the release gate. The earlier proposed extra Preview/Cancel/Refused and surviving-gesture completion cases are possible broader coverage, not a claim that this candidate promised or passed them.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# Connection envelope review for prerelease 37
|
||||
|
||||
2026-09-24. Read-only review of current local source identified by the parent as `4421730`, including the early `App::update` guard and three new tests in `app/allocation_drag_tests/group/connection_lifetime.rs`. No Cargo, Git, Gitea, GUI or production changes. Build results are outside this review. This local report is the only artifact written.
|
||||
|
||||
## One material evaluator correction
|
||||
|
||||
**The tests do not independently exercise the live-route-context comparison.** In `crates/oa-desktop/src/app/allocation_drag_tests/group/connection_lifetime.rs:175`, each captured-field mismatch leaves `session.context == current` and changes `captured`. Therefore both `route_context() != Some(captured)` and `session.context != captured` are true. All these cases would still reject if the first guard predicate in `crates/oa-desktop/src/app.rs:1315` were removed. The missing-session tests also reject through the second predicate. The converse case at test line 201 correctly isolates the session-context predicate, because captured equals the current route context while the retained session differs.
|
||||
|
||||
Add one complementary case: retain a drawing session and its captured context, advance the current canvas epoch without rewriting the retained session, then deliver Endpoint and Restart with that old captured context. Explicitly assert `captured == connection.context` and `Some(captured) != route_context()` before using `assert_captured_noop`. This tests the first predicate independently and requires the full newer state to remain unchanged. The existing captured-field matrix remains useful for checking all Context fields, but is not a substitute for this isolated relationship.
|
||||
|
||||
## Code assessment
|
||||
|
||||
No material implementation defect found in the inspected guard. It returns before `reconcile_pending_diagram_reveal`, `allocation_before`, `source_interaction`, connection-session taking, or selection reconciliation. The two preceding computations (`process_preview_primary` and `multiselect_boundary`) read state. Endpoint and Restart must match both the live route context and the extant drawing session, including its document, diagram, revision and epoch.
|
||||
|
||||
Choose and Begin do not match the early guard. They still cross the intentional Allocation retirement boundary; Begin invokes the existing current-context Choose/Endpoint flow inside `edit_connection`. A current Restart passes and delegates to Choose, creating a fresh canvas epoch/session. The test at `connection_lifetime.rs:209` exercises Choose and Begin from a live Allocation preview, current Restart, and a completed endpoint pair.
|
||||
|
||||
The no-op helper compares Allocation epoch/arm, active press identity, preview and routed identity, feedback, exact selection, drawing session, camera/context, source-interaction counter, status, Document snapshot identity and source bundle. The OA/SA cancellation test then completes the retained group action and uses a literal whole-Project geometry expectation. The positive connection test checks relation kind/endpoints and whole-Project preservation except the new generated relation. Both use `group.rs:171` to verify reopen and exact source/model Undo/Redo with one history step. Those checks are appropriate to this bounded fix; this review does not claim broader prerelease integration or native acceptance.
|
||||
|
||||
## Follow-up readback: 72b1bb7
|
||||
|
||||
The parent reports correction source `72b1bb7`. Read back the two scoped edits without running any build/test/format gates. The earlier material evaluator finding is addressed: the test now advances `canvas_epoch`, explicitly asserts that the live route differs while the retained session equals the captured context, and sends both Endpoint and Restart through the full no-op assertion helper. Restoring `canvas_epoch = current.epoch` before the converse case keeps that case independent. Removing either guard predicate would now expose its corresponding isolated case.
|
||||
|
||||
The test module declaration now explicitly names `#[path = "group/connection_lifetime.rs"]`, matching the existing source file relative to `app/allocation_drag_tests/group.rs`. This resolves the inspected source-path ambiguity; actual module resolution/compilation and test success remain for the running gates. The parent reports retained initial Rust/format module-resolution failures; this follow-up does not erase them or claim fresh gates passed. No further material correction found in these two edits.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Route envelope independent review
|
||||
|
||||
2026-09-24. Read-only review of the parent's initially frozen, uncommitted Route guard and four `group/route_lifetime.rs` tests in `C:/ar-multiselect-9e8r6t`. Production scope is the extra captured Commit/CommitAttachment guard in `app.rs`; test registration uses the explicit `group/route_lifetime.rs` path in `app/allocation_drag_tests/group.rs`. No Cargo, formatting, Git/Gitea, GUI or production edits were performed by this reviewer. This report is the only file written. Compilation and native behavior remain outside the review.
|
||||
|
||||
## Initial material finding: same context, different selected edge
|
||||
|
||||
The initial guard at `crates/oa-desktop/src/app.rs:1323` rejects only a Route Context mismatch. A change in selected target is a separate ownership boundary that does not necessarily change that Context.
|
||||
|
||||
Concrete counterexample for both Commit and CommitAttachment:
|
||||
|
||||
1. Use the routed fixture; select its valid edge and capture the proposed route packet and current Context.
|
||||
2. Without Escape or a model/camera mutation, select the two group placements and arm Arrange. `inspect_multiselect_primary` (`app/multiselect.rs:286`) changes `self.selected` directly and does not advance `canvas_epoch`; therefore the captured Context can still equal `route_context()`.
|
||||
3. Deliver the old edge packet, first while Arrange is armed, and separately after a fresh group press/motion establishes an active preview.
|
||||
4. The initial context-only guard admits it. `allocation_before` (`app/allocation_drag.rs:686`) retires the newer Allocation arm/token/preview. The later `edit_route` then finds no projected edge matching the now-selected work object (`app.rs:4745`) and returns, after the damage.
|
||||
|
||||
The original negative helper `stale_route_preserves_new_group` calls Escape before selecting the group, so its explicit epoch mismatch masks this case. This is a source-demonstrated control-flow defect; it was not runtime-tested by this reviewer.
|
||||
|
||||
Recommended bounded correction: bind the route payload in the early pattern and reject if **Context differs OR payload.edge differs from self.selected**, before shared boundaries. No drawing session or selected-attachment requirement belongs here: current route edits are valid without them. Add the same-context changed-selection case for both route event variants, explicitly asserting captured Context equality and changed selected identity, then verify both the armed and live-preview stages remain intact and the current group still completes.
|
||||
|
||||
The parent acknowledged this finding and requested the correction. Follow-up readback and gate status must be recorded separately rather than treating the initial guard as accepted.
|
||||
|
||||
## Other reviewed behavior
|
||||
|
||||
- Guard placement is correct for rejected envelopes: it precedes pending-reveal reconciliation, Allocation retirement, source interaction, legacy route handlers and later selection reconciliation. The two preceding App computations read state.
|
||||
- The context field tests independently vary document/diagram/revision/epoch, advance the live epoch with a fixed capture, and test absent active diagram. They do not rely on a missing route session to reject; Route has no equivalent to Connection session ownership.
|
||||
- The positive test exercises both current Commit and current CommitAttachment with no Connection session and no armed attachment. It requires a revision increment, preserved selected edge, the expected bend selection, and a literal complete expected Project. Commit changes only the pin to `(750,450)`; CommitAttachment changes only the source attachment to Top/0.25. Existing label, target attachment, unrelated routes/model records remain protected by whole-Project equality.
|
||||
- The completed Arrange oracle is independently authored: selected performer position becomes `(500,100)`, its carried work becomes `(540,200)`, and the other selected work becomes `(540,600)`. Both route endpoint placements move by `(400,0)`, so the original saved pin `(700,400)` must become `(1100,400)` exactly once. The expected label and endpoint attachment fields remain inherited from the unchanged baseline. This is the correct literal pin oracle; leaving `(700,400)` would contradict simultaneous two-endpoint movement.
|
||||
- `exact_history` verifies the whole expected Project, source reopen, one Undo to exact original Project/source, and one Redo to exact completed Project/source with no additional undo/redo entries. The positive expected Project is not cloned from the observed output or computed by the route planner.
|
||||
- The no-op helper checks exact selection/primary, context/camera, Allocation epoch/arm, active press Arc, preview Arc, feedback, handle state, source-interaction counter, status, Document snapshot Arc and source bundle. Together with successful completion/history, these are meaningful checks for the intended early-rejection fix.
|
||||
|
||||
No other material correction found in the initial bounded review. This does not establish broader route-session ABA semantics, complete prerelease integration, passing gates or native acceptance.
|
||||
## Follow-up: corrected frozen source readback
|
||||
|
||||
Read back the corrected, still-uncommitted guard and six registered tests. The material finding above is addressed: the early pattern binds `changed` for both Commit variants and rejects `route_context() != Some(captured) || changed.edge != self.selected`. It retains current route semantics without inventing a session/armed-handle requirement.
|
||||
|
||||
The two separately named `same_context_route_commit_after_selection_preserves_arrange_arm_and_live_group` and `same_context_route_attachment_after_selection_preserves_arrange_arm_and_live_group` tests execute the helper without Escape. Before delivering the old packet at both the armed and live-preview stages, they explicitly assert that the captured Context still equals the live Context and that the selected object differs from the packet edge. They then use the full no-op checks and complete the retained Arrange with the literal whole-Project pin/placement and history oracle. These cases discriminate the new edge predicate; the existing Context cases keep the edge selected and discriminate Context separately. Positive session-free route edits remain covered with independently authored expected route fields.
|
||||
|
||||
No further material correction found in this bounded readback. The initial finding is source-addressed, not a claim that compilation, tests, formatting or native gates passed. This reviewer ran none of those gates.
|
||||
Reference in New Issue
Block a user