@windyroad/risk-scorer 0.16.0 → 0.16.1-preview.900
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
|
@@ -41,7 +41,10 @@
|
|
|
41
41
|
# - Resolves the ticket file via dual-tolerant glob (ADR-031 + RFC-002):
|
|
42
42
|
# docs/problems/<NNN>-*.md (flat) AND docs/problems/*/<NNN>-*.md (per-state)
|
|
43
43
|
# - Extracts the Priority value from the ticket's `**Priority**: N (...)` line.
|
|
44
|
-
# - Detects Rule 2 VP carve-out (ticket file ends in .verifying.md)
|
|
44
|
+
# - Detects Rule 2 VP carve-out (ticket file ends in .verifying.md) — narrowed
|
|
45
|
+
# per P398: a verifying ticket graduates (resolved) when its `## Fix Released`
|
|
46
|
+
# section is populated (code already shipped, only the changelog is held);
|
|
47
|
+
# it stays vp-blocked only while the fix is not yet shipped.
|
|
45
48
|
# - Parses docs/changesets-holding/README.md "Currently held" section and
|
|
46
49
|
# groups entries by normalised reinstate-trigger prose (Phase 2b).
|
|
47
50
|
# - Multi-member groups emit class=3b + cohort=<id> with cohort-level
|
|
@@ -121,6 +124,12 @@ held_files = sys.argv[3:]
|
|
|
121
124
|
FILENAME_TICKET_RE = re.compile(r'-p(\d+)-', re.IGNORECASE)
|
|
122
125
|
BODY_TICKET_RE = re.compile(r'\bP(\d+)\b')
|
|
123
126
|
PRIORITY_LINE_RE = re.compile(r'^\*\*Priority\*\*:\s*(\d+)\b')
|
|
127
|
+
FIX_RELEASED_HEADING_RE = re.compile(r'^##\s+Fix Released\s*$', re.IGNORECASE)
|
|
128
|
+
# Placeholder-only tokens that do NOT count as a populated `## Fix Released`
|
|
129
|
+
# section (a fix that is scaffolded-but-not-shipped). Kept mechanical per
|
|
130
|
+
# ADR-015 — this is deterministic detection, not prose judgement.
|
|
131
|
+
FIX_RELEASED_PLACEHOLDERS = frozenset({'tbd', 'n/a', 'na', 'pending', '(pending)',
|
|
132
|
+
'(deferred)', 'deferred', '(none)', 'none'})
|
|
124
133
|
|
|
125
134
|
# Phase 2b — README "Currently held" bullet parser.
|
|
126
135
|
# Matches `- \`<filename>\` ... **Reinstate trigger**: <trigger-text>`.
|
|
@@ -168,6 +177,39 @@ def extract_priority(ticket_path: str):
|
|
|
168
177
|
return None
|
|
169
178
|
|
|
170
179
|
|
|
180
|
+
def fix_shipped(ticket_path: str) -> bool:
|
|
181
|
+
"""True when the ticket carries a populated `## Fix Released` section.
|
|
182
|
+
|
|
183
|
+
A verifying ticket whose fix has shipped carries real prose under
|
|
184
|
+
`## Fix Released` (per ADR-022 / manage-problem closing contract); an
|
|
185
|
+
absent, empty, or placeholder-only section means the fix has not actually
|
|
186
|
+
shipped yet. Mechanical predicate per ADR-015 (ADR-061 Rule 2 amended,
|
|
187
|
+
P398): section present AND ≥1 non-blank line that is not a placeholder
|
|
188
|
+
token counts as shipped.
|
|
189
|
+
"""
|
|
190
|
+
try:
|
|
191
|
+
with open(ticket_path, 'r', encoding='utf-8') as f:
|
|
192
|
+
lines = f.readlines()
|
|
193
|
+
except (OSError, IOError):
|
|
194
|
+
return False
|
|
195
|
+
in_section = False
|
|
196
|
+
for line in lines:
|
|
197
|
+
if FIX_RELEASED_HEADING_RE.match(line.strip()):
|
|
198
|
+
in_section = True
|
|
199
|
+
continue
|
|
200
|
+
if not in_section:
|
|
201
|
+
continue
|
|
202
|
+
stripped = line.strip()
|
|
203
|
+
if stripped.startswith('## '):
|
|
204
|
+
break # next section reached with no real content
|
|
205
|
+
if not stripped:
|
|
206
|
+
continue
|
|
207
|
+
if stripped.lower() in FIX_RELEASED_PLACEHOLDERS:
|
|
208
|
+
continue
|
|
209
|
+
return True # real content under the heading
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
|
|
171
213
|
def resolve_ticket_ids(changeset_path: str):
|
|
172
214
|
"""Apply Rule 1a join: filename convention primary, body-grep fallback.
|
|
173
215
|
|
|
@@ -325,7 +367,7 @@ for changeset_path in held_files:
|
|
|
325
367
|
priority = extract_priority(path)
|
|
326
368
|
if priority is None:
|
|
327
369
|
continue
|
|
328
|
-
resolutions.append((tid, priority, suffix))
|
|
370
|
+
resolutions.append((tid, priority, suffix, path))
|
|
329
371
|
|
|
330
372
|
if not resolutions:
|
|
331
373
|
per_changeset[basename] = {
|
|
@@ -336,14 +378,20 @@ for changeset_path in held_files:
|
|
|
336
378
|
continue
|
|
337
379
|
|
|
338
380
|
resolutions.sort(key=lambda r: r[1], reverse=True)
|
|
339
|
-
chosen_tid, chosen_priority, chosen_suffix = resolutions[0]
|
|
340
|
-
if chosen_suffix == 'verifying':
|
|
381
|
+
chosen_tid, chosen_priority, chosen_suffix, chosen_path = resolutions[0]
|
|
382
|
+
if chosen_suffix == 'verifying' and not fix_shipped(chosen_path):
|
|
383
|
+
# ADR-061 Rule 2 (narrowed per P398): the VP carve-out holds a changeset
|
|
384
|
+
# only while its fix is NOT yet shipped. A verifying ticket with no
|
|
385
|
+
# populated `## Fix Released` section is still mid-flight — hold it.
|
|
341
386
|
per_changeset[basename] = {
|
|
342
387
|
'ticket_label': f'P{chosen_tid}',
|
|
343
388
|
'priority': chosen_priority,
|
|
344
389
|
'status': 'vp-blocked',
|
|
345
390
|
}
|
|
346
391
|
continue
|
|
392
|
+
# A verifying ticket whose `## Fix Released` is populated means the code is
|
|
393
|
+
# already live on npm; only the changelog-attribution changeset is held, so
|
|
394
|
+
# it graduates as `resolved` rather than stranding indefinitely (P398).
|
|
347
395
|
|
|
348
396
|
per_changeset[basename] = {
|
|
349
397
|
'ticket_label': f'P{chosen_tid}',
|
|
@@ -202,6 +202,66 @@ EOF
|
|
|
202
202
|
echo "$output" | grep -q 'GRADUATION_SUMMARY: total=1 resolved=0 vp_blocked=1 halts=0'
|
|
203
203
|
}
|
|
204
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
|
+
|
|
205
265
|
# Case (f) — status-agnostic Priority lookup (open, known-error, closed all resolve)
|
|
206
266
|
@test "case (f.1): open ticket resolves" {
|
|
207
267
|
seed_problem "400" "open" "8"
|