Prior-art reconnaissance reshaped the wave before it opened

A read-only scout ran ahead of any implementation node and returned a ten-row reuse map covering four capability areas, each row citing a concrete path:line with a fitness verdict, backed by sixteen counted search commands. It changed the wave's shape in one decisive way and is recorded here because the correction is more valuable than the map.

The third node as originally cut was redundant. The plan's §3 treats "the wheel ships its UI assets rather than reaching into a source tree" as the acceptance that matters, implying it needed building. It was already built and already tested: pyproject.toml force-includes docs/ui and docs/_shared into reckon/_assets, reckon/cli.py:14 resolves _assets ahead of a source docs/, and tests/test_build.py:427 already installs the built wheel outside the checkout and runs the console script against a scratch docs tree.

What was genuinely missing was the other half of the same acceptance. reckon/__init__.py looked up version("reckon") while the distribution is named reckon-plans, so an installed wheel silently reported the "dev" fallback — and the CLI exposed no --version at all. The plan's stated done-when, that the installed reckon --version match the git tag exactly, could not have passed at any point. The node was re-cut against that defect.

Scout run r-20260824T091136734891-prior-art-scout, 474 s, gate passed, read-only sandbox, no repository file changed.

§2 — Distribution metadata

Commit dff9e38. pyproject.toml gained a [project.urls] table and a real classifier set, with tests/test_packaging_metadata.py asserting the properties against the built wheel's METADATA rather than against the source file — so the test fails if packaging drops them in transit.

MeasureBeforeAfter
Project-URL entries04 — Homepage, Documentation, Issues, Source
Classifiers1 (license only)7 — incl. Development Status :: 4 - Beta, Intended Audience :: Developers, Topic :: Software Development :: Libraries :: Python Modules, Programming Language :: Python :: 3.12
Focused test1 passed, 0 failed
Artifactswheel 486,535 B and sdist 1,530,480 B both built

§3 — Trusted publishing, and the version the release must assert

Two nodes landed against this section. They are recorded together because neither is sufficient alone: a workflow that publishes correctly is worthless if the artifact it publishes reports the wrong version.

The release workflow

Commit 6616c6d. .github/workflows/release.yml carries three jobs, an exact semantic-version tag filter at line 5, full VCS history at line 18 — the fetch-depth: 0 that the plan names as the trap that fails late — a wheel and sdist build at line 22, TestPyPI with its own OIDC environment at lines 29–48, and a PyPI job at lines 50–66 that depends on the TestPyPI publication succeeding. The workflow references no password, API token, or secret anywhere: a leaked credential cannot leak if it does not exist.

tests/test_release_workflow.py asserts those properties structurally — 6 passed, 0 failed. The tests parse the YAML rather than pattern-matching text, so the contract survives reformatting.

Version provenance

Commit b9732eb. The metadata lookup now names the actual reckon-plans distribution and narrows its exception handling to PackageNotFoundError, so a genuine packaging failure is no longer swallowed into a "dev" string. reckon --version is exposed through Click's root version option. The existing installed-wheel fixture now invokes both --version and importlib.metadata.version from its isolated wheel target with a working directory outside the checkout, and asserts exact equality.

Measured: the shared distribution, the imported module and the CLI all reported the same version string, and the scratch-installed wheel's CLI equalled that wheel's own reckon-plans metadata from outside the checkout. tests/test_build.py 16 passed, 0 failed in 4.82 s; ruff check clean on all three touched files.

Regression verdict: nothing the wave touched broke

The first suite run returned 1942 passed, 35 failed, 10 errors — and the worker reported honestly that this was not a clean product verdict, because all 45 non-passing outcomes traced to sandbox restrictions: 31 attempted envelope locks under a read-only config home, 3 failures and 7 errors attempted loopback sockets, and 4 invoked offline builds against an empty cache.

An absolute count could not settle it, so the question was changed to a difference. The same suite was run once at the pre-wave base and once at the integrated revision, bypassing uv entirely — the shared cache is mounted read-only in the worker sandbox, which kills uv run before pytest collects — and invoking the repository interpreter directly instead.

RevisionPassedFailedErrors
Pre-wave a45367319373410
Integrated 5e193b619443510

All 34 pre-wave failing ids still fail, all 10 pre-wave error ids still error, and none recovered. Exactly one non-passing id is new: tests/test_packaging_metadata.py::test_built_wheel_carries_discovery_metadata — a test the wave itself added, whose traceback shows its internal uv build failing to initialise the read-only shared cache, not an assertion mismatch. Its authoring worker ran it green with a working cache. The +7 passed are the wave's other new tests.

So the measured delta is 1 and its cause is the sandbox, not the change. No existing test changed state in either direction.

A rehearsal that cannot reach PyPI

Commit 026c8cd. Reading the merged workflow surfaced a gap against §3's own deliverable: it asks for a TestPyPI dry run before the first real upload, but the workflow triggered only on a version tag, and its TestPyPI and PyPI jobs are chained — so there was no way to exercise TestPyPI alone, and the only rehearsal available was spending the real version number.

workflow_dispatch now takes a testpypi-only input defaulting to true, and the PyPI publish job cannot be reached while it is set. Two new tests assert the manual trigger exists and that a dry run cannot reach PyPI; the original 6 structural cases stay green, 8 passed 0 failed.

What remains, and why this session cannot finish it

The chain is complete up to the point where it leaves this machine. Registering the trusted publisher requires an authenticated session as the PyPI project owner — the release workflow's own manifest names this as fenced out of the worker node, and it is the one step §3 of the plan already predicted could not be automated from here. The first upload is additionally irreversible: a version number, once published, can be yanked but never reused.

The lead selected 0.2.0 as the first released version, following the existing v0.2.0rc24 / v0.2.0rc25 line to its release. Sub-1.0 matches an interface that the same sprint is about to change: a tag field and project-qualified sprint refs are in flight, and declaring 1.0 now would force 2.0.0 on landing them under the locked strict-semver contract.

Two actions remain, both the project owner's: register release.yml as the trusted-publisher workflow for the testpypi environment on TestPyPI and the pypi environment on PyPI, then push v0.2.0.