@ikon85/agent-workflow-kit 0.26.2 → 0.27.1

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.
@@ -90,8 +90,7 @@ An independent second model catches what one model rationalises. Read-only, boun
90
90
 
91
91
  Not feature work — upkeep.
92
92
 
93
- - **`/improve-codebase-architecture`** — run in spare moments to keep the codebase good for agents. Surfaces **deepening opportunities**; picking one _generates an idea_ for the main flow at `/grill-with-docs`.
94
- - **`/zoom-out`** — step back from the diff to the structure when a change is fighting the codebase; recon that becomes a planned slice or guides a refactor in place.
93
+ - **`/improve-codebase-architecture`** — run in spare moments to keep the codebase good for agents, or step back from the diff to the structure when a change is fighting the codebase. Surfaces **deepening opportunities**; picking one _generates an idea_ for the main flow at `/grill-with-docs`.
95
94
  - **`/security-audit`** — a whole-app, application-layer security audit run as an independent two-model pass (two models audit the same code separately, then the remediation plan is hardened before any fix). Run before a release or after the attack surface changes (new endpoint, new input source, auth change, dependency bump). Infra hardening (ports/TLS/SSH/backups) is audited separately.
96
95
 
97
96
  ## Vocabulary underneath
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: board-to-waves
3
3
  disable-model-invocation: false
4
- "description": "Use when sweeping/grooming the GitHub backlog board to cluster open issues into thematic waves (Wellen) — \"lass uns das Board durchgehen\", \"was ist offen und passt zusammen\", \"clustern wir den Backlog\", \"welche Wellen stecken da drin\" — or to spot candidate waves before planning. Reads all open issues, groups them by the Gate+Booster+Splitter heuristic, estimates size/risk + grill-needed per candidate, and after the user confirms creates cluster/Wave-less candidate STUB issues with a To-Do checklist. STOPS at stubs: no PRD, no slicing, no sub-issue links, no promotion (downstream: to-prd matures the stub, to-issues slices + promotes). NOT for maturing/slicing ONE already-chosen candidate (to-prd/to-issues), NOT for per-issue triage labelling (triage), NOT for code comprehension (zoom-out) — this is board-WIDE thematic affinity grouping, not per-issue and not code-reading."
4
+ "description": "Use when sweeping/grooming the GitHub backlog board to cluster open issues into thematic waves (Wellen) — \"lass uns das Board durchgehen\", \"was ist offen und passt zusammen\", \"clustern wir den Backlog\", \"welche Wellen stecken da drin\" — or to spot candidate waves before planning. Reads all open issues, groups them by the Gate+Booster+Splitter heuristic, estimates size/risk + grill-needed per candidate, and after the user confirms creates cluster/Wave-less candidate STUB issues with a To-Do checklist. STOPS at stubs: no PRD, no slicing, no sub-issue links, no promotion (downstream: to-prd matures the stub, to-issues slices + promotes). NOT for maturing/slicing ONE already-chosen candidate (to-prd/to-issues), NOT for per-issue triage labelling (triage), NOT for code comprehension — this is board-WIDE thematic affinity grouping, not per-issue and not code-reading."
5
5
  ---
6
6
 
7
7
  # board-to-waves
@@ -130,6 +130,6 @@ Next step (<maintainer>'s call, separate): grill → to-prd (matures the stub)
130
130
 
131
131
  ## Notes
132
132
  - **Stops at stubs.** Never write the PRD, slice, link, promote, or call `to-prd`/`to-issues` yourself.
133
- - Boundary: `triage` = per-issue state machine; `zoom-out` = code comprehension; `board-to-waves` = board-wide thematic affinity. Different altitudes.
133
+ - Boundary: `triage` = per-issue state machine; `improve-codebase-architecture` = code structure; `board-to-waves` = board-wide thematic affinity. Different altitudes.
134
134
  - A leftover bucket is OK and intentional — not every issue belongs in a wave. Force-clustering dilutes the gate.
135
135
  - Size/risk are a rough estimate, not a commitment. <maintainer> can override the stub's "when to grill" recommendation at any time.
@@ -8,8 +8,6 @@ description: "Find deepening opportunities in a codebase, informed by the domain
8
8
 
9
9
  Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
10
10
 
11
- For a quick one-off map of an unfamiliar area without the report/grilling loop below, use `zoom-out` instead.
12
-
13
11
  ## Glossary
14
12
 
15
13
  Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service," "API," or "boundary." Full definitions in [LANGUAGE.md](LANGUAGE.md).
@@ -71,6 +71,9 @@ The optional `wrapup` block is a wrapup-only switch, not a board field: `wrapup.
71
71
  "headings": {
72
72
  "vorBau": "Clarify Before Build"
73
73
  },
74
+ "titles": {
75
+ "wavePrefix": "Wave"
76
+ },
74
77
  "wrapup": {
75
78
  "remoteBranchSweep": false
76
79
  }
@@ -93,6 +96,14 @@ follows; renaming an option later is one edit here. A role you don't have
93
96
  with a visible hint, and `--status-role` commands fail with the exact snippet
94
97
  to add.
95
98
 
99
+ ### Wave titles (`titles.wavePrefix`)
100
+
101
+ The word that opens a wave anchor's title: `<prefix> <N> — <topic>` (e.g.
102
+ `Wave 7 — Auth hardening`). `board-sync.py promote` writes it and strips an
103
+ existing one idempotently on re-promote. A profile **without** the key keeps
104
+ the historical default `"Welle"`, so boards created before this key keep
105
+ their titles unchanged; set it once to match your board's language.
106
+
96
107
  ## If the IDs are not yet filled (stub)
97
108
 
98
109
  1. Create a GitHub-Projects (v2) board for this owner and add the fields above (at minimum a `Status` single-select with your stage options — the recommended stage names are `Idea, Triaged, Spec, In Progress, Review, Done`, matching the seeded `roles` defaults).
@@ -90,8 +90,7 @@ An independent second model catches what one model rationalises. Read-only, boun
90
90
 
91
91
  Not feature work — upkeep.
92
92
 
93
- - **`/improve-codebase-architecture`** — run in spare moments to keep the codebase good for agents. Surfaces **deepening opportunities**; picking one _generates an idea_ for the main flow at `/grill-with-docs`.
94
- - **`/zoom-out`** — step back from the diff to the structure when a change is fighting the codebase; recon that becomes a planned slice or guides a refactor in place.
93
+ - **`/improve-codebase-architecture`** — run in spare moments to keep the codebase good for agents, or step back from the diff to the structure when a change is fighting the codebase. Surfaces **deepening opportunities**; picking one _generates an idea_ for the main flow at `/grill-with-docs`.
95
94
  - **`/security-audit`** — a whole-app, application-layer security audit run as an independent two-model pass (two models audit the same code separately, then the remediation plan is hardened before any fix). Run before a release or after the attack surface changes (new endpoint, new input source, auth change, dependency bump). Infra hardening (ports/TLS/SSH/backups) is audited separately.
96
95
 
97
96
  ## Vocabulary underneath
@@ -12,7 +12,7 @@ description: >-
12
12
  stubs: no PRD, no slicing, no sub-issue links, no promotion (downstream:
13
13
  to-prd matures the stub, to-issues slices + promotes). NOT for
14
14
  maturing/slicing ONE already-chosen candidate (to-prd/to-issues), NOT for
15
- per-issue triage labelling (triage), NOT for code comprehension (zoom-out)
15
+ per-issue triage labelling (triage), NOT for code comprehension —
16
16
  this is board-WIDE thematic affinity grouping, not per-issue and not
17
17
  code-reading.
18
18
  ---
@@ -143,6 +143,6 @@ Next step (<maintainer>'s call, separate): grill → to-prd (matures the stub)
143
143
 
144
144
  ## Notes
145
145
  - **Stops at stubs.** Never write the PRD, slice, link, promote, or call `to-prd`/`to-issues` yourself.
146
- - Boundary: `triage` = per-issue state machine; `zoom-out` = code comprehension; `board-to-waves` = board-wide thematic affinity. Different altitudes.
146
+ - Boundary: `triage` = per-issue state machine; `improve-codebase-architecture` = code structure; `board-to-waves` = board-wide thematic affinity. Different altitudes.
147
147
  - A leftover bucket is OK and intentional — not every issue belongs in a wave. Force-clustering dilutes the gate.
148
148
  - Size/risk are a rough estimate, not a commitment. <maintainer> can override the stub's "when to grill" recommendation at any time.
@@ -8,8 +8,6 @@ description: Find deepening opportunities in a codebase, informed by the domain
8
8
 
9
9
  Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
10
10
 
11
- For a quick one-off map of an unfamiliar area without the report/grilling loop below, use `zoom-out` instead.
12
-
13
11
  ## Glossary
14
12
 
15
13
  Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service," "API," or "boundary." Full definitions in [LANGUAGE.md](LANGUAGE.md).
@@ -102,3 +102,6 @@ both: pick one hooks mechanism per repo.
102
102
  - Hooks are just scripts — `echo`-debug them freely; nothing is hidden behind a
103
103
  tool.
104
104
  - Exit non-zero = block the commit. Exit 0 = allow.
105
+ - Keep the template's `unset GIT_DIR …` line: git exports these to hooks, and a
106
+ gate that runs tests which call git in their own temp repos would otherwise
107
+ operate on the host repo's index instead.
@@ -11,6 +11,12 @@
11
11
 
12
12
  set -euo pipefail
13
13
 
14
+ # --- Env sanitize (keep this) ----------------------------------------------
15
+ # Git exports GIT_DIR & co. to hooks. If your gate runs a test suite whose
16
+ # child tests call git in their own temp repos, these inherited vars redirect
17
+ # every such git call onto THIS repo's index (mass-staged deletions).
18
+ unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_PREFIX
19
+
14
20
  # --- Optional preflight ----------------------------------------------------
15
21
  # Bail early with a clear message if the toolchain isn't installed, so the
16
22
  # real checks don't fail with cryptic errors. Adapt or delete for your stack.
@@ -71,6 +71,9 @@ The optional `wrapup` block is a wrapup-only switch, not a board field: `wrapup.
71
71
  "headings": {
72
72
  "vorBau": "Clarify Before Build"
73
73
  },
74
+ "titles": {
75
+ "wavePrefix": "Wave"
76
+ },
74
77
  "wrapup": {
75
78
  "remoteBranchSweep": false
76
79
  }
@@ -93,6 +96,14 @@ follows; renaming an option later is one edit here. A role you don't have
93
96
  with a visible hint, and `--status-role` commands fail with the exact snippet
94
97
  to add.
95
98
 
99
+ ### Wave titles (`titles.wavePrefix`)
100
+
101
+ The word that opens a wave anchor's title: `<prefix> <N> — <topic>` (e.g.
102
+ `Wave 7 — Auth hardening`). `board-sync.py promote` writes it and strips an
103
+ existing one idempotently on re-promote. A profile **without** the key keeps
104
+ the historical default `"Welle"`, so boards created before this key keep
105
+ their titles unchanged; set it once to match your board's language.
106
+
96
107
  ## If the IDs are not yet filled (stub)
97
108
 
98
109
  1. Create a GitHub-Projects (v2) board for this owner and add the fields above (at minimum a `Status` single-select with your stage options — the recommended stage names are `Idea, Triaged, Spec, In Progress, Review, Done`, matching the seeded `roles` defaults).
@@ -167,15 +167,6 @@
167
167
  ],
168
168
  "provenance": "matt-pocock"
169
169
  },
170
- "zoom-out": {
171
- "class": "vendored",
172
- "publish": true,
173
- "surfaces": [
174
- "claude",
175
- "codex"
176
- ],
177
- "provenance": "matt-pocock"
178
- },
179
170
  "improve-codebase-architecture": {
180
171
  "class": "vendored",
181
172
  "publish": true,
package/PROVENANCE.md CHANGED
@@ -6,7 +6,7 @@ not byte-identity.
6
6
 
7
7
  ## Matt Pocock (MIT) — https://github.com/mattpocock/skills @ d574778
8
8
 
9
- to-prd, to-issues, grill-with-docs, grill-me, tdd, diagnose, zoom-out,
9
+ to-prd, to-issues, grill-with-docs, grill-me, tdd, diagnose,
10
10
  improve-codebase-architecture, prototype, triage, write-a-skill,
11
11
  codebase-design, domain-modeling, implement, resolving-merge-conflicts, ask-matt,
12
12
  wayfinder, research, git-guardrails-claude-code, setup-workflow.
@@ -15,9 +15,10 @@ Folder↔upstream-name divergence (upstream renamed; local folder kept for
15
15
  invocation stability): `diagnose` = upstream `diagnosing-bugs`,
16
16
  `write-a-skill` = upstream `writing-great-skills`, `to-prd` = upstream
17
17
  `to-spec`, `to-issues` = upstream `to-tickets` (upstream merged to-issues +
18
- the short-lived to-plan into to-tickets @ v1.1.0). `zoom-out` was removed
19
- upstream (attribution pins to its last-seen SHA 2bf7005) and is retained here
20
- as a local fork.
18
+ the short-lived to-plan into to-tickets @ v1.1.0). `zoom-out` (last-seen
19
+ upstream SHA 2bf7005) was removed upstream and, after a period as a local
20
+ fork, removed from this kit as well `improve-codebase-architecture` covers
21
+ the step-back-to-the-structure door.
21
22
 
22
23
  Each carries a `THIRD-PARTY-NOTICES.md` with its upstream path.
23
24
 
package/README.md CHANGED
@@ -58,22 +58,20 @@ The skills aren't a grab-bag — they're four phases of one loop, entered throug
58
58
  single funnel no matter where your work starts. Each phase below names the failure
59
59
  mode it removes and the skills that remove it.
60
60
 
61
- ![The workflow loop: idea / plan / backlog / raw-issue all funnel through an optional grill into one uniform PRD, which to-issues decomposes into an atomic issue or a wave anchor; a gate clears any unknown, then Execute Land Learn, which compounds back into the next idea. Two further streams enter outside the funnel a bug/anomaly through diagnose, and a code-fighting-the-structure change through zoom-out both joining at Execute; a one-time repo-setup lane (setup-workflow, git-guardrails, setup-pre-commit) sits off the loop and installs the pre-commit/pre-push gate that Land relies on.](docs/workflow.png)
61
+ ![The workflow as a subway map one row per route, all converging on the same implement → wrapup → retro spine: a scale-check router sizes unclear work; the main line runs idea through an optional grill, to-prd, to-issues and a gate into implement (which drives tdd inside), wrapup, retro; a grill line scales from none over grill-me and grill-with-docs up to the +codex variants; a codex line adds cross-model review and build; program and board lines fund numbered waves built by orchestrate-wave; repair (diagnose) and structure (improve-codebase-architecture) lines enter from broken or tangled work; a learn line routes retro's findings into config by weight. Interchanges: verify-spike / decision-gate before an unknown, prototype for an unclear path, local-ci plus pre-commit/pre-push hooks before landing, ask-matt when lost, and a one-time setup lane.](docs/workflow.png)
62
62
 
63
63
  <!--
64
- The image above is a pre-rendered PNG, not a live Mermaid block, on purpose:
65
- GitHub's Mermaid renderer clips long node labels, and an <img>-embedded SVG
66
- can't render Mermaid's foreignObject node text (shows blank). A rasterised PNG
67
- renders identically everywhere.
68
- Source of truth: tools/agent-workflow-kit/assets/workflow.mmd
69
- Regenerate after editing the source:
70
- npx -y @mermaid-js/mermaid-cli \
71
- -i tools/agent-workflow-kit/assets/workflow.mmd \
72
- -o tools/agent-workflow-kit/assets/workflow.png \
73
- -t dark -b '#0d1117' -s 3
74
- build-kit.mjs copies assets/workflow.png -> dist-kit/docs/workflow.png.
64
+ The image above is a pre-rendered PNG, not live HTML, on purpose: GitHub
65
+ READMEs can't embed HTML pages, and a PNG renders identically everywhere.
66
+ Source of truth: docs/workflow.html (same design language as
67
+ docs/methodology.html). Regenerate after editing the source:
68
+ serve docs/ locally, open workflow.html?theme=dark in a 1160px-wide
69
+ viewport, take a full-page screenshot -> docs/workflow.png
70
+ The interactive version is linked below and published via GitHub Pages.
75
71
  -->
76
72
 
73
+ **[view the workflow map →](https://ikon85.github.io/agent-workflow-kit/workflow.html)**
74
+
77
75
 
78
76
  ### 1. Plan — turn a vague idea into shaped, tracked work
79
77
 
@@ -155,7 +153,7 @@ enter outside the plan funnel and feed straight into Execute:
155
153
  - **`diagnose`** — a disciplined root-cause hunt for bugs (reproduce → isolate →
156
154
  fix → prove), not a guess-and-patch. Entered from a bug or anomaly, never the PRD
157
155
  funnel; the fix it lands flows on into Execute.
158
- - **`zoom-out` / `improve-codebase-architecture`** — step back from the diff to
156
+ - **`improve-codebase-architecture`** — step back from the diff to
159
157
  the structure when a change is fighting the codebase. A recon stream that either
160
158
  becomes a planned slice (back through `to-issues`) or guides a refactor in place.
161
159
 
@@ -332,6 +330,29 @@ still reference. Flags: `--force` (overwrite pre-existing files on `init`),
332
330
 
333
331
  ## Release notes
334
332
 
333
+ ### 0.27.1
334
+
335
+ - changed: `scripts/release-parity.mjs`
336
+
337
+ ### 0.27.0
338
+
339
+ - removed: `.agents/skills/zoom-out/SKILL.md`
340
+ - removed: `.agents/skills/zoom-out/THIRD-PARTY-NOTICES.md`
341
+ - removed: `.claude/skills/zoom-out/SKILL.md`
342
+ - removed: `.claude/skills/zoom-out/THIRD-PARTY-NOTICES.md`
343
+ - changed: `.agents/skills/ask-matt/SKILL.md`
344
+ - changed: `.agents/skills/board-to-waves/SKILL.md`
345
+ - changed: `.agents/skills/improve-codebase-architecture/SKILL.md`
346
+ - changed: `.agents/skills/setup-workflow/board-sync.md`
347
+ - changed: `.claude/skills/ask-matt/SKILL.md`
348
+ - changed: `.claude/skills/board-to-waves/SKILL.md`
349
+ - changed: `.claude/skills/improve-codebase-architecture/SKILL.md`
350
+ - changed: `.claude/skills/setup-pre-commit/SKILL.md`
351
+ - changed: `.claude/skills/setup-pre-commit/scripts/pre-commit.template.sh`
352
+ - changed: `.claude/skills/setup-workflow/board-sync.md`
353
+ - changed: `scripts/board-sync.py`
354
+ - changed: `scripts/board_config.py`
355
+
335
356
  ### 0.26.2
336
357
 
337
358
  - Metadata-only release.
@@ -743,14 +764,15 @@ still reference. Flags: `--force` (overwrite pre-existing files on `init`),
743
764
 
744
765
  ## What's in the box
745
766
 
746
- **42 skills** (Router: ask-matt — "which skill/flow fits?" · Plan: grill-me,
767
+ **43 skills** (Router: ask-matt — "which skill/flow fits?" · Plan: grill-me,
747
768
  grill-with-docs, to-prd, to-issues, board-to-waves, triage, spec-self-critique,
748
769
  verify-spike, decision-gate, scale-check, to-waves, wayfinder, research · Execute: tdd, prototype, implement, orchestrate-wave ·
749
- Design/diagnose/refactor streams: diagnose, zoom-out,
770
+ Design/diagnose/refactor streams: diagnose,
750
771
  improve-codebase-architecture, codebase-design, domain-modeling, security-audit · Land: wrapup,
751
772
  resolving-merge-conflicts, code-review, local-ci, git-worktree-recover, kit-release,
752
- project-release · Learn: retro, audit-skills, write-a-skill · Setup:
753
- setup-workflow, git-guardrails, setup-pre-commit · Codex cross-model: grill-me-codex,
773
+ project-release, kit-update · Learn: retro, audit-skills, write-a-skill,
774
+ memory-lifecycle · Setup:
775
+ setup-workflow, git-guardrails, setup-pre-commit, census-update · Codex cross-model: grill-me-codex,
754
776
  grill-with-docs-codex, codex-review, codex-build),
755
777
  installed for both surfaces — `.claude/skills`
756
778
  (Claude Code) and `.agents/skills` (Codex) — plus `codex-adapter-sync`
@@ -1,5 +1,5 @@
1
1
  {
2
- "kitVersion": "0.26.2",
2
+ "kitVersion": "0.27.1",
3
3
  "files": [
4
4
  {
5
5
  "path": ".agents/skills/ask-matt/SKILL.md",
@@ -7,7 +7,7 @@
7
7
  "ownerSkill": "ask-matt",
8
8
  "surface": "codex",
9
9
  "installRole": "consumer",
10
- "sha256": "0231dfe9e39406a4c86f86fe93f00a01dbe0390cbc2ca0f425ac6fca4c2f0406",
10
+ "sha256": "23997067d90e7a14c49b0bf2000a1f042cca0db8f45197dac7a8e3a8316bfed9",
11
11
  "mode": 420,
12
12
  "origin": "kit"
13
13
  },
@@ -37,7 +37,7 @@
37
37
  "ownerSkill": "board-to-waves",
38
38
  "surface": "codex",
39
39
  "installRole": "consumer",
40
- "sha256": "edcfbab80c6fc019ae1ba25b2fbec564c832b1619a4a92d175e214415335160a",
40
+ "sha256": "1e145fd3da4426aa210169d9a47c5558588ee7317cbeb4ae8d0852c2cd672943",
41
41
  "mode": 420,
42
42
  "origin": "kit"
43
43
  },
@@ -347,7 +347,7 @@
347
347
  "ownerSkill": "improve-codebase-architecture",
348
348
  "surface": "codex",
349
349
  "installRole": "consumer",
350
- "sha256": "d7a51056b856fced86046c365ac68e3b7353af831a6313e6b0adf5a9c9042160",
350
+ "sha256": "594ef87762120abab778e454c27d240bf5b2c3411f7fe6c5f6ff028a6cdfeb4b",
351
351
  "mode": 420,
352
352
  "origin": "kit"
353
353
  },
@@ -557,7 +557,7 @@
557
557
  "ownerSkill": "setup-workflow",
558
558
  "surface": "codex",
559
559
  "installRole": "consumer",
560
- "sha256": "71fd0b2d637922d405981d12c2c65ba3b7509bb2ff48ea4eb12499b8c5dcdac4",
560
+ "sha256": "032c9654baf6f5e60007e7d0767a96ab823a442ed86cb52e801836130c9b5ba2",
561
561
  "mode": 420,
562
562
  "origin": "kit"
563
563
  },
@@ -971,26 +971,6 @@
971
971
  "mode": 420,
972
972
  "origin": "kit"
973
973
  },
974
- {
975
- "path": ".agents/skills/zoom-out/SKILL.md",
976
- "kind": "skill",
977
- "ownerSkill": "zoom-out",
978
- "surface": "codex",
979
- "installRole": "consumer",
980
- "sha256": "cc448ec9d88e3c9d06fd1d6083a3c15a342b8399bc46829169b8c30d5e657a27",
981
- "mode": 420,
982
- "origin": "kit"
983
- },
984
- {
985
- "path": ".agents/skills/zoom-out/THIRD-PARTY-NOTICES.md",
986
- "kind": "skill",
987
- "ownerSkill": "zoom-out",
988
- "surface": "codex",
989
- "installRole": "consumer",
990
- "sha256": "87a6e3845e705204a009b26bab75f09e64d97f2d00e19ccd9ee1da39564dbc69",
991
- "mode": 420,
992
- "origin": "kit"
993
- },
994
974
  {
995
975
  "path": ".claude/hooks/_hook_utils.py",
996
976
  "kind": "hook",
@@ -1181,7 +1161,7 @@
1181
1161
  "ownerSkill": "ask-matt",
1182
1162
  "surface": "claude",
1183
1163
  "installRole": "consumer",
1184
- "sha256": "0d6dd8e359cb3084cb14fd700536d5bcd1e60edfb9a9f616078b8eb3d7ccd85d",
1164
+ "sha256": "931ccbc6645adcb02f585061aaf32655ddc1ae547ebb131b6956e8fe227376aa",
1185
1165
  "mode": 420,
1186
1166
  "origin": "kit"
1187
1167
  },
@@ -1211,7 +1191,7 @@
1211
1191
  "ownerSkill": "board-to-waves",
1212
1192
  "surface": "claude",
1213
1193
  "installRole": "consumer",
1214
- "sha256": "c68eb8a3328835bbad977b5981d38d64e43a67977f825a0b56766d6386caf4bc",
1194
+ "sha256": "28c47d10f7508cadf5056c66a62d5891dfb61733a30ae517be24dd766c7996dd",
1215
1195
  "mode": 420,
1216
1196
  "origin": "kit"
1217
1197
  },
@@ -1641,7 +1621,7 @@
1641
1621
  "ownerSkill": "improve-codebase-architecture",
1642
1622
  "surface": "claude",
1643
1623
  "installRole": "consumer",
1644
- "sha256": "ea979ce3b71da45cb15408f10f2481fb71bb72bba7b52747dd4f7fad45c64948",
1624
+ "sha256": "bf96773f4ba75a53c8b916052e36a6c0f4bcae8b46b94bb72beb446048ca6d8a",
1645
1625
  "mode": 420,
1646
1626
  "origin": "kit"
1647
1627
  },
@@ -1841,7 +1821,7 @@
1841
1821
  "ownerSkill": "setup-pre-commit",
1842
1822
  "surface": "claude",
1843
1823
  "installRole": "consumer",
1844
- "sha256": "b64a0af66a52f397f7495d2e27ea6a2bf33cd7bc9c57fd18061ae5036296e5d3",
1824
+ "sha256": "32a6aba5f5a88079ea81ca31a04fcffd3973e7d52584dca570cd8777c0dd618d",
1845
1825
  "mode": 420,
1846
1826
  "origin": "kit"
1847
1827
  },
@@ -1851,7 +1831,7 @@
1851
1831
  "ownerSkill": "setup-pre-commit",
1852
1832
  "surface": "claude",
1853
1833
  "installRole": "consumer",
1854
- "sha256": "b2c727f5922dce2acb21bfa055c208a20893f7a54e4967656a64beeedec90ebe",
1834
+ "sha256": "61ab40452ed411170ef16fbe2d6e5df39a61457c343ecedd23dbf726c1d800b7",
1855
1835
  "mode": 420,
1856
1836
  "origin": "kit"
1857
1837
  },
@@ -1871,7 +1851,7 @@
1871
1851
  "ownerSkill": "setup-workflow",
1872
1852
  "surface": "claude",
1873
1853
  "installRole": "consumer",
1874
- "sha256": "71fd0b2d637922d405981d12c2c65ba3b7509bb2ff48ea4eb12499b8c5dcdac4",
1854
+ "sha256": "032c9654baf6f5e60007e7d0767a96ab823a442ed86cb52e801836130c9b5ba2",
1875
1855
  "mode": 420,
1876
1856
  "origin": "kit"
1877
1857
  },
@@ -2315,26 +2295,6 @@
2315
2295
  "mode": 420,
2316
2296
  "origin": "kit"
2317
2297
  },
2318
- {
2319
- "path": ".claude/skills/zoom-out/SKILL.md",
2320
- "kind": "skill",
2321
- "ownerSkill": "zoom-out",
2322
- "surface": "claude",
2323
- "installRole": "consumer",
2324
- "sha256": "9e4fbb4e8c5d8cc847812b324db8e72aa716148bceb7d2d368e8bfda146fb84f",
2325
- "mode": 420,
2326
- "origin": "kit"
2327
- },
2328
- {
2329
- "path": ".claude/skills/zoom-out/THIRD-PARTY-NOTICES.md",
2330
- "kind": "skill",
2331
- "ownerSkill": "zoom-out",
2332
- "surface": "claude",
2333
- "installRole": "consumer",
2334
- "sha256": "87a6e3845e705204a009b26bab75f09e64d97f2d00e19ccd9ee1da39564dbc69",
2335
- "mode": 420,
2336
- "origin": "kit"
2337
- },
2338
2298
  {
2339
2299
  "path": "assets/memory-templates/meta_decision_layer_choice.md",
2340
2300
  "kind": "template",
@@ -2379,7 +2339,7 @@
2379
2339
  "path": "scripts/board_config.py",
2380
2340
  "kind": "script",
2381
2341
  "installRole": "consumer",
2382
- "sha256": "7fc5250de90ff87a11f169e05bc13b88fac1ef7765a447809ea546c6757bc637",
2342
+ "sha256": "020b44d1df176d44d531e24bd7d01ead25182b287a0316239489475dfeb00aa3",
2383
2343
  "mode": 420,
2384
2344
  "origin": "kit"
2385
2345
  },
@@ -2395,7 +2355,7 @@
2395
2355
  "path": "scripts/board-sync.py",
2396
2356
  "kind": "script",
2397
2357
  "installRole": "consumer",
2398
- "sha256": "98e7d7a9dea198d0ea8bb3fc9f7d63a5cf621c3d263389c140f33e8bec16926c",
2358
+ "sha256": "4cf206abfec87578863c99f11e00c99a2048f4f8776cbcb38a23c69e73e37c16",
2399
2359
  "mode": 493,
2400
2360
  "origin": "kit"
2401
2361
  },
@@ -2579,7 +2539,7 @@
2579
2539
  "path": "scripts/release-parity.mjs",
2580
2540
  "kind": "script",
2581
2541
  "installRole": "consumer",
2582
- "sha256": "faa4a1d1116d58f69c8541a586ebd6076ebc3f7af7809e9e4a53928f49298365",
2542
+ "sha256": "32f61bf776974be63a017e90f428d4dab6098a9e2ba0c25c865d18ea3bd3e89e",
2583
2543
  "mode": 420,
2584
2544
  "origin": "kit"
2585
2545
  },
