#!/usr/bin/env python3
"""Package the current completed C732--C831 checkpoint without changing its journal.
The lead creates sources/SOURCE_MANIFEST/C731_PREDECESSOR and research artifacts.
This helper creates a reading report, payload manifest, and consistent ZIP snapshot.
"""
from pathlib import Path
from datetime import datetime, timezone
import argparse
import hashlib
import json
import re
import zipfile
ROOT = Path(__file__).resolve().parent.parent
WORKSPACE = ROOT.parent
def require(condition, message):
if not condition:
raise RuntimeError(message)
def sha(data):
return hashlib.sha256(data).hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--first', type=int, default=732, help='First step displayed in the reading report; evidence retains the full current cycle')
parser.add_argument('--through', type=int, help='Assert the current final completed step before packaging')
parser.add_argument('--date', default=datetime.now(timezone.utc).strftime('%Y%m%d'))
args = parser.parse_args()
require(re.fullmatch(r'\d{8}', args.date), 'Date must be YYYYMMDD')
raw = (ROOT / 'journal.json').read_bytes()
rows = json.loads(raw)
require(rows and [r['step'] for r in rows] == list(range(732, 732 + len(rows))), 'Journal is not the contiguous C732 prefix')
last = rows[-1]['step']
require(last <= 831 and 732 <= args.first <= last, 'Invalid report range')
require(args.through is None or args.through == last, 'Current journal endpoint differs from --through')
source_manifest = json.loads((ROOT / 'evidence/SOURCE_MANIFEST.json').read_text())
require(source_manifest, 'Missing source manifest')
for name, expected in source_manifest.items():
require(Path(name).name == name, f'Unsafe snapshot name: {name}')
require(sha((ROOT / 'evidence/sources' / name).read_bytes()) == expected, f'Source hash mismatch: {name}')
require((ROOT / 'evidence/C731_PREDECESSOR.json').is_file(), 'Missing C731 predecessor snapshot')
if last == 831:
verification = json.loads((ROOT / 'VERIFY.json').read_text())
require(verification['status'] == 'PASS' and verification['last'] == 831 and verification['sequential_steps'] == 100, 'Final packaging requires the successful100-step VERIFY.json')
require(verification['journal_sha256_at_read'] == sha(raw), 'Final verification does not bind the current journal')
require(not (ROOT / 'pending.json').exists(), 'Final packet still has pending research')
lines = [f'# 490d — C{args.first}–C{last}: sequential research record', '', f'Working continuation of C731 · packet date{args.date}.', '',
'Each completed step retains its question, declared inputs, exact results, finding, and reassessment. These are bounded research actions, not a count of independent discoveries. Canonical sources remain unchanged; second decimal inversion remains deferred.', '']
for row in rows:
if row['step'] < args.first:
continue
lines += [f'## C{row["step"]} — {row["title"]}', '', f'**Question.** {row["question"]}', '', f'**Sources.** {"; ".join(row["sources"])}', '', '**Inputs**', '', '```json', json.dumps(row['inputs'], indent=2, ensure_ascii=False), '```', '', '**Results**', '', '```json', json.dumps(row['results'], indent=2, ensure_ascii=False), '```', '', f'**Finding.** {row["finding"]}', '', f'**Reassessment.** {row["reassessment"]}', '']
report_name = f'490d_C{args.first}_C{last}_Research_{args.date}.md'
report_data = ('\n'.join(lines) + '\n').encode()
(ROOT / report_name).write_bytes(report_data)
(WORKSPACE / report_name).write_bytes(report_data)
payloads = {}
for path in sorted(ROOT.rglob('*')):
if not path.is_file() or '__pycache__' in path.parts or path.name in {'MANIFEST.sha256', 'pending.json'}:
continue
match = re.fullmatch(r's(\d+)\.py', path.name)
if match and int(match.group(1)) > last:
continue
payloads[str(path.relative_to(ROOT))] = path.read_bytes()
require((ROOT / 'journal.json').read_bytes() == raw, 'Journal changed during packaging; retry between research steps')
require(payloads['journal.json'] == raw, 'Captured journal differs from package input')
manifest_data = ''.join(f'{sha(data)} {name}\n' for name, data in sorted(payloads.items())).encode()
(ROOT / 'MANIFEST.sha256').write_bytes(manifest_data)
payloads['MANIFEST.sha256'] = manifest_data
archive = WORKSPACE / f'490d_C732_Through_C{last}_Evidence_{args.date}.zip'
with zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED) as handle:
for name, data in sorted(payloads.items()):
handle.writestr(name, data)
print(json.dumps({'report': str(WORKSPACE / report_name), 'evidence': str(archive), 'completed_steps': len(rows), 'last_step': last, 'source_snapshots': len(source_manifest), 'payloads': len(payloads), 'journal_sha256': sha(raw), 'zip_sha256': sha(archive.read_bytes())}, ensure_ascii=False))
if __name__ == '__main__':
main()
Evidence
package.py
Edition and provenance
package.py
SHA-256 c722c25b59ba088149a792344b5fe06f5d3df8c7abe66473cac3af71fde1675f
C480–C1634/Research_Cycles/C0732_C0831/evidence/package.py