@windyroad/itil 0.47.1 → 0.47.3

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.
@@ -497,5 +497,5 @@
497
497
  }
498
498
  },
499
499
  "name": "wr-itil",
500
- "version": "0.47.1"
500
+ "version": "0.47.3"
501
501
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.47.1",
3
+ "version": "0.47.3",
4
4
  "description": "ITIL-aligned IT service management for Claude Code (problem, and future incident/change skills)",
5
5
  "bin": {
6
6
  "windyroad-itil": "./bin/install.mjs"
@@ -251,8 +251,16 @@ if [ -d "$PROBLEMS_DIR" ]; then
251
251
  # problem_rfc_ids["P168"] = "RFC-001 RFC-002 ..."
252
252
  declare -A problem_rfc_rows
253
253
  declare -A problem_rfc_ids
254
+ # P312 / ADR-031: scan both flat docs/problems/<NNN>-*.md AND per-state
255
+ # subdir docs/problems/<state>/<NNN>-*.md layouts so the reverse-trace
256
+ # remains valid post-migration. Mirrors reconcile-readme.sh lines 74-110.
254
257
  shopt -s nullglob
255
- for pf in "$PROBLEMS_DIR"/[0-9][0-9][0-9]-*.md; do
258
+ problem_files=( "$PROBLEMS_DIR"/[0-9][0-9][0-9]-*.md )
259
+ for ticket_status in open known-error verifying closed parked; do
260
+ problem_files+=( "$PROBLEMS_DIR"/"$ticket_status"/[0-9][0-9][0-9]-*.md )
261
+ done
262
+ shopt -u nullglob
263
+ for pf in "${problem_files[@]}"; do
256
264
  pbase="$(basename "$pf")"
257
265
  pnum="${pbase%%-*}"
258
266
  pid="P${pnum}"
@@ -274,7 +282,6 @@ if [ -d "$PROBLEMS_DIR" ]; then
274
282
  done < <(awk -v start="$sec_start" 'NR>start { if (/^## /) exit; print }' "$pf")
275
283
  problem_rfc_ids["$pid"]="$rfcs_in_p"
276
284
  done
277
- shopt -u nullglob
278
285
 
279
286
  # 1. MISSING_REVERSE_TRACE: RFC claims P, P does not list RFC.
280
287
  for rfc_id in "${!rfc_problems_claim[@]}"; do
@@ -131,6 +131,40 @@ EOF
131
131
  fi
132
132
  }
133
133
 
134
+ # Helper: write a problem ticket under the per-state subdir layout per
135
+ # ADR-031 (state is the parent directory; filename has NO `.state.md` suffix).
136
+ # Args: <pid-num> <slug> <state> <rfcs-rows-block>
137
+ # Used to regression-test P312 — reconcile-rfcs reverse-trace must traverse
138
+ # docs/problems/<state>/<NNN>-*.md, not just flat docs/problems/<NNN>-*.<state>.md.
139
+ write_problem_subdir() {
140
+ local num="$1" slug="$2" state="$3" rfcs_rows="${4:-}"
141
+ mkdir -p "$PROBLEMS_DIR/$state"
142
+ local file="$PROBLEMS_DIR/$state/${num}-${slug}.md"
143
+ cat > "$file" <<EOF
144
+ # Problem ${num}: ${slug}
145
+
146
+ **Status**: ${state}
147
+
148
+ ## Description
149
+
150
+ stub
151
+
152
+ ## Related
153
+
154
+ stub
155
+ EOF
156
+ if [ -n "$rfcs_rows" ]; then
157
+ cat >> "$file" <<EOF
158
+
159
+ ## RFCs
160
+
161
+ | RFC | Status | Title |
162
+ |-----|--------|-------|
163
+ ${rfcs_rows}
164
+ EOF
165
+ fi
166
+ }
167
+
134
168
  # ── Existence + executable ──────────────────────────────────────────────────
135
169
 
136
170
  @test "reconcile-rfcs: script exists" {
@@ -413,6 +447,35 @@ EOF
413
447
  done <<< "$output"
414
448
  }
415
449
 
