diff --git a/research/p104-owned-holder-handoffs/INPUTS.json b/research/p104-owned-holder-handoffs/INPUTS.json new file mode 100644 index 0000000000..6dfe655d2f --- /dev/null +++ b/research/p104-owned-holder-handoffs/INPUTS.json @@ -0,0 +1,18 @@ +[ + { + "local_path": "inputs/pr5569-WORD.json.gz", + "repository": "CrocSwap/integer-mult-bounds", + "commit": "af9f091ac6f30c6eaacb9c27777bfbe2d0e5ffdf", + "path": "research/p104-priced-handoff/WORD.json.gz", + "git_blob": "3f4eb4be87a2f7a96391c39d960eedeb7bef5d1c", + "sha256": "12224e9a13a49c830b304338a8d31610fb43da15d1a650537733c27f7864310f" + }, + { + "local_path": "inputs/pr5561-WORD.json.gz", + "repository": "CrocSwap/integer-mult-bounds", + "commit": "af9f091ac6f30c6eaacb9c27777bfbe2d0e5ffdf", + "path": "research/p104-priced-handoff/inputs/pr5561-WORD.json.gz", + "git_blob": "c367d865a154ffd2ae0b6f6053469580a139f487", + "sha256": "b3701d78bee3661382ebe5414e678dfc9ee292e254cb9dc56339c2d2f45fdf44" + } +] diff --git a/research/p104-owned-holder-handoffs/README.md b/research/p104-owned-holder-handoffs/README.md new file mode 100644 index 0000000000..4b74805b86 --- /dev/null +++ b/research/p104-owned-holder-handoffs/README.md @@ -0,0 +1,144 @@ +# Close the owned-holder gap in the P104 handoff screen + +This package proves a finite obstruction for the complete P104 words of +PR #5561 and PR #5569. At all 5,252 event boundaries where an owned scratch +register holds a productive helper input, two public holders have incompatible +frame anchors. This includes all 2,832 points omitted by the predecessor scan. +No common frame can contain all the public holders at such a cut. + +The result rules out a single common-frame successor reset at every one of +these cuts, with the scalar event order and its scalar frames fixed. It allows +any binary subspace as the new frame, not just a listed source frame. It also +allows any number of extra common-frame registers. It grants free removal of +all owned holders: the public holders alone still obstruct the reset. + +This closes a named search gap. It does not raise kappa. It tells a new search +to change public scalar anchors or use a reset with more than one frame, +instead of spending time on cheaper scratch transport in this fixed word. + +## Sources and overlap + +Base: canonical main `5e0052ca0aa3036a3afa8b0438927377c8d97d53`, priority 7. +The selected result is unchanged. The source-binding baseline passed after +byte-exact materialization of its files omitted by the sparse checkout. + +The [claim in Discussion #5576](https://github.com/CrocSwap/integer-mult-bounds/discussions/5576) +states the bounded domain. The current PR and Discussion cache, the handoff +hub and all-state related PR search were checked before this work. These +checks do not cover unpublished work and do not prove novelty. This work does +not take the event-retime scope in #5574 or the basis and private-COPY scope +in #5125. + +The submission refresh also found the concurrent claim in +[Discussion #5575](https://github.com/CrocSwap/integer-mult-bounds/discussions/5575). +That task changes the scalar realization at the owned boundary. This package +keeps that realization and all its scalar anchors fixed. The separator result +was sent there as supporting evidence. It does not close that changed-program +scope or reserve a new search. + +The two compressed words are byte-exact inputs from GamingPuzzled's +[PR #5569](https://github.com/CrocSwap/integer-mult-bounds/pull/5569), head +`af9f091ac6f30c6eaacb9c27777bfbe2d0e5ffdf`. `INPUTS.json` records the exact +Git object and SHA256 for each file. One is DreamingOfClouds' PR #5561 word. +The other contains GamingPuzzled's helper-706 handoff. We retain both, so the +result binds both the original omitted domain and the stronger successor. +No pending Git object or network is needed to replay this package. + +## Proof + +Fix a boundary after event i and a productive input h. Let S contain h's own +public register and every public register whose exact scalar row has a +nonzero coefficient of h. An immediate successor reset at one frame F must +remove the old input from every other live row before it substitutes a new +independent input. In the common-frame reset model, every register in S must +be at F. A dirty owned row may also need service; we grant that service free. +This relaxation can only help a proposed reset. + +For public register j, let A_j be its last scalar frame at or before the cut, +or its initial frame if there was no scalar event. Let B_j be its next scalar +frame after the cut, or its final frame. All legal public MOVE events increase +the binary subspace. Thus, even if all MOVE stops in this interval can change, + + A_j <= F <= B_j. (1) + +This condition uses scalar reads as well as writes. Omitting a read would +permit a false frame window. For an owned descending register the inequalities +would reverse. We do not need either owned inequality for the obstruction. + +The certificate gives public holders j,k and a binary vector v at each cut, +with + + v in A_j, v not in B_k. (2) + +Equations (1) and (2) contradict A_j <= F <= B_k. This excludes every F, +including unlisted subspaces and frames of the wrong or right dimension. +Extra registers cannot remove the two mandatory public constraints. A more +favorable paid tariff, free scratch cleanup, or a cheaper frame path cannot +repair this contradiction while the anchors stay fixed. + +The exact scalar replay enumerates every boundary after every event, including +MOVE, INIT and CLEAR. It finds all helpers with a nonzero coefficient in any +live owned row. There are no additional cases before event zero: all owned +rows are empty. Both full source words have 5,252 cases and 2,832 touched cases. +The latter count reproduces the predecessor's omitted domain. Final scalar +rows and all public frame endpoints are checked as part of the replay. + +## Verification + +`verify.py` constructs the complete separator list from sparse rational rows. +A second pass uses integer rows with one binary denominator. It reconstructs +the required cuts and checks every certificate entry, with separate binary +Gaussian elimination for frame membership. Both passes check all final scalar +rows. The result receipt binds a SHA256 digest of the ordered separator list. + +Controls reject a missing cut, a zero separator, a false scalar anchor and an +owned register presented as a public holder. Small frame fixtures also contain +both feasible and infeasible memberships. The normal and optimized Python +replays must reproduce `RESULT.json` exactly. + +```sh +python3.12 -B scripts/research_package.py --package research/p104-owned-holder-handoffs +``` + +The package checks a finite scalar and frame obstruction. It does not replay +native phases, the full physical caller, compiler or outer budgets. None is +needed to exclude this necessary condition. A new positive kappa claim would +need all of them. The source's free helper endpoint contract and five framework +assumptions are not independently admitted here. Independent human review and +hosted CI are separate from the local evidence. + +## Maximization and limits + +The scope was extended from 2,832 selected scalar points to every owned-holder +event boundary, from listed frames to every binary subspace, and from actual +scratch costs to free scratch service. All cases still fail for public-only +reasons. This is a complete stopping rule for this common-frame reset family. + +The proof does not cover moving a scalar event or its frame, a different public +basis, a new scalar prefix, a distributed reset, or a construction that retains +old-input information for a later consumer. It is not a lower bound on all +P104 constructions. The predecessor's other screened domains and its numerical +cost decisions are not independently certified by this package. + +## Open next questions + +1. Can a changed public scalar anchor remove a listed separator and still save + net paid moment after all transports and target debits? Start with one + certificate cut, publish the changed events, and check the full suffix. + The event-retime and basis scopes already have active researchers. +2. Can a reset at several frames preserve later uses of the old input and return + all owned fields at lower net cost? A concrete caller and complete invoice + are missing. A static span calculation alone will not answer this question. + +## Attribution + +GamingPuzzled (Joel Pulikkan), PR #5569 and the successor-handoff method in +#5472; DreamingOfClouds, PR #5561 and #5553; thu-nmrc, PR #5551; Ali Romman, +PR #5544/#5545; Gareth Davidson (bitplane), David Arias (DavidAriasC), Cinna +Davis (1337isnot1337), Cao Yifan (intqwq), zxv, Jacob Alan Sussman and the +retained productive-helper, native and compiler lineage. The input words are +unchanged. The scalar semantics follow the published P104 contract; no source +interpreter is copied or imported. + +New proof and verifier: Ali Romman with substantial OpenAI Codex assistance. +Apache-2.0. Two arithmetic passes do not constitute independent human review. diff --git a/research/p104-owned-holder-handoffs/RESULT.json b/research/p104-owned-holder-handoffs/RESULT.json new file mode 100644 index 0000000000..daab80874f --- /dev/null +++ b/research/p104-owned-holder-handoffs/RESULT.json @@ -0,0 +1,30 @@ +{ + "new_kappa": null, + "rejected_controls": [ + "missing cut", + "zero separator", + "wrong anchor", + "owned instead of public holder" + ], + "status": "PASS_P104_OWNED_HOLDER_HANDOFFS", + "words": { + "pr5561-WORD.json.gz": { + "certificate_sha256": "dfcace797d5a8f49e2a104fcf8820232f02b0a174f478b24469c9b94a3b55211", + "distinct_public_anchor_states": 2340, + "events": 25140, + "helpers": 527, + "owned_holder_cuts": 5252, + "public_separators": 5252, + "touched_cuts": 2832 + }, + "pr5569-WORD.json.gz": { + "certificate_sha256": "dfcace797d5a8f49e2a104fcf8820232f02b0a174f478b24469c9b94a3b55211", + "distinct_public_anchor_states": 2340, + "events": 25140, + "helpers": 527, + "owned_holder_cuts": 5252, + "public_separators": 5252, + "touched_cuts": 2832 + } + } +} diff --git a/research/p104-owned-holder-handoffs/SOURCES.json b/research/p104-owned-holder-handoffs/SOURCES.json new file mode 100644 index 0000000000..095c982619 --- /dev/null +++ b/research/p104-owned-holder-handoffs/SOURCES.json @@ -0,0 +1,18 @@ +{ + "schema_version": 1, + "base_commit": "5e0052ca0aa3036a3afa8b0438927377c8d97d53", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "fc3d4ba105e1f097d0d7d051a308281e86ab1446", + "sha256": "66974efdced4083c00025f97d95d83909a226cdf801edb9301c195c6641f6903" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "f126dd2e2dad2b669e75d1d0d0bd05c600725602", + "sha256": "f7a48bfa2659b88378282a8eee7d9fe52cdf505594124c4d1686b58510fffd72" + } + ] +} diff --git a/research/p104-owned-holder-handoffs/TASK.json b/research/p104-owned-holder-handoffs/TASK.json new file mode 100644 index 0000000000..d65490c420 --- /dev/null +++ b/research/p104-owned-holder-handoffs/TASK.json @@ -0,0 +1,106 @@ +{ + "schema_version": 1, + "task_id": "scalar-to-moment", + "priority_revision": 7, + "base_commit": "5e0052ca0aa3036a3afa8b0438927377c8d97d53", + "original_card": { + "id": "scalar-to-moment", + "question": "Can a changed frame itinerary remove a paid transition beyond the already covered scalar A2 contractions?", + "starting_points": [ + "docs/research/research-family-status.md", + "certificates/selected-result.json", + "research/staged544-a2-graph-rewrite/README.md", + "research/staged544-separated-a2/README.md", + "research/staged544-a2-composition-audit/README.md", + "research/staged544-a1858-contraction-audit/README.md" + ], + "methods": [ + "Derive the changed legal frame itinerary and exact child histogram.", + "Prove a scoped obstruction showing why that scalar gain cannot change the paid contract." + ], + "acceptance_gate": "An unchanged histogram is not a kappa gain; record any separate runtime or proof benefit explicitly.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read the A2 and separated-A2 constructions and their composition/contraction audits. Scalar contractions can preserve the complete recursive histogram; fewer ADDs alone do not justify further scans.", + "scope": "Source-binding orientation only; no scientific replay, theorem acceptance or supplier admission. Budget any heavy replay separately." + }, + "first_experiment": "Identify an actual paid transition to remove and construct a legal replacement frame itinerary, charging restoration, source windows and changed helpers. Compare complete before/after histograms. Stop if only scalar count changes; report that benefit honestly, without expanding the same scalar-only census.", + "bottleneck_link": "A legal change to the histogram, not addition count alone, is the bridge to an exponent gain.", + "allowed_variations": [ + "One existing scalar simplification and one proposed frame-path change." + ], + "scope_change_triggers": [ + "Submitting only another scalar count as completion of a paid-profile task." + ], + "overlap_terms": [ + "scalar simplification", + "paid profile", + "R439", + "3998", + "4019", + "4112", + "4152", + "staged544-a2-graph-rewrite", + "staged544-separated-a2", + "staged544-a2-composition-audit", + "staged544-a1858-contraction-audit" + ], + "existing_checks": [ + { + "scope": "PR #3998: consecutive A2 scalar contraction", + "evidence": "research/staged544-a2-graph-rewrite/README.md", + "remaining": "A genuinely changed paid itinerary or a distinct independent audit; no default scalar-only expansion.", + "status": "author-checked", + "source_commit": "389268a6b5e0373a3125d5c6fcd6995f4ea324f5", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "PR #4019: separated A2 sites with unchanged recursive histogram", + "evidence": "research/staged544-separated-a2/README.md", + "remaining": "A genuinely changed paid itinerary or a distinct independent audit; no default scalar-only expansion.", + "status": "author-checked", + "source_commit": "f408e09df300b575235f7f77f81948071397e16d", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "PR #4112: composition audit", + "evidence": "research/staged544-a2-composition-audit/README.md", + "remaining": "A genuinely changed paid itinerary or a distinct independent audit; no default scalar-only expansion.", + "status": "author-checked", + "source_commit": "205b7380656ba4b6316a8a493b1a3c679b8f7a0e", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "PR #4152: A1858 contraction audit", + "evidence": "research/staged544-a1858-contraction-audit/README.md", + "remaining": "A genuinely changed paid itinerary or a distinct independent audit; no default scalar-only expansion.", + "status": "author-checked", + "source_commit": "caaa14cde5962adc372fdca71f63d0f29e73c8b2", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + } + ] + }, + "scope_status": "changed", + "actual_scope": "Close all owned-holder cuts of the fixed P104 words with public-only frame separators.", + "scope_change_reason": "The strongest complete handoff search explicitly omits this domain. A necessary frame condition decides it before more paid construction work.", + "bottleneck_link": "Exclude every common-frame reset in a named omitted domain and identify which fixed public constraints a kappa improvement must change.", + "budget_minutes": 90, + "stop_rule": "Check every owned-holder event cut, all possible common frames and free scratch service. Stop after the complete obstruction certificate; do not claim a global lower bound.", + "outcome": "completed", + "evidence": "README.md proof and RESULT.json; two exact scalar arithmetic passes.", + "author": "Ali Romman", + "ai_assistance": "Substantial OpenAI Codex assistance.", + "open_questions": [ + { + "question": "Can changed public anchors or a distributed reset defeat a separator?", + "why_it_matters": "Those changes can leave the excluded family and may change the paid moment.", + "first_step": "Supply one changed chronological prefix and its complete target/transport debit.", + "blocked_by": "No such caller is supplied; related event and basis scopes are active." + } + ] +} diff --git a/research/p104-owned-holder-handoffs/VERIFY.json b/research/p104-owned-holder-handoffs/VERIFY.json new file mode 100644 index 0000000000..6bf1a3b0f9 --- /dev/null +++ b/research/p104-owned-holder-handoffs/VERIFY.json @@ -0,0 +1,34 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [], + "commands": [ + { + "argv": [ + "{python}", + "-I", + "-W", + "error", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 150, + "checks": "All cuts, exact scalar exits, public separators and rejection controls." + }, + { + "argv": [ + "{python}", + "-I", + "-W", + "error", + "-O", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 150, + "checks": "The same complete receipt with optimized Python." + } + ] +} diff --git a/research/p104-owned-holder-handoffs/inputs/pr5561-WORD.json.gz b/research/p104-owned-holder-handoffs/inputs/pr5561-WORD.json.gz new file mode 100644 index 0000000000..c367d865a1 Binary files /dev/null and b/research/p104-owned-holder-handoffs/inputs/pr5561-WORD.json.gz differ diff --git a/research/p104-owned-holder-handoffs/inputs/pr5569-WORD.json.gz b/research/p104-owned-holder-handoffs/inputs/pr5569-WORD.json.gz new file mode 100644 index 0000000000..3f4eb4be87 Binary files /dev/null and b/research/p104-owned-holder-handoffs/inputs/pr5569-WORD.json.gz differ diff --git a/research/p104-owned-holder-handoffs/verify.py b/research/p104-owned-holder-handoffs/verify.py new file mode 100644 index 0000000000..ba5cf75cda --- /dev/null +++ b/research/p104-owned-holder-handoffs/verify.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 +"""Check all owned-holder cuts in two pinned P104 words. + +Ali Romman, with substantial OpenAI Codex assistance. Apache-2.0. +Word construction: GamingPuzzled, DreamingOfClouds, thu-nmrc and the P104 +predecessors listed in README.md. No source interpreter is imported. +""" +import bisect +from fractions import Fraction as Q +from hashlib import sha256 +import gzip +import json +from pathlib import Path +import sys + +HERE = Path(__file__).resolve().parent + + +def need(ok, message): + if not ok: + raise ValueError(message) + + +def span(basis): + out = {0} + for v in basis: + out |= {x ^ v for x in out} + return out + + +def member(v, basis): + pivots = {} + for x in basis: + while x: + k = x.bit_length() - 1 + if k in pivots: + x ^= pivots[k] + else: + pivots[k] = x + break + while v: + k = v.bit_length() - 1 + if k not in pivots: + return False + v ^= pivots[k] + return True + + +def timelines(w): + n = w['public'] + cur = list(w['initial']) + [None] * 8 + out = [[] for _ in cur] + owned = set() + for i, e in enumerate(w['program']): + op, r = e[:2] + if op == 'MOVE': + need(cur[r] == e[2] and (r in owned) == e[4], 'MOVE entry') + a, b = map(lambda f: span(w['frames'][f]), e[2:4]) + need(b < a if r in owned else a < b, 'MOVE direction') + cur[r] = e[3] + continue + if op == 'INIT': + need(r >= n and r not in owned, 'INIT role') + owned.add(r) + cur[r] = e[2] + regs = (r, e[2]) if op in ('ADD', 'COPY') else (r,) + frame = e[2] if op in ('INIT', 'CLEAR') else e[-1] + need(all(cur[j] == frame for j in regs), 'Scalar frames') + for j in regs: + out[j].append((i, frame)) + if op == 'CLEAR': + need(r in owned, 'CLEAR role') + owned.remove(r) + cur[r] = None + need(not owned and cur[:n] == w['final'], 'Final frames') + return out + + +def row_stream(w, dyadic=False): + """Use sparse fractions or integer rows with one binary denominator.""" + n = w['public'] + rows = [{r: 1} for r in range(n)] + [{} for _ in range(8)] + powers = [0] * len(rows) + for i, e in enumerate(w['program']): + op, r = e[:2] + old = set(rows[r]) + if op in ('INIT', 'CLEAR'): + rows[r] = {} + powers[r] = 0 + elif op != 'MOVE': + q = Q(e[-2]) + if dyadic: + k = q.denominator.bit_length() - 1 + need(q.denominator == 1 << k, 'Dyadic input') + s = r if op == 'SCALE' else e[2] + new = {x: q.numerator * a for x, a in rows[s].items()} + power = powers[s] + k + if op == 'ADD': + level = max(power, powers[r]) + new = {x: a << (level - power) for x, a in new.items()} + for x, a in rows[r].items(): + new[x] = new.get(x, 0) + (a << (level - powers[r])) + power = level + rows[r] = {x: a for x, a in new.items() if a} + powers[r] = power + while powers[r] and not any(a & 1 for a in rows[r].values()): + rows[r] = {x: a // 2 for x, a in rows[r].items()} + powers[r] -= 1 + else: + if op == 'SCALE': + rows[r] = {x: q * a for x, a in rows[r].items()} + elif op == 'COPY': + need(not rows[r], 'Empty COPY destination') + rows[r] = {x: q * a for x, a in rows[e[2]].items()} + else: + need(op == 'ADD', 'Opcode') + new = dict(rows[r]) + for x, a in rows[e[2]].items(): + new[x] = new.get(x, 0) + q * a + rows[r] = {x: a for x, a in new.items() if a} + touched = old | set(rows[r]) | {r} + if op in ('ADD', 'COPY'): + touched |= set(rows[e[2]]) | {e[2]} + yield i, rows, touched + live = set(w['live']) + for r, row in enumerate(rows): + unit = (1 << powers[r]) if dyadic else 1 + want = {r: unit} if r < n else {} + if r - 112 in live: + want[r - 112] = unit + need(row == want, 'Exact final scalar rows') + + +def audit(w, supplied=None, dyadic=False): + n = w['public'] + main = set(w['live']) | {r + 112 for r in w['live']} + helpers = set(range(n)) - main + need(len(helpers) == 527, 'Helper domain') + tl = timelines(w) + ix = [[i for i, _ in t] for t in tl] + frames = w['frames'] + masks = [sum(1 << v for v in span(b)) for b in frames] + records, states = [], set() + touched_points = 0 + witnesses = {} if supplied is None else {(r[0], r[1]): r for r in supplied} + if supplied is not None: + need(len(witnesses) == len(supplied), 'Unique certificate keys') + for i, rows, touched in row_stream(w, dyadic): + candidates = set().union(*(rows[j] for j in range(n, n + 8))) & helpers + for h in sorted(candidates): + holders = [j for j in range(n) if h in rows[j] or j == h] + bounds = [] + for j in holders: + k = bisect.bisect_right(ix[j], i) + a = tl[j][k - 1][1] if k else w['initial'][j] + b = tl[j][k][1] if k < len(tl[j]) else w['final'][j] + bounds.append((j, a, b)) + states.add((h, tuple(bounds))) + touched_points += w['program'][i][0] != 'MOVE' and h in touched + if supplied is None: + rec = next(([i, h, j, a, k, b, v] + for j, a, _ in bounds for k, _, b in bounds + for v in frames[a] if not (masks[b] >> v & 1)), None) + need(rec is not None, 'No public separator at cut ' + str((i, h))) + else: + need((i, h) in witnesses, 'Missing cut') + rec = witnesses.pop((i, h)) + _, _, j, a, k, b, v = rec + anchor = {r: (aa, bb) for r, aa, bb in bounds} + need(j in anchor and k in anchor, 'Public holder witness') + need(anchor[j][0] == a and anchor[k][1] == b, 'Exact scalar anchors') + need(member(v, frames[a]) and not member(v, frames[b]), 'Separator') + records.append(rec) + if supplied is not None: + need(not witnesses, 'Extra cuts') + return records, {'events': len(w['program']), 'helpers': len(helpers), + 'owned_holder_cuts': len(records), 'touched_cuts': touched_points, + 'distinct_public_anchor_states': len(states), + 'public_separators': len(records)} + + +def reject(fn, label): + try: + fn() + except ValueError: + return label + raise ValueError('Control accepted: ' + label) + + +def main(): + results = {} + for pin in json.loads((HERE / 'INPUTS.json').read_bytes()): + raw = (HERE / pin['local_path']).read_bytes() + need(sha256(raw).hexdigest() == pin['sha256'], 'Source byte pin') + w = json.loads(gzip.decompress(raw)) + records, summary = audit(w) + checked, second = audit(w, records, dyadic=True) + need(checked == records and second == summary, 'Independent arithmetic pass') + summary['certificate_sha256'] = sha256(json.dumps(records, separators=(',', ':')).encode()).hexdigest() + need(summary['owned_holder_cuts'] == 5252 and summary['touched_cuts'] == 2832, + 'Complete coverage count') + results[Path(pin['local_path']).name] = summary + if pin['local_path'].endswith('pr5569-WORD.json.gz'): + controls = [reject(lambda: audit(w, records[1:], True), 'missing cut')] + for label, index, value in [('zero separator', 6, 0), + ('wrong anchor', 3, -1), + ('owned instead of public holder', 2, 735)]: + bad = [r[:] for r in records] + bad[0][index] = value + controls.append(reject(lambda: audit(w, bad, True), label)) + # Nontrivial positive frame interval and two membership outcomes. + low, middle, high = [1], [1, 2], [1, 2, 4] + need(span(low) < span(middle) < span(high), 'Positive public interval') + need(member(3, middle) and not member(4, middle), 'Membership controls') + out = {'status': 'PASS_P104_OWNED_HOLDER_HANDOFFS', 'words': results, + 'rejected_controls': controls, 'new_kappa': None} + expected = HERE / 'RESULT.json' + if '--write' in sys.argv: + expected.write_text(json.dumps(out, indent=2, sort_keys=True) + '\n') + else: + need(out == json.loads(expected.read_bytes()), 'Exact result receipt') + print(json.dumps(out, sort_keys=True)) + + +if __name__ == '__main__': + main()