reckon build — CI static site generation
Portable static delivery is shipped: the
reckon-plans wheel carries canonical assets, the repository workflow
validates without publishing, and distributed project resources replace the
monolithic index as the canonical write store.
§ 1 — Problem
The reckon server (v8+) serves UI assets via canonical routes:
/_ui/<file> and /_shared/<file>. Generated
index.html uses these absolute paths. This works perfectly with the live server
but breaks for static deployment — GitHub Pages has no reckon process to serve those routes.
Additionally, sprint and milestone definitions currently require authoring index.json
by hand. This is the last piece of wiring that plans do not have: plans auto-discover from
HTML meta tags; sprints and milestones do not.
§ 2 — reckon build command
Packaged all 16 UI and 4 shared assets inside the reckon-plans wheel
and made reckon build fail loudly when assets are missing. Installed-wheel
smoke generation produced relative paths, .nojekyll and usable static state.
Full record: §2–§6 landed
(commits e827e56, 948b2c5).
§ 3 — Sprint and milestone discovery from HTML
Verified dedicated sprint/milestone HTML discovery and excluded definition pages from plan inventory;
static builds preserve authored sprint, milestone, timeline and blocker state. Distributed
semantic resources now own canonical writes, while the frozen legacy index is retained only
for compatibility and rollback. The dissent review rejected reopening because the original
index-json-fate → deprecate entirely lock is now realized.
Full record: passing result.
§ 4 — CI YAML template
Replaced the wrong-package pip workflow with uv-native generation:
consumer workflows use uvx --from pinned to v0.2.0rc25,
while Reckon’s own Pages workflow builds its checked-out SHA with
uv run --frozen. Full record:
CI evidence.
§ 5 — reckon as a versioned package
Renamed only the distribution to reckon-plans, retaining the
reckon import and console command. The verified wheel contains 35 entries,
including every SPA asset; tag v0.2.0rc25 pins the consumer workflow.
Publishing to PyPI remains deliberately outside this sprint.
Full record: package evidence.
§ 6 — Implementation review & recommendations (2026-05-27, Opus)
6 of 6 actions landed Distribution naming, installed-wheel assets, uv CI, build tests, release tagging, and distributed project state are complete. The historical analysis below is retained as the record that motivated the package and storage corrections. Final evidence.
Reviewed the shipped code against this plan. The build command, sprint/milestone
HTML discovery, and the sync --generate-ci flag all landed. But the CI install
story (§4 + §5) is not deployable as written — three blockers, detailed below. The driving
fix is the user's instinct: CI should pull reckon in as a uv-managed dependency, not
pip install reckon.
§ 6.1 — Section-by-section status
| § | Item | State | Evidence / gap |
|---|---|---|---|
| §2 | reckon build command | ✅ landed (from source) | cli.py build(); smoke test copies 12 _ui/ files + _shared/, writes relative-path index.html, drops .nojekyll, bakes index.json inventory. Only works from a source checkout (see §6.2-B). |
| §3 | Sprint/milestone discovery | ⚠️ coded, unexercised | discover_plans() scans docs/sprints/*.html + docs/milestones/*.html (serve.py). No such files exist in any repo yet, so the path has never run with real input. |
| §3 | Eliminate index.json | ❌ contradicts code | Locked decision index-json-fate = "deprecate entirely", but both sync and build still write index.json, and discover_plans() still falls back to it. Decision is not realised. |
| §4 | CI YAML template | ⚠️ landed but broken | sync --generate-ci writes .github/workflows/reckon-pages.yml, but it uses setup-python + pip install reckon — wrong package and wrong tool (see §6.2). reckon's own repo has no committed workflow to deploy its plans. |
| §5 | Versioned PyPI package | ❌ blocked | Name reckon is taken on PyPI by an unrelated caching library; 0 git tags, so hatch-vcs yields 0.1.devNN; no release workflow. pip install reckon==1.2.3 is impossible as written. |
| — | Tests | ❌ missing | tests/ holds only MCP tests; no tests/test_build.py. |
§ 6.2 — The three blockers
A. PyPI name collision (§4, §5). https://pypi.org/project/reckon/ resolves to
"reckon: Dead simple, dynamic caching" by Sean Stewart — not this project. Any CI that runs
pip install reckon installs that package, and reckon build is simply not found.
The distribution must be renamed to an available name (e.g. reckon-plans) while keeping the
reckon import package and reckon console-script unchanged.
B. The wheel does not ship the UI assets (§2, §4). build copies assets from
Path(__file__).parent.parent / "docs" / {ui,_shared}, but the wheel
([tool.hatch.build.targets.wheel] packages = ["reckon"]) contains only reckon/*.py
— verified: 11 entries, no docs/. So from an installed wheel the if ui_src.is_dir()
guard is false and asset copying is silently skipped — producing a broken static site with
no error. build only works today when run from a git checkout (where docs/ui/ exists
beside the package). Fix: ship the assets in the wheel via hatch force-include, or move them under
reckon/_assets/ and resolve with importlib.resources.
C. CI is pip-based, not uv-managed (§4) — the user's ask. Even after A+B, the workflow
should use uv for reproducibility and to match how reckon is run everywhere else on this host
(uv run --project ~/Code/reckon reckon …).
§ 6.3 — Recommendation: bring reckon in as a uv-managed CI dependency
Variant 1 — works today, no packaging changes (source checkout via uv). Because the wheel
omits assets (blocker B), the only correct install is from source. Check out reckon as a second repo and
run it through uv from that tree — pin with ref: for reproducibility:
name: Deploy plans to GitHub Pages
on: { push: { branches: [main] }, workflow_dispatch: {} }
permissions: { contents: read, pages: write, id-token: write }
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # the project whose docs/ we publish
- uses: actions/checkout@v4 # reckon itself (ships docs/ui + docs/_shared)
with: { repository: Simon-McIntosh/reckon, ref: v0.2.0, path: .reckon-src }
- uses: astral-sh/setup-uv@v6
- run: uv run --project .reckon-src reckon build "$GITHUB_WORKSPACE/docs"
- uses: actions/upload-pages-artifact@v3
with: { path: docs/ }
deploy:
needs: build
runs-on: ubuntu-latest
environment: { name: github-pages, url: "${{ steps.deployment.outputs.page_url }}" }
steps:
- uses: actions/deploy-pages@v4
id: deployment
Variant 2 — after blockers A + B are fixed (packaged + published). Once assets ship in the wheel and the dist is renamed, the install collapses to one uv line, pinned to a published version:
- uses: astral-sh/setup-uv@v6
- run: uvx --from "reckon-plans==0.2.0" reckon build docs/
# or, git-pinned without a PyPI release:
# uvx --from "git+https://github.com/Simon-McIntosh/reckon@v0.2.0" reckon build docs/
A consuming repo that already uses uv can instead declare reckon in its own
pyproject.toml under [tool.uv.sources] (git + ref) and call
uv run reckon build docs/ — same lockfile-pinned reproducibility, no extra checkout step.
§ 6.4 — Recommended next steps (queued as the followup below)
- Open a
dist-namedecision and rename the distribution off the squattedreckonname. - Ship UI assets in the wheel (hatch
force-includeorreckon/_assets/+importlib.resources); add a wheel-install smoke test. - Rewrite the generated CI template to uv (Variant 1 now; Variant 2 after 1+2), and commit a copy for reckon's own Pages deploy.
- Add
tests/test_build.pycovering asset copy, relative paths,.nojekyll, inventory. - Reconcile
index-json-fate: realise "deprecate entirely" or reopen the decision via the dissent flow — today it is locked but unimplemented. - Cut the first git tag so hatch-vcs emits a real version.