@windyroad/itil 0.57.1 → 0.57.2-preview.929

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.57.1"
500
+ "version": "0.57.2"
501
501
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.57.1",
3
+ "version": "0.57.2-preview.929",
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"
@@ -121,6 +121,10 @@ After the delegated `/wr-itil:manage-problem <NNN>` completes:
121
121
  - Loop over multiple tickets — that's `/wr-itil:work-problems` (plural).
122
122
  - Commit the ranking refresh or the per-ticket work — delegated skills commit per ADR-014.
123
123
 
124
+ ## Goal anchor for headless runs (P390 / ADR-094)
125
+
126
+ A headless single-ticket run can anchor its completion with Claude Code's native `/goal` external evaluator (≥ v2.1.139), so a fresh model — not the working agent — judges whether the ticket genuinely reached an end state: `claude -p "/goal Run /wr-itil:work-problem to work the top ticket. Complete when the report printed in the conversation shows a committed outcome (with commit SHA) or a recorded blocker for the selected ticket."` (No turn-bound: trust the goal — the loop stops only at its real end states: a committed outcome, a recorded blocker, or quota exhaustion.) There is no programmatic mid-session surface for setting a goal (probed 2026-07-06, v2.1.201) — interactive users type `/goal` themselves; the skill proceeds identically either way. The plural orchestrator's anchor contract (canonical condition, printed-evidence rule, one-directional semantics) lives at `/wr-itil:work-problems` Step 0e.
127
+
124
128
  ## Related
125
129
 
126
130
  - **P071** (`docs/problems/071-argument-based-skill-subcommands-are-not-discoverable.open.md`) — originating ticket. This skill is phase 3 of the P071 phased-landing plan (list-problems was phase 1; review-problems was phase 2).
