v0.23.0 — The release that reached nobody, and the toolchain consolidation
v0.23.0 — The release that reached nobody, and the toolchain consolidation
Release date: 2026-08-07
v0.x.0 bump carrying full MAJOR paperwork under the newly-added
docs/RELEASING.md §2.1.
Read this first if you skip everything else
v0.22.0 did not reach anyone.
Every adopter-facing copier copy example omitted --vcs-ref. Copier
resolves an unpinned git source to the highest-sorting tag, and this
repo carries the frozen v1.0.0–v1.12.0 audit snapshots (ADR-014)
alongside the active v0.x line:
$ git tag --sort=-v:refname | head -1
v1.12.0
v1.12.0 sorts above every v0.x tag. The documented command served the
April 2026 snapshot:
| Command | Result |
|---|---|
copier copy <url> Svc |
435 files, no .copier-answers.yml |
copier copy --vcs-ref=v0.22.0 <url> Svc |
626 files, correct answers file |
Nothing errored. The scaffold was complete and plausible — just three
months stale. Which is why it survived the entire life of the v0.x line.
Use --vcs-ref=v0.23.0. The docs now say so in all four places, and
scripts/check_adopter_scaffold_ref.py fails the build if they ever drift
from VERSION again.
The v1.x tags were not deleted or renamed. agentic/rules/18 and
ADR-014 both declare them immutable; changing that is a governance
decision needing its own ADR, not a side effect of a docs fix. The pin is
correct under every long-term option, so it lands now regardless.
Breaking for adopters
See MIGRATION.md §"v0.22.0 → v0.23.0" for the actions.
| Change | Why it breaks |
|---|---|
black/isort/flake8 hooks removed, ruff-check/ruff-format added |
§1.3 classifies a pre-commit hook-set change as MAJOR |
ruff format ≠ black output |
one-time reflow of your Python tree |
scaffold command requires --vcs-ref |
without it you get a v1.x snapshot |
A governance gap this release forced open
§1.3 says a pre-commit hook change requires MAJOR. §2 reserves v1.0.0
for cloud E2E evidence. So a MAJOR-class change had nowhere to go —
the policy simply did not cover it.
New §2.1: on the v0.x channel a §1.3 MAJOR-class change ships as
v0.x.0 but carries the full MAJOR paperwork — a Breaking for adopters
block plus a MIGRATION.md section. The bump number is smaller; the
obligations are identical. Nothing about being pre-GA reduces what an
adopter is owed when a contract breaks.
Ruff consolidation (ADR-044)
The speed argument did not survive measurement and is not claimed. The
previous suite already ran --all-files in 2.62 s, inside the config
header's own < 5 s target.
What actually justified it:
- Six copies of one exclude list — three tools × two pre-commit
configs, plus[tool.black]/[tool.isort]in two pyprojects. Same
defect class as the gitleaks drift closed inv0.22.0: one truth,
several declarations, silent divergence. - A lint coverage hole.
flake8'sfiles:was
^(templates/service/|examples/).scripts/andtemplates/tests/
were type-checked and security-linted but never style-linted.
That hole was hiding a real defect: test_different_cache_keys_isolated
created ctx1, never read it, and asserted only on ctx2. The test
named for an isolation property never asserted it — it would have passed
with caching absent entirely. Fixed by adding the missing assertion, not
by deleting the variable as the linter suggested.
Scope is deliberately parity-only (E,W,F,I). Ruff's UP/B/S
rulesets measured at 90 additional findings and are not enabled:
mixing them in would turn a toolchain swap into a code change, and a
reviewer could not tell which line moved for which reason. Ruff does not
replace mypy and only partially overlaps bandit; both retained.
The 55-file formatter reflow is isolated in its own commit and registered
in .git-blame-ignore-revs. Equivalence verified by running the
collectible test suite before and after and confirming an identical result.
Generated services no longer carry unresolvable ADR references
The render root cites 39 template ADRs and vendors 6; the other 33
were dangling, and a consuming repo's reference checker flagged them.
Renaming to template-ADR-NNN is structurally blocked:
check_vendored_runtime_drift.py holds templates/service/agentic, the
shipped ADR files, and the config schemas byte-identical to their root
counterparts. Rewriting identifiers would break that gate or fork the
generated service from upstream — making every future copier update a
conflict. A pointer costs nothing; a fork costs every future update.
Shipped a resolution layer instead:
templates/service/docs/decisions/README.md, enforced by
scripts/check_service_adr_references.py.
Known follow-ons
- Clock-isolation allowlist is keyed by
file:lineand any reformat
invalidates it. It did; entries were remapped 1:1 after verifying same
call count, same APIs, same order. Keying on the enclosing test name
would survive that churn. - Ruff
UP/B/S— 90 findings, own ADR. - mypy → pyright — considered in ADR-044, not ruled out.
- Shadow-lane precision data — the ADR-019 lane fires now but has
classified nothing. Phase 2 still needs 14 days of real data.
Verification
All contract tests green on main at the tagged commit. Execution
evidence in VALIDATION_LOG.md Entry 017.