@windyroad/itil 0.54.0 → 0.54.1-preview.780

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.54.0"
500
+ "version": "0.54.1"
501
501
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.54.0",
3
+ "version": "0.54.1-preview.780",
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"
@@ -74,7 +74,7 @@ fi
74
74
  | Flag | Effect on Step 1.5b |
75
75
  |------|-------------------|
76
76
  | `--jtbd=JTBD-NNN[,JTBD-NNN...]` | Pre-resolves the JTBD-trace value. Step 1.5b skips JTBD-trace derivation. Comma-separated list of JTBD IDs (no spaces). |
77
- | `--persona=<value>` | Pre-resolves the persona value. Step 1.5b skips persona derivation. Value MUST be one of: `developer`, `tech-lead`, `plugin-developer`, `plugin-user`. |
77
+ | `--persona=<value>` | Pre-resolves the persona value. Step 1.5b skips persona derivation. Value MUST be one of the directory names under `docs/jtbd/*/` (the adopter's real persona corpus — e.g. `maintainer`, `smb-owner` in adopter repos), falling back to the home-repo set `{developer, tech-lead, plugin-developer, plugin-user}` only when no `docs/jtbd/` directories exist. P383 — never hardcode the home-repo enum (P151/P317 adopter-portability). |
78
78
  | `--no-prompt` | **AFK mode marker** (REVIVED 2026-06-02 per ADR-060 Amendment 2026-06-02). Suppresses the I12 derive-then-ratify AskUserQuestion fallback. When set AND derivation fails (no JTBD-NNN citations + no `--persona=` flag + no `--jtbd=` flag, OR cited-JTBD persona-disagreement) → capture halts-with-stderr-directive and exits non-zero. AFK orchestrators (`/wr-itil:work-problems` capture-on-correction sub-flow, agent-mid-iter `capture-problem` invocations) MUST pass this flag PLUS pre-resolved `--persona` + `--jtbd` flags to avoid the halt. |
79
79
 
80
80
  Strip recognised leading flags from `$ARGUMENTS`; the remainder (after flags) is the free-text description. Unknown leading flags halt-with-stderr-directive: print "capture-problem: unknown flag '<flag>' — recognised flags: --jtbd=JTBD-NNN, --persona=<value>, --no-prompt" and exit.
@@ -96,14 +96,14 @@ Per ADR-060 § Phase 3 + Phase 4 in-scope amendment (2026-05-13), as amended by
96
96
  3. **Else (no flag, no lexical detection — derive-failure path)**: enter the **derive-then-ratify dispatch**. Propose up-to-3 candidate JTBD IDs to the user via `AskUserQuestion`. The candidates come from LLM analysis of the description's domain signals (e.g. "AFK loop" + "iter dispatch" → propose JTBD-006; "ADR / governance" → propose JTBD-001; "plugin discoverability" → propose JTBD-101). The 4th option is **Reject** (4-option cap per ADR-044 Rule 1). User response semantics:
97
97
  - **REJECT** → halt-with-stderr-directive (`capture-problem: user rejected proposed JTBD trace; per I12 derive-then-ratify (ADR-060 Amendment 2026-06-02), rejection of proposed persona/JTBD = rejection of the problem; no ticket created`); exit non-zero.
98
98
  - **Option-pick** (acceptance of a proposed JTBD as-is) → assign `jtbd_trace_value` to the picked ID; proceed silently.
99
- - **Free-text correction** (user supplies a different JTBD-NNN ID via the AskUserQuestion free-text path) → validate the supplied ID matches `\bJTBD-[0-9]+\b` AND a `docs/jtbd/<persona>/JTBD-<NNN>-*.md` file exists; assign `jtbd_trace_value` to the corrected ID; proceed silently (correction-as-acceptance).
99
+ - **Free-text correction** (user supplies a different JTBD-NNN ID via the AskUserQuestion free-text path) → validate the supplied ID matches `\bJTBD-([A-Za-z]+-)?[0-9]+\b` (tolerating the maintainer `JTBD-M-NNN` alpha-infix scheme, P383) AND a matching `docs/jtbd/<persona>/JTBD-<id>-*.md` file exists; assign `jtbd_trace_value` to the corrected ID; proceed silently (correction-as-acceptance).
100
100
  4. **AFK halt (the `--no-prompt` branch)**: if `--no-prompt` was set in Step 1 AND control reaches step 3 (no flag + no lexical detection) → SKIP the `AskUserQuestion`; halt-with-stderr-directive (`capture-problem: cannot derive JTBD interactively under AFK and no --jtbd= flag supplied; capture refused — re-invoke with explicit anchoring`); exit non-zero.
101
101
 
102
- **Resolve `persona_value`** (a scalar enum value drawn from `{developer, tech-lead, plugin-developer, plugin-user}`) via the following derive-then-ratify dispatch:
102
+ **Resolve `persona_value`** (a scalar persona value validated against the **adopter's persona corpus**: the directory names under `docs/jtbd/*/`, falling back to the home-repo set `{developer, tech-lead, plugin-developer, plugin-user}` only when no `docs/jtbd/` directories exist; P383, never hardcode the home-repo enum — P151/P317 adopter-portability) via the following derive-then-ratify dispatch:
103
103
 
104
- 1. **If `--persona=<value>` was set in Step 1**: validate `<value>` against the enum; halt-with-directive if invalid; otherwise assign and proceed silently. Caller pre-resolved.
104
+ 1. **If `--persona=<value>` was set in Step 1**: validate `<value>` against the persona corpus (the `docs/jtbd/*/` directory names, or the home-repo fallback set when no jtbd dirs exist — e.g. `ls -d docs/jtbd/*/ | xargs -n1 basename`); halt-with-directive if invalid; otherwise assign and proceed silently. Caller pre-resolved.
105
105
  2. **Else if `jtbd_trace_value` is non-empty AND cited JTBDs agree on a single persona**: derive `persona_value` from the cited JTBDs' `persona:` (and optionally `secondary-persona:`) frontmatter; emit stderr advisory: `capture-problem: derived persona=<value> from cited JTBD <id> frontmatter`; proceed silently.
106
- 3. **Else (no flag + (no cited JTBDs OR cited-JTBD persona-disagreement) — derive-failure / ambiguity path)**: enter the **derive-then-ratify dispatch**. Propose up-to-3 candidate persona values to the user via `AskUserQuestion`. Candidate generation: on cited-JTBD persona-disagreement, the candidates are the union-of-derived-personas from the cited JTBDs. On no-cited-JTBDs, the candidates come from LLM analysis of the description's persona signals (e.g. "ADR / governance" → propose `developer`; "plugin install / autocomplete" → propose `plugin-user`; "plugin maintainer / scaffold" → propose `plugin-developer`). The 4th option is **Reject** (4-option cap per ADR-044 Rule 1). User response semantics same as JTBD-trace step 3: REJECT → halt-with-stderr-directive + exit non-zero (rejection of proposed persona = rejection of the problem); option-pick → assign + proceed silently (acceptance); free-text correction → validate against enum + assign + proceed silently (correction-as-acceptance).
106
+ 3. **Else (no flag + (no cited JTBDs OR cited-JTBD persona-disagreement) — derive-failure / ambiguity path)**: enter the **derive-then-ratify dispatch**. Propose up-to-3 candidate persona values to the user via `AskUserQuestion`. Candidate generation: on cited-JTBD persona-disagreement, the candidates are the union-of-derived-personas from the cited JTBDs. On no-cited-JTBDs, the candidates come from LLM analysis of the description's persona signals (e.g. "ADR / governance" → propose `developer`; "plugin install / autocomplete" → propose `plugin-user`; "plugin maintainer / scaffold" → propose `plugin-developer`). The 4th option is **Reject** (4-option cap per ADR-044 Rule 1). User response semantics same as JTBD-trace step 3: REJECT → halt-with-stderr-directive + exit non-zero (rejection of proposed persona = rejection of the problem); option-pick → assign + proceed silently (acceptance); free-text correction → validate against the persona corpus (the `docs/jtbd/*/` directory names, enum fallback when no jtbd dirs) + assign + proceed silently (correction-as-acceptance).
107
107
  4. **AFK halt (the `--no-prompt` branch)**: if `--no-prompt` was set in Step 1 AND control reaches step 3 → SKIP the `AskUserQuestion`; halt-with-stderr-directive (`capture-problem: cannot derive persona interactively under AFK and no --persona= flag supplied; capture refused — re-invoke with explicit anchoring`); exit non-zero.
108
108
 
109
109
  **JTBD-301 scope preservation**: this dispatch fires on the maintainer-side `/wr-itil:capture-problem` only. Plugin-user-side `.github/ISSUE_TEMPLATE/problem-report.yml` MUST NOT prompt for JTBD trace or persona — preserves the JTBD-301 firewall per ADR-060 P4.3 maintainer-side / plugin-user-side asymmetry clarifier. Triage during `/wr-itil:manage-problem` ingestion assigns both fields from the reporter's symptom signals (per the JTBD-301 maintainer-side-complement extension, amended 2026-06-02 to remove the type-axis residue).
@@ -436,6 +436,41 @@ Exit-code routing:
436
436
 
437
437
  **Compose-with**: ADR-068 (surface 3 single-artifact predicate — mirrored to orchestrator), ADR-074 (substance-confirm-before-build — JTBD-as-driver symmetric sibling to ADR-as-driver), ADR-076 (tier-first selection preserved by the loopback), ADR-031 (degenerate adopter silent-pass when per-JTBD shim absent), ADR-049 / ADR-080 (PATH shim grammar + highest-version-wins wrapper), ADR-014 (no commit at this step — predicate is read-only). The sibling-class gap for ADRs cited as Decision Drivers (ADR-074 master class) is RFC-016 § Deferred item 1 — captured for follow-on after this Step 3.5 dogfoods.
438
438
 
439
+ ### Step 3.6: Pre-dispatch relevance gate (per P385)
440
+
441
+ After Step 3.5's JTBD predicate-check and before Step 4 classifies the selected ticket, run the cheap deterministic relevance evaluator on the **selected ticket only**. On a mature backlog a meaningful fraction of "open" / known-error tickets have already been fixed by later work but never transitioned (observed: 3 of 6 worked tickets in one session). A full Step 5 `manage-problem` dispatch (~$3-5 + 5-10 min) against such a ticket only rediscovers the shipped fix and transitions it — the conclusion is correct but the rediscovery is expensive. This step shifts that conclusion left to a millisecond shell check, exactly as Step 3.5 shifts the JTBD-ratification predicate left and Step 0c pre-flights the backlog-wide relevance-close.
442
+
443
+ **Mechanism:**
444
+
445
+ ```bash
446
+ wr-itil-evaluate-relevance "<selected-ticket-path>"
447
+ relevance_exit=$?
448
+ ```
449
+
450
+ `wr-itil-evaluate-relevance` is the ADR-049 `$PATH` shim dispatching `packages/itil/scripts/evaluate-relevance.sh` — the **same evaluator** `/wr-itil:review-problems` Step 4.6 uses for its relevance-close pass (ADR-079). It emits one verdict line and carries a built-in ≥7-day age gate (a freshly-reported ticket SKIPs and falls through to normal work — no self-close paradox). Behavioural coverage of the verdict shapes is the existing `packages/itil/scripts/test/evaluate-relevance.bats`; Step 3.6 adds no new computational surface, only routing, so it carries no separate behavioural script of its own (a SKILL.md-prose grep would be a structural test, rejected per P081 / ADR-052).
451
+
452
+ Exit-code routing:
453
+
454
+ | `relevance_exit` / verdict | Meaning | Action |
455
+ |---|---|---|
456
+ | `0` `CLOSE-CANDIDATE` (no caveat) | cited fix shipped — clean evidence per ADR-079 shapes | Do **not** dispatch a full iter. Dispatch ONE `/wr-itil:review-problems` relevance-close sweep reusing the Step 0c `claude -p` pre-flight shape (see Step 0c / Step 5). Its Step 4.6 batch-closes the selected ticket **and any sibling CLOSE-CANDIDATEs** in one ADR-014 commit. Set the once-per-session sweep sentinel. Loop back to **Step 1** (the sweep changed the backlog + refreshed the README — re-scan re-applies the ADR-076 tier partition from the refreshed rankings). |
457
+ | `0` `CLOSE-CANDIDATE-WITH-CAVEAT` | partial / mixed-phase evidence | Do **not** auto-close — a caveat is the maintainer's decision input, not a mechanical close (review-problems 4.6b/4.6d route AFK caveats to the next interactive confirm). Route to Step 4's user-answerable skip (`skip_reason_category: user-answerable`); queue an `outstanding_questions` entry (`category: "direction"`) carrying the **caveat short-tag + one-line verbatim** from the verdict (P350 brief-before-ID — surface the close-confirmation question, not a bare ID) + the remedy *"Run `/wr-itil:review-problems` to confirm/close this ticket."* Loop back to **Step 3** (minus the skipped ticket). |
458
+ | `1` `KEEP` / `KEEP-WITH-NOTE` | still relevant (paths present, or Phase-1 false-positive class) | Proceed to Step 4 — dispatch the full iter normally. |
459
+ | `2` `SKIP` | age gate (<7 d) OR no extractable evidence | Proceed to Step 4 — the evaluator gives no close signal; default to work. |
460
+ | `3` error | evaluator failed | Proceed to Step 4 — fail-soft, non-blocking (mirrors review-problems Step 4.6 exit-3 "do not abort the pass" + the Step 0 P358 pre-flight failure contract). |
461
+
462
+ **Why the sweep, not an inline close**: ADR-079 constraint #1 forbids a standalone relevance-close — the close MUST run inside `/wr-itil:review-problems`. The orchestrator main turn holds no Edit/Write surface (allowed-tools), so it dispatches the sweep rather than closing inline; Step 3.6 must never grow an inline `git mv` to Closed. The dispatched sweep runs as a `claude -p` subprocess that is **AFK-by-construction** — the Step 5 dispatch constraint forbids `AskUserQuestion` in the worker, so review-problems Step 4.6's surface-batch-confirm flow takes the silent-close branch automatically, identical to the existing Step 0c side-effect path ("Step 0c dispatches `/wr-itil:review-problems` which includes Step 4.6 relevance-close"). The sweep is structurally a pre-flight subprocess (backlog refresh it owns end-to-end), so it inherits the **Step 0 pre-flight subprocess failure handling (P358)** non-blocking revert-and-proceed contract — a failed sweep does NOT halt the loop; fall through to Step 4 normal dispatch and let the full iter rediscover-and-transition (status-quo correctness).
463
+
464
+ **Sweep sentinel (bounded re-dispatch)**: a single review-problems sweep closes every clean CLOSE-CANDIDATE ≥7 d in one pass, so after it commits no clean CLOSE-CANDIDATE should survive the Step 1 re-scan. If a clean CLOSE-CANDIDATE is selected again **after the sentinel is set** (the sweep failed to close it — e.g. review-problems errored), do NOT re-dispatch the sweep (avoids an unbounded sweep loop) and do NOT dispatch a full iter against the already-fixed ticket (the P385 anti-goal). Route it to Step 4's user-answerable skip + queue an `outstanding_questions` entry (`category: "direction"`) naming the ticket + *"clean CLOSE-CANDIDATE survived a relevance-close sweep — confirm/close manually"*, then loop back to Step 3. This keeps the higher-tier ticket visible (ADR-076) rather than silently re-worked or silently dropped.
465
+
466
+ **Loopback tier preservation**: the Step 1 re-scan (clean-close branch) and the Step 3 loopback (caveat / sentinel-survivor branches) both re-apply the ADR-076 tier-first selection (Critical-bypass → Inbound-reported → Internal) and within-tier WSJF ladder over the remaining backlog. If every actionable ticket is filtered out, Step 2 stop-condition #1 fires naturally and the accumulated `outstanding_questions` surface at the Step 2.4 gate.
467
+
468
+ **AFK authorisation per ADR-013 Rule 6**: the evaluator is read-only (no writes, no commits, no external comms); routing is deterministic per the table above — no `AskUserQuestion` at this step (ADR-044 framework-resolution boundary + P132 mechanical-stage carve-out, identical posture to Step 3.5). User input is preserved at the loop-end Step 2.4 surface where the caveat / survivor questions accumulate. The dispatched sweep's own commit grain is ADR-014 (the orchestrator main turn does not commit at Step 3.6).
469
+
470
+ **Compose-with**: ADR-079 (relevance-close evaluator + constraint #1 sweep-not-standalone), ADR-076 (tier-first selection preserved on every loopback), ADR-026 (evidence-grounded verdict + structured caveat field), ADR-013 Rule 5/6 (silent-pass + AFK fail-safe), ADR-044 cat 4 + P132 (mechanical-stage carve-out — no AskUserQuestion), ADR-014 (sweep owns its commit), ADR-032 + P084 (subprocess isolation — AFK-by-construction silent-close), ADR-049 (PATH shim), ADR-052 / P081 (behavioural coverage via the reused evaluator's bats; no structural SKILL-prose test), P358 (pre-flight subprocess failure → non-blocking revert-and-proceed), P271 / Step 0c (dispatch-shape reuse), P344 / RFC-016 / Step 3.5 (sibling shift-left orchestrator predicate), P346 / P347 (relevance-close drivers).
471
+
472
+ <!-- @jtbd JTBD-006 (Progress the Backlog While I'm Away — pre-dispatch relevance gate closes already-shipped tickets cheaply instead of rediscovering the fix at full iter cost) -->
473
+
439
474
  ### Step 4: Classify each problem
440
475
 
441
476
  Read the problem file and apply these deterministic rules: