#!/usr/bin/env python3
"""Portable, stdlib-only replay of C1433–C1482 arithmetic and provenance.
The declared six source pairs are transcribed here independently of model/*.json.
Decimal reversal uses integer division/remainders, not research.py's string method.
Only original source durations are reversed. Counts describe verification groups,
not independent discoveries, probability evidence, or historical authentication.
Usage: python verify_cycle.py [--allow-pending] [--require-50]
"""
from __future__ import annotations
import argparse
from datetime import datetime, timezone
from fractions import Fraction
import hashlib
import json
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parent
F = Fraction
SOURCES = {
'MT_regular': (4106, 2456, 'Noah', 'Creation'),
'MT_cumulative': (14006, 4836, 'Shem', 'Creation'),
'LXX_regular': (5486, 3236, 'Noah', 'Creation'),
'LXX_cumulative': (14896, 5746, 'Shem', 'Creation'),
'SP_regular': (4406, 3106, 'Noah', 'Fall'),
'SP_cumulative': (13396, 4716, 'Shem', 'Fall'),
}
CHECKS = []
MODELS = []
def encode(value):
if isinstance(value, Fraction):
return value.numerator if value.denominator == 1 else str(value)
if isinstance(value, dict):
return {str(k): encode(v) for k, v in value.items()}
if isinstance(value, (tuple, list)):
return [encode(v) for v in value]
return value
def check(name, actual, expected=True):
actual, expected = encode(actual), encode(expected)
passed = actual == expected
CHECKS.append({'name': name, 'pass': passed, 'actual': actual, 'expected': expected})
return passed
def read_json(path):
return json.loads(path.read_text(encoding='utf-8'))
def model(name, expected, subset=False):
actual = read_json(ROOT / 'model' / (name + '.json'))
if subset:
actual = {key: actual.get(key) for key in expected}
MODELS.append(name + '.json')
check('model/' + name, actual, expected)
def digest(value):
# Matches research.digest: compact sorted JSON, Unicode preserved, UTF-8.
raw = json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False)
return hashlib.sha256(raw.encode('utf-8')).hexdigest()
def numeric_reverse(n):
if not isinstance(n, int) or isinstance(n, bool) or n <= 0:
raise ValueError('Original positive integer duration required')
place = 1
while n % 10 == 0:
n //= 10
place *= 10
reversed_core = 0
while n:
n, digit = divmod(n, 10)
reversed_core = reversed_core * 10 + digit
return place * reversed_core
def original_core(n):
places = 0
while n % 10 == 0:
n //= 10
places += 1
return n, places
def build_path(legs, anchor):
result = [anchor]
for span in legs:
result.append(result[-1] + numeric_reverse(span))
return result
def key(q, x, factor=F(25, 23)):
return F(q) + factor * (x-q)
def replay_arithmetic():
spines, stage, gains, hinges, parts = {}, {}, [], {}, {}
for name, (c, flood, role, head_role) in SOURCES.items():
lower, upper = flood-1406, c-flood
legs = [lower, upper]
p2, p3 = build_path(legs, 1406), build_path([1400]+legs, 6)
spines[name] = {'C': c, 'F': flood, 'hinge_role': role, 'legs': legs,
'path2': p2, 'path3': p3, 'source_head_role': head_role}
stage[name] = {'tail_gain': numeric_reverse(1400)-1400,
'head_difference': p3[-1]-p2[-1],
'interior_suffix_difference': [a-b for a,b in zip(p3[1:],p2)]}
hinges[name] = {'F': flood, 'hinge': flood+600, 'role': role, 'span': 600,
'construction': 'same rounded source state; no new Gear transport'}
for span in legs:
core, zeros = original_core(span)
digits, remaining = [], core
while remaining:
remaining, digit = divmod(remaining,10)
digits.append(digit)
formula = 10**zeros * sum(d*(10**(len(digits)-1-i)-10**i)
for i,d in enumerate(digits))
gains.append({'spine': name, 'n': span, 'I_n': numeric_reverse(span),
'core': str(core), 'zeros': zeros,
'gain': numeric_reverse(span)-span, 'formula_gain': formula})
partitions = {'Flood': legs, role: [lower+600,upper-600],
'Both': [lower,600,upper-600]}
parts[name] = {}
for label, original in partitions.items():
parts[name][label] = {'source_legs': original,
'images': [numeric_reverse(n) for n in original],
'path2': build_path(original,1406),
'path3': build_path([1400]+original,6)}
check(f'original duration retained: {name}/{label}',sum(original),c-1406)
check(f'complete path stage translation: {name}/{label}',
[a-b for a,b in zip(parts[name][label]['path3'][1:],
parts[name][label]['path2'])], [2700]*(len(original)+1))
model('spines',spines)
model('stage_translation',stage)
model('digit_gains',gains)
model('hinges',hinges)
model('partitions',parts)
model('regular_partitions',{n:p for n,p in parts.items() if n.endswith('_regular')})
model('cumulative_partitions',{n:p for n,p in parts.items() if n.endswith('_cumulative')})
check('six source objects and eighteen named partitions',
[len(spines),sum(len(p) for p in parts.values())],[6,18])
congruence = {}
for tradition in ('MT','LXX','SP'):
r,c = spines[tradition+'_regular'],spines[tradition+'_cumulative']
sg,hg = c['C']-r['C'],c['path2'][-1]-r['path2'][-1]
modulus = 90 if tradition == 'SP' else 990
congruence[tradition] = {'source_gap':sg,'head_gap':hg,'modulus':modulus,
'residue':hg%modulus,'same_residue':sg%modulus==hg%modulus}
model('congruence',congruence)
law, classes, full, upper_translations = {}, {}, {}, {}
for name,(c,flood,role,_) in SOURCES.items():
lower, upper = flood-1406,c-flood
d_l = numeric_reverse(lower+600)-numeric_reverse(lower)-numeric_reverse(600)
d_u = numeric_reverse(upper)-numeric_reverse(600)-numeric_reverse(upper-600)
law[name] = {'dL':d_l,'dU':d_u,'alternate_change':d_l-d_u,'refined_change':-d_u}
classes.setdefault(str((d_l,d_u)),[]).append(name)
check('partition law alternate: '+name,
parts[name][role]['path2'][-1]-parts[name]['Flood']['path2'][-1],d_l-d_u)
check('partition law refined: '+name,
parts[name]['Both']['path2'][-1]-parts[name]['Flood']['path2'][-1],-d_u)
original = [1406,flood,flood+600,c]
rebuilt = parts[name]['Both']['path2']
offsets = [a-b for a,b in zip(rebuilt,original)]
full[name] = {'source_path':original,'inverse_path':rebuilt,
'node_displacements':offsets,'retained_edge':rebuilt[2]-rebuilt[1]}
upper_translations[name] = {'upper_offsets':offsets[1:],
'uniform':len(set(offsets[1:]))==1}
# JSON object order does not matter. Class member order is the model's
# manuscript-mode presentation order, so compare these as memberships.
model('partition_law',law)
existing_classes = read_json(ROOT/'model/partition_classes.json')
MODELS.append('partition_classes.json')
check('model/partition_classes membership',
{k:sorted(v) for k,v in existing_classes.items()},
{k:sorted(v) for k,v in classes.items()})
model('full_node_displacements',full)
model('upper_branch_translations',upper_translations)
mt, lx, sp = (spines[n+'_cumulative'] for n in ('MT','LXX','SP'))
model('MT_SP_cumulative_preservation',{
'source_legs_difference':[a-b for a,b in zip(mt['legs'],sp['legs'])],
'inverse_legs_difference':[numeric_reverse(a)-numeric_reverse(b)
for a,b in zip(mt['legs'],sp['legs'])],
'head_gaps':[mt['C']-sp['C'],mt['path2'][-1]-sp['path2'][-1],
mt['path3'][-1]-sp['path3'][-1]],
'intermediate_gaps':[mt['F']-sp['F'],mt['path2'][1]-sp['path2'][1]]})
model('LXX_MT_cumulative',{
'source_lower_gap':lx['legs'][0]-mt['legs'][0],
'source_upper_gap':lx['legs'][1]-mt['legs'][1],
'inverse_lower_gap':numeric_reverse(lx['legs'][0])-numeric_reverse(mt['legs'][0]),
'inverse_upper_gap':numeric_reverse(lx['legs'][1])-numeric_reverse(mt['legs'][1]),
'source_head_gap':lx['C']-mt['C'],
'inverse_head_gap':lx['path2'][-1]-mt['path2'][-1]})
model('LXX_SP_cumulative',{
'source_gap':lx['C']-sp['C'],
'inverse_gap':lx['path2'][-1]-sp['path2'][-1],
'source_triangle':(lx['C']-mt['C'])+(mt['C']-sp['C']),
'inverse_triangle':(lx['path2'][-1]-mt['path2'][-1])+(mt['path2'][-1]-sp['path2'][-1]),
'extra_contraction':(lx['path2'][-1]-lx['C'])-(sp['path2'][-1]-sp['C'])})
lr,sr = spines['LXX_regular'],spines['SP_regular']
model('mode_interaction',{
'SP_minus_LXX_regular':sr['path3'][-1]-lr['path3'][-1],
'SP_minus_LXX_cumulative':sp['path3'][-1]-lx['path3'][-1],
'difference_of_differences':(sr['path3'][-1]-lr['path3'][-1])-(sp['path3'][-1]-lx['path3'][-1]),
'equivalent_mode_difference':(lx['path3'][-1]-lr['path3'][-1])-(sp['path3'][-1]-sr['path3'][-1])})
native = read_json(ROOT/'evidence/Moses_LXX_native_complete_path.json')
native_rows = {row['name']:row for row in native['rows']}
check('source LXX Noah row',native_rows['Noah']['rounded_BC'],3836)
check('source LXX Shem row',native_rows['Shem']['rounded_BC'],3336)
check('source LXX original Noah to Shem',
native_rows['Noah']['rounded_BC']-native_rows['Shem']['rounded_BC'],500)
model('Shem_incidences',{
'SP_two_stage_path':parts['SP_cumulative']['Both']['path2'],
'SP_completed_path':parts['SP_cumulative']['Both']['path3'],
'SP_inverse_Shem_at1406':parts['SP_cumulative']['Both']['path2'][2],
'LXX_regular_Shem':native_rows['Shem']['rounded_BC'],
'SP_completed_inverse_Flood':sp['path3'][2],
'MT_cumulative_Shem':mt['F']+600,'source_state':native['source_state']})
model('Covenant_path_scope',[
{'role':role,'LXX':a,'Key_image':key(1866,a),'SP':b,'matches':key(1866,a)==b}
for role,a,b in zip(('Nativity','reversed_Conquest','reversed_Flood','head'),
lr['path3'],sp['path3'])])
l_head, s_head = lr['path3'][-1],sp['path3'][-1]
prophetic = key(1406,l_head,F(70,69))
lower_sp = SOURCES['SP_regular'][1]-1406
route = [l_head,prophetic,prophetic-lower_sp,
prophetic-lower_sp+2700,prophetic-lower_sp+2700+(s_head-sr['path3'][-1])]
model('closed_Key_circuit',{'route':route,
'edge_changes':[b-a for a,b in zip(route,route[1:])],
'direct':key(1866,l_head),'target':s_head})
check('C1432 direct head closure',key(1866,l_head),s_head)
check('C1432 reciprocal closure',key(1866,s_head,F(23,25)),l_head)
check('C1432 translated 11270 family',
[14726-(6+numeric_reverse(5436-6)),14006-sp['path2'][1],l_head-1866],
[23*490]*3)
check('C1432 70-fold Covenant law',[l_head-1866,s_head-1866],[70*161,70*175])
cap = read_json(ROOT/'evidence/cap_round_loss_identity.json')
cap_loss = sum(row['rounded_reduction'] for row in cap['rows'])
model('source_to_head_bridge',{
'cap_loss':cap_loss,'accepted_Fall':13401-SOURCES['SP_cumulative'][0],
'upper_difference':mt['legs'][1]-sp['legs'][1],
'MT_SP_source_Flood_gap':mt['F']-sp['F'],
'MT_SP_preserved_head_gap':mt['path3'][-1]-sp['path3'][-1],
'Covenant_head_gain':s_head-l_head,
'MT_LXX_regular_inverse_gap':spines['MT_regular']['path3'][-1]-l_head})
check('C1432 cap plus accepted Fall',[cap_loss+5,mt['legs'][1]-sp['legs'][1]],[490,490])
nearby = key(4106,prophetic)
model('two_Key_nearby_route',{'Prophetic_head':prophetic,'radius_to4106':prophetic-4106,
'Priestly_head_about4106':nearby,'SP_head':s_head,'head_difference':s_head-nearby,
'paired_intervals':[[nearby,4106],[s_head,4116]]})
model('anchor_covariance',{'original':nearby,'anchor_only':key(4116,prophetic),
'both_input_and_anchor':key(4116,prophetic+10),
'anchor_only_change':key(4116,prophetic)-nearby,'required_pair_translation':10})
hgap,tgap = lx['path3'][-1]-l_head,1446-1406
kr = F(25,23)
model('paired_LXX_join',{'head_gap':hgap,'target_gap':tgap,
'head_gap_required':kr*tgap/(kr-1),
'regular_output':key(l_head,1406),'cumulative_output':key(lx['path3'][-1],1446),
'output_difference_formula':kr*tgap+(1-kr)*hgap})
paired = {}
for tradition in ('MT','LXX','SP'):
r,c = (spines[tradition+'_'+mode]['path3'][-1] for mode in ('regular','cumulative'))
a,b = key(r,1406),key(c,1446)
paired[tradition] = {'regular_result':a,'cumulative_result':b,
'difference':b-a,'predicted_difference':kr*tgap+(1-kr)*(c-r)}
model('paired_anchor_comparison',paired)
model('stage_Key_interaction',{
'Covenant_two_stage_image':key(1866,lr['path2'][-1]),
'SP_two_stage_target':sp['path2'][-1],
'Covenant_two_stage_residual':key(1866,lr['path2'][-1])-sp['path2'][-1],
'LXX_two_stage_regular_join':key(lr['path2'][-1],1406),
'LXX_two_stage_cumulative_join':key(lx['path2'][-1],1446),
'positive_stage_commutator':key(1866,lr['path2'][-1]+2700)-key(1866,lr['path2'][-1])-2700
},subset=True)
model('reconstruction_contract',{'sufficient_inputs':{'source_pairs':6,'named_offset':600,
'later_anchors':[1406,6]}},subset=True)
def replay_bindings():
bindings = read_json(ROOT/'model/source_bindings.json')
MODELS.append('source_bindings.json')
names = [item['file'] for item in bindings]
check('source binding filenames unique',len(names),len(set(names)))
for item in bindings:
path = ROOT/'evidence'/item['file']
# Never follow recorded original absolute paths: the evidence is portable.
check('evidence exists: '+item['file'],path.is_file())
if not path.is_file():
continue
data = path.read_bytes()
check('source SHA256 and bytes: '+item['file'],
[hashlib.sha256(data).hexdigest(),len(data)],[item['sha256'],item['bytes']])
return len(bindings)
def replay_journal(args):
parent = read_json(ROOT/'evidence/C1432_record.json')
parent_payload = {k:v for k,v in parent.items() if k not in ('sha256','hash_note')}
check('C1432 predecessor content hash',digest(parent_payload),parent['sha256'])
journal = read_json(ROOT/'journal.json')
check('journal count bounded',0 < len(journal) <= 50)
if args.require_50:
check('final cycle has exactly fifty records',len(journal),50)
expected_predecessor = parent['sha256']
for offset,record in enumerate(journal):
expected_step = 1433+offset
check(f'C{expected_step} number and predecessor',
[record['step'],record['predecessor_sha256']],
[expected_step,expected_predecessor])
payload = {k:v for k,v in record.items() if k != 'sha256'}
check(f'C{expected_step} content hash',digest(payload),record['sha256'])
check(f'C{expected_step} completed and reassessed',
bool(record.get('finding')) and bool(record.get('reassessment')) and
bool(record.get('closed_utc')) and
datetime.fromisoformat(record['closed_utc']) >= datetime.fromisoformat(record['opened_utc']) and
all(record.get('checks',{}).values()))
expected_predecessor = record['sha256']
pending_path = ROOT/'pending.json'
pending = pending_path.exists()
if args.allow_pending and pending:
pending_record = read_json(pending_path)
check('allowed pending record follows completed chain',
[pending_record['step'],pending_record['predecessor_sha256']],
[1433+len(journal),expected_predecessor])
else:
check('no pending research record',pending,False)
return len(journal),pending
def replay_reader():
metadata = read_json(ROOT/'model/reader_integration.json')
MODELS.append('reader_integration.json')
before = (ROOT/'evidence/Reader_before.md').read_bytes()
after = (ROOT/'deliverables/490d_Chronological_Families_Explanation_C1431.md').read_bytes()
check('reviewed reader before/after hashes',
[hashlib.sha256(before).hexdigest(),hashlib.sha256(after).hexdigest()],
[metadata['before_sha256'],metadata['after_sha256']])
old,new = before.decode(),after.decode()
for start,end in [('## Local changes become','## The same spine operation'),
('## Calendar Keys preserve','## What the common explanation')]:
check('unchanged reader section: '+start,
old[old.index(start):old.index(end)],new[new.index(start):new.index(end)])
addition = new[new.index('## How the three inverse families connect internally'):new.index('## Calendar Keys preserve')]
check('reader addition word count',len(addition.split()),metadata['addition_words'])
check('reader display math balanced',new.count('\\['),new.count('\\]'))
headings = [line for line in new.splitlines() if line.startswith('#')]
check('reader headings unique',len(headings),len(set(headings)))
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--allow-pending',action='store_true',
help='Permit the current incomplete action during an interim replay.')
parser.add_argument('--require-50',action='store_true',
help='Require all fifty completed records C1433 through C1482.')
args = parser.parse_args()
bound_count = journal_count = 0
pending = None
try:
replay_arithmetic()
bound_count = replay_bindings()
replay_reader()
journal_count,pending = replay_journal(args)
except Exception as exc:
CHECKS.append({'name':'verification execution','pass':False,
'actual':type(exc).__name__+': '+str(exc),'expected':'successful replay'})
failed = [item for item in CHECKS if not item['pass']]
non_arithmetic = ['dependency_families.json','review_summary.json']
present = sorted(p.name for p in (ROOT/'model').glob('*.json') if p.name!='verification.json')
unchecked = [name for name in present if name not in MODELS and name not in non_arithmetic]
result = {
'status':'PASS' if not failed else 'FAIL',
'verified_utc':datetime.now(timezone.utc).isoformat(),
'grouped_check_count':len(CHECKS),'failed_check_count':len(failed),
'scope':'Six declared source pairs, eighteen named partitions and their complete paths; local source/Key relationships; evidence hashes and completed journal chain. Verification counts are not independent discoveries or statistical evidence.',
'method':'Independent arithmetic digit reversal using integer divmod; exact fractions; research.digest-compatible canonical JSON hashes.',
'second_decimal_reversal_performed':False,
'require_50':args.require_50,'allow_pending':args.allow_pending,
'completed_journal_records':journal_count,'pending_present':pending,
'bound_evidence_files':bound_count,
'arithmetic_models_checked':sorted(set(MODELS)),
'non_arithmetic_review_files':non_arithmetic,
'additional_model_files_not_checked':unchecked,
'checks':CHECKS}
(ROOT/'model/verification.json').write_text(json.dumps(result,indent=2,ensure_ascii=False)+'\n',encoding='utf-8')
print(json.dumps({k:result[k] for k in ('status','grouped_check_count','failed_check_count',
'completed_journal_records','bound_evidence_files','additional_model_files_not_checked')}))
for item in failed:
print(json.dumps(item,ensure_ascii=False),file=sys.stderr)
return 1 if failed else 0
if __name__ == '__main__':
raise SystemExit(main())
Evidence
verify cycle.py
Edition and provenance
verify_cycle.py
SHA-256 d06af2033e7797c70a8a4c457ccf07e68d9721c67bb3a9e21c590555acb71964
C480–C1634/Research_Cycles/C1433_C1482/verify_cycle.py