@@ -285,7 +285,7 @@ The annotation pre-empts the "surprise heavy iter" perception JTBD-006 expects a
285
285
  <!-- @jtbd JTBD-006 (Progress the Backlog While I'm Away — AFK orchestrator pre-flights check-upstream-responses so outbound STATE/NEW deltas surface without manual polling) -->
286
286
  <!-- @jtbd JTBD-004 (Connect Agents Across Repos to Collaborate — closes the outbound symmetric feedback loop) -->
287
287
 
288
- After Step 0d completes (whether dispatched or silent-passed), proceed to the shared pre-flight failure-handling contract below, then to Step 1.
288
+ After Step 0d completes (whether dispatched or silent-passed), proceed to the shared pre-flight failure-handling contract below, then to Step 0e, then to Step 1.
289
289
 
290
290
  ### Step 0 pre-flight subprocess failure handling (P358 — non-blocking revert-and-proceed)
291
291
 
@@ -303,6 +303,34 @@ Step 0b / Step 0c / Step 0d (and **any future Step 0x pre-flight** that reuses t
303
303
 
304
304
  **Compose-with**: ADR-032 § "Pre-flight subprocess failure handling — non-blocking revert-and-proceed (P358 amendment)" (the architectural record + the iter-vs-pre-flight failure-semantics distinction), Step 5 exit-code semantics (the iter-failure HALT contract this is distinguished from), ADR-019 (preflight-reconciliation clean-tree surface), ADR-009 (no-trust-window-extension — the `git reset` of any staged residue), ADR-013 Rule 6 (non-interactive recovery), P358 (driver ticket). This is a fourth symmetric pre-flight surface alongside the three "Staleness contract drift" clauses (lines for Step 0b/0c/0d) — a future Step 0x pre-flight inherits this failure rule by construction; do NOT re-derive a step-specific copy.
305
305
 
306
+ ### Step 0e: `/goal` loop-anchor (P390 / ADR-094 / RFC-047 / STORY-040)
307
+
308
+ The loop's stop decision is anchored by Claude Code's native [`/goal`](https://code.claude.com/docs/en/goal) command (≥ v2.1.139): a per-turn **external evaluator** (the configured small fast model, wrapping a session-scoped prompt-based Stop hook) judges a completion condition against what the orchestrator has printed in the transcript. This breaks the P390 same-actor conflation — the working agent that is prone to inventing subjective stops no longer decides whether stopping is justified; Step 2.4 Gate (0) remains the first-line objective *self*-check, and `/goal` is the *external* check that the orchestrator keeps turning until Gate (0) genuinely passes.
309
+
310
+ **Canonical goal condition** (owned here; the Step 2.4 Gate (0) table shape and this condition are a coupled contract — reshape both in the same commit):
311
+
312
+ ```
313
+ The /wr-itil:work-problems AFK backlog drain is complete: the final summary printed in the conversation contains a Step 2.4 Gate (0) re-scan table (fresh open/known-error glob) classifying every ticket and showing ZERO dispatchable tickets, followed by the ALL_DONE sentinel — or the session ends with a Hard-fail halt directive naming the gate that could not complete — or the summary reports quota exhaustion.
314
+ ```
315
+
316
+ There is no turn-bound: the loop runs until a real end state (printed Gate (0) zero-dispatchable + ALL_DONE, a Hard-fail halt, or quota exhaustion). Trust the goal — a turn cap would just re-create the premature stop this anchor exists to prevent (P422). P160/ADR-093 quota pacing throttles token burn so an honest ALL_DONE is reachable within the window.
317
+
318
+ **Placement — orchestrator session ONLY.** The goal lives on the orchestrator session, never on the `claude -p` iter subprocesses: iters end naturally after one ticket (ADR-032 / P077 / P084), and a backlog-empty goal there would push an iter past its one-ticket carve-out.
319
+
320
+ **Setting the anchor.** There is no programmatic mid-session surface (empirically probed 2026-07-06, v2.1.201: no `--goal` CLI flag; the Skill tool rejects it — "goal is a UI command, not a skill"; only the user can type it mid-session). So:
321
+
322
+ - **Headless AFK launch (the anchor-guaranteed path)** — start the orchestrator with the goal set. Copy-paste-complete one-liner:
323
+
324
+ ```bash
325
+ claude -p --permission-mode bypassPermissions "/goal Run /wr-itil:work-problems to drain the problem backlog. The drain is complete only when the final summary printed in the conversation contains a Step 2.4 Gate (0) re-scan table showing zero dispatchable open/known-error tickets followed by the ALL_DONE sentinel, or a Hard-fail halt directive, or reported quota exhaustion."
326
+ ```
327
+
328
+ (The condition text itself carries the skill invocation, so the anchored session enters the loop — a bare condition would set a goal over an empty session.)
329
+
330
+ - **Interactive invocation (nudge-and-proceed)** — when the loop starts without an active goal (no `/goal` directive or evaluator-reason lines visible in the session context), print ONE nudge line surfacing the exact command for the user to type — `/goal <canonical condition above>` — then **proceed with the loop regardless**. The anchor is defense-in-depth over Gate (0), never a precondition: halting an AFK loop for a missing anchor would itself defeat JTBD-006. No `AskUserQuestion` fires here (mechanical stage; ADR-044 category 4).
331
+
332
+ **One-directional anchor.** The goal forces continuation; it never authorises a stop. A goal that is cleared (or was never set) does NOT discharge Gate (0) — `ALL_DONE` still requires the full Step 2.4 sequence. Requirements floor: `/goal` needs workspace trust + hooks enabled; below the floor the loop degrades honestly to Gate (0)-only behaviour.
333
+
306
334
  ### Step 1: Scan the backlog
307
335
 
308
336
  Read `docs/problems/README.md` if it exists and is fresh (check via git history — see manage-problem step 9 for the cache freshness check). If stale or missing, scan all open + known-error tickets via the dual-tolerant pattern `ls docs/problems/*.open.md docs/problems/*.known-error.md docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null` (RFC-002 migration window — covers BOTH the flat `<NNN>-<title>.<state>.md` filename-suffix layout AND the per-state subdir `<state>/<NNN>-<title>.md` layout), extract their WSJF scores, and rank them.
@@ -340,12 +368,14 @@ Before the orchestrator emits the final `ALL_DONE` sentinel for the AFK loop, it
340
368
  - it carries an upstream-blocked marker (`## Reported Upstream` / `- **Upstream report pending** --` / em-dash legacy) or a recorded blocked classification with a dead-end investigation (stop-condition #3);
341
369
  - it was filtered out THIS session by Step 3.5 (interactive-ratification predicate) or Step 3.6 (already-shipped relevance gate) — keyed off the durable per-session skip record those steps write (the `outstanding_questions` entry in `.afk-run-state/outstanding-questions.jsonl` carrying the ticket id), NOT agent recollection, so the classification is reproducible across the re-scan and cannot loop forever;
342
370
  - its fix changeset sits in `docs/changesets-holding/` with an unmet reinstate criterion (held, not dispatchable).
343
- Every other open / known-error ticket is **dispatchable** — ordinary autonomous fix-and-commit work. The agent MUST NOT reclassify a dispatchable ticket as non-dispatchable because the *salient* remainder of the backlog is interactive-gated, because the ticket "feels" out of scope, or because a user-directed pivot consumed the loop's attention. The subjective "this is a natural stopping point" judgement is exactly the P390 failure; the classification is per-ticket and marker-bound.
371
+ Every other open / known-error ticket is **dispatchable** — ordinary autonomous fix-and-commit work. The agent MUST NOT reclassify a dispatchable ticket as non-dispatchable because the *salient* remainder of the backlog is interactive-gated, because the ticket "feels" out of scope, or because a user-directed pivot consumed the loop's attention. The subjective "this is a natural stopping point" judgement is exactly the P390 failure; the classification is per-ticket and marker-bound. **The classification MUST be PRINTED as a table in the turn output** (ticket → dispatchable/non-dispatchable → the recorded marker that decided) — not merely computed. A computed-but-unprinted re-scan is invisible to the Step 0e `/goal` external evaluator, which judges only what the transcript surfaces (ADR-026 grounding); the printed table is the evidence the canonical goal condition names.
344
372
 
345
373
  3. *Decide.* If the re-scan yields **≥1 dispatchable ticket**, `ALL_DONE` is FORBIDDEN: the stop-condition the orchestrator was about to emit does NOT objectively hold. The orchestrator loops back to Step 3 tier-first selection (Critical-bypass → Inbound-reported → Internal, within-tier WSJF per ADR-076) over the dispatchable set and dispatches the next iter — it does NOT proceed to gate (a)/(b)/(c). Only when the re-scan yields **zero dispatchable tickets** does gate (0) pass and the sequence proceed to gate (a). Gate (0) finding work is a **loopback, not a halt** — it is productive (the loop resumes draining), so it is NOT a Hard-fail halt trigger.
346
374
 
347
375
  **Why gate (0) fires first**: gates (a)/(b)/(c) (surface questions → retro → emit) presume the loop is genuinely done; running the retro and emitting `ALL_DONE` while dispatchable work remains prematurely ends the AFK drain (P390), forcing the user to re-prompt "keep working the backlog" and defeating JTBD-006. Gate (0) makes "the backlog is objectively empty of dispatchable tickets" a hard, re-verified precondition of the whole sequence rather than a subjective agent judgement. A user-directed mid-loop pivot (e.g. an eval-cohort detour) does NOT discharge the Tier-exhaustion obligation: after the pivot, gate (0)'s re-scan resumes tier selection rather than terminating — which also catches the P390 coverage miss where a Tier-1 ticket (P382) was skipped entirely. Sibling class: P332 (run-retro skip rationalisation), P148 (Stage-1 ticketing skip), P175 (scope-pin loop-control inference) — all agent-invented loop-control stops the framework did not authorise (ADR-044 "Continue / stop loops" is framework-resolved: the natural stop is concrete — `ALL_DONE` conditions objectively met — not "this feels done").
348
376
 
377
+ **Gate (0) × Step 0e `/goal` anchor (ADR-094).** Under an active goal, the `ALL_DONE` emit does not by itself end the session — the Step 0e external evaluator reads the printed gate (0) table + sentinel and independently confirms the condition holds; a premature emit just triggers a "keep working" turn with the evaluator's reason as guidance. The anchor is one-directional: a cleared goal (or a loop that was never anchored) does NOT relax this gate — gate (0) fires unconditionally either way.
378
+
349
379
  **Gate (a) — Outstanding-questions surface + oversight-unconfirmed drain (P348 amendment 2026-06-02).** Two sub-surfaces, both fire in this gate:
350
380
 
351
381
  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.
@@ -1198,7 +1228,7 @@ When every skipped ticket is in the `upstream-blocked` category (stop-condition
1198
1228
  ## Related
1199
1229
 
1200
1230
  - **P341** (`docs/problems/open/341-work-problems-skill-must-surface-outstanding-questions-then-run-retro-before-emitting-all-done.md`) — driver for Step 2.4 Pre-`ALL_DONE` gate sequence (UNCONDITIONAL fire of outstanding-questions surface + session-level retro before `ALL_DONE` emit). 2026-05-31 user direction (verbatim in ticket Description): *"The work-problems skill MUST surface the outstanding questions at the end before emitting ALL_DONE. It MUST then run a retro. Only then should it emit ALL_DONE."* Closes the structural gap that allowed `ALL_DONE` to emit while direction-class observations remained queued AND without a session-level retro running. Behavioural second-source: `test/work-problems-p341-pre-all-done-gate.bats`. Composes with P086 (extends iter-level retro-on-exit to orchestrator-level), P126 (preserves `halt-paths-must-route-design-questions-through-Step-2.5b` principle), ADR-014 (retro commits its own work), ADR-044 (framework-resolution boundary for when to surface — now framework-resolved as unconditional pre-`ALL_DONE`).
1201
- - **P390** (`docs/problems/known-error/390-agent-declares-all-done-prematurely-while-actionable-backlog-remains.md`) — driver for Step 2.4 **Gate (0) — Objective backlog-empty assertion** (prepended ahead of gate (a)). Bug shape: the orchestrator emitted `ALL_DONE` while a dispatchable Tier-2 backlog remained, by generalising "the *salient* remainder is interactive-gated" to "Step 2 stop-condition #2 holds" — a subjective stop the framework did not authorise; it also skipped P382 (Tier-1 sev-16) entirely. Fix: before `ALL_DONE`, gate (0) re-scans the live open/known-error backlog (fresh dual-tolerant glob, not the Step 1 cache) and classifies each ticket dispatchable/non-dispatchable OBJECTIVELY by recorded marker (verifying / `## Fix Released`; upstream-blocked; blocked dead-end; Step 3.5/3.6 durable per-session skip record; held changeset). ≥1 dispatchable ticket FORBIDS `ALL_DONE` and loops back to Step 3 tier-first selection (loopback, not halt); a user-directed pivot does not discharge the Tier-exhaustion obligation. Sibling loop-control-stop class: P332 (run-retro skip rationalisation), P148 (Stage-1 ticketing skip), P175 (scope-pin loop-control inference); hardens P341's precondition. Behavioural second-source: `eval/promptfooconfig.yaml` Tier-A regex + Tier-B llm-rubric asserting the orchestrator does NOT emit `ALL_DONE` when ≥1 dispatchable ticket remains. Per ADR-044 "Continue / stop loops" framework-resolution (the natural stop is concrete — `ALL_DONE` conditions objectively met).
1231
+ - **P390** (`docs/problems/known-error/390-agent-declares-all-done-prematurely-while-actionable-backlog-remains.md`) — driver for Step 2.4 **Gate (0) — Objective backlog-empty assertion** (prepended ahead of gate (a)). Bug shape: the orchestrator emitted `ALL_DONE` while a dispatchable Tier-2 backlog remained, by generalising "the *salient* remainder is interactive-gated" to "Step 2 stop-condition #2 holds" — a subjective stop the framework did not authorise; it also skipped P382 (Tier-1 sev-16) entirely. Fix: before `ALL_DONE`, gate (0) re-scans the live open/known-error backlog (fresh dual-tolerant glob, not the Step 1 cache) and classifies each ticket dispatchable/non-dispatchable OBJECTIVELY by recorded marker (verifying / `## Fix Released`; upstream-blocked; blocked dead-end; Step 3.5/3.6 durable per-session skip record; held changeset). ≥1 dispatchable ticket FORBIDS `ALL_DONE` and loops back to Step 3 tier-first selection (loopback, not halt); a user-directed pivot does not discharge the Tier-exhaustion obligation. Sibling loop-control-stop class: P332 (run-retro skip rationalisation), P148 (Stage-1 ticketing skip), P175 (scope-pin loop-control inference); hardens P341's precondition. Behavioural second-source: `eval/promptfooconfig.yaml` Tier-A regex + Tier-B llm-rubric asserting the orchestrator does NOT emit `ALL_DONE` when ≥1 dispatchable ticket remains. Per ADR-044 "Continue / stop loops" framework-resolution (the natural stop is concrete — `ALL_DONE` conditions objectively met). **Reopened fix (2026-07-05 → 2026-07-06, ADR-094 / RFC-047 / STORY-040)**: Gate (0) alone proved insufficient (self-assessment — the same actor decides "should I stop" and "is stopping justified"); Step 0e anchors the loop with the native `/goal` external evaluator (per-turn small-fast-model judgement of the printed gate (0) table), gate (0)'s classification MUST be printed in turn output as the evaluator's evidence, and the headless launch shape `claude -p "/goal <canonical condition>"` is the anchor-guaranteed path (interactive starts get a nudge-and-proceed fallback).
1202
1232
  - **P342** (`docs/problems/open/342-iter-retros-queue-observations-as-outstanding-questions-instead-of-auto-ticketing-same-trust-boundary-as-step-4a.md`) — driver for Step 5 iter-prompt body's retro-surfaced observation classification taxonomy and capture-* carve-out. Iter retros' observations of recurring class-of-behaviour now route to `/wr-itil:capture-problem` (mechanical-stage carve-out per run-retro Step 4a precedent); only direction-setting observations queue at `outstanding_questions`; ambiguous defaults to auto-ticket per the trust-boundary asymmetry. The "no `capture-*` siblings mid-loop" rule is preserved for non-retro mid-iter capture (P078-class spam); the carve-out is bounded to the retro path. Sibling locus: `packages/retrospective/skills/run-retro/SKILL.md` Step 4b carries the symmetric mirror (same trust-boundary fires whether retro runs in iter context OR standalone in main turn). Behavioural second-source: `test/work-problems-p342-retro-auto-ticket-carveout.bats` + `packages/retrospective/skills/run-retro/test/run-retro-step-4b-retro-auto-ticket-carveout.bats`. Composes with run-retro Step 4a (precedent), ADR-013 Rule 5 (policy-authorised silent proceed), ADR-032 (foreground-spawns-N-background fanout already documented for Stage 1 in run-retro Step 4b), ADR-044 (mechanical-stage carve-out), P130 (mid-loop AskUserQuestion ban unchanged), P078 (capture-on-correction — distinct trigger surface; both end in capture but for different signals).
1203
1233
  - **P121** (`docs/problems/121-afk-orchestrator-should-sigterm-stuck-subprocesses-after-idle-timeout.verifying.md`) — driver for Step 5's backgrounded-poll-loop dispatch shape (replacing the prior foreground-synchronous form) and the idle-timeout SIGTERM branch. The 2026-04-25 P118 iter 5 evidence: an iteration subprocess sat idle ~70 min after its final commit, then SIGTERM produced a clean JSON exit-flush. Fix: orchestrator backgrounds the subprocess, polls every 60s, computes `LAST_ACTIVITY_MARK = max(DISPATCH_START_EPOCH, git log -1 --format=%at HEAD)`, and sends SIGTERM when `now - LAST_ACTIVITY_MARK > WORK_PROBLEMS_IDLE_TIMEOUT_S` (default 3600s = 60 min). Behavioural second-source: `test/work-problems-step-5-idle-timeout-sigterm.bats` exercises a fake `claude -p` shim that sleeps past the threshold and asserts SIGTERM, JSON exit-flush, env-var override, and within-threshold no-fire. Step 6's per-iter progress line SHOULD annotate `(SIGTERM_SENT)` when the branch fires so users can distinguish recovered iters from natural completions. ADR-032's subprocess-boundary variant amended 2026-04-26 with the backgrounded-poll-loop refinement.
1204
1234
  - **P146** (`docs/problems/146-afk-iteration-subprocess-bash-until-loop-polls-bats-output-with-bats-console-regex-against-tap-format.verifying.md`) — driver for Step 5 iteration prompt body's bats-output-polling-discipline clause. The 2026-04-29 incident (iter 1, PID 23580 child PID 16408) saw a `bash until`-loop poll a backgrounded bats output file with regex `^[0-9]+ tests?,` (bats's *default* console-summary format) against `bats --tap` output that never emits that line — silent infinite spin after bats completed; manual SIGTERM at 68m34s wall-clock; metadata loss per the P147 stuck-before-emit subclass. The polling idiom is NOT taught by any SKILL.md (audit confirmed via repo grep) — it is agent-learned from training data. Fix: prompt-discipline rule in the iteration prompt body's Constraints list explicitly forbidding the antipattern, naming `wait $bg_pid` (or Bash-tool `run_in_background=true` + `BashOutput`) as the safe substitute, and citing the TAP-vs-console-summary divergence so future contributors don't "fix" the rule incorrectly. Behavioural second-source: `test/work-problems-step-5-bats-polling-discipline.bats` asserts the prohibition phrase, the safe-substitute pointer, the P146 cite, the divergence explanation, and the Related-section cite.