Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ updates:
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
day: monday
interval: monthly
time: "09:00"
timezone: America/Los_Angeles
open-pull-requests-limit: 5
Expand Down
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ the development lock with `pip install -r requirements/dev.lock`, then run
- Lint: `.venv/bin/ruff check .` (CI enforces this; must stay clean). Auto-fix: `ruff check . --fix`.
- Dependency changes: edit `pyproject.toml`, then run
`scripts/update_dependency_locks.sh`; commit `poetry.lock` and every changed
export under `requirements/`.
export under `requirements/`. Because `pyproject.toml` and `poetry.lock` are
release-critical fingerprinted inputs, a dependency change also requires
regenerating both artifact releases from the new clean source commit
(expansion wrapper first, then the paper wrapper); Dependabot lock-only PRs
therefore cannot pass CI and are handled through this coordinated flow.
- Operational scripts import `quantcortex.*`, so run them with the root on the
path: `PYTHONPATH=. .venv/bin/python scripts/<name>.py`
(validate_performance, generate_report, survivorship_demo, verify_brokers,
Expand Down
4 changes: 2 additions & 2 deletions docs/img/performance_manifest.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"schema_version": 4,
"generated_at": "2026-07-02T05:47:22Z",
"generated_at": "2026-07-02T06:57:57Z",
"generator": {
"path": "scripts/generate_report.py",
"script_sha256": "b536aa7fc5e4fe7df6c7ff28c0992629a489869eaec46486db7aff1cb946099b",
"git": {
"source_commit": "dddead6351d956223e2c7aecf959dd2d93388be9",
"source_commit": "34096ad11c1ae33531186a86961a9e2e883d60a9",
"worktree_clean_at_start": true
},
"source_tree": {
Expand Down
10 changes: 5 additions & 5 deletions paper/build_manifest.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
{
"anonymous_pdf": {
"path": "quantcortex_audit_anonymous.pdf",
"sha256": "7a7e5283b098957602eea34e38dde3ffe5b2968a622f4789e0ad8bdbe0c3bbb6"
"sha256": "74bd8f67606e4bad177b81c49f3818fd2682abceef3b39cb279bb497d5b8d062"
},
"pdf": {
"path": "quantcortex_audit_neurips2026.pdf",
"sha256": "cf50fea34c2020061d86815c586106de87f26c9e29af46336524b899d1365cc2"
"sha256": "c2a16206747a6f9205396cb040d7cd01d0f5306319570286a218d9072388e7af"
},
"schema_version": 1,
"source_commit": "dddead6351d956223e2c7aecf959dd2d93388be9",
"source_date_epoch": 1782970812,
"source_commit": "34096ad11c1ae33531186a86961a9e2e883d60a9",
"source_date_epoch": 1782975290,
"source_manifest": {
"path": "quantcortex_audit_neurips2026.sources.sha256",
"sha256": "39e28d3967612a086eaab5d81bbb69023dbb939900c119f42d28cab3ec48a631"
"sha256": "e550f507515e06a0ee005ec23b828d0ed346230841a99c718063589249c3d443"
},
"tectonic_bundle": {
"name": "default_bundle_v33.tar",
Expand Down
8 changes: 4 additions & 4 deletions paper/expansion/results/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@
}
]
},
"generated_at": "2026-07-02T05:40:12Z",
"generated_at": "2026-07-02T06:54:51Z",
"git": {
"source_commit": "dddead6351d956223e2c7aecf959dd2d93388be9",
"source_commit": "34096ad11c1ae33531186a86961a9e2e883d60a9",
"tracked_worktree_clean_at_start": true
},
"protocol": {
Expand Down Expand Up @@ -290,9 +290,9 @@
"quantcortex/timing/vix_scaler.py": "a3667424e5573fb289e63c26c69da6a68d6c943742359f0466d29b25c56e3686",
"schemas/canonical_target_tape.schema.json": "4f1c0bf6d5360305d2982adea78de3f61c4bc1ebae9207cb2ba2bd4379b43d44",
"scripts/fetch_expansion_data.py": "678b5c7fcc1b89e333fc5298b1fdaeb8994de713bc7b2b5ed461e1ec1eb94403",
"scripts/release_expansion_artifacts.sh": "c66de150012bc2ad4ea06f65e2f8993a564c003c717e3b220cf6c2f665d363e0",
"scripts/release_expansion_artifacts.sh": "727e7b9c023df48550481f38887624e5c6154217e279af2d4e77da11b93c7400",
"scripts/run_expansion_experiments.py": "df9932dc67a1e1151faebc5dfd742f0aa622dfcf8093ce5ef9c55d9be4fbaf59"
},
"sha256": "3aeaab12c2c63f8022700be3a45978b4ca4f429703fde4566877eb4f2c830f0f"
"sha256": "3cea145651a58cdbb70509df5f53c56ff22d6ddf07c3bbe282280940d918777f"
}
}
Binary file modified paper/quantcortex_audit_anonymous.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion paper/quantcortex_audit_anonymous.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7a7e5283b098957602eea34e38dde3ffe5b2968a622f4789e0ad8bdbe0c3bbb6 quantcortex_audit_anonymous.pdf
74bd8f67606e4bad177b81c49f3818fd2682abceef3b39cb279bb497d5b8d062 quantcortex_audit_anonymous.pdf
Binary file modified paper/quantcortex_audit_neurips2026.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion paper/quantcortex_audit_neurips2026.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cf50fea34c2020061d86815c586106de87f26c9e29af46336524b899d1365cc2 quantcortex_audit_neurips2026.pdf
c2a16206747a6f9205396cb040d7cd01d0f5306319570286a218d9072388e7af quantcortex_audit_neurips2026.pdf
6 changes: 3 additions & 3 deletions paper/quantcortex_audit_neurips2026.sources.sha256
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ c2b36aafee0ad2e3ac631e05a5fc1b20e1acce10dd0b4758667f0a809cafff51 checklist.tex
62609e68cbc90516cf19a46d52f80bf07ab0a4751880e4c078cc9be4ba842a5c references.bib
0c1ad36961fcd9198dcc2558cf2793e1df39973bde8264fd701f5e7970672757 neurips_2026.sty
06f4407daed7bcd594e00bbf2751e7bd32c5d00eae530b2a6b2f66625b864162 preregistration.md
32e1a703329d6767d4184a940009c158a723738ba53c7de0ecc57bfac861da9b results/generated_values.tex
105d350319bac9a92bfb746d10da7414ad7de43140b69f6987ec5b54eca4c713 results/manifest.json
4cee7de114227526d83abaae47a0c647451a864056955e91d099f846dfc4dfc4 results/generated_values.tex
046e5cc9146f4344c144b9fa947e8b18d6f8c2b7a2bce26c5cc96f03c4d5576c results/manifest.json
e49e41a12a19fa5404a573ba5e21eb8a2888e616985f8c610d9652866923315c expansion/protocol.json
2dbaa11bfdd9a1936b45114f61bd96c53e3d57eefe103a75c488352486c0e2f9 expansion/results/generated_values.tex
1db304ba19a96ac6890c3b6bec2bb7dc13793bf1298af990712b03ce617c4066 expansion/results/manifest.json
8c9cfa381e4382bd7fe45af82e9f9d6a1a4c0503fd35db824053381e5c8d16a8 expansion/results/manifest.json
18608aa1250c4554b2e27b507211f764fdf1ec1fb8e4b9f09e080601505e2e3a figures/accounting_summary.pdf
6765f7d1f827577ba648af7545d006ef7f005f0091194605c6130100215ac18c figures/audit_protocol.pdf
19df4c9eabe88add863ed4be4e9315aa0084b833be9deb4d5bb7633e769ced07 figures/bootstrap_robustness.pdf
Expand Down
2 changes: 1 addition & 1 deletion paper/results/generated_values.tex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
\newcommand{\PaperRequiredWarmupSessions}{274}
\newcommand{\PaperBootstrapReplications}{5,000}
\newcommand{\PaperInputDigest}{efb384a62157e56a0cd8065abf45c1ed07d90ec26c681e5d54d74fe4cb9c55e1}
\newcommand{\PaperSourceTreeDigest}{5bf7019e1a5f23262701fa5d8e02780c9f274a9b12821a4323818fd282efd295}
\newcommand{\PaperSourceTreeDigest}{1c05d00cea1d0e9acc5118c3e9302fc50111a327ed15c48e25019a40cecec5b8}
\newcommand{\PaperNetCAGR}{1.40\%}
\newcommand{\PaperGrossCAGR}{3.17\%}
\newcommand{\PaperCashCAGR}{2.50\%}
Expand Down
14 changes: 7 additions & 7 deletions paper/results/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"results/cost_sensitivity.csv": "25b9969cf8ecdabd19e7761ad2973252c9f5b9f994b6dc147595a2a112c24d88",
"results/engine_comparison.csv": "ea0322dcd2aa4a1eb3b5996045c3a4b2ed25a85684a578ee788b013b99b643cd",
"results/evaluation_contract.json": "77ee05ce64622ef9ba1bfbd7dae85c4f6fd44f07db0b21feaf6b3e0e418673e7",
"results/generated_values.tex": "32e1a703329d6767d4184a940009c158a723738ba53c7de0ecc57bfac861da9b",
"results/generated_values.tex": "4cee7de114227526d83abaae47a0c647451a864056955e91d099f846dfc4dfc4",
"results/protocol_switches.csv": "20d5a0dc37e07a20c1f2772c8e9940ff6464ef1da98692edd5fdb06531bc2393",
"results/return_decomposition.csv": "53a84aa9b91c92f0037e48dc09154807cbf003ccf0038f39da8406108bf709cc",
"results/sharpe_uncertainty.csv": "aead873ced9b25c76c6944aa6e3ba0901ade65c784fa5cb8cc10ad1c6c01f136",
Expand Down Expand Up @@ -240,14 +240,14 @@
"path": "results/evaluation_contract.json",
"schema_version": 1
},
"generated_at": "2026-07-02T05:47:22Z",
"generated_at": "2026-07-02T06:57:57Z",
"generator": {
"dependency_lock": {
"path": "poetry.lock",
"sha256": "d4e2e756f8ba3ca67ca0e7592c56d1e3c42303fd8b5af06dd52f25658d9e6ceb"
},
"git": {
"source_commit": "dddead6351d956223e2c7aecf959dd2d93388be9",
"source_commit": "34096ad11c1ae33531186a86961a9e2e883d60a9",
"worktree_clean_at_start": true
},
"packages": {
Expand All @@ -261,7 +261,7 @@
"path": "scripts/run_paper_experiments.py",
"platform": "macOS-26.4-arm64-arm-64bit-Mach-O",
"python": "3.14.4",
"script_sha256": "922bf3c414e0eadca5c13ee831528347dd83a0cd174efbdf15a987b511845de5",
"script_sha256": "25010ce7302d1113e48fb7095dabf1e0bb5b283ba9270455cdbf55382718530e",
"source_tree": {
"file_count": 112,
"files": {
Expand Down Expand Up @@ -375,10 +375,10 @@
"quantcortex/timing/vix_scaler.py": "a3667424e5573fb289e63c26c69da6a68d6c943742359f0466d29b25c56e3686",
"schemas/canonical_target_tape.schema.json": "4f1c0bf6d5360305d2982adea78de3f61c4bc1ebae9207cb2ba2bd4379b43d44",
"schemas/evaluation_contract.schema.json": "970f24f587e669925306625d12c5a84dffd03ff5b222a59905849b2fa222784f",
"scripts/release_paper_artifacts.sh": "410b1f444ce242b94fa9ba7bcf5868fcc74ad664f2c24f9503d4f3a230e8b38c",
"scripts/run_paper_experiments.py": "922bf3c414e0eadca5c13ee831528347dd83a0cd174efbdf15a987b511845de5"
"scripts/release_paper_artifacts.sh": "fbf68e79be2479e089e638d88923de3aa675dacbfc1e180ffe75b2a05eaecf39",
"scripts/run_paper_experiments.py": "25010ce7302d1113e48fb7095dabf1e0bb5b283ba9270455cdbf55382718530e"
},
"sha256": "5bf7019e1a5f23262701fa5d8e02780c9f274a9b12821a4323818fd282efd295"
"sha256": "1c05d00cea1d0e9acc5118c3e9302fc50111a327ed15c48e25019a40cecec5b8"
},
"threadpools": [
{
Expand Down
10 changes: 6 additions & 4 deletions scripts/release_expansion_artifacts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ if [[ ! -x "${python_bin}" ]]; then
printf '%s\n' "Python environment not found: ${python_bin}" >&2
exit 1
fi
# Generation runs from a detached worktree at the source commit, so only
# uncommitted changes to release-critical source can corrupt a release.
# Scoping the cleanliness check to those paths keeps the wrapper rerunnable
# while regenerated artifacts sit uncommitted in the working tree.
# Generation runs from a detached worktree at the source commit, so
# uncommitted changes outside release-critical source cannot alter what is
# generated here. Scoping the cleanliness check to those paths keeps the
# wrapper rerunnable while regenerated artifacts sit uncommitted in the
# working tree; the paper wrapper independently verifies those artifacts
# against this release's manifest before republishing them.
release_source_paths=(
quantcortex
schemas/canonical_target_tape.schema.json
Expand Down
22 changes: 17 additions & 5 deletions scripts/release_paper_artifacts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ if [[ ! -x "${python_bin}" ]]; then
exit 1
fi

# Generation runs from a detached worktree at the source commit, so only
# uncommitted changes to release-critical source can corrupt a release.
# Scoping the cleanliness check to those paths lets the paper release run
# after the expansion wrapper has deposited regenerated artifacts in the
# working tree, which the documented dual-release flow requires.
# Generation runs from a detached worktree at the source commit, so
# uncommitted changes outside release-critical source cannot alter what is
# generated. Scoping the cleanliness check to those paths lets the paper
# release run after the expansion wrapper has deposited regenerated artifacts
# in the working tree, which the documented dual-release flow requires. The
# expansion artifacts that this release republishes are separately verified
# against the expansion manifest below before they are copied.
release_source_paths=(
quantcortex
schemas
Expand Down Expand Up @@ -250,6 +252,16 @@ if [[ "${expansion_source_commit}" != "${source_commit}" ]]; then
"expansion source: ${expansion_source_commit}" >&2
exit 1
fi
# The copied expansion artifacts are republished inside the built PDFs, so
# verify every file against the expansion manifest before copying. A tampered
# or unexpected expansion output must fail the release, not flow into print.
PYTHONPATH="${repo_root}" "${python_bin}" - "${repo_root}/paper/expansion" <<'PY'
import sys

from scripts.run_paper_experiments import verify_expansion_artifacts

verify_expansion_artifacts(sys.argv[1])
PY
rm -rf "${source_worktree}/paper/expansion/results" \
"${source_worktree}/paper/expansion/figures"
cp -R "${repo_root}/paper/expansion/results" \
Expand Down
55 changes: 55 additions & 0 deletions scripts/run_paper_experiments.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,61 @@ def _json_sha256(value: object) -> str:
return hashlib.sha256(encoded).hexdigest()


def verify_expansion_artifacts(expansion_root: Path) -> None:
"""Fail closed unless the expansion artifact tree matches its manifest.

The paper release republishes ``paper/expansion/results`` and
``paper/expansion/figures`` inside the built PDFs, so every file it copies
must be exactly the file the expansion manifest recorded. Rejects absolute
or parent-traversing manifest paths, symlinks, missing files, digest
mismatches, and files present on disk that the manifest does not list
(``results/manifest.json`` itself is the only exception, because it cannot
record its own hash).
"""
expansion_root = Path(expansion_root)
manifest_path = expansion_root / "results" / "manifest.json"
if manifest_path.is_symlink() or not manifest_path.is_file():
raise SystemExit(f"expansion manifest missing: {manifest_path}")
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
artifacts = manifest.get("artifacts")
if not isinstance(artifacts, dict) or not artifacts:
raise SystemExit("expansion manifest lists no artifacts")

resolved_root = expansion_root.resolve()
for relative, expected in artifacts.items():
candidate = Path(relative)
if candidate.is_absolute() or ".." in candidate.parts:
raise SystemExit(f"unsafe expansion artifact path: {relative}")
artifact = expansion_root / candidate
if artifact.is_symlink():
raise SystemExit(f"expansion artifact is a symlink: {relative}")
if not artifact.is_file():
raise SystemExit(f"expansion artifact missing: {relative}")
if not artifact.resolve().is_relative_to(resolved_root):
raise SystemExit(f"unsafe expansion artifact path: {relative}")
if _sha256(artifact) != expected:
raise SystemExit(f"expansion artifact digest mismatch: {relative}")

allowed = {Path(relative) for relative in artifacts}
allowed.add(Path("results/manifest.json"))
for subdirectory in ("results", "figures"):
directory = expansion_root / subdirectory
if not directory.is_dir():
raise SystemExit(f"expansion directory missing: {subdirectory}")
for found in sorted(directory.rglob("*")):
if found.is_symlink():
raise SystemExit(
"unexpected symlink under expansion artifacts: "
f"{found.relative_to(expansion_root)}"
)
if found.is_file():
relative_found = found.relative_to(expansion_root)
if relative_found not in allowed:
raise SystemExit(
f"unexpected expansion artifact: {relative_found}"
)


def _threadpool_environment() -> list[dict[str, object]]:
"""Return stable BLAS/OpenMP metadata without machine-specific paths."""
try:
Expand Down
106 changes: 105 additions & 1 deletion tests/test_paper_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
import subprocess
from pathlib import Path

from scripts.run_paper_experiments import _json_sha256, source_tree_manifest
import pytest

from scripts.run_paper_experiments import (
_json_sha256,
source_tree_manifest,
verify_expansion_artifacts,
)

REPO_ROOT = Path(__file__).resolve().parent.parent
PAPER_ROOT = REPO_ROOT / "paper"
Expand Down Expand Up @@ -446,3 +452,101 @@ def test_paper_citations_are_defined_used_and_unique():

for eprint in eprints:
assert f"https://arxiv.org/abs/{eprint}" in bibliography


def _build_expansion_fixture(root: Path) -> Path:
"""Create a minimal valid expansion artifact tree with a correct manifest."""
expansion = root / "expansion"
(expansion / "results").mkdir(parents=True)
(expansion / "figures").mkdir()
(expansion / "results" / "summary.csv").write_text("a,b\n1,2\n", encoding="ascii")
(expansion / "figures" / "plot.pdf").write_bytes(b"%PDF-1.4 fixture")
artifacts = {
"results/summary.csv": _sha256(expansion / "results" / "summary.csv"),
"figures/plot.pdf": _sha256(expansion / "figures" / "plot.pdf"),
}
manifest = {"git": {"source_commit": "0" * 40}, "artifacts": artifacts}
(expansion / "results" / "manifest.json").write_text(
json.dumps(manifest), encoding="ascii"
)
return expansion


def test_verify_expansion_artifacts_accepts_valid_tree(tmp_path):
expansion = _build_expansion_fixture(tmp_path)
verify_expansion_artifacts(expansion)


def test_verify_expansion_artifacts_rejects_tampered_artifact(tmp_path):
# Regression: the paper release once copied expansion artifacts into the
# built PDFs after checking only the manifest's source commit, so a
# tampered aggregate (for example an edited generated value) was published
# without any digest failure.
expansion = _build_expansion_fixture(tmp_path)
(expansion / "results" / "summary.csv").write_text("a,b\n9,9\n", encoding="ascii")
with pytest.raises(SystemExit, match="digest mismatch"):
verify_expansion_artifacts(expansion)


def test_verify_expansion_artifacts_rejects_missing_artifact(tmp_path):
expansion = _build_expansion_fixture(tmp_path)
(expansion / "figures" / "plot.pdf").unlink()
with pytest.raises(SystemExit, match="missing"):
verify_expansion_artifacts(expansion)


def test_verify_expansion_artifacts_rejects_unexpected_file(tmp_path):
expansion = _build_expansion_fixture(tmp_path)
(expansion / "results" / "extra.csv").write_text("x\n", encoding="ascii")
with pytest.raises(SystemExit, match="unexpected expansion artifact"):
verify_expansion_artifacts(expansion)


def test_verify_expansion_artifacts_rejects_symlink(tmp_path):
expansion = _build_expansion_fixture(tmp_path)
target = expansion / "results" / "summary.csv"
link = expansion / "figures" / "plot.pdf"
link.unlink()
link.symlink_to(target)
with pytest.raises(SystemExit, match="symlink"):
verify_expansion_artifacts(expansion)


def test_verify_expansion_artifacts_rejects_unsafe_manifest_paths(tmp_path):
expansion = _build_expansion_fixture(tmp_path)
manifest_path = expansion / "results" / "manifest.json"
manifest = json.loads(manifest_path.read_text(encoding="ascii"))

outside = tmp_path / "outside.txt"
outside.write_text("leak\n", encoding="ascii")
for unsafe in ("../outside.txt", str(outside)):
tampered = dict(manifest)
tampered["artifacts"] = dict(manifest["artifacts"])
tampered["artifacts"][unsafe] = _sha256(outside)
manifest_path.write_text(json.dumps(tampered), encoding="ascii")
with pytest.raises(SystemExit, match="unsafe"):
verify_expansion_artifacts(expansion)


def test_verify_expansion_artifacts_rejects_empty_manifest(tmp_path):
expansion = _build_expansion_fixture(tmp_path)
manifest_path = expansion / "results" / "manifest.json"
manifest_path.write_text(json.dumps({"artifacts": {}}), encoding="ascii")
with pytest.raises(SystemExit, match="lists no artifacts"):
verify_expansion_artifacts(expansion)


def test_release_wrapper_verifies_expansion_artifacts_before_copying():
release_script = (REPO_ROOT / "scripts" / "release_paper_artifacts.sh").read_text(
encoding="utf-8"
)
assert "verify_expansion_artifacts" in release_script
# The verification must run before the expansion artifacts are copied into
# the detached build worktree.
assert release_script.index("verify_expansion_artifacts") < release_script.index(
'cp -R "${repo_root}/paper/expansion/results"'
)


def test_committed_expansion_artifacts_validate():
verify_expansion_artifacts(PAPER_ROOT / "expansion")