#!/usr/bin/env python3 """Build the handoff's Markdown with Pandoc and its PDF with LaTeX. The LaTeX is authoritative. Generated Markdown is deliberately not post-edited, so a reader can regenerate both delivery formats from the same research record. """ from pathlib import Path import argparse import hashlib import json import shutil import subprocess import sys import time def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--markdown-only', action='store_true') args = parser.parse_args() reports = Path(__file__).resolve().parent stem = 'MeanField_research_handoff_2026-09-20' source = reports / (stem + '.tex') build = reports.parent / 'working/report_builds/research_handoff' build.mkdir(parents=True, exist_ok=True) commands = [[ 'pandoc', '--from=latex', '--to=gfm-tex_math_gfm+tex_math_dollars', '--standalone', '--toc', '--toc-depth=2', '--lua-filter=pandoc_tables.lua', '--wrap=none', '--output=' + stem + '.md', source.name, ], [sys.executable, 'check_report_conversion.py', '--output', str(build / 'conversion_audit.json')]] if not args.markdown_only: commands.append([ 'latexmk', '-pdf', '-interaction=nonstopmode', '-halt-on-error', '-outdir=' + str(build), source.name, ]) record = {'source_sha256': hashlib.sha256(source.read_bytes()).hexdigest(), 'commands': [], 'outputs': {}} for index, command in enumerate(commands): executable = shutil.which(command[0]) if executable is None: raise SystemExit(f'Required program is unavailable: {command[0]}') command[0] = executable start = time.monotonic() log = build / f'build_{index}.log' with log.open('w') as stream: result = subprocess.run(command, cwd=reports, stdout=stream, stderr=subprocess.STDOUT) record['commands'].append({'command': command, 'returncode': result.returncode, 'seconds': time.monotonic() - start}) (build / 'build_manifest.json').write_text(json.dumps(record, indent=2) + '\n') if result.returncode: raise SystemExit(f'Report build failed; inspect {log}') if not args.markdown_only: shutil.copy2(build / (stem + '.pdf'), reports / (stem + '.pdf')) for suffix in ['.tex', '.md'] + ([] if args.markdown_only else ['.pdf']): path = reports / (stem + suffix) record['outputs'][path.name] = { 'sha256': hashlib.sha256(path.read_bytes()).hexdigest(), 'bytes': path.stat().st_size, } (build / 'build_manifest.json').write_text(json.dumps(record, indent=2) + '\n') print('Generated ' + ', '.join(record['outputs'])) if __name__ == '__main__': main()