Skip to content

Commit 91634af

Browse files
committed
feat(v0.19.1)!: local-only release bulletproofing (skill flow + pk-doctor release_integrity)
Addresses the v0.19.0 post-mortem: a tag push does not create a GitHub Release. Rejected CI-workflow approach (DEC-MerryArch) on vendor-lock + cost grounds. Shipped local-only fix instead (DEC-SnowyWolf): Prevention — release-semver SKILL.md (both trees) collapsed into a single bulletproof 9-step flow. /pk-release now prepares + publishes + verifies in one turn; not complete until `gh release view vX.Y.Z` exits 0. /pk-publish retained as recovery alias. New Gotcha: "git push --tags is not a GitHub Release." Detection — pk-doctor gains a 6th check category `release_integrity`. Walks every local v* git tag, probes GitHub via `gh release view` for a matching Release, WARNs on missing. Degrades gracefully when gh is unavailable (INFO, not ERROR). Opt-out via env var. No GitHub Actions workflow shipped; the earlier .github/workflows/ direction from DEC-MerryArch was withdrawn. DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing supersedes DEC-20260422_0926-MerryArch-auto-create-github-releases. Closes BACK-20260422_0925-MightyOtter-v0-19-1-release. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 54c568a commit 91634af

17 files changed

Lines changed: 802 additions & 24 deletions

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,28 @@ Versions follow [Semantic Versioning](https://semver.org/).
55

66
---
77

8+
## [v0.19.1] — 2026-04-22
9+
10+
Local-only release bulletproofing — no CI workflows. Addresses the
11+
v0.19.0 post-mortem finding that a tag push does not create a GitHub
12+
Release. Supersedes `DEC-20260422_0926-MerryArch` (CI workflow
13+
approach, rejected on vendor-lock + cost grounds) with
14+
`DEC-20260422_1348-SnowyWolf` (skill flow + doctor detection).
15+
16+
### Added
17+
18+
- **feat(pk-doctor): 6th check category `release_integrity`** — walks every local `v*` git tag, probes GitHub via `gh release view` for a matching Release, and WARNs on any tag without one. INFO when `gh` is unavailable or the tag set is empty. Opt-out via `PK_DOCTOR_SKIP_RELEASE_INTEGRITY=1`; tag-scan cap via `PK_DOCTOR_RELEASE_INTEGRITY_MAX` (default 50). Each WARN carries a ready-to-paste `gh release create <TAG>` command with CHANGELOG extraction inlined. See `DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing`.
19+
20+
### Changed
21+
22+
- **chore(release-semver): collapse /pk-release into a single bulletproof flow.** `SKILL.md` (both trees) rewritten with a 9-step recipe that prepares, publishes, and **verifies** in one turn — the release is not considered complete until `gh release view vX.Y.Z` succeeds (step 8). `/pk-publish` retained as a recovery alias for historical tags that are missing a Release. New Gotcha: "`git push --tags` is not a GitHub Release." Closes `BACK-20260422_0925-MightyOtter-v0-19-1-release`.
23+
24+
### Fixed
25+
26+
- **fix(release flow): v0.19.0 tag was pushed without a corresponding GitHub Release** — the prepare phase of release-semver completed but the publish phase was skipped. v0.19.0 Release was created manually; v0.19.1 ships the prevention (collapsed flow) and detection (`release_integrity` check) mechanisms.
27+
28+
---
29+
830
## [v0.19.0] — 2026-04-22
931

1032
### Added (v0.19.0 architecture refactor)

aibox.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ synced_at = "2026-04-19T08:03:58.481219+00:00"
44

55
[processkit]
66
source = "https://github.com/projectious-work/processkit.git"
7-
version = "v0.19.0"
7+
version = "v0.19.1"
88
src_path = "src"
99
release_asset_sha256 = "3c6bf672e3a4ac2bf8c0b807c2e5fe948ff27906b6a0aa0ff73d36a1b2f7c98c"
1010
installed_at = "2026-04-19T08:03:58.481219+00:00"
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
apiVersion: processkit.projectious.work/v1
3+
kind: DecisionRecord
4+
metadata:
5+
id: DEC-20260422_0926-MerryArch-auto-create-github-releases
6+
created: '2026-04-22T09:26:42+00:00'
7+
updated: '2026-04-22T13:54:52+00:00'
8+
spec:
9+
title: Auto-create GitHub Releases via tag-push workflow; mirror to src/ for downstream
10+
projects
11+
state: superseded
12+
decision: 'Ship `.github/workflows/release.yml` at the processkit repo root that,
13+
on push of any `v*` tag, extracts the matching section from `CHANGELOG.md` and
14+
creates a GitHub Release via `softprops/action-gh-release@v2`. Mirror the same
15+
workflow into `src/.github/workflows/release.yml` so downstream projects installed
16+
through aibox inherit the automation. Tighten the `release-semver` SKILL.md''s
17+
Publish section with a concrete `gh release create` example and the shipped-workflow
18+
pattern, and add an explicit Gotcha: "a `git push --tags` is not a GitHub Release."
19+
Cut v0.19.1 to deliver this fix and dogfood the workflow.'
20+
context: 'During v0.19.0 release, `/pk-release` was invoked (prepare phase: bump
21+
+ CHANGELOG + tag + push) but `/pk-publish` was not invoked afterwards. The tag
22+
landed on GitHub but no Release entry existed — downstream consumers of `gh api`
23+
/ the Releases page / aibox sync would have missed the release. The fix required
24+
a manual `gh release create`. The miss is easy to repeat: a developer can stop
25+
after `/pk-release` without realising the publish step is separate.'
26+
rationale: '(1) **Automation over discipline.** A tag-push workflow removes the
27+
human step entirely: any future `v*` tag push reliably produces a Release with
28+
CHANGELOG-derived notes. (2) **Mirror to src/.** processkit is consumed by downstream
29+
projects via aibox; shipping the workflow under `src/.github/workflows/` means
30+
those projects inherit the automation without needing to copy it by hand. (3)
31+
**Doc tightening is secondary but cheap.** The SKILL.md currently says "Publish
32+
according to the project''s distribution channel" — too abstract. A concrete `gh
33+
release create` command, plus the CI-workflow snippet, makes the publish-phase
34+
expectation unambiguous for humans and agents. (4) **Dogfood the fix in the same
35+
release.** Cutting v0.19.1 with the workflow already committed means pushing the
36+
tag validates the end-to-end automation. Follow-up: the `decisionrecord.yaml`
37+
deciders[] pattern still requires `ACTOR-*` — that schema needs TEAMMEMBER-* support;
38+
filed separately.'
39+
alternatives:
40+
- option: 'Discipline-only: require /pk-publish after /pk-release; add a Gotcha
41+
but no CI'
42+
rejected_because: Relies on every future release-runner remembering the second
43+
step. Exactly the failure mode that caused v0.19.0's miss.
44+
- option: Merge /pk-release and /pk-publish into a single step
45+
rejected_because: Publishing is irreversible; keeping prepare and publish distinct
46+
preserves a reviewable checkpoint. Automating publish via CI is safer than short-circuiting
47+
the slash command.
48+
- option: Pk-doctor category that flags tags missing Releases
49+
rejected_because: Good complement but doesn't prevent the miss; we chose to prioritise
50+
prevention (CI) over detection (doctor). Doctor check can be added later as
51+
a second-line defence.
52+
- option: Use release-please or goreleaser instead of softprops/action-gh-release
53+
rejected_because: Those tools own the CHANGELOG/versioning workflow; processkit
54+
already has its own release-semver skill + manual CHANGELOG. softprops is a
55+
minimal single-purpose action that only does the GH Release step — matches our
56+
existing pipeline exactly.
57+
consequences: '`.github/workflows/` directory created at repo root (first workflow).
58+
Requires the repo to allow GitHub Actions (already enabled). `softprops/action-gh-release@v2`
59+
added as a transitive dependency (pinned to a major version). `src/.github/workflows/release.yml`
60+
shipped in aibox-synced template tree — downstream processkit projects will gain
61+
the workflow on next sync. Existing Releases (v0.19.0 and earlier) unchanged.
62+
A future tag-push that lacks a matching CHANGELOG section will get a fallback
63+
placeholder body; acceptable minor ugliness, not a blocker. v0.19.1 patch release
64+
required to ship the workflow. Separately flagged: the schema pattern for deciders[]
65+
/ assignee is still `ACTOR-*` and needs TEAMMEMBER-* support (not blocking this
66+
fix).'
67+
related_workitems:
68+
- BACK-20260422_0925-MightyOtter-v0-19-1-release
69+
decided_at: '2026-04-22T09:26:42+00:00'
70+
superseded_by: DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing
71+
---
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
apiVersion: processkit.projectious.work/v1
3+
kind: DecisionRecord
4+
metadata:
5+
id: DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing
6+
created: '2026-04-22T13:48:39+00:00'
7+
updated: '2026-04-22T13:54:52+00:00'
8+
spec:
9+
title: 'Local-only release bulletproofing: single-turn release-semver flow + pk-doctor
10+
release_integrity check (no CI workflows)'
11+
state: accepted
12+
decision: 'Supersede `DEC-20260422_0926-MerryArch` (which proposed a `.github/workflows/release.yml`
13+
tag-push workflow). Do **not** add GitHub Actions workflows — vendor-specific,
14+
metered, and against the owner''s cost stance. Instead, make the release process
15+
bulletproof through two strictly local mechanisms: (1) **Prevention** — collapse
16+
`/pk-release` into a single end-to-end flow that prepares *and* publishes in the
17+
same turn, with an explicit `gh release create` step and a mandatory verification
18+
(`gh release view`) before the command is considered complete. Remove the split
19+
between `/pk-release` and `/pk-publish` that made half-done releases possible.
20+
(2) **Detection** — add a 6th pk-doctor check category `release_integrity` that
21+
enumerates every local `v*` git tag, probes GitHub (via `gh release view`) for
22+
a matching Release, and emits a WARN for any tag without one. Gracefully INFO
23+
when `gh` is unavailable. Both mechanisms ship in v0.19.1, replacing the now-discarded
24+
CI workflow approach.'
25+
context: 'User rejected the CI-workflow approach on two grounds: vendor lock-in
26+
(GitHub Actions only helps GitHub-hosted projects) and cost (Actions minutes are
27+
metered on private or heavy-use accounts). Both arguments are durable — processkit
28+
should not assume a paid CI runtime. The original v0.19.0 miss was the agent stopping
29+
after prepare; the root cause is fixable entirely within the processkit layer
30+
(skill flow + doctor check) without reaching for CI.'
31+
rationale: (1) **Local-only stays with processkit's value proposition.** processkit
32+
is a file-based, provider-neutral toolkit; CI workflow files break that story.
33+
(2) **Single-turn /pk-release prevents the miss structurally.** If the slash command's
34+
documented flow ends with `gh release create` + verification, an agent that claims
35+
"done" without running those steps is violating the skill — caught by skill-reviewer
36+
or review. No human discipline required mid-release. (3) **pk-doctor provides
37+
the safety net.** Even if the skill is mis-invoked or skipped, `release_integrity`
38+
catches missing Releases on the next `/pk-doctor` run. Warnings, not errors —
39+
it flags the gap without blocking the repo. (4) **`gh release create` is free.**
40+
It's a single REST API call, runs locally, no metered runtime. (5) **Split prepare/publish
41+
is the wrong abstraction here.** It was useful when publishing meant pushing to
42+
crates.io / PyPI (multi-minute work that justified a checkpoint). For processkit's
43+
current reality — the release artifact is the GitHub Release page — the two phases
44+
collapse to one natural turn.
45+
alternatives:
46+
- option: Keep the CI workflow; user just learns to live with the cost
47+
rejected_because: User explicitly rejected on cost + vendor-lock grounds; durable
48+
objection.
49+
- option: Third-party non-GitHub CI (e.g., local runner, custom webhook)
50+
rejected_because: Adds more infrastructure than it removes; not provider-neutral
51+
either; violates simplicity.
52+
- option: Only add pk-doctor detection; don't change the skill flow
53+
rejected_because: Detection catches the miss after the fact; it doesn't prevent
54+
it. Pairing detection with prevention is stronger and both are cheap.
55+
- option: Only change the skill flow; skip the pk-doctor check
56+
rejected_because: No safety net if the skill is ever bypassed or a future agent
57+
mis-interprets the flow. Belt-plus-suspenders here is worth the ~60 LOC.
58+
- option: Pre-commit / pre-push git hook that blocks tag pushes without a prior
59+
Release
60+
rejected_because: git hooks are local-machine only and not version-controlled
61+
by default; unreliable across contributors.
62+
consequences: '- `.github/workflows/release.yml` and `src/.github/workflows/release.yml`
63+
will NOT ship. Local files removed from this session''s uncommitted state.\n-
64+
`release-semver/SKILL.md` (both trees) rewritten: single flow, mandatory verification
65+
step, updated Gotcha ("prepare-only is not a release; the flow only completes
66+
when `gh release view vX.Y.Z` succeeds").\n- `/pk-publish` slash command deprecated
67+
(kept as an alias for recovery scenarios: tag already pushed, Release missing).\n-
68+
pk-doctor gains a 6th check category `release_integrity`. Depends on `gh` CLI
69+
being installed and authenticated in the local environment — INFO (not ERROR)
70+
when unavailable.\n- v0.19.1 still ships this turn, now with the revised scope.
71+
The v0.19.1 Release itself will be created via the new single-flow command, dogfooding
72+
the prevention mechanism.\n- `DEC-20260422_0926-MerryArch` transitions to `superseded`
73+
with a pointer to this DEC.'
74+
related_workitems:
75+
- BACK-20260422_0925-MightyOtter-v0-19-1-release
76+
decided_at: '2026-04-22T13:48:39+00:00'
77+
supersedes: DEC-20260422_0926-MerryArch-auto-create-github-releases
78+
---
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
apiVersion: processkit.projectious.work/v1
3+
kind: LogEntry
4+
metadata:
5+
id: LOG-20260422_0925-StoutThorn-workitem-created
6+
created: '2026-04-22T09:25:57+00:00'
7+
spec:
8+
event_type: workitem.created
9+
timestamp: '2026-04-22T09:25:57+00:00'
10+
summary: 'Created WorkItem ''BACK-20260422_0925-MightyOtter-v0-19-1-release'': ''v0.19.1
11+
— release automation workflow + release-semver SKILL.md tightening'''
12+
subject: BACK-20260422_0925-MightyOtter-v0-19-1-release
13+
subject_kind: WorkItem
14+
actor: BACK-20260422_0925-MightyOtter-v0-19-1-release
15+
---
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
apiVersion: processkit.projectious.work/v1
3+
kind: LogEntry
4+
metadata:
5+
id: LOG-20260422_0926-SmartAnt-workitem-transitioned
6+
created: '2026-04-22T09:26:25+00:00'
7+
spec:
8+
event_type: workitem.transitioned
9+
timestamp: '2026-04-22T09:26:25+00:00'
10+
summary: Transitioned WorkItem 'BACK-20260422_0925-MightyOtter-v0-19-1-release'
11+
from 'backlog' to 'in-progress'
12+
subject: BACK-20260422_0925-MightyOtter-v0-19-1-release
13+
subject_kind: WorkItem
14+
actor: BACK-20260422_0925-MightyOtter-v0-19-1-release
15+
---
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
apiVersion: processkit.projectious.work/v1
3+
kind: LogEntry
4+
metadata:
5+
id: LOG-20260422_0926-SpryBird-decision-created
6+
created: '2026-04-22T09:26:42+00:00'
7+
spec:
8+
event_type: decision.created
9+
timestamp: '2026-04-22T09:26:42+00:00'
10+
summary: 'Created DecisionRecord ''DEC-20260422_0926-MerryArch-auto-create-github-releases'':
11+
''Auto-create GitHub Releases via tag-push workflow; mirror to src/ for downstream
12+
projects'''
13+
subject: DEC-20260422_0926-MerryArch-auto-create-github-releases
14+
subject_kind: DecisionRecord
15+
actor: DEC-20260422_0926-MerryArch-auto-create-github-releases
16+
---
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
apiVersion: processkit.projectious.work/v1
3+
kind: LogEntry
4+
metadata:
5+
id: LOG-20260422_1348-QuickFalcon-decision-created
6+
created: '2026-04-22T13:48:39+00:00'
7+
spec:
8+
event_type: decision.created
9+
timestamp: '2026-04-22T13:48:39+00:00'
10+
summary: 'Created DecisionRecord ''DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing'':
11+
''Local-only release bulletproofing: single-turn release-semver flow + pk-doctor
12+
release_integrity check (no CI workflows)'''
13+
subject: DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing
14+
subject_kind: DecisionRecord
15+
actor: DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing
16+
---
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
apiVersion: processkit.projectious.work/v1
3+
kind: LogEntry
4+
metadata:
5+
id: LOG-20260422_1354-StrongGrove-decision-superseded
6+
created: '2026-04-22T13:54:52+00:00'
7+
spec:
8+
event_type: decision.superseded
9+
timestamp: '2026-04-22T13:54:52+00:00'
10+
summary: DecisionRecord 'DEC-20260422_0926-MerryArch-auto-create-github-releases'
11+
superseded by 'DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing'
12+
subject: DEC-20260422_0926-MerryArch-auto-create-github-releases
13+
subject_kind: DecisionRecord
14+
actor: DEC-20260422_0926-MerryArch-auto-create-github-releases
15+
---

context/skills/devops/release-semver/SKILL.md

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,61 @@ Group entries under **Added**, **Changed**, **Fixed**, and
5656
prominently — call them out in their own section if a major bump
5757
is involved.
5858

59-
### Bump, commit, tag, publish
60-
61-
Update the version in every relevant file (`Cargo.toml`,
62-
`pyproject.toml`, `package.json`, lockfiles). Commit with a message
63-
like `chore: bump version to vX.Y.Z`. Tag the commit `vX.Y.Z`.
64-
Publish according to the project's distribution channel
65-
(crates.io, PyPI, npm, GitHub Releases, container registry, etc.).
59+
### The release flow (single turn, bulletproof)
60+
61+
A release is **one continuous sequence** — you do not stop halfway.
62+
`/pk-release vX.Y.Z` runs every step below in order, in the same
63+
turn, and is not considered done until the final verification
64+
passes. See DEC-20260422_1348-SnowyWolf.
65+
66+
1. **Bump version** in every relevant file (`aibox.lock`,
67+
`Cargo.toml`, `pyproject.toml`, `package.json`, lockfiles).
68+
2. **Finalize CHANGELOG** — rename the `[vX.Y.Z-candidate]` section
69+
to `[vX.Y.Z] — YYYY-MM-DD`; ensure Added/Changed/Fixed/Removed
70+
grouping; call out breaking changes if a major bump.
71+
3. **Regenerate provenance / transitive artifacts** — for processkit,
72+
`scripts/stamp-provenance.sh vX.Y.Z`.
73+
4. **Commit** the bump with message `chore(release): bump to vX.Y.Z`.
74+
5. **Tag** the commit: `git tag -a vX.Y.Z -m "<tag summary>"`.
75+
6. **Push** branch then tag: `git push origin main && git push origin vX.Y.Z`.
76+
A tag push alone does **not** create a GitHub Release — it is a
77+
git ref, not a distribution-channel artifact.
78+
7. **Create the GitHub Release** with notes extracted from the
79+
CHANGELOG. The single canonical command:
80+
```bash
81+
gh release create vX.Y.Z \
82+
--repo <org>/<repo> \
83+
--title "vX.Y.Z — <one-line summary>" \
84+
--notes-file <(awk -v v=X.Y.Z '
85+
$0 ~ "^## \\[v" v "\\]" { f=1; next }
86+
f && /^## \[/ { f=0 }
87+
f
88+
' CHANGELOG.md) \
89+
--latest
90+
```
91+
8. **Verify** the Release is live. This is the release's
92+
completion gate:
93+
```bash
94+
gh release view vX.Y.Z --repo <org>/<repo>
95+
```
96+
If this exits non-zero, the release is **incomplete** — return
97+
to step 7. Do not report the release as done until step 8
98+
succeeds.
99+
9. **Other channels** (crates.io, PyPI, npm, container registry,
100+
homebrew tap, etc.) — run after step 8 so the GitHub Release
101+
remains the canonical artifact pointer.
102+
103+
**Recovery pattern** — if a tag was pushed in a prior session
104+
without the Release, run `gh release create` and `gh release view`
105+
for that tag directly; `/pk-publish` exists as an alias for this
106+
recovery scenario.
107+
108+
**pk-doctor `release_integrity` check (detection layer)** — walks
109+
every local `v*` git tag, probes GitHub for a matching Release, and
110+
WARNs on any tag without one. Run `/pk-doctor --category=release_integrity`
111+
after a release to confirm; run `/pk-doctor` routinely to catch any
112+
historical gap. Requires `gh` CLI + auth; emits INFO when `gh` is
113+
unavailable.
66114

67115
### Example
68116

@@ -84,6 +132,8 @@ Agent-specific failure modes — provider-neutral pause-and-self-check items:
84132
- **Publishing from a developer machine without a clean checkout.** Uncommitted local changes — WIP files, debug flags, `console.log` statements — may sneak into the artifact. Publish from CI with a clean checkout or from a fresh clone.
85133
- **Re-tagging a published version to fix a mistake.** Package registries (crates.io, PyPI, npm) prohibit or block re-publishing to an existing version. Cut a new patch version (e.g. `1.2.1`) to fix a mistake in `1.2.0`; never overwrite a tag.
86134
- **Pre-1.0 projects that promise stability they cannot keep.** If the API is still moving, stay below 1.0 and say so. Cutting `1.0.0` too early creates a compatibility obligation that slows future improvements.
135+
- **Treating `git push --tags` as "the release is out."** Pushing a tag creates a git ref; it does **not** create a GitHub Release, a crates.io release, a PyPI release, or any other distribution-channel artifact. Downstream consumers (package managers, `gh api`, the Releases page, auto-sync tools like aibox) read from the channel, not the tag. Until `gh release create` (or the equivalent channel publish command) has run, the release is not visible. The flow above bakes this in: the release is not complete until step 8 (`gh release view`) succeeds. See DEC-20260422_1348-SnowyWolf.
136+
- **Stopping after `/pk-release` prepare-like work.** In an older split where `/pk-release` prepared and `/pk-publish` published, it was easy to do only half. The current flow is single-turn: /pk-release runs every step through GitHub-Release verification. /pk-publish remains only as a recovery alias for historical tags without Releases.
87137

88138
## Full reference
89139

0 commit comments

Comments
 (0)