diff --git a/research/ledger-decision-binding/CLAIM.json b/research/ledger-decision-binding/CLAIM.json new file mode 100644 index 0000000000..691ae42323 --- /dev/null +++ b/research/ledger-decision-binding/CLAIM.json @@ -0,0 +1,24 @@ +{ + "track": "research consolidation", + "seed": 99, + "base_commit": "66849e1475c930fd7732b1a0067ffee7e1e95f60", + "candidates": [ + { + "task": "Check selected dependency heads against ledger receipts", + "decision": "Initial comparison agrees for all 20 explicit heads; no discrepancy established." + }, + { + "task": "Bind later-family review-ledger provenance to authoritative dispositions", + "decision": "Chosen: test whether internally consistent forged ledger heads or disposition fields evade the documented exact-head check." + }, + { + "task": "Reconcile historical exceptional-root negative-result scope", + "decision": "Overlap with open PR630; not selected." + } + ], + "object": "scripts/verify_pr_reconciliation.py and later-family ledger rows, especially seed-selected k492 family (99 modulo 5 = 4)", + "method": "Read pinned dispositions; baseline offline verifier; mutate only ledger provenance fields; bind those fields to authoritative records and add rejection controls. No mathematical frontier changes.", + "deliverable": "One focused provenance consistency fix with reproducible before/after evidence, tests and attribution.", + "coordination": "No pinned issue existed at the initial GitHub check; the only open issue was #650 requesting Discussions. Scope was recorded locally before the experiment. Open PR titles and bodies were queried and screened for overlap.", + "expected_finish": "2026-10-13T12:15:00+08:00" +} diff --git a/research/ledger-decision-binding/README.md b/research/ledger-decision-binding/README.md new file mode 100644 index 0000000000..734a78d0f7 --- /dev/null +++ b/research/ledger-decision-binding/README.md @@ -0,0 +1,106 @@ +# Bind review-ledger state to the recorded family decision + +Research consolidation and tooling correction; **no new κ claim**. +Contributor: roife, with substantial OpenAI Codex assistance, including a +separate agent's read-only review and regression-test work. Apache-2.0. + +Base: `66849e1475c930fd7732b1a0067ffee7e1e95f60`. +Assignment seed: **99**, drawn with Python `secrets.randbelow(100)` using OS +randomness. Seed modulo five selects the fifth recorded family, K492, for the +concrete mutation experiment. This is a bounded metadata audit, not a search +for a stronger construction. + +## Finding and resolution + +At this base, `scripts/verify_pr_reconciliation.py` accepted a PR626 ledger row +whose `reviewed_head` and `current_head` were both replaced with forty zeroes. +It compared the two ledger fields to each other, while separately comparing +the authoritative decision's reviewed head to its receipt. The missing +comparison was between the ledger row and that decision. + +Four other independent changes also passed the complete baseline checker: +marking the row `superseded`, substituting an unrelated integration location, +removing its closure record, and changing its GitHub state from `MERGED` to +`OPEN`. The last is an inconsistency with the frozen decision, not a claim +about live GitHub state. See [RESULT.json](RESULT.json) for exact inputs, +source hashes and both checker outcomes. + +The fix checks the following review-state fields projected by +`reconcile_pr_queue.py` against their final authoritative family decision: number, reviewed/current heads, +disposition, GitHub state, integration location, remaining question and closure, +plus completion flags and evidence when supplied by that family. A missing +field is distinct from an explicitly recorded null closure; JSON booleans +remain distinct from integers even inside completion records. The check applies +to all five recorded family rounds (89 records at this base). + +This adds a consistency condition to the existing source-hash, receipt, +changed-file, execution and incorporation checks. It does not replace any of +them. Partial reviews may still have incomplete execution and different +reviewed/current heads when those are the recorded values. The earlier +family533 records need not acquire completion fields they never had. + +All 89 committed family projections already match their decisions. **No +corruption of the committed ledger or mathematical certificate was found.** +The accepted conditional result at the base remains +`51110230636731/31250000000000000 = 0.001635527380375392`, from PR626 with PR543. +The selected certificate, construction packages and `upstream/` are unchanged. + +## Reproduce + +From a clone retaining the exact base commit, run: + +```sh +python3 -B research/ledger-decision-binding/reproduce.py +``` + +Only Python's standard library and Git are needed. The script reads the +baseline checker from the pinned commit and checks its SHA-256 before executing +it. It requires the original ledger bytes. Each case substitutes just one +in-memory ledger read; every other read still reaches the checkout. Accepted +baseline runs complete all existing checks; the fixed checker rejects altered +rows early. The script never writes a ledger or contacts GitHub. Expect both checkers to accept the unchanged ledger, the baseline to +accept all five mutations, and the fixed checker to reject all five with a +field-specific error. Its final line is: + +```text +PASS: unchanged ledger accepted; all five previously accepted provenance mutations rejected +``` + +Add `--output /path/to/result.json` to save a fresh report. Further focused +checks are: + +```sh +python3 -B -m unittest discover -s tests -p test_pr_reconciliation.py -v +python3 -B scripts/verify_pr_reconciliation.py +``` + +Expect 14 passing tests and a final `PASS immutable snapshot plus explicit +family rounds: 528 PRs, attributed heads, evidence pins and unresolved questions`. +The tests also reject removed fields and preserve legacy/partial-review +semantics. This is finite metadata validation, not a formal proof of the full +multiplication theorem. All inherited mathematical assumptions remain. + +## Selection, overlap and attribution + +Three candidate tasks were considered: selected dependency-head consistency, +later-family ledger consistency, and a historical exceptional-root negative +result. All 20 explicit selected dependency heads initially agreed with the +ledger; the exceptional-root topic overlapped open PR630. The ledger failure +mode was chosen. No pinned coordination issue existed when queried; the +scope was claimed locally before the experiment. All 491 open PR titles and +bodies were queried and screened for overlap, including searches for both +reconciliation script names and `pr-ledger`; none targeted this check. A later +refresh covered 497 open PRs through #677. PR675 audits the same selected +construction's mathematical owner boundaries, whereas this contribution binds +review-ledger metadata; neither depends on the other. The stopping rule was +to test the five listed mutations against PR626, repair their common missing +binding, and verify the full existing family projection without expanding +into construction or theorem review. + +This builds on Douglas R. Colkitt's reconciliation, family review and evidence +infrastructure, including commit `5c5567d` and its predecessors. PR626's owner +allocation is Chafik Boukhalfa's work on Jeremy Kothe's PR622/623 construction; +eumemic supplied PR543. Their original sources, notices, review limitations +and cumulative attribution remain intact. The independent agent corroborated +four equivalent full-checker mutations on PR622 using the same failure +mechanism; that corroboration is not a separate mathematical audit. diff --git a/research/ledger-decision-binding/RESULT.json b/research/ledger-decision-binding/RESULT.json new file mode 100644 index 0000000000..f9f2437a87 --- /dev/null +++ b/research/ledger-decision-binding/RESULT.json @@ -0,0 +1,101 @@ +{ + "base_commit": "66849e1475c930fd7732b1a0067ffee7e1e95f60", + "seed": 99, + "scope": "Offline review metadata projection; no mathematical or live GitHub-state claim.", + "baseline_checker_sha256": "423991a79b1ee0304c1ab075c041eff6d3111da08a75a5815ddc5661a47c12e2", + "fixed_checker_sha256": "0f65931402ccef5fc4a03ea5ce7195f6145fef74b50baf715904d67807e8ba50", + "source_sha256": { + "docs/research/pr-ledger.json": "367601c55451fb116ea8a2578520536e1dbef63116b0623faf6d29d42ca2eabd", + "research/community-family533-audit/DISPOSITIONS.json": "e0e51f92dc715525b8ff5e4987551336c292b0a466f1f3fb87221fb96d54544d", + "research/community-family588-audit/DISPOSITIONS.json": "70f66f8c223b0929904ff88e27713b5bdbdb0d6b92f4d55cf518565135d678b5", + "research/community-hyperplane599-audit/DISPOSITIONS.json": "00763b888859d15618eda871a42a9c0e6912ea33b932a29c02bc6e2df984efc1", + "research/community-cold617-audit/DISPOSITIONS.json": "d74bcfc9ae5219ecfe57e74466ae327c7e0d2524c87f52c143e74c6187db0984", + "research/community-k492-audit/DISPOSITIONS.json": "3f80fcee52c2d0abf1f428e9bf2ab9fab54985032535ae81495da759ee78d903" + }, + "cases": [ + { + "case": "unchanged", + "pr": 626, + "updates": {}, + "baseline": { + "status": "ACCEPTED" + }, + "fixed": { + "status": "ACCEPTED" + } + }, + { + "case": "paired_forged_heads", + "pr": 626, + "updates": { + "reviewed_head": "0000000000000000000000000000000000000000", + "current_head": "0000000000000000000000000000000000000000" + }, + "baseline": { + "status": "ACCEPTED" + }, + "fixed": { + "status": "REJECTED", + "reason": "#626: ledger differs from family decision: reviewed_head" + } + }, + { + "case": "unsupported_superseded", + "pr": 626, + "updates": { + "disposition": "superseded" + }, + "baseline": { + "status": "ACCEPTED" + }, + "fixed": { + "status": "REJECTED", + "reason": "#626: ledger differs from family decision: disposition" + } + }, + { + "case": "invented_location", + "pr": 626, + "updates": { + "integration_location": [ + "research/not-the-reviewed-contribution" + ] + }, + "baseline": { + "status": "ACCEPTED" + }, + "fixed": { + "status": "REJECTED", + "reason": "#626: ledger differs from family decision: integration_location" + } + }, + { + "case": "erased_closure", + "pr": 626, + "updates": { + "closure": null + }, + "baseline": { + "status": "ACCEPTED" + }, + "fixed": { + "status": "REJECTED", + "reason": "#626: ledger differs from family decision: closure" + } + }, + { + "case": "contradictory_github_state", + "pr": 626, + "updates": { + "github_state": "OPEN" + }, + "baseline": { + "status": "ACCEPTED" + }, + "fixed": { + "status": "REJECTED", + "reason": "#626: ledger differs from family decision: github_state" + } + } + ] +} diff --git a/research/ledger-decision-binding/reproduce.py b/research/ledger-decision-binding/reproduce.py new file mode 100644 index 0000000000..5b1e4370d2 --- /dev/null +++ b/research/ledger-decision-binding/reproduce.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +"""Replay a pinned before/after ledger-projection failure without changing files. + +roife, with substantial OpenAI Codex assistance. Apache-2.0. +This checks review metadata integrity, not the multiplication theorem. +""" +import argparse +import contextlib +from hashlib import sha256 +import io +import json +from pathlib import Path +import subprocess +import types +from unittest.mock import patch + +ROOT = Path(__file__).resolve().parents[2] +BASE = '66849e1475c930fd7732b1a0067ffee7e1e95f60' +CHECKER = 'scripts/verify_pr_reconciliation.py' +BASE_CHECKER_SHA256 = '423991a79b1ee0304c1ab075c041eff6d3111da08a75a5815ddc5661a47c12e2' +CASES = ( + ('unchanged', {}), + ('paired_forged_heads', {'reviewed_head': '0' * 40, 'current_head': '0' * 40}), + ('unsupported_superseded', {'disposition': 'superseded'}), + ('invented_location', {'integration_location': ['research/not-the-reviewed-contribution']}), + ('erased_closure', {'closure': None}), + ('contradictory_github_state', {'github_state': 'OPEN'}), +) + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +def load_checker(source): + module = types.ModuleType('ledger_reproduction_checker') + module.__file__ = str(ROOT / CHECKER) + exec(compile(source, module.__file__, 'exec'), module.__dict__) + return module + + +def run_case(checker, ledger, updates): + # Only the ledger JSON read is replaced. Every source, receipt, archived + # artifact and decision read still reaches the unchanged checkout. + changed = json.loads(json.dumps(ledger)) + row = next(p for p in changed['pull_requests'] if p['number'] == 626) + row.update(updates) + read_text = Path.read_text + + def read(path, *args, **kwargs): + if path == ROOT / 'docs/research/pr-ledger.json': + return json.dumps(changed) + return read_text(path, *args, **kwargs) + + output = io.StringIO() + try: + with patch.object(Path, 'read_text', read), contextlib.redirect_stdout(output): + checker.main() + except ValueError as exc: + return {'status': 'REJECTED', 'reason': str(exc)} + return {'status': 'ACCEPTED'} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--output', type=Path, help='Optional JSON evidence file') + args = parser.parse_args() + original = subprocess.check_output(['git', 'show', BASE + ':' + CHECKER], cwd=ROOT) + require(sha256(original).hexdigest() == BASE_CHECKER_SHA256, 'Unexpected baseline checker bytes') + current = (ROOT / CHECKER).read_bytes() + ledger_path = 'docs/research/pr-ledger.json' + ledger_bytes = (ROOT / ledger_path).read_bytes() + pinned_ledger = subprocess.check_output(['git', 'show', BASE + ':' + ledger_path], cwd=ROOT) + require(ledger_bytes == pinned_ledger, 'This historical replay requires the base ledger bytes') + ledger = json.loads(ledger_bytes) + before, after = load_checker(original), load_checker(current) + results = [] + for name, updates in CASES: + baseline = run_case(before, ledger, updates) + fixed = run_case(after, ledger, updates) + require(baseline['status'] == 'ACCEPTED', 'Baseline did not accept ' + name) + require(fixed['status'] == ('REJECTED' if updates else 'ACCEPTED'), + 'Unexpected fixed result for ' + name) + if updates: + require('ledger differs from family decision' in fixed['reason'], + 'Mutation failed for an unrelated reason: ' + name) + results.append({'case': name, 'pr': 626, 'updates': updates, + 'baseline': baseline, 'fixed': fixed}) + print(name + ': baseline ' + baseline['status'] + ', fixed ' + fixed['status'], flush=True) + inputs = {ledger_path: sha256(ledger_bytes).hexdigest()} + for name in ledger['family_rounds']: + inputs[name] = sha256((ROOT / name).read_bytes()).hexdigest() + report = {'base_commit': BASE, 'seed': 99, + 'scope': 'Offline review metadata projection; no mathematical or live GitHub-state claim.', + 'baseline_checker_sha256': BASE_CHECKER_SHA256, + 'fixed_checker_sha256': sha256(current).hexdigest(), + 'source_sha256': inputs, 'cases': results} + if args.output: + args.output.write_text(json.dumps(report, indent=2) + '\n') + print('PASS: unchanged ledger accepted; all five previously accepted provenance mutations rejected') + + +if __name__ == '__main__': + main() diff --git a/scripts/verify_pr_reconciliation.py b/scripts/verify_pr_reconciliation.py index f5b3788546..22d8d8e943 100644 --- a/scripts/verify_pr_reconciliation.py +++ b/scripts/verify_pr_reconciliation.py @@ -18,6 +18,18 @@ def require(condition, message): raise ValueError(message) +def check_family_decision(item, decision): + """Bind projected ledger state to its authoritative, pinned family decision.""" + fields = ('number', 'reviewed_head', 'current_head', 'disposition', + 'github_state', 'integration_location', 'unresolved_question', 'closure') + for field in fields + ('review_completion', 'verification_evidence'): + if field in fields or field in decision: + # JSON equality must distinguish true from 1, including nested flags. + require(field in item and json.dumps(item[field], sort_keys=True) == + json.dumps(decision[field], sort_keys=True), + f'#{decision["number"]}: ledger differs from family decision: {field}') + + def check_review_completion(item, decision): """An incorporated disposition must not outrun its source/execution/publication record.""" flags = decision['review_completion'] @@ -52,6 +64,7 @@ def main(): ledger = json.loads((ROOT / 'docs/research/pr-ledger.json').read_text()) snapshot = json.loads((AUDIT / 'starting-snapshot.json').read_text()) expected = {p['number']: p for p in snapshot['pull_requests']} + family_decisions = {} for family_path in (ROOT / 'research/community-family533-audit/DISPOSITIONS.json', ROOT / 'research/community-family588-audit/DISPOSITIONS.json', ROOT / 'research/community-hyperplane599-audit/DISPOSITIONS.json', @@ -63,11 +76,16 @@ def main(): numbers = [p['number'] for p in family['pull_requests']] require(len(numbers) == len(set(numbers)), 'Duplicate family PR') for row in family['pull_requests']: + # The generator applies later rounds in this order; retain the final + # authoritative projection without rewriting historical metadata. + family_decisions[row['number']] = row if row['number'] not in expected: expected[row['number']] = row['metadata'] actual = {p['number']: p for p in ledger['pull_requests']} require(len(actual) == len(ledger['pull_requests']) == len(expected), 'Duplicate or missing PR') require(actual.keys() == expected.keys(), 'Starting PR set changed') + for number, decision in family_decisions.items(): + check_family_decision(actual[number], decision) for number, pr in actual.items(): require(pr['starting_head'] == expected[number]['headRefOid'], f'#{number}: starting head changed') require(pr['contributor'] == expected[number]['author'], f'#{number}: attribution changed') diff --git a/tests/test_pr_reconciliation.py b/tests/test_pr_reconciliation.py index 2f198003cd..7ceaf41607 100644 --- a/tests/test_pr_reconciliation.py +++ b/tests/test_pr_reconciliation.py @@ -2,9 +2,11 @@ from copy import deepcopy from hashlib import sha256 import importlib.util +import json from pathlib import Path import tempfile import unittest +from unittest.mock import patch ROOT = Path(__file__).resolve().parents[1] spec = importlib.util.spec_from_file_location('family_review_check', ROOT / 'scripts/verify_pr_reconciliation.py') @@ -12,6 +14,110 @@ spec.loader.exec_module(checker) +class FamilyDecision(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.ledger = json.loads((ROOT / 'docs/research/pr-ledger.json').read_text()) + cls.items = {row['number']: row for row in cls.ledger['pull_requests']} + cls.families = {} + for family in ('family533', 'family588', 'hyperplane599', 'cold617', 'k492'): + path = ROOT / f'research/community-{family}-audit/DISPOSITIONS.json' + cls.families[family] = json.loads(path.read_text())['pull_requests'] + + def setUp(self): + self.decision = deepcopy(next(row for row in self.families['k492'] + if row['number'] == 626)) + self.item = deepcopy(self.items[626]) + + def test_current_family_projections_match_authoritative_decisions(self): + for family, decisions in self.families.items(): + for decision in decisions: + with self.subTest(family=family, number=decision['number']): + checker.check_family_decision(self.items[decision['number']], decision) + + def test_equal_forged_heads_do_not_establish_review_provenance(self): + self.item['reviewed_head'] = self.item['current_head'] = '0' * 40 + with self.assertRaisesRegex(ValueError, '#626:.*reviewed_head'): + checker.check_family_decision(self.item, self.decision) + + def test_changed_or_missing_projection_fields_are_rejected(self): + replacements = { + 'number': 625, + 'reviewed_head': '0' * 40, + 'current_head': '0' * 40, + 'disposition': 'superseded', + 'github_state': 'OPEN', + 'integration_location': ['research/unrelated'], + 'unresolved_question': 'A different remaining question.', + 'closure': None, + 'review_completion': {}, + 'verification_evidence': ['research/unrelated/RESULT.json'], + } + for field, value in replacements.items(): + for operation in ('replace', 'drop'): + item = deepcopy(self.item) + if operation == 'replace': + item[field] = value + else: + item.pop(field) + with self.subTest(field=field, operation=operation), self.assertRaisesRegex( + ValueError, '#626:.*' + field): + checker.check_family_decision(item, self.decision) + + def test_missing_closure_is_distinct_from_recorded_null(self): + decision = next(row for row in self.families['k492'] if row['number'] == 620) + item = deepcopy(self.items[620]) + self.assertIsNone(decision['closure']) + item.pop('closure') + with self.assertRaisesRegex(ValueError, '#620:.*closure'): + checker.check_family_decision(item, decision) + + def test_boolean_completion_flags_cannot_be_replaced_by_integers(self): + for number in (626, 620): + decision = next(row for row in self.families['k492'] if row['number'] == number) + for flag in decision['review_completion']: + item = deepcopy(self.items[number]) + item['review_completion'][flag] = int(item['review_completion'][flag]) + with self.subTest(number=number, flag=flag), self.assertRaisesRegex( + ValueError, f'#{number}:.*review_completion'): + checker.check_family_decision(item, decision) + + def test_earlier_family_without_completion_metadata_remains_valid(self): + decision = next(row for row in self.families['family533'] if row['number'] == 463) + self.assertNotIn('review_completion', decision) + self.assertNotIn('verification_evidence', decision) + checker.check_family_decision(self.items[463], decision) + + def test_partial_decisions_can_preserve_distinct_reviewed_and_current_heads(self): + for disposition in ('partially_reviewed', 'changed_since_review'): + decision = deepcopy(next(row for row in self.families['k492'] + if row['number'] == 620)) + decision['disposition'] = disposition + decision['current_head'] = '0' * 40 + item = deepcopy(decision) + with self.subTest(disposition=disposition): + self.assertNotEqual(item['reviewed_head'], item['current_head']) + self.assertFalse(item['review_completion']['required_execution_complete']) + checker.check_family_decision(item, decision) + checker.check_review_completion(item, decision) + + def test_entrypoint_rejects_forged_ledger_heads(self): + ledger = deepcopy(self.ledger) + item = next(row for row in ledger['pull_requests'] if row['number'] == 626) + item['reviewed_head'] = item['current_head'] = '0' * 40 + ledger_text = json.dumps(ledger) + original_read_text = Path.read_text + + def read_text(path, *args, **kwargs): + if path == ROOT / 'docs/research/pr-ledger.json': + return ledger_text + return original_read_text(path, *args, **kwargs) + + with patch.object(Path, 'read_text', read_text), self.assertRaisesRegex( + ValueError, '#626:.*reviewed_head'): + checker.main() + + class ReviewCompletion(unittest.TestCase): def setUp(self): self.decision = {