@windyroad/itil 0.59.1 → 0.59.2
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/.claude-plugin/plugin.json +1 -1
- package/hooks/lib/changeset-detect.sh +7 -24
- package/hooks/test/itil-changeset-discipline.bats +3 -60
- package/package.json +1 -1
- package/scripts/derive-release-vehicle.sh +5 -73
- package/scripts/test/derive-release-vehicle.bats +13 -31
- package/skills/manage-incident/SKILL.md +1 -1
- package/skills/manage-problem/SKILL.md +4 -3
- package/skills/manage-rfc/SKILL.md +3 -7
- package/skills/update-upstream/SKILL.md +2 -2
- package/skills/update-upstream/test/update-upstream-contract.bats +2 -3
- package/skills/work-problems/SKILL.md +12 -43
- package/skills/work-problems/test/work-problems-above-appetite-remediation.bats +8 -7
- package/skills/work-problems/test/work-problems-step-6-5-always-drain.bats +3 -14
- package/skills/work-problems/test/work-problems-step-6-5-cohort-graduation.bats +0 -326
|
@@ -32,10 +32,9 @@
|
|
|
32
32
|
# * any other path: ignored (non-publishable surface — `.github/`,
|
|
33
33
|
# root config, top-level `docs/`, etc.).
|
|
34
34
|
# - If any path is publishable source:
|
|
35
|
-
# * **Check 2a (Phase 1)**: a `.changeset/*.md`
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# held-window entry) targeting the plugin via YAML frontmatter
|
|
35
|
+
# * **Check 2a (Phase 1)**: a `.changeset/*.md` staged → allow.
|
|
36
|
+
# * **Check 2b (Phase 2)**: an in-scope `.changeset/*.md`
|
|
37
|
+
# targeting the plugin via YAML frontmatter
|
|
39
38
|
# `"@windyroad/<slug>": <any-bump>` → allow. Scope =
|
|
40
39
|
# in-unpushed-range additions (`<base>..HEAD`) + untracked
|
|
41
40
|
# working-tree files + modified-not-staged working-tree files.
|
|
@@ -98,8 +97,7 @@
|
|
|
98
97
|
# shape — per-invocation deterministic, no markers).
|
|
99
98
|
# P141 — this helper.
|
|
100
99
|
|
|
101
|
-
# P141 Phase 2 helper — does any `.changeset/*.md`
|
|
102
|
-
# `docs/changesets-holding/*.md`) ALREADY in scope target the plugin
|
|
100
|
+
# P141 Phase 2 helper — does any `.changeset/*.md` ALREADY in scope target the plugin
|
|
103
101
|
# slug via its YAML frontmatter `"@windyroad/<slug>": <bump>` line?
|
|
104
102
|
#
|
|
105
103
|
# Scope = files reachable from HEAD but not from `origin/<base>`,
|
|
@@ -142,11 +140,11 @@ _changeset_in_scope_covers_plugin() {
|
|
|
142
140
|
candidates=$(
|
|
143
141
|
{
|
|
144
142
|
git log --diff-filter=A --name-only --pretty=format: "${base}..HEAD" \
|
|
145
|
-
-- '.changeset/*.md'
|
|
143
|
+
-- '.changeset/*.md' 2>/dev/null
|
|
146
144
|
git ls-files --others --exclude-standard \
|
|
147
|
-
-- '.changeset/*.md'
|
|
145
|
+
-- '.changeset/*.md' 2>/dev/null
|
|
148
146
|
git diff --name-only \
|
|
149
|
-
-- '.changeset/*.md'
|
|
147
|
+
-- '.changeset/*.md' 2>/dev/null
|
|
150
148
|
} | grep -v '/README\.md$' | sort -u
|
|
151
149
|
)
|
|
152
150
|
|
|
@@ -237,21 +235,6 @@ detect_changeset_required() {
|
|
|
237
235
|
.changeset/*.md)
|
|
238
236
|
has_changeset=1
|
|
239
237
|
;;
|
|
240
|
-
docs/changesets-holding/README.md)
|
|
241
|
-
# README in the holding dir is meta-doc, not a real changeset
|
|
242
|
-
# (mirrors the .changeset/README.md exclusion above).
|
|
243
|
-
;;
|
|
244
|
-
docs/changesets-holding/*.md)
|
|
245
|
-
# P177: a held-window changeset entry IS a changeset — authored
|
|
246
|
-
# and audit-trailed, just intentionally held outside `.changeset/`
|
|
247
|
-
# per ADR-042 Rule 7 (held-window blessing). Recognising it here
|
|
248
|
-
# gives the gate a held-window-awareness branch so held-window-
|
|
249
|
-
# bound work commits no longer need a separate move-to-holding
|
|
250
|
-
# chore commit. Release/drain semantics are unchanged — the
|
|
251
|
-
# Release workflow reads `.changeset/` only; a held entry is never
|
|
252
|
-
# drained without a graduation `git mv` back into `.changeset/`.
|
|
253
|
-
has_changeset=1
|
|
254
|
-
;;
|
|
255
238
|
packages/*)
|
|
256
239
|
rest="${path#packages/}"
|
|
257
240
|
slug="${rest%%/*}"
|
|
@@ -229,54 +229,11 @@ run_bash_hook() {
|
|
|
229
229
|
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
#
|
|
234
|
-
# P141's gate purpose is "every publishable iter has a changeset to drain".
|
|
235
|
-
# A `docs/changesets-holding/<name>.md` entry IS a changeset — authored and
|
|
236
|
-
# audit-trailed, just intentionally held outside `.changeset/` per ADR-042
|
|
237
|
-
# Rule 7 (held-window blessing). Before P177 the gate ignored the holding
|
|
238
|
-
# directory (held entries fell through the `*)` catch-all), forcing held-
|
|
239
|
-
# window-bound work through a 2-commit workaround (work commit + a separate
|
|
240
|
-
# `chore(changeset): move ... to holding`). The gate now recognises a staged
|
|
241
|
-
# held entry as satisfying the discipline, mirroring the `.changeset/*.md`
|
|
242
|
-
# branch (and its README.md meta-doc exclusion). Release/drain semantics are
|
|
243
|
-
# unchanged — the Release workflow reads `.changeset/` only; a held entry is
|
|
244
|
-
# recognised at the commit-gate layer, never drained without a graduation
|
|
245
|
-
# `git mv` back into `.changeset/`.
|
|
246
|
-
|
|
247
|
-
@test "P177 allow: staged packages/<plugin>/ source WITH a staged docs/changesets-holding/ entry allows the commit" {
|
|
248
|
-
echo "skill body" > packages/itil/skills/foo/SKILL.md
|
|
249
|
-
mkdir -p docs/changesets-holding
|
|
250
|
-
printf -- '---\n"@windyroad/itil": patch\n---\nheld fix\n' > docs/changesets-holding/wr-itil-p177.md
|
|
251
|
-
git add packages/itil/skills/foo/SKILL.md docs/changesets-holding/wr-itil-p177.md
|
|
252
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
253
|
-
[ "$status" -eq 0 ]
|
|
254
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
@test "P177 allow path with held-window entry emits 0 bytes (ADR-045 Pattern 1 silent-on-pass)" {
|
|
232
|
+
@test "ADR-099 deny: a staged legacy holding entry does NOT satisfy changeset discipline" {
|
|
258
233
|
echo "skill body" > packages/itil/skills/foo/SKILL.md
|
|
259
234
|
mkdir -p docs/changesets-holding
|
|
260
|
-
printf -- '---\n"@windyroad/itil": patch\n---\
|
|
261
|
-
git add packages/itil/skills/foo/SKILL.md docs/changesets-holding/
|
|
262
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
263
|
-
[ "$status" -eq 0 ]
|
|
264
|
-
[ "${#output}" -eq 0 ]
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
@test "P177 deny: staged docs/changesets-holding/README.md alone does NOT count as a valid held changeset" {
|
|
268
|
-
echo "skill body" > packages/itil/skills/foo/SKILL.md
|
|
269
|
-
mkdir -p docs/changesets-holding
|
|
270
|
-
echo "# Changesets Holding Area" > docs/changesets-holding/README.md
|
|
271
|
-
git add packages/itil/skills/foo/SKILL.md docs/changesets-holding/README.md
|
|
272
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
273
|
-
[ "$status" -eq 0 ]
|
|
274
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
@test "P177 deny: staged source with NEITHER .changeset/*.md NOR a holding entry still denies (regression guard)" {
|
|
278
|
-
echo "skill body" > packages/itil/skills/foo/SKILL.md
|
|
279
|
-
git add packages/itil/skills/foo/SKILL.md
|
|
235
|
+
printf -- '---\n"@windyroad/itil": patch\n---\nlegacy metadata\n' > docs/changesets-holding/legacy.md
|
|
236
|
+
git add packages/itil/skills/foo/SKILL.md docs/changesets-holding/legacy.md
|
|
280
237
|
run run_bash_hook "git commit -m 'feat'"
|
|
281
238
|
[ "$status" -eq 0 ]
|
|
282
239
|
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
@@ -549,20 +506,6 @@ mark_origin_at_head() {
|
|
|
549
506
|
[[ "$output" == *"itil"* ]]
|
|
550
507
|
}
|
|
551
508
|
|
|
552
|
-
@test "P141 Phase 2 allow: held-window docs/changesets-holding/*.md in-range entry also covers (ADR-042 Rule 7 composes with Phase 2)" {
|
|
553
|
-
mark_origin_at_head
|
|
554
|
-
mkdir -p docs/changesets-holding
|
|
555
|
-
printf -- '---\n"@windyroad/itil": patch\n---\nheld fix\n' > docs/changesets-holding/wr-itil-p347.md
|
|
556
|
-
git add docs/changesets-holding/wr-itil-p347.md
|
|
557
|
-
git -c commit.gpgsign=false commit --quiet -m "held changeset"
|
|
558
|
-
# Subsequent itil source commit — held entry in range covers.
|
|
559
|
-
echo "skill body" > packages/itil/skills/foo/SKILL.md
|
|
560
|
-
git add packages/itil/skills/foo/SKILL.md
|
|
561
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
562
|
-
[ "$status" -eq 0 ]
|
|
563
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
564
|
-
}
|
|
565
|
-
|
|
566
509
|
@test "P141 Phase 2: when no upstream and no origin/main ref exists, Check 2b skips silently and Phase 1 strict-deny is preserved" {
|
|
567
510
|
# No mark_origin_at_head — refs/remotes/origin/main is absent.
|
|
568
511
|
# Stage source without any changeset. Phase 1 strict-deny must fire
|
package/package.json
CHANGED
|
@@ -24,24 +24,12 @@
|
|
|
24
24
|
# merge-commit: <SHA>
|
|
25
25
|
# release-date: <YYYY-MM-DD>
|
|
26
26
|
#
|
|
27
|
-
# De-facto-released variant (P361, exit 0) when the changeset is present in
|
|
28
|
-
# .changeset/ but its code already shipped with a sibling release:
|
|
29
|
-
# RELEASE_VEHICLE:
|
|
30
|
-
# changeset: .changeset/<name>.md
|
|
31
|
-
# status: de-facto-released (attribution pending)
|
|
32
|
-
# fix-commit: <SHA>
|
|
33
|
-
# shipped-with-version-packages-commit: <SHA>
|
|
34
|
-
# release-date: <YYYY-MM-DD>
|
|
35
|
-
# note: ...
|
|
36
|
-
#
|
|
37
27
|
# Exit codes:
|
|
38
|
-
# 0 = OK (full citation emitted
|
|
39
|
-
# changeset whose code already shipped with a sibling release — P361)
|
|
28
|
+
# 0 = OK (full citation emitted)
|
|
40
29
|
# 1 = ticket file not found
|
|
41
30
|
# 2 = no changeset reference in ticket body AND no co-committed changeset
|
|
42
31
|
# derivable from the ticket's git history (P389 fallback)
|
|
43
|
-
# 3 = changeset present in working tree
|
|
44
|
-
# (genuinely unreleased)
|
|
32
|
+
# 3 = changeset present in working tree (unreleased)
|
|
45
33
|
# 4 = deletion commit found but no merge PR / merge commit resolvable
|
|
46
34
|
#
|
|
47
35
|
# @problem P267 — Codify derive-release-vehicle.sh helper for K→V release-
|
|
@@ -49,14 +37,6 @@
|
|
|
49
37
|
# fragile to wrong-release-cited errors when sessions
|
|
50
38
|
# pre-apply transitions across sibling tickets (observed
|
|
51
39
|
# 2026-05-18 P250 K→V cited P247's release refs).
|
|
52
|
-
# @problem P361 — exit-3 "unreleased" false positive on ADR-061 graduated
|
|
53
|
-
# holding changesets; helper now distinguishes "attribution
|
|
54
|
-
# pending" (de-facto-released, exit 0) from "code unreleased"
|
|
55
|
-
# (exit 3) via an add-commit ancestry test against the latest
|
|
56
|
-
# version-packages commit.
|
|
57
|
-
# @adr ADR-061 (dogfood graduation criteria — held changeset reinstated to
|
|
58
|
-
# .changeset/ awaiting attribution after its code shipped; the
|
|
59
|
-
# de-facto-released exit-0 path)
|
|
60
40
|
# @adr ADR-049 (bin/ on PATH shim — adopter-safe script resolution; helper
|
|
61
41
|
# is invoked as `wr-itil-derive-release-vehicle`)
|
|
62
42
|
# @adr ADR-022 (Verifying lifecycle — citation supports the K→V transition's
|
|
@@ -79,10 +59,10 @@ USAGE: derive-release-vehicle.sh <ticket-id> [<problems-dir>]
|
|
|
79
59
|
<problems-dir> — defaults to ./docs/problems
|
|
80
60
|
|
|
81
61
|
Exit codes:
|
|
82
|
-
0 ok (full citation
|
|
62
|
+
0 ok (full citation)
|
|
83
63
|
1 ticket file not found
|
|
84
64
|
2 no changeset reference in body AND no co-committed changeset in ticket history (P389)
|
|
85
|
-
3 changeset present in working tree
|
|
65
|
+
3 changeset present in working tree (unreleased)
|
|
86
66
|
4 deletion commit found but no merge PR / merge commit resolvable
|
|
87
67
|
EOF
|
|
88
68
|
}
|
|
@@ -157,57 +137,9 @@ if [ -z "$CHANGESET_PATH" ]; then
|
|
|
157
137
|
exit 2
|
|
158
138
|
fi
|
|
159
139
|
|
|
160
|
-
# ── Released?
|
|
140
|
+
# ── Released? Changeset must be ABSENT from the working tree (deleted by
|
|
161
141
|
# chore: version packages) AND have a deletion commit in git history. ────
|
|
162
|
-
# Exception (P361 / ADR-061 Rule 5 + P359): a changeset can be reinstated to
|
|
163
|
-
# .changeset/ awaiting changelog attribution AFTER its code already de-facto
|
|
164
|
-
# shipped with a sibling release (held code ships with any sibling release —
|
|
165
|
-
# P359). Present-in-tree therefore does NOT always mean unreleased. Before
|
|
166
|
-
# exiting 3, test whether the commit that originally ADDED this changeset is
|
|
167
|
-
# an ancestor of the latest published "chore: version packages" commit; if so
|
|
168
|
-
# the fix code shipped → emit a de-facto-released citation (exit 0). A
|
|
169
|
-
# genuinely-unreleased fresh changeset has its add-commit NEWER than the last
|
|
170
|
-
# bump, so the is-ancestor test is false and it correctly stays exit 3.
|
|
171
142
|
if [ -f "$CHANGESET_PATH" ]; then
|
|
172
|
-
# Oldest Add of the path = the original fix commit. Robust to the
|
|
173
|
-
# hold→graduate `git mv`, which (without rename detection) records a later
|
|
174
|
-
# Add at the same path; `tail -1` selects the original.
|
|
175
|
-
ADD_SHA="$(
|
|
176
|
-
git log --diff-filter=A --format='%H' -- "$CHANGESET_PATH" 2>/dev/null \
|
|
177
|
-
| tail -1
|
|
178
|
-
)"
|
|
179
|
-
|
|
180
|
-
# Resolve the published-history ref (same ladder used for merge resolution).
|
|
181
|
-
DEFACTO_REF=""
|
|
182
|
-
for ref in origin/main main HEAD; do
|
|
183
|
-
if git rev-parse --verify "$ref" >/dev/null 2>&1; then
|
|
184
|
-
DEFACTO_REF="$ref"
|
|
185
|
-
break
|
|
186
|
-
fi
|
|
187
|
-
done
|
|
188
|
-
|
|
189
|
-
LATEST_VERSION_BUMP=""
|
|
190
|
-
if [ -n "$DEFACTO_REF" ]; then
|
|
191
|
-
LATEST_VERSION_BUMP="$(
|
|
192
|
-
git log --grep='^chore: version packages' --format='%H' -1 "$DEFACTO_REF" 2>/dev/null
|
|
193
|
-
)"
|
|
194
|
-
fi
|
|
195
|
-
|
|
196
|
-
if [ -n "$ADD_SHA" ] && [ -n "$LATEST_VERSION_BUMP" ] \
|
|
197
|
-
&& git merge-base --is-ancestor "$ADD_SHA" "$LATEST_VERSION_BUMP" 2>/dev/null; then
|
|
198
|
-
RELEASE_DATE="$(git log -1 --format='%cs' "$LATEST_VERSION_BUMP" 2>/dev/null)"
|
|
199
|
-
cat <<EOF
|
|
200
|
-
RELEASE_VEHICLE:
|
|
201
|
-
changeset: $CHANGESET_PATH
|
|
202
|
-
status: de-facto-released (attribution pending)
|
|
203
|
-
fix-commit: $ADD_SHA
|
|
204
|
-
shipped-with-version-packages-commit: $LATEST_VERSION_BUMP
|
|
205
|
-
release-date: $RELEASE_DATE
|
|
206
|
-
note: changeset present in .changeset/ awaiting changelog attribution (ADR-061 holding-graduation); code already shipped with a sibling release (P359).
|
|
207
|
-
EOF
|
|
208
|
-
exit 0
|
|
209
|
-
fi
|
|
210
|
-
|
|
211
143
|
echo "ERROR: changeset $CHANGESET_PATH still present in working tree (unreleased)" >&2
|
|
212
144
|
exit 3
|
|
213
145
|
fi
|
|
@@ -21,11 +21,10 @@
|
|
|
21
21
|
# release-date: <YYYY-MM-DD>
|
|
22
22
|
#
|
|
23
23
|
# Exit codes:
|
|
24
|
-
# 0 = OK (full citation emitted
|
|
25
|
-
# changeset whose code already shipped with a sibling release — P361)
|
|
24
|
+
# 0 = OK (full citation emitted)
|
|
26
25
|
# 1 = ticket file not found
|
|
27
26
|
# 2 = no changeset reference in ticket body
|
|
28
|
-
# 3 = changeset still present
|
|
27
|
+
# 3 = changeset still present (unreleased)
|
|
29
28
|
# 4 = deletion commit found but no merge PR / merge commit resolvable
|
|
30
29
|
#
|
|
31
30
|
# @adr ADR-049 (bin/ on PATH shim — adopter-safe script resolution)
|
|
@@ -158,37 +157,26 @@ EOF
|
|
|
158
157
|
echo "$output" | grep -qi "unreleased\|not.*delet"
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
@test "derive-release-vehicle: graduated holding changeset whose code shipped with a sibling release → exit 0 de-facto-released" {
|
|
164
|
-
# P361 / ADR-061 Rule 5 + P359: a changeset can be reinstated to
|
|
165
|
-
# .changeset/ awaiting changelog attribution AFTER its code already shipped
|
|
166
|
-
# with a sibling release. Present-in-tree must NOT read as unreleased.
|
|
167
|
-
cat > .changeset/p211-graduated.md <<'EOF'
|
|
160
|
+
@test "ADR-099: present changeset remains unreleased after a sibling version bump" {
|
|
161
|
+
cat > .changeset/p211-present.md <<'EOF'
|
|
168
162
|
---
|
|
169
163
|
'@windyroad/itil': patch
|
|
170
164
|
---
|
|
171
165
|
|
|
172
|
-
P211 fix
|
|
166
|
+
P211 fix.
|
|
173
167
|
EOF
|
|
174
|
-
cat > docs/problems/
|
|
175
|
-
# Problem 211:
|
|
168
|
+
cat > docs/problems/known-error/211-present.md <<'EOF'
|
|
169
|
+
# Problem 211: Present Changeset
|
|
176
170
|
|
|
177
171
|
**Status**: Known Error
|
|
178
172
|
|
|
179
173
|
## Fix Strategy
|
|
180
174
|
|
|
181
|
-
Ship via `.changeset/p211-
|
|
175
|
+
Ship via `.changeset/p211-present.md`.
|
|
182
176
|
EOF
|
|
183
177
|
git add .
|
|
184
|
-
git commit -q -m "feat(itil): P211 fix
|
|
185
|
-
|
|
186
|
-
# Hold it out of the active release queue (ADR-042 Rule 7).
|
|
187
|
-
mkdir -p docs/changesets-holding
|
|
188
|
-
git mv .changeset/p211-graduated.md docs/changesets-holding/p211-graduated.md
|
|
189
|
-
git commit -q -m "chore(itil): hold P211 changeset (above-appetite)"
|
|
178
|
+
git commit -q -m "feat(itil): P211 fix"
|
|
190
179
|
|
|
191
|
-
# A sibling release ships AFTER the fix landed (P359: code ships regardless).
|
|
192
180
|
cat > .changeset/p999-sibling.md <<'EOF'
|
|
193
181
|
---
|
|
194
182
|
'@windyroad/itil': patch
|
|
@@ -197,19 +185,13 @@ EOF
|
|
|
197
185
|
Sibling fix.
|
|
198
186
|
EOF
|
|
199
187
|
git add .changeset/p999-sibling.md
|
|
200
|
-
git commit -q -m "feat(itil): sibling fix
|
|
188
|
+
git commit -q -m "feat(itil): sibling fix"
|
|
201
189
|
git rm -q .changeset/p999-sibling.md
|
|
202
|
-
git commit -q -m "chore: version packages"
|
|
203
|
-
|
|
204
|
-
# Graduate the held changeset back to .changeset/ awaiting attribution.
|
|
205
|
-
mkdir -p .changeset
|
|
206
|
-
git mv docs/changesets-holding/p211-graduated.md .changeset/p211-graduated.md
|
|
207
|
-
git commit -q -m "chore(itil): graduate P211 changeset"
|
|
190
|
+
git commit -q -m "chore: version packages"
|
|
208
191
|
|
|
209
192
|
run "$SCRIPT" P211 docs/problems
|
|
210
|
-
[ "$status" -eq
|
|
211
|
-
echo "$output" | grep -qi "
|
|
212
|
-
echo "$output" | grep -q "changeset: .changeset/p211-graduated.md"
|
|
193
|
+
[ "$status" -eq 3 ]
|
|
194
|
+
echo "$output" | grep -qi "unreleased"
|
|
213
195
|
}
|
|
214
196
|
|
|
215
197
|
@test "derive-release-vehicle: fresh changeset added AFTER the last release → still exit 3 (not a false de-facto-released)" {
|
|
@@ -168,7 +168,7 @@ Resolve each field via the following dispatch. **The order is load-bearing** —
|
|
|
168
168
|
|-------|----------|------------------|
|
|
169
169
|
| **Title** | Derive silently. Kebab-case the first 8-10 non-stopword tokens of the user's prose description (same slug derivation as `/wr-itil:capture-problem` Step 1.4 and `/wr-itil:manage-problem` Step 4). Emit stderr advisory: `manage-incident: derived title='<slug>' from description; re-invoke or rename the file if the slug is wrong`. Do NOT fire AskUserQuestion. | category-4 silent-framework |
|
|
170
170
|
| **Symptoms** | Pull from user prose verbatim — the description text IS the symptoms surface for declaration. Place into the `## Observations` section template at Step 5. Do NOT fire AskUserQuestion. | category-4 silent-framework |
|
|
171
|
-
| **Start time** | Derive silently, three sources in priority order: (a) explicit timestamp in description (regex `\b\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2})?\b`, or relative form `"<N> (minutes|hours|days) ago"` resolved against current wall-clock); (b) if the description cites a specific file
|
|
171
|
+
| **Start time** | Derive silently, three sources in priority order: (a) explicit timestamp in description (regex `\b\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2})?\b`, or relative form `"<N> (minutes|hours|days) ago"` resolved against current wall-clock); (b) if the description cites a specific file or directory, run `git log --diff-filter=A --follow -- <path> \| tail -1` for first-touch evidence; (c) otherwise default to current wall-clock UTC. Emit stderr advisory: `manage-incident: start-time derived as <ts> from <source>; cite an additional evidence anchor in the Timeline section if symptoms began earlier`. Do NOT fire AskUserQuestion. | category-4 silent-framework |
|
|
172
172
|
| **Severity** | Derive silently when evidence maps to a clear `RISK-POLICY.md` Impact × Likelihood cell. Cross-reference description signals against the matrix: (a) impact signals (service disruption keywords like `down` / `degraded` / `unavailable` → high; latency / throughput keywords → moderate; cosmetic / typo keywords → low); (b) likelihood signals (`reproducible` / `every request` → high; `intermittent` / `flaky` → medium; `one-off` / `single user` → low); (c) named anchors (held-cluster age cited → use that age to map cell; scorer state cited → use the cited band). When the cross-reference produces a single clear cell, set it silently and emit stderr advisory: `manage-incident: severity derived as <score> (<label>) from RISK-POLICY matrix + evidence: <evidence list>; re-invoke or update if mis-rated`. **Ambiguous-evidence fallback** (no mappable signal in description, or signals point to conflicting cells): fire AskUserQuestion with the Impact (1-5) × Likelihood (1-5) options as the genuine ADR-044 **category-5 (taste)** fallback surface. The fallback is genuine ambiguity, NOT defaults. | category-4 silent-framework (derivable); category-5 fallback (ambiguous) |
|
|
173
173
|
| **Scope** | Retain AskUserQuestion. Scope is the user-judgment surface — only the user knows whether downstream-adopter-risk is in scope, whether mobile is affected, whether the blast radius extends past the cited symptoms. The framework cannot resolve semantic scope deterministically (same reasoning as Step 2 duplicate-check). Construct the call with `header: "Incident scope"`, `multiSelect: false` if a closed enum applies or free-text capture otherwise. This is the canonical ADR-044 **category-1 (direction-setting)** surface — *"only the user knows the goals that haven't been written down yet."* | category-1 direction-setting |
|
|
174
174
|
|
|
@@ -1084,7 +1084,7 @@ Commit the completed work per ADR-014 (governance skills commit their own work):
|
|
|
1084
1084
|
- Fix implemented: `fix(<scope>): <description> (closes P<NNN>)` — include problem file changes (rename to `.verifying.md` + `## Fix Released` section) in the same commit per ADR-022
|
|
1085
1085
|
5. If commit risk is above appetite: auto-apply scorer remediations per **ADR-042 Rule 1** incrementally until residual commit risk is within appetite (≤ 4/25), OR halt per ADR-042 Rule 5 if the scorer cannot converge. **The skill MUST NOT commit above appetite, and MUST NOT call `AskUserQuestion` to ask whether to commit anyway** (P377/RFC-029 amendment 2026-06-24 — above-appetite is framework-mediated, never a category-3 one-time-override; same invariant the push/release branch at Step 12 already enforces). The ADR-013 Rule 6 fail-safe (no `AskUserQuestion` available / non-interactive → skip the commit and report the uncommitted state) remains the terminal fallback. This applies only to the risk-above-appetite branch, not to the delegation-unavailable case above.
|
|
1086
1086
|
|
|
1087
|
-
**Multi-commit slice changeset discipline (P141 Phase 2)**: when a single logical fix lands across multiple ADR-014-grain commits targeting the same plugin (e.g. helper extraction in commit 1, callers wired in commit 2, SKILL note + transition in commit 3 — all `packages/<plugin>/`), author ONE changeset on the first commit in the slice. Subsequent same-plugin commits do NOT need their own changeset — the `itil-changeset-discipline.sh` hook's Check 2b recognises any `.changeset/*.md`
|
|
1087
|
+
**Multi-commit slice changeset discipline (P141 Phase 2)**: when a single logical fix lands across multiple ADR-014-grain commits targeting the same plugin (e.g. helper extraction in commit 1, callers wired in commit 2, SKILL note + transition in commit 3 — all `packages/<plugin>/`), author ONE changeset on the first commit in the slice. Subsequent same-plugin commits do NOT need their own changeset — the `itil-changeset-discipline.sh` hook's Check 2b recognises any `.changeset/*.md` already in the unpushed slice scope (`origin/<base>..HEAD` + untracked + modified-not-staged) that targets `"@windyroad/<plugin>": <any-bump>` and allows. This eliminates the per-commit changeset ceremony that previously produced N redundant `.changeset/*.md` files for one logical release entry (changesets-action collapses bump-class at version-package time, so per-commit changesets rendered N near-identical CHANGELOG bullets for one release). Once a changeset hits `origin/<base>` (drained at release time), it no longer counts — a fresh changeset is required for the next slice. Cross-plugin coverage is NOT permitted: an `@windyroad/itil` changeset does not satisfy a `packages/voice-tone/` commit.
|
|
1088
1088
|
|
|
1089
1089
|
### 12. Auto-release when changesets are queued (ADR-020)
|
|
1090
1090
|
|
|
@@ -1121,8 +1121,9 @@ Otherwise, after the commit in step 11 lands, drain the release queue so the fix
|
|
|
1121
1121
|
2. Read the descriptions. Decide what to do. The agent MAY follow a scorer suggestion, adapt it, or do something else entirely. There is no requirement to rank all suggestions upfront or iterate through them in order.
|
|
1122
1122
|
3. **Verification Pending carve-out (ADR-042 Rule 2b)**: skip remediations that target a commit attached to a `.verifying.md` ticket. Do NOT auto-revert VP commits.
|
|
1123
1123
|
4. Apply the chosen action using standard primitives (git, Edit, Bash). Example actions:
|
|
1124
|
-
- `move-to-holding`: `git mv .changeset/<name>.md docs/changesets-holding/<name>.md` + append to holding-area README "Currently held" per ADR-042 Rule 6. Since the non-AFK skill has no iteration wrapper to amend into, each auto-apply is its own commit (ADR-042 Rule 3). Each commit goes through the standard ADR-014 commit flow — architect + JTBD + risk-scorer gates.
|
|
1125
1124
|
- `revert-commit`: `git revert --no-edit <sha>`. The scorer SHOULD supply the target commit SHA in the `description` column. Before executing, verify the SHA is NOT attached to a `.verifying.md` ticket (Rule 2b carve-out). After revert, commit the revert as a standalone auto-apply commit (no amend folding in non-AFK mode). If `git revert` produces merge conflicts, route to Rule 5 halt with the conflict detail.
|
|
1125
|
+
- `split-change`: split the work into independently releasable, within-appetite commits and re-score each slice.
|
|
1126
|
+
- `disable-or-revert`: remove the risky behaviour from shipped code until its required evidence or control exists.
|
|
1126
1127
|
5. Re-score via the same delegation path as step 1 above.
|
|
1127
1128
|
6. **Loop**: re-score within appetite → drain per the Drain action above. Re-score still above → continue working to reduce risk. The agent reads the new remediations and decides what to do next. Loop. Exhausted or unsupported class → Rule 5 halt.
|
|
1128
1129
|
|
|
@@ -1130,7 +1131,7 @@ Otherwise, after the commit in step 11 lands, drain the release queue so the fix
|
|
|
1130
1131
|
- The final `RISK_SCORES:` line
|
|
1131
1132
|
- An "Auto-apply trail" subsection listing each remediation attempted with outcome
|
|
1132
1133
|
- Any Verification Pending ticket IDs implicated per Rule 2b
|
|
1133
|
-
- A one-line scorer-gap note (e.g., "
|
|
1134
|
+
- A one-line scorer-gap note (e.g., "available remediations did not reduce residual risk below 5/25 — extend scorer vocabulary per P108")
|
|
1134
1135
|
|
|
1135
1136
|
The user resolves interactively — typical resolutions include splitting the commit, feature-flagging the change, or opening a problem ticket documenting the scorer gap.
|
|
1136
1137
|
|
|
@@ -259,11 +259,7 @@ Satisfy the commit gate per ADR-014 — primary path delegates to `wr-risk-score
|
|
|
259
259
|
|
|
260
260
|
### 12. Auto-release (skip in AFK orchestrator)
|
|
261
261
|
|
|
262
|
-
Same conditional drain as `manage-problem` Step 12: if not in an AFK orchestrator AND `.changeset/` is non-empty AND push/release within appetite, run `npm run push:watch` then `npm run release:watch`.
|
|
263
|
-
|
|
264
|
-
## Held-changeset window scope (Phase 1)
|
|
265
|
-
|
|
266
|
-
Phase 1 of the RFC framework (Slices 2-5 per `docs/plans/170-rfc-framework-story-map.md`) ships under a held-changeset window. ADR-042 auto-apply is paused until RFC-001 (P168 retro) reaches `closed` status. Counterfactual risk assessment per P162 governs graduation: delay-risk vs release-risk. The full chain graduates atomically — the entire RFC-001 commit chain ships or nothing does.
|
|
262
|
+
Same conditional drain as `manage-problem` Step 12: if not in an AFK orchestrator AND `.changeset/` is non-empty AND push/release are within appetite, run `npm run push:watch` then `npm run release:watch`.
|
|
267
263
|
|
|
268
264
|
## Composition with capture-rfc
|
|
269
265
|
|
|
@@ -291,7 +287,7 @@ The two skills share the `/tmp/wr-itil-rfc-capture-grep-${SESSION_ID}` create-ga
|
|
|
291
287
|
- **ADR-022** — lifecycle suffix-based (RFC mirrors).
|
|
292
288
|
- **ADR-032** — lightweight + heavyweight split.
|
|
293
289
|
- **ADR-038** — progressive disclosure; future REFERENCE.md split deferred per ADR-054.
|
|
294
|
-
- **ADR-042** —
|
|
290
|
+
- **ADR-042** — above-appetite remediation and halt discipline.
|
|
295
291
|
- **ADR-044** — decision delegation contract; authority classes.
|
|
296
292
|
- **ADR-049** — `wr-itil-reconcile-rfcs` shim grammar (Slice 3).
|
|
297
293
|
- **ADR-051** — load-bearing-from-the-start; I1 hard-block on day one.
|
|
@@ -304,4 +300,4 @@ The two skills share the `/tmp/wr-itil-rfc-capture-grep-${SESSION_ID}` create-ga
|
|
|
304
300
|
- **P134** — Last-reviewed line discipline (single fragment + history archive).
|
|
305
301
|
- **P138** — tie-break ladder consistency.
|
|
306
302
|
- **P150** — Verification Queue sort direction.
|
|
307
|
-
- **
|
|
303
|
+
- **P359** — changeset holding does not withhold shipped code.
|
|
@@ -18,7 +18,7 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Skill, Agen
|
|
|
18
18
|
@adr ADR-010 amended (sibling-skill naming; split execution ownership)
|
|
19
19
|
@adr ADR-044 (decision-delegation contract — framework-resolution boundary)
|
|
20
20
|
@adr ADR-075 (Amendment 2026-06-02 — paired promptfoo eval discharges R009 prose floor)
|
|
21
|
-
@adr ADR-
|
|
21
|
+
@adr ADR-075 (evidence-floor — paired Tier-A/B eval ships in same commit as SKILL)
|
|
22
22
|
-->
|
|
23
23
|
|
|
24
24
|
# Update Upstream — Lifecycle-Update Skill
|
|
@@ -549,7 +549,7 @@ The skill's no-op exit (Step 1) means firing the trigger unconditionally on ever
|
|
|
549
549
|
- [ADR-044](../../../docs/decisions/044-decision-delegation-contract.proposed.md) — framework-resolution boundary; the gate verdict IS the framework, no per-transition AskUserQuestion for the gate firing itself.
|
|
550
550
|
- [ADR-042](../../../docs/decisions/042-auto-apply-scorer-remediations-open-vocabulary.proposed.md) — within-axis precedent for open-vocabulary risk-reducing measures.
|
|
551
551
|
- [ADR-075](../../../docs/decisions/075-promptfoo-agent-prose-verdict-eval-harness.proposed.md) Amendment 2026-06-02 — paired promptfoo Tier-A/B eval discharges the R009 prose-floor for SKILL surfaces.
|
|
552
|
-
- [ADR-
|
|
552
|
+
- [ADR-075](../../../docs/decisions/075-agent-prose-behavioural-test-harness.proposed.md) — evidence-floor; the paired eval ships in the same commit as this SKILL prose for atomic R009 discharge.
|
|
553
553
|
- **P080** — driving problem ticket (No bidirectional update of upstream-reported problems).
|
|
554
554
|
- **P363** — driving problem ticket for the [§ Inbound-origin verdict dispatch](#inbound-origin-verdict-dispatch-p363) leg (inbound-reported tickets never received a fix-released verdict on the originating issue). Fix option (b) — consume the `**Origin**` field — user-ratified 2026-06-22. **Rework 2026-06-23** (four user directives): the inbound verdict is LLM-generated per-context (not templated); O→KE shares the workaround with reporter/commenter provenance-credit; anti-leakage is visibility-gated (PUBLIC → titled+linked refs); the gate chain gains a cog-a11y leg first (P338-gated).
|
|
555
555
|
- [ADR-055](../../../docs/decisions/055-plugin-published-namespace-prefixed-internal-ids.proposed.md) — grounds the PUBLIC-repo titled+linked reference discipline (permalink-progressive-enhancement; cures the bare-ID collision failure mode).
|
|
@@ -346,12 +346,11 @@ setup() {
|
|
|
346
346
|
[ "$status" -eq 0 ]
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
# ─── Paired promptfoo eval (ADR-075 Amendment 2026-06-02
|
|
349
|
+
# ─── Paired promptfoo eval (ADR-075 Amendment 2026-06-02) ────────────────────
|
|
350
350
|
#
|
|
351
351
|
# The paired promptfoo eval at packages/itil/skills/update-upstream/eval/
|
|
352
352
|
# discharges the R009 prose-floor for this SKILL surface atomically per
|
|
353
|
-
# ADR-
|
|
354
|
-
# move-to-holding would apply per the P080 iter contract.
|
|
353
|
+
# ADR-075 evidence floor.
|
|
355
354
|
|
|
356
355
|
@test "update-upstream: paired promptfoo eval config exists (ADR-075 Amendment 2026-06-02)" {
|
|
357
356
|
[ -f "$EVAL_CONFIG" ]
|
|
@@ -367,7 +367,6 @@ Before the orchestrator emits the final `ALL_DONE` sentinel for the AFK loop, it
|
|
|
367
367
|
- it is `verifying` / carries `## Fix Released` awaiting user verification (stop-condition #2, interactive);
|
|
368
368
|
- it carries an upstream-blocked marker (`## Reported Upstream` / `- **Upstream report pending** --` / em-dash legacy) or a recorded blocked classification with a dead-end investigation (stop-condition #3);
|
|
369
369
|
- it was filtered out THIS session by Step 3.5 (interactive-ratification predicate) or Step 3.6 (already-shipped relevance gate) — keyed off the durable per-session skip record those steps write (the `outstanding_questions` entry in `.afk-run-state/outstanding-questions.jsonl` carrying the ticket id), NOT agent recollection, so the classification is reproducible across the re-scan and cannot loop forever;
|
|
370
|
-
- its fix changeset sits in `docs/changesets-holding/` with an unmet reinstate criterion (held, not dispatchable).
|
|
371
370
|
Every other open / known-error ticket is **dispatchable** — ordinary autonomous fix-and-commit work. The agent MUST NOT reclassify a dispatchable ticket as non-dispatchable because the *salient* remainder of the backlog is interactive-gated, because the ticket "feels" out of scope, or because a user-directed pivot consumed the loop's attention. The subjective "this is a natural stopping point" judgement is exactly the P390 failure; the classification is per-ticket and marker-bound. **The classification MUST be PRINTED as a table in the turn output** (ticket → dispatchable/non-dispatchable → the recorded marker that decided) — not merely computed. A computed-but-unprinted re-scan is invisible to the Step 0e `/goal` external evaluator, which judges only what the transcript surfaces (ADR-026 grounding); the printed table is the evidence the canonical goal condition names.
|
|
372
371
|
|
|
373
372
|
3. *Decide.* If the re-scan yields **≥1 dispatchable ticket**, `ALL_DONE` is FORBIDDEN: the stop-condition the orchestrator was about to emit does NOT objectively hold. The orchestrator loops back to Step 3 tier-first selection (Critical-bypass → Inbound-reported → Internal, within-tier WSJF per ADR-076) over the dispatchable set and dispatches the next iter — it does NOT proceed to gate (a)/(b)/(c). Only when the re-scan yields **zero dispatchable tickets** does gate (0) pass and the sequence proceed to gate (a). Gate (0) finding work is a **loopback, not a halt** — it is productive (the loop resumes draining), so it is NOT a Hard-fail halt trigger.
|
|
@@ -889,32 +888,8 @@ After the iteration's commit lands but before starting the next iteration, check
|
|
|
889
888
|
2. Read the returned `RISK_SCORES: commit=X push=Y release=Z` line and the `RISK_REMEDIATIONS:` block (if present).
|
|
890
889
|
3. **Classify the residual + queue state (P250)**:
|
|
891
890
|
- **Above appetite (≥ 5/25)** — route to the **Above-appetite branch** below. Do NOT drain. Do NOT proceed to Step 6.75 until either (a) the auto-apply loop re-converges within appetite and drain succeeds, or (b) Rule 5 halt fires.
|
|
892
|
-
- **Within appetite (≤ 4/25) AND there is releasable material** (any unpushed commits on `HEAD..origin/<base>` OR any entries in `.changeset/`
|
|
893
|
-
- **Within appetite (≤ 4/25) AND empty queue** (no unpushed commits AND no `.changeset/` entries
|
|
894
|
-
|
|
895
|
-
**Cohort-graduation pre-check (per ADR-061 Rule 5; P246):** when the within-appetite-with-releasable-material branch fires AND `docs/changesets-holding/` is non-empty, invoke the graduation evaluator BEFORE the Drain action. The evaluator is the deterministic Rule 1a join + Rule 2 VP carve-out + Rule 3b cohort-grouping pass shipped in `@windyroad/risk-scorer` Phase 2a/2b. **The graduation criterion is evidence-of-working-as-desired (Rule 4 per-class evidence floor), not elapsed wall-clock time** — per user direction 2026-05-17: *"Dogfooding makes sense, but it shouldn't be time based, it should be until we are happy that it's working as desired."* + *"Why are we waiting? That seems to go against the principles if you ask me."* Calendar predicates are NEVER a primary graduation trigger; the evaluator's `status=resolved` IS the graduation signal.
|
|
896
|
-
|
|
897
|
-
1. Run the shim: `wr-risk-scorer-evaluate-graduation` (resolves to `packages/risk-scorer/scripts/evaluate-graduation.sh` per ADR-049 naming grammar). The script enumerates `docs/changesets-holding/*.md` (excluding README), applies ADR-061 Rule 1a join + Rule 2 VP carve-out + Rule 3b cohort grouping, and emits one `GRADUATION_CANDIDATE:` line per held entry plus a final `GRADUATION_SUMMARY:` line.
|
|
898
|
-
|
|
899
|
-
**Evaluator scope (load-bearing per P308)**: the evaluator script implements ONLY the deterministic Rule 1a + Rule 2 + Rule 3b passes. It does NOT compute release-risk and does NOT apply Rule 4 evidence-floor judgement — those are LLM-judgement surfaces owned by the orchestrator + the `wr-risk-scorer:pipeline` agent. Therefore evaluator `status=resolved` means *"the ticket-join succeeded and the entry is not VP-blocked"* only — it is **necessary but not sufficient** for graduation. The Rule 4 evidence-floor judgement (per-class evidence per ADR-061 Rule 4) MUST run as a separate orchestrator-side step before any `git mv` (P308 amendment).
|
|
900
|
-
2. Parse each `GRADUATION_CANDIDATE: changeset=<basename> | ticket=<P-id> | priority=<N> | class=<3a|3b> | [cohort=<id> |] status=<resolved|vp-blocked|halt-no-resolution>` line. Branch on `status`:
|
|
901
|
-
- **`status=resolved`** — route to **Rule 4 evidence-floor judgement** (P308 amendment; see step 2a below). The evaluator's `status=resolved` is necessary-but-not-sufficient; Rule 4 judgement is the LLM-owned surface that ratifies the *evidence floor* per ADR-061 Rule 4 + ADR-044 framework-resolution boundary. Do NOT auto-graduate at this point. Per ADR-061 Rule 5: the graduation criterion authorises the *intent*; Rule 4 judgement is the precondition that admits *evaluation* of that criterion.
|
|
902
|
-
- **`status=vp-blocked`** — skip. Per ADR-061 Rule 2 (Verification Pending carve-out; symmetric to ADR-042 Rule 2b). Do NOT graduate; held entry stays. The `.verifying.md` → `.closed.md` transition auto-clears the carve-out at a later Step 6.5 graduation pass.
|
|
903
|
-
- **`status=halt-no-resolution`** — halt. Per ADR-061 Rule 1a terminal: when neither filename-convention join nor body-grep fallback resolves a ticket, OR the resolved ticket file is missing/unreadable, the orchestrator MUST NOT auto-graduate. Route to the **Step 6.5 cohort-graduation halt-no-resolution** halt point (framework-prescribed halt — see Mid-loop ask discipline subsection); halt-with-batched-questions per the Step 2.5b cross-reference.
|
|
904
|
-
|
|
905
|
-
2a. **Rule 4 evidence-floor judgement (P308 — LLM-owned, NOT framework-resolved)**. For each `status=resolved` candidate, the orchestrator MUST evaluate whether the class-specific evidence floor has been met before graduating. The evidence floor (ADR-061 Rule 4) is per-class: PreToolUse:Bash gates need ≥1 gate-fire trace; UserPromptSubmit detectors need ≥1 detector firing; commit-hook-with-auto-fix needs ≥1 correctness-verified auto-fix commit; SessionStart additionalContext needs ≥1 session-trail entry. The orchestrator reads the held entry's `Reinstate criterion (evidence-based, ...)` line from `docs/changesets-holding/README.md` Currently held entry + cross-references the on-disk evidence artefact named in the criterion. Per ADR-044, Rule 4 judgement is LLM-owned (not framework-resolved). Route per AskUserQuestion availability:
|
|
906
|
-
|
|
907
|
-
- **Interactive (`AskUserQuestion` available)**: fire `AskUserQuestion` **per held entry** (NOT batched across cohort — each entry's evidence is independent, except for class=3b cohorts which graduate atomically per Rule 3b — when any cohort member's `AskUserQuestion` returns Graduate, ALL same-cohort members graduate together; when any returns Defer/Reject, the entire cohort stays held). Question text MUST inline (P350 brief-before-ID discipline): the held entry's basename + ticket + Priority, the per-entry evidence summary (from the README's Currently held entry — Rule 4 class-specific shape + cited artefact), and the candidate verdict. Options: **Graduate (evidence cited and met)** / **Defer (evidence not yet met — preserve hold)** / **Reject (manual intervention — surface back to user)**. Per ADR-013 Rule 1 cap (≤4 per call, sequential if >4). On `Graduate`, fall through to step 2b graduate-action. On `Defer`/`Reject`, the held entry stays in `docs/changesets-holding/`; emit one Auto-apply-trail line per ADR-061 Rule 6 citing the user's verdict + reason; proceed to next candidate. This per-entry AskUserQuestion is a framework-prescribed user-interaction surface — see Mid-loop ask discipline subsection.
|
|
908
|
-
- **AFK (`AskUserQuestion` forbidden — iter subprocess / non-interactive context per P352 / ADR-013 Rule 6)**: queue one `outstanding_question` entry (category: `direction`) per held entry to `.afk-run-state/outstanding-questions.jsonl`. The entry's `question` field inlines the same evidence summary the interactive `AskUserQuestion` would surface (P350 brief-before-ID — do NOT use opaque IDs alone). Do NOT graduate. Continue Drain action: any pre-existing `.changeset/` entries still drain per the within-appetite contract (those entries were not graduations and are unaffected). Per P352 user-ratified universal AFK default: queue-and-continue (NOT halt, NOT silent skip, NOT auto-default). Loop-end Step 2.5 surfaces the queued questions as batched `AskUserQuestion` per the existing accumulated-question discipline.
|
|
909
|
-
|
|
910
|
-
2b. **Graduate-action (Rule 4-ratified path)**. When Rule 4 evidence-floor judgement returns `Graduate` (interactive `AskUserQuestion` Graduate verdict) for a held entry: perform `git mv docs/changesets-holding/<basename> .changeset/<basename>`. Append the entry to `docs/changesets-holding/README.md` "Recently reinstated" with the citation: `<basename> — graduation criterion met (status=resolved per Rule 1a join to <P-id>, Priority <N>; Rule 4 evidence-floor ratified per user verdict <verbatim>); class <3a|3b>; evidence cited`. For class=3b cohorts: when any cohort member's Rule 4 judgement returns `Graduate`, ALL same-cohort members with `status=resolved` graduate together atomically (Rule 3b cohort propagation — entire cohort ships or none does); when any returns Defer/Reject, the entire cohort stays held. Amend the iter's main commit per ADR-042 Rule 3 amend-based folding to preserve the ADR-032 one-commit-per-iteration invariant.
|
|
911
|
-
|
|
912
|
-
3. After processing all candidates: if anything graduated, the just-moved entries are now in `.changeset/` and ride the existing Drain action (no separate re-entry needed — the Drain action's `release:watch` step picks them up when `.changeset/` is non-empty). Proceed to the Drain action below.
|
|
913
|
-
4. **Governance gates apply (ADR-061 Rule 7)**: every graduation reinstate goes through the standard ADR-014 commit flow — architect / JTBD / risk-scorer gates ride the amend commit; gate rejection routes to ADR-042 Rule 5 halt with the rejection reason logged. The graduation criterion authorises the *intent*; the gates authorise the *action*.
|
|
914
|
-
|
|
915
|
-
**Idempotency**: safe to invoke when holding-area is empty (script exits 1 with `GRADUATION_SUMMARY: total=0` — orchestrator skips graduation, proceeds to Drain action). Safe when no candidates resolve (all `vp-blocked`) — no `git mv` operations, no README mutation, no commit amendment. Safe when AFK + all `status=resolved` route to queue (no graduation performed; outstanding_questions accumulate for loop-end surfacing per P352 / ADR-013 Rule 6).
|
|
916
|
-
|
|
917
|
-
**Audit trail (ADR-061 Rule 6)**: every `reinstate-from-holding` graduation appends one Auto-apply-trail line to the iter report AND one "Recently reinstated" line to `docs/changesets-holding/README.md` with the resolved problem-ticket ID, Priority value, graduation class (3a or 3b), and the evidence citation. The audit trail is the load-bearing artefact for ADR-026 cite + persist + uncertainty grounding.
|
|
891
|
+
- **Within appetite (≤ 4/25) AND there is releasable material** (any unpushed commits on `HEAD..origin/<base>` OR any entries in `.changeset/`) — drain the queue per the Drain action below, then proceed to Step 6.75. The release-action threshold is "is there something to release?", NOT "has accumulated risk reached the safety band?" Per user direction 2026-05-17 (P250 Description): *"If it's low risk, you should release."* Low cost to release + low residual risk = release now; never accumulate.
|
|
892
|
+
- **Within appetite (≤ 4/25) AND empty queue** (no unpushed commits AND no `.changeset/` entries) — no drain (literally nothing to release). Proceed to Step 6.75. This is the genuine no-op fast-path; the gate is *absence of releasable material*, not residual band.
|
|
918
893
|
|
|
919
894
|
**Drain action (non-interactive, policy-authorised per ADR-013 Rule 6):**
|
|
920
895
|
|
|
@@ -948,8 +923,6 @@ ADR-022 prescribes that Known Error tickets transition to Verification Pending o
|
|
|
948
923
|
|
|
949
924
|
**Composition with the Above-appetite branch (below)**: the K→V callback is anchored to the within-appetite Drain action step 4 — it does NOT fire after the above-appetite Rule 5 halt (no release shipped → nothing to match) and it does NOT fire mid-loop in the above-appetite auto-apply loop. When the auto-apply loop converges and re-enters the within-appetite Drain action, the K→V callback fires there per step 4.
|
|
950
925
|
|
|
951
|
-
**Composition with Cohort-graduation pre-check (P246)**: the cohort-graduation pre-check (step 2a above) fires BEFORE the Drain action; its `git mv` operations from `docs/changesets-holding/` to `.changeset/` happen BEFORE release:watch and ship as part of the same release. The K→V callback fires AFTER release:watch and consumes the just-shipped changeset set — so graduated cohorts that ship in the same release are correctly matched by the enumerator (the deleted-from-tree changeset has the graduated basename; the K-ticket's `**Release vehicle**: .changeset/<basename>.md` reference matches).
|
|
952
|
-
|
|
953
926
|
Per ADR-022 (Verifying lifecycle) + ADR-018 (release-cadence host) + ADR-010 amended P093 (transition-problem authoritative executor) + ADR-014 (per-transition commit grain) + ADR-013 Rule 5 (policy-authorised silent-proceed) + ADR-044 (framework-resolution boundary) + P228 (this ticket) + P233 (sibling callback) + P267 (derive-release-vehicle composed helper) + P330 (Release vehicle seed reference — input signal).
|
|
954
927
|
|
|
955
928
|
**Post-release cache refresh (P233) — fires only after within-appetite Drain action step 5 (above):**
|
|
@@ -1006,11 +979,12 @@ After a successful release-cadence drain has shipped a new plugin version to npm
|
|
|
1006
979
|
- R1 | <description> | <effort S/M/L> | <risk_delta -N> | <files affected>
|
|
1007
980
|
- R2 | ...
|
|
1008
981
|
```
|
|
1009
|
-
2. Read the descriptions. Decide what to do. The agent MAY follow a scorer suggestion, adapt it, or do something else entirely. There is no requirement to rank all suggestions upfront or iterate through them in order.
|
|
982
|
+
2. Read the descriptions. Decide what to do. The agent MAY follow a scorer suggestion, adapt it, or do something else entirely. There is no requirement to rank all suggestions upfront or iterate through them in order. The selected action and the branch report MUST name how actual shipped risk changes (for example: split the change, add a control, disable the risky behaviour, or revert it); generic "apply remediation" wording is not enough.
|
|
1010
983
|
3. **Verification Pending carve-out (ADR-042 Rule 2b)**: if a remediation targets a commit attached to a `.verifying.md` ticket, do NOT auto-revert it. Skip that suggestion and decide on the next one.
|
|
1011
984
|
4. Apply the chosen action using standard primitives (git, Edit, Bash). Example actions the agent might take:
|
|
1012
|
-
- `move-to-holding`: `git mv .changeset/<name>.md docs/changesets-holding/<name>.md`. Append the entry to `docs/changesets-holding/README.md` under "Currently held" per ADR-042 Rule 6. Amend the iteration's commit to fold the move (per ADR-042 Rule 3 amend-based folding — preserves ADR-032 one-commit-per-iteration invariant).
|
|
1013
985
|
- `revert-commit`: `git revert --no-edit <sha>`. The scorer SHOULD supply the target commit SHA in the `description` column (e.g., "Revert commit 9a1f96c that introduced the risky gate"). Before executing, verify the SHA is NOT attached to a `.verifying.md` ticket (Rule 2b carve-out). After revert, amend the iteration's commit to fold the revert. If `git revert` produces merge conflicts, route to Rule 5 halt with the conflict detail — do not attempt non-interactive conflict resolution.
|
|
986
|
+
- `split-change`: split the iteration into independently releasable, within-appetite commits and re-score each slice.
|
|
987
|
+
- `disable-or-revert`: remove the risky behaviour from shipped code until its required evidence or control exists.
|
|
1014
988
|
5. Re-invoke the risk scorer (same delegation path as step 1 above — subagent preferred, skill fallback). Read the new `RISK_SCORES:` line.
|
|
1015
989
|
6. **Loop classification**:
|
|
1016
990
|
- **Re-score within appetite (≤ 4/25)** — proceed to Drain action above. Done with the above-appetite branch.
|
|
@@ -1025,13 +999,13 @@ After a successful release-cadence drain has shipped a new plugin version to npm
|
|
|
1025
999
|
- The final `RISK_SCORES:` line
|
|
1026
1000
|
- An "Auto-apply trail" subsection listing each remediation attempted with outcome
|
|
1027
1001
|
- Any Verification Pending ticket IDs implicated per Rule 2b
|
|
1028
|
-
- A one-line scorer-gap note (e.g., "
|
|
1002
|
+
- A one-line scorer-gap note (e.g., "available remediations did not reduce residual risk below 5/25 — extend scorer vocabulary per P108")
|
|
1029
1003
|
|
|
1030
1004
|
**Step 2.5b cross-reference (P126)**: before emitting the Rule 5 halt iteration summary, run Step 2.5b's surfacing routine. The routine is gated on ≥1 accumulated user-answerable skip; Rule 5 halts that fire late in a long AFK loop frequently have accumulated skips from prior iters, so Step 2.5b's AskUserQuestion-default branch typically fires (`halt-paths-must-route-design-questions-through-Step-2.5b`). **Critical guard (architect FLAG)**: Step 2.5b surfaces *prior-iter accumulated user-answerable skips only* — it does NOT ask the user how to remediate the above-appetite state itself; the halt-causing scorer-gap remains a halt-with-bug-signal per ADR-042 Rule 5 invariant ("never release above appetite", scorer is the decision surface, not the user). Surfacing prior-iter skips does not retry the above-appetite remediation, does not bypass the never-release-above-appetite invariant, and does not convert the halt into a non-halt — it just takes the existing prior-iter user-input round-trip with it.
|
|
1031
1005
|
|
|
1032
|
-
Halt is a **bug signal** — the scorer should always have progressively more aggressive remediations available once P108 lands.
|
|
1006
|
+
Halt is a **bug signal** — the scorer should always have progressively more aggressive remediations available once P108 lands.
|
|
1033
1007
|
|
|
1034
|
-
**Audit trail (ADR-042 Rule 6):** append one line per auto-apply to the iteration summary's Auto-apply trail subsection, including remediation ID, action class, pre/post scores, action taken, and description citation.
|
|
1008
|
+
**Audit trail (ADR-042 Rule 6):** append one line per auto-apply to the iteration summary's Auto-apply trail subsection, including remediation ID, action class, pre/post scores, action taken, and description citation.
|
|
1035
1009
|
|
|
1036
1010
|
### Step 6.75: Inter-iteration verification (P036)
|
|
1037
1011
|
|
|
@@ -1086,11 +1060,8 @@ When `AskUserQuestion` is unavailable or the user is AFK, the skill (and the del
|
|
|
1086
1060
|
| Scope expansion during work | Update problem file, re-score WSJF, move to next problem instead of continuing |
|
|
1087
1061
|
| Commit when risk within appetite | Auto-commit (manage-problem step 9e fallback) |
|
|
1088
1062
|
| Commit when risk above appetite | Skip commit, report uncommitted state |
|
|
1089
|
-
| Pipeline risk within appetite (≤ 4/25) with releasable material (any unpushed commits OR any `.changeset/` entries
|
|
1090
|
-
| Pipeline risk within appetite (≤ 4/25) AND empty queue (no unpushed commits AND no `.changeset/`
|
|
1091
|
-
| Cohort-graduation pre-check fires before Drain action (within-appetite branch, `docs/changesets-holding/` non-empty) — evaluator returns `status=resolved` | Route to Rule 4 evidence-floor judgement (LLM-owned per ADR-061 Rule 4 + ADR-044 framework-resolution boundary). Evaluator's `status=resolved` is necessary-but-not-sufficient (P308 — evaluator script disclaims Rule 4 at lines 19-22). Interactive: per-held-entry `AskUserQuestion` with inline evidence summary (P350 brief-before-ID) + 3 options (Graduate / Defer / Reject). AFK: queue per-held-entry `outstanding_question` to `.afk-run-state/outstanding-questions.jsonl` (P352 / ADR-013 Rule 6 queue-and-continue universal default) — do NOT graduate, continue Drain for any pre-existing `.changeset/` entries. On Graduate verdict: `git mv docs/changesets-holding/<basename> .changeset/<basename>`, append README "Recently reinstated" entry citing the user's Rule 4 verdict, amend the iter's main commit per ADR-042 Rule 3. For class=3b cohorts, all cohort members graduate atomically on any-member Graduate verdict (Rule 3b cohort propagation); any Defer/Reject keeps entire cohort held. Per ADR-061 Rule 4 + Rule 5 + Rule 6 + Rule 7 + ADR-013 Rule 6 + P246 + P308 + P350 + P352 (Step 6.5 Cohort-graduation pre-check; step 2a Rule 4 evidence-floor judgement). Graduation criterion is evidence-of-working-as-desired (Rule 4 evidence floor), not elapsed wall-clock time — user direction 2026-05-17: "Dogfooding makes sense, but it shouldn't be time based, it should be until we are happy that it's working as desired." |
|
|
1092
|
-
| Cohort-graduation pre-check — evaluator returns `status=vp-blocked` | Skip. Per ADR-061 Rule 2 Verification Pending carve-out (symmetric to ADR-042 Rule 2b). Do NOT graduate; held entry stays. `.verifying.md` → `.closed.md` transition auto-clears the carve-out at a later pass. Per ADR-061 Rule 2 + P246. |
|
|
1093
|
-
| Cohort-graduation pre-check — evaluator returns `status=halt-no-resolution` | Halt at the framework-prescribed "Step 6.5 cohort-graduation halt-no-resolution" halt point. Per ADR-061 Rule 1a terminal: ambiguous join is a user-decision surface, not an agent-decision surface. Halt-with-batched-questions per the Step 2.5b cross-reference. Per ADR-061 Rule 1a + P246. |
|
|
1063
|
+
| Pipeline risk within appetite (≤ 4/25) with releasable material (any unpushed commits OR any `.changeset/` entries) | Drain release queue (`push:watch` then, if releasable changesets exist, `release:watch`) before next iteration — per ADR-018 (Step 6.5) as amended by P250. Trigger is *presence of releasable material*, not residual band reaching appetite. User direction 2026-05-17: "If it's low risk, you should release." |
|
|
1064
|
+
| Pipeline risk within appetite (≤ 4/25) AND empty queue (no unpushed commits AND no `.changeset/`) | No drain — literally nothing to release. Proceed directly to Step 6.75. The genuine no-op fast-path per P250. |
|
|
1094
1065
|
| Post-release K→V auto-transition between iters (P228) | After a successful within-appetite Drain action shipped a release to npm, invoke `wr-itil-enumerate-postrelease-kv-candidates` to enumerate `.known-error.md` tickets whose `**Release vehicle**: .changeset/<name>.md` citation matches a just-shipped (deleted-from-tree) changeset, and dispatch `/wr-itil:transition-problem <NNN> verifying` per emitted `KV_CANDIDATE` line. Conditional on actual release (skipped when `push:watch` ran alone with no changeset); non-blocking on individual transition failure (logs per-ticket, continues to next candidate; persistent failures route to Step 2.5b accumulated questions). V→C remains a maintainer-only surface — this callback fires K→V only. Per ADR-022 + ADR-018 + ADR-010 amended P093 + ADR-014 + ADR-013 Rule 5 + ADR-044 + P228 + P233 + P267 + P330 (Step 6.5 Post-release K→V auto-transition subsection). |
|
|
1095
1066
|
| Post-release plugin cache refresh between iters (P233) | After a successful within-appetite Drain action shipped a release to npm, chain `/install-updates` to refresh the plugin cache before the next iter dispatches. Conditional on actual release (skipped when `push:watch` ran alone with no changeset); non-blocking on `/install-updates` failure (degrades to cache-stays-stale, equivalent to pre-amendment behaviour). Mid-loop ask discipline preserved by treating any `/install-updates` AskUserQuestion surface AS the Non-interactive fallback dry-run path. Per ADR-013 Rule 5 + ADR-044 + P130 + P106 + P233 (Step 6.5 Post-release cache refresh subsection). |
|
|
1096
1067
|
| CI failure during Step 6.5 drain (within-appetite branch) | Diagnose via `gh run view --log-failed`, classify against the closed fixable-in-iter allow-list (P081-class stale-grep-string, hook stub mismatch, test ID drift, environmental flake), fix-and-continue for fixable classes (each retry rides its own ADR-014 commit gate), 3-retry cap per iteration, halt for unrecoverable classes. Ambiguous classification defaults to halt. ADR-013 Rule 5 policy-authorised. Per ADR-026 grounding + ADR-044 framework-resolution boundary + P140 (Step 6.5 Failure handling). |
|
|
@@ -1105,7 +1076,7 @@ When `AskUserQuestion` is unavailable or the user is AFK, the skill (and the del
|
|
|
1105
1076
|
| Unexpected dirty state between iterations | Halt the loop. Report the `git status --porcelain` output, the last iteration's reported outcome, and the divergence — per P036 (Step 6.75). Run Step 2.5b before emitting the halt summary if ≥1 accumulated user-answerable skip from prior iters (P126). Do NOT attempt non-interactive recovery of the dirty state itself. |
|
|
1106
1077
|
| Iter committed cleanly + claim contradicts on-disk ADR Confirmation state (P335) | Halt the loop with `outcome: halted-iter-over-claim`. Include the `wr-itil-verify-iter-summary` stdout (the `OVER-CLAIM: ADR-NNN has N unchecked Confirmation item(s)...` lines) as the divergence detail. Run Step 2.5b before emitting the halt summary if ≥1 accumulated user-answerable skip from prior iters. Do NOT auto-correct the iter's claim — the orchestrator cannot retroactively make a false claim true; the user adjudicates on return (re-dispatch / accept partial / amend). Per ADR-013 Rule 6 + ADR-032 subprocess-boundary trust contract + P335 (Step 6.75 verify-iter-claims sub-step). |
|
|
1107
1078
|
| External root cause detected at Open → Known Error, or at park with `upstream-blocked` reason | **Auto-invoke `/wr-itil:report-upstream`** via the manage-problem Step 6 external-root-cause detection AFK fallback (per ADR-024 2026-06-04 (P270) amendment). The report-upstream skill composes the draft then scores the prose via `wr-risk-scorer:external-comms` (ADR-028); below-appetite → sends (public-issue Step 5 / comment Step 5c / security Step 6 per classification); above-appetite → risk-reduces (open-ended LLM judgement per leaf (a)) then re-scores → sends-or-queues to `## Queued Upstream Report` (leaf (c)). Security routing per leaf (b): upstream-with-`SECURITY.md` + below-appetite → files via declared channel; upstream-without-`SECURITY.md` → external-comms-gated impact assessment. Queue does NOT halt (P352). Tickets already carrying the stable `- **Upstream report pending** -- external dependency identified; invoke /wr-itil:report-upstream when ready` marker from prior sessions are detected via the already-noted grep check and routed to the report-upstream invocation; the marker shape is retained as the detection substrate (ASCII `--` per P210 — em-dash variant is the legacy form, still matched by the already-noted check for backward compatibility). Per P063 (amended 2026-06-04) + P270 + ADR-013 Rule 6. |
|
|
1108
|
-
| Mid-loop ask between iters in the orchestrator's main turn | Forbidden except at framework-prescribed user-interaction points (Step 0 session-continuity / fetch-failure halt; Step 2.5 / 2.5b loop-end emit; Step 6.5 above-appetite Rule 5 halt; Step 6.5 CI-failure / release:watch halt; Step 6.
|
|
1079
|
+
| Mid-loop ask between iters in the orchestrator's main turn | Forbidden except at framework-prescribed user-interaction points (Step 0 session-continuity / fetch-failure halt; Step 2.5 / 2.5b loop-end emit; Step 6.5 above-appetite Rule 5 halt; Step 6.5 CI-failure / release:watch halt; Step 6.75 dirty-for-unknown-reason halt). The loop's purpose is **progress + accumulation**; mechanical-stage transitions between iters are framework-resolved and MUST NOT prompt the user. Per ADR-044 framework-resolution boundary + ADR-013 Rule 1 (as amended by ADR-044) + P130. |
|
|
1109
1080
|
|
|
1110
1081
|
### Mid-loop ask discipline (orchestrator main turn) — P130
|
|
1111
1082
|
|
|
@@ -1118,8 +1089,6 @@ The orchestrator MUST NOT call `AskUserQuestion` between iterations except at th
|
|
|
1118
1089
|
- **Step 2.5 / Step 2.5b loop-end emit** — accumulated `outstanding_questions` queue presented as batched `AskUserQuestion` (or fallback Outstanding Design Questions table per ADR-013 Rule 6). This is the framework's prescribed user-interaction point; do NOT dilute it by asking earlier.
|
|
1119
1090
|
- **Step 6.5 above-appetite Rule 5 halt** — auto-apply loop exhausted without convergence; halt-with-batched-questions per the Step 2.5b cross-reference (Step 2.5b surfaces *prior-iter accumulated user-answerable skips only* — the halt-causing scorer-gap remains a halt-with-bug-signal per ADR-042 Rule 5).
|
|
1120
1091
|
- **Step 6.5 CI-failure / `release:watch` failure halt** — push:watch or release:watch failed AND the failure is genuinely-unrecoverable (outside the fixable-in-iter allow-list, or 3-retry cap reached); halt-with-batched-questions per the Step 2.5b cross-reference. Failures inside the closed allow-list route to fix-and-continue per Step 6.5 Failure handling (P140), not this halt point.
|
|
1121
|
-
- **Step 6.5 cohort-graduation halt-no-resolution halt (P246)** — graduation evaluator returned `status=halt-no-resolution` for one or more held candidates (Rule 1a terminal: neither filename-convention join nor body-grep fallback resolved a problem ticket, OR the resolved ticket file is missing/unreadable). The orchestrator MUST NOT auto-graduate under ambiguity per ADR-061 Rule 1a; halt-with-batched-questions per the Step 2.5b cross-reference. The halt-causing ambiguity itself remains a halt-with-bug-signal (the held entry stays in `docs/changesets-holding/`; manual reinstate or ticket-file correction required); Step 2.5b surfaces *prior-iter accumulated user-answerable skips only* and does NOT ask the user to resolve the ambiguity itself.
|
|
1122
|
-
- **Step 6.5 cohort-graduation per-entry Rule 4 evidence-floor judgement (P308) — interactive only** — graduation evaluator returned `status=resolved` for ≥1 held candidate AND `AskUserQuestion` is available. Per ADR-061 Rule 4 + ADR-044 framework-resolution boundary, Rule 4 evidence-floor judgement is LLM-owned (not framework-resolved); the user ratifies per held entry with Graduate / Defer / Reject before any `git mv`. This is NOT a halt — the orchestrator continues the loop after the user verdict (graduate path performs git mv + README append + ADR-042 Rule 3 amend; defer/reject paths preserve the hold). When `AskUserQuestion` is unavailable (AFK path), the orchestrator queues `outstanding_question` entries per held candidate per P352 / ADR-013 Rule 6 queue-and-continue universal default — does NOT halt, does NOT silently proceed, does NOT auto-default. The held entries' user ratifications then surface at Step 2.5 loop-end via the existing accumulated-questions discipline.
|
|
1123
1092
|
- **Step 6.75 dirty-for-unknown-reason halt** — `git status --porcelain` divergence; halt-with-batched-questions per the Step 2.5b cross-reference.
|
|
1124
1093
|
- **Step 6.75 iter-over-claim halt (P335)** — `wr-itil-verify-iter-summary` detected the iter's commit message or `ITERATION_SUMMARY.notes` contains completion-claim language for an ADR whose `## Confirmation` section still has unchecked `- [ ]` items; halt-with-batched-questions per the Step 2.5b cross-reference. The over-claim itself remains a halt-with-bug-signal — Step 2.5b surfaces *prior-iter accumulated user-answerable skips only*; it does NOT ask the user how to remediate the false claim (re-dispatch / accept partial / amend the commit remains a user decision on return).
|
|
1125
1094
|
|
|
@@ -1228,7 +1197,7 @@ When every skipped ticket is in the `upstream-blocked` category (stop-condition
|
|
|
1228
1197
|
## Related
|
|
1229
1198
|
|
|
1230
1199
|
- **P341** (`docs/problems/open/341-work-problems-skill-must-surface-outstanding-questions-then-run-retro-before-emitting-all-done.md`) — driver for Step 2.4 Pre-`ALL_DONE` gate sequence (UNCONDITIONAL fire of outstanding-questions surface + session-level retro before `ALL_DONE` emit). 2026-05-31 user direction (verbatim in ticket Description): *"The work-problems skill MUST surface the outstanding questions at the end before emitting ALL_DONE. It MUST then run a retro. Only then should it emit ALL_DONE."* Closes the structural gap that allowed `ALL_DONE` to emit while direction-class observations remained queued AND without a session-level retro running. Behavioural second-source: `test/work-problems-p341-pre-all-done-gate.bats`. Composes with P086 (extends iter-level retro-on-exit to orchestrator-level), P126 (preserves `halt-paths-must-route-design-questions-through-Step-2.5b` principle), ADR-014 (retro commits its own work), ADR-044 (framework-resolution boundary for when to surface — now framework-resolved as unconditional pre-`ALL_DONE`).
|
|
1231
|
-
- **P390** (`docs/problems/known-error/390-agent-declares-all-done-prematurely-while-actionable-backlog-remains.md`) — driver for Step 2.4 **Gate (0) — Objective backlog-empty assertion** (prepended ahead of gate (a)). Bug shape: the orchestrator emitted `ALL_DONE` while a dispatchable Tier-2 backlog remained, by generalising "the *salient* remainder is interactive-gated" to "Step 2 stop-condition #2 holds" — a subjective stop the framework did not authorise; it also skipped P382 (Tier-1 sev-16) entirely. Fix: before `ALL_DONE`, gate (0) re-scans the live open/known-error backlog (fresh dual-tolerant glob, not the Step 1 cache) and classifies each ticket dispatchable/non-dispatchable OBJECTIVELY by recorded marker (verifying / `## Fix Released`; upstream-blocked; blocked dead-end; Step 3.5/3.6 durable per-session skip record
|
|
1200
|
+
- **P390** (`docs/problems/known-error/390-agent-declares-all-done-prematurely-while-actionable-backlog-remains.md`) — driver for Step 2.4 **Gate (0) — Objective backlog-empty assertion** (prepended ahead of gate (a)). Bug shape: the orchestrator emitted `ALL_DONE` while a dispatchable Tier-2 backlog remained, by generalising "the *salient* remainder is interactive-gated" to "Step 2 stop-condition #2 holds" — a subjective stop the framework did not authorise; it also skipped P382 (Tier-1 sev-16) entirely. Fix: before `ALL_DONE`, gate (0) re-scans the live open/known-error backlog (fresh dual-tolerant glob, not the Step 1 cache) and classifies each ticket dispatchable/non-dispatchable OBJECTIVELY by recorded marker (verifying / `## Fix Released`; upstream-blocked; blocked dead-end; Step 3.5/3.6 durable per-session skip record). ≥1 dispatchable ticket FORBIDS `ALL_DONE` and loops back to Step 3 tier-first selection (loopback, not halt); a user-directed pivot does not discharge the Tier-exhaustion obligation. Sibling loop-control-stop class: P332 (run-retro skip rationalisation), P148 (Stage-1 ticketing skip), P175 (scope-pin loop-control inference); hardens P341's precondition. Behavioural second-source: `eval/promptfooconfig.yaml` Tier-A regex + Tier-B llm-rubric asserting the orchestrator does NOT emit `ALL_DONE` when ≥1 dispatchable ticket remains. Per ADR-044 "Continue / stop loops" framework-resolution (the natural stop is concrete — `ALL_DONE` conditions objectively met). **Reopened fix (2026-07-05 → 2026-07-06, ADR-094 / RFC-047 / STORY-040)**: Gate (0) alone proved insufficient (self-assessment — the same actor decides "should I stop" and "is stopping justified"); Step 0e anchors the loop with the native `/goal` external evaluator (per-turn small-fast-model judgement of the printed gate (0) table), gate (0)'s classification MUST be printed in turn output as the evaluator's evidence, and the headless launch shape `claude -p "/goal <canonical condition>"` is the anchor-guaranteed path (interactive starts get a nudge-and-proceed fallback).
|
|
1232
1201
|
- **P342** (`docs/problems/open/342-iter-retros-queue-observations-as-outstanding-questions-instead-of-auto-ticketing-same-trust-boundary-as-step-4a.md`) — driver for Step 5 iter-prompt body's retro-surfaced observation classification taxonomy and capture-* carve-out. Iter retros' observations of recurring class-of-behaviour now route to `/wr-itil:capture-problem` (mechanical-stage carve-out per run-retro Step 4a precedent); only direction-setting observations queue at `outstanding_questions`; ambiguous defaults to auto-ticket per the trust-boundary asymmetry. The "no `capture-*` siblings mid-loop" rule is preserved for non-retro mid-iter capture (P078-class spam); the carve-out is bounded to the retro path. Sibling locus: `packages/retrospective/skills/run-retro/SKILL.md` Step 4b carries the symmetric mirror (same trust-boundary fires whether retro runs in iter context OR standalone in main turn). Behavioural second-source: `test/work-problems-p342-retro-auto-ticket-carveout.bats` + `packages/retrospective/skills/run-retro/test/run-retro-step-4b-retro-auto-ticket-carveout.bats`. Composes with run-retro Step 4a (precedent), ADR-013 Rule 5 (policy-authorised silent proceed), ADR-032 (foreground-spawns-N-background fanout already documented for Stage 1 in run-retro Step 4b), ADR-044 (mechanical-stage carve-out), P130 (mid-loop AskUserQuestion ban unchanged), P078 (capture-on-correction — distinct trigger surface; both end in capture but for different signals).
|
|
1233
1202
|
- **P121** (`docs/problems/121-afk-orchestrator-should-sigterm-stuck-subprocesses-after-idle-timeout.verifying.md`) — driver for Step 5's backgrounded-poll-loop dispatch shape (replacing the prior foreground-synchronous form) and the idle-timeout SIGTERM branch. The 2026-04-25 P118 iter 5 evidence: an iteration subprocess sat idle ~70 min after its final commit, then SIGTERM produced a clean JSON exit-flush. Fix: orchestrator backgrounds the subprocess, polls every 60s, computes `LAST_ACTIVITY_MARK = max(DISPATCH_START_EPOCH, git log -1 --format=%at HEAD)`, and sends SIGTERM when `now - LAST_ACTIVITY_MARK > WORK_PROBLEMS_IDLE_TIMEOUT_S` (default 3600s = 60 min). Behavioural second-source: `test/work-problems-step-5-idle-timeout-sigterm.bats` exercises a fake `claude -p` shim that sleeps past the threshold and asserts SIGTERM, JSON exit-flush, env-var override, and within-threshold no-fire. Step 6's per-iter progress line SHOULD annotate `(SIGTERM_SENT)` when the branch fires so users can distinguish recovered iters from natural completions. ADR-032's subprocess-boundary variant amended 2026-04-26 with the backgrounded-poll-loop refinement.
|
|
1234
1203
|
- **P146** (`docs/problems/146-afk-iteration-subprocess-bash-until-loop-polls-bats-output-with-bats-console-regex-against-tap-format.verifying.md`) — driver for Step 5 iteration prompt body's bats-output-polling-discipline clause. The 2026-04-29 incident (iter 1, PID 23580 child PID 16408) saw a `bash until`-loop poll a backgrounded bats output file with regex `^[0-9]+ tests?,` (bats's *default* console-summary format) against `bats --tap` output that never emits that line — silent infinite spin after bats completed; manual SIGTERM at 68m34s wall-clock; metadata loss per the P147 stuck-before-emit subclass. The polling idiom is NOT taught by any SKILL.md (audit confirmed via repo grep) — it is agent-learned from training data. Fix: prompt-discipline rule in the iteration prompt body's Constraints list explicitly forbidding the antipattern, naming `wait $bg_pid` (or Bash-tool `run_in_background=true` + `BashOutput`) as the safe substitute, and citing the TAP-vs-console-summary divergence so future contributors don't "fix" the rule incorrectly. Behavioural second-source: `test/work-problems-step-5-bats-polling-discipline.bats` asserts the prohibition phrase, the safe-substitute pointer, the P146 cite, the divergence explanation, and the Related-section cite.
|
|
@@ -46,17 +46,18 @@ setup() {
|
|
|
46
46
|
[ "$status" -eq 0 ]
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
@test "SKILL.md
|
|
50
|
-
# The one currently-implemented action class moves changesets to the holding
|
|
51
|
-
# area. The path must be named so the skill body is unambiguous about target.
|
|
49
|
+
@test "SKILL.md does not use a held-changeset directory as remediation" {
|
|
52
50
|
run grep -n "docs/changesets-holding/" "$SKILL_FILE"
|
|
53
|
-
[ "$status" -
|
|
51
|
+
[ "$status" -ne 0 ]
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
@test "SKILL.md
|
|
57
|
-
# "move-to-holding" is the single supported class today; later P108 extends.
|
|
58
|
-
# The string must appear so the enumeration is greppable.
|
|
54
|
+
@test "SKILL.md does not offer move-to-holding as a remediation" {
|
|
59
55
|
run grep -n "move-to-holding" "$SKILL_FILE"
|
|
56
|
+
[ "$status" -ne 0 ]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@test "SKILL.md offers shipment-affecting remediation classes" {
|
|
60
|
+
run grep -nE "split-change|disable-or-revert|revert-commit" "$SKILL_FILE"
|
|
60
61
|
[ "$status" -eq 0 ]
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
# pivot on releasable material, not residual band. The defective prior
|
|
5
5
|
# clause "Within appetite (≤ 3/25) — no drain needed" encoded
|
|
6
6
|
# accumulation-permitted-below-threshold semantics that violated the
|
|
7
|
-
#
|
|
8
|
-
# direction: "If it's low risk, you should release."
|
|
7
|
+
# user's verbatim direction: "If it's low risk, you should release."
|
|
9
8
|
#
|
|
10
9
|
# Amended contract (three-band):
|
|
11
10
|
# 1. Above appetite (≥ 5/25) → ADR-042 auto-apply (unchanged).
|
|
@@ -13,8 +12,7 @@
|
|
|
13
12
|
# 3. Within appetite (≤ 4/25) AND empty queue → no drain (no-op fast-path).
|
|
14
13
|
#
|
|
15
14
|
# The trigger for the drain action is *presence of releasable material*
|
|
16
|
-
# (any unpushed commits OR any .changeset/ entries
|
|
17
|
-
# eligible held entries per ADR-061 Rule 1). The residual band remains
|
|
15
|
+
# (any unpushed commits OR any .changeset/ entries). The residual band remains
|
|
18
16
|
# the safety check (above-appetite never releases) but is no longer the
|
|
19
17
|
# action gate for the within-appetite branch.
|
|
20
18
|
#
|
|
@@ -32,7 +30,6 @@
|
|
|
32
30
|
# @adr ADR-018 (release-cadence policy parent — amended in same commit)
|
|
33
31
|
# @adr ADR-037 (skill-testing strategy — contract-assertion class)
|
|
34
32
|
# @adr ADR-042 (above-appetite branch — preserved unchanged)
|
|
35
|
-
# @adr ADR-061 (Rule 1 symmetric-balance principle — parent principle)
|
|
36
33
|
# @jtbd JTBD-006 (Progress the Backlog While I'm Away — primary)
|
|
37
34
|
# @jtbd JTBD-002 (Ship with Confidence — composes; small frequent releases)
|
|
38
35
|
|
|
@@ -85,7 +82,7 @@ setup() {
|
|
|
85
82
|
|
|
86
83
|
@test "work-problems P250: Within-appetite + releasable material triggers drain" {
|
|
87
84
|
# The load-bearing positive contract: within appetite AND any unpushed
|
|
88
|
-
# commits OR changeset
|
|
85
|
+
# commits OR changeset entry → drain.
|
|
89
86
|
run grep -nE 'Within appetite \(≤ 4/25\) AND there is releasable material' "$SKILL_MD"
|
|
90
87
|
[ "$status" -eq 0 ]
|
|
91
88
|
}
|
|
@@ -100,14 +97,6 @@ setup() {
|
|
|
100
97
|
[ "$status" -eq 0 ]
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
@test "work-problems P250: releasable-material clause enumerates ADR-061 Rule 1 graduation-eligible held entries" {
|
|
104
|
-
# ADR-061 cross-reference: the symmetric-balance disjunct ensures
|
|
105
|
-
# held entries that have decayed within appetite are graduation-
|
|
106
|
-
# eligible AND drainable.
|
|
107
|
-
run grep -nE 'graduation-eligible.*ADR-061 Rule 1|ADR-061 Rule 1.*graduation-eligible|docs/changesets-holding.*ADR-061' "$SKILL_MD"
|
|
108
|
-
[ "$status" -eq 0 ]
|
|
109
|
-
}
|
|
110
|
-
|
|
111
100
|
# ── Within-appetite + empty queue no-op fast-path ─────────────────────────
|
|
112
101
|
|
|
113
102
|
@test "work-problems P250: Within-appetite + empty queue does NOT drain (no-op fast-path)" {
|
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# P246: /wr-itil:work-problems Step 6.5 cohort-graduation pre-check must
|
|
4
|
-
# invoke the deterministic graduation evaluator BEFORE the Drain action
|
|
5
|
-
# when the within-appetite-with-releasable-material branch fires AND
|
|
6
|
-
# docs/changesets-holding/ is non-empty.
|
|
7
|
-
#
|
|
8
|
-
# Refined framing (per user direction 2026-05-17): graduation criterion
|
|
9
|
-
# is positive evidence that the surface works as desired (ADR-061 Rule 4
|
|
10
|
-
# per-class evidence floor), NOT elapsed wall-clock time. Calendar
|
|
11
|
-
# predicates (`≥7 days in-repo dogfood`, `on or after <date>`) are NEVER
|
|
12
|
-
# a primary graduation trigger.
|
|
13
|
-
#
|
|
14
|
-
# Pre-check parses `GRADUATION_CANDIDATE` lines from the evaluator and
|
|
15
|
-
# branches per the 3-status taxonomy the evaluator actually emits:
|
|
16
|
-
#
|
|
17
|
-
# status=resolved → git mv from holding to .changeset/,
|
|
18
|
-
# README "Recently reinstated" append,
|
|
19
|
-
# amend iter commit per ADR-042 Rule 3
|
|
20
|
-
# (policy-authorised silent proceed per
|
|
21
|
-
# ADR-013 Rule 5 + ADR-061 Rule 5).
|
|
22
|
-
# status=vp-blocked → skip (ADR-061 Rule 2 VP carve-out).
|
|
23
|
-
# status=halt-no-resolution → halt at the framework-prescribed
|
|
24
|
-
# "Step 6.5 cohort-graduation halt-
|
|
25
|
-
# no-resolution" halt point (ADR-061
|
|
26
|
-
# Rule 1a terminal).
|
|
27
|
-
#
|
|
28
|
-
# Class=3b cohorts graduate atomically per ADR-061 Rule 3b — entire
|
|
29
|
-
# cohort ships or none does.
|
|
30
|
-
#
|
|
31
|
-
# Doc-lint contract assertions per ADR-037 Permitted Exception (contract-
|
|
32
|
-
# assertion class). The asserted prose IS the load-bearing policy surface
|
|
33
|
-
# — re-reading SKILL.md is the only way an AFK reader (and the iteration
|
|
34
|
-
# subprocess) learns the new pre-check behaviour. These tests function as
|
|
35
|
-
# regression guards against re-introducing calendar-trigger framings,
|
|
36
|
-
# silently removing the pre-check, or drifting the 3-status branching.
|
|
37
|
-
#
|
|
38
|
-
# @problem P246
|
|
39
|
-
# @adr ADR-061 (parent principle — Rules 1/1a/2/3/4/5/6/7)
|
|
40
|
-
# @adr ADR-042 (Rule 3 amend-based folding for graduation reinstate commit)
|
|
41
|
-
# @adr ADR-018 (release-cadence policy parent — drain condition unchanged)
|
|
42
|
-
# @adr ADR-013 (Rule 5 policy-authorised silent proceed)
|
|
43
|
-
# @adr ADR-037 (skill-testing strategy — contract-assertion class)
|
|
44
|
-
# @adr ADR-044 (framework-resolution boundary — no AskUserQuestion mid-iter)
|
|
45
|
-
# @jtbd JTBD-006 (Progress the Backlog While I'm Away — primary)
|
|
46
|
-
# @jtbd JTBD-302 (Trust That the README Describes the Plugin I Just Installed)
|
|
47
|
-
# @jtbd JTBD-001 (Enforce Governance Without Slowing Down)
|
|
48
|
-
# @jtbd JTBD-101 (Extend the Suite with New Plugins)
|
|
49
|
-
|
|
50
|
-
setup() {
|
|
51
|
-
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
52
|
-
SKILL_MD="$REPO_ROOT/packages/itil/skills/work-problems/SKILL.md"
|
|
53
|
-
HOLDING_README="$REPO_ROOT/docs/changesets-holding/README.md"
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
# ── Preconditions ──────────────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
@test "work-problems P246: SKILL.md exists" {
|
|
59
|
-
[ -f "$SKILL_MD" ]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@test "work-problems P246: changesets-holding README exists" {
|
|
63
|
-
[ -f "$HOLDING_README" ]
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
# ── Pre-check sub-step exists and cites the evaluator shim ────────────────
|
|
67
|
-
|
|
68
|
-
@test "work-problems P246: SKILL.md Step 6.5 contains 'Cohort-graduation pre-check' sub-step" {
|
|
69
|
-
run grep -nE 'Cohort-graduation pre-check.*ADR-061 Rule 5.*P246' "$SKILL_MD"
|
|
70
|
-
[ "$status" -eq 0 ]
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@test "work-problems P246: pre-check invokes wr-risk-scorer-evaluate-graduation shim" {
|
|
74
|
-
run grep -nE 'wr-risk-scorer-evaluate-graduation' "$SKILL_MD"
|
|
75
|
-
[ "$status" -eq 0 ]
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
@test "work-problems P246: pre-check fires BEFORE the Drain action (on within-appetite branch)" {
|
|
79
|
-
# The pre-check must precede the Drain action heading; if SKILL.md
|
|
80
|
-
# ever inverts the order, the just-graduated entries would not ride
|
|
81
|
-
# the existing release flow.
|
|
82
|
-
pre_check_line=$(grep -nE '^\*\*Cohort-graduation pre-check' "$SKILL_MD" | head -1 | cut -d: -f1)
|
|
83
|
-
drain_line=$(grep -nE '^\*\*Drain action ' "$SKILL_MD" | head -1 | cut -d: -f1)
|
|
84
|
-
[ -n "$pre_check_line" ]
|
|
85
|
-
[ -n "$drain_line" ]
|
|
86
|
-
[ "$pre_check_line" -lt "$drain_line" ]
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
# ── Three-status branching contract ───────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
@test "work-problems P246/P308: status=resolved routes to Rule 4 evidence-floor judgement (NOT auto-graduate)" {
|
|
92
|
-
# P308 amendment: status=resolved is necessary-but-not-sufficient.
|
|
93
|
-
# The evaluator script disclaims Rule 4 (LLM-owned). SKILL prose must
|
|
94
|
-
# surface Rule 4 evidence-floor judgement before any git mv.
|
|
95
|
-
run grep -nE '`status=resolved`.*route to.*Rule 4 evidence-floor judgement' "$SKILL_MD"
|
|
96
|
-
[ "$status" -eq 0 ]
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
@test "work-problems P308: Rule 4-ratified graduate path performs git mv from holding to .changeset/" {
|
|
100
|
-
# The git mv is downstream of the Rule 4 evidence-floor ratification,
|
|
101
|
-
# not directly on the evaluator's status=resolved.
|
|
102
|
-
run grep -nE 'git mv docs/changesets-holding/<basename> \.changeset/<basename>' "$SKILL_MD"
|
|
103
|
-
[ "$status" -eq 0 ]
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
@test "work-problems P308: Rule 4-ratified graduate path amends the iter commit per ADR-042 Rule 3" {
|
|
107
|
-
run grep -nE 'Amend the iter.s main commit per ADR-042 Rule 3 amend-based folding' "$SKILL_MD"
|
|
108
|
-
[ "$status" -eq 0 ]
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
@test "work-problems P246: pre-check branches on status=vp-blocked → skip (ADR-061 Rule 2)" {
|
|
112
|
-
run grep -nE '`status=vp-blocked`.*skip.*ADR-061 Rule 2' "$SKILL_MD"
|
|
113
|
-
[ "$status" -eq 0 ]
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
@test "work-problems P246: pre-check branches on status=halt-no-resolution → halt at framework-prescribed point" {
|
|
117
|
-
run grep -nE '`status=halt-no-resolution`.*halt.*ADR-061 Rule 1a terminal' "$SKILL_MD"
|
|
118
|
-
[ "$status" -eq 0 ]
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@test "work-problems P246: SKILL.md does NOT branch on non-existent status=no-graduate-evidence-floor" {
|
|
122
|
-
# Architect verdict: the evaluator emits only resolved | vp-blocked |
|
|
123
|
-
# halt-no-resolution. A SKILL branch handling a non-existent token
|
|
124
|
-
# would be dead code (Confirmation Violation).
|
|
125
|
-
run grep -nE 'no-graduate-evidence-floor' "$SKILL_MD"
|
|
126
|
-
[ "$status" -ne 0 ]
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
# ── Cohort propagation (class=3b atomic) ──────────────────────────────────
|
|
130
|
-
|
|
131
|
-
@test "work-problems P246: class=3b cohorts graduate atomically (entire cohort ships or none does)" {
|
|
132
|
-
run grep -nE 'class=3b cohorts.*ALL members.*graduate together atomically|cohort ships or none does' "$SKILL_MD"
|
|
133
|
-
[ "$status" -eq 0 ]
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
@test "work-problems P246: cohort propagation cites ADR-061 Rule 3b" {
|
|
137
|
-
run grep -nE 'Rule 3b cohort propagation' "$SKILL_MD"
|
|
138
|
-
[ "$status" -eq 0 ]
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
# ── Framework-prescribed halt point added ─────────────────────────────────
|
|
142
|
-
|
|
143
|
-
@test "work-problems P246: new halt point 'Step 6.5 cohort-graduation halt-no-resolution' in Mid-loop ask discipline list" {
|
|
144
|
-
run grep -nE 'Step 6\.5 cohort-graduation halt-no-resolution halt.*P246' "$SKILL_MD"
|
|
145
|
-
[ "$status" -eq 0 ]
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
@test "work-problems P246: halt-no-resolution halt routes through Step 2.5b cross-reference" {
|
|
149
|
-
# Architect verdict: halt-no-resolution must inherit the established
|
|
150
|
-
# Step 2.5b routing pattern (CI failure / Rule 5 above-appetite /
|
|
151
|
-
# dirty-unknown all route this way).
|
|
152
|
-
run grep -nE 'cohort-graduation.*Step 2\.5b cross-reference|halt-with-batched-questions per the Step 2\.5b cross-reference' "$SKILL_MD"
|
|
153
|
-
[ "$status" -eq 0 ]
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
@test "work-problems P246: Mid-loop ask between iters row enumerates the new halt point" {
|
|
157
|
-
run grep -nE 'Mid-loop ask between iters.*Step 6\.5 cohort-graduation halt-no-resolution halt' "$SKILL_MD"
|
|
158
|
-
[ "$status" -eq 0 ]
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
# ── Non-Interactive Decision Making table reflects amendment ──────────────
|
|
162
|
-
|
|
163
|
-
@test "work-problems P246: Decision Making table carries the resolved-graduate row" {
|
|
164
|
-
run grep -nE '\| Cohort-graduation pre-check.*status=resolved' "$SKILL_MD"
|
|
165
|
-
[ "$status" -eq 0 ]
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
@test "work-problems P246: Decision Making table carries the vp-blocked-skip row" {
|
|
169
|
-
run grep -nE '\| Cohort-graduation pre-check.*status=vp-blocked' "$SKILL_MD"
|
|
170
|
-
[ "$status" -eq 0 ]
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
@test "work-problems P246: Decision Making table carries the halt-no-resolution-halt row" {
|
|
174
|
-
run grep -nE '\| Cohort-graduation pre-check.*status=halt-no-resolution' "$SKILL_MD"
|
|
175
|
-
[ "$status" -eq 0 ]
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
# ── Evidence-based criterion (calendar-trigger rejection) ─────────────────
|
|
179
|
-
|
|
180
|
-
@test "work-problems P246: pre-check states graduation criterion is evidence-of-working-as-desired, NOT elapsed wall-clock time" {
|
|
181
|
-
run grep -nE 'evidence-of-working-as-desired.*not elapsed wall-clock time|not elapsed wall-clock time' "$SKILL_MD"
|
|
182
|
-
[ "$status" -eq 0 ]
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
@test "work-problems P246: pre-check explicitly rejects calendar predicates as primary triggers" {
|
|
186
|
-
run grep -nE 'Calendar predicates are NEVER a primary graduation trigger' "$SKILL_MD"
|
|
187
|
-
[ "$status" -eq 0 ]
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
@test "work-problems P246: pre-check cites user direction verbatim ('Dogfooding makes sense, but it shouldn't be time based')" {
|
|
191
|
-
run grep -nE "Dogfooding makes sense, but it shouldn.t be time based, it should be until we are happy that it.s working as desired" "$SKILL_MD"
|
|
192
|
-
[ "$status" -eq 0 ]
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
@test "work-problems P246: pre-check cites user correction verbatim ('Why are we waiting?')" {
|
|
196
|
-
run grep -nE "Why are we waiting\?.*That seems to go against the principles if you ask me" "$SKILL_MD"
|
|
197
|
-
[ "$status" -eq 0 ]
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
# ── Policy authorisation (ADR-013 Rules 1/5/6 + ADR-061 Rules 4/5 + P352) ─
|
|
201
|
-
|
|
202
|
-
@test "work-problems P308: pre-check cites ADR-061 Rule 4 as the LLM-owned evidence-floor surface" {
|
|
203
|
-
# Rule 4 evidence-floor judgement is class-specific and LLM-owned per
|
|
204
|
-
# ADR-044 framework-resolution boundary; surfacing it as AskUserQuestion
|
|
205
|
-
# (interactive) or queue (AFK) is the load-bearing delegation surface.
|
|
206
|
-
run grep -nE 'ADR-061 Rule 4' "$SKILL_MD"
|
|
207
|
-
[ "$status" -eq 0 ]
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
@test "work-problems P308: pre-check cites ADR-013 Rule 6 as the AFK queue-and-continue fail-safe" {
|
|
211
|
-
# Per P352 user-ratified universal default: when AskUserQuestion is
|
|
212
|
-
# unavailable (AFK iter), queue the question + continue — do NOT halt,
|
|
213
|
-
# do NOT silently fail-soft, do NOT auto-default.
|
|
214
|
-
run grep -nE 'ADR-013 Rule 6.*P352|P352.*ADR-013 Rule 6' "$SKILL_MD"
|
|
215
|
-
[ "$status" -eq 0 ]
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
@test "work-problems P308: pre-check cites ADR-061 Rule 5 (graduation criterion authorises intent, gates authorise action)" {
|
|
219
|
-
run grep -nE 'ADR-061 Rule 5' "$SKILL_MD"
|
|
220
|
-
[ "$status" -eq 0 ]
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
@test "work-problems P308: interactive branch fires AskUserQuestion per-held-entry (not silent proceed)" {
|
|
224
|
-
# The amendment lifts the prior silent-proceed clause — Rule 4 judgement
|
|
225
|
-
# is LLM-owned; the user must ratify before the git mv.
|
|
226
|
-
run grep -nE 'interactive.*AskUserQuestion.*per[- ]held[- ]entry|per[- ]held[- ]entry.*AskUserQuestion' "$SKILL_MD"
|
|
227
|
-
[ "$status" -eq 0 ]
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
@test "work-problems P308: AFK branch queues outstanding_question per held entry (queue-and-continue)" {
|
|
231
|
-
run grep -nE 'AFK.*queue.*outstanding_question.*per held entry|queue.*outstanding_question.*per held entry.*AFK|AFK.*queue.*outstanding_question|outstanding_question.*do NOT graduate' "$SKILL_MD"
|
|
232
|
-
[ "$status" -eq 0 ]
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
@test "work-problems P308: AskUserQuestion option labels inline evidence + Rule 4 criterion (P350 brief-before-ID)" {
|
|
236
|
-
# P350 empathy-gap correction: AskUserQuestion text must inline the
|
|
237
|
-
# per-held-entry evidence summary + Rule 4 class-specific criterion;
|
|
238
|
-
# opaque P-ID / ADR-ID references alone are non-compliant.
|
|
239
|
-
run grep -nE 'P350|brief.*before.*ID|inline.*evidence' "$SKILL_MD"
|
|
240
|
-
[ "$status" -eq 0 ]
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
# ── Governance gates apply (ADR-061 Rule 7) ───────────────────────────────
|
|
244
|
-
|
|
245
|
-
@test "work-problems P246: pre-check governance gates apply per ADR-061 Rule 7" {
|
|
246
|
-
run grep -nE 'Governance gates apply.*ADR-061 Rule 7|graduation reinstate goes through the standard ADR-014 commit flow' "$SKILL_MD"
|
|
247
|
-
[ "$status" -eq 0 ]
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
# ── Audit trail (ADR-061 Rule 6) ──────────────────────────────────────────
|
|
251
|
-
|
|
252
|
-
@test "work-problems P246: pre-check audit trail cites ADR-061 Rule 6" {
|
|
253
|
-
run grep -nE 'Audit trail.*ADR-061 Rule 6' "$SKILL_MD"
|
|
254
|
-
[ "$status" -eq 0 ]
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
@test "work-problems P246: audit trail appends README 'Recently reinstated' entry" {
|
|
258
|
-
run grep -nE 'Recently reinstated.*resolved problem-ticket ID.*Priority value|graduation criterion met.*status=resolved' "$SKILL_MD"
|
|
259
|
-
[ "$status" -eq 0 ]
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
# ── Idempotency ───────────────────────────────────────────────────────────
|
|
263
|
-
|
|
264
|
-
@test "work-problems P246: pre-check is idempotent when holding-area is empty" {
|
|
265
|
-
run grep -nE 'Idempotency.*holding-area is empty|safe to invoke when holding-area is empty' "$SKILL_MD"
|
|
266
|
-
[ "$status" -eq 0 ]
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
@test "work-problems P246: pre-check is idempotent when no candidates resolve (all vp-blocked)" {
|
|
270
|
-
run grep -nE 'Safe when no candidates resolve.*all .vp-blocked.' "$SKILL_MD"
|
|
271
|
-
[ "$status" -eq 0 ]
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
# ── Holding README Process amendment ──────────────────────────────────────
|
|
275
|
-
|
|
276
|
-
@test "work-problems P246: holding README Process step 5 cites P246 refined framing" {
|
|
277
|
-
run grep -nE 'refined by P246|P246 refined framing' "$HOLDING_README"
|
|
278
|
-
[ "$status" -eq 0 ]
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
@test "work-problems P246: holding README Process step 5 states criterion is positive evidence (NOT calendar)" {
|
|
282
|
-
run grep -nE 'positive evidence that the surface works as desired' "$HOLDING_README"
|
|
283
|
-
[ "$status" -eq 0 ]
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
@test "work-problems P246: holding README Process step 5 rejects calendar predicates as primary trigger" {
|
|
287
|
-
run grep -nE 'Calendar predicates.*never.*primary graduation trigger|NEVER a primary graduation trigger' "$HOLDING_README"
|
|
288
|
-
[ "$status" -eq 0 ]
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
@test "work-problems P246: holding README Process step 5 cites user direction verbatim" {
|
|
292
|
-
run grep -nE "Dogfooding makes sense, but it shouldn.t be time based" "$HOLDING_README"
|
|
293
|
-
[ "$status" -eq 0 ]
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
@test "work-problems P246: holding README Process step 5 enumerates the 3-status taxonomy" {
|
|
297
|
-
run grep -nE 'status=<resolved\|vp-blocked\|halt-no-resolution>' "$HOLDING_README"
|
|
298
|
-
[ "$status" -eq 0 ]
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
@test "work-problems P246: holding README Process step 5 explicitly preserves at-hold-time historical contracts" {
|
|
302
|
-
# Architect verdict: per-entry Currently held lines authored BEFORE
|
|
303
|
-
# the P246 framing are retained as at-hold-time historical contracts
|
|
304
|
-
# (not retroactively rewritten).
|
|
305
|
-
run grep -nE 'retained as at-hold-time historical contracts.*not retroactively rewritten' "$HOLDING_README"
|
|
306
|
-
[ "$status" -eq 0 ]
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
# ── P246 self-identification (ticket-trace) ───────────────────────────────
|
|
310
|
-
|
|
311
|
-
@test "work-problems P246: SKILL.md self-identifies the cohort-graduation pre-check as P246's amendment" {
|
|
312
|
-
run grep -nE 'P246' "$SKILL_MD"
|
|
313
|
-
[ "$status" -eq 0 ]
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
@test "work-problems P246: holding README self-identifies the Process amendment as P246's refined framing" {
|
|
317
|
-
run grep -nE 'P246' "$HOLDING_README"
|
|
318
|
-
[ "$status" -eq 0 ]
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
# ── P308 self-identification (ticket-trace) ───────────────────────────────
|
|
322
|
-
|
|
323
|
-
@test "work-problems P308: SKILL.md self-identifies the Rule 4 evidence-floor amendment as P308" {
|
|
324
|
-
run grep -nE 'P308' "$SKILL_MD"
|
|
325
|
-
[ "$status" -eq 0 ]
|
|
326
|
-
}
|