@windyroad/risk-scorer 0.17.4 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +13 -2
- package/bin/install.mjs +42 -8
- package/hooks/codex-agent-completion.mjs +82 -0
- package/hooks/hooks.json +5 -2
- package/hooks/risk-scorer-dispatch.sh +3 -0
- package/hooks/risk-scorer-scaffold-nudge.sh +15 -4
- package/package.json +5 -2
- package/scripts/codex-agents.mjs +208 -0
- package/scripts/sync-codex-skills.mjs +8 -3
- package/skills/assess-external-comms/SKILL.md +5 -4
- package/skills/assess-inbound-report/SKILL.md +5 -4
- package/skills/assess-release/SKILL.md +5 -4
- package/skills/assess-wip/SKILL.md +5 -4
- package/skills/bootstrap-catalog/SKILL.md +5 -4
- package/skills/create-risk/SKILL.md +5 -4
- package/skills/external-comms/SKILL.md +5 -4
- package/skills/pipeline/SKILL.md +5 -4
- package/skills/update-policy/SKILL.md +6 -5
- package/skills/wip/SKILL.md +5 -4
- package/agents/test/inbound-report-contract.bats +0 -225
- package/agents/test/risk-scorer-above-appetite-stop.bats +0 -80
- package/agents/test/risk-scorer-catalog-consumption.bats +0 -138
- package/agents/test/risk-scorer-commit-verdict.bats +0 -66
- package/agents/test/risk-scorer-monitoring-not-a-control.bats +0 -76
- package/agents/test/risk-scorer-reducing-bypass-criteria.bats +0 -62
- package/agents/test/risk-scorer-register-hint.bats +0 -111
- package/agents/test/risk-scorer-structured-remediations.bats +0 -123
- package/agents/test/risk-scorer-user-stated-preconditions.bats +0 -89
- package/hooks/test/ci-status-gate.bats +0 -234
- package/hooks/test/external-comms-gate.bats +0 -528
- package/hooks/test/external-comms-key-substance.bats +0 -74
- package/hooks/test/git-push-gate.bats +0 -82
- package/hooks/test/pipeline-state-changeset-partition.bats +0 -181
- package/hooks/test/pipeline-state-hash.bats +0 -195
- package/hooks/test/plan-risk-guidance-once-per-session.bats +0 -95
- package/hooks/test/reducing-marker-persistence.bats +0 -236
- package/hooks/test/risk-gate.bats +0 -330
- package/hooks/test/risk-policy-project-root.bats +0 -20
- package/hooks/test/risk-score-commit-gate-adr-031-bypass.bats +0 -98
- package/hooks/test/risk-score-commit-gate-cadence-staleness.bats +0 -105
- package/hooks/test/risk-score-mark-external-comms-prompt-parse.bats +0 -94
- package/hooks/test/risk-score-mark-register-queue.bats +0 -253
- package/hooks/test/risk-score-mark.bats +0 -112
- package/hooks/test/risk-scorer-hook-dispatch.bats +0 -77
- package/hooks/test/risk-scorer-no-stop-hook.bats +0 -19
- package/hooks/test/risk-scorer-scaffold-nudge.bats +0 -132
- package/hooks/test/slide-marker-on-subprocess-return.bats +0 -118
- package/hooks/test/wip-risk-mark.bats +0 -64
- package/scripts/test/drain-register-queue.bats +0 -359
- package/scripts/test/evaluate-graduation.bats +0 -533
- package/scripts/test/extract-risks-from-reports.bats +0 -366
- package/scripts/test/restage-commit.bats +0 -137
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
# Behavioural-fixture coverage for packages/risk-scorer/scripts/evaluate-graduation.sh
|
|
3
|
-
# per ADR-052 (behavioural tests default) and ADR-061 (dogfood graduation criteria).
|
|
4
|
-
#
|
|
5
|
-
# Phase 2a coverage — orthogonal-gate class (Class 3a). Maps to
|
|
6
|
-
# ADR-061 Confirmation criterion 2 items a-f.
|
|
7
|
-
#
|
|
8
|
-
# Phase 2b coverage — atomic-cohort class (Class 3b — Rule 3b cohort enumeration).
|
|
9
|
-
# Maps to ADR-061 Confirmation criterion 2 item g (full-cohort evaluation,
|
|
10
|
-
# max(Priority) across cohort tickets, atomic VP-blocked + halt propagation).
|
|
11
|
-
# Cohort detection reads docs/changesets-holding/README.md "Currently held"
|
|
12
|
-
# section and groups entries by shared reinstate-trigger prose (parenthetical
|
|
13
|
-
# elaborations stripped before grouping). Single-member "cohorts" fall back
|
|
14
|
-
# to class=3a (no Phase 2a regression).
|
|
15
|
-
|
|
16
|
-
setup() {
|
|
17
|
-
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
18
|
-
SCRIPT="$REPO_ROOT/packages/risk-scorer/scripts/evaluate-graduation.sh"
|
|
19
|
-
SHIM="$REPO_ROOT/packages/risk-scorer/bin/wr-risk-scorer-evaluate-graduation"
|
|
20
|
-
WORK_DIR="$(mktemp -d)"
|
|
21
|
-
cd "$WORK_DIR"
|
|
22
|
-
# Minimal git setup — fixture follows drain-register-queue.bats precedent
|
|
23
|
-
# so dual-layout problem-ticket lookup (flat + per-state subdir) exercises
|
|
24
|
-
# the same shape the script handles in canonical adopter trees.
|
|
25
|
-
git init --quiet
|
|
26
|
-
git config user.email "graduation-test@example.com"
|
|
27
|
-
git config user.name "Graduation Test"
|
|
28
|
-
git commit --quiet --allow-empty -m "init"
|
|
29
|
-
mkdir -p docs/changesets-holding docs/problems/open docs/problems/known-error \
|
|
30
|
-
docs/problems/verifying docs/problems/closed docs/problems/parked
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
teardown() {
|
|
34
|
-
cd /
|
|
35
|
-
rm -rf "$WORK_DIR"
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# ----- Helpers -----
|
|
39
|
-
|
|
40
|
-
seed_problem() {
|
|
41
|
-
# seed_problem <id-padded> <state> <priority> [<extra-body>]
|
|
42
|
-
local id="$1" state="$2" priority="$3" extra="${4:-}"
|
|
43
|
-
local path
|
|
44
|
-
# Use per-state subdir layout (RFC-002 canonical post-migration shape)
|
|
45
|
-
path="docs/problems/${state}/${id}-fixture-ticket.md"
|
|
46
|
-
cat > "$path" <<EOF
|
|
47
|
-
# Problem ${id}: Fixture ticket
|
|
48
|
-
|
|
49
|
-
**Status**: ${state}
|
|
50
|
-
**Reported**: 2026-05-01
|
|
51
|
-
**Priority**: ${priority} (label) — Impact: 3 x Likelihood: $(( priority / 3 ))
|
|
52
|
-
|
|
53
|
-
## Description
|
|
54
|
-
|
|
55
|
-
Fixture body for graduation-evaluator tests.
|
|
56
|
-
|
|
57
|
-
${extra}
|
|
58
|
-
EOF
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
seed_problem_flat() {
|
|
62
|
-
# seed_problem_flat <id-padded> <state> <priority>
|
|
63
|
-
# Exercises the flat-layout half of the RFC-002 dual-tolerant glob.
|
|
64
|
-
local id="$1" state="$2" priority="$3"
|
|
65
|
-
cat > "docs/problems/${id}-fixture-flat.${state}.md" <<EOF
|
|
66
|
-
# Problem ${id}: Fixture flat ticket
|
|
67
|
-
|
|
68
|
-
**Status**: ${state}
|
|
69
|
-
**Reported**: 2026-05-01
|
|
70
|
-
**Priority**: ${priority} (label) — Impact: 3 x Likelihood: $(( priority / 3 ))
|
|
71
|
-
|
|
72
|
-
## Description
|
|
73
|
-
|
|
74
|
-
Flat-layout fixture body for graduation-evaluator tests.
|
|
75
|
-
EOF
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
seed_changeset() {
|
|
79
|
-
# seed_changeset <filename> [<body>]
|
|
80
|
-
local filename="$1" body="${2:-Default fixture changeset body.}"
|
|
81
|
-
cat > "docs/changesets-holding/${filename}" <<EOF
|
|
82
|
-
---
|
|
83
|
-
'@windyroad/itil': minor
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
${body}
|
|
87
|
-
EOF
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
# ----- Smoke tests -----
|
|
91
|
-
|
|
92
|
-
@test "shim wrapper exists and is executable" {
|
|
93
|
-
[ -x "$SHIM" ]
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
@test "shim resolves canonical script (not exit 127)" {
|
|
97
|
-
# Empty holding-area → exit 1 (no-op caller signal); but not exit 127.
|
|
98
|
-
run "$SHIM" "$WORK_DIR"
|
|
99
|
-
[ "$status" -ne 127 ]
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
@test "missing docs/ → exit 2 (invalid project root)" {
|
|
103
|
-
cd "$BATS_TEST_TMPDIR"
|
|
104
|
-
empty_dir=$(mktemp -d)
|
|
105
|
-
run bash "$SCRIPT" "$empty_dir"
|
|
106
|
-
[ "$status" -eq 2 ]
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
@test "missing holding dir → exit 1 (no-op signal)" {
|
|
110
|
-
rm -rf docs/changesets-holding
|
|
111
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
112
|
-
[ "$status" -eq 1 ]
|
|
113
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=0'
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
@test "empty holding dir → exit 1 (no-op signal)" {
|
|
117
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
118
|
-
[ "$status" -eq 1 ]
|
|
119
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=0'
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
@test "README.md in holding-area is ignored" {
|
|
123
|
-
echo "# Holding Area" > docs/changesets-holding/README.md
|
|
124
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
125
|
-
# No other entries — exit 1 (empty after exclusion)
|
|
126
|
-
[ "$status" -eq 1 ]
|
|
127
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=0'
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
# ----- ADR-061 Confirmation criterion 2 cases -----
|
|
131
|
-
|
|
132
|
-
# Case (a) — filename-convention join resolves correctly
|
|
133
|
-
@test "case (a): filename-convention join — wr-itil-p085-...md → P085 → Priority" {
|
|
134
|
-
seed_problem "085" "open" "9"
|
|
135
|
-
seed_changeset "wr-itil-p085-assistant-output-gate.md"
|
|
136
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
137
|
-
[ "$status" -eq 0 ]
|
|
138
|
-
echo "$output" | grep -q 'changeset=wr-itil-p085-assistant-output-gate.md'
|
|
139
|
-
echo "$output" | grep -q 'ticket=P085'
|
|
140
|
-
echo "$output" | grep -q 'priority=9'
|
|
141
|
-
echo "$output" | grep -q 'status=resolved'
|
|
142
|
-
echo "$output" | grep -q 'class=3a'
|
|
143
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=1 resolved=1 vp_blocked=0 halts=0'
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
# Case (b) — body-grep fallback resolves when filename lacks convention
|
|
147
|
-
@test "case (b): body-grep fallback — non-conventional filename resolves via P<NNN> in body" {
|
|
148
|
-
seed_problem "100" "known-error" "12"
|
|
149
|
-
seed_changeset "feature-rollout-cohort.md" "Fixes P100. Related to feature work."
|
|
150
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
151
|
-
[ "$status" -eq 0 ]
|
|
152
|
-
echo "$output" | grep -q 'changeset=feature-rollout-cohort.md'
|
|
153
|
-
echo "$output" | grep -q 'ticket=P100'
|
|
154
|
-
echo "$output" | grep -q 'priority=12'
|
|
155
|
-
echo "$output" | grep -q 'status=resolved'
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
# Case (c) — multi-ticket changeset uses max(Priority)
|
|
159
|
-
@test "case (c): multi-ticket changeset uses max(Priority) across referenced set" {
|
|
160
|
-
seed_problem "200" "open" "6"
|
|
161
|
-
seed_problem "201" "open" "15"
|
|
162
|
-
seed_problem "202" "open" "9"
|
|
163
|
-
seed_changeset "multi-ticket-cohort.md" "References P200, P201, and P202 in body for max-priority test."
|
|
164
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
165
|
-
[ "$status" -eq 0 ]
|
|
166
|
-
# Max priority across P200(6) P201(15) P202(9) is 15 → ticket=P201
|
|
167
|
-
echo "$output" | grep -q 'changeset=multi-ticket-cohort.md'
|
|
168
|
-
echo "$output" | grep -q 'ticket=P201'
|
|
169
|
-
echo "$output" | grep -q 'priority=15'
|
|
170
|
-
echo "$output" | grep -q 'status=resolved'
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
# Case (d) — halt-and-prompt when no resolution path succeeds
|
|
174
|
-
@test "case (d.1): halt-no-resolution when filename lacks convention AND body has no P<NNN>" {
|
|
175
|
-
seed_changeset "no-ticket-reference.md" "Body has no problem-ticket references at all."
|
|
176
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
177
|
-
[ "$status" -eq 0 ]
|
|
178
|
-
echo "$output" | grep -q 'changeset=no-ticket-reference.md'
|
|
179
|
-
echo "$output" | grep -q 'status=halt-no-resolution'
|
|
180
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=1 resolved=0 vp_blocked=0 halts=1'
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
@test "case (d.2): halt-no-resolution when filename references missing ticket" {
|
|
184
|
-
# Filename references P999 but ticket file does not exist in fixture
|
|
185
|
-
seed_changeset "wr-itil-p999-orphan.md"
|
|
186
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
187
|
-
[ "$status" -eq 0 ]
|
|
188
|
-
echo "$output" | grep -q 'status=halt-no-resolution'
|
|
189
|
-
echo "$output" | grep -q 'halts=1'
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
# Case (e) — VP-blocked ticket emitted with vp-blocked marker
|
|
193
|
-
@test "case (e): VP-blocked ticket emits status=vp-blocked (Rule 2 carve-out)" {
|
|
194
|
-
seed_problem "300" "verifying" "10"
|
|
195
|
-
seed_changeset "wr-itil-p300-mid-verification.md"
|
|
196
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
197
|
-
[ "$status" -eq 0 ]
|
|
198
|
-
echo "$output" | grep -q 'changeset=wr-itil-p300-mid-verification.md'
|
|
199
|
-
echo "$output" | grep -q 'ticket=P300'
|
|
200
|
-
echo "$output" | grep -q 'priority=10'
|
|
201
|
-
echo "$output" | grep -q 'status=vp-blocked'
|
|
202
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=1 resolved=0 vp_blocked=1 halts=0'
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
# ----- P398: Rule 2 narrowed — shipped verifying graduates, unshipped holds -----
|
|
206
|
-
|
|
207
|
-
# Case (h.1) — verifying ticket WITH populated `## Fix Released` graduates (code shipped)
|
|
208
|
-
@test "case (h.1): shipped verifying (populated Fix Released) → resolved (P398)" {
|
|
209
|
-
seed_problem "310" "verifying" "9" "## Fix Released
|
|
210
|
-
|
|
211
|
-
Deployed in v0.42.0. Awaiting user verification."
|
|
212
|
-
seed_changeset "wr-itil-p310-shipped.md"
|
|
213
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
214
|
-
[ "$status" -eq 0 ]
|
|
215
|
-
echo "$output" | grep -q 'changeset=wr-itil-p310-shipped.md'
|
|
216
|
-
echo "$output" | grep -q 'ticket=P310'
|
|
217
|
-
echo "$output" | grep -q 'status=resolved'
|
|
218
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=1 resolved=1 vp_blocked=0 halts=0'
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
# Case (h.2) — verifying ticket with placeholder-only `## Fix Released` stays held
|
|
222
|
-
@test "case (h.2): unshipped verifying (placeholder Fix Released) → vp-blocked (P398)" {
|
|
223
|
-
seed_problem "311" "verifying" "9" "## Fix Released
|
|
224
|
-
|
|
225
|
-
(pending)"
|
|
226
|
-
seed_changeset "wr-itil-p311-pending.md"
|
|
227
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
228
|
-
[ "$status" -eq 0 ]
|
|
229
|
-
echo "$output" | grep -q 'ticket=P311'
|
|
230
|
-
echo "$output" | grep -q 'status=vp-blocked'
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
# Case (h.3) — verifying ticket with NO `## Fix Released` section stays held (case (e) invariant)
|
|
234
|
-
@test "case (h.3): verifying with no Fix Released section → vp-blocked (P398 unshipped branch)" {
|
|
235
|
-
seed_problem "312" "verifying" "9"
|
|
236
|
-
seed_changeset "wr-itil-p312-no-section.md"
|
|
237
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
238
|
-
[ "$status" -eq 0 ]
|
|
239
|
-
echo "$output" | grep -q 'ticket=P312'
|
|
240
|
-
echo "$output" | grep -q 'status=vp-blocked'
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
# Case (h.4) — an all-shipped-verifying cohort rolls up to resolved and graduates atomically
|
|
244
|
-
@test "case (h.4): cohort of all-shipped-verifying members rolls up to resolved (P398)" {
|
|
245
|
-
seed_problem "320" "verifying" "9" "## Fix Released
|
|
246
|
-
|
|
247
|
-
Shipped in v0.50.0."
|
|
248
|
-
seed_problem "321" "verifying" "12" "## Fix Released
|
|
249
|
-
|
|
250
|
-
Shipped in v0.50.0."
|
|
251
|
-
seed_changeset "wr-itil-p320-slice-a.md"
|
|
252
|
-
seed_changeset "wr-itil-p321-slice-b.md"
|
|
253
|
-
seed_holding_readme \
|
|
254
|
-
"- \`wr-itil-p320-slice-a.md\` — patch. **Reinstate trigger**: shipped cohort graduates." \
|
|
255
|
-
"- \`wr-itil-p321-slice-b.md\` — patch. **Reinstate trigger**: shipped cohort graduates."
|
|
256
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
257
|
-
[ "$status" -eq 0 ]
|
|
258
|
-
# Both members class=3b, both resolved (shipped), cohort priority = max(9,12) = 12
|
|
259
|
-
echo "$output" | grep 'changeset=wr-itil-p320-slice-a.md' | grep -q 'class=3b'
|
|
260
|
-
echo "$output" | grep 'changeset=wr-itil-p320-slice-a.md' | grep -q 'status=resolved'
|
|
261
|
-
echo "$output" | grep 'changeset=wr-itil-p321-slice-b.md' | grep -q 'status=resolved'
|
|
262
|
-
echo "$output" | grep 'changeset=wr-itil-p320-slice-a.md' | grep -q 'priority=12'
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
# Case (f) — status-agnostic Priority lookup (open, known-error, closed all resolve)
|
|
266
|
-
@test "case (f.1): open ticket resolves" {
|
|
267
|
-
seed_problem "400" "open" "8"
|
|
268
|
-
seed_changeset "wr-itil-p400-open.md"
|
|
269
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
270
|
-
[ "$status" -eq 0 ]
|
|
271
|
-
echo "$output" | grep -q 'ticket=P400'
|
|
272
|
-
echo "$output" | grep -q 'status=resolved'
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
@test "case (f.2): known-error ticket resolves" {
|
|
276
|
-
seed_problem "401" "known-error" "8"
|
|
277
|
-
seed_changeset "wr-itil-p401-ke.md"
|
|
278
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
279
|
-
[ "$status" -eq 0 ]
|
|
280
|
-
echo "$output" | grep -q 'ticket=P401'
|
|
281
|
-
echo "$output" | grep -q 'status=resolved'
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
@test "case (f.3): closed ticket resolves (Priority still readable)" {
|
|
285
|
-
seed_problem "402" "closed" "8"
|
|
286
|
-
seed_changeset "wr-itil-p402-closed.md"
|
|
287
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
288
|
-
[ "$status" -eq 0 ]
|
|
289
|
-
echo "$output" | grep -q 'ticket=P402'
|
|
290
|
-
echo "$output" | grep -q 'status=resolved'
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
@test "case (f.4): parked ticket resolves" {
|
|
294
|
-
seed_problem "403" "parked" "8"
|
|
295
|
-
seed_changeset "wr-itil-p403-parked.md"
|
|
296
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
297
|
-
[ "$status" -eq 0 ]
|
|
298
|
-
echo "$output" | grep -q 'ticket=P403'
|
|
299
|
-
echo "$output" | grep -q 'status=resolved'
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
# Dual-tolerant layout coverage (ADR-031 / RFC-002 migration window)
|
|
303
|
-
@test "flat-layout ticket resolves (RFC-002 pre-migration shape)" {
|
|
304
|
-
seed_problem_flat "500" "open" "12"
|
|
305
|
-
seed_changeset "wr-itil-p500-flat-layout.md"
|
|
306
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
307
|
-
[ "$status" -eq 0 ]
|
|
308
|
-
echo "$output" | grep -q 'ticket=P500'
|
|
309
|
-
echo "$output" | grep -q 'priority=12'
|
|
310
|
-
echo "$output" | grep -q 'status=resolved'
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
# Mixed-state holding-area — multiple changesets in one run
|
|
314
|
-
@test "mixed-state holding-area — resolved + vp-blocked + halt in single run" {
|
|
315
|
-
seed_problem "600" "open" "9"
|
|
316
|
-
seed_problem "601" "verifying" "12"
|
|
317
|
-
seed_problem "602" "open" "6"
|
|
318
|
-
seed_changeset "wr-itil-p600-resolved.md"
|
|
319
|
-
seed_changeset "wr-itil-p601-vp.md"
|
|
320
|
-
seed_changeset "no-resolution-ref.md" "Generic body, no P references."
|
|
321
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
322
|
-
[ "$status" -eq 0 ]
|
|
323
|
-
# Counts: 3 total, 1 resolved (P600), 1 vp-blocked (P601), 1 halt
|
|
324
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=3 resolved=1 vp_blocked=1 halts=1'
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
# Filename-convention takes precedence over body-grep when both present
|
|
328
|
-
@test "filename takes precedence over body — wr-itil-p700-fix.md with P800 in body resolves to P700" {
|
|
329
|
-
seed_problem "700" "open" "5"
|
|
330
|
-
seed_problem "800" "open" "20"
|
|
331
|
-
seed_changeset "wr-itil-p700-fix.md" "Also references P800 in body (should be ignored — filename wins)."
|
|
332
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
333
|
-
[ "$status" -eq 0 ]
|
|
334
|
-
echo "$output" | grep -q 'ticket=P700'
|
|
335
|
-
echo "$output" | grep -q 'priority=5'
|
|
336
|
-
# Confirm body-referenced P800 was NOT picked up
|
|
337
|
-
! echo "$output" | grep -q 'ticket=P800'
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
# ----- Phase 2b: ADR-061 Confirmation criterion 2 item (g) — atomic-cohort -----
|
|
341
|
-
|
|
342
|
-
# Helper: seed a Currently held entry into docs/changesets-holding/README.md.
|
|
343
|
-
# Cohort detection reads this file and groups entries by shared reinstate-trigger
|
|
344
|
-
# prose (parenthetical elaborations stripped) — see evaluate-graduation.sh.
|
|
345
|
-
seed_holding_readme() {
|
|
346
|
-
# seed_holding_readme <bullet-line> [<bullet-line>...]
|
|
347
|
-
local readme="docs/changesets-holding/README.md"
|
|
348
|
-
if [ ! -f "$readme" ]; then
|
|
349
|
-
cat > "$readme" <<'EOF'
|
|
350
|
-
# Changesets Holding Area
|
|
351
|
-
|
|
352
|
-
## Currently held
|
|
353
|
-
|
|
354
|
-
EOF
|
|
355
|
-
fi
|
|
356
|
-
for bullet in "$@"; do
|
|
357
|
-
printf '%s\n' "$bullet" >> "$readme"
|
|
358
|
-
done
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
# Case (g.1) — two members sharing identical reinstate-trigger prose form a cohort
|
|
362
|
-
@test "case (g.1): two members sharing reinstate-trigger form Class 3b cohort" {
|
|
363
|
-
seed_problem "170" "open" "9"
|
|
364
|
-
seed_problem "171" "open" "12"
|
|
365
|
-
seed_changeset "wr-itil-p170-phase4.md"
|
|
366
|
-
seed_changeset "wr-itil-p171-phase3.md"
|
|
367
|
-
seed_holding_readme \
|
|
368
|
-
"- \`wr-itil-p170-phase4.md\` — patch. **Reinstate trigger**: Phase 3 + Phase 4 end-of-chain user verification fires." \
|
|
369
|
-
"- \`wr-itil-p171-phase3.md\` — minor. **Reinstate trigger**: Phase 3 + Phase 4 end-of-chain user verification fires."
|
|
370
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
371
|
-
[ "$status" -eq 0 ]
|
|
372
|
-
# Both members emit class=3b
|
|
373
|
-
echo "$output" | grep 'changeset=wr-itil-p170-phase4.md' | grep -q 'class=3b'
|
|
374
|
-
echo "$output" | grep 'changeset=wr-itil-p171-phase3.md' | grep -q 'class=3b'
|
|
375
|
-
# Both members share the same cohort= column
|
|
376
|
-
cohort_p170=$(echo "$output" | grep 'changeset=wr-itil-p170-phase4.md' | sed -n 's/.*cohort=\([^ |]*\).*/\1/p')
|
|
377
|
-
cohort_p171=$(echo "$output" | grep 'changeset=wr-itil-p171-phase3.md' | sed -n 's/.*cohort=\([^ |]*\).*/\1/p')
|
|
378
|
-
[ -n "$cohort_p170" ]
|
|
379
|
-
[ "$cohort_p170" = "$cohort_p171" ]
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
# Case (g.2) — cohort uses max(Priority) across all member tickets per ADR-061 Rule 3b
|
|
383
|
-
@test "case (g.2): cohort priority is max across member tickets" {
|
|
384
|
-
seed_problem "172" "open" "6"
|
|
385
|
-
seed_problem "173" "open" "15"
|
|
386
|
-
seed_problem "174" "open" "9"
|
|
387
|
-
seed_changeset "wr-itil-p172-slice-a.md"
|
|
388
|
-
seed_changeset "wr-itil-p173-slice-b.md"
|
|
389
|
-
seed_changeset "wr-itil-p174-slice-c.md"
|
|
390
|
-
seed_holding_readme \
|
|
391
|
-
"- \`wr-itil-p172-slice-a.md\` — patch. **Reinstate trigger**: RFC-009 end-of-chain verification." \
|
|
392
|
-
"- \`wr-itil-p173-slice-b.md\` — patch. **Reinstate trigger**: RFC-009 end-of-chain verification." \
|
|
393
|
-
"- \`wr-itil-p174-slice-c.md\` — patch. **Reinstate trigger**: RFC-009 end-of-chain verification."
|
|
394
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
395
|
-
[ "$status" -eq 0 ]
|
|
396
|
-
# Every cohort member carries priority=15 (max across P172/P173/P174)
|
|
397
|
-
echo "$output" | grep 'changeset=wr-itil-p172-slice-a.md' | grep -q 'priority=15'
|
|
398
|
-
echo "$output" | grep 'changeset=wr-itil-p173-slice-b.md' | grep -q 'priority=15'
|
|
399
|
-
echo "$output" | grep 'changeset=wr-itil-p174-slice-c.md' | grep -q 'priority=15'
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
# Case (g.3) — one VP-blocked cohort member marks the entire cohort vp-blocked
|
|
403
|
-
@test "case (g.3): VP-blocked member blocks entire cohort (Rule 2 carve-out symmetric)" {
|
|
404
|
-
seed_problem "175" "open" "9"
|
|
405
|
-
seed_problem "176" "verifying" "12"
|
|
406
|
-
seed_changeset "wr-itil-p175-slice-a.md"
|
|
407
|
-
seed_changeset "wr-itil-p176-slice-b.md"
|
|
408
|
-
seed_holding_readme \
|
|
409
|
-
"- \`wr-itil-p175-slice-a.md\` — minor. **Reinstate trigger**: cohort verification fires." \
|
|
410
|
-
"- \`wr-itil-p176-slice-b.md\` — minor. **Reinstate trigger**: cohort verification fires."
|
|
411
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
412
|
-
[ "$status" -eq 0 ]
|
|
413
|
-
# Both members report status=vp-blocked even though only P176 is in verifying state
|
|
414
|
-
echo "$output" | grep 'changeset=wr-itil-p175-slice-a.md' | grep -q 'status=vp-blocked'
|
|
415
|
-
echo "$output" | grep 'changeset=wr-itil-p176-slice-b.md' | grep -q 'status=vp-blocked'
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
# Case (g.4) — one halt-no-resolution member propagates to entire cohort (architect C1)
|
|
419
|
-
@test "case (g.4): halt-no-resolution member propagates to entire cohort" {
|
|
420
|
-
seed_problem "177" "open" "9"
|
|
421
|
-
# P178 deliberately NOT seeded → halt-no-resolution
|
|
422
|
-
seed_changeset "wr-itil-p177-slice-a.md"
|
|
423
|
-
seed_changeset "wr-itil-p178-slice-b.md"
|
|
424
|
-
seed_holding_readme \
|
|
425
|
-
"- \`wr-itil-p177-slice-a.md\` — patch. **Reinstate trigger**: shared cohort fires." \
|
|
426
|
-
"- \`wr-itil-p178-slice-b.md\` — patch. **Reinstate trigger**: shared cohort fires."
|
|
427
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
428
|
-
[ "$status" -eq 0 ]
|
|
429
|
-
# Both members report status=halt-no-resolution — cohort cannot graduate partially
|
|
430
|
-
echo "$output" | grep 'changeset=wr-itil-p177-slice-a.md' | grep -q 'status=halt-no-resolution'
|
|
431
|
-
echo "$output" | grep 'changeset=wr-itil-p178-slice-b.md' | grep -q 'status=halt-no-resolution'
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
# Case (g.5) — single-member "cohort" falls back to Class 3a (no Phase 2a regression)
|
|
435
|
-
@test "case (g.5): single-member 'cohort' falls back to class=3a" {
|
|
436
|
-
seed_problem "179" "open" "9"
|
|
437
|
-
seed_changeset "wr-itil-p179-solo.md"
|
|
438
|
-
seed_holding_readme \
|
|
439
|
-
"- \`wr-itil-p179-solo.md\` — patch. **Reinstate trigger**: nobody else shares this trigger."
|
|
440
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
441
|
-
[ "$status" -eq 0 ]
|
|
442
|
-
echo "$output" | grep 'changeset=wr-itil-p179-solo.md' | grep -q 'class=3a'
|
|
443
|
-
! echo "$output" | grep 'changeset=wr-itil-p179-solo.md' | grep -q 'cohort='
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
# Case (g.6) — parenthetical elaborations stripped before grouping
|
|
447
|
-
@test "case (g.6): parenthetical elaborations stripped before cohort grouping" {
|
|
448
|
-
seed_problem "180" "open" "9"
|
|
449
|
-
seed_problem "181" "open" "9"
|
|
450
|
-
seed_changeset "wr-itil-p180-a.md"
|
|
451
|
-
seed_changeset "wr-itil-p181-b.md"
|
|
452
|
-
# P180 trigger has no parens; P181 trigger has parenthetical elaboration —
|
|
453
|
-
# cohort detection must strip the paren content before comparison.
|
|
454
|
-
seed_holding_readme \
|
|
455
|
-
"- \`wr-itil-p180-a.md\` — patch. **Reinstate trigger**: end-of-chain fires." \
|
|
456
|
-
"- \`wr-itil-p181-b.md\` — patch. **Reinstate trigger**: end-of-chain fires (only the slice 3 dependency remains, can defer per Reassessment criterion k)."
|
|
457
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
458
|
-
[ "$status" -eq 0 ]
|
|
459
|
-
# Despite different surface prose, the normalised trigger matches → both class=3b
|
|
460
|
-
echo "$output" | grep 'changeset=wr-itil-p180-a.md' | grep -q 'class=3b'
|
|
461
|
-
echo "$output" | grep 'changeset=wr-itil-p181-b.md' | grep -q 'class=3b'
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
# Case (g.7) — README without "Currently held" section → all entries fall back to class=3a
|
|
465
|
-
@test "case (g.7): README without 'Currently held' section falls back to class=3a (defensive)" {
|
|
466
|
-
seed_problem "182" "open" "9"
|
|
467
|
-
seed_problem "183" "open" "12"
|
|
468
|
-
seed_changeset "wr-itil-p182-a.md"
|
|
469
|
-
seed_changeset "wr-itil-p183-b.md"
|
|
470
|
-
# README exists but has no "Currently held" section — cohort detection finds nothing.
|
|
471
|
-
cat > "docs/changesets-holding/README.md" <<'EOF'
|
|
472
|
-
# Holding Area
|
|
473
|
-
Some unrelated content.
|
|
474
|
-
EOF
|
|
475
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
476
|
-
[ "$status" -eq 0 ]
|
|
477
|
-
echo "$output" | grep 'changeset=wr-itil-p182-a.md' | grep -q 'class=3a'
|
|
478
|
-
echo "$output" | grep 'changeset=wr-itil-p183-b.md' | grep -q 'class=3a'
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
# Case (g.8) — README absent entirely → all entries fall back to class=3a (defensive)
|
|
482
|
-
@test "case (g.8): missing README falls back to class=3a" {
|
|
483
|
-
seed_problem "184" "open" "9"
|
|
484
|
-
seed_changeset "wr-itil-p184-a.md"
|
|
485
|
-
# Do NOT create README at all
|
|
486
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
487
|
-
[ "$status" -eq 0 ]
|
|
488
|
-
echo "$output" | grep 'changeset=wr-itil-p184-a.md' | grep -q 'class=3a'
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
# Case (g.9) — multiple distinct cohorts in the same holding-area resolve independently
|
|
492
|
-
@test "case (g.9): multiple distinct cohorts coexist with distinct cohort= ids" {
|
|
493
|
-
seed_problem "185" "open" "9"
|
|
494
|
-
seed_problem "186" "open" "10"
|
|
495
|
-
seed_problem "187" "open" "12"
|
|
496
|
-
seed_problem "188" "open" "8"
|
|
497
|
-
seed_changeset "wr-itil-p185-cohort-a.md"
|
|
498
|
-
seed_changeset "wr-itil-p186-cohort-a.md"
|
|
499
|
-
seed_changeset "wr-itil-p187-cohort-b.md"
|
|
500
|
-
seed_changeset "wr-itil-p188-cohort-b.md"
|
|
501
|
-
seed_holding_readme \
|
|
502
|
-
"- \`wr-itil-p185-cohort-a.md\` — minor. **Reinstate trigger**: cohort alpha fires." \
|
|
503
|
-
"- \`wr-itil-p186-cohort-a.md\` — minor. **Reinstate trigger**: cohort alpha fires." \
|
|
504
|
-
"- \`wr-itil-p187-cohort-b.md\` — minor. **Reinstate trigger**: cohort beta fires." \
|
|
505
|
-
"- \`wr-itil-p188-cohort-b.md\` — minor. **Reinstate trigger**: cohort beta fires."
|
|
506
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
507
|
-
[ "$status" -eq 0 ]
|
|
508
|
-
cohort_a1=$(echo "$output" | grep 'changeset=wr-itil-p185-cohort-a.md' | sed -n 's/.*cohort=\([^ |]*\).*/\1/p')
|
|
509
|
-
cohort_a2=$(echo "$output" | grep 'changeset=wr-itil-p186-cohort-a.md' | sed -n 's/.*cohort=\([^ |]*\).*/\1/p')
|
|
510
|
-
cohort_b1=$(echo "$output" | grep 'changeset=wr-itil-p187-cohort-b.md' | sed -n 's/.*cohort=\([^ |]*\).*/\1/p')
|
|
511
|
-
cohort_b2=$(echo "$output" | grep 'changeset=wr-itil-p188-cohort-b.md' | sed -n 's/.*cohort=\([^ |]*\).*/\1/p')
|
|
512
|
-
[ -n "$cohort_a1" ] && [ "$cohort_a1" = "$cohort_a2" ]
|
|
513
|
-
[ -n "$cohort_b1" ] && [ "$cohort_b1" = "$cohort_b2" ]
|
|
514
|
-
[ "$cohort_a1" != "$cohort_b1" ]
|
|
515
|
-
# Cohort A priority is max(9,10) = 10; Cohort B priority is max(12,8) = 12
|
|
516
|
-
echo "$output" | grep 'changeset=wr-itil-p185-cohort-a.md' | grep -q 'priority=10'
|
|
517
|
-
echo "$output" | grep 'changeset=wr-itil-p187-cohort-b.md' | grep -q 'priority=12'
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
# Case (g.10) — cohort detection does NOT regress Phase 2a summary counts
|
|
521
|
-
@test "case (g.10): cohort members still count individually in GRADUATION_SUMMARY" {
|
|
522
|
-
seed_problem "190" "open" "9"
|
|
523
|
-
seed_problem "191" "open" "9"
|
|
524
|
-
seed_changeset "wr-itil-p190-cohort.md"
|
|
525
|
-
seed_changeset "wr-itil-p191-cohort.md"
|
|
526
|
-
seed_holding_readme \
|
|
527
|
-
"- \`wr-itil-p190-cohort.md\` — patch. **Reinstate trigger**: shared cohort." \
|
|
528
|
-
"- \`wr-itil-p191-cohort.md\` — patch. **Reinstate trigger**: shared cohort."
|
|
529
|
-
run bash "$SCRIPT" "$WORK_DIR"
|
|
530
|
-
[ "$status" -eq 0 ]
|
|
531
|
-
# Phase 2a parsers see total=2 resolved=2 — backwards compatible
|
|
532
|
-
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=2 resolved=2 vp_blocked=0 halts=0'
|
|
533
|
-
}
|