Skip to content

release: cut 0.3.0 — the ranking face exists in no published release - #14

Merged
hseshadr merged 1 commit into
mainfrom
release/cut-0.3.0
Aug 3, 2026
Merged

release: cut 0.3.0 — the ranking face exists in no published release#14
hseshadr merged 1 commit into
mainfrom
release/cut-0.3.0

Conversation

@hseshadr

@hseshadr hseshadr commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Why this is urgent, not a chore

assay.ranking merged in #13 after the v0.2.0 tag, so it ships in no published release. PyPI avow 0.2.0 has neither the module nor ir-measures in its metadata.

The consequence is a live supply-chain hole. The first consumer had to pin avow to the git merge commit 1c382ce by full SHA to reach the module. A URL requirement cannot be resolved to a version, so that consumer's dependency audit now skips avow entirely"URL requirements cannot be pinned to a specific package version." A CVE against avow or its transitive dependencies would go unseen for as long as that pin stands.

Cutting the release closes it: the consumer moves back to a PyPI range, avow[assay]>=0.3.0, and its audit sees the package again.

A trap worth naming: the git checkout of 1c382ce reports version 0.2.0 — the same string as the published artifact that does not contain this code. Version strings were never the identity here.

Why 0.3.0, not 0.2.1

Two independent reasons, either sufficient on its own:

  1. A new public face + a new runtime dependency. assay.ranking and assay.ranking_score are new API; ir-measures is a new runtime dep. That is a feature, not a patch.
  2. A BREAKING rename is in the same unreleased set. ReplayMismatchPayloadHashMismatch, and the code string avow.replay_mismatchavow.payload_hash_mismatch. This repo bumps the minor for breaking changes in 0.x — that is exactly what 0.2.0 was ("a breaking release that makes three trust claims literally true").

The repo had also already committed to the number in prose: README.md:690 says the rename "ships in 0.3.0", and ts/README.md:57 says "renamed in 0.3.0". 0.2.1 would have contradicted both.

Every place the version lives — all three bumped

File Role
src/avow/_version.py hatchling's single source, read at build time so installed metadata and avow.__version__ cannot drift
ts/package.json @edgeproc/avow, pair-versioned. A v* tag publishes PyPI and npm; npm rejects a re-publish at an unchanged version
CHANGELOG.md [Unreleased][0.3.0] - 2026-08-03

The npm half is not optional, and it is not a judgement call — tests/test_packaging.py::test_python_and_typescript_packages_carry_the_same_version asserts ts/package.json version == avow.__version__. Bumping _version.py alone turns the gate red. publish.yml states the same thing in its header: "A tag push that re-runs this workflow at an unchanged version will be REJECTED by both registries. Bump the version before tagging."

Changelog correction: the entry named the wrong engine

The [Unreleased] entry claimed the ranking arithmetic was scikit-learn's. It is not, and has not been since the engine swap — it is trec_eval's, reached through ir_measures, the reference implementation the IR field validates its own numbers against.

The entry now records why, because the reasoning is the reusable part:

ndcg_score and label_ranking_average_precision_score are multilabel-classification metrics. Neither has any notion of a relevant document that was never retrieved, so both had to be talked into retrieval semantics at the boundary: nDCG by padding the positions the ranker left empty, average precision by rescaling LRAP by |relevant retrieved| / |relevant| to undo its habit of dividing by the labels it was handed — without which, retrieving 1 of 4 relevant documents scored 1.0. Two hand-written semantic corrections wrapped around a mismatched engine, and a subtle error in either would have silently corrupted every number downstream. Both are deleted.

The adapter code was the tell, not the fix. Maturity was never the question — scikit-learn is impeccably mature and was still the wrong engine for this field. It stays in the assay extra, because metrics.py still uses it for the classification metrics it is actually right for.

How ir-measures is declared

Under the assay extra, not the base install:

[project.optional-dependencies]
assay = [
    "scikit-learn>=1.9",
    "scipy>=1.18",
    "numpy>=2.5",
    "ir-measures>=0.4.3",
]

That is correct and unchanged by this PR — it landed with #13. The base install stays the envelope only (pydantic, pydantic-settings, pynacl, rfc8785) so pip install avow and micropip in Pyodide never pull the scientific stack. ir-measures carries exactly one transitive dependency, pytrec-eval-terrier (the C++ binding to trec_eval itself), whose own deps are numpy + scipy — already pinned in the same extra.

Claim this PR touches

None directly — no code, no public API, no guard. It makes the version strings and the changelog tell the truth about what is on main. The guard that protects this change is the packaging parity test, and it is pre-existing and green.

Evidence

Claim Proof
Python gate passes uv run poe gateexit 0; ruff → ruff-format → mypy --strict → xenon A → pytest
Test count + coverage 184 passed; 100% statement and branch coverage (803 statements, 68 branches, none missed) against a 90% floor
TypeScript gate passes cd ts && pnpm gateexit 0; 40 tests in 2 files (biome → tsc --noEmit → vitest → build)
The artifact actually carries the fix avow-0.3.0-py3-none-any.whlVersion: 0.3.0, Requires-Dist: ir-measures>=0.4.3; extra == 'assay', and assay/ranking.py present in the wheel
Base install not widened wheel Requires-Dist outside the extras is still pydantic / pydantic-settings / pynacl / rfc8785 only

