diff --git a/research/selected-ownermap-binding-gap/EXPECTED.json b/research/selected-ownermap-binding-gap/EXPECTED.json new file mode 100644 index 0000000000..1981de5198 --- /dev/null +++ b/research/selected-ownermap-binding-gap/EXPECTED.json @@ -0,0 +1,442 @@ +{ + "audit_script_sha256": "1467a2acb654e62552c69fb9a7c4c85f70a02120881d222313865058ab71a6cc", + "fixtures": { + "F0": { + "accepted_by_chain": true, + "expected": "accepted; link check passes", + "final_binding": { + "checks": { + "admission_controls": true, + "assembly_status_and_kappa": true, + "audit_file_hashes": true, + "bit_supplier_controls": true, + "charged_prefix_bits": true, + "chart_counts": true, + "controller_mutation_controls": true, + "finite_strict_checks": true, + "idle_equality": true, + "independent_t16_status": true, + "invocation_counts": true, + "kappa_improves_t8": true, + "outer_margins": true, + "prefix_bits": true, + "profile_equality": true, + "published_inventory": true, + "rank_mass_deficit": true, + "roles_counts": true, + "row_coefficients": true, + "template_sha_binding": true + }, + "passed": 20, + "total": 20 + }, + "link_check": { + "diff_count": 0, + "diffs": [], + "owner_map_sha256_names_frozen_gzip": true, + "pooled_slots_equal_frozen_owners": true, + "roles": 69120 + }, + "mutation": { + "kind": "unmutated re-emit", + "payload_identical": true, + "summary_identical": true + }, + "receipt_diff_paths_vs_archived": { + "ASSEMBLY.json": [], + "ASYNC-AUDIT.json": [], + "GLOBAL-PREFIX.json": [], + "IDLE-REMAINDER-FRAMES.json": [], + "INDEPENDENT-T16.json": [], + "OWNERS-AND-PROFILE.json": [], + "PREFIX-AND-INVOICE.json": [] + }, + "receipts_match_archived": { + "ASSEMBLY.json": true, + "ASYNC-AUDIT.json": true, + "GLOBAL-PREFIX.json": true, + "IDLE-REMAINDER-FRAMES.json": true, + "INDEPENDENT-T16.json": true, + "OWNERS-AND-PROFILE.json": true, + "PREFIX-AND-INVOICE.json": true + }, + "stage_ledger": [ + { + "exit_code": 0, + "stage": "audit" + }, + { + "exit_code": 0, + "stage": "invoice" + }, + { + "exit_code": 0, + "stage": "assembly" + }, + { + "exit_code": 0, + "stage": "admission-controls" + }, + { + "exit_code": 0, + "stage": "bit-supplier-controls" + }, + { + "exit_code": 0, + "stage": "independent-t16" + } + ] + }, + "F1": { + "accepted_by_chain": true, + "expected": "accepted; link check fails on exactly two role times", + "final_binding": { + "checks": { + "admission_controls": true, + "assembly_status_and_kappa": true, + "audit_file_hashes": true, + "bit_supplier_controls": true, + "charged_prefix_bits": true, + "chart_counts": true, + "controller_mutation_controls": true, + "finite_strict_checks": true, + "idle_equality": true, + "independent_t16_status": true, + "invocation_counts": true, + "kappa_improves_t8": true, + "outer_margins": true, + "prefix_bits": true, + "profile_equality": true, + "published_inventory": true, + "rank_mass_deficit": true, + "roles_counts": true, + "row_coefficients": true, + "template_sha_binding": true + }, + "passed": 20, + "total": 20 + }, + "link_check": { + "diff_count": 2, + "diffs": [ + { + "field": "time", + "index": 0, + "owners": 0, + "template": 1 + }, + { + "field": "time", + "index": 480, + "owners": 1, + "template": 0 + } + ], + "owner_map_sha256_names_frozen_gzip": true, + "pooled_slots_equal_frozen_owners": false, + "roles": 69120 + }, + "mutation": { + "kind": "equal-width cross-time pooled-slot time swap (coherent re-emit)", + "swapped": [ + { + "helper": 0, + "index": 0, + "replica": 0, + "stage": 0, + "time_after": 1, + "time_before": 0, + "width": 9 + }, + { + "helper": 0, + "index": 480, + "replica": 8, + "stage": 0, + "time_after": 0, + "time_before": 1, + "width": 9 + } + ], + "template_sha256": "f5fee85b4e9b49f577dbb715b92035c3e469402d9c1c3680654e13189d504345", + "width_profile_preserved": true + }, + "receipt_diff_paths_vs_archived": { + "ASSEMBLY.json": [ + "/input_sha256/owner_audit", + "/template_sha256" + ], + "ASYNC-AUDIT.json": [ + "/complex_template_sha256", + "/template_sha256" + ], + "GLOBAL-PREFIX.json": [ + "/controller_audit_sha256", + "/template_sha256" + ], + "IDLE-REMAINDER-FRAMES.json": [ + "/template_sha256" + ], + "INDEPENDENT-T16.json": [], + "OWNERS-AND-PROFILE.json": [ + "/controller_audit_sha256", + "/template_sha256" + ], + "PREFIX-AND-INVOICE.json": [ + "/controller_audit_sha256", + "/owner_audit_sha256", + "/template_sha256" + ] + }, + "receipts_match_archived": { + "ASSEMBLY.json": false, + "ASYNC-AUDIT.json": false, + "GLOBAL-PREFIX.json": false, + "IDLE-REMAINDER-FRAMES.json": false, + "INDEPENDENT-T16.json": true, + "OWNERS-AND-PROFILE.json": false, + "PREFIX-AND-INVOICE.json": false + }, + "stage_ledger": [ + { + "exit_code": 0, + "stage": "audit" + }, + { + "exit_code": 0, + "stage": "invoice" + }, + { + "exit_code": 0, + "stage": "assembly" + }, + { + "exit_code": 0, + "stage": "admission-controls" + }, + { + "exit_code": 0, + "stage": "bit-supplier-controls" + }, + { + "exit_code": 0, + "stage": "independent-t16" + } + ] + }, + "F2": { + "accepted_by_chain": true, + "expected": "accepted; link check fails on owner_map_sha256", + "final_binding": { + "checks": { + "admission_controls": true, + "assembly_status_and_kappa": true, + "audit_file_hashes": true, + "bit_supplier_controls": true, + "charged_prefix_bits": true, + "chart_counts": true, + "controller_mutation_controls": true, + "finite_strict_checks": true, + "idle_equality": true, + "independent_t16_status": true, + "invocation_counts": true, + "kappa_improves_t8": true, + "outer_margins": true, + "prefix_bits": true, + "profile_equality": true, + "published_inventory": true, + "rank_mass_deficit": true, + "roles_counts": true, + "row_coefficients": true, + "template_sha_binding": true + }, + "passed": 20, + "total": 20 + }, + "link_check": { + "diff_count": 0, + "diffs": [], + "owner_map_sha256_names_frozen_gzip": false, + "pooled_slots_equal_frozen_owners": true, + "roles": 69120 + }, + "mutation": { + "kind": "hosting.owner_map_sha256 set to a wrong digest (coherent re-emit)", + "template_sha256": "f5a0376deb2d3d2213b04bb64af2618b018a13ce3e6a7afb62c72f05a500a3a3", + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "receipt_diff_paths_vs_archived": { + "ASSEMBLY.json": [ + "/input_sha256/owner_audit", + "/template_sha256" + ], + "ASYNC-AUDIT.json": [ + "/complex_template_sha256", + "/template_sha256" + ], + "GLOBAL-PREFIX.json": [ + "/controller_audit_sha256", + "/template_sha256" + ], + "IDLE-REMAINDER-FRAMES.json": [ + "/template_sha256" + ], + "INDEPENDENT-T16.json": [], + "OWNERS-AND-PROFILE.json": [ + "/controller_audit_sha256", + "/template_sha256" + ], + "PREFIX-AND-INVOICE.json": [ + "/controller_audit_sha256", + "/owner_audit_sha256", + "/template_sha256" + ] + }, + "receipts_match_archived": { + "ASSEMBLY.json": false, + "ASYNC-AUDIT.json": false, + "GLOBAL-PREFIX.json": false, + "IDLE-REMAINDER-FRAMES.json": false, + "INDEPENDENT-T16.json": true, + "OWNERS-AND-PROFILE.json": false, + "PREFIX-AND-INVOICE.json": false + }, + "stage_ledger": [ + { + "exit_code": 0, + "stage": "audit" + }, + { + "exit_code": 0, + "stage": "invoice" + }, + { + "exit_code": 0, + "stage": "assembly" + }, + { + "exit_code": 0, + "stage": "admission-controls" + }, + { + "exit_code": 0, + "stage": "bit-supplier-controls" + }, + { + "exit_code": 0, + "stage": "independent-t16" + } + ] + }, + "F3": { + "accepted_by_chain": false, + "expected": "rejected by audit at line 172", + "link_check": { + "diff_count": 1, + "diffs": [ + { + "field": "port", + "index": 0, + "owners": 14, + "template": 15 + } + ], + "owner_map_sha256_names_frozen_gzip": true, + "pooled_slots_equal_frozen_owners": false, + "roles": 69120 + }, + "mutation": { + "changed": { + "field": "port", + "frm": 14, + "helper": 0, + "index": 0, + "replica": 0, + "stage": 0, + "to": 15 + }, + "kind": "pooled-slot port (checked roles field) changed (coherent re-emit)", + "template_sha256": "14064e1a31212e26486f285efc29749b4638fdb99fe0cfa6487e89519d91f7bd" + }, + "stage_ledger": [ + { + "exit_code": 1, + "failure": { + "exception": "AssertionError", + "line": 172, + "script": "audit_allcold.py" + }, + "stage": "audit" + } + ] + }, + "F4": { + "accepted_by_chain": false, + "expected": "rejected by audit at line 185", + "link_check": { + "diff_count": 2, + "diffs": [ + { + "field": "time", + "index": 0, + "owners": 0, + "template": 1 + }, + { + "field": "time", + "index": 480, + "owners": 1, + "template": 0 + } + ], + "owner_map_sha256_names_frozen_gzip": true, + "pooled_slots_equal_frozen_owners": false, + "roles": 69120 + }, + "mutation": { + "kind": "time swap with stale HOSTED.json template_sha256 (artifact inconsistency)", + "stale_summary": true, + "swapped": [ + { + "helper": 0, + "index": 0, + "replica": 0, + "stage": 0, + "time_before": 0 + }, + { + "helper": 0, + "index": 480, + "replica": 8, + "stage": 0, + "time_before": 1 + } + ], + "template_sha256": "f5fee85b4e9b49f577dbb715b92035c3e469402d9c1c3680654e13189d504345" + }, + "stage_ledger": [ + { + "exit_code": 1, + "failure": { + "exception": "AssertionError", + "line": 185, + "script": "audit_allcold.py" + }, + "stage": "audit" + } + ] + } + }, + "pinned_inputs": [ + "research/community-frontier1094-audit/selected-replay.tar.gz", + "research/complex-t16-rare-pattern-exchanges/upstream1087.tar.gz", + "research/complex-t16-rare-pattern-exchanges/data/OWNERS.json.gz", + "research/complex-t16-rare-pattern-exchanges/data/PLAN.json", + "research/complex-t16-rare-pattern-exchanges/data/SPEC.json", + "research/complex-k492-owner-repair/vendor/multicold/code/run_portable.py", + "research/complex-k492-owner-repair/vendor/multicold/vendor/pr558/vendor/pr552/complex-global/vendor531/evidence/audit/PREFIX-AND-INVOICE.json" + ], + "run_portable_sha256": "322d29d43dbee8130cde15d4bf05396698b5e3dfe3735813899db91cc0ba7955", + "status": "PASS_SELECTED_OWNERMAP_BINDING_GAP", + "verify_t16_sha256": "050e2dfba59911b8f29fd94422833d421367a0ad01c5e4ae916a75a235ee5e46" +} diff --git a/research/selected-ownermap-binding-gap/README.md b/research/selected-ownermap-binding-gap/README.md new file mode 100644 index 0000000000..dc0ce062f4 --- /dev/null +++ b/research/selected-ownermap-binding-gap/README.md @@ -0,0 +1,160 @@ +# Accepted-but-unbound: the selected chain's cross-stage owner-map binding + +**A scoped adversarial audit of one named acceptance boundary of the selected +chain. κ, the certificate, the suppliers and the frontier are unchanged. This +is not a bug claim against the selected fixture and not a cold replay.** + +Prepared by **Aaron Wang** with help from opencode (substantial AI assistance; +no independent human review). Apache-2.0. + +## Question and result + +The frozen PR1087 selected chain certifies the frozen `data/OWNERS.json.gz` +role map twice: `lift16` checks every role inside its owner's actual window and +`independent-t16` rechecks the map against invocation times. `emit` copies that +map verbatim into the hosted template's `hosting.pooled_slots` (eleven fields +per role, `time` included), and `audit_allcold.py` binds ten of the eleven +fields back to the base-derived call owners (line 172, "the frozen pooled +metadata must bind the actual serialized owner maps"). The whole chain also +binds the template's **bytes** tightly by hash (`audit` line 185, `invoice` +line 175, `assemble` line 240 and `verify_t16.py`'s +`asm.template_sha256 == owner.template_sha256 == inv.template_sha256`). + +Two fields are bound to nothing: the pooled slot's `time` (which invocation +hosts the role) and `hosting.owner_map_sha256` (the field that names the owner +map). Josie Elliston's +[PR #1959](https://github.com/CrocSwap/integer-mult-bounds/pull/1959) recorded +this in source text ("line 172 compares a pooled slot's roles fields but not +its `time`"; the owner-map link "was previously implied by that determinism, +not checked") and added the boundary check to its own verifier. This package +**executes** the gap: the smallest invalid fixtures that a defective-but-coherent +`emit` could write are accepted by the complete frozen post-emit stage sequence +and by `verify_t16.py`'s final binding, while failing the owner-map binding. + +Fixtures (each is a canonical re-emit of the archived selected hosted template, +so every template-hash binding stays tight and only the audited field differs +from the frozen owner map): + +| Fixture | Mutation | audit | invoice | assembly | both control stages | independent-t16 | final binding | link check | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| F0 | none (re-emit fidelity control) | pass | pass | pass | pass | pass | 20/20 | passes | +| F1 | two equal-width pooled slots exchange `time` | pass | pass | pass | pass | pass | 20/20 | fails on 2 role times | +| F2 | `hosting.owner_map_sha256` set to a wrong digest | pass | pass | pass | pass | pass | 20/20 | fails on the naming field | +| F3 | one pooled slot's `port` (checked roles field) changed | rejected, line 172 | not run | not run | not run | not run | not run | fails on `port` | +| F4 | F1's swap with a stale `HOSTED.json` hash | rejected, line 185 | not run | not run | not run | not run | not run | fails on 2 role times | + +**The accepted counterexample (F1).** Pooled slots 0 and 480 — `(stage 0, +replica 0, helper 0)` and `(stage 0, replica 8, helper 0)`, both `width 9` — +exchange their invocation times (`time` 0 and 1). Every per-time width count is +preserved, so no count, profile, `rank_mass`, `deficit`, prefix or row +coefficient moves: the complete frozen chain from `audit` through +`independent-t16` accepts the template, and all 20 of `verify_t16.py`'s final +binding assertions pass. The only differences from the archived receipts are +the expected `template_sha256` hash cascades. The owner-map binding +(`pooled_slots` equals the frozen OWNERS list, times included) fails on exactly +those two roles: the template now says each helper runs at the other's +invocation. + +**What would change downstream.** The certified owner-window admissibility +(lift16's actual-window check and independent-t16's invocation-time check) +applies to the frozen OWNERS times, not to the template's. A hosted-schedule +consumer that reads `hosting.pooled_slots` from the accepted template would +host those two helpers at invocations that no check validated. Inside the +frozen chain the `time` column is inert (F1's receipt diffs are hash cascades +only), so this is a contract gap at the accepted artifact boundary, not a +change to the selected arithmetic. + +**What the chain does catch.** The rejection controls delimit the gap: a +checked roles field is rejected at `audit` line 172 (F3), and a template whose +bytes disagree with `HOSTED.json`'s recorded hash is rejected at `audit` line +185 (F4). Artifact integrity is enforced; semantic equality of the pooled role +map with the frozen owner map is not, beyond the ten fields line 172 compares. + +## Reproduce + +```sh +python3 -B research/selected-ownermap-binding-gap/verify.py +``` + +Expected status `PASS_SELECTED_OWNERMAP_BINDING_GAP` (about 3–5 minutes on the +author's host; it runs the frozen stages out-of-process per fixture). The +deterministic output must equal [EXPECTED.json](EXPECTED.json). +`--write` regenerates `EXPECTED.json`; normal verification writes no files. +Stdlib only. Checked on CPython 3.12 (Windows); the generic replay declares +Python 3.12. + +The harness pins its inputs ([SOURCES.json](SOURCES.json)), reproduces PR #1959's +in-process/complete-caller discipline, runs the frozen stages exactly as +`verify_t16.py` invokes them (through the pinned `run_portable.py` wrapper), and +reimplements `verify_t16.py`'s final binding 1:1 (20 assertions, driver pinned +by archive-member SHA-256 `050e2dfb…`). The audited guard +(`link_check`) is PR #1959's `template_to_frozen_owners_binding`. + +**Serialisation notes** (CONTRIBUTING.md "Regenerated artifacts and hashes"): +two machine-dependent serialisations are normalized before comparing receipts +with the archived selected ones — `independent-t16` keys its `input_sha256` by +absolute input paths, and the frozen invoice's `artifact_semantic_sha256` map +is serialized in directory-enumeration order, so the byte hash `assemble` +records as `input_sha256.fresh_invoice` varies by filesystem. On this host the +fresh invoice receipt differs from the archived copy only in nested key order; +parsed proof-relevant content is identical. Everything else reproduces the +archived receipts exactly. + +## Scope and not checked + +- The pre-emit stages (`restore-parent`, `bind8`, `lift16`, `bind`, `emit`) are + not re-run: the fixture mutation stands in for a defect of `emit` in the + unchecked fields (or post-emit tampering), and `emit` itself pins the OWNERS + input by hash against the SPEC. Provider content binding against + baseline1042's `slots/local` is not re-checked (upstream of the mutation + point; in a fixture run the bound copy is its own input). +- `audit`'s module-level provider checks (its lines 20–80), the nine other + archived `audit_allcold.py` versions (PR #1959 open question 1) and every + stage of the cold baseline are untouched. +- No cold rebuild, no Linux/macOS run, no formal kernel, no human review, no + supplier, certificate or κ change. The archived selected fixture satisfies + the binding; the counterexample is synthetic and scoped to this interface. +- This is one missing implication at one interface, executed end to end. It is + not a general statement about emitter correctness or about other templates. + +## Repair status and credit + +The minimal repair is the two-line guard this package's `link_check` implements +(pooled slots equal the frozen OWNERS content; `owner_map_sha256` equals the +SHA-256 of the frozen gzip), added to a successor replay driver with the frozen +code unchanged. That repair remains **blocked on a maintainer decision** on +wrapping the frozen `verify_t16.py` (PR #1959 open question 2), so it is not +attempted here. + +Credit and predecessors: Josie Elliston's +[PR #1959](https://github.com/CrocSwap/integer-mult-bounds/pull/1959) +(`research/t16-audit-mutant-coverage/`) identified the gap, supplied the +boundary-check precedent and open question 2, and is the direct predecessor of +this package. The frozen chain is Jeremy Kothe's PR1087/#870 `gauge44-barch-t16` +code with Hao Yan's (@FanPUYen) PR #858 T16 controller, `lift16` and `indep16`; +Rohan Arun's [PR #1094](https://github.com/CrocSwap/integer-mult-bounds/pull/1094) +supplies the selected exchanges and archived run; the maintainer's frontier1094 +audit archived the fixture. Concurrent and complementary: +[PR #2273](https://github.com/CrocSwap/integer-mult-bounds/pull/2273) +machine-checks that the 15 audit-unread fields (owner_map_sha256 among them) +have no post-emit reader; this package supplies the executed end-to-end +acceptance of the wrong value and of the retimed role map. + +## Open next questions + +1. **Add the two-equality owner-map guard to a successor replay driver.** Why it + matters: it closes the demonstrated gap without touching frozen code. + First step: wrap `verify_t16.py` with this package's `link_check` (or PR + #1959's `template_to_frozen_owners_binding`) between `emit` and `audit`. + Blocked by: the maintainer decision on wrapping the frozen driver (PR #1959 + open question 2). +2. **Does any accepted consumer of the hosted template outside the frozen chain + read pooled-slot `time`?** Why it matters: it decides whether F1 is inert + contract metadata or a live hosting-schedule hazard. First step: extend PR + #2273's consumer-universe machine check from field names to time-directed + reads of `pooled_slots`. Blocked by: nothing but session time. +3. **The nine other archived `audit_allcold.py` versions** (PR #1959 open + question 1): does each bind its pooled roles the same way? First step: + parametrize a receipt over archive member and script hash, using the #1042 + audit archive as fixture. Blocked by: locating each version's hosted + template in the retained receipts. diff --git a/research/selected-ownermap-binding-gap/SOURCES.json b/research/selected-ownermap-binding-gap/SOURCES.json new file mode 100644 index 0000000000..f13ec1dab9 --- /dev/null +++ b/research/selected-ownermap-binding-gap/SOURCES.json @@ -0,0 +1,60 @@ +{ + "schema_version": 1, + "base_commit": "0fdd7d82de03e43c9e5a1000dfbcd380d1b687d2", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "ae6a99f28aa468649eb66a361e764d769363bf11", + "sha256": "10b9e340fd8e354a37a0c4813c90e5de1dd42044b5f6aca4a3fc5a42b685fa32" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "e8870e5917057d59b730eb14721a778639eca050", + "sha256": "d46eb6d5b3e279428753d770653663fbdf1063e55c255ea073d768f344927ef7" + }, + { + "path": "research/community-frontier1094-audit/selected-replay.tar.gz", + "usage": "input", + "git_blob": "c24daf067f9c543421d89ca2eeaa4eb78a57fb6f", + "sha256": "f1da80610bf33c58527f18ad63b4350d0b864e1d4486141ee17ac9bed4d529d6" + }, + { + "path": "research/complex-t16-rare-pattern-exchanges/upstream1087.tar.gz", + "usage": "input", + "git_blob": "2970ef79b0441e937449bed30f68f2e0558186ba", + "sha256": "d4acf04daef85d4514f80ec5fcd2492d0c60e5d6c49fa71b3e6523f8db2dea86" + }, + { + "path": "research/complex-t16-rare-pattern-exchanges/data/OWNERS.json.gz", + "usage": "input", + "git_blob": "d3ba33d7e703a3229d9fb8a360f69d2f318bbb70", + "sha256": "87902c528dfab7d503dcf8a784260e3ab6fb7424c418a1820f7261f25be7b6bc" + }, + { + "path": "research/complex-t16-rare-pattern-exchanges/data/PLAN.json", + "usage": "input", + "git_blob": "f1bec76e8e9b7a1f09c1e05cb6cce973b6e59f1b", + "sha256": "7bd786ba8e66408f02aa9ef446eaf20c1bfe981ea2e4dd1e5a929f0aa7767bc0" + }, + { + "path": "research/complex-t16-rare-pattern-exchanges/data/SPEC.json", + "usage": "input", + "git_blob": "af013ccc2975d9d575d116f8a463cc947f220c8d", + "sha256": "8cb51f25a5da4a29d3783853025b73ff1f452429d4fdb30c599e598bcab0e71d" + }, + { + "path": "research/complex-k492-owner-repair/vendor/multicold/code/run_portable.py", + "usage": "input", + "git_blob": "2fed13f432df4d76ce72185dd79fac63d96aee96", + "sha256": "322d29d43dbee8130cde15d4bf05396698b5e3dfe3735813899db91cc0ba7955" + }, + { + "path": "research/complex-k492-owner-repair/vendor/multicold/vendor/pr558/vendor/pr552/complex-global/vendor531/evidence/audit/PREFIX-AND-INVOICE.json", + "usage": "input", + "git_blob": "a16bf40bccc81278a443e0d5f09b44495035de81", + "sha256": "ce1bfa49be88ae67a404f606a11db2232b7e1f2610d4418058e39749d86d8478" + } + ] +} diff --git a/research/selected-ownermap-binding-gap/TASK.json b/research/selected-ownermap-binding-gap/TASK.json new file mode 100644 index 0000000000..173437afe8 --- /dev/null +++ b/research/selected-ownermap-binding-gap/TASK.json @@ -0,0 +1,77 @@ +{ + "schema_version": 1, + "task_id": "supplier-boundary-audit", + "track": "adversarial-audit", + "chooser_nonce": "f96018873928dd5ec93e3ca2fe16f1d9", + "priority_revision": 4, + "base_commit": "0fdd7d82de03e43c9e5a1000dfbcd380d1b687d2", + "original_card": { + "id": "supplier-boundary-audit", + "question": "Does one selected supplier obligation follow from its actual source-bound checks?", + "starting_points": [ + "certificates/selected-result.json", + "research/owner-boundary-audit/README.md" + ], + "methods": [ + "Trace one hypothesis through the source, checker and complete caller.", + "Construct one semantically invalid but superficially well-formed fixture and test rejection." + ], + "acceptance_gate": "Pin the current sources and one obligation; the historical owner audit is an example, not evidence of a current bug.", + "first_step": { + "command": [ + "python3", + "-B", + "research/owner-boundary-audit/verify.py" + ], + "expected": "PASS_PR626_NEXT_TOUCH_BOUNDARY_AUDIT", + "inspect": "Identify why the timing-only omission passes but the full production controller still rejects it.", + "scope": "Positive control / source-bound orientation only; this is not a new construction or full cold replay." + }, + "first_experiment": "Choose one current supplier obligation and trace whether its acceptance depends on an independent check.", + "bottleneck_link": "Closing a current acceptance gap or proof obligation strengthens reliability even without changing kappa.", + "allowed_variations": [ + "Audit one named current condition using a new semantic counterexample." + ], + "scope_change_triggers": [ + "Reporting the historical timing-only diagnostic as a new current acceptance bug." + ], + "overlap_terms": [ + "owner boundary", + "bridge omission", + "hosting" + ] + }, + "scope_status": "refined", + "actual_scope": "One named current condition of the selected chain: the cross-stage owner-map binding. Does chain acceptance of the hosted template imply hosting.pooled_slots equals the frozen data/OWNERS.json.gz role list (times included) and hosting.owner_map_sha256 names that gzip? Executed end to end on the archived selected fixture: the smallest coherent-emitter counterexamples (an equal-width cross-time pooled-slot time swap; a wrong owner_map_sha256) run through the frozen post-emit stages audit, invoice, assembly, admission-controls, bit-supplier-controls, independent-t16 and verify_t16.py's final binding, with rejection-power controls (a checked roles field; a stale HOSTED.json hash).", + "scope_change_reason": "The drawn cards uniform-transfer-audit and selected-stock-envelope matched recent claimed objects (PR 2273 completed the uniform-transfer object today; the one-stage stock object was claimed in discussion 1166 comment 18932014 and is under five open PRs), so the second-choice card was taken and refined to the unclaimed cross-stage owner-map binding gap that PR1959 recorded but did not execute. Recorded per the pair-collision rule.", + "existing_restriction_changed": "PR1959 checked the binding inside its own verifier and asserted the gap from source text; this package executes the accepted counterexample through the complete frozen caller and the final binding.", + "bottleneck_link": "Closing a concrete inherited acceptance obligation of the selected chain: the certified owner-window and invocation-time evidence applies to the frozen OWNERS map, and the accepted template's role-to-time column was bound to it by nothing. Reliability result; no kappa arithmetic is changed.", + "method": "Trace the hypothesis through source, checker and complete caller; then the card's second method: one semantically invalid but superficially well-formed fixture per unchecked field, coherent re-emit, and rejection testing. Deterministic stdlib harness, EXPECTED.json replay.", + "stop_rule": "Stop at the first complete-caller acceptance of a fixture failing the owner-map binding (recorded as the counterexample), or, if every fixture is rejected, at the recorded rejecting stage and line. Both rejection controls must fail at their intended lines (172 and 185).", + "evidence": "verify.py/EXPECTED.json: F0 accepted and reproduces every archived selected receipt (normalized for two machine-dependent serialisations), final binding 20/20, link check passes. F1 (pooled slots 0 and 480, (stage 0, replica 0, helper 0) and (stage 0, replica 8, helper 0), both width 9, exchange times 0 and 1; every per-time width count preserved): accepted by all six frozen post-emit stages, final binding 20/20, receipt diffs limited to template_sha256 hash cascades, link check fails on exactly the two role times. F2 (owner_map_sha256 = 64 zeros): accepted, final binding 20/20, link check fails on the naming field. F3 (pooled slot port changed): rejected by audit at line 172. F4 (time swap with stale HOSTED.json hash): rejected by audit at line 185. Status PASS_SELECTED_OWNERMAP_BINDING_GAP.", + "outcome": "completed", + "budget_minutes": 60, + "author": "Aaron Wang", + "ai_assistance": "With help from opencode (substantial AI assistance in task selection, harness implementation, verification and writing). No independent human review is claimed. The audited code and receipts retain their authors' notices.", + "predecessors": "Josie Elliston PR1959 (research/t16-audit-mutant-coverage): identified the gap, supplied the boundary-check precedent and open question 2. Frozen chain: Jeremy Kothe PR1087/#870 gauge44-barch-t16 code; Hao Yan (@FanPUYen) PR858 T16 controller, lift16, indep16; Rohan Arun PR1094 selected exchanges and archived run; maintainer frontier1094 audit fixture. Concurrent complementary work: PR2273 (post-emit unread fields).", + "open_questions": [ + { + "question": "Add the two-equality owner-map guard to a successor replay driver without touching frozen code.", + "why_it_matters": "It closes the demonstrated gap between the accepted template and the certified owner map.", + "first_step": "Wrap verify_t16.py with this package's link_check (or PR1959's template_to_frozen_owners_binding) between emit and audit.", + "blocked_by": "Maintainer decision on wrapping the frozen driver (PR1959 open question 2)." + }, + { + "question": "Does any accepted consumer of the hosted template outside the frozen chain read pooled-slot time?", + "why_it_matters": "It decides whether the accepted retimed map is inert contract metadata or a live hosting-schedule hazard.", + "first_step": "Extend PR2273's consumer-universe machine check from field names to time-directed reads of pooled_slots.", + "blocked_by": "Nothing but session time." + }, + { + "question": "Do the nine other archived audit_allcold.py versions bind their pooled roles the same way?", + "why_it_matters": "Each version ships its own controls; the selected record's reuse path depends on them.", + "first_step": "Parametrize a receipt over archive member and script hash; use the #1042 audit archive as fixture.", + "blocked_by": "Locating each version's hosted template in the retained receipts (PR1959 open question 1)." + } + ] +} diff --git a/research/selected-ownermap-binding-gap/VERIFY.json b/research/selected-ownermap-binding-gap/VERIFY.json new file mode 100644 index 0000000000..65cabbf55a --- /dev/null +++ b/research/selected-ownermap-binding-gap/VERIFY.json @@ -0,0 +1,26 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/community-frontier1094-audit/selected-replay.tar.gz", + "research/complex-t16-rare-pattern-exchanges/upstream1087.tar.gz", + "research/complex-t16-rare-pattern-exchanges/data/OWNERS.json.gz", + "research/complex-t16-rare-pattern-exchanges/data/PLAN.json", + "research/complex-t16-rare-pattern-exchanges/data/SPEC.json", + "research/complex-k492-owner-repair/vendor/multicold/code/run_portable.py", + "research/complex-k492-owner-repair/vendor/multicold/vendor/pr558/vendor/pr552/complex-global/vendor531/evidence/audit/PREFIX-AND-INVOICE.json" + ], + "commands": [ + { + "argv": [ + "{python}", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 600, + "checks": "Input SHA-256 pins; pinned audit_allcold.py, verify_t16.py (archive member) and run_portable.py hashes; F0 unmutated re-emit reproduces every archived selected receipt and passes the 20 final-binding assertions and the link check; F1 equal-width cross-time pooled-slot time swap and F2 wrong owner_map_sha256 are accepted by all six frozen post-emit stages with the final binding passing while the link check fails on exactly the mutated fields; F3 is rejected by audit at line 172 and F4 at line 185; all outcomes must equal EXPECTED.json byte for byte." + } + ] +} diff --git a/research/selected-ownermap-binding-gap/verify.py b/research/selected-ownermap-binding-gap/verify.py new file mode 100644 index 0000000000..a1998ca89e --- /dev/null +++ b/research/selected-ownermap-binding-gap/verify.py @@ -0,0 +1,411 @@ +"""Accepted-but-unbound: the selected chain's cross-stage owner-map binding. + +The frozen PR1087 selected chain (verify_t16.py stage sequence) certifies the +frozen data/OWNERS.json.gz role map twice over: lift16 checks every role inside +its owner's actual window and independent-t16 rechecks it against invocation +times. `emit` copies that map verbatim into the hosted template's +`hosting.pooled_slots`, and `audit` (audit_allcold.py line 172) binds ten of the +eleven pooled-slot fields back to the base-derived call owners. The eleventh +field, `time`, and the naming field `hosting.owner_map_sha256`, are bound to +nothing: no frozen stage and no line of the complete caller compares them with +the frozen OWNERS map. + +This harness executes that missing implication against the archived selected +fixture (research/community-frontier1094-audit/selected-replay.tar.gz). Each +fixture models a defective-but-coherent emitter: the hosted template is +rewritten exactly as emit serialises it (canonical JSON, HOSTED.json's +template_sha256 recomputed), so every template-hash binding stays tight and +only the audited field differs from the frozen owner map. The full frozen stage +sequence from audit through independent-t16 runs unchanged and out-of-process, +followed by verify_t16.py's final binding (reimplemented 1:1, pinned below). + +Fixtures: + F0 unmutated fixture: every stage passes, receipts reproduce the + archived selected receipts, and the link check passes. + F1 two equal-width pooled slots swap their `time` (a role-to-time + permutation that preserves every per-time width count): accepted by + the whole chain, link check fails on exactly two roles. + F2 `hosting.owner_map_sha256` set to a wrong digest: accepted by the + whole chain, link check fails on the naming field. + F3 control: one pooled slot's `port` (a checked roles field) changes: + rejected by audit at line 172. + F4 control: a time change with a stale HOSTED.json template_sha256 + (artifact inconsistency): rejected by audit at line 185. + +Stdlib only; Python 3.12 replay. Usage: + python3 -B research/selected-ownermap-binding-gap/verify.py # compare with EXPECTED.json + python3 -B research/selected-ownermap-binding-gap/verify.py --write # regenerate EXPECTED.json +""" +import argparse, gzip, hashlib, json, os, re, shutil, subprocess, sys, tarfile, tempfile +from collections import Counter +from fractions import Fraction +from pathlib import Path + +assert __debug__, 'assertions must stay enabled' +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] +STAGE = ROOT / 'research/complex-t16-rare-pattern-exchanges' +MC = ROOT / 'research/complex-k492-owner-repair/vendor/multicold' +ARCHIVE = ROOT / 'research/community-frontier1094-audit/selected-replay.tar.gz' +RETAINED_INVOICE = MC / 'vendor/pr558/vendor/pr552/complex-global/vendor531/evidence/audit/PREFIX-AND-INVOICE.json' +RUNPORT = MC / 'code/run_portable.py' +sha = lambda b: hashlib.sha256(b).hexdigest() +AUDIT_SHA = '1467a2acb654e62552c69fb9a7c4c85f70a02120881d222313865058ab71a6cc' +VERIFY_T16_SHA = '050e2dfba59911b8f29fd94422833d421367a0ad01c5e4ae916a75a235ee5e46' +RUNPORT_SHA = '322d29d43dbee8130cde15d4bf05396698b5e3dfe3735813899db91cc0ba7955' +ARCHIVED_RECEIPTS = ('ASYNC-AUDIT.json', 'OWNERS-AND-PROFILE.json', 'GLOBAL-PREFIX.json', + 'PREFIX-AND-INVOICE.json', 'IDLE-REMAINDER-FRAMES.json') + + +def pins_ok(): + rows = [f for f in json.loads((HERE / 'SOURCES.json').read_text())['files'] if f['usage'] == 'input'] + for f in rows: + assert sha((ROOT / f['path']).read_bytes()) == f['sha256'], 'pinned input changed: ' + f['path'] + return [f['path'] for f in rows] + + +def extract(tmp): + source = tmp / 'source' + source.mkdir() + with tarfile.open(STAGE / 'upstream1087.tar.gz') as t: + assert sha(t.extractfile('verify_t16.py').read()) == VERIFY_T16_SHA, 'verify_t16.py is not the pinned PR1087 driver' + t.extractall(source, filter='data') + for name in ('OWNERS.json.gz', 'PLAN.json', 'SPEC.json'): # the stage package's three frozen overlays + (source / 'data' / name).write_bytes((STAGE / 'data' / name).read_bytes()) + script = source / 'code/audit_allcold.py' + assert sha(script.read_bytes()) == AUDIT_SHA, 'audit_allcold.py is not the pinned PR1087 file' + assert sha(RUNPORT.read_bytes()) == RUNPORT_SHA, 'run_portable.py is not the pinned wrapper' + strict = tmp / 'strict' + strict.mkdir() + prefix = 'candidate/strict/' + with tarfile.open(ARCHIVE) as t: + members = [m for m in t.getmembers() if m.name.startswith(prefix) and m.isfile()] + for m in members: + m.name = m.name[len(prefix):] + t.extractall(strict, members=members, filter='data') + return source, strict + + +CANON = lambda t: json.dumps(t, sort_keys=True, separators=(',', ':')).encode() + + +def load_template(hosted): + raw = gzip.decompress((hosted / 'GLOBAL-TEMPLATE.json.gz').read_bytes()) + t = json.loads(raw) + assert CANON(t) == raw, 'fixture template is not emit-canonical' + return t + + +def reemit(hosted, t, summary_raw=None): + """Rewrite GLOBAL-TEMPLATE.json.gz exactly as emit serialises it (canonical JSON payload, + portable gzip, HOSTED.json coherent). Only decompressed bytes are ever hashed downstream.""" + raw = CANON(t) + gz = bytearray(gzip.compress(raw, 9, mtime=0)) + gz[9] = 255 + (hosted / 'GLOBAL-TEMPLATE.json.gz').write_bytes(bytes(gz)) + res = json.loads((hosted / 'HOSTED.json').read_text()) if summary_raw is None else summary_raw + res['template_sha256'] = sha(raw) + (hosted / 'HOSTED.json').write_bytes((json.dumps(res, indent=2) + '\n').encode('utf-8')) + return sha(raw) + + +def time_swap(t): + """First deterministic equal-width cross-time pair in pooled-slot order; swap their times.""" + slots = t['hosting']['pooled_slots'] + for a in range(len(slots)): + for b in range(a + 1, len(slots)): + if slots[a]['width'] == slots[b]['width'] and slots[a]['time'] != slots[b]['time']: + pair = (a, b, dict(slots[a]), dict(slots[b])) + slots[a]['time'], slots[b]['time'] = slots[b]['time'], slots[a]['time'] + return pair + raise AssertionError('no equal-width cross-time pooled-slot pair') + + +def width_profile(slots): + return dict(sorted(Counter((s['time'], s['width']) for s in slots).items())) + + +def run_stages(tmp, source, strict, fixture): + """Run the frozen post-emit stage sequence exactly as verify_t16.py invokes it; stop at the first failure.""" + g, g8 = strict / 'global', strict / 'global8' + work = tmp / ('run-' + fixture) + work.mkdir() + env = dict(os.environ, PYTHONDONTWRITEBYTECODE='1', PYTHONUTF8='1', PYTHONINTMAXSTRDIGITS='0', + MULTICOLD_SOURCE=str(MC), GAIN_PR564_SOURCE=str(MC)) + for k in ('PYTHONOPTIMIZE', 'WITNESS_DISCOVER', 'WITNESS_RECORD'): + env.pop(k, None) + env['WITNESS_SPEC'] = str(source / 'data/SPEC.json') + spec = json.loads((source / 'data/SPEC.json').read_text()) + ledger = [] + + def run(name, script, *args, extra=None): + log = work / (name + '.log') + with log.open('w') as s: + rc = subprocess.run([sys.executable, '-B', str(RUNPORT), str(script), *map(str, args)], + cwd=str(ROOT), env=dict(env, **(extra or {})), + stdout=s, stderr=subprocess.STDOUT).returncode + entry = dict(stage=name, exit_code=rc) + if rc: + text = log.read_text(errors='replace') + frames = re.findall(r'File "[^"]*?([A-Za-z_0-9]+\.py)", line (\d+)', text) + entry['failure'] = dict(exception=(re.findall(r'^(\w*(?:Error|Exception))(?::|$)', text, re.M) or ['?'])[-1], + line=int(frames[-1][1]) if frames else None, + script=frames[-1][0] if frames else None) + ledger.append(entry) + return rc == 0 + + code, t16 = source / 'code', source / 't16' + ok = run('audit', code / 'audit_allcold.py', g / 'base', g / 'hosted', g / 'audit/ASYNC-AUDIT.json', + '--mutation-controls') + if ok: + ok = run('invoice', code / 'invoice_allcold.py', '--base', g / 'base', '--hosted', g / 'hosted', + '--audit', g / 'audit', '--out', g / 'audit', '--retained-invoice', RETAINED_INVOICE) + if ok: + ok = run('assembly', code / 'assemble.py', MC, g, work / 'ASSEMBLY.json') + if ok: + for nm, sc, out in (('admission-controls', 'assembly_controls.py', 'ADMISSION-CONTROLS.json'), + ('bit-supplier-controls', 'bit_supplier_controls.py', 'BIT-SUPPLIER-CONTROLS.json')): + if not run(nm, code / sc, '--source-package', MC, '--owner', g / 'audit/OWNERS-AND-PROFILE.json', + '--invoice', g / 'audit/PREFIX-AND-INVOICE.json', '--out', work / out): + ok = False + break + if ok: + ok = run('independent-t16', t16 / 'indep16.py', '--baseline-parent', + g8 / 'base/split-centres/GLOBAL-TEMPLATE.json.gz', '--owners', source / 'data/OWNERS.json.gz', + '--word', g / 'base/providers/gauge44/CANONICAL-WORD.json.gz', '--out', work / 'INDEPENDENT-T16.json', + extra={'T16_EXPECTED_IDLE': json.dumps(spec['expected']['idle_remainder_profile']), + 'T16_EXPECTED_WIDTHS': json.dumps(spec['providers']['gauge44']['inventory'])}) + return ledger, work + + +def final_binding(work, strict, source, strict_pristine): + """verify_t16.py's final binding, asserted 1:1 against fresh stage outputs (plus verify_frontier1094's + published physical inventory). Provider content binding against baseline1042's slots/local is not + re-checked here: in a fixture run the bound copy is its own input, upstream of the mutation point.""" + g, g8 = strict / 'global', strict / 'global8' + rd = lambda p: json.loads(Path(p).read_text()) + asm, ind = rd(work / 'ASSEMBLY.json'), rd(work / 'INDEPENDENT-T16.json') + inv = rd(g / 'audit/PREFIX-AND-INVOICE.json') + bc, ac = rd(work / 'BIT-SUPPLIER-CONTROLS.json'), rd(work / 'ADMISSION-CONTROLS.json') + lift = rd(strict_pristine / 'lift/PARENT-LIFT-RECEIPT.json') + owner, audit = rd(g / 'audit/OWNERS-AND-PROFILE.json'), rd(g / 'audit/ASYNC-AUDIT.json') + spec8, spec16 = rd(source / 'data/SPEC-T8.json'), rd(source / 'data/SPEC.json') + r16 = len(rd(source / 'data/T8/PLAN.json')['provider_registry']['gauge44']['widths']) + checks = {} + + def ck(name, cond): + checks[name] = bool(cond) + + ck('assembly_status_and_kappa', asm['status'] == 'PASS_GAUGE44_NATIVE_EXACT_GLOBAL_ASSEMBLY' + and asm['kappa'] == spec16['expected']['kappa']) + ck('bit_supplier_controls', bc['status'] == 'PASS_PR589_BIT_SUPPLIER_DERIVED_DEFICIT_NEGATIVE_CONTROLS' + and len(bc['rejected_controls']) == 12 and bc['derived_deficit'] == 10272) + ck('admission_controls', not ac['discovery_accepted_controls'] and len(ac['rejected_controls']) == 69) + ck('independent_t16_status', ind['status'] == 'PASS_INDEPENDENT_T16_LITERAL_WINDOWS_CHARTS_PREFIX_ROW' + and len(ind['controls_rejected']) == 6) + ck('profile_equality', ind['profile'] == asm['complex_profile']) + ck('idle_equality', {str(k): v for k, v in lift['idle16'].items()} == {str(k): v for k, v in ind['idle'].items()}) + ck('prefix_bits', ind['prefix_bits'] == inv['global_prefix_bits'] < 512) + ck('row_coefficients', ind['physical_row_coefficient'] == inv['finite']['physical_row_coefficient'] + < inv['finite']['retained_row_coefficient'] == 20161) + ck('charged_prefix_bits', inv['charged_prefix_bits'] == 512) + ck('rank_mass_deficit', ind['rank_mass'] == owner['rank_mass'] == 1041408 and ind['deficit'] == owner['deficit'] == 5120) + ck('finite_strict_checks', inv['strict_checks'] and all(inv['strict_checks'].values())) + ck('outer_margins', len(asm['outer']) == 47 and all(Fraction(v) > 0 for v in asm['outer'].values())) + ck('controller_mutation_controls', audit['mutation_controls_run'] and len(audit['mutations_rejected']) == 15) + ck('roles_counts', ind['roles'] == owner['hosted_residuals'] == audit['complex_hosted_residuals'] == 144 * r16) + ck('invocation_counts', ind['calls'] == audit['complete_invocations'] == inv['complete_invocation_sweeps'] == 144) + ck('chart_counts', ind['actual_73_axis_charts'] == owner['actual_73column_sigmas'] + == audit['actual_orthogonal_73column_maps']) + ck('template_sha_binding', asm['template_sha256'] == owner['template_sha256'] == inv['template_sha256']) + ck('audit_file_hashes', inv['owner_audit_sha256'] == sha((g / 'audit/OWNERS-AND-PROFILE.json').read_bytes()) + and inv['controller_audit_sha256'] == sha((g / 'audit/ASYNC-AUDIT.json').read_bytes())) + ck('kappa_improves_t8', Fraction(asm['kappa']) > Fraction(spec8['expected']['kappa'])) + ck('published_inventory', ind['roles'] == 69120 and ind['prefix_bits'] == 397 and inv['charged_prefix_bits'] == 512 + and ind['physical_row_coefficient'] == 16469 + and bc['derived_deficit'] == 10272 + and asm['kappa'] == '836330098538459/500000000000000000') + return dict(checks=checks, passed=sum(checks.values()), total=len(checks)) + + +def link_check(g, owners_bytes): + """The audited obligation: accepted template pooled_slots == frozen OWNERS (times included), and + hosting.owner_map_sha256 names that exact gzip. PR #1959's template_to_frozen_owners_binding.""" + owners = json.loads(gzip.decompress(owners_bytes)) + t = load_template(g / 'hosted') + pooled = t['hosting']['pooled_slots'] + diffs = [] + for i, (z, o) in enumerate(zip(pooled, owners)): + for k in o: + if z[k] != o[k]: + diffs.append(dict(index=i, field=k, template=z[k], owners=o[k])) + if len(pooled) != len(owners): + diffs.append(dict(index=None, field='length', template=len(pooled), owners=len(owners))) + return dict(pooled_slots_equal_frozen_owners=not diffs, + owner_map_sha256_names_frozen_gzip=t['hosting']['owner_map_sha256'] == sha(owners_bytes), + roles=len(owners), diff_count=len(diffs), diffs=diffs[:8]) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument('--write', action='store_true', help='regenerate EXPECTED.json instead of comparing with it') + a = ap.parse_args() + pinned = pins_ok() + result = dict(status='PASS_SELECTED_OWNERMAP_BINDING_GAP', pinned_inputs=pinned, + audit_script_sha256=AUDIT_SHA, verify_t16_sha256=VERIFY_T16_SHA, + run_portable_sha256=RUNPORT_SHA, fixtures={}) + with tempfile.TemporaryDirectory(prefix='ownermap-binding-gap-') as d: + tmp = Path(d) + source, strict = extract(tmp) + owners_bytes = (source / 'data/OWNERS.json.gz').read_bytes() + pristine_hosted = tmp / 'pristine-hosted' + shutil.copytree(strict / 'global/hosted', pristine_hosted) + pristine_audit = tmp / 'pristine-audit' + shutil.copytree(strict / 'global/audit', pristine_audit) + strict_pristine = strict # archived receipts (lift/, top level) are never overwritten + g = strict / 'global' + + def restore(): + for src in (pristine_hosted, pristine_audit): + dst = g / ('hosted' if src is pristine_hosted else 'audit') + for f in dst.iterdir(): + f.unlink() + for f in src.iterdir(): + (dst / f.name).write_bytes(f.read_bytes()) + + def norm(name, r): + """Receipts compared through canonical proof-relevant content. Two normalizations are + environment serialisation, not mathematics (CONTRIBUTING.md 'Regenerated artifacts and + hashes'): indep16 keys input_sha256 by absolute input paths, and the frozen invoice's + artifact_semantic_sha256 map is serialized in directory-enumeration order, so the byte + hash assemble records as input_sha256.fresh_invoice is machine-dependent.""" + r = json.loads(json.dumps(r)) + if name == 'INDEPENDENT-T16.json': + r['input_sha256'] = sorted(r['input_sha256'].values()) + elif name == 'ASSEMBLY.json': + r['input_sha256'] = {k: v for k, v in sorted(r['input_sha256'].items()) if k != 'fresh_invoice'} + return r + + def diff_paths(a, b, prefix='', out=None): + out = [] if out is None else out + if type(a) is not type(b): + out.append(prefix) + elif isinstance(a, dict): + for k in sorted(set(a) | set(b)): + diff_paths(a.get(k), b.get(k), prefix + '/' + str(k), out) + elif isinstance(a, list) and len(a) == len(b): + for i, (x, y) in enumerate(zip(a, b)): + diff_paths(x, y, prefix + '/' + str(i), out) + elif a != b: + out.append(prefix) + return out + + def record(name, mutate, expect): + restore() + hosted = g / 'hosted' + t = load_template(hosted) + facts = mutate(t, hosted) + ledger, work = run_stages(tmp, source, strict, name) + accepted = all(e['exit_code'] == 0 for e in ledger) + entry = dict(mutation=facts, stage_ledger=ledger, accepted_by_chain=accepted) + if accepted: + entry['final_binding'] = final_binding(work, strict, source, strict_pristine) + archived = {f: json.loads((pristine_audit / f).read_text()) for f in ARCHIVED_RECEIPTS} + archived['ASSEMBLY.json'] = json.loads(strict_pristine.joinpath('ASSEMBLY.json').read_text()) + archived['INDEPENDENT-T16.json'] = json.loads(strict_pristine.joinpath('INDEPENDENT-T16.json').read_text()) + fresh = {f: json.loads((g / 'audit' / f).read_text()) for f in ARCHIVED_RECEIPTS} + fresh['ASSEMBLY.json'] = json.loads((work / 'ASSEMBLY.json').read_text()) + fresh['INDEPENDENT-T16.json'] = json.loads((work / 'INDEPENDENT-T16.json').read_text()) + entry['receipt_diff_paths_vs_archived'] = { + f: diff_paths(norm(f, archived[f]), norm(f, fresh[f]))[:12] for f in sorted(archived)} + entry['receipts_match_archived'] = {f: not entry['receipt_diff_paths_vs_archived'][f] + for f in sorted(archived)} + entry['link_check'] = link_check(g, owners_bytes) + entry['expected'] = expect + result['fixtures'][name] = entry + return entry + + def f0(t, hosted): + raw = CANON(t) + hosted_bytes = (hosted / 'HOSTED.json').read_bytes() + reemit(hosted, t) # serialization fidelity: rewrite with zero semantic change + same_payload = gzip.decompress((hosted / 'GLOBAL-TEMPLATE.json.gz').read_bytes()) == raw + same_summary = json.loads((hosted / 'HOSTED.json').read_text()) == json.loads(hosted_bytes) + assert same_payload and same_summary, 're-emit changed the fixture semantically' + return dict(kind='unmutated re-emit', payload_identical=True, summary_identical=True) + + def f1(t, hosted): + before = width_profile(t['hosting']['pooled_slots']) + a, b, sa, sb = time_swap(t) + after = width_profile(t['hosting']['pooled_slots']) + assert before == after, 'the swap must preserve every per-time width count' + digest = reemit(hosted, t) + return dict(kind='equal-width cross-time pooled-slot time swap (coherent re-emit)', + template_sha256=digest, width_profile_preserved=True, + swapped=[dict(index=i, stage=s['stage'], replica=s['replica'], helper=s['helper'], + width=s['width'], time_before=s['time'], time_after=t2) + for i, s, t2 in ((a, sa, sb['time']), (b, sb, sa['time']))]) + + def f2(t, hosted): + t['hosting']['owner_map_sha256'] = '0' * 64 + digest = reemit(hosted, t) + return dict(kind='hosting.owner_map_sha256 set to a wrong digest (coherent re-emit)', + template_sha256=digest, value='0' * 64) + + def f3(t, hosted): + slot = t['hosting']['pooled_slots'][0] + old = slot['port'] + slot['port'] = old + 1 + digest = reemit(hosted, t) + return dict(kind='pooled-slot port (checked roles field) changed (coherent re-emit)', + template_sha256=digest, + changed=dict(index=0, stage=slot['stage'], replica=slot['replica'], helper=slot['helper'], + field='port', frm=old, to=old + 1)) + + def f4(t, hosted): + pair = time_swap(t) + raw = CANON(t) + (hosted / 'GLOBAL-TEMPLATE.json.gz').write_bytes(gzip.compress(raw, mtime=0)) + return dict(kind='time swap with stale HOSTED.json template_sha256 (artifact inconsistency)', + template_sha256=sha(raw), stale_summary=True, + swapped=[dict(index=i, stage=s['stage'], replica=s['replica'], helper=s['helper'], + time_before=s['time']) for i, s in ((pair[0], pair[2]), (pair[1], pair[3]))]) + + e0 = record('F0', f0, 'accepted; link check passes') + assert e0['accepted_by_chain'] and e0['link_check']['pooled_slots_equal_frozen_owners'] \ + and e0['link_check']['owner_map_sha256_names_frozen_gzip'], 'F0 positive control failed' + assert all(e0['receipts_match_archived'].values()), 'F0 does not reproduce the archived selected receipts: ' + \ + json.dumps(e0['receipt_diff_paths_vs_archived'], sort_keys=True) + assert e0['final_binding']['passed'] == e0['final_binding']['total'], 'F0 final binding failed: ' + \ + json.dumps(e0['final_binding'], sort_keys=True) + + e1 = record('F1', f1, 'accepted; link check fails on exactly two role times') + assert e1['accepted_by_chain'] and e1['final_binding']['passed'] == e1['final_binding']['total'], \ + 'F1 must pass the complete caller and the final binding' + assert not e1['link_check']['pooled_slots_equal_frozen_owners'] and e1['link_check']['diff_count'] == 2, \ + 'F1 must fail the link check on exactly two fields' + assert all(d['field'] == 'time' for d in e1['link_check']['diffs']), 'F1 diff must be time-only' + assert e1['link_check']['owner_map_sha256_names_frozen_gzip'], 'F1 keeps the correct owner_map_sha256' + + e2 = record('F2', f2, 'accepted; link check fails on owner_map_sha256') + assert e2['accepted_by_chain'] and e2['final_binding']['passed'] == e2['final_binding']['total'], \ + 'F2 must pass the complete caller and the final binding' + assert e2['link_check']['pooled_slots_equal_frozen_owners'] and not e2['link_check']['owner_map_sha256_names_frozen_gzip'] + + e3 = record('F3', f3, 'rejected by audit at line 172') + assert not e3['accepted_by_chain'] and e3['stage_ledger'][0]['failure']['line'] == 172, 'F3 rejection line' + + e4 = record('F4', f4, 'rejected by audit at line 185') + assert not e4['accepted_by_chain'] and e4['stage_ledger'][0]['failure']['line'] == 185, 'F4 rejection line' + out = json.dumps(result, indent=1, sort_keys=True) + '\n' + if a.write: + (HERE / 'EXPECTED.json').write_text(out) + else: + assert out == (HERE / 'EXPECTED.json').read_text(), 'result differs from EXPECTED.json' + print('PASS_SELECTED_OWNERMAP_BINDING_GAP f0_accepted=1 f1_accepted_time_swap=1 f2_accepted_sha=1 ' + 'f3_rejected_line=%s f4_rejected_line=%s' % (result['fixtures']['F3']['stage_ledger'][0]['failure']['line'], + result['fixtures']['F4']['stage_ledger'][0]['failure']['line'])) + + +if __name__ == '__main__': + main()