Files
MeanField/reports/build_report.py
Emily Boudreaux e7e53ce682 Consolidate numerical research and preserve a reproducible handoff
Document the baseline, hypotheses, measured results, failed approaches,
current policy defaults, and remaining rotating n=3 and virial limitations.
Include the authoritative LaTeX report, Pandoc-generated Markdown, compiled
PDF, source provenance, branch decisions, and consolidation validation.

Organize raw results and miscellaneous artifacts under working/ without
deleting them; version the directory guide, relocation hashes, archive
verifier, and report build/conversion checks. Keep build caches at their
original paths and clearly distinguish retained binaries from the validated
research build. Preserve experimental policies without promoting failed
trials to defaults.
2026-09-20 09:10:27 -04:00

68 lines
2.8 KiB
Python

#!/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()