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.
| Measure | Before | After |
|---|---|---|
Project-URL entries | 0 | 4 — Homepage, Documentation, Issues, Source |
| Classifiers | 1 (license only) | 7 — incl. Development Status :: 4 - Beta, Intended Audience :: Developers, Topic :: Software Development :: Libraries :: Python Modules, Programming Language :: Python :: 3.12 |
| Focused test | — | 1 passed, 0 failed |
| Artifacts | — | wheel 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.
| Revision | Passed | Failed | Errors |
|---|---|---|---|
Pre-wave a453673 | 1937 | 34 | 10 |
Integrated 5e193b6 | 1944 | 35 | 10 |
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.