@windyroad/itil 0.49.5 → 0.50.0-preview.694

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.49.5"
500
+ "version": "0.50.0"
501
501
  }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # Generated by scripts/sync-shim-wrappers.sh from
3
+ # packages/shared/lib/shim-wrapper-template.sh. DO NOT EDIT individual
4
+ # shim files in packages/*/bin/wr-* directly; edit the template + run
5
+ # `npm run sync:shim-wrappers` to regenerate.
6
+ #
7
+ # Resolution (ADR-080):
8
+ # 1. If the wrapper's parent dir is semver-shaped, treat as installed-
9
+ # cache execution and resolve to the highest-version sibling's
10
+ # scripts/ entry below.
11
+ # 2. Otherwise (parent dir is e.g. `architect`), treat as source-
12
+ # monorepo execution and dispatch to own scripts/. The source-repo-
13
+ # guard `exec` is the anchor parsed by
14
+ # packages/retrospective/scripts/check-tarball-shipped-shims.sh.
15
+ # 3. If the cache parent contains zero semver-shaped siblings, exit
16
+ # 127 with a stderr message naming the cache parent (per SQ-080-2).
17
+ #
18
+ # @adr ADR-080 (highest-version-wins shim wrapper plugin scaffold)
19
+ # @adr ADR-049 (plugin-bundled scripts resolve via bin/ on $PATH — amended)
20
+ # @problem P343 (mid-session staleness window)
21
+
22
+ set -euo pipefail
23
+
24
+ SHIM_DIR="$(cd "$(dirname "$0")" && pwd)"
25
+ OWN_VERSION_DIR="$(dirname "$SHIM_DIR")"
26
+ OWN_VERSION_NAME="$(basename "$OWN_VERSION_DIR")"
27
+ CACHE_PARENT="$(dirname "$OWN_VERSION_DIR")"
28
+
29
+ SEMVER_RE='^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$'
30
+
31
+ # Source-repo guard: own parent dir is NOT semver → dispatch to own scripts/.
32
+ if ! [[ "$OWN_VERSION_NAME" =~ $SEMVER_RE ]]; then
33
+ exec "$SHIM_DIR/../scripts/check-fix-rfc-trace.sh" "$@"
34
+ fi
35
+
36
+ # Cache execution: pick the highest-semver sibling under CACHE_PARENT.
37
+ HIGHEST=""
38
+ while IFS= read -r dir; do
39
+ name="$(basename "$dir")"
40
+ [[ "$name" =~ $SEMVER_RE ]] || continue
41
+ if [[ -z "$HIGHEST" ]] || [[ "$(printf '%s\n%s\n' "$HIGHEST" "$name" | sort -V | tail -1)" == "$name" ]]; then
42
+ HIGHEST="$name"
43
+ fi
44
+ done < <(find "$CACHE_PARENT" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
45
+
46
+ if [[ -z "$HIGHEST" ]]; then
47
+ printf 'wr-shim: no cached versions in %s\n' "$CACHE_PARENT" >&2
48
+ exit 127
49
+ fi
50
+
51
+ exec "$CACHE_PARENT/$HIGHEST/scripts/check-fix-rfc-trace.sh" "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.49.5",
3
+ "version": "0.50.0-preview.694",
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"
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bash
2
+ # packages/itil/scripts/check-fix-rfc-trace.sh
3
+ #
4
+ # The load-bearing PREDICATE half of the fix-time RFC-trace gate
5
+ # (P314 Phase 2 / RFC-005 B3). Before fix work commences on a Known
6
+ # Error, ADR-072 requires an RFC tracing the problem; ADR-073 requires
7
+ # that a *missing* RFC be auto-created (never blocked), everywhere the
8
+ # gate fires (interactive /wr-itil:manage-problem AND AFK
9
+ # /wr-itil:work-problems).
10
+ #
11
+ # This script answers the deterministic question — "does any RFC trace
12
+ # this problem?" — and, when none does, emits an auto-create DIRECTIVE
13
+ # on stdout naming the canonical vehicle (/wr-itil:capture-rfc). It does
14
+ # NOT create the RFC itself: the create is skill-orchestrated through
15
+ # capture-rfc (the canonical ADR-070-compliant problem-traced-skeleton
16
+ # vehicle — already allocates the next RFC ID, refreshes the README, and
17
+ # carries no Considered-Options block), so the create logic is not
18
+ # duplicated here. Architect verdict 2026-06-16 (P314 iter 11): the
19
+ # detect-in-script / create-via-capture-rfc split is consistent with the
20
+ # ADR-060 I1 load-bearing-from-the-start standard (the sibling trace
21
+ # invariant enforces identically — deterministic detection in committed
22
+ # shell, create orchestrated through capture).
23
+ #
24
+ # Usage:
25
+ # check-fix-rfc-trace.sh <problem-file> [<rfcs-dir>]
26
+ #
27
+ # Default <rfcs-dir> is `docs/rfcs`.
28
+ #
29
+ # The PID is derived from the problem filename: `<NNN>-<slug>.md`
30
+ # (under any docs/problems/<state>/ subdir) → `P<NNN>`.
31
+ #
32
+ # Behaviour (ADR-073 — auto-create-not-block; the gate NEVER blocks):
33
+ # - An RFC traces the problem when its frontmatter `problems:` array
34
+ # contains the exact PID (boundary-safe: P31 / P3140 do NOT match a
35
+ # P314 query). → exit 0, EMPTY stdout (fix proceeds; no auto-create).
36
+ # - No RFC traces the problem → exit 0, stdout carries the directive
37
+ # `no-rfc-trace: P<NNN> — ...capture-rfc...ADR-073...` for the calling
38
+ # skill to act on (auto-create then proceed). Exit stays 0: a missing
39
+ # RFC is never a block (ADR-073).
40
+ # - Missing problem file / no args → exit 2 (caller misuse), stderr usage.
41
+ #
42
+ # @adr ADR-072 (RFC required at the propose-fix step on a Known Error —
43
+ # the gate placement this predicate serves)
44
+ # @adr ADR-073 (fix-time gate auto-creates a missing RFC, everywhere —
45
+ # never blocks; hence exit 0 on the absent branch + a directive, not a
46
+ # deny)
47
+ # @adr ADR-071 (every fix goes through an RFC — unconditional, no carve-out)
48
+ # @adr ADR-070 (the auto-created RFC is a problem-traced skeleton with no
49
+ # independent decisions — guaranteed by routing the create through
50
+ # capture-rfc rather than a second create surface)
51
+ # @adr ADR-060 (I1 load-bearing-from-the-start; I13 fix-proposal invariant)
52
+ # @adr ADR-049 (invoked via the wr-itil-check-fix-rfc-trace bin shim on
53
+ # $PATH; never repo-relative from a SKILL)
54
+ # @adr ADR-052 (behavioural bats coverage in
55
+ # packages/itil/scripts/test/check-fix-rfc-trace.bats)
56
+ # @problem P314
57
+
58
+ set -uo pipefail
59
+
60
+ PROBLEM_FILE="${1:-}"
61
+ RFCS_DIR="${2:-docs/rfcs}"
62
+
63
+ if [ -z "$PROBLEM_FILE" ]; then
64
+ echo "usage: check-fix-rfc-trace.sh <problem-file> [<rfcs-dir>]" >&2
65
+ exit 2
66
+ fi
67
+
68
+ if [ ! -f "$PROBLEM_FILE" ]; then
69
+ echo "check-fix-rfc-trace.sh: problem file not found: $PROBLEM_FILE" >&2
70
+ exit 2
71
+ fi
72
+
73
+ # ── Derive PID from the problem filename (`<NNN>-<slug>.md`). ────────────────
74
+ PBASE="$(basename "$PROBLEM_FILE")"
75
+ PNUM="${PBASE%%-*}"
76
+ if ! [[ "$PNUM" =~ ^[0-9]+$ ]]; then
77
+ echo "check-fix-rfc-trace.sh: cannot derive PID from filename: $PBASE" >&2
78
+ exit 2
79
+ fi
80
+ PID="P${PNUM}"
81
+
82
+ # ── Scan rfcs-dir for any RFC whose frontmatter `problems:` claims PID. ──────
83
+ # Mirrors the PID-boundary-safe parse in update-problem-rfcs-section.sh.
84
+ traced=0
85
+ if [ -d "$RFCS_DIR" ]; then
86
+ shopt -s nullglob
87
+ for f in "$RFCS_DIR"/RFC-[0-9][0-9][0-9]-*.md; do
88
+ # Single-line frontmatter form: `problems: [P<NNN>, P<NNN>, ...]`.
89
+ raw="$(awk '/^problems:/ { print; exit }' "$f")"
90
+ [ -z "$raw" ] && continue
91
+ inner="$(printf '%s' "$raw" | sed -E 's/^[[:space:]]*problems:[[:space:]]*\[//; s/\][[:space:]]*$//')"
92
+ while IFS= read -r tok; do
93
+ tok="$(printf '%s' "$tok" | tr -d " \"'")"
94
+ if [ "$tok" = "$PID" ]; then
95
+ traced=1
96
+ break
97
+ fi
98
+ done <<< "$(printf '%s' "$inner" | tr ',' '\n')"
99
+ [ "$traced" -eq 1 ] && break
100
+ done
101
+ shopt -u nullglob
102
+ fi
103
+
104
+ if [ "$traced" -eq 1 ]; then
105
+ # An RFC traces the problem — the fix proceeds; no auto-create needed.
106
+ exit 0
107
+ fi
108
+
109
+ # No RFC traces the problem. Emit the auto-create directive (ADR-073) and
110
+ # exit 0 — a missing RFC is NEVER a block. The calling skill auto-creates a
111
+ # problem-traced skeleton via /wr-itil:capture-rfc, then proceeds with the fix.
112
+ printf 'no-rfc-trace: %s — auto-create a problem-traced RFC via /wr-itil:capture-rfc before fix work, then proceed (ADR-072 placement / ADR-073 auto-create-not-block)\n' "$PID"
113
+ exit 0
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # @problem P314 — Phase 2 (RFC-005 B3/B4): the fix-time propose-fix
4
+ # RFC-trace gate. Before fix work commences on a Known Error, the
5
+ # framework requires an RFC tracing the problem; if none exists, a
6
+ # problem-traced skeleton RFC is auto-created (per ADR-073) rather than
7
+ # blocked. This script is the load-bearing PREDICATE half of that gate
8
+ # (ADR-060 I1 load-bearing-from-the-start carried forward): it detects
9
+ # whether any RFC traces the problem and emits an auto-create directive
10
+ # on stdout when none does. The CREATE half is skill-orchestrated
11
+ # (delegates to /wr-itil:capture-rfc — the canonical ADR-070-compliant
12
+ # problem-traced-skeleton vehicle, no duplication).
13
+ #
14
+ # Behavioural per ADR-052: assert on the script's exit code + stdout
15
+ # directive given fixture RFC corpora — not on script source content
16
+ # (no structural greps per P081).
17
+ #
18
+ # Contract:
19
+ # - <problem-file> whose basename is `<NNN>-<slug>.md` → PID = P<NNN>.
20
+ # - An RFC traces the problem when its frontmatter `problems:` array
21
+ # contains the exact PID (boundary-safe: P31 / P3140 must NOT match
22
+ # a P314 query).
23
+ # - Trace present → exit 0, EMPTY stdout (fix proceeds; no auto-create).
24
+ # - Trace absent → exit 0, stdout carries `no-rfc-trace: P<NNN>` directive
25
+ # (NEVER a non-zero/block exit — ADR-073 auto-create-not-block).
26
+ # - Missing problem file / no args → exit 2 (caller misuse), stderr usage.
27
+ #
28
+ # @adr ADR-072 (RFC required at the propose-fix step on a Known Error)
29
+ # @adr ADR-073 (fix-time gate auto-creates a missing RFC, everywhere —
30
+ # never blocks; hence exit 0 on the absent branch)
31
+ # @adr ADR-071 (every fix goes through an RFC — unconditional, no carve-out)
32
+ # @adr ADR-060 (I1 load-bearing-from-the-start; I13 fix-proposal invariant)
33
+ # @adr ADR-052 (behavioural bats default)
34
+ # @jtbd JTBD-008 (Decompose a Fix Into Coordinated Changes — trace invariant)
35
+ # @jtbd JTBD-006 (Progress the Backlog While I'm Away — never stall/skip)
36
+
37
+ setup() {
38
+ SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
39
+ SCRIPT="$SCRIPTS_DIR/check-fix-rfc-trace.sh"
40
+ RFCS_DIR="$(mktemp -d)"
41
+ PROBLEMS_DIR="$(mktemp -d)"
42
+ }
43
+
44
+ teardown() {
45
+ rm -rf "$RFCS_DIR" "$PROBLEMS_DIR"
46
+ }
47
+
48
+ # Write an RFC fixture whose frontmatter `problems:` array is set verbatim.
49
+ write_rfc() {
50
+ local id="$1" slug="$2" status="$3" problems="$4"
51
+ cat > "$RFCS_DIR/RFC-${id}-${slug}.${status}.md" <<EOF
52
+ ---
53
+ status: ${status}
54
+ rfc-id: ${slug}
55
+ problems: ${problems}
56
+ ---
57
+
58
+ # RFC-${id}: ${slug}
59
+ EOF
60
+ }
61
+
62
+ # Write a problem fixture; returns the path on stdout.
63
+ write_problem() {
64
+ local num="$1" slug="${2:-some-problem}"
65
+ local f="$PROBLEMS_DIR/${num}-${slug}.md"
66
+ cat > "$f" <<EOF
67
+ # Problem ${num}: ${slug}
68
+
69
+ **Status**: Known Error
70
+ EOF
71
+ printf '%s' "$f"
72
+ }
73
+
74
+ @test "trace present (single-PID array) → exit 0, empty stdout" {
75
+ write_rfc 005 some-fix accepted "[P314]"
76
+ pf="$(write_problem 314)"
77
+ run "$SCRIPT" "$pf" "$RFCS_DIR"
78
+ [ "$status" -eq 0 ]
79
+ [ -z "$output" ]
80
+ }
81
+
82
+ @test "trace present (multi-PID array) → exit 0, empty stdout" {
83
+ write_rfc 005 some-fix accepted "[P100, P314, P200]"
84
+ pf="$(write_problem 314)"
85
+ run "$SCRIPT" "$pf" "$RFCS_DIR"
86
+ [ "$status" -eq 0 ]
87
+ [ -z "$output" ]
88
+ }
89
+
90
+ @test "trace absent (empty rfcs dir) → exit 0, auto-create directive on stdout" {
91
+ pf="$(write_problem 314)"
92
+ run "$SCRIPT" "$pf" "$RFCS_DIR"
93
+ [ "$status" -eq 0 ]
94
+ [[ "$output" == *"no-rfc-trace: P314"* ]]
95
+ }
96
+
97
+ @test "trace absent (RFCs exist but none claim this PID) → exit 0, directive" {
98
+ write_rfc 005 other-fix accepted "[P251]"
99
+ write_rfc 013 another accepted "[P346]"
100
+ pf="$(write_problem 314)"
101
+ run "$SCRIPT" "$pf" "$RFCS_DIR"
102
+ [ "$status" -eq 0 ]
103
+ [[ "$output" == *"no-rfc-trace: P314"* ]]
104
+ }
105
+
106
+ @test "PID boundary: P31 in an RFC does NOT satisfy a P314 query" {
107
+ write_rfc 005 short-pid accepted "[P31]"
108
+ pf="$(write_problem 314)"
109
+ run "$SCRIPT" "$pf" "$RFCS_DIR"
110
+ [ "$status" -eq 0 ]
111
+ [[ "$output" == *"no-rfc-trace: P314"* ]]
112
+ }
113
+
114
+ @test "PID boundary: P3140 in an RFC does NOT satisfy a P314 query" {
115
+ write_rfc 005 long-pid accepted "[P3140]"
116
+ pf="$(write_problem 314)"
117
+ run "$SCRIPT" "$pf" "$RFCS_DIR"
118
+ [ "$status" -eq 0 ]
119
+ [[ "$output" == *"no-rfc-trace: P314"* ]]
120
+ }
121
+
122
+ @test "exact match works for a zero-padded low PID (P080 ← [P080])" {
123
+ # PIDs are canonically 3-digit zero-padded (filenames 080-..., citations P080).
124
+ write_rfc 005 padded-pid accepted "[P080]"
125
+ pf="$(write_problem 080)"
126
+ run "$SCRIPT" "$pf" "$RFCS_DIR"
127
+ [ "$status" -eq 0 ]
128
+ [ -z "$output" ]
129
+ }
130
+
131
+ @test "default rfcs-dir is docs/rfcs when second arg omitted" {
132
+ # Run from a temp cwd containing docs/rfcs with a tracing RFC.
133
+ workdir="$(mktemp -d)"
134
+ mkdir -p "$workdir/docs/rfcs"
135
+ cat > "$workdir/docs/rfcs/RFC-005-x.accepted.md" <<'EOF'
136
+ ---
137
+ problems: [P314]
138
+ ---
139
+ # RFC-005: x
140
+ EOF
141
+ pf="$(write_problem 314)"
142
+ run bash -c "cd '$workdir' && '$SCRIPT' '$pf'"
143
+ [ "$status" -eq 0 ]
144
+ [ -z "$output" ]
145
+ rm -rf "$workdir"
146
+ }
147
+
148
+ @test "missing problem file → exit 2, stderr usage" {
149
+ run "$SCRIPT" "$PROBLEMS_DIR/999-nonexistent.md" "$RFCS_DIR"
150
+ [ "$status" -eq 2 ]
151
+ }
152
+
153
+ @test "no args → exit 2" {
154
+ run "$SCRIPT"
155
+ [ "$status" -eq 2 ]
156
+ }
157
+
158
+ @test "directive names the canonical auto-create vehicle (capture-rfc) and ADRs" {
159
+ pf="$(write_problem 314)"
160
+ run "$SCRIPT" "$pf" "$RFCS_DIR"
161
+ [ "$status" -eq 0 ]
162
+ [[ "$output" == *"capture-rfc"* ]]
163
+ [[ "$output" == *"ADR-073"* ]]
164
+ }
@@ -182,9 +182,20 @@ What "work" means depends on the problem's status:
182
182
  - **AFK** (`/wr-itil:work-problems` orchestrator): NEVER ask mid-loop — queue the substance to the iteration's `outstanding_questions` (ADR-044 AFK carve-out) and skip the build; do not guess. This is the **queue-and-continue** universal default per ADR-013 Rule 6 (P352, 2026-06-06 amendment): the iter queues the substance + advances; the orchestrator main turn surfaces the queued question at loop end via the Step 2.5 batched AskUserQuestion.
