Skip to content

Commit e0c5048

Browse files
committed
refactor: integrate native replication protocols
1 parent f69f373 commit e0c5048

1,083 files changed

Lines changed: 86131 additions & 5422 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
"""Fail when a release artifact leaks operational or audit-only material."""
2+
3+
from __future__ import annotations
4+
5+
import argparse
6+
import configparser
7+
import io
8+
import tarfile
9+
from collections.abc import Iterable
10+
from pathlib import Path, PurePosixPath
11+
from zipfile import ZipFile
12+
13+
FORBIDDEN_PARTS = {
14+
"__pycache__",
15+
"hpc",
16+
"modssc_cache",
17+
"provenance",
18+
"slurm",
19+
"tools",
20+
"uv.lock",
21+
"weka",
22+
}
23+
FORBIDDEN_SUFFIXES = {".class", ".jar", ".java", ".pyc", ".pyo"}
24+
FORBIDDEN_FRAGMENTS = {
25+
"bench/assets/",
26+
"bench/campaign/",
27+
"bench/campaigns/",
28+
"bench/configs/reproductions/resources/",
29+
"graphlearningold-04bece45/",
30+
"match_reference/sources/",
31+
"tests/bench/test_hpc_",
32+
"tests/bench/test_match_continuation_controller.py",
33+
"tests/bench/test_public_hpc_portability.py",
34+
}
35+
REQUIRED_PATHS = {"bench/main.py"}
36+
37+
38+
def _required_paths() -> set[str]:
39+
repo_root = Path(__file__).resolve().parents[2]
40+
cards_root = repo_root / "bench" / "configs" / "reproductions"
41+
cards = {
42+
path.relative_to(repo_root).as_posix()
43+
for path in cards_root.rglob("*.yaml")
44+
if path.is_file()
45+
}
46+
if not cards:
47+
raise AssertionError("no reproduction cards found in the source tree")
48+
return REQUIRED_PATHS | cards
49+
50+
51+
def _normalized_names(names: Iterable[str], *, strip_root: bool) -> set[str]:
52+
normalized: set[str] = set()
53+
for name in names:
54+
parts = PurePosixPath(name).parts
55+
if strip_root and parts:
56+
parts = parts[1:]
57+
if parts:
58+
normalized.add(PurePosixPath(*parts).as_posix())
59+
return normalized
60+
61+
62+
def _assert_safe(names: set[str], *, artifact: Path) -> None:
63+
forbidden: list[str] = []
64+
for name in names:
65+
path = PurePosixPath(name)
66+
lowered = name.lower()
67+
if (
68+
FORBIDDEN_PARTS.intersection(part.lower() for part in path.parts)
69+
or path.suffix.lower() in FORBIDDEN_SUFFIXES
70+
or any(fragment in lowered for fragment in FORBIDDEN_FRAGMENTS)
71+
):
72+
forbidden.append(name)
73+
if forbidden:
74+
raise AssertionError(f"forbidden entries in {artifact.name}: {sorted(forbidden)}")
75+
missing = _required_paths() - names
76+
if missing:
77+
raise AssertionError(f"missing entries in {artifact.name}: {sorted(missing)}")
78+
79+
80+
def audit_wheel(wheel: Path) -> None:
81+
with ZipFile(wheel) as archive:
82+
names = _normalized_names(archive.namelist(), strip_root=False)
83+
_assert_safe(names, artifact=wheel)
84+
entry_points = next(
85+
(name for name in names if name.endswith(".dist-info/entry_points.txt")),
86+
None,
87+
)
88+
if entry_points is None:
89+
raise AssertionError(f"missing entry_points.txt in {wheel.name}")
90+
scripts = archive.read(entry_points).decode("utf-8")
91+
parser = configparser.ConfigParser()
92+
parser.read_file(io.StringIO(scripts))
93+
console_scripts = dict(parser.items("console_scripts"))
94+
if console_scripts.get("modssc-bench") != "bench.main:main":
95+
raise AssertionError(f"missing modssc-bench entry point in {wheel.name}")
96+
benchmark_runners = {
97+
name: target for name, target in console_scripts.items() if target.startswith("bench.")
98+
}
99+
if benchmark_runners != {"modssc-bench": "bench.main:main"}:
100+
raise AssertionError(
101+
f"unexpected benchmark runners in {wheel.name}: {benchmark_runners}"
102+
)
103+
104+
105+
def audit_sdist(sdist: Path) -> None:
106+
with tarfile.open(sdist, mode="r:gz") as archive:
107+
names = _normalized_names(archive.getnames(), strip_root=True)
108+
_assert_safe(names, artifact=sdist)
109+
110+
111+
def main() -> None:
112+
parser = argparse.ArgumentParser()
113+
parser.add_argument("--wheel", type=Path, required=True)
114+
parser.add_argument("--sdist", type=Path, required=True)
115+
args = parser.parse_args()
116+
audit_wheel(args.wheel)
117+
audit_sdist(args.sdist)
118+
119+
120+
if __name__ == "__main__":
121+
main()