450
+ # ── P312: per-state subdir reverse-trace (ADR-031 layout) ───────────────────
451
+ # Closes P312 — reconcile-rfcs reported spurious MISSING_REVERSE_TRACE for
452
+ # tickets that live under docs/problems/<state>/<NNN>-*.md because the
453
+ # reverse-trace pass only globbed the flat docs/problems/<NNN>-*.md layout.
454
+ # RFC-002-class dual-tolerant-glob fix mirroring the sibling already shipped
455
+ # in reconcile-readme.sh (P118).
456
+
457
+ @test "P312: reverse-trace clean when problem ticket lives in per-state subdir" {
458
+ write_rfc "001" "foo" "accepted"
459
+ write_minimal_readme "| 2.0 | RFC-001 | foo | 3 Med | Accepted | M | 2026-05-05 |"
460
+ # Ticket lives under docs/problems/verifying/168-p168.md (no .state suffix).
461
+ write_problem_subdir "168" "p168" "verifying" "| RFC-001 | accepted | foo |"
462
+ run bash "$SCRIPT" "$FIXTURE_DIR" "$PROBLEMS_DIR"
463
+ [ "$status" -eq 0 ]
464
+ [[ "$output" != *"MISSING_REVERSE_TRACE"* ]]
465
+ }
466
+
467
+ @test "P312: reverse-trace detects missing trace when problem ticket lives in per-state subdir" {
468
+ write_rfc "001" "foo" "accepted"
469
+ write_minimal_readme "| 2.0 | RFC-001 | foo | 3 Med | Accepted | M | 2026-05-05 |"
470
+ # Subdir ticket WITHOUT a `## RFCs` section → MISSING_REVERSE_TRACE must fire.
471
+ write_problem_subdir "168" "p168" "verifying" ""
472
+ run bash "$SCRIPT" "$FIXTURE_DIR" "$PROBLEMS_DIR"
473
+ [ "$status" -eq 1 ]
474
+ [[ "$output" == *"MISSING_REVERSE_TRACE"* ]]
475
+ [[ "$output" == *"RFC-001"* ]]
476
+ [[ "$output" == *"P168"* ]]
477
+ }
478
+
416
479
  # ── ADR-049 bin shim contract ───────────────────────────────────────────────
417
480
 
418
481
  @test "wr-itil-reconcile-rfcs bin shim exists" {
@@ -170,6 +170,7 @@ If interactive (AskUserQuestion available) AND the description is short enough t
170
170
  status: proposed
171
171
  rfc-id: <kebab-slug>
172
172
  reported: <YYYY-MM-DD>
173
+ human-oversight: unconfirmed
173
174
  decision-makers: [<git config user.name>]
174
175
  problems: [P<NNN>, P<NNN>, ...]
175
176
  adrs: []
@@ -275,6 +276,8 @@ After the commit, report:
275
276
 
276
277
  The trailing pointer is **not optional** — it is the user-visible signal that the RFC is intentionally skeleton-only and how to advance it.
277
278
 
279
+ **Oversight marker discipline (ADR-066 + ADR-068 amendments 2026-06-02 / P348).** The skeleton frontmatter MUST include `human-oversight: unconfirmed`. capture-rfc is the AFK-friendly aside surface; there is no substance-confirm `AskUserQuestion` pass in this flow (deferred to `/wr-itil:manage-rfc accepted`), so `confirmed` would be a hollow marker (the P348 bug class). The architect-side hook (`architect-oversight-marker-discipline.sh`) does NOT gate `docs/rfcs/`, but downstream paths that promote a captured RFC to `confirmed` MUST do so via a proper substance-confirm AskUserQuestion (e.g. `/wr-itil:manage-rfc accepted` for genuine ratification) — agents authoring RFCs at capture-time MUST NOT write `confirmed`. The drain pattern mirrors the ADR drain: an RFC's `unconfirmed` state surfaces interactively during the `accepted` transition.
280
+
278
281
  ## Composition with manage-rfc
279
282
 
280
283
  | Concern | manage-rfc | capture-rfc |
@@ -216,7 +216,11 @@ For stop-conditions #1 and #3 (no actionable problems / all blocked), Step 2.5 s
216
216
 
217
217
  Before the orchestrator emits the final `ALL_DONE` sentinel for the AFK loop, it MUST run the following gate sequence. The sequence fires **unconditionally** — at every stop-condition (`#1`, `#2`, `#3` per Step 2) AND at every halt-path that emits a final AFK summary AND on quota-exhaustion / natural loop end. The sequence has three parts that MUST complete in order; the structural rule is `ALL_DONE` emits ONLY after (a) AND (b) complete cleanly. Per-state subdir layout reminder: this step's order in the SKILL is logical (Step 2.4 fires *between* the Step 2 stop-check and the Step 2.5 surfacing routine *only as a wrapper*); the numerical ordering reflects the conceptual sequence (Step 2.4 wraps Step 2.5 + the new retro gate, then Step 2.5/2.5b execute as gate (a)'s worker).
