"""Read-only extraction: stdout is a public metadata record; no verse text/logs.""" import argparse import collections import hashlib import json from pathlib import Path parser = argparse.ArgumentParser() parser.add_argument('--source-output', type=Path, required=True) args = parser.parse_args() root = Path(__file__).resolve().parents[3] audit_path = root / 'cli/translate-bible/reports/nichijo-v1-numbering-alignment.json' raw_audit = audit_path.read_bytes() audit = json.loads(raw_audit) models = collections.Counter() dates = [] matched = missing = changed = records = undated = 0 for name, expected in audit['input_files'].items(): path = args.source_output / name if not path.is_file(): missing += 1 continue raw = path.read_bytes() if hashlib.sha256(raw).hexdigest() != expected: changed += 1 continue matched += 1 for verse in json.loads(raw)['verses'].values(): records += 1 models[verse.get('model', 'unrecorded')] += 1 if verse.get('timestamp'): dates.append(verse['timestamp']) else: undated += 1 usage = audit['usage'] stages = [ ('initial_alignment', usage['initial_alignment']['batches']), ('repair_proposals', usage['repair_round']['batches']), ('repair_verification', usage['repair_round']['independent_verification']['usage']), ] fields = ['input_tokens', 'cache_creation_input_tokens', 'cache_read_input_tokens', 'output_tokens'] rows = [] for stage, batches in stages: entries = [] for batch in batches: entry = {'record': batch.get('batch', batch.get('file')), 'response_sha256': batch.get('response_sha256', batch.get('sha256')), 'model_labels': [batch['model']] if 'model' in batch else list(batch['modelUsage']), 'tokens': {field: batch['usage'][field] for field in fields}, 'is_error': batch.get('is_error'), 'terminal_reason': batch.get('terminal_reason')} entries.append(entry) totals = {field: sum(e['tokens'][field] for e in entries) for field in fields} rows.append({'stage': stage, 'requests': len(entries), 'tokens': totals, 'total_tokens': sum(totals.values()), 'records': entries}) print(json.dumps({ 'captured_at': '2026-10-03', 'audit_sha256': hashlib.sha256(raw_audit).hexdigest(), 'generation': {'scope': 'Only local source chapter files whose SHA-256 matches the release audit input_files.', 'matched_chapter_files': matched, 'missing_chapter_files': missing, 'changed_chapter_files': changed, 'source_records': records, 'models': dict(models), 'earliest_record_timestamp': min(dates), 'latest_record_timestamp': max(dates), 'records_without_timestamp': undated, 'timezone': 'Not recorded in timestamp strings; do not infer UTC.', 'total_generation_tokens': None, 'limitations': 'Saved verse timestamps, not a complete run start/end log. Source records are not final aligned release verses. No whole-project model or token claim.'}, 'review_usage': {'stages': rows, 'total_tokens': sum(r['total_tokens'] for r in rows), 'scope': 'Recorded alignment, repair-proposal and repair-verification requests only; includes unsuccessful recorded requests. Excludes original generation, Codex work and unrecorded calls.', 'counting': 'Sum top-level input_tokens + cache_creation_input_tokens + cache_read_input_tokens + output_tokens once per recorded request. Do not add nested iterations, modelUsage or thinking tokens again.', 'dates': None, 'model_label_note': 'Labels as recorded in the audit, not independently verified vendor model identities.'}, 'website': {'created_on': '2026-10-03', 'tool': 'Codex', 'model': None, 'tokens': None, 'note': 'Exact underlying model and session token total are not supplied in the site evidence.'} }, ensure_ascii=False, indent=2))