diff --git a/research/centre-seven-owner-fano-independent/README.md b/research/centre-seven-owner-fano-independent/README.md new file mode 100644 index 0000000000..e7bf97a43c --- /dev/null +++ b/research/centre-seven-owner-fano-independent/README.md @@ -0,0 +1,88 @@ +# Independent check of the seven-owner centre-repair exclusion + +**Independent verification. The audited claim reproduces. No new bound, supplier or κ.** + +[`centre-seven-owner-information-floor`](../centre-seven-owner-information-floor/README.md) +claims that no nonzero perturbation invisible to both the old centre +observations and the later query field has support on exactly seven owners. +With the earlier six-owner result, this keeps the repair floor at 104 +observations per owner for blocks of up to seven owners. The package was +archived on 2026-10-16 with no scientific replay. This package checks that +claim with a second implementation. For the central step, it uses exhaustive +search in place of the written argument. + +## What is checked, and how it differs + +| Step | Audited package | This check | +|---|---|---| +| 112 routes | Primary forward interpreter (`verify.py` path) | Chart-swap derivation in the predecessor's `independent.py` | +| Matrix products | 73 columns, hash buckets plus exact comparison | Exact 17×17 active block (spectator block checked to be the identity), row-major packing, exact dict keys | +| Relation census | Reduced triples, 50 classes of size 2 | The same quantity, recomputed. Histogram `{1: 1,379,852, 2: 50}` and the 50 braid edges agree with the receipt. Every relation word is multiplied out and gives the identity. | +| Support 7 forces a Fano plane | Written counting argument | Exhaustive search: of all line families on 7 points with line size ≥ 3, pair multiplicity ≤ 1 and point degree ≥ 3, the only ones are the 30 labelled Fano planes | +| Fano plane cannot be labelled | Written braid-pair / triangle-free argument | Exhaustive backtracking over all route labels of the 21 flags. Labels at each owner and on each line must be distinct. Each of the 28 six-cycles must read as a group relation. Result: no labelling. | + +The labelling search uses only the relation words. It does not use the fact +that they are braids or that the braid graph has no triangles. Two controls +show that the search is not vacuous. A Fano labelling is planted in the group +Z2^4 (label = point code XOR line code). When only its 28 cycle words are +admitted, the search recovers it. When one relation class is removed, the +search no longer accepts it. + +The six-cycle condition is necessary. For a cycle g–L1–h–L3–k–L2–g with port +labels a,b (at L1), c,d (at L3), e,f (at L2), the regular action +`source = output · ρ_p` gives `ρ_a ρ_b ρ_c ρ_d ρ_e ρ_f = 1`. The word is +cyclically reduced because distinct lines at one owner use distinct ports. +Each such word occurs as two distinct equal reduced triples, so the triple +census finds all of them. + +## Scope and limits + +The check covers the finite premises and the support-seven implication only. +It inherits, and does not recheck, three things: + +1. The predecessor's support ≥ 7 argument and its column premises. +2. The source-bound observation model (`Sx = R x`, `Tx(d) = Σ_p H[:,p] x_p(dρ_p)`). +3. The faithful matrix representation of the regular cover group. + +The chart-swap route derivation and the audited forward-interpreter routes come +from the same AI-assisted contributor lineage. They are separate code paths, +not separate human review. Blocks of eight or more owners, changed query cuts, +and all physical acquisition and restoration costs are outside this check. + +## Reproduce + +From the repository root, standard-library Python (measured: CPython 3.12 and 3.13, about 30 s each): + +```sh +python3 -I -B research/centre-seven-owner-fano-independent/verify.py +python3 scripts/research_package.py --package research/centre-seven-owner-fano-independent --check-only +``` + +Expected marker: `PASS_CENTRE_SEVEN_OWNER_FANO_INDEPENDENT`. On the same host, +the audited package's own `verify.py` also passed, in about 60 s. + +## Open next questions + +**Does open PR #3147's block floor for 8 to 12 owners survive an independent +search?** [#3147](https://github.com/CrocSwap/integer-mult-bounds/pull/3147) +(head `5cbc48b18cb05327a8dacab3f626412d3bc5e924`) claims no hidden perturbation +on blocks of up to 12 owners. It also recounts the same 50 braid relations. Its +circle-cancellation part was audited in #3149/#3151; this block-floor part has +no independent check yet. First step: list the incidence structures on 8 to 12 +points with line size ≥ 3, point degree ≥ 3 and pair multiplicity ≤ 1. Then run +`labelings()` over each, adding the length-8 relations from a reduced +4-against-4 product census where a structure has 8-cycles. Blocker: none for +the check itself; its relevance to κ stays gated by the replacement-endpoint +screen in the audited package. + +## Credit + +The audited claim, the relation census design and the Fano argument are from +the `centre-seven-owner-information-floor` contribution. The route interface, +the support ≥ 7 theorem and the chart-swap derivation are from +`centre-block-repair-information` (Aaron Wang/navinor's PR2391, Ross +Richardson). The selected `R/H` data are from ywz65's +`centre-owner-observation-floor`. Further lineage is credited in those packages. + +Prepared by Sudarsh Chaturvedi, with substantial assistance from Claude +(Anthropic). No independent human review is claimed. Apache-2.0. diff --git a/research/centre-seven-owner-fano-independent/RESULT.json b/research/centre-seven-owner-fano-independent/RESULT.json new file mode 100644 index 0000000000..d9570834fc --- /dev/null +++ b/research/centre-seven-owner-fano-independent/RESULT.json @@ -0,0 +1,21 @@ +{ + "active_block": 17, + "audited_package": "research/centre-seven-owner-information-floor", + "braid_edges": 50, + "class_size_histogram": { + "1": 1379852, + "2": 50 + }, + "cyclic_length6_relation_words_with_symmetry": 100, + "fano_labellings_meeting_all_relations": 0, + "fano_six_cycles": 28, + "mutant_control_rejects_planted": true, + "positive_control_labellings_found": 168, + "positive_control_planted_labelling_recovered": true, + "reduced_triples": 1379952, + "route_derivation": "centre-block-repair-information/independent.py chart swaps", + "scope": "independent finite check of the audited support-7 exclusion; no supplier, tariff or kappa change", + "status": "PASS_CENTRE_SEVEN_OWNER_FANO_INDEPENDENT", + "support_seven_structures": 30, + "support_seven_structures_all_fano": true +} diff --git a/research/centre-seven-owner-fano-independent/SOURCES.json b/research/centre-seven-owner-fano-independent/SOURCES.json new file mode 100644 index 0000000000..6c0d566393 --- /dev/null +++ b/research/centre-seven-owner-fano-independent/SOURCES.json @@ -0,0 +1,42 @@ +{ + "schema_version": 1, + "base_commit": "997e6a9ca7b4f95d50e366f61412fd640d72a88a", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "b1fea0c94436b236cc2b7d41a7be032811b94c81", + "sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "67f085acd86acd7d4e03922d9cf4b26bca66fed5", + "sha256": "47b42433f5a85809a3aadd29c2c339da2c6ed728e4f7762df6b88bccb7874cf0" + }, + { + "path": "research/centre-block-repair-information/RESULT.json", + "usage": "input", + "git_blob": "1127acd01bf0f575cfd25d36797cf320e8f5ace1", + "sha256": "0a53e2ee6f31165a9fff6950f78e965c5fc12a648bd31dcca46c6bbe2a352915" + }, + { + "path": "research/centre-block-repair-information/independent.py", + "usage": "input", + "git_blob": "d2db50ec7a595b1a1a87b7843fd5ca043c43fd25", + "sha256": "b298ff3902c14db11e4ed057259785efc185e8ae3bb39c0d1c432e2340ceb4ea" + }, + { + "path": "research/centre-seven-owner-information-floor/RESULT.json", + "usage": "input", + "git_blob": "5b8e492269934de4a0e2186a4c93d68f51b389f9", + "sha256": "47788ad1342fa96389e5c34469ea91d3b74f4975d886858ed9e1e6f336a8affc" + }, + { + "path": "research/community-frontier1094-audit/selected-replay.tar.gz", + "usage": "input", + "git_blob": "c24daf067f9c543421d89ca2eeaa4eb78a57fb6f", + "sha256": "f1da80610bf33c58527f18ad63b4350d0b864e1d4486141ee17ac9bed4d529d6" + } + ] +} diff --git a/research/centre-seven-owner-fano-independent/TASK.json b/research/centre-seven-owner-fano-independent/TASK.json new file mode 100644 index 0000000000..f0e2d26470 --- /dev/null +++ b/research/centre-seven-owner-fano-independent/TASK.json @@ -0,0 +1,66 @@ +{ + "schema_version": 1, + "task_id": "independent-moment", + "priority_revision": 6, + "base_commit": "997e6a9ca7b4f95d50e366f61412fd640d72a88a", + "original_card": { + "id": "independent-moment", + "question": "Does one changed supplier or assembly claim lack a source-bound independent check of a specific new implication?", + "starting_points": [ + "certificates/selected-result.json", + "research/rank7-joint-endframes/README.md", + "docs/research/research-family-status.md" + ], + "methods": [ + "Independently reconstruct a coordinated frame/circuit change and its single-change control, then derive the complete rank and physical ledger.", + "Implement certified moment inequalities through a different exact enclosure method." + ], + "acceptance_gate": "Demonstrate independence, source binding, positive cases and semantic failure controls. Name the existing checks and the missing coverage. More digits or another equivalent evaluation of an unchanged root is not by itself new verification coverage.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read #2979 joint rank-7 end-frame proposal and the revision-6 evidence status. Its claimed kappa is not selected. Compare coordinated changes with the losing single-change controls and locate the complete physical ledger; an aggregate moment alone does not validate the mechanism.", + "scope": "Positive control / source-bound orientation only; this is not a new construction or full cold replay." + }, + "first_experiment": "Prioritize a bounded independent audit of #2979: reconstruct one joint group and its single-lowering control, check the declared frames/chronology, then trace changed children, idle widths and cleanup buffers into the complete invoice and both supplier caps. Choose one still-missing implication after checking existing audits. Stop with exact unresolved dependencies if full admission exceeds the session; do not launch a new lowering census on an unvalidated premise or substitute extra root digits for mechanism checking.", + "bottleneck_link": "Independent pricing prevents unsupported frontier claims and can reveal genuine accounting slack.", + "allowed_variations": [ + "Reconstruct either a source ledger or a numeric enclosure with a different method." + ], + "scope_change_triggers": [ + "Importing the primary function and calling its output independent verification.", + "Promoting #2979 from merged source archive to validated supplier solely from author PASS receipts or aggregate pricing." + ], + "overlap_terms": [ + "independent moment", + "rank histogram", + "exact enclosure", + "2979", + "rank7 joint endframes", + "paired extraction", + "joint lowering" + ] + }, + "scope_status": "refined", + "actual_scope": "Independent finite check of the support-seven (Fano) exclusion in research/centre-seven-owner-information-floor: re-derive the 112 routes via the chart-swap derivation, recompute all cyclically reduced length-six route relations with a separate exact product, and replace the written braid/triangle step with exhaustive searches over support-seven incidence structures and Fano flag labellings.", + "scope_change_reason": "The card asks for an independent check of one new implication. The newest unreviewed implication on the centre-path lever is the support-seven exclusion archived on 2026-10-16 without scientific replay; this package checks exactly that step and nothing downstream.", + "bottleneck_link": "The centre path takes 64 of about 99.6 unpaid coordinates per call. The audited package rules out information compression of the shared centre repair through blocks of at most seven owners; this check makes that negative result rest on two implementations instead of one.", + "budget_minutes": 60, + "stop_rule": "Stop after the census and both exhaustive searches agree with the audited receipt, or report the first disagreement. Do not extend to eight-owner blocks.", + "outcome": "completed", + "evidence": "RESULT.json; verify.py reproduces it in about 30 s on CPython 3.13 (standard library only). The audited package's own verify.py also passed on the same host in about 60 s.", + "author": "Sudarsh Chaturvedi", + "ai_assistance": "Substantial assistance from Claude (Anthropic) for implementation and writing; no independent human review claimed.", + "open_questions": [ + { + "question": "Does open PR #3147's block floor for 8 to 12 owners (head 5cbc48b18cb05327a8dacab3f626412d3bc5e924) survive an independent search?", + "why_it_matters": "#3147 extends the centre-repair information floor from 7 to 12 owners. Its circle-cancellation part was audited in #3149/#3151, but the block-floor part has no independent check.", + "first_step": "List incidence structures on 8 to 12 points with line size >= 3, point degree >= 3 and pair multiplicity <= 1, then run labelings() over each, adding length-8 relations from a reduced 4-against-4 product census where needed.", + "blocked_by": "None for the check itself; relevance to kappa stays gated by the audited package's replacement-endpoint screen." + } + ] +} diff --git a/research/centre-seven-owner-fano-independent/VERIFY.json b/research/centre-seven-owner-fano-independent/VERIFY.json new file mode 100644 index 0000000000..efb92fe0a6 --- /dev/null +++ b/research/centre-seven-owner-fano-independent/VERIFY.json @@ -0,0 +1,23 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/centre-block-repair-information/RESULT.json", + "research/centre-block-repair-information/independent.py", + "research/centre-seven-owner-information-floor/RESULT.json", + "research/community-frontier1094-audit/selected-replay.tar.gz" + ], + "commands": [ + { + "argv": [ + "{python}", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 300, + "checks": "Authenticate pins; derive routes by chart swaps; exact reduced-triple census on the 17-coordinate active block; compare class histogram and braid edges to the audited receipt; check every relation multiplies to the identity; exhaust support-seven incidence structures (only the 30 labelled Fano planes) and Fano flag labellings against all six-cycle relations (none); a planted Z2^4 Fano labelling is recovered from its own cycle words and rejected once one relation class is removed." + } + ] +} diff --git a/research/centre-seven-owner-fano-independent/verify.py b/research/centre-seven-owner-fano-independent/verify.py new file mode 100644 index 0000000000..e2625e93fd --- /dev/null +++ b/research/centre-seven-owner-fano-independent/verify.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""Independent check of the support-seven (Fano) exclusion for selected centre repair. + +Re-derives the 112 owner routes through the predecessor's *independent* +chart-swap derivation (not the forward interpreter used by the audited +package), recomputes every cyclically reduced length-six route relation with a +separate row-major GF(2) product on the exact 17-coordinate active block, and +then replaces the written braid/triangle step with two exhaustive searches: + +1. every 7-point incidence structure meeting the support-seven degree and + pair conditions is a Fano plane; +2. no flag labelling of the Fano plane satisfies all 28 six-cycle relations. + +Exact dict keys (Python ints) decide matrix equality; there is no hash bucket. +Prepared with substantial Claude (Anthropic) assistance. +""" +from collections import Counter +from itertools import combinations +from pathlib import Path +import hashlib +import json +import types + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] +INDEPENDENT = ROOT / 'research/centre-block-repair-information/independent.py' +AUDITED = ROOT / 'research/centre-seven-owner-information-floor/RESULT.json' +ACTIVE = 17 +PORTS = 112 + + +def need(ok, message): + if not ok: + raise ValueError(message) + + +def canonical(value): + return (json.dumps(value, sort_keys=True, indent=2) + '\n').encode() + + +def check_pins(): + manifest = json.loads((HERE / 'SOURCES.json').read_bytes()) + for row in manifest['files']: + if row['usage'] == 'input': + raw = (ROOT / row['path']).read_bytes() + need(hashlib.sha256(raw).hexdigest() == row['sha256'], 'source pin: ' + row['path']) + + +def load_routes(): + """Routes from the chart-swap derivation, compiled from authenticated bytes.""" + module = types.ModuleType('pinned_independent_routes') + module.__file__ = str(INDEPENDENT) + exec(compile(INDEPENDENT.read_bytes(), module.__file__, 'exec'), module.__dict__) + word, phases = module.sources() + routes, _, _ = module.route_certificates(word, phases) + need(len(routes) == PORTS and len(set(routes)) == PORTS, 'route count') + return routes + + +def to_rows(columns): + """Restrict a 73-column involution to its active block, as packed rows.""" + need(all(columns[k] == 1 << k for k in range(ACTIVE, len(columns))), 'spectator block') + need(all(columns[k] < 1 << ACTIVE for k in range(ACTIVE)), 'active block closed') + return tuple(sum(((columns[j] >> i) & 1) << j for j in range(ACTIVE)) for i in range(ACTIVE)) + + +def mul(a, b): + """Row-major GF(2) product: row i of AB is the XOR of B's rows selected by A's row i.""" + out = [] + for row in a: + acc = 0 + while row: + j = (row & -row).bit_length() - 1 + acc ^= b[j] + row &= row - 1 + out.append(acc) + return tuple(out) + + +def pack(rows): + return sum(r << (ACTIVE * i) for i, r in enumerate(rows)) + + +IDENTITY = tuple(1 << i for i in range(ACTIVE)) + + +def relation_census(m): + """All reduced triples; exact classes keyed by the packed matrix.""" + n = len(m) + classes = {} + for a in range(n): + for b in range(n): + if a == b: + continue + ab = mul(m[a], m[b]) + for c in range(n): + if c != b: + classes.setdefault(pack(mul(ab, m[c])), []).append((a, b, c)) + sizes = Counter(map(len, classes.values())) + relations = set() + for words in classes.values(): + for left in words: + for right in words: + if left != right: + a, b, c = left + d, e, f = right + w = (a, b, c, f, e, d) + if all(w[i] != w[(i + 1) % 6] for i in range(6)): + relations.add(w) + return sum(sizes[k] * k for k in sizes), sizes, relations + + +def closed_under_symmetry(words): + out = set() + for w in words: + for r in range(6): + v = w[r:] + w[:r] + out.add(v) + out.add(v[::-1]) + return out + + +def product_is_identity(m, w): + acc = IDENTITY + for x in w: + acc = mul(acc, m[x]) + return acc == IDENTITY + + +def support_seven_structures(points=7, min_line=3, min_degree=3, pair_cap=1): + """All families of lines (subsets of size >= min_line) with each point pair in + at most pair_cap lines and each point on at least min_degree lines.""" + candidates = [frozenset(s) for k in range(min_line, points + 1) + for s in combinations(range(points), k)] + found = [] + + def extend(start, chosen, pair_count): + degree = Counter(p for line in chosen for p in line) + if all(degree[p] >= min_degree for p in range(points)): + found.append(tuple(sorted(tuple(sorted(l)) for l in chosen))) + for i in range(start, len(candidates)): + line = candidates[i] + pairs = list(combinations(sorted(line), 2)) + if all(pair_count[p] < pair_cap for p in pairs): + for p in pairs: + pair_count[p] += 1 + chosen.append(line) + extend(i + 1, chosen, pair_count) + chosen.pop() + for p in pairs: + pair_count[p] -= 1 + + extend(0, [], Counter()) + return found + + +FANO = tuple(tuple(sorted({i, (i + 1) % 7, (i + 3) % 7})) for i in range(7)) + + +def fano_six_cycles(lines): + """Six-cycles g-L1-h-L3-k-L2-g of the incidence graph, as six flags in + cycle order: (g,L1),(h,L1),(h,L3),(k,L3),(k,L2),(g,L2).""" + on = [[i for i, l in enumerate(lines) if p in l] for p in range(7)] + cycles = set() + for g in range(7): + for l1, l2 in combinations(on[g], 2): + for h in lines[l1]: + for k in lines[l2]: + if g in (h, k): + continue + for l3 in set(on[h]) & set(on[k]): + if l3 in (l1, l2): + continue + flags = ((g, l1), (h, l1), (h, l3), (k, l3), (k, l2), (g, l2)) + # one representative per undirected cycle + key = frozenset(flags) + cycles.add((key, flags)) + by_key = {} + for key, flags in cycles: + by_key.setdefault(key, flags) + return list(by_key.values()) + + +def labelings(lines, relations, alphabet, first=False): + """Backtracking over flag labels; returns every labelling meeting all six-cycle + relations, with distinct labels at each point and on each line.""" + cycles = fano_six_cycles(lines) + flags = sorted({f for c in cycles for f in c}) + index = {} + for w in relations: + for pos, x in enumerate(w): + index.setdefault((pos, x), set()).add(w) + cyc_of = {f: [(c, c.index(f)) for c in cycles if f in c] for f in flags} + label = {} + found = [] + + def consistent(cycle): + sets = [index.get((pos, label[f]), set()) for pos, f in enumerate(cycle) if f in label] + return not sets or bool(set.intersection(*sets)) + + def go(i): + if found and first: + return + if i == len(flags): + found.append(dict(label)) + return + f = flags[i] + used = {label[o] for o in label if o[0] == f[0] or o[1] == f[1]} + for x in alphabet: + if x in used: + continue + label[f] = x + if all(consistent(c) for c, _ in cyc_of[f]): + go(i + 1) + del label[f] + + go(0) + return cycles, found + + +def build(): + check_pins() + routes = load_routes() + m = [to_rows(r) for r in routes] + need(all(mul(x, x) == IDENTITY for x in m), 'involutions') + pair_products = {pack(mul(m[a], m[b])) for a in range(PORTS) for b in range(PORTS) if a != b} + need(len(pair_products) == PORTS * (PORTS - 1), 'unique ordered products') + + total, sizes, rel = relation_census(m) + need(total == PORTS * (PORTS - 1) ** 2, 'reduced triple count') + rel6 = closed_under_symmetry(rel) + need(rel6 == rel, 'relation set closed under rotation and reversal') + need(all(product_is_identity(m, w) for w in rel6), 'relations multiply to identity') + braid = sorted({tuple(sorted(set(w))) for w in rel6}) + need(all(len(set(w)) == 2 for w in rel6), 'every relation is a braid word') + + audited = json.loads(AUDITED.read_bytes()) + need([list(e) for e in braid] == audited['braid_edges'], 'braid edges differ from audited receipt') + need({str(k): v for k, v in sorted(sizes.items())} + == audited['exact_product_class_size_histogram'], 'class histogram differs from audited receipt') + + structures = support_seven_structures() + need(all(len(s) == 7 and all(len(l) == 3 for l in s) for s in structures), 'non-Fano structure') + need(len(structures) == 30, 'labelled Fano plane count') + loose = support_seven_structures(min_degree=2) + need(len(loose) > len(structures), 'degree control is not vacuous') + + cycles, found = labelings(FANO, rel6, range(PORTS)) + need(len(cycles) == 28, 'Heawood six-cycle count') + need(not found, 'a Fano labelling meets every six-cycle relation') + + # Positive control: plant a Fano labelling realised in the abelian group + # Z2^4 (label = point code XOR line code; every six-cycle word XORs to 0), + # admit only its own cycle words, and require the search to recover it. + # Removing one admitted cycle word must lose it: the pruning is not vacuous. + points, line_codes = (1, 2, 4, 8, 3, 5, 6), (9, 10, 12, 7, 11, 13, 14) + planted = {(p, l): points[p] ^ line_codes[l] + for l, line in enumerate(FANO) for p in line} + planted_words = {tuple(planted[f] for f in c) for c in cycles} + need(all(w[0] ^ w[1] ^ w[2] ^ w[3] ^ w[4] ^ w[5] == 0 for w in planted_words), + 'planted words are group relations') + _, control = labelings(FANO, closed_under_symmetry(planted_words), range(16)) + need(planted in control, 'search positive control lost the planted labelling') + admitted = closed_under_symmetry(planted_words) + dropped = admitted - closed_under_symmetry([min(planted_words)]) + need(dropped != admitted, 'mutant removes a relation class') + _, mutant = labelings(FANO, dropped, range(16)) + need(planted not in mutant, 'search accepted a labelling with a missing relation') + + return { + 'status': 'PASS_CENTRE_SEVEN_OWNER_FANO_INDEPENDENT', + 'audited_package': 'research/centre-seven-owner-information-floor', + 'route_derivation': 'centre-block-repair-information/independent.py chart swaps', + 'active_block': ACTIVE, + 'reduced_triples': total, + 'class_size_histogram': {str(k): v for k, v in sorted(sizes.items())}, + 'cyclic_length6_relation_words_with_symmetry': len(rel6), + 'braid_edges': len(braid), + 'support_seven_structures': len(structures), + 'support_seven_structures_all_fano': True, + 'fano_six_cycles': len(cycles), + 'fano_labellings_meeting_all_relations': 0, + 'positive_control_planted_labelling_recovered': True, + 'positive_control_labellings_found': len(control), + 'mutant_control_rejects_planted': True, + 'scope': 'independent finite check of the audited support-7 exclusion; no supplier, tariff or kappa change', + } + + +def main(): + import argparse + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--write', type=Path) + args = parser.parse_args() + result = build() + if args.write: + args.write.write_bytes(canonical(result)) + else: + need(canonical(result) == (HERE / 'RESULT.json').read_bytes(), 'receipt mismatch') + print('PASS_CENTRE_SEVEN_OWNER_FANO_INDEPENDENT braid_edges=50 fano_structures_only ' + 'fano_labellings=0 no_new_kappa') + + +if __name__ == '__main__': + main()