@@ -61,6 +61,9 @@ The workflow board is [Agent Workflow Kit](https://github.com/users/iKon85/proje
61
61
  "headings": {
62
62
  "vorBau": "Clarify Before Build"
63
63
  },
64
+ "titles": {
65
+ "wavePrefix": "Welle"
66
+ },
64
67
  "wrapup": {
65
68
  "remoteBranchSweep": false
66
69
  }
@@ -0,0 +1,259 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>agent-workflow-kit — Workflow</title>
7
+ <style>
8
+ :root {
9
+ --page-bg: #F5F1E8;
10
+ --surface: #FAFAF7;
11
+ --ink: #1F2532;
12
+ --muted: #5B6579;
13
+ --border: #E8E4D8;
14
+ --accent-deep: #5C5630;
15
+
16
+ --plan: #4A6280; --plan-bg: #DDE3EC; --plan-text: #2F4258;
17
+ --exec: #5C7A4A; --exec-bg: #E8EDE0; --exec-text: #3D5230;
18
+ --land: #C2902B; --land-bg: #F2E5C7; --land-text: #7A5B1D;
19
+ --learn: #8A95A6; --learn-bg: #E8EBF0; --learn-text: #5B6579;
20
+ --gate: #A8432D; --gate-bg: #F2DAD0; --gate-text: #6E2B1D;
21
+ --door: #5B6579; --door-bg: #EFEDE4; --door-text: #454E60;
22
+ }
23
+ @media (prefers-color-scheme: dark) {
24
+ :root {
25
+ --page-bg:#1F2532; --surface:#2E3548; --ink:#F5F1E8; --muted:#A6B0BF;
26
+ --border:rgba(255,255,255,0.14); --accent-deep:#B5A86B;
27
+ --plan:#7C9BB2; --plan-bg:#2E3850; --plan-text:#8FA9C4;
28
+ --exec:#82A36C; --exec-bg:#2A3A26; --exec-text:#94B57E;
29
+ --land:#DBAC4F; --land-bg:#3A3220; --land-text:#DBAC4F;
30
+ --learn:#8A95A6; --learn-bg:#2A3142; --learn-text:#9CA6B5;
31
+ --gate:#D9554F; --gate-bg:#3A2620; --gate-text:#EC8780;
32
+ --door:#8A95A6; --door-bg:#272E3E; --door-text:#A6B0BF;
33
+ }
34
+ }
35
+ :root[data-theme="dark"] {
36
+ --page-bg:#1F2532; --surface:#2E3548; --ink:#F5F1E8; --muted:#A6B0BF;
37
+ --border:rgba(255,255,255,0.14); --accent-deep:#B5A86B;
38
+ --plan:#7C9BB2; --plan-bg:#2E3850; --plan-text:#8FA9C4;
39
+ --exec:#82A36C; --exec-bg:#2A3A26; --exec-text:#94B57E;
40
+ --land:#DBAC4F; --land-bg:#3A3220; --land-text:#DBAC4F;
41
+ --learn:#8A95A6; --learn-bg:#2A3142; --learn-text:#9CA6B5;
42
+ --gate:#D9554F; --gate-bg:#3A2620; --gate-text:#EC8780;
43
+ --door:#8A95A6; --door-bg:#272E3E; --door-text:#A6B0BF;
44
+ }
45
+ :root[data-theme="light"] {
46
+ --page-bg:#F5F1E8; --surface:#FAFAF7; --ink:#1F2532; --muted:#5B6579;
47
+ --border:#E8E4D8; --accent-deep:#5C5630;
48
+ --plan:#4A6280; --plan-bg:#DDE3EC; --plan-text:#2F4258;
49
+ --exec:#5C7A4A; --exec-bg:#E8EDE0; --exec-text:#3D5230;
50
+ --land:#C2902B; --land-bg:#F2E5C7; --land-text:#7A5B1D;
51
+ --learn:#8A95A6; --learn-bg:#E8EBF0; --learn-text:#5B6579;
52
+ --gate:#A8432D; --gate-bg:#F2DAD0; --gate-text:#6E2B1D;
53
+ --door:#5B6579; --door-bg:#EFEDE4; --door-text:#454E60;
54
+ }
55
+
56
+ * { box-sizing: border-box; }
57
+ body {
58
+ margin: 0; background: var(--page-bg); color: var(--ink);
59
+ font-family: Charter, "Bitstream Charter", "Iowan Old Style", Georgia, serif;
60
+ font-size: 17px; line-height: 1.6; overflow-x: hidden;
61
+ }
62
+ .wrap { max-width: 1010px; margin: 0 auto; padding: 40px 24px 80px; }
63
+ h1,h3 { font-family: system-ui, "Segoe UI", -apple-system, sans-serif; }
64
+ h1 { font-size: 1.9rem; font-weight: 650; letter-spacing: -0.015em; margin: 0 0 8px; text-wrap: balance; }
65
+ p { margin: 0 0 1em; max-width: 72ch; }
66
+ a { color: var(--accent-deep); }
67
+ .eyebrow {
68
+ font-family: ui-monospace, Menlo, monospace; font-size: 0.72rem;
69
+ letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); margin-bottom: 12px;
70
+ }
71
+ .lede { color: var(--muted); font-size: 1.05rem; max-width: 66ch; }
72
+
73
+ .legend { display: flex; flex-wrap: wrap; gap: 8px 18px; margin: 18px 0 22px; }
74
+ .key { display: flex; align-items: center; gap: 7px; font-family: system-ui, sans-serif; font-size: 0.8rem; color: var(--muted); }
75
+ .dot { width: 11px; height: 11px; border-radius: 3px; background: var(--c); flex: none; }
76
+
77
+ .station {
78
+ font-family: ui-monospace, Menlo, monospace; font-size: 0.73rem;
79
+ background: var(--soft, var(--page-bg)); color: var(--sc, var(--ink));
80
+ border: 1px solid var(--border); border-radius: 5px; padding: 4px 8px; white-space: nowrap;
81
+ }
82
+ .station.plain { --soft: var(--page-bg); --sc: var(--muted); font-style: italic; }
83
+ .st-plan { --soft: var(--plan-bg); --sc: var(--plan-text); border-color: color-mix(in srgb, var(--plan) 40%, var(--border)); }
84
+ .st-exec { --soft: var(--exec-bg); --sc: var(--exec-text); border-color: color-mix(in srgb, var(--exec) 40%, var(--border)); }
85
+ .st-land { --soft: var(--land-bg); --sc: var(--land-text); border-color: color-mix(in srgb, var(--land) 40%, var(--border)); }
86
+ .st-learn { --soft: var(--learn-bg); --sc: var(--learn-text); border-color: color-mix(in srgb, var(--learn) 40%, var(--border)); }
87
+ .st-gate { --soft: var(--gate-bg); --sc: var(--gate-text); border-color: color-mix(in srgb, var(--gate) 50%, var(--border)); font-weight: 600; }
88
+ .st-door { --soft: var(--door-bg); --sc: var(--door-text); border-color: color-mix(in srgb, var(--door) 40%, var(--border)); }
89
+ .arrow { color: var(--muted); font-family: system-ui, sans-serif; font-size: 0.85rem; padding: 0 2px; user-select: none; }
90
+ .decision { font-size: 0.76rem; color: var(--muted); font-style: italic; padding: 0 4px; }
91
+
92
+ .subway { display: grid; grid-template-columns: 158px 1fr; border: 1px solid var(--border);
93
+ border-radius: 8px; background: var(--surface); overflow: hidden; }
94
+ .sw-row { display: contents; }
95
+ .sw-label { padding: 14px 14px; border-bottom: 1px solid var(--border); border-right: 1px solid var(--border);
96
+ font-family: system-ui, sans-serif; font-size: 0.8rem; font-weight: 650; color: var(--lc, var(--muted));
97
+ display: flex; flex-direction: column; gap: 2px; justify-content: center; }
98
+ .sw-label small { font-weight: 400; color: var(--muted); font-size: 0.72rem; }
99
+ .sw-track { padding: 13px 16px; border-bottom: 1px solid var(--border); display: flex; flex-wrap: wrap;
100
+ align-items: center; gap: 7px 4px; background: linear-gradient(to right, color-mix(in srgb, var(--lbg, transparent) 45%, transparent), transparent 55%); }
101
+ .sw-row:last-of-type .sw-label, .sw-row:last-of-type .sw-track { border-bottom: none; }
102
+
103
+ .inter { grid-column: 1 / -1; padding: 12px 16px 14px; border-top: 1px dashed var(--border); }
104
+ .inter .i-title { font-family: system-ui, sans-serif; font-weight: 650; font-size: 0.82rem; color: var(--muted); margin-bottom: 8px; }
105
+ .inter .i-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 4px; font-size: 0.85rem; color: var(--muted); margin-bottom: 7px; }
106
+ .inter .i-line:last-child { margin-bottom: 0; }
107
+
108
+ footer { margin-top: 28px; font-size: 0.85rem; color: var(--muted); font-family: system-ui, sans-serif; }
109
+
110
+ @media (max-width: 640px) {
111
+ .subway { grid-template-columns: 1fr; }
112
+ .sw-label { border-right: none; padding-bottom: 4px; }
113
+ .sw-track { border-bottom: 1px solid var(--border); }
114
+ }
115
+ </style>
116
+ </head>
117
+ <body>
118
+ <div class="wrap">
119
+ <div class="eyebrow">agent-workflow-kit · workflow</div>
120
+ <h1>One loop, many doors</h1>
121
+ <p class="lede">Every piece of work — idea, plan, backlog, bug — boards its own line, and every line converges on the same spine: <em>implement → wrapup → retro</em>. implement drives tdd internally — red → green → refactor at pre-agreed seams, code-review before landing.</p>
122
+
123
+ <div class="legend">
124
+ <span class="key"><span class="dot" style="--c: var(--door)"></span> Door / input</span>
125
+ <span class="key"><span class="dot" style="--c: var(--plan)"></span> Plan</span>
126
+ <span class="key"><span class="dot" style="--c: var(--exec)"></span> Execute</span>
127
+ <span class="key"><span class="dot" style="--c: var(--land)"></span> Land</span>
128
+ <span class="key"><span class="dot" style="--c: var(--learn)"></span> Learn</span>
129
+ <span class="key"><span class="dot" style="--c: var(--gate)"></span> Gate</span>
130
+ </div>
131
+
132
+ <div class="subway">
133
+ <div class="sw-row">
134
+ <div class="sw-label" style="--lc: var(--door-text); --lbg: var(--door-bg);">router<small>size unclear? start here</small></div>
135
+ <div class="sw-track" style="--lbg: var(--door-bg);">
136
+ <span class="station st-plan">scale-check</span>
137
+ <span class="decision">3–6 questions</span><span class="arrow">→</span>
138
+ <span class="station plain">Program → program line</span><span class="arrow">/</span>
139
+ <span class="station plain">Feature → main line</span><span class="arrow">/</span>
140
+ <span class="station plain">Direct-Slice → implement</span><span class="arrow">/</span>
141
+ <span class="station plain">Bug → repair line</span>
142
+ </div>
143
+ </div>
144
+ <div class="sw-row">
145
+ <div class="sw-label" style="--lc: var(--plan-text); --lbg: var(--plan-bg);">main line<small>idea → shipped</small></div>
146
+ <div class="sw-track" style="--lbg: var(--plan-bg);">
147
+ <span class="station plain">idea / plan / issue</span><span class="arrow">→</span>
148
+ <span class="station st-gate">grill? — see grill line</span><span class="arrow">→</span>
149
+ <span class="station st-plan">to-prd</span><span class="arrow">→</span>
150
+ <span class="station st-plan">to-issues</span><span class="arrow">→</span>
151
+ <span class="station st-gate">gate</span><span class="arrow">→</span>
152
+ <span class="station st-exec">implement</span>
153
+ <span class="decision">drives tdd inside</span><span class="arrow">→</span>
154
+ <span class="station st-land">wrapup</span><span class="arrow">→</span>
155
+ <span class="station st-learn">retro</span>
156
+ </div>
157
+ </div>
158
+ <div class="sw-row">
159
+ <div class="sw-label" style="--lc: var(--gate-text); --lbg: var(--gate-bg);">grill line<small>as deep as the stakes</small></div>
160
+ <div class="sw-track" style="--lbg: var(--gate-bg);">
161
+ <span class="station plain">mechanical change → none</span><span class="arrow">/</span>
162
+ <span class="station st-gate">grill-me</span>
163
+ <span class="decision">relentless interview, one question at a time</span><span class="arrow">/</span>
164
+ <span class="station st-gate">grill-with-docs</span>
165
+ <span class="decision">+ CONTEXT.md, ADRs updated inline</span><span class="arrow">/</span>
166
+ <span class="station st-gate">grill-me-codex · grill-with-docs-codex</span>
167
+ <span class="decision">+ Act 2: Codex reviews PLAN.md adversarially until APPROVED</span>
168
+ </div>
169
+ </div>
170
+ <div class="sw-row">
171
+ <div class="sw-label" style="--lc: var(--gate-text); --lbg: var(--gate-bg);">codex line<small>second model, high stakes</small></div>
172
+ <div class="sw-track" style="--lbg: var(--gate-bg);">
173
+ <span class="station st-gate">codex-review</span>
174
+ <span class="decision">plan already exists — cross-model review only</span><span class="arrow">·</span>
175
+ <span class="station st-exec">codex-build</span>
176
+ <span class="decision">Act 3: Codex implements the frozen spec, Claude verifies diff + proof</span>
177
+ </div>
178
+ </div>
179
+ <div class="sw-row">
180
+ <div class="sw-label" style="--lc: var(--plan-text); --lbg: var(--plan-bg);">program line<small>too big for one wave</small></div>
181
+ <div class="sw-track" style="--lbg: var(--plan-bg);">
182
+ <span class="station st-plan">scale-check</span><span class="arrow">→</span>
183
+ <span class="station st-gate">grill</span><span class="arrow">→</span>
184
+ <span class="station st-plan">to-waves</span>
185
+ <span class="decision">preview gate · zero board writes until yes</span><span class="arrow">→</span>
186
+ <span class="station st-exec">orchestrate-wave</span>
187
+ <span class="decision">per wave, often AFK</span><span class="arrow">→</span>
188
+ <span class="station st-land">wrapup</span><span class="arrow">→</span>
189
+ <span class="station st-learn">retro</span>
190
+ </div>
191
+ </div>
192
+ <div class="sw-row">
193
+ <div class="sw-label" style="--lc: var(--plan-text); --lbg: var(--plan-bg);">board line<small>it grew bottom-up</small></div>
194
+ <div class="sw-track" style="--lbg: var(--plan-bg);">
195
+ <span class="station plain">loose backlog</span><span class="arrow">→</span>
196
+ <span class="station st-plan">board-to-waves</span>
197
+ <span class="decision">wave-stub candidates, no number yet</span><span class="arrow">→</span>
198
+ <span class="station st-plan">to-prd</span>
199
+ <span class="decision">matures the stub</span><span class="arrow">→</span>
200
+ <span class="station st-plan">to-issues</span>
201
+ <span class="decision">≥2 slices → promotes to a numbered wave anchor</span><span class="arrow">→</span>
202
+ <span class="station plain">joins the program line</span>
203
+ </div>
204
+ </div>
205
+ <div class="sw-row">
206
+ <div class="sw-label" style="--lc: var(--gate-text); --lbg: var(--gate-bg);">repair line<small>something's broken</small></div>
207
+ <div class="sw-track" style="--lbg: var(--gate-bg);">
208
+ <span class="station plain">bug / anomaly</span><span class="arrow">→</span>
209
+ <span class="station st-door">diagnose</span>
210
+ <span class="decision">reproduce → isolate → fix → prove</span><span class="arrow">→</span>
211
+ <span class="station st-exec">implement</span><span class="arrow">→</span>
212
+ <span class="station st-land">wrapup</span>
213
+ </div>
214
+ </div>
215
+ <div class="sw-row">
216
+ <div class="sw-label" style="--lc: var(--exec-text); --lbg: var(--exec-bg);">structure line<small>code fights back</small></div>
217
+ <div class="sw-track" style="--lbg: var(--exec-bg);">
218
+ <span class="station plain">change fighting the codebase</span><span class="arrow">→</span>
219
+ <span class="station st-door">improve-codebase-architecture</span><span class="arrow">→</span>
220
+ <span class="station st-plan">to-issues</span>
221
+ <span class="decision">plan a slice — or refactor in place</span>
222
+ </div>
223
+ </div>
224
+ <div class="sw-row">
225
+ <div class="sw-label" style="--lc: var(--learn-text); --lbg: var(--learn-bg);">learn line<small>retro's real output</small></div>
226
+ <div class="sw-track" style="--lbg: var(--learn-bg);">
227
+ <span class="station st-learn">retro</span>
228
+ <span class="decision">user + agent friction, symmetric</span><span class="arrow">→</span>
229
+ <span class="station st-gate">CLAUDE.md rule</span><span class="arrow">/</span>
230
+ <span class="station st-land">spec-self-critique layer · ADR</span><span class="arrow">/</span>
231
+ <span class="station st-learn">memory</span><span class="arrow">/</span>
232
+ <span class="station st-plan">board issue</span>
233
+ <span class="decision">per-patch approval · Meta section in PR body</span>
234
+ </div>
235
+ </div>
236
+
237
+ <div class="inter">
238
+ <div class="i-title">Interchanges</div>
239
+ <div class="i-line">a slice hinging on an unknown inserts <span class="station st-gate">verify-spike / decision-gate</span> before its build</div>
240
+ <div class="i-line">unclear path inserts <span class="station st-exec">prototype</span> — throwaway that answers the design question first</div>
241
+ <div class="i-line">inside implement, <span class="station st-exec">tdd</span> runs red → green → refactor at pre-agreed seams</div>
242
+ <div class="i-line">before every PR, <span class="station st-land">local-ci</span> runs the full gate; the <span class="station st-gate">pre-commit / pre-push hooks</span> fire automatically once installed</div>
243
+ <div class="i-line">lost? <span class="station st-door">ask-matt</span> routes you to the right skill</div>
244
+ <div class="i-line">one-time setup, off-map: <span class="station st-door">setup-workflow</span> <span class="station st-door">git-guardrails</span> <span class="station st-door">setup-pre-commit</span></div>
245
+ </div>
246
+ </div>
247
+
248
+ <footer>
249
+ How waves, programs, and slices relate — the altitude model — lives on the
250
+ <a href="methodology.html">methodology page</a>.
251
+ </footer>
252
+ </div>
253
+ <script>
254
+ // Capture helper: ?theme=dark|light pins the palette for PNG rendering.
255
+ var t = new URLSearchParams(location.search).get("theme");
256
+ if (t === "dark" || t === "light") document.documentElement.dataset.theme = t;
257
+ </script>
258
+ </body>
259
+ </html>
package/docs/workflow.png CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikon85/agent-workflow-kit",
3
- "version": "0.26.2",
3
+ "version": "0.27.1",
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": {
@@ -35,7 +35,7 @@ from typing import Optional
35
35
  sys.path.insert(0, str(Path(__file__).resolve().parent))
