@ikon85/agent-workflow-kit 0.34.5 → 0.34.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/kit-release/SKILL.md +29 -21
- package/.claude/skills/kit-release/SKILL.md +29 -21
- package/README.md +5 -0
- package/agent-workflow-kit.package.json +3 -3
- package/docs/adr/0004-release-intent-is-a-version-tag.md +18 -5
- package/package.json +1 -1
- package/scripts/kit-release.test.mjs +10 -4
- package/scripts/test_release_authorization_contract.py +101 -0
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kit-release
|
|
3
|
-
description: "Prepare and
|
|
3
|
+
description: "Prepare, integrate and publish a verified agent-workflow-kit release: one confirmed Semver authorizes the annotated version tag, then monitor npm/GitHub parity to released."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Kit Release
|
|
7
7
|
|
|
8
8
|
Prepare a release deterministically. This skill owns the shipped-delta decision,
|
|
9
|
-
metadata preparation, verification, and the
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
metadata preparation, verification, and the post-merge publication intent. It
|
|
10
|
+
delegates commit, branch push, PR, merge, and cleanup to wrapup. It never
|
|
11
|
+
publishes to a registry or creates a GitHub release by hand — it records intent
|
|
12
|
+
with the annotated tag and lets the trusted workflow publish.
|
|
13
|
+
|
|
14
|
+
The release has **one** human gate: the confirmed Semver in step 2. Everything
|
|
15
|
+
after it — merge, tag, publish, parity check — is the agent's to carry out.
|
|
12
16
|
|
|
13
17
|
## Workflow
|
|
14
18
|
|
|
@@ -33,8 +37,8 @@ never publishes to a registry or creates a GitHub release directly.
|
|
|
33
37
|
Do not turn a narrower build-only or single-action request into this
|
|
34
38
|
authority.
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
Either route authorizes exactly one target — and that authorization carries
|
|
41
|
+
through to its tag and publish. This is the release's single human gate.
|
|
38
42
|
|
|
39
43
|
3. Prepare that authorized exact version:
|
|
40
44
|
|
|
@@ -52,21 +56,25 @@ never publishes to a registry or creates a GitHub release directly.
|
|
|
52
56
|
`scripts/wrapup-land.py` exclusively own commit, push, PR creation, merge,
|
|
53
57
|
and cleanup. Do not reproduce those operations here.
|
|
54
58
|
|
|
55
|
-
5.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
5. Publish. Merging integrates the prepared release; only the annotated tag
|
|
60
|
+
starts publication. Verify that the package version on current `origin/main`
|
|
61
|
+
is exactly `<version>`, then create and push the matching annotated
|
|
62
|
+
`v<version>` tag — **the confirmed Semver authorizes the whole release,
|
|
63
|
+
through tag and publish, so do this without asking again**. The target was
|
|
64
|
+
chosen once, at step 2; nothing between there and here produces information
|
|
65
|
+
a second gate could act on. Report the integrated commit as you tag it, not
|
|
66
|
+
as a question.
|
|
67
|
+
|
|
68
|
+
Publication is **irreversible** — npm versions cannot be reused, and the
|
|
69
|
+
unpublish window is narrow and breaks consumers. The safety lives in gates
|
|
70
|
+
that already ran, not in a prompt: `release:guard`, `kit:staleness`, the full
|
|
71
|
+
suite and `npm pack --dry-run` before merge, then the workflow's own tag
|
|
72
|
+
identity, package version, main ancestry and artifact checks after it. A
|
|
73
|
+
lightweight tag, a mismatching version, or a commit outside canonical `main`
|
|
74
|
+
is invalid release intent. Never infer a tag target, move an existing tag, or
|
|
75
|
+
tag an unmerged commit.
|
|
76
|
+
|
|
77
|
+
6. Monitor the tag-triggered `release.yml` workflow and inspect its externally
|
|
70
78
|
reconstructable state:
|
|
71
79
|
|
|
72
80
|
```sh
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kit-release
|
|
3
|
-
description: "Prepare and
|
|
3
|
+
description: "Prepare, integrate and publish a verified agent-workflow-kit release: one confirmed Semver authorizes the annotated version tag, then monitor npm/GitHub parity to released."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Kit Release
|
|
7
7
|
|
|
8
8
|
Prepare a release deterministically. This skill owns the shipped-delta decision,
|
|
9
|
-
metadata preparation, verification, and the
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
metadata preparation, verification, and the post-merge publication intent. It
|
|
10
|
+
delegates commit, branch push, PR, merge, and cleanup to wrapup. It never
|
|
11
|
+
publishes to a registry or creates a GitHub release by hand — it records intent
|
|
12
|
+
with the annotated tag and lets the trusted workflow publish.
|
|
13
|
+
|
|
14
|
+
The release has **one** human gate: the confirmed Semver in step 2. Everything
|
|
15
|
+
after it — merge, tag, publish, parity check — is the agent's to carry out.
|
|
12
16
|
|
|
13
17
|
## Workflow
|
|
14
18
|
|
|
@@ -33,8 +37,8 @@ never publishes to a registry or creates a GitHub release directly.
|
|
|
33
37
|
Do not turn a narrower build-only or single-action request into this
|
|
34
38
|
authority.
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
Either route authorizes exactly one target — and that authorization carries
|
|
41
|
+
through to its tag and publish. This is the release's single human gate.
|
|
38
42
|
|
|
39
43
|
3. Prepare that authorized exact version:
|
|
40
44
|
|
|
@@ -52,21 +56,25 @@ never publishes to a registry or creates a GitHub release directly.
|
|
|
52
56
|
`scripts/wrapup-land.py` exclusively own commit, push, PR creation, merge,
|
|
53
57
|
and cleanup. Do not reproduce those operations here.
|
|
54
58
|
|
|
55
|
-
5.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
5. Publish. Merging integrates the prepared release; only the annotated tag
|
|
60
|
+
starts publication. Verify that the package version on current `origin/main`
|
|
61
|
+
is exactly `<version>`, then create and push the matching annotated
|
|
62
|
+
`v<version>` tag — **the confirmed Semver authorizes the whole release,
|
|
63
|
+
through tag and publish, so do this without asking again**. The target was
|
|
64
|
+
chosen once, at step 2; nothing between there and here produces information
|
|
65
|
+
a second gate could act on. Report the integrated commit as you tag it, not
|
|
66
|
+
as a question.
|
|
67
|
+
|
|
68
|
+
Publication is **irreversible** — npm versions cannot be reused, and the
|
|
69
|
+
unpublish window is narrow and breaks consumers. The safety lives in gates
|
|
70
|
+
that already ran, not in a prompt: `release:guard`, `kit:staleness`, the full
|
|
71
|
+
suite and `npm pack --dry-run` before merge, then the workflow's own tag
|
|
72
|
+
identity, package version, main ancestry and artifact checks after it. A
|
|
73
|
+
lightweight tag, a mismatching version, or a commit outside canonical `main`
|
|
74
|
+
is invalid release intent. Never infer a tag target, move an existing tag, or
|
|
75
|
+
tag an unmerged commit.
|
|
76
|
+
|
|
77
|
+
6. Monitor the tag-triggered `release.yml` workflow and inspect its externally
|
|
70
78
|
reconstructable state:
|
|
71
79
|
|
|
72
80
|
```sh
|
package/README.md
CHANGED
|
@@ -387,6 +387,11 @@ the old way. Decision record:
|
|
|
387
387
|
|
|
388
388
|
## Release notes
|
|
389
389
|
|
|
390
|
+
### 0.34.6
|
|
391
|
+
|
|
392
|
+
- changed: `.agents/skills/kit-release/SKILL.md`
|
|
393
|
+
- changed: `.claude/skills/kit-release/SKILL.md`
|
|
394
|
+
|
|
390
395
|
### 0.34.5
|
|
391
396
|
|
|
392
397
|
- changed: `.agents/skills/kit-update/SKILL.md`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"kitVersion": "0.34.
|
|
2
|
+
"kitVersion": "0.34.6",
|
|
3
3
|
"files": [
|
|
4
4
|
{
|
|
5
5
|
"path": ".agents/skills/ask-matt/SKILL.md",
|
|
@@ -367,7 +367,7 @@
|
|
|
367
367
|
"ownerSkill": "kit-release",
|
|
368
368
|
"surface": "codex",
|
|
369
369
|
"installRole": "maintainer",
|
|
370
|
-
"sha256": "
|
|
370
|
+
"sha256": "38a3df1f9d4b5573180dc67daba85eb3fd143ae3b60ca6dbfed3297d6a0855cd",
|
|
371
371
|
"mode": 420,
|
|
372
372
|
"origin": "kit"
|
|
373
373
|
},
|
|
@@ -1679,7 +1679,7 @@
|
|
|
1679
1679
|
"ownerSkill": "kit-release",
|
|
1680
1680
|
"surface": "claude",
|
|
1681
1681
|
"installRole": "maintainer",
|
|
1682
|
-
"sha256": "
|
|
1682
|
+
"sha256": "38a3df1f9d4b5573180dc67daba85eb3fd143ae3b60ca6dbfed3297d6a0855cd",
|
|
1683
1683
|
"mode": 420,
|
|
1684
1684
|
"origin": "kit"
|
|
1685
1685
|
},
|
|
@@ -6,6 +6,17 @@ Clarified 2026-07-25 by issue #239: an explicit AFK end-to-end mandate may
|
|
|
6
6
|
authorize deterministic reversible release preparation, but never the
|
|
7
7
|
publication tag itself.
|
|
8
8
|
|
|
9
|
+
Amended 2026-07-25 by issue #257: the confirmed Semver authorizes the whole
|
|
10
|
+
release, through tag and publish. The separation of integration and publication
|
|
11
|
+
below is unchanged — merging still cannot publish, and only an annotated tag
|
|
12
|
+
records release intent — but that intent no longer waits for a second human
|
|
13
|
+
confirmation. The maintainer chooses the version once; the agent then merges,
|
|
14
|
+
tags, and monitors to `released`. The second gate had proven worse than no gate:
|
|
15
|
+
it left prepared versions sitting in `awaiting-tag` until someone returned,
|
|
16
|
+
which is how 0.34.2 was skipped and buried under 0.34.3 (#243). Safety stays in
|
|
17
|
+
the gates that run regardless of who is watching — guard, staleness, suite,
|
|
18
|
+
pack, and the workflow's own tag/version/ancestry validation.
|
|
19
|
+
|
|
9
20
|
The release workflow currently publishes whenever a merge to `main` changes
|
|
10
21
|
`package.json`. That keeps the canonical branch and npm close together, but it
|
|
11
22
|
also makes an ordinary merge an irreversible public action whose consequence is
|
|
@@ -39,11 +50,13 @@ We decided that integration and publication are separate transitions:
|
|
|
39
50
|
|
|
40
51
|
## Consequences
|
|
41
52
|
|
|
42
|
-
- A prepared version
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
- A prepared version exists on `main` in an `awaiting-tag` state only for as
|
|
54
|
+
long as the agent needs to tag it — not until a human returns (amended by
|
|
55
|
+
#257).
|
|
56
|
+
- The authorizing act is the confirmed Semver. An explicit AFK Wave/Program
|
|
57
|
+
mandate that names release preparation carries the same weight, and either
|
|
58
|
+
route reaches through tag and publish for that one target. A narrower
|
|
59
|
+
build-only or single-action request never becomes this authority.
|
|
47
60
|
- A single-Wave mandate does not authorize the next Program Wave. A later
|
|
48
61
|
explicit whole-Program mandate does authorize all planned Waves and remains in
|
|
49
62
|
force across their boundaries.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikon85/agent-workflow-kit",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.6",
|
|
4
4
|
"description": "Portable AI-agent workflow skills (plan → execute → land → learn) for Claude Code & Codex — grilling, TDD, diagnosis, two-axis code review, cross-model Codex review, design & domain-modeling, plus a skill router (ask-matt). npx init/update/diff/uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -184,15 +184,21 @@ test('both release skill surfaces name only the owned scoped npm package', async
|
|
|
184
184
|
}
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
// Amended by #257: one gate, at the Semver. The previous contract required a
|
|
188
|
+
// second confirmation before the tag; that gate left prepared versions stranded
|
|
189
|
+
// in `awaiting-tag` and is gone. What must survive is the narrowing rule — a
|
|
190
|
+
// build-only request never becomes release authority.
|
|
191
|
+
test('one confirmed Semver authorizes the release through tag and publish', async () => {
|
|
188
192
|
const claude = await readFile(join(REPO, '.claude/skills/kit-release/SKILL.md'), 'utf8');
|
|
189
193
|
const codex = await readFile(join(REPO, '.agents/skills/kit-release/SKILL.md'), 'utf8');
|
|
190
|
-
assert.equal(codex, claude);
|
|
194
|
+
assert.equal(codex.split('\n---\n')[1], claude.split('\n---\n')[1]);
|
|
191
195
|
for (const body of [claude, codex]) {
|
|
192
196
|
assert.match(body, /explicit AFK end-to-end mandate/i);
|
|
193
197
|
assert.match(body, /deterministic recommendation/i);
|
|
194
|
-
assert.match(body, /
|
|
195
|
-
assert.match(body, /
|
|
198
|
+
assert.match(body, /annotated\s+`v<version>` tag/i);
|
|
199
|
+
assert.match(body, /confirmed Semver authorizes[\s\S]*without asking again/i);
|
|
200
|
+
assert.match(body, /narrower build-only or single-action request/i);
|
|
201
|
+
assert.doesNotMatch(body, /separate explicit confirmation/i);
|
|
196
202
|
}
|
|
197
203
|
});
|
|
198
204
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Release-authorization contract (#257).
|
|
3
|
+
|
|
4
|
+
The confirmed Semver authorizes the whole release, through tag and publish.
|
|
5
|
+
One human gate at version choice, not two — the second gate stalled every
|
|
6
|
+
release at `awaiting-tag` until someone returned, which is how 0.34.2 was
|
|
7
|
+
skipped and buried under 0.34.3 (#243).
|
|
8
|
+
|
|
9
|
+
What this pins:
|
|
10
|
+
|
|
11
|
+
- Both `kit-release` surfaces carry the carried-through authorization, and
|
|
12
|
+
neither demands a separate confirmation before tagging.
|
|
13
|
+
- The safety framing survives the change: irreversibility and the pre-tag
|
|
14
|
+
gates stay named, so autonomy never reads as "tag whatever".
|
|
15
|
+
- `CLAUDE.md`, `AGENTS.md` and ADR-0004 agree with the skill.
|
|
16
|
+
|
|
17
|
+
Run: python3 scripts/test_release_authorization_contract.py
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import unittest
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
25
|
+
SURFACES = (".claude/skills/kit-release/SKILL.md", ".agents/skills/kit-release/SKILL.md")
|
|
26
|
+
ADR = "docs/adr/0004-release-intent-is-a-version-tag.md"
|
|
27
|
+
DOCTRINE_FILES = ("CLAUDE.md", "AGENTS.md")
|
|
28
|
+
# Wording that would reinstate the second gate anywhere in the release surface.
|
|
29
|
+
SECOND_GATE = (
|
|
30
|
+
"separate explicit confirmation",
|
|
31
|
+
"separately confirmed publication intent",
|
|
32
|
+
"obtain a separate",
|
|
33
|
+
"After that confirmation",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def read(relative: str) -> str:
|
|
38
|
+
return (ROOT / relative).read_text(encoding="utf-8")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def normalized(relative: str) -> str:
|
|
42
|
+
return " ".join(read(relative).split())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SkillSurfaceContract(unittest.TestCase):
|
|
46
|
+
def test_both_surfaces_carry_the_authorization_through_to_the_tag(self):
|
|
47
|
+
for surface in SURFACES:
|
|
48
|
+
with self.subTest(surface=surface):
|
|
49
|
+
prose = normalized(surface)
|
|
50
|
+
self.assertIn("confirmed Semver authorizes", prose)
|
|
51
|
+
self.assertIn("tag and publish", prose)
|
|
52
|
+
self.assertIn("without asking again", prose)
|
|
53
|
+
|
|
54
|
+
def test_no_surface_reinstates_a_second_confirmation_gate(self):
|
|
55
|
+
for surface in SURFACES:
|
|
56
|
+
for phrase in SECOND_GATE:
|
|
57
|
+
with self.subTest(surface=surface, phrase=phrase):
|
|
58
|
+
self.assertNotIn(phrase, normalized(surface))
|
|
59
|
+
|
|
60
|
+
def test_autonomy_never_drops_the_safety_framing(self):
|
|
61
|
+
"""Tagging stays irreversible and gated — the gates just move earlier."""
|
|
62
|
+
for surface in SURFACES:
|
|
63
|
+
with self.subTest(surface=surface):
|
|
64
|
+
prose = normalized(surface)
|
|
65
|
+
self.assertIn("irreversible", prose)
|
|
66
|
+
self.assertIn("cannot be reused", prose)
|
|
67
|
+
for gate in ("release:guard", "kit:staleness", "npm pack"):
|
|
68
|
+
self.assertIn(gate, prose)
|
|
69
|
+
|
|
70
|
+
def test_the_tag_remains_the_sole_publication_intent(self):
|
|
71
|
+
for surface in SURFACES:
|
|
72
|
+
with self.subTest(surface=surface):
|
|
73
|
+
prose = normalized(surface)
|
|
74
|
+
self.assertIn("Merging integrates", prose)
|
|
75
|
+
self.assertIn("annotated", prose)
|
|
76
|
+
|
|
77
|
+
def test_both_surfaces_stay_mirrored_below_the_frontmatter(self):
|
|
78
|
+
bodies = [read(surface).split("\n---\n", 1)[1] for surface in SURFACES]
|
|
79
|
+
self.assertEqual(bodies[0], bodies[1])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ProjectDoctrineContract(unittest.TestCase):
|
|
83
|
+
def test_root_convention_files_agree_with_the_skill(self):
|
|
84
|
+
for name in DOCTRINE_FILES:
|
|
85
|
+
with self.subTest(document=name):
|
|
86
|
+
prose = normalized(name)
|
|
87
|
+
self.assertIn("confirmed Semver authorizes", prose)
|
|
88
|
+
for phrase in SECOND_GATE:
|
|
89
|
+
self.assertNotIn(phrase, prose)
|
|
90
|
+
|
|
91
|
+
def test_the_adr_records_an_amendment_instead_of_a_silent_rewrite(self):
|
|
92
|
+
prose = normalized(ADR)
|
|
93
|
+
self.assertIn("Amended", prose)
|
|
94
|
+
self.assertIn("2026-07-25", prose)
|
|
95
|
+
self.assertIn("confirmed Semver authorizes", prose)
|
|
96
|
+
# The original decision must remain readable, not be overwritten.
|
|
97
|
+
self.assertIn("version tag", prose)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
unittest.main()
|