SPB Git

Repository Public Research preview

REPRODUCIBILITY.md 7.8 KB

# 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;
  • .venv on 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-v2 cached locally. It is loaded with HF_HUB_OFFLINE=1 and never downloaded.
  • PyMuPDF 1.27.
  • pandoc and TeX Live (xelatex, latexmk, biber) for the paper.

Clean environment:

bash
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.py selects passages stratified by (discipline, section role) with --seed. Only splits listed in --allowed-splits are eligible.
  • Spend. No money is spent without --live.
  • Corpus default path. The --corpus default in several scripts is data/parsed/pilot. The existing pilot directory is data/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

  1. Clone gitsrv:srv/git/wpai0002_uqo.git.
  2. Create the venv as in §1 and run the unit tests.
  3. Run scripts/vertical_slice.sh (no network, no cost) to check the pipeline end to end.
  4. Load a released checkpoint:
    python
    HTRReconstructor.from_pretrained(dir).reconstruct(text, metadata)
    Or run scripts/predict.py --checkpoint <dir>.
  5. 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.

Files main
Menu

Theme