Skip to content

📦 WP-140 — release: v0.5.0 keypoint detection

Engineering log · Phase 12 — Keypoint detection, v0.5.0

🔗 Depends on WP-126, WP-139 · Needed by WP-140b

⬅️ WP-139 · Roadmap · WP-140b ➡️

WP-140 — a release is a boundary, not an achievement

0.5.0 closes the keypoint phase. __version__ moves from 0.5.0.dev2 to a plain 0.5.0, which is the whole of the version change — pyproject.toml declares the version dynamic and reads the module attribute — and the ## [Unreleased] section WP-133 opened closes as ## [0.5.0], on the schedule its own opening paragraph wrote down.

The frozen goldens are the release's regression contract, and this one is a set larger than the last. make freeze-goldens MINOR=0.5 copies the eight live metric sets into goldens/frozen/0.5/, where 0.4.0 froze seven; the extra set is WP-139's keypoint size golden, which did not exist a commit earlier. That is the difference between freezing what happens to be lying around and freezing what the release actually claims, and it is only a difference because the missing golden was found before the freeze rather than after it.

What this release does not claim. The keypoint tier's acceptance is a direction of effect against a control — RLE beat WP-135's Laplace-NLL arm at e2e OKS AP 0.2738 against 0.2527 on real COCO human-pose data, the same sign as R14 Table 7's own 70.5-against-67.4 — and it is not a pose figure comparable to anything published. R14's own numbers come from a different backbone, a different schedule and a different scale; this project's smoke tier is fifty epochs at n. Three keypoint assumptions stay open and each names an experiment nobody has run: A68's tuned gain adequacy (the mechanism-failure bar cleared, no sweep run), A69's one shared flow against one per branch (untested against the alternative, and the overfit gate structurally cannot tell), A70's off-canvas point policy (exercised live in both arms, its frequency and effect unmeasured). The oriented figures are unchanged from 0.4.0's caveat only in that WP-111 has since measured a whole-image number; every other limit that release recorded still stands, and no trained weights ship (D14).

The fourth task trains, validates and evaluates; it does not yet deploy. tests/models/test_onnx_export.py still runs three tasks, and predict.py still ships three functions. That is parity work for a later phase, named here rather than left for a reader to infer from an absence.

Two CI failures the offline gate structurally cannot see, both found on the release's own run and folded in here. The first is a four-character bug with a document-wide blast radius: docs/ENGINEERING_LOG.md and docs/ROADMAP.md both wrote the placeholder test_ followed by an angle-bracketed script, and Python-Markdown's HTML block pass runs before inline code spans are recognized, so the backticks protect nothing — an angle-bracketed script opens a raw-text element that swallows the rest of the document, and every <a id="wp-NNN"></a> anchor after it vanishes from the element tree mkdocs build --strict reads. Eighteen roadmap links broke at once, every one of them from the WP-130 paragraph onward, and the anchors were all still present in the rendered HTML — the post-processing step restores stashed raw HTML, so the page looked correct while the validator that never sees that step was right to fail. Renamed to test_<script_name>.py, which is not an HTML tag. The second is scripts/lint/audit_docs_site.py's # type: ignore[no-untyped-call] on add_multi_constructor: whether that call is typed depends on the resolved types-PyYAML, which the lockfile does not pin, so the ignore is load-bearing locally and dead on CI's newer stubs, where warn_unused_ignores then fails the run. unused-ignore now rides along in the same bracket, correct under either version without pinning a stub package for one comment.

The general shape is worth keeping: make gate is an offline gate and does not build the docs site, so a docs-site failure is invisible until CI, and a lint that depends on an unpinned transitive stub version is invisible until CI resolves a different one. Neither is a gate to add — the docs build is genuinely CI's job and pinning stubs to satisfy one comment is worse than the comment — but both are reasons the gate being green is not the same claim as the branch being green.

No tag is cut here and no distribution is published, as at 0.4.0. release.yml fires on a pushed v0.* tag and re-runs the guard, which refuses a tag whose changelog section is missing or whose gate is red; both are the operator's to trigger. v0.4.0 is also still uncut, so the train has now shipped two release commits ahead of its own tags — a gap worth stating plainly, since a reader who looks for v0.5.0 will find neither it nor its predecessor.