36
36
  from board_config import ( # noqa: E402
37
37
  ConfigError, load_board_config, phase_field_id, program_type_label,
38
- status_roles, STATUS_ROLE_KEYS,
38
+ status_roles, wave_title_prefix, STATUS_ROLE_KEYS,
39
39
  )
40
40
  # stamp-batch / field-value / promote-guards — pure logic, see module docstring.
41
41
  from board_fields import ( # noqa: E402
@@ -414,9 +414,14 @@ def promote_label_args(issue: int, strip: Optional[list[str]] = None) -> list[st
414
414
  return args
415
415
 
416
416
 
417
- # Existing `Welle <N> ` prefix (any number, em-dash/en-dash/hyphen separator) —
418
- # replaced on re-promote so the wave number never doubles up.
419
- _WAVE_PREFIX_RE = re.compile(r"^\s*Welle\s+\d+\s*[—–-]\s*", re.IGNORECASE)
417
+ # The wave-anchor title prefix word comes from the profile (`titles.wavePrefix`,
418
+ # literal default "Welle" see board_config.wave_title_prefix).
419
+ WAVE_TITLE_PREFIX = wave_title_prefix(_CFG)
420
+ # Existing `<prefix> <N> — ` prefix (any number, em-dash/en-dash/hyphen
421
+ # separator) — replaced on re-promote so the wave number never doubles up.
422
+ _WAVE_PREFIX_RE = re.compile(
423
+ rf"^\s*{re.escape(WAVE_TITLE_PREFIX)}\s+\d+\s*[—–-]\s*", re.IGNORECASE
424
+ )
420
425
  # Leading conventional-commit token (`fix:`, `feat(ui):`, `chore!:`) — anchors
421
426
  # don't carry these, so strip it when present. Matches only a lowercase token,
422
427
  # so prose titles like "Supabase-Residuen entfernen: …" are left intact.
@@ -424,18 +429,18 @@ _CONVENTIONAL_PREFIX_RE = re.compile(r"^[a-z]+(\([^)]*\))?!?:\s*")
424
429
 
425
430
 
426
431
  def wave_title(current: str, wave: int) -> str:
427
- """Title for a wave anchor: `Welle <N> — <Thema>`.
432
+ """Title for a wave anchor: `<prefix> <N> — <topic>`.
428
433
 
429
434
  Strips a leading conventional-commit prefix FIRST, then any existing
430
- `Welle X — ` prefix underneath it (idempotent re-promote), then re-prefixes
431
- with the given wave. Order matters: a title like `fix: Welle 7 — X` only
432
- reveals its `Welle 7 — ` prefix once the conventional prefix is gone
433
- stripping Wave first leaves it intact and doubles up into
434
- `Welle 29 — Welle 7 — X`.
435
+ `<prefix> X — ` prefix underneath it (idempotent re-promote), then
436
+ re-prefixes with the given wave. Order matters: a title like
437
+ `fix: <prefix> 7 — X` only reveals its wave prefix once the conventional
438
+ prefix is gone — stripping Wave first leaves it intact and doubles up into
439
+ `<prefix> 29 — <prefix> 7 — X`.
435
440
  """
436
441
  thema = _CONVENTIONAL_PREFIX_RE.sub("", current)
437
442
  thema = _WAVE_PREFIX_RE.sub("", thema).strip()
438
- return f"Welle {wave} — {thema}"
443
+ return f"{WAVE_TITLE_PREFIX} {wave} — {thema}"
439
444
 
440
445
 
441
446
  def title_edit_args(issue: int, title: str) -> list[str]:
@@ -770,7 +775,7 @@ def cmd_promote(args) -> int:
770
775
  if args.dry_run:
771
776
  _print_dry(promote_label_args(args.issue))
772
777
  if rename:
773
- print(f"[dry-run] gh issue edit {args.issue} --title 'Welle {args.wave} — <Thema>'")
778
+ print(f"[dry-run] gh issue edit {args.issue} --title '{WAVE_TITLE_PREFIX} {args.wave} — <topic>'")
774
779
  _add_and_stamp(url, args.wave, args.status, None, None, None, dry_run=True)
775
780
  return 0
776
781
  labels = _issue_type_labels(args.issue)
@@ -1158,7 +1163,7 @@ def build_parser() -> argparse.ArgumentParser:
1158
1163
  help="explicit Wave (read `next-wave` first — no in-promote race)")
1159
1164
  _add_status_flags(pr)
1160
1165
  pr.add_argument("--no-rename", action="store_true",
1161
- help="keep the title as-is (skip the `Welle <N> — ` prefix)")
1166
+ help="keep the title as-is (skip the wave title prefix)")
1162
1167
  pr.add_argument("--dry-run", action="store_true")
1163
1168
  pr.set_defaults(func=cmd_promote)
1164
1169
 
@@ -126,6 +126,13 @@ def program_type_label(cfg: dict) -> str:
126
126
  return cfg.get("labels", {}).get("programType", "type:program")
127
127
 
128
128
 
129
+ def wave_title_prefix(cfg: dict) -> str:
130
+ """The `titles.wavePrefix` word that opens a wave anchor's title
131
+ (`<prefix> <N> — <topic>`), or the literal default `"Welle"` when the
132
+ profile hasn't set one — existing boards keep their titles unchanged."""
133
+ return cfg.get("titles", {}).get("wavePrefix", "Welle")
134
+
135
+
129
136
  # The semantic role keys of the workflow — schema, not
130
137
  # vocabulary; stable once shipped, extend additively, never rename. Single
131
138
  # home so CLI `choices`, docs, and future consumers reference one list.
@@ -50,3 +50,37 @@ export function assertReleaseParity(identities) {
50
50
  }
51
51
  return identities.local;
52
52
  }
53
+
54
+ // Consumer-side identity: an unpacked installation cannot be re-packed
55
+ // byte-identically (npm normalizes at publish/unpack), so the installed copy
56
+ // proves itself by content — never by a fresh tarball hash.
57
+ const INSTALLED_FIELDS = ['name', 'version', 'manifestSha256'];
58
+
59
+ export async function installedIdentityFromDir(kitRoot) {
60
+ const packageJson = JSON.parse(await readFile(`${kitRoot}/package.json`, 'utf8'));
61
+ const manifest = await readFile(`${kitRoot}/agent-workflow-kit.package.json`);
62
+ if (packageJson.version !== JSON.parse(manifest).kitVersion) {
63
+ throw new Error('installed package and manifest versions mismatch');
64
+ }
65
+ return {
66
+ name: packageJson.name,
67
+ version: packageJson.version,
68
+ manifestSha256: digest('sha256', manifest),
69
+ };
70
+ }
71
+
72
+ export function assertConsumerReleaseParity({ installed, npm, github }) {
73
+ validate('npm', npm);
74
+ validate('github', github);
75
+ for (const field of FIELDS) {
76
+ if (github[field] !== npm[field]) throw new Error(`github ${field} mismatch`);
77
+ }
78
+ if (!installed || typeof installed !== 'object') throw new Error('installed release identity missing');
79
+ for (const field of INSTALLED_FIELDS) {
80
+ if (typeof installed[field] !== 'string' || !installed[field]) {
81
+ throw new Error(`installed ${field} missing`);
82
+ }
83
+ if (installed[field] !== npm[field]) throw new Error(`installed ${field} mismatch`);
84
+ }
85
+ return npm;
86
+ }
@@ -51,3 +51,40 @@ test('a package tarball produces a deterministic content identity', async () =>
51
51
  assert.deepEqual(first, second);
52
52
  } finally { await rm(root, { recursive: true, force: true }); }
53
53
  });
54
+
55
+ test('consumer parity proves npm↔github and matches the installed copy without re-packing', async () => {
56
+ const { assertConsumerReleaseParity } = await import('./release-parity.mjs');
57
+ const installed = { name: identity.name, version: identity.version, manifestSha256: identity.manifestSha256 };
58
+ assert.deepEqual(
59
+ assertConsumerReleaseParity({ installed, npm: { ...identity }, github: { ...identity } }),
60
+ identity,
61
+ );
62
+ assert.throws(() => assertConsumerReleaseParity({
63
+ installed, npm: { ...identity }, github: { ...identity, tarballIntegrity: 'different' },
64
+ }), /github tarballIntegrity mismatch/);
65
+ assert.throws(() => assertConsumerReleaseParity({
66
+ installed: { ...installed, manifestSha256: 'different' },
67
+ npm: { ...identity }, github: { ...identity },
68
+ }), /installed manifestSha256 mismatch/);
69
+ assert.throws(() => assertConsumerReleaseParity({
70
+ installed: { ...installed, version: '9.9.9' },
71
+ npm: { ...identity }, github: { ...identity },
72
+ }), /installed version mismatch/);
73
+ });
74
+
75
+ test('an installed kit directory yields a content identity without npm pack', async () => {
76
+ const { installedIdentityFromDir } = await import('./release-parity.mjs');
77
+ const root = await mkdtemp(join(tmpdir(), 'release-parity-installed-'));
78
+ try {
79
+ const manifest = JSON.stringify({ kitVersion: '1.2.3', files: [] });
80
+ await writeFile(join(root, 'package.json'), JSON.stringify({ name: identity.name, version: '1.2.3' }));
81
+ await writeFile(join(root, 'agent-workflow-kit.package.json'), manifest);
82
+ const installed = await installedIdentityFromDir(root);
83
+ assert.equal(installed.name, identity.name);
84
+ assert.equal(installed.version, '1.2.3');
85
+ assert.equal(typeof installed.manifestSha256, 'string');
86
+ assert.equal('tarballIntegrity' in installed, false);
87
+ } finally {
88
+ await rm(root, { recursive: true, force: true });
89
+ }
90
+ });
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env python3
2
+ """Wave anchor titles take their prefix from the board profile, not a hardcoded
3
+ German literal — an optional `titles.wavePrefix` key with the literal default
4
+ `"Welle"` so every existing profile keeps working unchanged."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import copy
9
+ import importlib.util
10
+ import json
11
+ import os
12
+ import tempfile
13
+ import unittest
14
+ from pathlib import Path
15
+
16
+
17
+ BASE_PROFILE = {
18
+ "repo": "example/kit",
19
+ "project": {"number": 1, "owner": "example", "nodeId": "PVT_test"},
20
+ "fields": {
21
+ "status": {"id": "STATUS", "options": {}},
22
+ "wave": "WAVE",
23
+ "cluster": "CLUSTER",
24
+ "specPath": "SPEC",
25
+ "planPath": "PLAN",
26
+ },
27
+ "labels": {
28
+ "readyForAgent": "ready-for-agent",
29
+ "typePrefix": "type:",
30
+ "clusterType": "type:cluster",
31
+ "waveStub": "wave-stub",
32
+ },
33
+ "branchPrefixes": ["feat", "fix"],
34
+ "prMarkers": {"partOf": "Part of", "retroMarker": "Retro", "retroValues": []},
35
+ "headings": {"vorBau": "Clarify Before Build"},
36
+ }
37
+
38
+
39
+ def load_board_sync(profile_dict: dict, module_name: str):
40
+ with tempfile.NamedTemporaryFile("w", suffix=".md", delete=False) as profile:
41
+ profile.write("<!-- board-sync:profile -->\n```json\n")
42
+ json.dump(profile_dict, profile)
43
+ profile.write("\n```\n")
44
+ profile_path = profile.name
45
+ os.environ["BOARD_SYNC_PROFILE"] = profile_path
46
+ spec = importlib.util.spec_from_file_location(
47
+ module_name, Path(__file__).with_name("board-sync.py")
48
+ )
49
+ module = importlib.util.module_from_spec(spec)
50
+ assert spec.loader is not None
51
+ spec.loader.exec_module(module)
52
+ Path(profile_path).unlink()
53
+ return module
54
+
55
+
56
+ class WaveTitleDefaultPrefix(unittest.TestCase):
57
+ """A profile without `titles.wavePrefix` keeps the historical `Welle` prefix."""
58
+
59
+ @classmethod
60
+ def setUpClass(cls):
61
+ cls.bs = load_board_sync(copy.deepcopy(BASE_PROFILE), "board_sync_wave_default")
62
+
63
+ def test_default_prefix_is_welle(self):
64
+ self.assertEqual(self.bs.wave_title("Auth hardening", 7), "Welle 7 — Auth hardening")
65
+
66
+ def test_repromote_is_idempotent(self):
67
+ self.assertEqual(
68
+ self.bs.wave_title("Welle 7 — Auth hardening", 9),
69
+ "Welle 9 — Auth hardening",
70
+ )
71
+
72
+ def test_conventional_prefix_stripped_before_wave(self):
73
+ self.assertEqual(
74
+ self.bs.wave_title("fix: Welle 7 — Auth hardening", 29),
75
+ "Welle 29 — Auth hardening",
76
+ )
77
+
78
+
79
+ class WaveTitleProfilePrefix(unittest.TestCase):
80
+ """A profile with `titles.wavePrefix` drives the anchor title language."""
81
+
82
+ @classmethod
83
+ def setUpClass(cls):
84
+ profile = copy.deepcopy(BASE_PROFILE)
85
+ profile["titles"] = {"wavePrefix": "Wave"}
86
+ cls.bs = load_board_sync(profile, "board_sync_wave_custom")
87
+
88
+ def test_configured_prefix_is_used(self):
89
+ self.assertEqual(self.bs.wave_title("Auth hardening", 7), "Wave 7 — Auth hardening")
90
+
91
+ def test_repromote_is_idempotent_for_configured_prefix(self):
92
+ self.assertEqual(
93
+ self.bs.wave_title("Wave 7 — Auth hardening", 9),
94
+ "Wave 9 — Auth hardening",
95
+ )
96
+
97
+ def test_regex_metacharacters_in_prefix_are_literal(self):
98
+ profile = copy.deepcopy(BASE_PROFILE)
99
+ profile["titles"] = {"wavePrefix": "W+A"}
100
+ bs = load_board_sync(profile, "board_sync_wave_meta")
101
+ self.assertEqual(
102
+ bs.wave_title("W+A 3 — Auth hardening", 4),
103
+ "W+A 4 — Auth hardening",
104
+ )
105
+
106
+
107
+ if __name__ == "__main__":
108
+ unittest.main()
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env python3
2
+ """The shipped pre-commit template must sanitize git's hook environment.
3
+
4
+ Git exports GIT_DIR/GIT_INDEX_FILE to hooks; a consumer gate that runs a test
5
+ suite whose child tests call git in their own temp repos would otherwise
6
+ redirect those calls onto the host repo's index (PR #121's incident)."""
7
+
8
+ from __future__ import annotations
9
+
10
+ import unittest
11
+ from pathlib import Path
12
+
13
+ TEMPLATE = (
14
+ Path(__file__).resolve().parent.parent
15
+ / ".claude/skills/setup-pre-commit/scripts/pre-commit.template.sh"
16
+ )
17
+
18
+
19
+ class PreCommitTemplateSanitizesGitEnv(unittest.TestCase):
20
+ def test_template_unsets_git_env_before_gate(self):
21
+ text = TEMPLATE.read_text(encoding="utf-8")
22
+ unset_pos = text.find("unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_PREFIX")
23
+ gate_pos = text.find(">>> your gate commands here <<<")
24
+ self.assertGreater(unset_pos, -1, "template must unset git's exported hook env")
25
+ self.assertGreater(gate_pos, -1, "template must keep the gate marker")
26
+ self.assertLess(unset_pos, gate_pos, "sanitize must happen before the gate runs")
27
+
28
+
29
+ if __name__ == "__main__":
30
+ unittest.main()
package/src/cli.mjs CHANGED
@@ -7,6 +7,7 @@ import { update } from './commands/update.mjs';
7
7
  import { diff } from './commands/diff.mjs';
8
8
  import { uninstall } from './commands/uninstall.mjs';
9
9
  import { createCommandAdapter } from '../scripts/release-state.mjs';
10
+ import { installedIdentityFromDir } from '../scripts/release-parity.mjs';
10
11
 
11
12
  const KIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
12
13
  const consumerRoot = process.cwd();
@@ -87,8 +88,10 @@ async function readUpdateRelease() {
87
88
  env: { ...process.env, GH_REPO: 'iKon85/agent-workflow-kit' },
88
89
  });
89
90
  try {
90
- const local = (await adapter.local()).identity;
91
- return { local, npm: await adapter.npm(local), github: await adapter.github(local) };
91
+ // Re-packing an unpacked install is never byte-identical to the registry
92
+ // tarball the installed copy proves itself by content identity instead.
93
+ const installed = await installedIdentityFromDir(KIT_ROOT);
94
+ return { installed, npm: await adapter.npm(installed), github: await adapter.github(installed) };
92
95
  } finally {
93
96
  await adapter.dispose();
94
97
  }
@@ -1,6 +1,6 @@
1
1
  import { readFile, rm } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- import { assertReleaseParity } from '../../scripts/release-parity.mjs';
3
+ import { assertConsumerReleaseParity } from '../../scripts/release-parity.mjs';
4
4
  import { activateCandidate, stageConsumer, verifyCandidate } from '../lib/updateCandidate.mjs';
5
5
  import { reconcile } from '../lib/updateReconcile.mjs';
6
6
  import {
@@ -86,7 +86,7 @@ async function applyTransaction(context) {
86
86
  }
87
87
 
88
88
  function verifyRelease(identities, kitVersion) {
89
- const release = assertReleaseParity(identities);
89
+ const release = assertConsumerReleaseParity(identities);
90
90
  if (release.name !== RELEASE_NAME) throw new Error(`invalid release origin: ${release.name}`);
91
91
  if (release.version !== kitVersion) {
92
92
  throw new Error(`release version ${release.version} does not match kit ${kitVersion}`);
@@ -1,7 +0,0 @@
1
- ---
2
- name: zoom-out
3
- description: "Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture."
4
- disable-model-invocation: true
5
- ---
6
-
7
- I don't know this area of code well. Go up a layer of abstraction. Give me a map of all the relevant modules and callers, using the project's domain glossary vocabulary. (For a full refactor/deepening pass with an HTML report and grilling loop, use `improve-codebase-architecture` instead.)
@@ -1,29 +0,0 @@
1
- # Third-Party Notices
2
-
3
- This skill is adapted from **Matt Pocock's skills**
4
- (https://github.com/mattpocock/skills), `skills/engineering/zoom-out` @ 2bf7005, Copyright (c) 2026 Matt Pocock,
5
- used under the MIT License. Locally adapted (fork-and-own); see PROVENANCE.md.
6
-
7
- ```
8
- MIT License
9
-
10
- Copyright (c) 2026 Matt Pocock
11
-
12
- Permission is hereby granted, free of charge, to any person obtaining a copy
13
- of this software and associated documentation files (the "Software"), to deal
14
- in the Software without restriction, including without limitation the rights
15
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
- copies of the Software, and to permit persons to whom the Software is
17
- furnished to do so, subject to the following conditions:
18
-
19
- The above copyright notice and this permission notice shall be included in all
20
- copies or substantial portions of the Software.
21
-
22
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
- SOFTWARE.
29
- ```
@@ -1,7 +0,0 @@
1
- ---
2
- name: zoom-out
3
- description: Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.
4
- disable-model-invocation: true
5
- ---
6
-
7
- I don't know this area of code well. Go up a layer of abstraction. Give me a map of all the relevant modules and callers, using the project's domain glossary vocabulary. (For a full refactor/deepening pass with an HTML report and grilling loop, use `improve-codebase-architecture` instead.)
@@ -1,29 +0,0 @@
1
- # Third-Party Notices
2
-
3
- This skill is adapted from **Matt Pocock's skills**
4
- (https://github.com/mattpocock/skills), `skills/engineering/zoom-out` @ 2bf7005, Copyright (c) 2026 Matt Pocock,
5
- used under the MIT License. Locally adapted (fork-and-own); see PROVENANCE.md.
6
-
7
- ```
8
- MIT License
9
-
10
- Copyright (c) 2026 Matt Pocock
11
-
12
- Permission is hereby granted, free of charge, to any person obtaining a copy
13
- of this software and associated documentation files (the "Software"), to deal
14
- in the Software without restriction, including without limitation the rights
15
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
- copies of the Software, and to permit persons to whom the Software is
17
- furnished to do so, subject to the following conditions:
18
-
19
- The above copyright notice and this permission notice shall be included in all
20
- copies or substantial portions of the Software.
21
-
22
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
- SOFTWARE.
29
- ```