@windyroad/itil 0.60.0 → 0.61.0-preview.1033

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.60.0"
500
+ "version": "0.61.0"
501
501
  }
@@ -16,6 +16,24 @@
16
16
  # Forward-compat: advertises /wr-itil:capture-problem (ADR-032 — not
17
17
  # yet shipped) with /wr-itil:manage-problem as the today-target
18
18
  # fallback. When capture-problem ships the wording stays valid.
19
+ #
20
+ # P430 / STORY-047 — WR_SUPPRESS_CORRECTION_DETECT is a DISTINCT guard
21
+ # class, not a split of WR_SUPPRESS_OVERSIGHT_NUDGE. That guard means
22
+ # "an absent user cannot ratify"; this one means "nobody authored a
23
+ # correction". A machine-authored iter prompt carrying an ordinary
24
+ # imperative ("DO NOT skip the gate") is not a correction signal, and
25
+ # conflating the two would couple this gate's suppression to the
26
+ # ADR-068 / ADR-047 oversight-nudge contract. Provenance is a property
27
+ # of the spawning process, so the dispatcher asserts it — the hook does
28
+ # not sniff content for it.
29
+
30
+ # Machine-authored prompt: no correction happened and no user is present to act
31
+ # on the nudge. Ahead of the jq parses so the suppressed path costs one string
32
+ # comparison, and before any marker write so the once-per-session budget is not
33
+ # burned by a prompt that produced no output.
34
+ if [ "${WR_SUPPRESS_CORRECTION_DETECT:-}" = "1" ]; then
35
+ exit 0
36
+ fi
19
37
 
20
38
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
39
  # shellcheck source=lib/session-marker.sh
@@ -20,9 +20,15 @@ setup() {
20
20
  REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
21
21
  HOOK="$REPO_ROOT/packages/itil/hooks/itil-correction-detect.sh"
22
22
  SID="itil-correction-test-$$-$RANDOM"
23
+ # An AFK iter exports WR_SUPPRESS_CORRECTION_DETECT=1, which the hook
24
+ # self-suppresses on. Unset it so the positive-detection cases below do not
25
+ # false-RED when this suite runs inside such an iter (the P391 hermeticity
26
+ # class). Suppression is asserted per-invocation via `run env ...` instead.
27
+ unset WR_SUPPRESS_CORRECTION_DETECT
23
28
  }
24
29
 
25
30
  teardown() {
31
+ unset WR_SUPPRESS_CORRECTION_DETECT
26
32
  rm -f "/tmp/itil-correction-detect-announced-${SID}"
27
33
  rm -f "/tmp/itil-correction-detect-announced-${SID}-alt"
28
34
  }
@@ -134,3 +140,50 @@ run_hook() {
134
140
  [ -z "$output" ]
135
141
  [ ! -f "/tmp/itil-correction-detect-announced-${SID}" ]
136
142
  }
143
+
144
+ # --- P430 / STORY-047: prompt-authorship guard --------------------------------
145
+ # A framework-authored iter prompt carrying an ordinary imperative ("DO NOT
146
+ # skip the gate") is not a correction — nobody is correcting anything, and the
147
+ # absent user cannot act on the nudge. Provenance is a property of the spawning
148
+ # process, so the dispatcher asserts it via env var rather than the hook
149
+ # guessing from content.
150
+
151
+ run_hook_env() {
152
+ local guard="$1" sid="$2" prompt="$3"
153
+ echo "{\"session_id\":\"$sid\",\"prompt\":$(printf '%s' "$prompt" | jq -Rs .)}" \
154
+ | env WR_SUPPRESS_CORRECTION_DETECT="$guard" bash "$HOOK"
155
+ }
156
+
157
+ @test "correction-detect: WR_SUPPRESS_CORRECTION_DETECT=1 suppresses a correction prompt" {
158
+ run run_hook_env 1 "$SID" "DO NOT skip the architect gate"
159
+ [ "$status" -eq 0 ]
160
+ [ -z "$output" ]
161
+ }
162
+
163
+ @test "correction-detect: suppressed path writes no announcement marker" {
164
+ run run_hook_env 1 "$SID" "FFS, you didn't run the test"
165
+ [ "$status" -eq 0 ]
166
+ [ -z "$output" ]
167
+ # The once-per-session budget must survive a prompt that produced no output,
168
+ # or the first real correction in the orchestrator session emits only the
169
+ # terse reminder.
170
+ [ ! -f "/tmp/itil-correction-detect-announced-${SID}" ]
171
+ }
172
+
173
+ @test "correction-detect: same prompt still fires with the guard unset" {
174
+ run run_hook "$SID" "DO NOT skip the architect gate"
175
+ [ "$status" -eq 0 ]
176
+ [ "${#output}" -gt 300 ]
177
+ [[ "$output" == *"MANDATORY"* ]]
178
+ }
179
+
180
+ @test "correction-detect: only the literal value 1 suppresses" {
181
+ for v in 0 true yes ""; do
182
+ run run_hook_env "$v" "$SID-$RANDOM" "FFS, that's wrong"
183
+ [ "$status" -eq 0 ]
184
+ [ -n "$output" ] || {
185
+ echo "guard value '$v' wrongly suppressed"
186
+ false
187
+ }
188
+ done
189
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.60.0",
3
+ "version": "0.61.0-preview.1033",
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"
@@ -582,6 +582,16 @@ export WR_SUPPRESS_PENDING_QUESTIONS=1
582
582
  # pending-questions guard above (JTBD-006 friction guard).
583
583
  export WR_SUPPRESS_OVERSIGHT_NUDGE=1
584
584
 
585
+ # AFK-iter correction-signal suppression (P430 / JTBD-006): the iteration prompt
586
+ # this loop writes is machine-authored, so an ordinary imperative in it ("DO NOT
587
+ # skip the gate") is not a user correcting anything, and no user is present to
588
+ # act on the capture nudge. itil-correction-detect.sh self-suppresses when this
589
+ # env var is set. A real correction is typed into the ORCHESTRATOR session,
590
+ # where this is unset, so P078 is preserved. Distinct guard class from the
591
+ # oversight nudge above — that one means "cannot ratify", this one means
592
+ # "nobody authored a correction".
593
+ export WR_SUPPRESS_CORRECTION_DETECT=1
594
+
585
595
  # Project-scoped governance plugins are NOT loaded by headless `claude -p`
586
596
  # (P382): it activates only USER-scoped enabledPlugins, and project activation
587
597
  # is trust-gated (headless skips trust), so `--setting-sources user,project`