‎.github/workflows/ci.yml‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,34 @@ jobs:
105105
python -m pip install --upgrade pip wheel
106106
pip install build
107107
python -m build
108+
109+
- name: Audit packaged benchmark runner and reproduction cards
110+
shell: bash
111+
run: |
112+
set -euo pipefail
113+
WHEEL="$(realpath "$(find dist -maxdepth 1 -name '*.whl' -print -quit)")"
114+
SDIST="$(realpath "$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)")"
115+
export WHEEL
116+
python .github/scripts/audit_distribution.py --wheel "$WHEEL" --sdist "$SDIST"
117+
118+
AUDIT_DIR="$(mktemp -d)"
119+
python -m venv "$AUDIT_DIR/venv"
120+
"$AUDIT_DIR/venv/bin/python" -m pip install "$WHEEL"
121+
mkdir "$AUDIT_DIR/empty"
122+
cd "$AUDIT_DIR/empty"
123+
env -u PYTHONPATH "$AUDIT_DIR/venv/bin/modssc-bench" --help
124+
env -u PYTHONPATH "$AUDIT_DIR/venv/bin/python" - <<'PY'
125+
from pathlib import Path
126+
127+
import bench
128+
import yaml
129+
from bench.schema import ExperimentConfig
130+
131+
cards_root = Path(bench.__file__).resolve().parent / "configs" / "reproductions"
132+
cards = sorted(cards_root.glob("*/*.yaml"))
133+
assert len(cards) == 20
134+
for card in cards:
135+
raw = yaml.safe_load(card.read_text(encoding="utf-8"))
136+
config = ExperimentConfig.from_dict(raw)
137+
assert config.dataset.integrity is not None
138+
PY

‎.github/workflows/release.yml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ name: Release
33
on:
44
push:
55
branches: ["main"]
6+
tags: ["v*"]
67
paths:
78
- "src/**"
9+
- "bench/**"
810
- "pyproject.toml"
11+
- ".github/scripts/audit_distribution.py"
912
- ".github/workflows/release.yml"
1013
workflow_dispatch:
1114

@@ -30,6 +33,12 @@ jobs:
3033
python -m pip install build
3134
python -m build
3235
36+
- name: Audit autonomous reproduction artifacts
37+
run: |
38+
python .github/scripts/audit_distribution.py \
39+
--wheel "$(find dist -maxdepth 1 -name '*.whl' -print -quit)" \
40+
--sdist "$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
41+
3342
- name: Upload dist artifacts
3443
uses: actions/upload-artifact@v7
3544
with:

‎.gitignore‎

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,29 @@ Thumbs.db
3434
site/
3535
docs/article_code/
3636

37+
# Replication publication staging and non-portable evidence
38+
docs/replications/**/raw/
39+
docs/replications/**/.staging/
40+
docs/replications/**/*.ckpt
41+
docs/replications/**/*.pt
42+
docs/replications/**/*.pth
43+
docs/replications/**/*.npy
44+
docs/replications/**/*.npz
45+
docs/replications/**/*.pkl
46+
docs/replications/**/*.pickle
47+
docs/replications/**/*.joblib
48+
docs/replications/**/*.bin
49+
docs/replications/**/*.h5
50+
docs/replications/**/*.hdf5
51+
docs/replications/**/*.onnx
52+
docs/replications/**/*.tar
53+
docs/replications/**/*.tar.gz
54+
docs/replications/**/*.tgz
55+
docs/replications/**/*.zip
56+
docs/replications/**/*.log
57+
docs/replications/**/*.out
58+
docs/replications/**/*.err
59+
3760
# Local artifacts, caches, runs
3861
/data/
3962
/outputs/
@@ -45,7 +68,7 @@ modssc_cache/*
4568
!modssc_cache/datasets/
4669
!modssc_cache/preprocess/
4770
!modssc_cache/output/
48-
!modssc_cache/graphs/
71+
!modssc_cache/graph/
4972
!modssc_cache/graph_views/
5073
!modssc_cache/splits/
5174
modssc_cache/datasets/*
@@ -54,8 +77,8 @@ modssc_cache/preprocess/*
5477
!modssc_cache/preprocess/.gitkeep
5578
modssc_cache/output/*
5679
!modssc_cache/output/.gitkeep
57-
modssc_cache/graphs/*
58-
!modssc_cache/graphs/.gitkeep
80+
modssc_cache/graph/*
81+
!modssc_cache/graph/.gitkeep
5982
modssc_cache/graph_views/*
6083
!modssc_cache/graph_views/.gitkeep
6184
modssc_cache/splits/*
@@ -64,11 +87,10 @@ modssc_cache/splits/*
6487
coverage.xml
6588
/analysis/
6689
/dev_wip/
67-
/logs_jeanzay/
6890
/audit_artifacts/
91+
/.modssc-private/
6992
/bench/generated/
7093
bench/configs/experiments/
7194
bench/configs/benchmarks/
7295
bench/configs/analysis/
7396
bench/configs/article/
74-
bench/slurm

‎.pre-commit-config.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ repos:
2424
- id: pytest
2525
name: pytest
2626
entry: >-
27-
bash -c 'if [ ! -x ./.venv/bin/python ]; then echo "Missing ./.venv/bin/python. Run: python3 -m venv .venv && make install-dev"; exit 1; fi; exec ./.venv/bin/python -m pytest'
27+
bash -c 'if [ ! -x ./.venv/bin/python ]; then echo "Missing ./.venv/bin/python. Run: python3 -m venv .venv && make install-dev"; exit 1; fi; exec ./.venv/bin/python -m pytest "$@"' --
2828
language: system
2929
args: ["-n", "4", "--dist", "loadgroup", "--cov-report=xml:coverage.xml"]
3030
pass_filenames: false

‎CHANGELOG.md‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,77 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on "Keep a Changelog", and this project adheres to Semantic Versioning.
66

7+
## Unreleased
8+
9+
### Added
10+
- Added native method capability contracts covering data modality,
11+
representation, graph/view/augmentation inputs, classifier outputs, backend,
12+
device, dtype, and checkpoint support.
13+
- Added explicit native execution contexts, content-addressed atomic
14+
checkpoints, seed-index execution, and honest multi-seed aggregation.
15+
- Added native sampling controls required by the published protocols, including
16+
exact holdout sizes, class-balanced streams, legacy RNG compatibility, and
17+
inclusive unlabeled pools.
18+
- Added paper-faithful method parameters and declarative reproduction cards for
19+
classic, Match-family, Calder graph-learning, and GRAND methods.
20+
- Added a composed pre-fit execution contract that verifies exact input roles,
21+
model outputs, optimizers, EMA objects, schedulers, and component relations,
22+
with a canonical report and SHA-256 in every benchmark result.
23+
- Added method-agnostic scientific acceptance in `modssc.evaluation`, with
24+
declarative targets and diagnostics, three-state assessment, independent
25+
fidelity classification, and a canonical SHA-256 report.
26+
27+
### Changed
28+
- Reduced `bench` to one responsibility: validate a YAML experiment, orchestrate
29+
registered ModSSC bricks, and report results. Method protocols and article
30+
identities no longer select hidden runner branches.
31+
- Calder cards now recompute their VAE representation and exact FAISS kNN graph
32+
through native preprocessing and graph-construction bricks.
33+
- Match-family and Democratic Co-Learning cards now construct their partitions
34+
from native sampling declarations instead of bundled replay files.
35+
- Reproduction claims distinguish historical frozen evidence from new
36+
statistical replications when a bit-identical source sequence is unavailable.
37+
- Moved each numerical acceptance specification into the reproduction YAML card
38+
it assesses. `bench` parses, orchestrates, and serializes the native result;
39+
it contains no article-specific acceptance mathematics.
40+
41+
### Removed
42+
- Removed the rejected benchmark and root campaign frameworks and bundled
43+
runtime paper artefacts; scientific behaviour now uses native registered
44+
components. The residual root `tools/`, `provenance/`, `tests/tools/`, and
45+
legacy HPC/continuation tests were removed after their recovery archive was
46+
checksummed and verified.
47+
- Removed the separate `modssc-reproduce` execution path; reproduction cards use
48+
the same `modssc-bench` runner as every other experiment.
49+
50+
### Fixed
51+
- Removed the Weka/Java runtime and all vendored GraphLearning, FixMatch,
52+
TorchSSL, and USB source dependencies; ModSSC executes its own scientific
53+
implementations.
54+
- Preserved historical public constructor signatures, standardized-method
55+
numerics and index spaces, cache read-only behavior, and graph precision while
56+
adding native replication contracts.
57+
- Removed hidden campaign/environment identity from Match checkpoints; resume
58+
behavior is now explicit in the run YAML and verified against run identity and
59+
payload integrity.
60+
- Re-hash declared dataset content both immediately after loading and immediately
61+
before writing a result, so same-size mutations and mid-run input changes fail
62+
with a typed integrity error.
63+
- Require SciPy for exact Student-t confidence intervals instead of silently
64+
substituting a normal approximation when the dependency is unavailable.
65+
- Exclude local cache contents and developer lock files from source
66+
distributions, with release-audit checks preventing either from being shipped.
67+
- Classify a declared non-convergence or insufficient pseudo-label outcome as
68+
`not_evaluable`, preserving native diagnostics instead of publishing a
69+
successful replication result.
70+
- Require DASO and TriNet to consume declared encoder/shared features, and
71+
require SimCLRv2 contrastive pretraining to consume a model-owned, optimized
72+
projection head. Classifier logits and undeclared feature aliases now fail
73+
closed instead of passing by shape.
74+
- Bind preprocess, graph, graph-view, and VAE cache keys to exact input content,
75+
implementation and software identity; publish authenticated entries
76+
atomically and reject legacy, partial, or modified cache data before reuse.
77+
778
## 1.2.2
879
### Fixed
980
- Stabilized Dynamic Label Propagation by renormalizing dynamic transition matrices after each update and failing explicitly on invalid transition weights instead of returning non-finite scores.

0 commit comments

Comments
 (0)