The wheel metadata is the load-bearing one — it is precisely what PyPI avow 0.2.0 lacks and what makes the consumer's audit work again.

Not in this PR — human gates

No tag and no GitHub release. git tag v0.3.0 && git push origin v0.3.0 is what fires publish.yml.

After merge, and remembering that a green publish job is not evidence:

  1. confirm the registries actually serve it (pip index versions avow, npm view @edgeproc/avow@0.3.0), not the workflow conclusion;
  2. inspect npm view @edgeproc/avow@0.3.0 dist.attestations --json for an actual value — it prints empty and exits 0 when provenance is missing, so an exit-code check proves nothing;
  3. then move the consumer off the git-SHA pin to avow[assay]>=0.3.0 and confirm its audit stops skipping the package.

🤖 Generated with Claude Code

https://claude.ai/code/session_015o7tjWLFZvzRv4KyNfDukx

`assay.ranking` merged in #13 after the `v0.2.0` tag, so it ships in nothing.
PyPI `avow` 0.2.0 has neither the module nor `ir-measures` in its metadata.

**Why this is urgent, not a chore.** The first consumer had to pin `avow` to
the git merge commit `1c382ce` by full SHA to reach the module. A URL
requirement cannot be resolved to a version, so that consumer's dependency
audit now skips `avow` entirely — "URL requirements cannot be pinned to a
specific package version". A CVE against `avow` or its transitive deps would
go unseen for as long as that pin stands. Cutting the release is what closes
it: the consumer moves back to `avow[assay]>=0.3.0` and its audit sees the
package again.

**Why 0.3.0 and not 0.2.1.** Two independent reasons, either sufficient:
the unreleased set adds a new public face (`assay.ranking`,
`assay.ranking_score`) plus a new runtime dependency, and it carries a
BREAKING rename (`ReplayMismatch` -> `PayloadHashMismatch`,
`avow.replay_mismatch` -> `avow.payload_hash_mismatch`). This repo bumps the
minor for breaking changes in 0.x — that is what 0.2.0 was. The repo had also
already committed to the number in prose: `README.md` and `ts/README.md` both
say the rename "ships in 0.3.0".

**Claim this PR touches:** none directly — no code, no public API, no guard.
It makes the version strings and the changelog tell the truth about what is
on `main`.

**Version lives in three places, all bumped:**
- `src/avow/_version.py` — hatchling's single source, read at build time
- `ts/package.json` — `@edgeproc/avow`, pair-versioned; a `v*` tag publishes
  BOTH registries and npm rejects a re-publish at an unchanged version
- `CHANGELOG.md` — `[Unreleased]` -> `[0.3.0] - 2026-08-03`

`tests/test_packaging.py::test_python_and_typescript_packages_carry_the_same_version`
is the guard that makes the npm half non-optional: bumping `_version.py`
alone turns the gate red.

**Changelog correction.** The `[Unreleased]` entry claimed the ranking
arithmetic was scikit-learn's. It is not, and has not been since the engine
swap: it is `trec_eval`'s, reached through `ir_measures` — the reference
implementation the IR field validates its own numbers against. The entry now
records why, because the reasoning is the reusable part: `ndcg_score` and
`label_ranking_average_precision_score` are multilabel-CLASSIFICATION metrics
with no notion of a relevant document that was never retrieved, so both had
to be talked into retrieval semantics by hand — nDCG by padding empty
positions, average precision by rescaling LRAP by
|relevant retrieved| / |relevant|, without which retrieving 1 of 4 relevant
documents scored 1.0. Two hand-written semantic corrections around a
mismatched engine, both now deleted. The adapter code was the tell, not the
fix. Maturity was never the question.

**Evidence:**
- `uv run poe gate` -> exit 0; 184 passed; 100% statement AND branch coverage
  (803 stmts, 68 branches, none missed) against a 90% floor
- `cd ts && pnpm gate` -> exit 0; 40 tests, 2 files (biome, tsc, vitest, build)
- Built artifact, which is the actual claim: `avow-0.3.0-py3-none-any.whl`
  carries `Version: 0.3.0`, `Requires-Dist: ir-measures>=0.4.3; extra ==
  'assay'`, and `assay/ranking.py`. Base install is unchanged — still the
  envelope only, so `pip install avow` and micropip in Pyodide do not pull
  the scientific stack.

**Not in this PR (human gates):** no tag, no GitHub release. `git tag v0.3.0
&& git push origin v0.3.0` is what fires `publish.yml`. A green publish job
is not evidence — verify the registries actually serve it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015o7tjWLFZvzRv4KyNfDukx
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@hseshadr
hseshadr merged commit 3c3c89f into main Aug 3, 2026
4 checks passed
@hseshadr
hseshadr deleted the release/cut-0.3.0 branch August 3, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant