Format: Keep a Changelog. This project follows Semantic Versioning.
The JSON output contract has its own SCHEMA_VERSION, tracked independently
of the package version below.
0.2.0 - 2026-06-21
First release published to PyPI. (0.1.0 was prepared and tagged in the changelog below but never uploaded; this is the first public artifact.) Builds on the 0.1.0 baseline with the cache wired in, a CPU-runnable full pipeline, and opt-in Schaefer-400 detail.
SCHEMA_VERSION moves 1.0 → 1.1 (additive, backward-compatible).
- Two-tier cache wired into the scoring path.
score()now reuses a cached TRIBE forward pass for the same video (prediction cache) and a cachedScoreResultfor identical scoring inputs (score cache);score_from_predictions/score_from_prediction_bundleuse the score cache.cache inforeports real, non-zero counts after a run. Toggle withScoreConfig(use_cache=..., refresh=...)/ the CLI--no-cacheand--refreshflags (the--no-cacheflag removed in 0.1.0 returns, as promised). ReplayRunner(cortex_score.runners.ReplayRunner): a torch-freePredictionRunnerthat replays a saved(T, 20484)tensor through the fullscore(video, runner=...)path. Re-score predictions offline on CPU and get byte-identical JSON to a GPU run; also gives the headline path deterministic CI coverage.- Opt-in Schaefer-400 per-parcel detail (
SCHEMA_VERSION = "1.1").ScoreConfig(include_schaefer_parcels=True)adds aScoreResult.schaeferobject withmean_energy/peak_energy/temporal_volatilityfor all 400 Schaefer-2018 parcels, each tagged with its parent Yeo-17 network. The default 5-network output is numerically unchanged; the only deltas in a default result are the bumped version string and a nullableschaeferfield. This is the first runtime consumer of the bundled Schaefer-400 vertex map, the Schaefer-400 → Yeo-17 mapping, andprocessing.metrics. python -m cortex_scoreentry point (mirrors thecortex-scoreCLI).torchaudioadded to the[gpu-deps]extra andwhisperxpinned inrequirements/tribev2-gpu.txt, sopip install "cortex-score[gpu-deps]"plus the requirements file is genuinely sufficient to run the full path.benchmarks/bench_aggregate.py: reproducible aggregation micro-benchmark.
aggregate_to_roisreimplemented with a stable-argsort grouping instead of anO(n_rois × V)per-ROI boolean scan - bit-identical output (proven by a property test against the previous loop; the default network scores are unchanged) and ~2.5× faster at Schaefer-400 scale._parse_segmentsextracted from the GPU-only runner into a testable, pandas-freerunners.segments.parse_segmentsseam (duck-types the DataFrame path via.columns/.iterrows).api._load_default_runnersimplified: the deadMissingOptionalDependencyError/ImportErrorhandlers were removed (importing the TRIBE runner is torch-free; the optional-dependency error correctly surfaces frompredict_video).- Documentation reconciled with the code: "aggregate to Yeo-17" (not
Schaefer-400); examples use the full 40-char TRIBE commit SHA; the
modal_smokeper-clip cost comment matches its own arithmetic (≈ $0.11); honest "Not a brain scan" framing added to the hero/spotlight SVGs.
processing.aggregate.remap_atlas- it was a one-line alias ofaggregate_to_roisused only by its own test; the Schaefer detail needs the parcel → network label map, not numeric Schaefer → Yeo aggregation.- The unused
[viz]extra (nibabel): the MZ3 overlay writer is pure stdlib + numpy and there is no NIfTI export. The dependency advertised a capability that did not exist.
Initial release preparation. Note: 0.1.0 was never published to PyPI -
it is recorded here as the development baseline that 0.2.0 builds on. The
CPU-only postprocessing tier (score_from_predictions /
score_from_prediction_bundle) and the ScoreResult JSON contract
(SCHEMA_VERSION = "1.0") were stabilized here.
- Initial package scaffold (Epics 0-8):
- Bundled atlas data (Schaefer-400 + Yeo-17 + 5-network rollup) with SHA-256 manifest.
- Pure-NumPy processing core: aggregate, normalize, metrics, networks, validate.
- Pydantic v2 schema layer with full provenance:
PredictionBundle,ScoreResult,NetworkScore,AtlasMeta,ProvenanceMeta,NormalizationMeta,LicenseRestriction,ScoreWarning. - Three-tier public API:
score_from_prediction_bundle(bundle)- type-safe.score_from_predictions(preds, mesh=..., tr_seconds=..., ...)- ergonomic, requires explicit scientific assumptions.score(video_path, runner=None)- full pipeline.
CortexScorerclass for batch reuse.- TRIBE v2 adapter under the
[gpu-deps]extra (TRIBE itself installed fromrequirements/tribev2-gpu.txt, pinned to commit34f52344e5ba96660fac877393e1954e399d3ef3). - Two-tier cache infrastructure (prediction cache + score cache, atomic
writes,
cache_manifest.json, invalidation matrix). NOTE: shipped as plumbing only in 0.1.0 - the scoring path did not read or write it yet (wired in 0.2.0). - Typer CLI under
[cli]extra:doctor,score,from-predictions,schema,cache info,cache clear. - Test suite with property tests, schema snapshot, cache invalidation matrix, packaging smoke, MZ3 format round-trip, result_id verifiability, and import-without-GPU gate.
result_idis now the SHA-256 of the result's own canonical JSON (withresult_idblanked), so it is reproducible from the serialized artifact. The previous hand-built hash payload disagreed with the serialized form (+00:00vsZdatetimes), making the documented audit hash unverifiable.- MZ3 scalar-overlay export now emits the real NiiVue format (uint16 magic
0x5A4D,attr=8/isSCALAR, 16-byte header, gzip). The earlier port wrote a header NiiVue rejected at the magic check. score_from_predictionsrejects 1-D/non-finite inputs and unsupported meshes with clear errors at the boundary (UnsupportedMeshError) instead of an opaqueIndexError/ a raw traceback through the CLI.- Version is read from installed distribution metadata
(
importlib.metadata); the build no longer ships a generated_version.py.
- The no-op
score --no-cacheflag (it silently did nothing). It returns in 0.2.0 now that the cache is wired into the scoring path.
- The package source is MIT-licensed. The bundled atlases ship under
their original licenses (Schaefer MIT, Yeo BSD-like). The full
score()path uses TRIBE v2 which is CC-BY-NC-4.0: outputs from that path inherit the non-commercial restriction. SeeLICENSE-THIRD-PARTY.md.