diff --git a/.github/workflows/two-scale-cluster-geometry.yml b/.github/workflows/two-scale-cluster-geometry.yml index b0ac3a4fe..d0ca8142f 100644 --- a/.github/workflows/two-scale-cluster-geometry.yml +++ b/.github/workflows/two-scale-cluster-geometry.yml @@ -1,6 +1,12 @@ name: Two-scale cluster geometry checks 'on': + push: + branches: + - main + paths: + - 'frontiers/two_scale_cluster_geometry_20260929/**' + - '.github/workflows/two-scale-cluster-geometry.yml' pull_request: paths: - 'frontiers/two_scale_cluster_geometry_20260929/**' diff --git a/frontiers/two_scale_cluster_geometry_20260929/ACCEPTED_BINDINGS.json b/frontiers/two_scale_cluster_geometry_20260929/ACCEPTED_BINDINGS.json new file mode 100644 index 000000000..fea550f6b --- /dev/null +++ b/frontiers/two_scale_cluster_geometry_20260929/ACCEPTED_BINDINGS.json @@ -0,0 +1,46 @@ +{ + "schema": 1, + "purpose": "Machine-readable binding of the exact manuscripts to the actual scoped nonauthor reviews accepted before PR #166 integration. Engineering evidence only; scientific effect NONE.", + "accepted_mathematical_head": "7c82252533c3fe14ee262f7f87c0549c10968592", + "manuscripts": [ + { + "path": "TWO_SCALE_LAW.md", + "bytes": 17139, + "sha256": "e81d7fe09d25c3266eb8e62922756f54761d7f74d692fb35d9a8071fffd73769", + "git_blob": "a32fd5f7d941bbe1fe943df045b1e0fbec8d691c", + "reviews": [ + { + "id": 5360227991, + "url": "https://github.com/d6g8k5htny-coder/Math-/pull/166#pullrequestreview-5360227991", + "provider": "Anthropic Claude", + "scope": "Slice A: Theorem T, Theorem L, ordered-pair law L3, sampling/order-of-limits statements, spatial-TV obstruction V1" + } + ] + }, + { + "path": "RADIAL_TAIL.md", + "bytes": 12533, + "sha256": "250897858c8b314c0ff85ec1860a725efacfdae0ff7409fced4cadf7bfb85973", + "git_blob": "0f14417ef7038b9c6f50e4b01e393a58b7b5e3a4", + "reviews": [ + { + "id": 5360178611, + "url": "https://github.com/d6g8k5htny-coder/Math-/pull/166#pullrequestreview-5360178611", + "provider": "Anthropic Claude", + "scope": "Slice B: R4-R13, sharp radius tail and moment threshold" + }, + { + "id": 5360216551, + "url": "https://github.com/d6g8k5htny-coder/Math-/pull/166#pullrequestreview-5360216551", + "provider": "Anthropic Claude", + "scope": "Slice C: Theorem H and H1-H4, universal extreme local marks" + } + ] + } + ], + "limitations": [ + "This file authenticates local binding metadata only; it does not contact GitHub or prove the truth of a review.", + "Changing either accepted manuscript requires a new mathematical review and a new explicit binding record; refreshing MANIFEST.json alone is insufficient.", + "The three review ids are one provider-distinct Claude session and do not constitute organizational independence." + ] +} diff --git a/frontiers/two_scale_cluster_geometry_20260929/MANIFEST.json b/frontiers/two_scale_cluster_geometry_20260929/MANIFEST.json index 89b7780e9..e7b48746e 100644 --- a/frontiers/two_scale_cluster_geometry_20260929/MANIFEST.json +++ b/frontiers/two_scale_cluster_geometry_20260929/MANIFEST.json @@ -3,6 +3,18 @@ "scientific_effect": "NONE", "review_status": "NONAUTHOR_ACCEPTED_SCOPED_CONDITIONAL_GEOMETRY", "files": [ + { + "path": "ACCEPTED_BINDINGS.json", + "bytes": 2080, + "sha256": "5d07d92f85099550f5240f3c2400ab46736f0d38ec0b5cc1936acfecd8beb4fd", + "git_blob": "fea550f6bcebe67d08aa590ed004cedd8a88bdfc" + }, + { + "path": "geometry.py", + "bytes": 7252, + "sha256": "e8582266faf3b4277412c00ea68751aeab0aa4e764ef831c56e34b8dbf68a7e9", + "git_blob": "f7fd5b69953d631b8cd9e1e92b3d25f1ce824f69" + }, { "path": "RADIAL_TAIL.md", "bytes": 12533, @@ -39,6 +51,18 @@ "sha256": "de09c2cf3c9082a09d168f451f78a1a60b150f9f55c0c6b9bfe94ac26652dfe0", "git_blob": "59d11113e208965fefeaadd1bd5866585e77dc82" }, + { + "path": "test_custody.py", + "bytes": 8958, + "sha256": "9499f9b6a0ba699a538f0c53155db8d1d43289d985c15a803f22c281ad0f3e7d", + "git_blob": "d65b868673d528ced6898d13385105d7c0f8243a" + }, + { + "path": "test_geometry.py", + "bytes": 5999, + "sha256": "ad87486bec154bf2ed9eb427c2527912579878ae7cd3462082c76bfd57acfa4d", + "git_blob": "a787f6e6bfe90385b51ce41d3709bf08af636d04" + }, { "path": "TWO_SCALE_LAW.md", "bytes": 17139, @@ -51,29 +75,11 @@ "sha256": "7600037a03b43c6d20e50002cc0afed912cf6c5bc71e51a490959b3725cfaec0", "git_blob": "33f00592e26a8f0a652c6554b6e3ddc389065425" }, - { - "path": "geometry.py", - "bytes": 7252, - "sha256": "e8582266faf3b4277412c00ea68751aeab0aa4e764ef831c56e34b8dbf68a7e9", - "git_blob": "f7fd5b69953d631b8cd9e1e92b3d25f1ce824f69" - }, - { - "path": "test_custody.py", - "bytes": 7574, - "sha256": "f46073d3ff276cd0fdc571bf76e1cda8e0e4b20d93f3f7ab52a43d936258b325", - "git_blob": "d055261a2ff56d038fcf1ce803e0167c0e04d95b" - }, - { - "path": "test_geometry.py", - "bytes": 5999, - "sha256": "ad87486bec154bf2ed9eb427c2527912579878ae7cd3462082c76bfd57acfa4d", - "git_blob": "a787f6e6bfe90385b51ce41d3709bf08af636d04" - }, { "path": "verify.py", - "bytes": 6002, - "sha256": "335dd00fabb70db04498a84eef926266faa9f34486458724d3b0655d10b4f4ca", - "git_blob": "e401a78a1a28200f3f7e473b9260f36919ab69f8" + "bytes": 9174, + "sha256": "9a5119f818cd10f7c734ec73e41d206d82e800df5cf2267620f6dd086fb4c52c", + "git_blob": "e5f2fe82cb8d7eeaedf9d4dd8d0c955768000c22" } ] } diff --git a/frontiers/two_scale_cluster_geometry_20260929/test_custody.py b/frontiers/two_scale_cluster_geometry_20260929/test_custody.py index d055261a2..d65b86867 100644 --- a/frontiers/two_scale_cluster_geometry_20260929/test_custody.py +++ b/frontiers/two_scale_cluster_geometry_20260929/test_custody.py @@ -169,5 +169,34 @@ def test_manifest_symlink(self): (root/'MANIFEST.json').symlink_to(self.repo/'a.md') with self.assertRaises(ValueError):v.verify_inventory(root) + def acceptance_fixture(self): + temp=tempfile.TemporaryDirectory();self.addCleanup(temp.cleanup) + root=Path(temp.name) + source=Path(__file__).resolve().parent + for name in ('TWO_SCALE_LAW.md','RADIAL_TAIL.md','ACCEPTED_BINDINGS.json'): + (root/name).write_bytes((source/name).read_bytes()) + return root + + def test_valid_acceptance_bindings(self): + root=self.acceptance_fixture() + self.assertEqual(v.verify_acceptance_bindings(root),2) + + def test_manifest_refresh_cannot_rebind_changed_manuscript(self): + root=self.acceptance_fixture() + (root/'TWO_SCALE_LAW.md').write_text('changed manuscript\n',encoding='utf-8') + with self.assertRaises(ValueError):v.verify_acceptance_bindings(root) + + def test_review_id_rewrite_rejected(self): + root=self.acceptance_fixture() + data=json.loads((root/'ACCEPTED_BINDINGS.json').read_text()) + data['manuscripts'][0]['reviews'][0]['id']=1 + (root/'ACCEPTED_BINDINGS.json').write_text(json.dumps(data),encoding='utf-8') + with self.assertRaises(ValueError):v.verify_acceptance_bindings(root) + + def test_missing_accepted_manuscript_rejected(self): + root=self.acceptance_fixture() + (root/'RADIAL_TAIL.md').unlink() + with self.assertRaises((ValueError,FileNotFoundError)):v.verify_acceptance_bindings(root) + if __name__=='__main__':unittest.main() diff --git a/frontiers/two_scale_cluster_geometry_20260929/verify.py b/frontiers/two_scale_cluster_geometry_20260929/verify.py index e401a78a1..e5f2fe82c 100644 --- a/frontiers/two_scale_cluster_geometry_20260929/verify.py +++ b/frontiers/two_scale_cluster_geometry_20260929/verify.py @@ -15,6 +15,24 @@ MUTANTS = ('omit-jet-factor-two','omit-z-reflection','power-ten','wrong-cusp', 'height-reversed','wrong-window','wrong-k-power','half-root-jacobian') +ACCEPTED_MANUSCRIPTS = { + 'TWO_SCALE_LAW.md': { + 'bytes': 17139, + 'sha256': 'e81d7fe09d25c3266eb8e62922756f54761d7f74d692fb35d9a8071fffd73769', + 'git_blob': 'a32fd5f7d941bbe1fe943df045b1e0fbec8d691c', + 'review_ids': (5360227991,), + }, + 'RADIAL_TAIL.md': { + 'bytes': 12533, + 'sha256': '250897858c8b314c0ff85ec1860a725efacfdae0ff7409fced4cadf7bfb85973', + 'git_blob': '0f14417ef7038b9c6f50e4b01e393a58b7b5e3a4', + 'review_ids': (5360178611, 5360216551), + }, +} +ESSENTIAL_PACKET_LEAVES = frozenset(('TWO_SCALE_LAW.md','RADIAL_TAIL.md','REVIEW_RECORD.md', + 'ACCEPTED_BINDINGS.json','SOURCES.json','RESULTS.json', + 'geometry.py','verify.py')) + def read_json(path): path=Path(path) @@ -92,6 +110,37 @@ def verify_sources(root): return len(entries) +def verify_acceptance_bindings(root): + """Bind the accepted mathematical bytes to the actual scoped review ids. + + MANIFEST.json is intentionally not authoritative for these identities: a + coordinated manuscript+manifest refresh must still fail until this explicit + acceptance contract is changed under review. + """ + data=read_json(root/'ACCEPTED_BINDINGS.json') + if data.get('schema')!=1 or data.get('accepted_mathematical_head')!='7c82252533c3fe14ee262f7f87c0549c10968592': + raise ValueError('unexpected acceptance-binding schema/head') + rows=data.get('manuscripts') + if not isinstance(rows,list) or {r.get('path') for r in rows}!=set(ACCEPTED_MANUSCRIPTS): + raise ValueError('accepted manuscript membership mismatch') + for row in rows: + path=row['path']; expected=ACCEPTED_MANUSCRIPTS[path] + pure=safe_path(path) + if len(pure.parts)!=1:raise ValueError('flat accepted manuscript required') + for key in ('bytes','sha256','git_blob'): + if row.get(key)!=expected[key]:raise ValueError('accepted manuscript binding changed: '+path) + identity((root/path).read_bytes(),{'path':path,'bytes':expected['bytes'], + 'sha256':expected['sha256'],'git_blob':expected['git_blob']}) + reviews=row.get('reviews') + if not isinstance(reviews,list):raise ValueError('accepted review list required: '+path) + ids=tuple(r.get('id') for r in reviews) + if ids!=expected['review_ids'] or len(ids)!=len(set(ids)): + raise ValueError('accepted review binding changed: '+path) + for review in reviews: + if review.get('provider')!='Anthropic Claude' or not str(review.get('url','')).startswith('https://github.com/d6g8k5htny-coder/Math-/pull/166#'): + raise ValueError('accepted review provenance malformed: '+path) + return len(rows) + def verify_inventory(root): entries=read_json(root/'MANIFEST.json')['files'] if not isinstance(entries,list) or not entries:raise ValueError('empty packet inventory') @@ -109,12 +158,21 @@ def verify_inventory(root): return len(entries) +def verify_essential_inventory(root): + names={e['path'] for e in read_json(root/'MANIFEST.json')['files']} + if not ESSENTIAL_PACKET_LEAVES <= names: + raise ValueError('essential accepted/replay leaf missing from packet inventory') + return len(ESSENTIAL_PACKET_LEAVES) + + def main(): parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('--local-only',action='store_true') args=parser.parse_args() root=Path(__file__).resolve().parent count=verify_inventory(root) + verify_essential_inventory(root) + accepted_count=verify_acceptance_bindings(root) source_count=0 if args.local_only else verify_sources(root) expected=(root/'RESULTS.json').read_bytes() for flags in (['-B','-S'],['-B','-O','-S']): @@ -129,7 +187,9 @@ def main(): if result.returncode!=(2 if mutant=='unknown' else 1): raise ValueError('negative control not rejected: '+mutant) verify_inventory(root) - print(json.dumps({'passed':True,'packet_leaves':count,'source_count':source_count, + verify_essential_inventory(root) + verify_acceptance_bindings(root) + print(json.dumps({'passed':True,'packet_leaves':count,'accepted_manuscripts':accepted_count,'source_count':source_count, 'source_pins_checked':not args.local_only,'scientific_effect':'NONE', 'mathematical_acceptance':False},sort_keys=True))