218
218
 
219
- **Gate (a) — Outstanding-questions surface.** Read `.afk-run-state/outstanding-questions.jsonl`. If non-empty, invoke Step 2.5b's surfacing routine to present the accumulated queue (via `AskUserQuestion`-when-available-else-table per ADR-013 Rule 1 / Rule 6). On completion, truncate the queue file. If the queue is empty, gate (a) returns immediately. The surfacing routine is the existing Step 2.5b — Step 2.4 does NOT re-implement; it sequences.
219
+ **Gate (a) — Outstanding-questions surface + oversight-unconfirmed drain (P348 amendment 2026-06-02).** Two sub-surfaces, both fire in this gate:
220
+
221
+ 1. *Outstanding-questions surface.* Read `.afk-run-state/outstanding-questions.jsonl`. If non-empty, invoke Step 2.5b's surfacing routine to present the accumulated queue (via `AskUserQuestion`-when-available-else-table per ADR-013 Rule 1 / Rule 6). On completion, truncate the queue file. If the queue is empty, this sub-surface returns immediately. The surfacing routine is the existing Step 2.5b — Step 2.4 does NOT re-implement; it sequences.
222
+
223
+ 2. *Oversight-unconfirmed drain.* Run `wr-architect-detect-unoversighted` and `wr-jtbd-detect-unoversighted` (both ADR-049 PATH shims, both always exit 0; output is the list of unoversighted artefact paths). If either lists ≥ 1 artefact whose frontmatter carries `human-oversight: unconfirmed` (the AFK-explicit-deferred state, distinct from the implicit-absent state pre-existing ADR/JTBD files carry), surface a one-line nudge: *"N iter-deferred decision(s)/job(s) carry `human-oversight: unconfirmed`. Run `/wr-architect:review-decisions` and `/wr-jtbd:confirm-jobs-and-personas` to drain."* If `AskUserQuestion` is available (`/wr-itil:work-problems` was invoked interactively before the AFK loop started), surface a 2-option choice — `Drain now` (invokes the appropriate drain skill before `ALL_DONE`) / `Defer to next session` (proceeds to gate (b) with the nudge in the final summary). If `AskUserQuestion` is unavailable, the nudge prints in the final summary table and gate (b) proceeds. The drain is NOT a halt — `unconfirmed` markers are explicit-by-design AFK signals (the iter wrote them KNOWING the user would need to confirm), and the drain is the documented path. Detector difference matters: ADRs/JTBDs that pre-date the ADR-066/ADR-068 marker contract carry NO `human-oversight:` line at all; they fall through to the existing review-decisions/confirm-jobs-and-personas backlog drain (no new surfacing here). The new surfacing fires ONLY on the explicit `unconfirmed` value — the AFK-iter-deferred class P348 introduces.
220
224
 
221
225
  **Gate (b) — Session-level retro.** Invoke `/wr-retrospective:run-retro` via the Skill tool. This is the **orchestrator-main-turn session-level retro**, distinct from the per-iter retro fired inside each iter subprocess (per P086 / Step 5 retro-on-exit clause). The session-level retro covers cross-iter patterns, friction observations, framework-improvement candidates, and the AFK loop's overall trajectory — surface visible only after multiple iters have completed. Retro commits its own work per ADR-014; any tickets retro creates ride retro's own commit, and the orchestrator picks them up on the *next* invocation of `/wr-itil:work-problems` rather than re-entering the loop here.
222
226