fix: harden cortex-score for first PyPI release#1
Merged
Conversation
Pre-release pass from a multi-agent review. Fixes correctness bugs, finishes the version-source migration, tightens the output contract, and makes the package PyPI-publishable. 137 tests green, ~90% coverage; ruff + mypy --strict clean; build + twine check + wheel install smoke ok. Correctness - result_id: hash the result's own canonical JSON (model_dump(mode=json), result_id blanked, sorted keys) instead of a hand-built parallel dict. The old payload used isoformat() (+00:00) while pydantic serializes Z, so the documented audit hash never verified against the artifact. - export/niivue: write the real NiiVue MZ3 format (uint16 magic 0x5A4D, attr=8/isSCALAR, 16-byte <HHIII header, gzip, deterministic). The previous port wrote a header NiiVue rejects at the magic check. - score_from_predictions: reject 1-D/non-finite inputs and unsupported meshes (new UnsupportedMeshError) at the boundary instead of an opaque IndexError / raw CLI traceback. - build_network_summary: raise AtlasMismatchError on out-of-range Yeo indices instead of silently zero-filling a network. - cli: catch FileNotFoundError/ValueError so bad input exits 1 cleanly. - tribev2: surface previously-silent segment-parse failures as warnings. Version source - Complete the importlib.metadata migration: track version.py, drop the hatch-vcs version-file hook and its ruff/coverage refs, remove the dead generated _version.py from the wheel. Fallback is a 0.0.0+unknown sentinel for uninstalled source trees. Contract / types - Constrain sha256 fields to hex and audit-identity strings to non-empty. - NetworkScore label/index check -> model_validator (no field-order dep). - Make cached atlas arrays read-only and labels immutable. Packaging / CI - PEP 639 SPDX license metadata (license = "MIT" + license-files), hatchling>=1.27, Python 3.13 classifier. - CI: add 3.13, least-privilege permissions, SHA-pinned actions, a built-wheel install smoke gate, and run the packaging tests. - release.yml: pin the publish action, add a pre-publish wheel smoke, enable attestations. Docs / cleanup - Add docs/install-gpu.md; fix README "not on PyPI yet" + result_id copy; add CHANGELOG 0.1.0 and CITATION version/date; honest cache wording. - Remove the no-op `score --no-cache` flag and dead code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Pre-release hardening pass driven by a multi-agent code review (10 specialist reviewers + adversarial verification). Fixes real correctness bugs, finishes the version-source migration, tightens the published output contract, and makes the package PyPI-publishable.
Verified green: 137 tests pass (~90% coverage, up from 125 / 88%);
ruff+ruff format --check+mypy --strictclean;python -m build+twine checkpass; the built wheel installs and scores in a clean, torch-free environment.Correctness fixes
result_idaudit hash now verifies. It was computed from a hand-built parallel dict usingcreated_at.isoformat()(+00:00) while the serialized JSON usesZ, so recomputing the hash from the artifact never matched the documented contract. Now hashed from the result's own canonical serialization — proven to round-trip from the JSON alone.<I 0x4D5A0003magic (reads as3, not23117),attr=1(=isFACE, not isSCALAR=8), an 18-byte header, and raw zlib — NiiVue rejects all of that at the magic check. Rewritten to the verified spec (<HHIII, magic0x5A4D,attr=8, gzip, deterministic) with a test that validates the real bytes.UnsupportedMeshError); the CLI exits1cleanly on missing files / bad input; out-of-range Yeo indices raiseAtlasMismatchErrorinstead of silently zero-filling a network; previously-silent TRIBE segment-parse failures now emit warnings.Version source
Completed the half-finished
importlib.metadatamigration (the previous tree had an untrackedversion.pythat imports would have broken on commit): tracksversion.py, drops the deadhatch-vcs_version.pyhook + its ruff/coverage refs, and stops shipping the generated artifact. Installed wheels resolve the real version into provenance; uninstalled source trees report an honest0.0.0+unknownsentinel.Contract / packaging / CI
license = "MIT"+license-files),hatchling>=1.27, Python 3.13 classifier.permissions, SHA-pinned actions, a built-wheel install-smoke gate, packaging tests wired in.release.yml: pinned publish action, pre-publish wheel smoke, attestations enabled.Docs / cleanup
Added
docs/install-gpu.md(was referenced but missing); fixed the README "not on PyPI yet" line andresult_idcopy; added a0.1.0CHANGELOG entry and CITATIONversion/date-released; made the cache wording honest (it's v0.1.1 plumbing, not yet wired into scoring); removed the no-opscore --no-cacheflag and dead code.Decisions
--no-cacheflag was removed.importlib.metadataswitch rather than reverting (modern standard; makes source checkouts importable).Before publishing (manual, can't be done in this PR)
pypiGitHub environment and no PyPI pending publisher yet. Add a pending publisher on PyPI (repomadhavcodez/cortex-score, workflowrelease.yml, environmentpypi) and create apypiGitHub environment. The namecortex-scoreis available.v0.1.0after merge — version is git-tag-driven; the tag triggersrelease.yml.Test plan
pytest -m "not gpu"— 137 passed, ~90% coverageruff check/ruff format --check/mypy --strict— cleanpython -m build+twine check— pass (Metadata 2.4,License-Expression: MIT)result_idround-trips from serialized JSON