Release-cut CI check: verify client/package.json version matches git tag #157

Closed
opened 2026-06-24 02:56:59 +02:00 by bosun · 1 comment
Owner

Background

PR #156 (#147 version build-stamp) opportunistically catches a substrate-of-record gap: client/package.json version was never bumped from 0.0.1 → 1.0.0 at the v1.0.0 release-cut. Tag + Forgejo release marked v1.0.0; manifest stayed 0.0.1; mismatch.

Shipwright flagged in PR #156 body: "optional release-CI-check follow-up offered."

Severity: LOW substrate-of-record discipline

Doesn't affect runtime; affects substrate-of-record-honesty (manifest version should match git tag + Forgejo release version).

Proposed scope

CI check that verifies on a release tag:

  1. Most recent git tag (vX.Y.Z) matches client/package.json version
  2. Fail CI if mismatch

Triggers on tag-push events or release-creation events.

Acceptance criteria

  1. CI job runs on tag push (e.g. git push origin v* or release-create event)
  2. Reads client/package.json version field
  3. Reads tag name (vX.Y.Z → X.Y.Z)
  4. Asserts equality; fails CI loudly if mismatch
  5. Documented in release-cut procedure (CONTRIBUTING.md or BookStack release-cut runbook)

Fix-direction

Small CI script in client/.forgejo/workflows/release-check.yml (or similar). Probably ~20 lines.

Cross-refs

Anchor

2026-06-24 02:55 — Shipwright surfaced during #147 build-stamp injection: client/package.json version 0.0.1 vs git tag v1.0.0 mismatch detected and corrected in PR #156. Filed per substrate-for-decision discipline (durable-tracker-home for the gap before it evaporates).

## Background PR #156 (#147 version build-stamp) opportunistically catches a substrate-of-record gap: client/package.json `version` was never bumped from 0.0.1 → 1.0.0 at the v1.0.0 release-cut. Tag + Forgejo release marked v1.0.0; manifest stayed 0.0.1; mismatch. Shipwright flagged in PR #156 body: "optional release-CI-check follow-up offered." ## Severity: LOW substrate-of-record discipline Doesn't affect runtime; affects substrate-of-record-honesty (manifest version should match git tag + Forgejo release version). ## Proposed scope CI check that verifies on a release tag: 1. Most recent git tag (vX.Y.Z) matches client/package.json `version` 2. Fail CI if mismatch Triggers on tag-push events or release-creation events. ## Acceptance criteria 1. CI job runs on tag push (e.g. `git push origin v*` or release-create event) 2. Reads client/package.json `version` field 3. Reads tag name (vX.Y.Z → X.Y.Z) 4. Asserts equality; fails CI loudly if mismatch 5. Documented in release-cut procedure (CONTRIBUTING.md or BookStack release-cut runbook) ## Fix-direction Small CI script in client/.forgejo/workflows/release-check.yml (or similar). Probably ~20 lines. ## Cross-refs - cellblock#147 (this PR's substrate-fix that surfaced the gap) - cellblock release v1.0.0 (https://git.frankenbit.de/frankenbit/cellblock/releases/tag/v1.0.0; tagged 2026-06-23; manifest mismatch not caught) - BookStack Game Testing book / cellblock playtest plan (substrate-of-record references version) ## Anchor 2026-06-24 02:55 — Shipwright surfaced during #147 build-stamp injection: client/package.json version 0.0.1 vs git tag v1.0.0 mismatch detected and corrected in PR #156. Filed per substrate-for-decision discipline (durable-tracker-home for the gap before it evaporates).

