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.
68 lines
2.8 KiB
Python
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()
|