@ikon85/agent-workflow-kit 0.27.1 → 0.29.0
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/ask-matt/SKILL.md +2 -2
- package/.agents/skills/board-to-waves/SKILL.md +13 -3
- package/.agents/skills/grill-me/SKILL.md +1 -1
- package/.agents/skills/grill-with-docs/SKILL.md +1 -1
- package/.agents/skills/kit-update/SKILL.md +12 -0
- package/.agents/skills/orchestrate-wave/SKILL.md +68 -21
- package/.agents/skills/orchestrate-wave/references/builder-contract.md +6 -4
- package/.agents/skills/retro/SKILL.md +24 -1
- package/.agents/skills/scale-check/SKILL.md +2 -2
- package/.agents/skills/setup-workflow/SKILL.md +44 -1
- package/.agents/skills/setup-workflow/workflow-overview.md +5 -5
- package/.agents/skills/tdd/SKILL.md +68 -14
- package/.agents/skills/to-issues/SKILL.md +104 -29
- package/.agents/skills/to-prd/SKILL.md +13 -3
- package/.agents/skills/to-waves/SKILL.md +16 -4
- package/.agents/skills/verify-spike/SKILL.md +1 -1
- package/.agents/skills/wrapup/SKILL.md +14 -3
- package/.claude/hooks/drift-guard.py +5 -2
- package/.claude/hooks/kit-origin-edit-hint.py +64 -0
- package/.claude/skills/ask-matt/SKILL.md +2 -2
- package/.claude/skills/board-to-waves/SKILL.md +13 -3
- package/.claude/skills/codex-build/SKILL.md +69 -23
- package/.claude/skills/codex-review/SKILL.md +47 -37
- package/.claude/skills/grill-me/SKILL.md +1 -1
- package/.claude/skills/grill-me-codex/SKILL.md +45 -34
- package/.claude/skills/grill-with-docs/SKILL.md +1 -1
- package/.claude/skills/grill-with-docs-codex/SKILL.md +46 -34
- package/.claude/skills/kit-update/SKILL.md +12 -0
- package/.claude/skills/orchestrate-wave/SKILL.md +68 -21
- package/.claude/skills/orchestrate-wave/references/builder-contract.md +6 -4
- package/.claude/skills/retro/SKILL.md +23 -0
- package/.claude/skills/scale-check/SKILL.md +2 -2
- package/.claude/skills/setup-workflow/SKILL.md +44 -1
- package/.claude/skills/setup-workflow/workflow-overview.md +5 -5
- package/.claude/skills/skill-manifest.json +1 -1
- package/.claude/skills/tdd/SKILL.md +68 -14
- package/.claude/skills/to-issues/SKILL.md +104 -29
- package/.claude/skills/to-prd/SKILL.md +13 -3
- package/.claude/skills/to-waves/SKILL.md +16 -4
- package/.claude/skills/verify-spike/SKILL.md +1 -1
- package/.claude/skills/wrapup/SKILL.md +14 -3
- package/README.md +70 -7
- package/agent-workflow-kit.package.json +99 -43
- package/docs/adr/0001-consumer-divergence-policy.md +49 -0
- package/docs/agents/wave-anchor-template.md +1 -1
- package/docs/research/wave-152-consumer-acceptance.md +98 -0
- package/package.json +1 -1
- package/scripts/board-sync.py +187 -12
- package/scripts/build-kit.test.mjs +42 -1
- package/scripts/codex-exec-scenarios/fake-codex.mjs +152 -0
- package/scripts/codex-exec.sh +566 -0
- package/scripts/codex-exec.test.mjs +815 -0
- package/scripts/codex_proc.py +602 -0
- package/scripts/find-by-marker.py +68 -0
- package/scripts/marker_lib.py +88 -0
- package/scripts/pr-body-check.py +34 -6
- package/scripts/pr_body_e2e.py +83 -0
- package/scripts/render-anchor.py +111 -0
- package/scripts/test_board_sync.py +208 -0
- package/scripts/test_census_backstop.py +56 -3
- package/scripts/test_marker_lib.py +154 -0
- package/scripts/test_orchestrate_wave_contract.py +116 -0
- package/scripts/test_pr_body_check.py +245 -0
- package/scripts/test_render_anchor.py +267 -0
- package/scripts/test_retro_wrapup_contract.py +117 -0
- package/scripts/test_skill_codex_exec_lifecycle.py +123 -0
- package/scripts/test_tdd_contract.py +78 -0
- package/src/cli.mjs +44 -8
- package/src/commands/diff.mjs +5 -2
- package/src/commands/init.mjs +12 -0
- package/src/commands/own.mjs +16 -0
- package/src/commands/uninstall.mjs +8 -1
- package/src/commands/update.mjs +37 -9
- package/src/lib/bundle.mjs +14 -0
- package/src/lib/consumerPath.mjs +30 -0
- package/src/lib/manifest.mjs +18 -0
- package/src/lib/ownedDiff.mjs +88 -0
- package/src/lib/updateCandidate.mjs +14 -0
- package/src/lib/updateReconcile.mjs +45 -6
|
@@ -54,7 +54,7 @@ Even when the slices were already cut upstream (a grill/PRD slice table), do NOT
|
|
|
54
54
|
- **Seam ownership check (Fix A):** does any slice **replace/unify/retire a central mechanism**? If yes, it MUST be its own 🧊 grill-needed slice — NOT hidden in a behavior-preserving naming/tweak leaf. A byte-neutral slice does not discharge the seam.
|
|
55
55
|
- **Blast-radius threshold:** for each slice, estimate the blast radius (~N files, from recon/grep — not a guess; workflow slices count SKILL.md + adapter mirrors + tests). **≥ 10 estimated files OR not estimable → check for a split.** If the slice stays deliberately large, it MUST be 🧊 **grill-needed** (HITL) with a "why indivisible" justification **in the issue body** — a guideline, not a hard block, but the deviation lives in the body, not in the agent's head. (Incident: a slice cut too coarse ballooned to 34 prod files at execute-recon, an emergency in-build split — no gate at the cut existed.) (Terminology note: this blast radius is a **slice estimate** — ~N files for this one cut — not a project-owned rollout-completeness census (e.g. an-style, code-derived "X of Y" census over all surfaces of a cross-cutting concept, if your repo runs one).)
|
|
56
56
|
<!-- mirror-xform:start codex-escalation -->
|
|
57
|
-
- **Gate type + sequencing (Retro):** every slice that is **not** a clean AFK build gets a gate tag — 🧭 **Design Grill** (a decision with alternatives, hard-to-reverse, ADR-worthy → `grill-with-docs`), 🔬 **Verify Spike** (a pure fact question, read-only), 📐 **Trade-off/Research** (a concrete trade-off choice OR "needs more research", **below** the grill threshold — read-only research + a documented trade-off in the issue), 📝 **Review Note** (a finding, **not** a build slice). A gate slice (🧭/🔬/📐) is cut as its **own slice**, sequenced **before** its dependent build slice, and blocks it (gate-before-build — published as a **native blocking edge** (§5a step 5), visible in the mirrored "Blocked by" section + table order). "Needs a decision / open call / research gap" while cutting → gate slice, **never** a blind AFK `/
|
|
57
|
+
- **Gate type + sequencing (Retro):** every slice that is **not** a clean AFK build gets a gate tag — 🧭 **Design Grill** (a decision with alternatives, hard-to-reverse, ADR-worthy → `grill-with-docs`), 🔬 **Verify Spike** (a pure fact question, read-only), 📐 **Trade-off/Research** (a concrete trade-off choice OR "needs more research", **below** the grill threshold — read-only research + a documented trade-off in the issue), 📝 **Review Note** (a finding, **not** a build slice). A gate slice (🧭/🔬/📐) is cut as its **own slice**, sequenced **before** its dependent build slice, and blocks it (gate-before-build — published as a **native blocking edge** (§5a step 5), visible in the mirrored "Blocked by" section + table order). "Needs a decision / open call / research gap" while cutting → gate slice, **never** a blind AFK `/implement`. (🔬 **Verify Spike** runs via the `verify-spike` skill — read-only fact question, throwaway harness, verdict as ADR/comment, the throwaway deleted. 📐 **Trade-off/Research** runs via the `decision-gate` skill — options + criteria, read-only research/measurement, a documented trade-off table, a reasoned decision as ADR/comment; throwaway measurement code deleted.)
|
|
58
58
|
<!-- mirror-xform:end -->
|
|
59
59
|
- **🧭-vs.-📐 discriminator:** if the decision replaces/unifies/retires a **central seam**, is a **one-way door** (hard/costly to reverse), or a **schema migration** → 🧭 Design Grill (ADR-worthy, see Seam Ownership above). Otherwise — a **bounded choice between concrete options**, easily revised → 📐 Trade-off/Research.
|
|
60
60
|
- **Absence-before-build (Retro):** a slice cut as "build feature/page/endpoint/flow X" (or a gate "X gap unclear") MUST cite an **existence grep** — *absent* (`grep <pattern> = 0` across route **and** UI **and** repo) OR *partial* (`exists @<file>, gap = <Y>`). A hedge ("current state unclear → verify during build") is **not** a valid slice state: the check is cheap (minutes), so it happens **at the cut**, not deferred into a gate. (Incident: a gate was cut for a feature that greps already showed built — the gap was deferred instead of resolved.)
|
|
@@ -143,33 +143,108 @@ else
|
|
|
143
143
|
WAVE="$CURRENT" # Stufe-1p Program-stub — reuse
|
|
144
144
|
fi
|
|
145
145
|
|
|
146
|
-
# 2.
|
|
147
|
-
# body header `**Welle $WAVE — <
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
146
|
+
# 2. Fill /tmp/anchor-template.md from docs/agents/wave-anchor-template.md (Tier 2):
|
|
147
|
+
# body header `**Welle $WAVE — <Topic>**`, anchor-owned markers at the head,
|
|
148
|
+
# filled Slices table, and the To-Do checklist collapsed to one line. Save the
|
|
149
|
+
# unchanged source PRD body as /tmp/source-prd.md. The renderer reads only those
|
|
150
|
+
# two files and writes only stdout: no GitHub/network call and no mutation.
|
|
151
|
+
python3 scripts/render-anchor.py --template /tmp/anchor-template.md \
|
|
152
|
+
--prd /tmp/source-prd.md --document anchor > /tmp/anchor.md
|
|
153
|
+
python3 scripts/render-anchor.py --template /tmp/anchor-template.md \
|
|
154
|
+
--prd /tmp/source-prd.md --document archive > /tmp/prd-archive.md
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The anchor output is the filled template byte-for-byte. The archive starts with
|
|
158
|
+
the stable `📄 Full PRD` marker and strips every canonical marker line
|
|
159
|
+
(`plan_revision`, `prd-source-id`, `prd-content-fp`, `<!-- prd: … -->`) only
|
|
160
|
+
from the source body's head block. Marker-looking text in a quote, fence, or
|
|
161
|
+
later section is content and remains untouched.
|
|
162
|
+
|
|
163
|
+
Promotion uses the remote issue itself as its journal. Classify four
|
|
164
|
+
observations before every resume. Before transition 1, `S=yes|no` compares a
|
|
165
|
+
freshly fetched remote body byte-for-byte with `/tmp/source-prd.md`, the source
|
|
166
|
+
snapshot that produced both rendered outputs. Once `B=yes`, that source
|
|
167
|
+
snapshot is no longer active and `S=n/a`. `B=yes|no` says whether the fetched
|
|
168
|
+
issue body is byte-identical to `/tmp/anchor.md`.
|
|
169
|
+
`C=0|exact-1|wrong-1|duplicates(<ids>)` classifies stable-marker comments:
|
|
170
|
+
none; exactly one whose whole body is byte-identical to
|
|
171
|
+
`/tmp/prd-archive.md`; exactly one with different bytes; or multiple matches
|
|
172
|
+
with every comment ID retained. `P=absent|complete|partial` classifies only
|
|
173
|
+
promotion-owned board state. `P=absent` accepts either an
|
|
174
|
+
ordinary pre-state (no `type:cluster`, Wave unset, no `Welle` title prefix) or a
|
|
175
|
+
valid Stufe-1p pre-state (`wave-stub` present, no `type:cluster`, and the
|
|
176
|
+
pre-stamped Wave/title match `$WAVE`). `P=complete` requires `type:cluster`, the
|
|
177
|
+
expected Wave/title, and no remaining `wave-stub`. Every other combination —
|
|
178
|
+
including a different Wave — is `P=partial`. Fetch comments with the paginated
|
|
179
|
+
API, never the first page alone:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
gh api --paginate --slurp \
|
|
183
|
+
"repos/<owner>/<repo>/issues/<prd#>/comments?per_page=100"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Flatten every returned page, retain the comment `id`, and exact-match the
|
|
187
|
+
start-of-body marker. The four valid states and their sole resume action are:
|
|
188
|
+
|
|
189
|
+
<!-- promotion-board-observation-table:start -->
|
|
190
|
+
| Scenario | Observable board facts | P classification |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| `ordinary-prestate` | no cluster; Wave unset; no Wave title | `absent` |
|
|
193
|
+
| `stufe-1p-prestate` | wave-stub; no cluster; expected Wave/title | `absent` |
|
|
194
|
+
| `promoted` | cluster; expected Wave/title; no wave-stub | `complete` |
|
|
195
|
+
| `cluster-only` | cluster; Wave/title missing | `partial` |
|
|
196
|
+
| `wrong-wave` | any different Wave value | `partial` |
|
|
197
|
+
<!-- promotion-board-observation-table:end -->
|
|
198
|
+
|
|
199
|
+
<!-- promotion-state-table:start -->
|
|
200
|
+
| State | Observable predicates | Resume action |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| `initial` | `S=yes`, `B=no`, `C=0`, `P=absent` | render + write body |
|
|
203
|
+
| `body-written` | `S=n/a`, `B=yes`, `C=0`, `P=absent` | reconcile + write archive comment |
|
|
204
|
+
| `comment-written` | `S=n/a`, `B=yes`, `C=exact-1`, `P=absent` | promote board state |
|
|
205
|
+
| `promoted` | `S=n/a`, `B=yes`, `C=exact-1`, `P=complete` | no-op; continue publish audit |
|
|
206
|
+
<!-- promotion-state-table:end -->
|
|
207
|
+
|
|
208
|
+
Each transition is idempotent and has an explicit contract:
|
|
209
|
+
|
|
210
|
+
1. **Write body (`initial → body-written`).** Pre: `S=yes`, `B=no`, `C=0`,
|
|
211
|
+
`P=absent`. Fetch the remote body again immediately before the command and
|
|
212
|
+
recompute `S`; a prior fetch is not sufficient.
|
|
213
|
+
Run `gh issue edit <prd#> --body-file /tmp/anchor.md`, refetch the body, and
|
|
214
|
+
require a byte match. Post: `S=n/a`, `B=yes`, `C=0`, `P=absent`.
|
|
215
|
+
2. **Write archive (`body-written → comment-written`).** Pre: `S=n/a`, `B=yes`,
|
|
216
|
+
`C=0`, `P=absent`. Re-run the pagination-aware lookup immediately before
|
|
217
|
+
create; if it now yields `C=exact-1`, classify as `comment-written` and do
|
|
218
|
+
not create. `C=wrong-1` or `C=duplicates(<ids>)` is drift and enters repair,
|
|
219
|
+
never create. Only `C=0` runs `gh issue comment <prd#> --body-file
|
|
220
|
+
/tmp/prd-archive.md`, then immediately paginate and reconcile again. Post:
|
|
221
|
+
`S=n/a`, `B=yes`, `C=exact-1`, `P=absent`.
|
|
222
|
+
3. **Promote (`comment-written → promoted`).** Pre: `S=n/a`, `B=yes`,
|
|
223
|
+
`C=exact-1`, `P=absent`. Run `python3 scripts/board-sync.py promote --issue <prd#> --wave
|
|
224
|
+
"$WAVE"`, refetch body, comments, labels, Wave, and title. Post: `S=n/a`,
|
|
225
|
+
`B=yes`, `C=exact-1`, `P=complete`. Re-running `promote` with the same Wave
|
|
226
|
+
is the repair for `P=partial`; a different Wave remains a hard stop.
|
|
227
|
+
|
|
228
|
+
Any other predicate combination is drift, not a fifth state. Repair it
|
|
229
|
+
explicitly, then reclassify. `S=no` before transition 1 means **STOP**: report
|
|
230
|
+
the diff between the fresh remote body and `/tmp/source-prd.md`, never write the
|
|
231
|
+
stale render, re-fetch the remote body into a reviewed source snapshot, and
|
|
232
|
+
rerender both outputs before recomputing `S`. `B=no` outside the valid `initial` tuple
|
|
233
|
+
means **STOP**, report the body diff against `/tmp/anchor.md`, and require
|
|
234
|
+
an explicit operator decision to restore the rendered anchor or adopt the remote
|
|
235
|
+
edit and rerender both outputs; never overwrite remote journal evidence automatically.
|
|
236
|
+
Only transition 1 performs the approved source-to-anchor body write without
|
|
237
|
+
this drift gate. `C=wrong-1` → report its ID and explicitly update it to the
|
|
238
|
+
rendered archive; `P=complete` with `C=0` → create/reconcile the archive without
|
|
239
|
+
demoting. For `P=partial`, a different Wave is a hard stop; otherwise rerun
|
|
240
|
+
same-Wave `promote`. `C=duplicates(<ids>)` always means **STOP and report every
|
|
241
|
+
comment ID**. An operator must choose and explicitly delete/update the
|
|
242
|
+
duplicates, then rerun the paginated lookup; never select the first match or
|
|
243
|
+
silently discard one. No local operation journal is written: these
|
|
244
|
+
observations are the journal.
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# 3. Continue only from the `promoted` state.
|
|
173
248
|
|
|
174
249
|
# 4. create each child (dependency order), then link it under the anchor — BEFORE the §7 exit audit,
|
|
175
250
|
# so the checker sees the anchor's children (a childless type:cluster anchor mis-reads as a leaf)
|
|
@@ -302,7 +377,7 @@ Reference the blocking ticket(s), or "None - can start immediately".
|
|
|
302
377
|
<!-- mirror-xform:start codex-escalation -->
|
|
303
378
|
```
|
|
304
379
|
Welle <N> · Slice <X> (<closes #x | refs #<prd#>, Parent #<prd#>>). Read #<prd#> for decisions.
|
|
305
|
-
Start skill: 🧭 Design Grill → /grill-with-docs · 🔬 Verify Spike → /verify-spike · 📐 Trade-off/Research → /decision-gate · AFK → /
|
|
380
|
+
Start skill: 🧭 Design Grill → /grill-with-docs · 🔬 Verify Spike → /verify-spike · 📐 Trade-off/Research → /decision-gate · AFK → /implement · HITL → /grill-me → /implement. Recommended model: <Model [Effort]>.
|
|
306
381
|
Worktree: your project's worktree helper, or `git worktree add`
|
|
307
382
|
Scope (<N> files) — REQUIRED FIELD, blast-radius estimate at cut time; the build session checks it against its own recon findings, >2x deviation → STOP:
|
|
308
383
|
- <concrete file + change>
|
|
@@ -49,11 +49,21 @@ gh project item-list 1 --owner <owner> --limit 500 --format json # check targe
|
|
|
49
49
|
- **Mode A — idempotency via two separate markers in the body:**
|
|
50
50
|
- **Stable source identity** `<!-- prd-source-id: <id> -->` — **never** changes across plan content edits (otherwise search-before-create misses the changed re-run → duplicate). **Default rule** for `<id>` (identity ≠ content; set on the **first** to-prd run, **never** changed after — the slug then lives in the issue body and is discoverable via search-before-create): kebab-case slug of the plan topic. Priority: **(1)** explicitly passed ID / durable issue number → **(2)** existing slug from a prior run (found via search-before-create) → **(3)** new kebab-case slug from the plan/title topic. The `PLAN.md` path is only a **secondary hint** (not stable across worktrees; external specs have none), **never** the identity itself.
|
|
51
51
|
- **Separate content fingerprint** `<!-- prd-content-fp: <hash> -->` — only for diff/audit/bump decisions, **not** for identity.
|
|
52
|
-
- **search-before-create:**
|
|
52
|
+
- **search-before-create:** use the shared all-state exact-marker lookup (it uses
|
|
53
|
+
`gh api --paginate`, discards REST pull-request items, and never relies on
|
|
54
|
+
GitHub Search or a capped issue list):
|
|
53
55
|
```bash
|
|
54
|
-
|
|
55
|
-
# filter locally on `prd-source-id: <id>` → 1 match ⇒ update; >1 ⇒ STOP+report; 0 ⇒ create
|
|
56
|
+
python3 scripts/find-by-marker.py --kind prd-source-id --slug "<id>"
|
|
56
57
|
```
|
|
58
|
+
Branch on its JSON contract (`count`, `issues[].number`, `issues[].state`,
|
|
59
|
+
`verdict`): `0` / `create` → create; exactly one `open` / `update` → update
|
|
60
|
+
that issue; exactly one `closed` / `user-decision` → ask the user whether to
|
|
61
|
+
reopen, use a new identity, or stop; `>1` / `STOP` → stop and report every
|
|
62
|
+
number/state. Never auto-delete or silently replace a closed/duplicate identity.
|
|
63
|
+
Immediately after a Mode-A create, run the same lookup with
|
|
64
|
+
`--created <new-issue-number>`. Continue only when it returns exactly the
|
|
65
|
+
newly-created open issue; duplicate reconciliation is a loud `STOP` that
|
|
66
|
+
reports both/all numbers for user-decided resolution.
|
|
57
67
|
|
|
58
68
|
## 4. Write the Draft PRD (deliverable)
|
|
59
69
|
|
|
@@ -192,10 +192,22 @@ A re-run of to-waves on the same program is **delta-apply** and doubles as
|
|
|
192
192
|
bottom-up `wave-stub-source` marker; `<prd-source-id>` is the PRD's own source
|
|
193
193
|
slug). These never change across revisions — they are the identity for
|
|
194
194
|
search-before-create.
|
|
195
|
-
- **Delta apply.**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
195
|
+
- **Delta apply.** Resolve every planned identity through the shared all-state,
|
|
196
|
+
exact-marker CLI (it uses `gh api --paginate`, discards REST pull-request
|
|
197
|
+
items, and does not rely on GitHub Search or a capped issue list):
|
|
198
|
+
```bash
|
|
199
|
+
python3 scripts/find-by-marker.py --kind program-stub-source --slug "<prd-source-id>/w<N>"
|
|
200
|
+
python3 scripts/find-by-marker.py --kind program-leaf-source --slug "<prd-source-id>/<local-id>"
|
|
201
|
+
```
|
|
202
|
+
Branch on each JSON result (`count`, `issues[].number`, `issues[].state`,
|
|
203
|
+
`verdict`): `0` / `create` → create; exactly one `open` / `update` → update in
|
|
204
|
+
place; exactly one `closed` / `user-decision` → ask the user whether to reopen,
|
|
205
|
+
use a new identity, or stop; `>1` / `STOP` → stop and report every number/state.
|
|
206
|
+
Never auto-delete or silently replace a closed/duplicate identity. Immediately
|
|
207
|
+
after every create, run the same lookup with `--created <new-issue-number>` and
|
|
208
|
+
continue only when exactly the newly-created open issue is returned; a duplicate
|
|
209
|
+
reconciliation stops loudly with both/all numbers for user-decided resolution.
|
|
210
|
+
A live issue carrying a
|
|
199
211
|
`program-*-source` for this program that the current plan no longer references →
|
|
200
212
|
report as **orphaned** (do not auto-close — closing is the abort convention, §8).
|
|
201
213
|
Cross-check the native children (`children-of <prd#>` and each stub) against the
|
|
@@ -17,7 +17,7 @@ This is the "Verify First" rule with a runnable harness behind it: when you cann
|
|
|
17
17
|
| "Which of these options, given these trade-offs?" / "I need to research this before I can choose." (bounded, sub-grill) | `decision-gate` |
|
|
18
18
|
| "Does this design / state model / UI feel right?" (open-ended) | `prototype` |
|
|
19
19
|
| "Why is this broken / slow?" (root-cause of a known defect) | `diagnose` |
|
|
20
|
-
| "Build this behaviour, test-first." (new feature/fix) | `
|
|
20
|
+
| "Build this behaviour, test-first." (new feature/fix) | `implement` |
|
|
21
21
|
|
|
22
22
|
If the question is really "what should this be", it is not a verify-spike — those are open, not yes/no.
|
|
23
23
|
|
|
@@ -29,9 +29,21 @@ Hard stop (the only pure precondition): not in a feature worktree / on `main`
|
|
|
29
29
|
|
|
30
30
|
### 2 · Retro gate (blocking, optional retro-exit — before anything is committed)
|
|
31
31
|
One reminder, not a merge confirmation:
|
|
32
|
-
> "Already ran a retro? **(a)** yes / continue → landing now. **(b)** you want one first →
|
|
32
|
+
> "Already ran a retro? **(a)** yes / continue → landing now. **(b)** you want one first → the retro starts now; afterwards, invoke `/wrapup` again — repo-file patches then travel in this PR."
|
|
33
33
|
|
|
34
|
-
(b) →
|
|
34
|
+
(b) → **invoke the `retro` skill immediately in this run.** Retro is
|
|
35
|
+
model-invocable and non-deploying, and every mutation still has its own approval
|
|
36
|
+
gate. After retro finishes, **land nothing in this run**. Require a **fresh
|
|
37
|
+
explicit `/wrapup` invocation** because retro may have changed the exact diff
|
|
38
|
+
that the next merge authorization covers.
|
|
39
|
+
|
|
40
|
+
General chaining rule: automatically chain only into a **model-invocable**,
|
|
41
|
+
**non-deploying** workflow. If the named target is user-only, deploys, or
|
|
42
|
+
depends on an external action, **return control to the user** and **state the
|
|
43
|
+
reason**. A forward chain never carries wrapup's merge/deploy authorization
|
|
44
|
+
into another run.
|
|
45
|
+
|
|
46
|
+
The answer materializes as the mandatory `**Retro:**` PR-body line in step 4 — that's recording, not a second question. Why the gate lives here: in a foreign project this is the only portable retro touchpoint; a project-local "offer retro before PR" convention usually answers it already.
|
|
35
47
|
|
|
36
48
|
### 3 · Commit (only if the tree is dirty)
|
|
37
49
|
Judgment first, then the guarded commit:
|
|
@@ -67,5 +79,4 @@ STOP → diagnose in the main conversation, fix, re-run `land` (an already-merge
|
|
|
67
79
|
|
|
68
80
|
## Out of scope
|
|
69
81
|
- Live-verify / DoD: must happen **before** `/wrapup` — this skill lands, it does not verify.
|
|
70
|
-
- `/retro`: offered before landing (step 2), never run by this skill.
|
|
71
82
|
- Other worktrees / their servers stay untouched.
|
|
@@ -48,8 +48,11 @@ GUARD_ACK_RE = re.compile(r"<!--\s*guard-ack:\s*#?\d+\s+r\d+\s+reason:.+\bby-use
|
|
|
48
48
|
# Load the shared checker (hyphenated filename → importlib). Repo root = parents[2].
|
|
49
49
|
_CHECKER_PATH = Path(__file__).resolve().parents[2] / "scripts" / "execute-ready-check.py"
|
|
50
50
|
_CENSUS_MODULE_PATH = Path(__file__).resolve().parents[2] / "scripts" / "census" / "index.mjs"
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
# Production repositories can need more than the former five-second proof and
|
|
52
|
+
# fifteen-second bridge budgets. Keep the default proof comfortably below the
|
|
53
|
+
# outer bridge so a hung repository-local proof still cannot wedge the hook.
|
|
54
|
+
CENSUS_BRIDGE_TIMEOUT_SECONDS = 30
|
|
55
|
+
CENSUS_PROOF_TIMEOUT_MS = 12_000
|
|
53
56
|
|
|
54
57
|
_CENSUS_SCAN = r"""
|
|
55
58
|
import { readFileSync } from 'node:fs';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Advisory PreToolUse hint for kit-origin Edit/Write targets.
|
|
3
|
+
|
|
4
|
+
Covered surface: agent Edit/Write tools only. Shell redirection, formatters, and
|
|
5
|
+
IDE edits are knowingly uncovered; the skills remain the rule's primary
|
|
6
|
+
carrier. This hook never enforces or blocks. It performs a single manifest read,
|
|
7
|
+
does no network work, and fails open silently when input or manifest is unusable.
|
|
8
|
+
"""
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
MANIFEST = "agent-workflow-kit.json"
|
|
14
|
+
TOOLS = {"Edit", "Write"}
|
|
15
|
+
HINT = (
|
|
16
|
+
"This file has kit origin. Before editing, ask: should this change go "
|
|
17
|
+
"upstream to agent-workflow-kit, or should the consumer own this file?"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def manifest_path(file_path: str, root: Path) -> str | None:
|
|
22
|
+
try:
|
|
23
|
+
target = Path(file_path)
|
|
24
|
+
target = target if target.is_absolute() else root / target
|
|
25
|
+
normalized = target.resolve().relative_to(root.resolve()).as_posix()
|
|
26
|
+
except (OSError, ValueError):
|
|
27
|
+
return None
|
|
28
|
+
return normalized or None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_kit_origin(payload: object, root: Path) -> bool:
|
|
32
|
+
if not isinstance(payload, dict) or payload.get("tool_name") not in TOOLS:
|
|
33
|
+
return False
|
|
34
|
+
tool_input = payload.get("tool_input")
|
|
35
|
+
if not isinstance(tool_input, dict) or not isinstance(tool_input.get("file_path"), str):
|
|
36
|
+
return False
|
|
37
|
+
path = manifest_path(tool_input["file_path"], root)
|
|
38
|
+
if path is None:
|
|
39
|
+
return False
|
|
40
|
+
manifest = json.loads((root / MANIFEST).read_text(encoding="utf-8"))
|
|
41
|
+
installed = manifest.get("installed", []) if isinstance(manifest, dict) else []
|
|
42
|
+
return any(
|
|
43
|
+
isinstance(entry, dict) and entry.get("path") == path and entry.get("origin") == "kit"
|
|
44
|
+
for entry in installed
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main() -> int:
|
|
49
|
+
try:
|
|
50
|
+
payload = json.load(sys.stdin)
|
|
51
|
+
if is_kit_origin(payload, Path.cwd()):
|
|
52
|
+
print(json.dumps({
|
|
53
|
+
"hookSpecificOutput": {
|
|
54
|
+
"hookEventName": "PreToolUse",
|
|
55
|
+
"additionalContext": HINT,
|
|
56
|
+
}
|
|
57
|
+
}))
|
|
58
|
+
except (OSError, ValueError, TypeError):
|
|
59
|
+
pass
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
sys.exit(main())
|
|
@@ -53,7 +53,7 @@ Prefer the smallest depth that produces a clear next action.
|
|
|
53
53
|
- **Deep:** `/grill-with-docs` followed by `/to-prd` and `/to-issues` when
|
|
54
54
|
terminology, contracts, rollout order, or ownership are still uncertain.
|
|
55
55
|
- **Medium:** `/to-issues` for a ready artefact that needs slicing.
|
|
56
|
-
- **Light:** direct `/
|
|
56
|
+
- **Light:** direct `/implement` for a small, well-understood change.
|
|
57
57
|
- **Gate:** insert `/verify-spike` or `/decision-gate` before any depth level
|
|
58
58
|
when a slice hinges on an unresolved fact or trade-off.
|
|
59
59
|
|
|
@@ -75,7 +75,7 @@ A starting situation that generates work, then merges onto the main flow.
|
|
|
75
75
|
- **Something's broken** → **`/diagnose`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression between two known-good states. Refuses to theorise until it has a **tight feedback loop** — one command that already goes red on *this* bug — then fixes with a regression test. Its post-mortem hands off to **`/improve-codebase-architecture`** when the finding is that there's no good seam.
|
|
76
76
|
- **A huge, foggy effort too big for one agent session** — a greenfield build or a sprawling feature where the way from here to the destination isn't yet visible, and it's still **pre-spec** (before any grill or PRD) → **`/wayfinder`** (user-invoked). It charts a **shared map** of investigation slices on the tracker and resolves them one at a time — producing **decisions, not deliverables** — until the fog lifts and the route is clear, then merges onto the main flow at **`/to-prd`** (or, if the effort turned out small enough, straight to **`/implement`**). Where `/grill-with-docs` sharpens an idea you can hold in one session, wayfinder is for the one you can't. Once an undertaking has already resolved into several independently-shippable waves, `/scale-check` → `/to-waves` stays the primary route.
|
|
77
77
|
- **A backlog to cluster** → **`/board-to-waves`**. Groups an existing board into themed waves when you need to *find* the next wave rather than start fresh.
|
|
78
|
-
- **A whole wave to LAND** → **`/orchestrate-wave`**. When a wave anchor (file-disjoint slices, specs already locked) is ready to build, verify and land end-to-end — often AFK: it dispatches an implementer per slice in its own worktree, integrates serially, verifies centrally, and lands the wave. The execute-and-land node of the wave ladder (`scale-check` → `to-waves`/`board-to-waves` → `orchestrate-wave`). A single slice just goes to `/
|
|
78
|
+
- **A whole wave to LAND** → **`/orchestrate-wave`**. When a wave anchor (file-disjoint slices, specs already locked) is ready to build, verify and land end-to-end — often AFK: it dispatches an implementer per slice in its own worktree, integrates serially, verifies centrally, and lands the wave. The execute-and-land node of the wave ladder (`scale-check` → `to-waves`/`board-to-waves` → `orchestrate-wave`). A single slice just goes to `/implement`.
|
|
79
79
|
|
|
80
80
|
## Cross-model review (Codex)
|
|
81
81
|
|
|
@@ -110,11 +110,21 @@ Body from `docs/agents/wave-anchor-template.md` **stage 1** (header + cluster or
|
|
|
110
110
|
|
|
111
111
|
**Idempotency — stub marker + search-before-create (mandatory, BEFORE `create`).** Re-runs of board-to-waves must **not** produce duplicate stubs (a duplicate stub would confuse Mode-B identity in `to-prd`). Mirrors the `to-prd` pattern:
|
|
112
112
|
- **Stable stub marker** `<!-- wave-stub-source: <topic-slug> -->` as the **first body line** of every stub. `<topic-slug>` = kebab-case slug of the gate outcome; set on the **first** run, **never** changed after (identity ≠ content — the slug stays findable even if members/size change later).
|
|
113
|
-
- **search-before-create** per candidate **before** the `create
|
|
113
|
+
- **search-before-create** per candidate **before** the `create`, through the
|
|
114
|
+
shared all-state exact-marker lookup (it uses `gh api --paginate`, discards REST
|
|
115
|
+
pull-request items, and never relies on GitHub Search or a capped issue list):
|
|
114
116
|
```bash
|
|
115
|
-
|
|
116
|
-
# locally filter on `wave-stub-source: <topic-slug>` → 1 match ⇒ skip + report (stub exists); >1 ⇒ STOP + report; 0 ⇒ create
|
|
117
|
+
python3 scripts/find-by-marker.py --kind wave-stub-source --slug "<topic-slug>"
|
|
117
118
|
```
|
|
119
|
+
Branch on its JSON contract (`count`, `issues[].number`, `issues[].state`,
|
|
120
|
+
`verdict`): `0` / `create` → create; exactly one `open` / `update` → skip the
|
|
121
|
+
create and report/update that stub; exactly one `closed` / `user-decision` → ask
|
|
122
|
+
the user whether to reopen, use a new identity, or stop; `>1` / `STOP` → stop
|
|
123
|
+
and report every number/state. Never auto-delete or silently replace a
|
|
124
|
+
closed/duplicate identity. Immediately after create, run the same lookup with
|
|
125
|
+
`--created <new-issue-number>`; continue only when it returns exactly the
|
|
126
|
+
newly-created open issue. Duplicate reconciliation stops loudly and reports
|
|
127
|
+
both/all numbers for user-decided resolution.
|
|
118
128
|
|
|
119
129
|
**Create the candidate stub (cluster/wave-less)** — issue **without** `type:cluster` and **without** `--wave` (exactly one `type:*` + one `priority:*`; title **without** a `Welle <N>` prefix, since the wave number is only assigned at promotion), **with `--wave-stub`** (a searchable "awaiting planning" filter — the HTML marker above is only locally greppable, GitHub doesn't index it), attach to the board, triaged-role status (clustered, not yet planned). `to-prd` then matures the stub (Mode B) into a Draft PRD, `to-issues` promotes it to an anchor (sets `type:cluster` + Wave then, **strips `wave-stub`** — the stub leaves the planning list):
|
|
120
130
|
```bash
|
|
@@ -13,12 +13,14 @@ The role-flip of `/codex-review`: there, Claude builds the plan and Codex critiq
|
|
|
13
13
|
|
|
14
14
|
## Prerequisites (verify once, fast)
|
|
15
15
|
|
|
16
|
-
- `codex
|
|
17
|
-
-
|
|
16
|
+
- Let `scripts/codex-exec.sh` preflight Codex before launch. It enforces the
|
|
17
|
+
exact tested-version allowlist, authentication, platform, and capabilities;
|
|
18
|
+
surface any failure rather than retrying silently.
|
|
18
19
|
- Do NOT pin `-m` or model config (e.g. `model_reasoning_effort`) unless the user asks. Pinning `gpt-5.x-codex` variants 400s on ChatGPT-account auth; config defaults come from `~/.codex/config.toml`.
|
|
19
20
|
- **Echo the active model at kickoff** so the user can confirm: read the `model` line from `~/.codex/config.toml` (absent = "CLI default"); state it with the resolved tunables. If the user objects, stop before launching the build.
|
|
20
|
-
- **Codex has a native image-generation tool** in
|
|
21
|
-
- Run from the target working directory's root
|
|
21
|
+
- **Codex has a native image-generation tool** in delegated sessions (ChatGPT-account backed, no API key; upstream-verified 2026-07-08). Specs may therefore include "generate these image assets yourself" steps: name exact file paths, dimensions, and style in the prompt contract.
|
|
22
|
+
- Run the wrapper from the target working directory's root so every round keeps
|
|
23
|
+
the same bounded workspace.
|
|
22
24
|
|
|
23
25
|
## Tunables (read from args, else default)
|
|
24
26
|
|
|
@@ -63,17 +65,34 @@ OUTPUT: End with a report — files changed (one line each: path + what/why),
|
|
|
63
65
|
EOF
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
## Step 2 — Launch Codex (fresh session
|
|
68
|
+
## Step 2 — Launch Codex (fresh wrapper-owned session)
|
|
67
69
|
|
|
68
70
|
```bash
|
|
69
|
-
codex
|
|
71
|
+
if ROUND_RESULT=$(scripts/codex-exec.sh new --profile build --mode workspace-write --prompt-file "$P"); then
|
|
72
|
+
RUN_ID=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["runId"])')
|
|
73
|
+
CODEX_REPORT=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["verdict"])')
|
|
74
|
+
else
|
|
75
|
+
FAILURE_RESULT=$(scripts/codex-exec.sh handle-failure --result "$ROUND_RESULT") || :
|
|
76
|
+
printf '%s\n' "$FAILURE_RESULT" >&2
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
70
79
|
```
|
|
71
80
|
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
- The `build` profile establishes and persists `workspace-write`; never request
|
|
82
|
+
`danger-full-access` or weaken the approval policy. The wrapper bounds Codex
|
|
83
|
+
to the working directory, and the allowed-write gate (Step 3) narrows it to
|
|
84
|
+
the declared set. Work outside those bounds belongs to Claude and must be
|
|
85
|
+
split out of the spec.
|
|
86
|
+
- The wrapper owns stdin closure, hang detection, the overall build timeout,
|
|
87
|
+
stderr redaction, and the launched process group. Never inspect, signal, or
|
|
88
|
+
kill foreign Codex processes.
|
|
89
|
+
- `RUN_ID` is opaque. Retain it only for resume/finalize/abort; harvest the
|
|
90
|
+
report from `CODEX_REPORT` before deleting run state.
|
|
91
|
+
- `handle-failure` surfaces every failed or cancelled structured result plus
|
|
92
|
+
cleanup metadata; the caller then stops before report handling. A surfaced
|
|
93
|
+
`HUNG` returns to the user for the choice to retry once with a fresh run or
|
|
94
|
+
stop delegation and let Claude take over. Never target a process directly.
|
|
95
|
+
|
|
77
96
|
- **Heads-up on completion (required):** when a background Codex run finishes, the FIRST line of your next message to the user must be a loud standalone banner — `🔔 CODEX FINISHED — <what> (exit ok/fail) — verifying now` — BEFORE any verification output. The user is not watching tool calls; never let a completed build slide silently into the verify phase.
|
|
78
97
|
|
|
79
98
|
## Step 3 — Verify (Claude, always, never delegated)
|
|
@@ -83,33 +102,60 @@ Codex's report is advisory. Verify yourself:
|
|
|
83
102
|
1. **Allowed-write gate:** `git status --porcelain=v1` — every changed AND untracked path must be inside the allowed-write set from Step 0.4. Any path outside it is a finding: surface it to the user before anything else, and don't fold it silently into the diff review.
|
|
84
103
|
2. Read the FULL diff (`git diff`). Judge it like a contributor PR: correctness, spec fidelity, style match with surrounding code, nothing touched outside scope.
|
|
85
104
|
3. Run `PROOF_CMD` yourself (or the focused tests for the changed area). Codex's pasted output doesn't count as proof.
|
|
86
|
-
4. Append to `LOG_FILE` under `## Act 3 — Build`: `### Round <n> — Codex build` +
|
|
105
|
+
4. Append to `LOG_FILE` under `## Act 3 — Build`: `### Round <n> — Codex build` + `CODEX_REPORT` + `### Claude's verdict` + what passed/failed review.
|
|
87
106
|
|
|
88
107
|
## Step 4 — Fix loop (same session, bounded)
|
|
89
108
|
|
|
90
109
|
Problems found → resume the SAME session (Codex keeps its context; cheaper and better than a fresh run). Write the fix list to a second temp file (`$CODEX_TMP/fix-prompt.txt`), same contract discipline: exact problem, exact file, proof expected, same allowed-write set.
|
|
91
110
|
|
|
92
111
|
```bash
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
codex
|
|
97
|
-
|
|
112
|
+
if ROUND_RESULT=$(scripts/codex-exec.sh resume "$RUN_ID" --prompt-file "$CODEX_TMP/fix-prompt.txt"); then
|
|
113
|
+
CODEX_REPORT=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["verdict"])')
|
|
114
|
+
else
|
|
115
|
+
FAILURE_RESULT=$(scripts/codex-exec.sh handle-failure --result "$ROUND_RESULT" --run-id "$RUN_ID") || :
|
|
116
|
+
printf '%s\n' "$FAILURE_RESULT" >&2
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
98
119
|
```
|
|
99
120
|
|
|
100
|
-
|
|
121
|
+
The wrapper enforces the persisted workspace-write mode on every resume.
|
|
122
|
+
Re-verify (Step 3, including the allowed-write gate) after each round. After
|
|
123
|
+
`MAX_FIX_ROUNDS` failed rounds: abort this run, STOP delegating, and let Claude
|
|
124
|
+
take over and finish the remaining fixes directly. Log the takeover.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
scripts/codex-exec.sh abort "$RUN_ID"
|
|
128
|
+
```
|
|
101
129
|
|
|
102
130
|
## Step 5 — Human gate (diff sign-off)
|
|
103
131
|
|
|
104
132
|
Present: 3-bullet summary of what was built, files-changed list, proof-test output (pass/fail, verbatim tail), rounds used, any spec deviations. Ask: *"Codex built it, proof passes, diff reviewed. Commit?"*
|
|
105
133
|
|
|
106
|
-
-
|
|
107
|
-
|
|
134
|
+
- On yes, harvest and log the final report, then delete the wrapper state before
|
|
135
|
+
Claude commits:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
scripts/codex-exec.sh finalize "$RUN_ID"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- Commit ONLY after that finalize succeeds — and Claude writes the commit,
|
|
142
|
+
never Codex.
|
|
143
|
+
- Rejected with another requested fix → keep the known run and route back to
|
|
144
|
+
Step 4 (or take over directly if fix rounds are spent).
|
|
145
|
+
- Cancellation or a decision to stop delegation → abort the known run before
|
|
146
|
+
returning control:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
scripts/codex-exec.sh abort "$RUN_ID"
|
|
150
|
+
```
|
|
108
151
|
|
|
109
152
|
## Hard rules
|
|
110
153
|
|
|
111
154
|
- Clean tree before launch. Always. No exceptions.
|
|
112
|
-
- **
|
|
155
|
+
- **The mode is bounded every round:** establish `workspace-write` in the
|
|
156
|
+
wrapper's new call and let every resume inherit it. Never `--yolo`, never
|
|
157
|
+
`danger-full-access`, never a weakened approval policy — if the task can't
|
|
158
|
+
be done inside those bounds, it isn't a Codex delegation.
|
|
113
159
|
- The allowed-write gate runs after EVERY round — out-of-set writes are findings, not noise.
|
|
114
160
|
- Claude never skips the diff read. Codex claims are advisory until Claude has read the diff and run the proof.
|
|
115
161
|
- Fix loop terminates at `MAX_FIX_ROUNDS` — then Claude takes over. No unbounded delegation ping-pong.
|
|
@@ -121,6 +167,6 @@ Present: 3-bullet summary of what was built, files-changed list, proof-test outp
|
|
|
121
167
|
- Don't build without a spec — that's designing by delegation, and it fails. Route to `/grill-me-codex` or `/codex-review` first.
|
|
122
168
|
- Don't use for ~<20-line single-obvious-change edits — just make the edit.
|
|
123
169
|
- Don't pin `-codex` model variants on ChatGPT-account auth — 400s.
|
|
124
|
-
- Don't resume with `--last
|
|
125
|
-
|
|
170
|
+
- Don't resume with `--last`; use the wrapper's explicit opaque `RUN_ID` so
|
|
171
|
+
parallel sessions cannot target each other.
|
|
126
172
|
- Don't let Codex commit, and don't auto-commit yourself — human gate first.
|