Closing — satisfied by release-toolkit adoption (#168 → v1.1.0 cut 2026-06-26)

Closing as done, per operator confirmation 2026-06-26. The bug class this tracker wanted to catch — manual-cut tag-vs-client/package.json version drift — can no longer fire via cellblock's normal release flow.

What changed

PR #168 adopted frankenbit/release-toolkit. Cellblock's first cut via the toolkit (v1.1.0, published 2026-06-26 22:29) ran end-to-end:

  • release-prep.sh updates client/package.json in the rolling PR's prep commit (chore(release): prepare vX.Y.Z) to the next version
  • manifest-check.yml validates version_files ↔ manifest ↔ CHANGELOG on every PR
  • draft-release.sh creates the tag at the rolling PR's merge SHA, which already has client/package.json at the correct version
  • → tag and client/package.json are guaranteed to match by construction at cut time

How this differs from the original AC

The original AC #1 called for "CI job runs on tag push" — a defense-in-depth check that catches manual cuts. The toolkit's approach is structurally stronger: it makes the manual-cut path the exception rather than the default, by orchestrating tag creation through the rolling-PR mechanism.

The bug PR #156 opportunistically caught (manifest stayed 0.0.1 while tag was 1.0.0) was a manual-cut artifact. With the toolkit driving cuts, this exact mismatch can't happen on the normal flow.

Remaining defense-in-depth gap (not addressed by this closure)

If someone bypasses the toolkit (e.g., git tag v1.2.0 && git push --tags outside the workflow), there's no tag-push-event check to catch the bypass. The toolkit's manifest-check.yml runs on PR events, not on tag-push events. This is a separate scope; not opening a follow-up tracker per operator's preference unless the gap surfaces empirically.

Refs

  • Adoption PR: #168 (closed #162 + this issue)
  • First cut: v1.1.0 2026-06-26 22:29, path-γ (rolling PR + manifest follow-up)
  • Original surfacing: PR #156 (#147 version build-stamp work)
## Closing — satisfied by release-toolkit adoption (#168 → v1.1.0 cut 2026-06-26) Closing as **done**, per operator confirmation 2026-06-26. The bug class this tracker wanted to catch — manual-cut tag-vs-`client/package.json` version drift — can no longer fire via cellblock's normal release flow. ### What changed [PR #168](https://git.frankenbit.de/frankenbit/cellblock/pulls/168) adopted [frankenbit/release-toolkit](https://git.frankenbit.de/frankenbit/release-toolkit). Cellblock's first cut via the toolkit (v1.1.0, published 2026-06-26 22:29) ran end-to-end: - `release-prep.sh` updates `client/package.json` in the rolling PR's prep commit (`chore(release): prepare vX.Y.Z`) to the next version - `manifest-check.yml` validates `version_files` ↔ manifest ↔ CHANGELOG on every PR - `draft-release.sh` creates the tag at the rolling PR's merge SHA, which already has `client/package.json` at the correct version - → tag and `client/package.json` are guaranteed to match **by construction** at cut time ### How this differs from the original AC The original AC #1 called for "CI job runs on tag push" — a defense-in-depth check that catches manual cuts. The toolkit's approach is **structurally stronger**: it makes the manual-cut path the exception rather than the default, by orchestrating tag creation through the rolling-PR mechanism. The bug PR #156 opportunistically caught (manifest stayed 0.0.1 while tag was 1.0.0) was a manual-cut artifact. With the toolkit driving cuts, this exact mismatch can't happen on the normal flow. ### Remaining defense-in-depth gap (not addressed by this closure) If someone bypasses the toolkit (e.g., `git tag v1.2.0 && git push --tags` outside the workflow), there's no tag-push-event check to catch the bypass. The toolkit's `manifest-check.yml` runs on PR events, not on tag-push events. This is a separate scope; not opening a follow-up tracker per operator's preference unless the gap surfaces empirically. ### Refs - **Adoption PR**: [#168](https://git.frankenbit.de/frankenbit/cellblock/pulls/168) (closed [#162](https://git.frankenbit.de/frankenbit/cellblock/issues/162) + this issue) - **First cut**: [v1.1.0](https://git.frankenbit.de/frankenbit/cellblock/releases/tag/v1.1.0) 2026-06-26 22:29, path-γ (rolling PR + manifest follow-up) - **Original surfacing**: [PR #156](https://git.frankenbit.de/frankenbit/cellblock/pulls/156) (#147 version build-stamp work)
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/cellblock#157
No description provided.