183
183
  4. This ask is **ADR-044 category-1 direction-setting** and is EXCLUDED from the lazy-AskUserQuestion regression metric (it is legitimate, not lazy). The trigger is narrow — detection is mechanical (the predicate); only genuine unconfirmed decisions about to be built on fire it. Do NOT over-fire on confirmed/superseded/obvious decisions (inverse-P078 / P132 guard). A born-`proposed` marker is fine for *recording*; it is not a licence to *build* (ADR-066 carve-out).
184
184
 
185
+ **I13 propose-fix RFC-trace gate (ADR-072 placement / ADR-073 auto-create — RFC-005 B3/B4).** BEFORE the traversal below, enforce the fix-time RFC-trace invariant: a fix proposed on a Known Error requires an RFC tracing the problem (ADR-071 unconditional; ADR-072 places the gate here, conforming to ADR-022 Known Error semantics — the fix is proposed *after* Known Error and produces the RFC). Run the load-bearing predicate (ADR-049 `$PATH` shim — never repo-relative from a SKILL):
186
+
187
+ ```bash
188
+ wr-itil-check-fix-rfc-trace <problem-file>
189
+ ```
190
+
191
+ - **Empty stdout** (an RFC already traces the problem): proceed to the traversal below.
192
+ - **Non-empty stdout** (directive `no-rfc-trace: P<NNN> …`): **auto-create** a problem-traced skeleton RFC — do NOT block, do NOT skip, do NOT ask (ADR-073 auto-create-everywhere; the create is framework-mediated, not cat-1 direction-setting, so NO `AskUserQuestion` consent gate fires here — P132 / inverse-P078). Delegate to `/wr-itil:capture-rfc` with a problem trace derived from this Known Error (`--problem=P<NNN>` + a fix-scoped title from the problem's `## Fix Strategy` / root cause), which is the canonical ADR-070-compliant vehicle (allocates the next RFC ID, refreshes the RFC README, emits no Considered-Options block — no create logic is duplicated). Then re-run the predicate (now empty) and proceed. The auto-created RFC is the fix-proposal artifact; its body is fleshed out as fix work proceeds (ADR-073 — a living scope artifact, not a one-shot).
193
+
194
+ The predicate is the load-bearing detection half (committed shell + behavioural bats per ADR-052: `packages/itil/scripts/test/check-fix-rfc-trace.bats`); the create half is orchestrated through `capture-rfc` (the ADR-060 I1 load-bearing-from-the-start standard — deterministic detection in committed shell, create through the canonical capture surface). This gate fires at **every** fix-time surface; the AFK `/wr-itil:work-problems` orchestrator dispatches its fix work *through this same manage-problem traversal*, so the gate covers the AFK surface transitively (ADR-073 "everywhere").
195
+
185
196
  The Phase 2 working-the-problem traversal makes "implement the fix" concretely traceable via stories (per ADR-060 lines 300-320). Replaces the prior vague "implement the fix following the project's development workflow" with a deterministic problem → RFC → story dispatch:
186
197
 
187
- 1. **Read the problem's `## Fix Strategy` section** — extract referenced RFC IDs (anchor links / inline references like `RFC-NNN`). If no RFCs are referenced, the problem is non-decomposed Phase 1 work: fall through to the legacy direct-implementation path (step 6 below).
198
+ 1. **Read the problem's `## Fix Strategy` section** — extract referenced RFC IDs (anchor links / inline references like `RFC-NNN`). The I13 gate above has already guaranteed an RFC traces the problem; if the `## Fix Strategy` prose itself references none (a freshly auto-created RFC may not yet be cited inline), fall through to the legacy direct-implementation path (step 6 below) using the traced/auto-created RFC.
188
199
  2. **For each referenced RFC** (in the order they appear in the Fix Strategy section), read its frontmatter `stories:` array (per ADR-060 line 259, the array is ORDERED — array position IS execution sequence):
189
200
  - **Non-empty `stories:` array** (story-decomposed RFC): pick the first story whose lifecycle status is `accepted` or `in-progress` — skip `done` stories that already shipped, skip `draft` stories that aren't ready (the `manage-story <NNN> accepted` gate enforces INVEST shape; a draft story is structurally unready). Continue to step 3.
190
201
  - **Empty `stories: []`** (atomic RFC per JTBD-101 friction guard, ADR-060 line 262): fall back to Phase 1 per-RFC iter dispatch — read the RFC body's `## Tasks` section directly; pick the first unticked task; no per-story scoping needed. Skip to step 5 (commit + trailer).
@@ -562,7 +562,7 @@ rm -f "$ITER_JSON"
562
562
 
563
563
  1. **Context**: this is one iteration of the AFK work-problems loop. The user is AFK. The orchestrator selected `P<NNN> (<title>)` as the highest-WSJF actionable ticket.
564
564
  2. **Task**: apply the `/wr-itil:manage-problem` workflow for `work highest WSJF problem that can be progressed non-interactively as the user is AFK`. Follow manage-problem SKILL.md verbatim, including architect / jtbd / style-guide / voice-tone gate reviews and the commit gate (manage-problem Step 11). Because this subprocess has the Agent tool in its own surface, the normal review-via-subagent paths work — no inline-verdict fallback needed.
565
- 3. **Constraints**: commit the completed work per ADR-014. Do NOT push, do NOT run `push:watch`, do NOT run `release:watch` — the orchestrator's Step 6.5 owns release cadence. Do NOT invoke `capture-*` background skills mid-iter (AFK carve-out — ADR-032), **EXCEPT for retro-surfaced observations of recurring class-of-behaviour** — those route to `/wr-itil:capture-problem` per the **P342 mechanical-stage carve-out** (see retro-on-exit constraint #4 below; same trust-boundary as `/wr-retrospective:run-retro` Step 4a verification close-on-evidence — P342). Do NOT use `ScheduleWakeup` under any circumstance (P083 — iteration workers must not self-reschedule). **NEVER call `AskUserQuestion` mid-loop in AFK** (P135 / ADR-044): direction / deviation-approval / one-time-override / silent-framework observations queue at `ITERATION_SUMMARY.outstanding_questions` for loop-end batched presentation. **This includes the manage-problem substance-confirm-before-build guard (ADR-074 (Confirm a decision's substance before building dependent work)):** when the propose-fix step detects that the fix builds on a born-`proposed` decision whose substance is unconfirmed (via `wr-architect-is-decision-unconfirmed`), the iter does NOT implement on it and does NOT ask mid-loop — it queues a `category: "direction"` entry naming the unconfirmed ADR + its Decision Outcome for loop-end confirmation, and routes the ticket to `action: skipped`, `skip_reason_category: user-answerable`. Building on the unconfirmed substance instead (or guessing the choice) is the P315 failure this guard exists to prevent. The queued substance-confirm is a legitimate cat-1 direction ask — it is NOT counted as lazy in the Step 2d Ask Hygiene Pass (ADR-074 lazy-count exclusion). Per-iter `AskUserQuestion` calls are sub-contracting framework-resolved decisions back to the user (lazy deferral per Step 2d Ask Hygiene Pass classification). Non-interactive defaults apply per ADR-013 Rule 6 + ADR-044's framework-resolution boundary. **Treat the user as transient** (P130): even when observably present at orchestrator dispatch time, the user may answer one question and disappear for hours; presence is not a reliable signal and is not the goal. The iter's job is to progress the ticket and accumulate questions for batched surfacing — not to ask "is it OK to proceed?" at a mechanical-stage boundary. **Do NOT poll `bats` output with a bats-console-summary regex against TAP-format output** (P146 — bash until-loop-deadlock antipattern). The bats-console-summary line `<N> tests, <M> failures` is emitted ONLY by bats's *default* (non-TAP) formatter; `bats --tap` does not emit a console summary, so a polling loop of shape `until [ -f $OUT ] && grep -qE '^[0-9]+ tests?,' $OUT; do sleep 5; done` spins forever after bats completes (silent deadlock — no error, no exit; recovery requires manual SIGTERM with metadata loss per the P146/P147 stuck-before-emit subclass). When you need to wait on a backgrounded bats run, prefer `wait $bg_pid` (Unix idiom — completion signaled by process exit, no regex required) or, for the Bash tool, `run_in_background=true` + `BashOutput` polling on the tool's exit-state field rather than regex-poll on stdout. If you genuinely must regex-poll TAP output, anchor on the TAP plan line `^[0-9]+\.\.[0-9]+` (e.g. `1..1455`) — TAP's plan line is emitted on completion and is format-stable across bats versions; the bats-console-summary line is not. The console-summary vs TAP-format divergence is the load-bearing detail: `bats` and `bats --tap` produce structurally different stdout, and the antipattern assumes the former when iter dispatch typically uses the latter. **Do NOT poll subprocess completion with `pgrep -f '<pattern>'` inside an `until` / `while` loop** (P232 — self-referential pgrep deadlock; sibling variant of P146). `pgrep -f` matches against the FULL command line of every running process, so the polling loop's own `zsh -c` argument (which contains the literal `pgrep -f '<pattern>'` text) matches itself; with multiple concurrent polling loops, each loop matches the others and spins forever. Worked example of the antipattern: `until ! pgrep -f 'bats --recursive' > /dev/null 2>&1; do sleep 5; done` — the 2026-05-16 P232 deadlock witness; 4 concurrent polling loops each matched the others' command lines while no actual bats process ran; 45 min wall-clock + $20-30 wasted before manual SIGTERM. The same self-reference shape applies to `while pgrep -f ...; do sleep; done` and to `until ! pkill -0 -f '<pattern>'` / `while pkill -0 -f '<pattern>'` (signal-0 polling). The structural fix is the same as P146: prefer `wait $bg_pid` (Unix idiom — shell-native completion signal, no regex / no pgrep) or Bash-tool `run_in_background=true` + `BashOutput` polling (harness-tracked completion state). The hook `packages/itil/hooks/itil-bash-polling-antipattern-detect.sh` denies these shapes at PreToolUse:Bash, but the prompt rule belongs here too — structural enforcement + prompt discipline together close the class. **If the fix changes shippable code or package behaviour** (any path under `packages/<plugin>/{src,bin,hooks,skills,scripts,lib,agents}` excluding test paths — `test/`, `hooks/test/`, `scripts/test/` — and excluding `README.md` + `docs/*.md`), **the iter MUST author a `.changeset/*.md` entry in the same single ADR-014-grain commit as the fix** (the changeset names the bumping plugin via the YAML frontmatter `"@windyroad/<plugin>": <patch|minor|major>` per the changesets-action contract). **Doc-only changes** (under `docs/`, `*.md`) **and test-only changes** (under any `test/` path) **that ship no behaviour MAY omit the changeset**. The orchestrator's Step 6.5 release-cadence drain runs `release:watch` only when `.changeset/` is non-empty after push — without an iter-authored changeset, code-shape fixes accumulate without ever shipping to npm (violating JTBD-006's audit-trail expectation + JTBD-007's "Keep Plugins Current" closure dependency). Hook `packages/itil/hooks/itil-changeset-discipline.sh` (P141) provides hook-level enforcement at `git commit` time as defence-in-depth — but plugin hook execution depends on the marketplace cache carrying the current hook version, so the prompt-time constraint here MUST land independently (composes-with the hook; does NOT rely on the hook being installed). Inbound-reported from downstream consumer bbstats as their P195 — see [Related](#related) for `**Origin**: inbound-reported (bbstats#195)` per ADR-076. **`@jtbd JTBD-006`** (load-bearing) **`@jtbd JTBD-007`** (closure-dependent).
565
+ 3. **Constraints**: commit the completed work per ADR-014. Do NOT push, do NOT run `push:watch`, do NOT run `release:watch` — the orchestrator's Step 6.5 owns release cadence. Do NOT invoke `capture-*` background skills mid-iter (AFK carve-out — ADR-032), **EXCEPT** (a) **retro-surfaced observations of recurring class-of-behaviour** — those route to `/wr-itil:capture-problem` per the **P342 mechanical-stage carve-out** (see retro-on-exit constraint #4 below; same trust-boundary as `/wr-retrospective:run-retro` Step 4a verification close-on-evidence — P342); and (b) **the I13 fix-time RFC auto-create** — when the propose-fix gate inside the delegated `/wr-itil:manage-problem` traversal detects an RFC-less Known Error (`wr-itil-check-fix-rfc-trace` emits a `no-rfc-trace:` directive), the iter auto-creates a problem-traced skeleton RFC via `/wr-itil:capture-rfc` then proceeds (ADR-073 auto-create-everywhere). This is NOT an aside-capture distraction: the auto-created RFC is the **mandatory vehicle for THIS iter's own fix** (ADR-071), not a tangential observation — it is in-scope working of the current ticket, framework-mediated (NOT cat-1 direction-setting → NO `AskUserQuestion`, P132), and the loop is NEVER skipped or blocked for a missing RFC (ADR-073). Structured-log the auto-create event to the iter summary (`notes`) per JTBD-006 audit-trail + the ADR-073 reassessment criterion (auto-created-RFC-under-scoped signal feeds `/wr-retrospective:run-retro`). Do NOT use `ScheduleWakeup` under any circumstance (P083 — iteration workers must not self-reschedule). **NEVER call `AskUserQuestion` mid-loop in AFK** (P135 / ADR-044): direction / deviation-approval / one-time-override / silent-framework observations queue at `ITERATION_SUMMARY.outstanding_questions` for loop-end batched presentation. **This includes the manage-problem substance-confirm-before-build guard (ADR-074 (Confirm a decision's substance before building dependent work)):** when the propose-fix step detects that the fix builds on a born-`proposed` decision whose substance is unconfirmed (via `wr-architect-is-decision-unconfirmed`), the iter does NOT implement on it and does NOT ask mid-loop — it queues a `category: "direction"` entry naming the unconfirmed ADR + its Decision Outcome for loop-end confirmation, and routes the ticket to `action: skipped`, `skip_reason_category: user-answerable`. Building on the unconfirmed substance instead (or guessing the choice) is the P315 failure this guard exists to prevent. The queued substance-confirm is a legitimate cat-1 direction ask — it is NOT counted as lazy in the Step 2d Ask Hygiene Pass (ADR-074 lazy-count exclusion). Per-iter `AskUserQuestion` calls are sub-contracting framework-resolved decisions back to the user (lazy deferral per Step 2d Ask Hygiene Pass classification). Non-interactive defaults apply per ADR-013 Rule 6 + ADR-044's framework-resolution boundary. **Treat the user as transient** (P130): even when observably present at orchestrator dispatch time, the user may answer one question and disappear for hours; presence is not a reliable signal and is not the goal. The iter's job is to progress the ticket and accumulate questions for batched surfacing — not to ask "is it OK to proceed?" at a mechanical-stage boundary. **Do NOT poll `bats` output with a bats-console-summary regex against TAP-format output** (P146 — bash until-loop-deadlock antipattern). The bats-console-summary line `<N> tests, <M> failures` is emitted ONLY by bats's *default* (non-TAP) formatter; `bats --tap` does not emit a console summary, so a polling loop of shape `until [ -f $OUT ] && grep -qE '^[0-9]+ tests?,' $OUT; do sleep 5; done` spins forever after bats completes (silent deadlock — no error, no exit; recovery requires manual SIGTERM with metadata loss per the P146/P147 stuck-before-emit subclass). When you need to wait on a backgrounded bats run, prefer `wait $bg_pid` (Unix idiom — completion signaled by process exit, no regex required) or, for the Bash tool, `run_in_background=true` + `BashOutput` polling on the tool's exit-state field rather than regex-poll on stdout. If you genuinely must regex-poll TAP output, anchor on the TAP plan line `^[0-9]+\.\.[0-9]+` (e.g. `1..1455`) — TAP's plan line is emitted on completion and is format-stable across bats versions; the bats-console-summary line is not. The console-summary vs TAP-format divergence is the load-bearing detail: `bats` and `bats --tap` produce structurally different stdout, and the antipattern assumes the former when iter dispatch typically uses the latter. **Do NOT poll subprocess completion with `pgrep -f '<pattern>'` inside an `until` / `while` loop** (P232 — self-referential pgrep deadlock; sibling variant of P146). `pgrep -f` matches against the FULL command line of every running process, so the polling loop's own `zsh -c` argument (which contains the literal `pgrep -f '<pattern>'` text) matches itself; with multiple concurrent polling loops, each loop matches the others and spins forever. Worked example of the antipattern: `until ! pgrep -f 'bats --recursive' > /dev/null 2>&1; do sleep 5; done` — the 2026-05-16 P232 deadlock witness; 4 concurrent polling loops each matched the others' command lines while no actual bats process ran; 45 min wall-clock + $20-30 wasted before manual SIGTERM. The same self-reference shape applies to `while pgrep -f ...; do sleep; done` and to `until ! pkill -0 -f '<pattern>'` / `while pkill -0 -f '<pattern>'` (signal-0 polling). The structural fix is the same as P146: prefer `wait $bg_pid` (Unix idiom — shell-native completion signal, no regex / no pgrep) or Bash-tool `run_in_background=true` + `BashOutput` polling (harness-tracked completion state). The hook `packages/itil/hooks/itil-bash-polling-antipattern-detect.sh` denies these shapes at PreToolUse:Bash, but the prompt rule belongs here too — structural enforcement + prompt discipline together close the class. **If the fix changes shippable code or package behaviour** (any path under `packages/<plugin>/{src,bin,hooks,skills,scripts,lib,agents}` excluding test paths — `test/`, `hooks/test/`, `scripts/test/` — and excluding `README.md` + `docs/*.md`), **the iter MUST author a `.changeset/*.md` entry in the same single ADR-014-grain commit as the fix** (the changeset names the bumping plugin via the YAML frontmatter `"@windyroad/<plugin>": <patch|minor|major>` per the changesets-action contract). **Doc-only changes** (under `docs/`, `*.md`) **and test-only changes** (under any `test/` path) **that ship no behaviour MAY omit the changeset**. The orchestrator's Step 6.5 release-cadence drain runs `release:watch` only when `.changeset/` is non-empty after push — without an iter-authored changeset, code-shape fixes accumulate without ever shipping to npm (violating JTBD-006's audit-trail expectation + JTBD-007's "Keep Plugins Current" closure dependency). Hook `packages/itil/hooks/itil-changeset-discipline.sh` (P141) provides hook-level enforcement at `git commit` time as defence-in-depth — but plugin hook execution depends on the marketplace cache carrying the current hook version, so the prompt-time constraint here MUST land independently (composes-with the hook; does NOT rely on the hook being installed). Inbound-reported from downstream consumer bbstats as their P195 — see [Related](#related) for `**Origin**: inbound-reported (bbstats#195)` per ADR-076. **`@jtbd JTBD-006`** (load-bearing) **`@jtbd JTBD-007`** (closure-dependent).
566
566
  4. **Retro-on-exit (P086) + retro-surfaced observation classification (P342) + iter-owned BRIEFING commit (P212)**: before emitting `ITERATION_SUMMARY`, invoke `/wr-retrospective:run-retro`. Retro runs INSIDE this subprocess so its Step 2b pipeline-instability scan has access to the iteration's rich tool-call history (hook misbehaviour, repeat-workaround patterns, subagent-delegation friction, release-path instability). Tickets retro creates ride a separate path: they delegate through `/wr-itil:manage-problem` which IS ADR-014 in-scope and self-commits each ticket per its own Step 11. Those commits land independently and the orchestrator picks them up on the next Step 1 scan.
567
567
 
568
568
  **BRIEFING.md commit responsibility — iter owns, run-retro does not (P212).** run-retro is explicitly out-of-scope for self-commit per ADR-014's Scope section (which lists `packages/retrospective/skills/run-retro/SKILL.md` under "Out of scope for now"). Retro therefore EDITS but DOES NOT COMMIT `docs/BRIEFING.md` / `docs/briefing/*.md`. The iter subprocess (NOT run-retro, NOT the orchestrator main turn) owns the BRIEFING commit. After retro completes, run `git status --porcelain docs/BRIEFING.md docs/briefing/`. If non-empty, the iter: