@windyroad/itil 0.32.2-preview.332 → 0.32.3-preview.334
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/package.json
CHANGED
|
@@ -537,7 +537,7 @@ After the iteration's commit lands and before the release-cadence check, drain a
|
|
|
537
537
|
|
|
538
538
|
### Step 6.5: Release-cadence check (per ADR-018, above-appetite branch per ADR-042)
|
|
539
539
|
|
|
540
|
-
After the iteration's commit lands but before starting the next iteration, check whether
|
|
540
|
+
After the iteration's commit lands but before starting the next iteration, check whether there is releasable material to drain. This prevents silent accumulation of unreleased changesets across AFK iterations (P041, P250) — accumulation costs audit fidelity and increases future drain risk with no governance benefit when residual stays within appetite. **The orchestrator MUST NOT release above appetite under any circumstance** — above-appetite states route to the ADR-042 auto-apply loop or halt.
|
|
541
541
|
|
|
542
542
|
**Mechanism — delegate, do not re-implement scoring:**
|
|
543
543
|
|
|
@@ -545,10 +545,10 @@ After the iteration's commit lands but before starting the next iteration, check
|
|
|
545
545
|
- **Primary**: delegate to subagent type `wr-risk-scorer:pipeline` via the Agent tool.
|
|
546
546
|
- **Fallback**: if that subagent type is not available, invoke skill `/wr-risk-scorer:assess-release` via the Skill tool. The skill wraps the same pipeline subagent.
|
|
547
547
|
2. Read the returned `RISK_SCORES: commit=X push=Y release=Z` line and the `RISK_REMEDIATIONS:` block (if present).
|
|
548
|
-
3. **Classify the residual**:
|
|
549
|
-
- **Within appetite (≤ 3/25)** — no drain needed. Proceed to Step 6.75.
|
|
550
|
-
- **At appetite (= 4/25)** — drain the queue per the Drain action below, then proceed to Step 6.75.
|
|
548
|
+
3. **Classify the residual + queue state (P250)**:
|
|
551
549
|
- **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.
|
|
550
|
+
- **Within appetite (≤ 4/25) AND there is releasable material** (any unpushed commits on `HEAD..origin/<base>` OR any entries in `.changeset/` OR any graduation-eligible entries in `docs/changesets-holding/` per ADR-061 Rule 1 that are not VP-blocked per Rule 2) — 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.
|
|
551
|
+
- **Within appetite (≤ 4/25) AND empty queue** (no unpushed commits AND no `.changeset/` entries AND no graduation-eligible held 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.
|
|
552
552
|
|
|
553
553
|
**Drain action (non-interactive, policy-authorised per ADR-013 Rule 6):**
|
|
554
554
|
|
|
@@ -676,7 +676,8 @@ When `AskUserQuestion` is unavailable or the user is AFK, the skill (and the del
|
|
|
676
676
|
| Scope expansion during work | Update problem file, re-score WSJF, move to next problem instead of continuing |
|
|
677
677
|
| Commit when risk within appetite | Auto-commit (manage-problem step 9e fallback) |
|
|
678
678
|
| Commit when risk above appetite | Skip commit, report uncommitted state |
|
|
679
|
-
| Pipeline risk
|
|
679
|
+
| Pipeline risk within appetite (≤ 4/25) with releasable material (any unpushed commits OR any `.changeset/` entries OR any graduation-eligible held entries per ADR-061 Rule 1) | 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." |
|
|
680
|
+
| Pipeline risk within appetite (≤ 4/25) AND empty queue (no unpushed commits AND no `.changeset/` AND no graduation-eligible held entries) | No drain — literally nothing to release. Proceed directly to Step 6.75. The genuine no-op fast-path per P250. |
|
|
680
681
|
| 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). |
|
|
681
682
|
| 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). |
|
|
682
683
|
| Pipeline risk above appetite (push or release >= 5/25) | Auto-apply scorer remediations incrementally (ADR-042 Rule 2). The agent reads suggestions and decides what to do. Re-score after each apply; drain when within appetite. **Never release above appetite** (ADR-042 Rule 1) — no AskUserQuestion shortcut. Halt the loop with `outcome: halted-above-appetite` if the loop exhausts without convergence (ADR-042 Rule 5). Verification Pending commits excluded from auto-revert (Rule 2b). Per ADR-042 (Step 6.5 Above-appetite branch). |
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P250: /wr-itil:work-problems Step 6.5 release-cadence classification must
|
|
4
|
+
# pivot on releasable material, not residual band. The defective prior
|
|
5
|
+
# clause "Within appetite (≤ 3/25) — no drain needed" encoded
|
|
6
|
+
# accumulation-permitted-below-threshold semantics that violated the
|
|
7
|
+
# symmetric-balance principle (ADR-061 Rule 1) and the user's verbatim
|
|
8
|
+
# direction: "If it's low risk, you should release."
|
|
9
|
+
#
|
|
10
|
+
# Amended contract (three-band):
|
|
11
|
+
# 1. Above appetite (≥ 5/25) → ADR-042 auto-apply (unchanged).
|
|
12
|
+
# 2. Within appetite (≤ 4/25) AND releasable material → drain.
|
|
13
|
+
# 3. Within appetite (≤ 4/25) AND empty queue → no drain (no-op fast-path).
|
|
14
|
+
#
|
|
15
|
+
# The trigger for the drain action is *presence of releasable material*
|
|
16
|
+
# (any unpushed commits OR any .changeset/ entries OR any graduation-
|
|
17
|
+
# eligible held entries per ADR-061 Rule 1). The residual band remains
|
|
18
|
+
# the safety check (above-appetite never releases) but is no longer the
|
|
19
|
+
# action gate for the within-appetite branch.
|
|
20
|
+
#
|
|
21
|
+
# Doc-lint contract assertions per ADR-037 Permitted Exception (contract-
|
|
22
|
+
# assertion class). The asserted prose IS the load-bearing policy surface
|
|
23
|
+
# — re-reading SKILL.md is the only way an AFK reader (and the iteration
|
|
24
|
+
# subprocess) learns the new classification. Behavioural verification
|
|
25
|
+
# would require executing the orchestrator's decision tree, which is not
|
|
26
|
+
# scriptable until the Phase 2 advisory-classifier from P081 ships. These
|
|
27
|
+
# tests function as regression guards against re-introducing the
|
|
28
|
+
# "Within appetite (≤ 3/25) — no drain needed" wording or any equivalent
|
|
29
|
+
# threshold-as-action-gate framing.
|
|
30
|
+
#
|
|
31
|
+
# @problem P250
|
|
32
|
+
# @adr ADR-018 (release-cadence policy parent — amended in same commit)
|
|
33
|
+
# @adr ADR-037 (skill-testing strategy — contract-assertion class)
|
|
34
|
+
# @adr ADR-042 (above-appetite branch — preserved unchanged)
|
|
35
|
+
# @adr ADR-061 (Rule 1 symmetric-balance principle — parent principle)
|
|
36
|
+
# @jtbd JTBD-006 (Progress the Backlog While I'm Away — primary)
|
|
37
|
+
# @jtbd JTBD-002 (Ship with Confidence — composes; small frequent releases)
|
|
38
|
+
|
|
39
|
+
setup() {
|
|
40
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
41
|
+
SKILL_MD="$REPO_ROOT/packages/itil/skills/work-problems/SKILL.md"
|
|
42
|
+
ADR_018="$REPO_ROOT/docs/decisions/018-inter-iteration-release-cadence-for-afk-loops.proposed.md"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# ── Preconditions ──────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
@test "work-problems P250: SKILL.md exists" {
|
|
48
|
+
[ -f "$SKILL_MD" ]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@test "work-problems P250: ADR-018 exists" {
|
|
52
|
+
[ -f "$ADR_018" ]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# ── Regression guard: defective prior wording is gone ──────────────────────
|
|
56
|
+
|
|
57
|
+
@test "work-problems P250: SKILL.md no longer contains 'Within appetite (≤ 3/25) — no drain needed' clause" {
|
|
58
|
+
# The defective clause. Re-introducing it regresses to accumulation-
|
|
59
|
+
# permitted-below-threshold semantics.
|
|
60
|
+
run grep -nE 'Within appetite \(≤ 3/25\).*no drain needed' "$SKILL_MD"
|
|
61
|
+
[ "$status" -ne 0 ]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@test "work-problems P250: SKILL.md no longer treats '= 4/25' as a discrete drain trigger band" {
|
|
65
|
+
# The prior "At appetite (= 4/25) — drain" band is collapsed into the
|
|
66
|
+
# new "≤ 4/25 AND releasable material" branch. The discrete = 4/25
|
|
67
|
+
# framing should be absent from the Step 6.5 classification.
|
|
68
|
+
run grep -nE 'At appetite \(= 4/25\) — drain' "$SKILL_MD"
|
|
69
|
+
[ "$status" -ne 0 ]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# ── Above-appetite branch preserved (ADR-042) ──────────────────────────────
|
|
73
|
+
|
|
74
|
+
@test "work-problems P250: Above-appetite (≥ 5/25) branch routes to ADR-042 auto-apply (preserved)" {
|
|
75
|
+
run grep -nE 'Above appetite \(≥ 5/25\).*Above-appetite branch' "$SKILL_MD"
|
|
76
|
+
[ "$status" -eq 0 ]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@test "work-problems P250: Above-appetite branch references the auto-apply loop (ADR-042 intact)" {
|
|
80
|
+
run grep -nE 'auto-apply loop|ADR-042' "$SKILL_MD"
|
|
81
|
+
[ "$status" -eq 0 ]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# ── Within-appetite + releasable material drains ──────────────────────────
|
|
85
|
+
|
|
86
|
+
@test "work-problems P250: Within-appetite + releasable material triggers drain" {
|
|
87
|
+
# The load-bearing positive contract: within appetite AND any unpushed
|
|
88
|
+
# commits OR changeset OR graduation-eligible held entry → drain.
|
|
89
|
+
run grep -nE 'Within appetite \(≤ 4/25\) AND there is releasable material' "$SKILL_MD"
|
|
90
|
+
[ "$status" -eq 0 ]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@test "work-problems P250: releasable-material clause enumerates unpushed commits" {
|
|
94
|
+
run grep -nE 'releasable material.*unpushed commits|unpushed commits.*releasable material|any unpushed commits' "$SKILL_MD"
|
|
95
|
+
[ "$status" -eq 0 ]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@test "work-problems P250: releasable-material clause enumerates .changeset/ entries" {
|
|
99
|
+
run grep -nE 'any entries in `\.changeset/`|`\.changeset/` non-empty|entries in `\.changeset/`' "$SKILL_MD"
|
|
100
|
+
[ "$status" -eq 0 ]
|
|
101
|
+
}
|
|
102
|
+
|
|
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
|
+
# ── Within-appetite + empty queue no-op fast-path ─────────────────────────
|
|
112
|
+
|
|
113
|
+
@test "work-problems P250: Within-appetite + empty queue does NOT drain (no-op fast-path)" {
|
|
114
|
+
# The genuine fast-path: nothing to release. Gate is *absence of
|
|
115
|
+
# releasable material*, not residual band.
|
|
116
|
+
run grep -nE 'Within appetite \(≤ 4/25\) AND empty queue' "$SKILL_MD"
|
|
117
|
+
[ "$status" -eq 0 ]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@test "work-problems P250: empty-queue branch is framed as 'literally nothing to release', not a threshold deferral" {
|
|
121
|
+
# Regression guard: the no-drain branch must NOT be reframed as a
|
|
122
|
+
# threshold-based defer. The gate is queue emptiness, not residual.
|
|
123
|
+
run grep -nE 'literally nothing to release|nothing to release.*genuine no-op' "$SKILL_MD"
|
|
124
|
+
[ "$status" -eq 0 ]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# ── User-direction citation preserved ─────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
@test "work-problems P250: amended classification cites user direction verbatim ('If it's low risk, you should release.')" {
|
|
130
|
+
# The user's verbatim direction is the load-bearing rationale. Future
|
|
131
|
+
# readers tracing the amendment back must find the citation without
|
|
132
|
+
# keyword-guessing.
|
|
133
|
+
run grep -nE 'If it.s low risk, you should release' "$SKILL_MD"
|
|
134
|
+
[ "$status" -eq 0 ]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@test "work-problems P250: classification cites P250 by ticket ID" {
|
|
138
|
+
# ADR-022 amendment trail: the amended clause must self-identify
|
|
139
|
+
# so future readers tracing back from the ticket find it.
|
|
140
|
+
run grep -nE 'P250' "$SKILL_MD"
|
|
141
|
+
[ "$status" -eq 0 ]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# ── Non-Interactive Decision Making table reflects amendment ──────────────
|
|
145
|
+
|
|
146
|
+
@test "work-problems P250: Decision Making table carries the within-appetite-with-releasable-material row" {
|
|
147
|
+
# The decision table is the AFK reader's quick-reference; the
|
|
148
|
+
# amendment must surface here too, otherwise the table contradicts
|
|
149
|
+
# Step 6.5's classification.
|
|
150
|
+
run grep -nE '\| Pipeline risk within appetite \(≤ 4/25\) with releasable material' "$SKILL_MD"
|
|
151
|
+
[ "$status" -eq 0 ]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@test "work-problems P250: Decision Making table carries the empty-queue no-drain row" {
|
|
155
|
+
run grep -nE '\| Pipeline risk within appetite \(≤ 4/25\) AND empty queue' "$SKILL_MD"
|
|
156
|
+
[ "$status" -eq 0 ]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@test "work-problems P250: Decision Making table row cites 'presence of releasable material', not 'residual reaching appetite'" {
|
|
160
|
+
# The trigger semantics must be explicit in the table row, not
|
|
161
|
+
# buried in Step 6.5 prose 100+ lines above.
|
|
162
|
+
run grep -nE 'presence of releasable material|releasable material.*not residual band|Trigger is.*presence' "$SKILL_MD"
|
|
163
|
+
[ "$status" -eq 0 ]
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# ── ADR-018 amendment (in same commit per ADR-014) ────────────────────────
|
|
167
|
+
|
|
168
|
+
@test "work-problems P250: ADR-018 contains the 2026-05-18 Drain-trigger amendment heading" {
|
|
169
|
+
# ADR-014 single-unit-of-work: the ADR amendment lands in the same
|
|
170
|
+
# commit as the SKILL change. ADR-018's Mechanism without this
|
|
171
|
+
# amendment contradicts SKILL.md Step 6.5.
|
|
172
|
+
run grep -nE 'Amendment 2026-05-18.*Drain trigger is releasable material' "$ADR_018"
|
|
173
|
+
[ "$status" -eq 0 ]
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@test "work-problems P250: ADR-018 amendment cites P250 by ticket ID" {
|
|
177
|
+
run grep -nE 'P250' "$ADR_018"
|
|
178
|
+
[ "$status" -eq 0 ]
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@test "work-problems P250: ADR-018 amendment quotes the user direction" {
|
|
182
|
+
run grep -nE 'If it.s low risk, you should release' "$ADR_018"
|
|
183
|
+
[ "$status" -eq 0 ]
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@test "work-problems P250: ADR-018 amendment encodes the new drain condition shape" {
|
|
187
|
+
# The amendment must contain the conjunction "≤ 4/25 AND (releasable
|
|
188
|
+
# material disjunct)" so future readers tracing the policy chain
|
|
189
|
+
# find the rule in the load-bearing ADR, not just in the SKILL.
|
|
190
|
+
run grep -nE 'residual ≤ 4/25 AND' "$ADR_018"
|
|
191
|
+
[ "$status" -eq 0 ]
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@test "work-problems P250: ADR-018 amendment preserves Above-appetite delegation to ADR-042" {
|
|
195
|
+
# The amendment MUST NOT touch the above-appetite invariant. ADR-042
|
|
196
|
+
# remains the safety gate.
|
|
197
|
+
run grep -nE 'Above-appetite states \(≥ 5/25\) route to ADR-042|Above-appetite.*ADR-042' "$ADR_018"
|
|
198
|
+
[ "$status" -eq 0 ]
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
# ── Cross-references intact ───────────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
@test "work-problems P250: SKILL.md Step 6.5 still cites ADR-018 (parent policy intact)" {
|
|
204
|
+
run grep -nE 'Step 6\.5.*ADR-018|ADR-018.*Step 6\.5|Release-cadence check.*ADR-018' "$SKILL_MD"
|
|
205
|
+
[ "$status" -eq 0 ]
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@test "work-problems P250: SKILL.md Step 6.5 still cites P041 (accumulation root cause cross-reference)" {
|
|
209
|
+
# P041 is the ancestor that established Step 6.5 itself. The
|
|
210
|
+
# amendment refines P041's solution; the cross-reference must
|
|
211
|
+
# survive.
|
|
212
|
+
run grep -nE 'P041' "$SKILL_MD"
|
|
213
|
+
[ "$status" -eq 0 ]
|
|
214
|
+
}
|