@ikon85/agent-workflow-kit 0.28.0 → 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/board-to-waves/SKILL.md +13 -3
- package/.agents/skills/to-issues/SKILL.md +102 -27
- package/.agents/skills/to-prd/SKILL.md +13 -3
- package/.agents/skills/to-waves/SKILL.md +16 -4
- package/.agents/skills/wrapup/SKILL.md +0 -1
- 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-codex/SKILL.md +45 -34
- package/.claude/skills/grill-with-docs-codex/SKILL.md +46 -34
- package/.claude/skills/to-issues/SKILL.md +102 -27
- package/.claude/skills/to-prd/SKILL.md +13 -3
- package/.claude/skills/to-waves/SKILL.md +16 -4
- package/.claude/skills/wrapup/SKILL.md +0 -1
- package/README.md +23 -0
- package/agent-workflow-kit.package.json +56 -16
- package/docs/research/wave-152-consumer-acceptance.md +98 -0
- package/package.json +1 -1
- package/scripts/board-sync.py +3 -7
- 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/render-anchor.py +111 -0
- package/scripts/test_marker_lib.py +154 -0
- package/scripts/test_render_anchor.py +267 -0
- package/scripts/test_retro_wrapup_contract.py +6 -0
- package/scripts/test_skill_codex_exec_lifecycle.py +123 -0
- package/src/lib/bundle.mjs +10 -0
|
@@ -41,7 +41,7 @@ For a cross-cutting plan, run `python3 .claude/hooks/drift-guard.py --census-sta
|
|
|
41
41
|
|
|
42
42
|
When the decision tree is resolved and we're aligned, **write the agreed plan to `PLAN.md`** in this structure, then move to Act 2:
|
|
43
43
|
|
|
44
|
-
> **Where to write it:** `PLAN.md` + `PLAN-REVIEW-LOG.md` are per-session scratch — write them in the working directory the implementing session will actually use, and
|
|
44
|
+
> **Where to write it:** `PLAN.md` + `PLAN-REVIEW-LOG.md` are per-session scratch — write them in the working directory the implementing session will actually use, and invoke the wrapper from that directory. A project may gitignore these files, so don't rely on git to carry them across checkouts/worktrees. In worktree-based repos, create the issue worktree BEFORE this write and plan inside it.
|
|
45
45
|
|
|
46
46
|
```markdown
|
|
47
47
|
# Plan: <task>
|
|
@@ -76,8 +76,9 @@ Act 1 (grill) complete — plan locked with the user. MAX_ROUNDS=<n>.
|
|
|
76
76
|
Now hand the locked plan to Codex for adversarial review. Same engine, mechanics verified end-to-end (2026-06-04).
|
|
77
77
|
|
|
78
78
|
### Prerequisites (verify once, fast)
|
|
79
|
-
- `codex
|
|
80
|
-
-
|
|
79
|
+
- Let `scripts/codex-exec.sh` preflight Codex before launch. It enforces the
|
|
80
|
+
exact tested-version allowlist, authentication, platform, and capabilities;
|
|
81
|
+
surface any failure rather than retrying silently.
|
|
81
82
|
- Do NOT pin `-m`. Use the config default. Pinning `gpt-5.x-codex` variants 400s on ChatGPT-account auth.
|
|
82
83
|
- **Echo the active model before Round 1** so the user can confirm: read the `model` line from `~/.codex/config.toml` (absent = "CLI default"); state it alongside the resolved tunables. If the user objects, stop before burning a round.
|
|
83
84
|
|
|
@@ -93,50 +94,59 @@ If invoked with e.g. `rounds=3`, use that for `MAX_ROUNDS`. Echo resolved values
|
|
|
93
94
|
### The review prompt (sent each round)
|
|
94
95
|
> You are an adversarial reviewer for an implementation plan. Be skeptical and specific — your job is to find what breaks, not to be agreeable. Read the plan at `PLAN.md` and any repo files you need (you are read-only). Identify concrete flaws: security holes, race conditions, missing edge cases, schema conflicts, wrong assumptions, observability gaps, simpler alternatives. For each, give a one-line fix. Do NOT modify any files. End your reply with EXACTLY one line: `VERDICT: APPROVED` if the plan is sound enough to implement, or `VERDICT: REVISE` if it still has material problems.
|
|
95
96
|
|
|
96
|
-
### Round 1 — fresh session
|
|
97
|
-
|
|
97
|
+
### Round 1 — fresh wrapper-owned session
|
|
98
|
+
|
|
98
99
|
```bash
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
BYTES=$(wc -c < $CODEX_TMP/r1.jsonl 2>/dev/null || echo 0)
|
|
107
|
-
if [ "${CPU:-00:00:00}" = "00:00:00" ] && [ "${BYTES:-0}" -eq 0 ]; then
|
|
108
|
-
kill -9 "$CODEX_PID" 2>/dev/null; echo "CODEX-HUNG" # alive + 0 CPU + 0 bytes = blocked, NOT working
|
|
109
|
-
fi
|
|
100
|
+
if ROUND_RESULT=$(scripts/codex-exec.sh new --profile review --mode read-only --prompt "$REVIEW_PROMPT"); then
|
|
101
|
+
RUN_ID=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["runId"])')
|
|
102
|
+
CODEX_REPORT=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["verdict"])')
|
|
103
|
+
else
|
|
104
|
+
FAILURE_RESULT=$(scripts/codex-exec.sh handle-failure --result "$ROUND_RESULT") || :
|
|
105
|
+
printf '%s\n' "$FAILURE_RESULT" >&2
|
|
106
|
+
exit 1
|
|
110
107
|
fi
|
|
111
|
-
wait "$CODEX_PID" 2>/dev/null
|
|
112
|
-
THREAD_ID=$(grep -o '"thread_id":"[^"]*"' $CODEX_TMP/r1.jsonl | head -1 | cut -d'"' -f4)
|
|
113
108
|
```
|
|
114
|
-
- **`CODEX-HUNG` printed** (alive + 0 CPU + 0 bytes at 90s) → **first suspect the stdin block**: confirm the launch has `< /dev/null` and retry. That fixes it in nearly every case. **NEVER `pgrep`/`kill` codex procs to "clear contention"** — that murders the user's live, unrelated codex sessions and does **not** fix a stdin hang. Only if `< /dev/null` is present and it still hangs (genuine sandbox deadlock) → **STOP Act 2**: tell the user, offer to sign off without the cross-model review or retry once. Don't touch other codex processes.
|
|
115
|
-
- **Healthy:** CPU climbs past `00:00:00` and/or `$CODEX_TMP/r1.jsonl` grows; `THREAD_ID` parses; critique lands in `$CODEX_TMP/verdict.txt`. `2>/dev/null` hides cosmetic MCP/auth noise.
|
|
116
|
-
- **Clean finish but no verdict file + no `THREAD_ID`** = auth/model failure → stop, tell the user.
|
|
117
109
|
|
|
118
|
-
### Rounds 2..MAX — resume the SAME session
|
|
110
|
+
### Rounds 2..MAX — resume the SAME session
|
|
111
|
+
|
|
119
112
|
```bash
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
< /dev/null 2>/dev/null >/dev/null &
|
|
113
|
+
if ROUND_RESULT=$(scripts/codex-exec.sh resume "$RUN_ID" --prompt "I revised the plan. Re-review PLAN.md — check whether your prior findings are addressed and flag anything new. End with VERDICT: APPROVED or VERDICT: REVISE."); then
|
|
114
|
+
CODEX_REPORT=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["verdict"])')
|
|
115
|
+
else
|
|
116
|
+
FAILURE_RESULT=$(scripts/codex-exec.sh handle-failure --result "$ROUND_RESULT" --run-id "$RUN_ID") || :
|
|
117
|
+
printf '%s\n' "$FAILURE_RESULT" >&2
|
|
118
|
+
exit 1
|
|
119
|
+
fi
|
|
128
120
|
```
|
|
129
|
-
Wrap resume in the **same 90s liveness probe** (background + `wait`). Resume discards the `--json` stream, so probe on the verdict file: `BYTES=$(wc -c < $CODEX_TMP/verdict.txt)` plus the `CPU` check — `00:00:00` CPU + empty verdict at 90s → kill, treat as `CODEX-HUNG`, same STOP path as round 1. Both `codex exec` and `codex exec resume` support `--json` and `-o/--output-last-message`.
|
|
130
121
|
|
|
131
|
-
|
|
122
|
+
The wrapper persists read-only mode and owns stdin closure, hang detection, the
|
|
123
|
+
overall timeout, stderr redaction, and its process group. `handle-failure`
|
|
124
|
+
surfaces the structured status and cleanup metadata, then the caller stops
|
|
125
|
+
before report handling. A surfaced `HUNG` returns to the user for the choice to
|
|
126
|
+
retry once with a fresh run or sign off without cross-model review. Never
|
|
127
|
+
inspect, signal, or kill foreign Codex processes.
|
|
128
|
+
|
|
129
|
+
Separately, cancellation or a decision to stop orchestration after an OK round
|
|
130
|
+
but before normal finalize must abort the known run:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
scripts/codex-exec.sh abort "$RUN_ID"
|
|
134
|
+
```
|
|
132
135
|
|
|
133
136
|
### Each round, after Codex returns
|
|
134
|
-
1. Read
|
|
137
|
+
1. Read `CODEX_REPORT`; append to `LOG_FILE`: `## Round <n> — Codex` + the full critique.
|
|
135
138
|
2. Grep the last line for the verdict:
|
|
136
139
|
- `VERDICT: APPROVED` → break to Resolution (converged).
|
|
137
140
|
- `VERDICT: REVISE` → Claude decides **what's actually worth acting on** (Claude is final arbiter — Codex advises, doesn't command). Revise `PLAN_FILE`. Append `### Claude's response` to `LOG_FILE`: what changed, what was rejected, why. Increment round.
|
|
138
141
|
3. If round > `MAX_ROUNDS` → break to Resolution (deadlock).
|
|
139
142
|
|
|
143
|
+
After harvesting the final report and updating the log, delete the successful
|
|
144
|
+
run state:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
scripts/codex-exec.sh finalize "$RUN_ID"
|
|
148
|
+
```
|
|
149
|
+
|
|
140
150
|
### Resolution (you sign off — final gate)
|
|
141
151
|
- **APPROVED:** present the final `PLAN_FILE`, a 3-bullet summary of what the two acts improved, and the round count. Ask: *"Grilled + survived N rounds of Codex. Implement it now — Codex builds it (`/codex-build`), Claude builds it, or stop here?"* Code only on a yes. **No code is written during either act.**
|
|
142
152
|
- **MAX_ROUNDS hit without APPROVED (deadlock):** do NOT fake convergence. List each unresolved point + Claude's counter-position; hand it to the user to break the tie. A flagged disagreement beats a false "approved."
|
|
@@ -149,7 +159,8 @@ If the user picks Codex: invoke the `codex-build` skill with `SPEC_FILE=PLAN.md`
|
|
|
149
159
|
|
|
150
160
|
## Hard rules
|
|
151
161
|
- Act 1 always precedes Act 2 — don't write `PLAN.md` until the grill has actually resolved the decision tree with the user.
|
|
152
|
-
- Codex is read-only EVERY round —
|
|
162
|
+
- Codex is read-only EVERY round — establish it once through the wrapper's
|
|
163
|
+
`review` + `read-only` new call; every resume inherits it.
|
|
153
164
|
- The loop ALWAYS terminates at `MAX_ROUNDS`.
|
|
154
165
|
- Claude is final arbiter on every REVISE — incorporate good critiques, reject bad ones *with a logged reason*. Don't cave to everything (defeats the cross-model check) and don't ignore it (defeats the point).
|
|
155
166
|
- Code only after the user's final sign-off.
|
|
@@ -125,7 +125,7 @@ For a cross-cutting plan, run `python3 .claude/hooks/drift-guard.py --census-sta
|
|
|
125
125
|
|
|
126
126
|
When the decision tree is resolved, the glossary/ADRs are updated, and we're aligned, **write the agreed plan to `PLAN.md`** (use the canonical terms from `CONTEXT.md`), then run Act 2:
|
|
127
127
|
|
|
128
|
-
> **Where to write it:** `PLAN.md` + `PLAN-REVIEW-LOG.md` are per-session scratch — write them in the working directory the implementing session will actually use, and
|
|
128
|
+
> **Where to write it:** `PLAN.md` + `PLAN-REVIEW-LOG.md` are per-session scratch — write them in the working directory the implementing session will actually use, and invoke the wrapper from that directory. A project may gitignore these files, so don't rely on git to carry them across checkouts/worktrees. In worktree-based repos, create the issue worktree BEFORE this write and plan inside it.
|
|
129
129
|
|
|
130
130
|
```markdown
|
|
131
131
|
# Plan: <task>
|
|
@@ -160,8 +160,9 @@ Act 1 (grill-with-docs) complete — plan locked, CONTEXT.md/ADRs updated. MAX_R
|
|
|
160
160
|
Hand the locked plan to Codex for adversarial review. Mechanics verified end-to-end (2026-06-04).
|
|
161
161
|
|
|
162
162
|
### Prerequisites
|
|
163
|
-
- `codex
|
|
164
|
-
-
|
|
163
|
+
- Let `scripts/codex-exec.sh` preflight Codex before launch. It enforces the
|
|
164
|
+
exact tested-version allowlist, authentication, platform, and capabilities;
|
|
165
|
+
surface any failure rather than retrying silently.
|
|
165
166
|
- Do NOT pin `-m` (config default is used; `gpt-5.x-codex` variants 400 on ChatGPT-account auth).
|
|
166
167
|
- **Echo the active model before Round 1** so the user can confirm: read the `model` line from `~/.codex/config.toml` (absent = "CLI default"); state it alongside the resolved tunables. If the user objects, stop before burning a round.
|
|
167
168
|
|
|
@@ -177,48 +178,57 @@ Invoked with e.g. `rounds=3` → use it. Echo resolved values first.
|
|
|
177
178
|
### Review prompt (each round)
|
|
178
179
|
> You are an adversarial reviewer for an implementation plan. Be skeptical and specific — your job is to find what breaks, not to be agreeable. Read the plan at `PLAN.md` (and `CONTEXT.md`/ADRs for the domain language) and any repo files you need (you are read-only). Identify concrete flaws: security holes, race conditions, missing edge cases, schema conflicts, domain-language mismatches, wrong assumptions, observability gaps, simpler alternatives. For each, give a one-line fix. Do NOT modify any files. End with EXACTLY one line: `VERDICT: APPROVED` or `VERDICT: REVISE`.
|
|
179
180
|
|
|
180
|
-
### Round 1 — fresh session
|
|
181
|
-
|
|
181
|
+
### Round 1 — fresh wrapper-owned session
|
|
182
|
+
|
|
182
183
|
```bash
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
BYTES=$(wc -c < $CODEX_TMP/r1.jsonl 2>/dev/null || echo 0)
|
|
191
|
-
if [ "${CPU:-00:00:00}" = "00:00:00" ] && [ "${BYTES:-0}" -eq 0 ]; then
|
|
192
|
-
kill -9 "$CODEX_PID" 2>/dev/null; echo "CODEX-HUNG" # alive + 0 CPU + 0 bytes = blocked, NOT working
|
|
193
|
-
fi
|
|
184
|
+
if ROUND_RESULT=$(scripts/codex-exec.sh new --profile review --mode read-only --prompt "$REVIEW_PROMPT"); then
|
|
185
|
+
RUN_ID=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["runId"])')
|
|
186
|
+
CODEX_REPORT=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["verdict"])')
|
|
187
|
+
else
|
|
188
|
+
FAILURE_RESULT=$(scripts/codex-exec.sh handle-failure --result "$ROUND_RESULT") || :
|
|
189
|
+
printf '%s\n' "$FAILURE_RESULT" >&2
|
|
190
|
+
exit 1
|
|
194
191
|
fi
|
|
195
|
-
wait "$CODEX_PID" 2>/dev/null
|
|
196
|
-
THREAD_ID=$(grep -o '"thread_id":"[^"]*"' $CODEX_TMP/r1.jsonl | head -1 | cut -d'"' -f4)
|
|
197
192
|
```
|
|
198
|
-
- **`CODEX-HUNG` printed** (alive + 0 CPU + 0 bytes at 90s) → **first suspect the stdin block**: confirm the launch has `< /dev/null` and retry. That fixes it in nearly every case (verified 2026-06-09). **NEVER `pgrep`/`kill` codex procs to "clear contention"** — that murders the user's live, unrelated codex sessions and does **not** fix a stdin hang. If `< /dev/null` is already present and it still hangs (genuine sandbox deadlock) → **STOP Act 2**: append the hang to `LOG_FILE`, tell the user, and offer to (a) proceed to sign-off **without** the cross-model review, or (b) retry once more. Do **not** keep waiting minutes, and do **not** touch other codex processes.
|
|
199
|
-
- **Healthy:** CPU climbs past `00:00:00` and/or `$CODEX_TMP/r1.jsonl` grows; `THREAD_ID` parses; critique lands in `$CODEX_TMP/verdict.txt`.
|
|
200
|
-
- **Clean finish but no verdict file + no `THREAD_ID`** = auth/model failure → stop, tell the user. `2>/dev/null` hides cosmetic MCP/auth noise.
|
|
201
193
|
|
|
202
194
|
### Rounds 2..MAX — resume SAME session
|
|
195
|
+
|
|
203
196
|
```bash
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
< /dev/null 2>/dev/null >/dev/null &
|
|
197
|
+
if ROUND_RESULT=$(scripts/codex-exec.sh resume "$RUN_ID" --prompt "I revised the plan. Re-review PLAN.md — check prior findings + flag anything new. End with VERDICT: APPROVED or VERDICT: REVISE."); then
|
|
198
|
+
CODEX_REPORT=$(printf '%s\n' "$ROUND_RESULT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["verdict"])')
|
|
199
|
+
else
|
|
200
|
+
FAILURE_RESULT=$(scripts/codex-exec.sh handle-failure --result "$ROUND_RESULT" --run-id "$RUN_ID") || :
|
|
201
|
+
printf '%s\n' "$FAILURE_RESULT" >&2
|
|
202
|
+
exit 1
|
|
203
|
+
fi
|
|
212
204
|
```
|
|
213
|
-
Wrap resume in the **same 90s liveness probe** (background + `wait`). Resume discards the `--json` stream, so probe on the verdict file instead: `BYTES=$(wc -c < $CODEX_TMP/verdict.txt)` plus the `CPU` check — `00:00:00` CPU + empty verdict at 90s → kill, treat as `CODEX-HUNG`, same STOP path as round 1.
|
|
214
205
|
|
|
215
|
-
|
|
206
|
+
The wrapper persists read-only mode and owns stdin closure, hang detection, the
|
|
207
|
+
overall timeout, stderr redaction, and its process group. `handle-failure`
|
|
208
|
+
surfaces the structured status and cleanup metadata for `LOG_FILE`, then the
|
|
209
|
+
caller stops before report handling. A surfaced `HUNG` returns to the user for
|
|
210
|
+
the choice to retry once with a fresh run or proceed without cross-model
|
|
211
|
+
review. Never inspect, signal, or kill foreign Codex processes.
|
|
212
|
+
|
|
213
|
+
Separately, cancellation or a decision to stop orchestration after an OK round
|
|
214
|
+
but before normal finalize must abort the known run:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
scripts/codex-exec.sh abort "$RUN_ID"
|
|
218
|
+
```
|
|
216
219
|
|
|
217
220
|
### Each round
|
|
218
|
-
1. Read
|
|
221
|
+
1. Read `CODEX_REPORT`; append `## Round <n> — Codex` + critique to `LOG_FILE`.
|
|
219
222
|
2. Last line verdict: `APPROVED` → Resolution (converged); `REVISE` → Claude decides what's worth acting on (final arbiter), revise `PLAN_FILE`, append `### Claude's response` (what changed/rejected + why), increment.
|
|
220
223
|
3. round > `MAX_ROUNDS` → Resolution (deadlock).
|
|
221
224
|
|
|
225
|
+
After harvesting the final report and updating the log, delete the successful
|
|
226
|
+
run state:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
scripts/codex-exec.sh finalize "$RUN_ID"
|
|
230
|
+
```
|
|
231
|
+
|
|
222
232
|
### Resolution (you sign off)
|
|
223
233
|
- **APPROVED:** present final plan + 3-bullet summary of what the two acts improved + round count. Ask: implement now — Codex builds it (`/codex-build`), Claude builds it, or stop? No code during either act.
|
|
224
234
|
- **Deadlock (cap hit, no APPROVED):** list unresolved points + Claude's counter-position; hand to user. Don't fake convergence.
|
|
@@ -228,9 +238,11 @@ Wrap resume in the **same 90s liveness probe** (background + `wait`). Resume dis
|
|
|
228
238
|
|
|
229
239
|
## Hard rules
|
|
230
240
|
- Act 1 precedes Act 2. `CONTEXT.md` stays a glossary only — no implementation details.
|
|
231
|
-
- Codex read-only EVERY round
|
|
241
|
+
- Codex is read-only EVERY round — establish it once through the wrapper's
|
|
242
|
+
`review` + `read-only` new call; every resume inherits it.
|
|
232
243
|
- Loop ALWAYS terminates at `MAX_ROUNDS`. Claude is final arbiter on REVISE (reject with logged reason). Code only after sign-off. `LOG_FILE` is the deliverable.
|
|
233
|
-
-
|
|
244
|
+
- A `HUNG` wrapper result always returns to the user for the proceed-without or
|
|
245
|
+
retry-once choice.
|
|
234
246
|
|
|
235
247
|
## What NOT to do
|
|
236
248
|
- Don't review already-written code (`/codex:review`). Don't pin `-codex` variants on ChatGPT auth. Don't let Codex edit files. Don't skip Act 1.
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -89,5 +89,4 @@ STOP → diagnose in the main conversation, fix, re-run `land` (an already-merge
|
|
|
89
89
|
|
|
90
90
|
## Out of scope
|
|
91
91
|
- Live-verify / DoD: must happen **before** `/wrapup` — this skill lands, it does not verify.
|
|
92
|
-
- `/retro`: offered before landing (step 2), never run by this skill.
|
|
93
92
|
- Other worktrees / their servers stay untouched.
|
package/README.md
CHANGED
|
@@ -348,6 +348,29 @@ concurrency-safe. Do not run manifest-mutating commands concurrently. Flags:
|
|
|
348
348
|
|
|
349
349
|
## Release notes
|
|
350
350
|
|
|
351
|
+
### 0.29.0
|
|
352
|
+
|
|
353
|
+
- added: `scripts/codex-exec.sh`
|
|
354
|
+
- added: `scripts/codex_proc.py`
|
|
355
|
+
- added: `scripts/find-by-marker.py`
|
|
356
|
+
- added: `scripts/marker_lib.py`
|
|
357
|
+
- added: `scripts/render-anchor.py`
|
|
358
|
+
- changed: `.agents/skills/board-to-waves/SKILL.md`
|
|
359
|
+
- changed: `.agents/skills/to-issues/SKILL.md`
|
|
360
|
+
- changed: `.agents/skills/to-prd/SKILL.md`
|
|
361
|
+
- changed: `.agents/skills/to-waves/SKILL.md`
|
|
362
|
+
- changed: `.agents/skills/wrapup/SKILL.md`
|
|
363
|
+
- changed: `.claude/skills/board-to-waves/SKILL.md`
|
|
364
|
+
- changed: `.claude/skills/codex-build/SKILL.md`
|
|
365
|
+
- changed: `.claude/skills/codex-review/SKILL.md`
|
|
366
|
+
- changed: `.claude/skills/grill-me-codex/SKILL.md`
|
|
367
|
+
- changed: `.claude/skills/grill-with-docs-codex/SKILL.md`
|
|
368
|
+
- changed: `.claude/skills/to-issues/SKILL.md`
|
|
369
|
+
- changed: `.claude/skills/to-prd/SKILL.md`
|
|
370
|
+
- changed: `.claude/skills/to-waves/SKILL.md`
|
|
371
|
+
- changed: `.claude/skills/wrapup/SKILL.md`
|
|
372
|
+
- changed: `scripts/board-sync.py`
|
|
373
|
+
|
|
351
374
|
### 0.28.0
|
|
352
375
|
|
|
353
376
|
- added: `.claude/hooks/kit-origin-edit-hint.py`
|