Reproducibility
Version 0.1 · 2026-11-09. All commands run from the repository root. PY=.venv/bin/python.
1. Environment
Reference machine (recorded in every run header):
- Apple M5 Max, 48 GB, macOS 27.0, arm64, 18 CPUs;
- Python 3.14.4;
.venvon Homebrew Python with system site-packages.
Library versions (logged by htr.utils.provenance.run_header):
| Library | Version |
|---|---|
| torch | 2.12.0 (MPS) |
| numpy | 2.5.4 |
| anthropic | 2.3.0 |
| transformers | 5.8.1 |
| sentencepiece | 0.2.2 |
| lxml | 6.1.3 |
| pyarrow | 24.0.0 |
| sacrebleu | 2.6.0 |
| datasketch | 2.0.0 |
| rapidfuzz | 3.14.6 |
Other packages present on the reference machine:
- sentence-transformers 5.5.0, with
all-MiniLM-L6-v2cached locally. It is loaded withHF_HUB_OFFLINE=1and never downloaded. - PyMuPDF 1.27.
- pandoc and TeX Live (xelatex, latexmk, biber) for the paper.
Clean environment:
python3 -m venv .venv
.venv/bin/pip install -e ".[api,hf,dev]" # pyproject.toml; Python >= 3.12
.venv/bin/python -m pytest tests -q -m "not network"Run headers. Every summary or manifest JSON starts with a run header containing created_at (UTC),
git{commit,branch,dirty}, libraries and hardware. The pilot artefacts record a dirty tree
(git.dirty = true). Confirmatory runs must be made from a clean, committed tree. A run with dirty = true
is development evidence only.
2. Pipeline commands
| Step | Command | Output |
|---|---|---|
| Vertical slice (zero cost) | scripts/vertical_slice.sh [data/parsed/smoke] [120] |
splits, data/pairs/vs_<name>/, tokenizer, artifacts/runs/vs_<name>-m1-nano/, predictions, metrics |
| PMC discovery + fetch | $PY scripts/discover_pmc.py --candidates 6000 --max-docs 1200 --seed 2 --out data/raw/pmc |
data/raw/pmc/*.xml + .meta.json; manifest data/manifests/discover_pmc_seed2.json |
| arXiv (in progress) | $PY scripts/discover_arxiv.py --sets q-fin econ --from 2008-01-01 --until 2017-12-31 --max-docs 12 --seed 1 |
data/raw/arxiv/ |
| Books | $PY scripts/discover_books.py --source gutenberg --max-docs 12 --seed 1 --out data/raw/books (also --source bookshelf, --source openstax) |
data/raw/books/, data/manifests/discover_books_* |
| Crossref (in progress) | script not yet present | — |
| Build corpus | $PY scripts/build_corpus.py --raw data/raw/pmc --out data/parsed/pilot_pmc --tag pilot_pmc |
documents.jsonl, passages.jsonl, excluded.jsonl, clusters.json, build_summary.json |
| Splits | $PY scripts/make_splits.py --corpus data/parsed/pilot_pmc --heldout-disciplines physics --heldout-before-year 1990 [--fixed old/splits.jsonl] |
data/splits/<name>/{splits.jsonl,splits_summary.json} |
| Rewrite: estimate | $PY scripts/rewrite.py --corpus data/parsed/pilot_pmc --job pilot_opus55 --n-passages 1000 --seed 1 --splits data/splits/pilot_pmc/splits.jsonl --estimate-only |
prints estimate:; writes the job manifest.json |
| Rewrite: mock | the same command without --estimate-only |
deterministic mock rewrites, 0 USD |
| Rewrite: live | ... --live [--max-requests 200] |
real API calls; ledger; stops at the ceiling |
| Rewrite: batch | ... --live --batch [--max-requests 200], then later ... --live --collect |
batches/<id>.json, then rewrites.jsonl |
| Held-out model / prompt | ... --live --batch --model claude-sonnet-5-5 · ... --variants H1,H2 |
separate job directories |
| Validate | $PY scripts/validate_pairs.py --corpus data/parsed/pilot_pmc --job pilot_opus55 [--no-embeddings] |
validated/{pairs,quarantine}.jsonl, validation_summary.json |
| Tokenizer | $PY scripts/train_tokenizer.py --pairs <pairs.jsonl> --splits <splits.jsonl> --out artifacts/tokenizers/<name> --vocab 32000 |
tokenizer.model, tokenizer_config.json, summary (train split only) |
| Train | $PY scripts/train.py --pairs … --splits … --tokenizer artifacts/tokenizers/<name> --run <run> --preset {m1,m2,m3,m3_*} --size {nano,tiny,small,base,large} --seed 1 [--overfit-test] [--resume] |
artifacts/runs/<run>/{manifest.json,train_log.jsonl,train_summary.json,best/,last/} |
| Predict | $PY scripts/predict.py --pairs … --splits … --split test --checkpoint artifacts/runs/<run>/best --baselines --out artifacts/predictions/<name> [--beam 4] |
outputs_<system>.jsonl |
| Evaluate | $PY scripts/evaluate.py --pairs … --outputs-dir artifacts/predictions/<name> --name <name> |
artifacts/metrics/<name>/{report.json,summary.md,items_*.jsonl} |
Notes on the commands.
- Rewrite selection.
rewrite.pyselects passages stratified by (discipline, section role) with--seed. Only splits listed in--allowed-splitsare eligible. - Spend. No money is spent without
--live. - Corpus default path. The
--corpusdefault in several scripts isdata/parsed/pilot. The existing pilot directory isdata/parsed/pilot_pmc, so pass it explicitly.
3. Seeds and determinism
| What | Seed |
|---|---|
| PMC candidate sampling | --seed (manifests seed 1 and 2) |
| Split assignment | sha256(seed:group) with seed 20261109; deterministic, independent of input order |
| Rewrite selection | --seed (1) |
| Mock provider | seed |
| Training | torch.manual_seed(seed), bucket-sampler shuffle seed + epoch |
| Bootstrap | seed = 0 |
What else is deterministic.
- Request ids are deterministic, so re-running a job skips completed requests.
- Inference is deterministic: greedy or beam, no sampling.
- MPS kernels are not guaranteed bit-identical across OS or torch versions. Report three training seeds instead of relying on bitwise reproducibility.
4. Data hashes and provenance
Per document:
raw_sha256;- the provider MD5 and its verification (
provider_md5_ok; 0 failures in 1 300 fetches); - the licence record with its evidence;
- the date with its evidence;
acquired_at.
Other identifiers and hashes:
- Passages:
text_sha256, and ids derived from (doc, paragraph index, text hash). - Rewrites: provider, model,
prompt_version(rw-1.0), prompt fingerprint (sha256 of system + preserve + variants), request id, usage and cost. - Pairs:
validator_version(val-1.0). - Gutenberg catalog: sha256 recorded in its manifest.
5. What is committed and what is not
| Committed | Not committed (.gitignore) |
|---|---|
code, configs, tests, docs, data/manifests/*.json, artifacts/{metrics,figures,tables,reports}, paper/ sources and paper/main.pdf |
data/{raw,parsed,segments,pairs,splits}/*, artifacts/checkpoints/*, artifacts/runs/, .env, keys |
Consequence. Third parties regenerate the data from the manifests and the commands above. Release-eligible
parsed text is distributed only through the benchmark bundle (LICENSE_MATRIX.md). The API key is read from
the environment (ANTHROPIC_API_KEY) and is never written to disk by the code.
6. From a clean environment to Gate F
- Clone
gitsrv:srv/git/wpai0002_uqo.git. - Create the venv as in §1 and run the unit tests.
- Run
scripts/vertical_slice.sh(no network, no cost) to check the pipeline end to end. - Load a released checkpoint:Or runpython
HTRReconstructor.from_pretrained(dir).reconstruct(text, metadata)scripts/predict.py --checkpoint <dir>. - Gate F re-runs step 4 on a machine that never saw the training data, and compares outputs bit-for-bit with the release's reference outputs on CPU.
Known test-environment issue (2026-11-09)
On the laptop, running the full unit suite while a PyTorch MPS training process is active can crash the test interpreter with a native stack trace in embedding (observed with torch 2.12 / Python 3.14 when tests/unit/test_models.py runs after another module; test_models.py alone passes). The suite passes (47 tests) when no MPS training is running. Run pytest on an idle machine or on CPU-only nodes; this is an environment interaction, not a test failure of the code under test.