@windyroad/itil 0.61.1 → 0.61.2

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.
Files changed (171) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/package.json +7 -2
  3. package/agents/test/fixtures/proceed-new-genuinely-new.md +0 -43
  4. package/agents/test/fixtures/proceed-new-subtle-sibling.md +0 -47
  5. package/agents/test/fixtures/regression-p347-vs-p346.md +0 -54
  6. package/agents/test/hang-off-check.bats +0 -225
  7. package/hooks/test/block-list.bats +0 -153
  8. package/hooks/test/command-detect.bats +0 -181
  9. package/hooks/test/itil-assistant-output-gate.bats +0 -114
  10. package/hooks/test/itil-assistant-output-review.bats +0 -224
  11. package/hooks/test/itil-bash-polling-antipattern-detect.bats +0 -154
  12. package/hooks/test/itil-changeset-discipline.bats +0 -603
  13. package/hooks/test/itil-claude-space-protection.bats +0 -294
  14. package/hooks/test/itil-commit-trailer-transition-advisory.bats +0 -80
  15. package/hooks/test/itil-correction-detect.bats +0 -189
  16. package/hooks/test/itil-deferral-cadence-gate.bats +0 -187
  17. package/hooks/test/itil-fictional-defer-detect.bats +0 -292
  18. package/hooks/test/itil-fix-title-lifecycle-advisory.bats +0 -129
  19. package/hooks/test/itil-mid-loop-ask-detect.bats +0 -220
  20. package/hooks/test/itil-no-implement-draft-gate.bats +0 -210
  21. package/hooks/test/itil-pending-questions-surface.bats +0 -307
  22. package/hooks/test/itil-readme-refresh-discipline.bats +0 -642
  23. package/hooks/test/itil-rfc-oversight-nudge.bats +0 -83
  24. package/hooks/test/itil-rfc-trailer-advisory.bats +0 -375
  25. package/hooks/test/itil-story-mirror-migration-nudge.bats +0 -122
  26. package/hooks/test/manage-problem-enforce-create.bats +0 -424
  27. package/hooks/test/p057-staging-trap-detect.bats +0 -231
  28. package/hooks/test/pre-publish-intake-gate.bats +0 -144
  29. package/hooks/test/runtime-sid-marker.bats +0 -90
  30. package/hooks/test/session-id.bats +0 -370
  31. package/scripts/migrate-problems-add-type.sh +0 -116
  32. package/scripts/test/catchup-scan.bats +0 -274
  33. package/scripts/test/check-afk-accept-eligible.bats +0 -310
  34. package/scripts/test/check-fail-soft-skip-discipline.bats +0 -172
  35. package/scripts/test/check-fix-rfc-trace.bats +0 -164
  36. package/scripts/test/check-locale-discipline.bats +0 -291
  37. package/scripts/test/check-problems-readme-budget.bats +0 -192
  38. package/scripts/test/check-rfc-has-stories.bats +0 -69
  39. package/scripts/test/check-rfc-rejected-alternatives.bats +0 -108
  40. package/scripts/test/check-rfc-stories-ratified.bats +0 -82
  41. package/scripts/test/check-upstream-responses.bats +0 -503
  42. package/scripts/test/classify-readme-drift.bats +0 -392
  43. package/scripts/test/derive-first-dispatch.bats +0 -274
  44. package/scripts/test/derive-release-vehicle.bats +0 -454
  45. package/scripts/test/detect-unratified-stories-maps.bats +0 -60
  46. package/scripts/test/dual-tolerant-glob-rfc-002-t2.bats +0 -309
  47. package/scripts/test/effort-tally.bats +0 -170
  48. package/scripts/test/evaluate-relevance.bats +0 -773
  49. package/scripts/test/kind-a-commands.bats +0 -46
  50. package/scripts/test/mark-create-gate.bats +0 -59
  51. package/scripts/test/mark-story-oversight-confirmed.bats +0 -53
  52. package/scripts/test/migrate-problems-add-type.bats +0 -280
  53. package/scripts/test/migrate-story-status-mirror.bats +0 -167
  54. package/scripts/test/no-type-regression-guard.bats +0 -74
  55. package/scripts/test/oversight-mirror-corpus-lint.bats +0 -150
  56. package/scripts/test/plugin-exercise-index.bats +0 -386
  57. package/scripts/test/plugin-maturity-doc-lint.bats +0 -470
  58. package/scripts/test/plugin-maturity-populate.bats +0 -631
  59. package/scripts/test/plugin-maturity-render.bats +0 -469
  60. package/scripts/test/plugin-validate-ci-gate.bats +0 -208
  61. package/scripts/test/reconcile-readme.bats +0 -1007
  62. package/scripts/test/reconcile-rfcs.bats +0 -496
  63. package/scripts/test/reconcile-stories.bats +0 -173
  64. package/scripts/test/reconcile-story-maps.bats +0 -74
  65. package/scripts/test/release-watch-poll-loop.bats +0 -180
  66. package/scripts/test/resolve-governance-plugin-dirs.bats +0 -78
  67. package/scripts/test/rfc-stories-extension.bats +0 -175
  68. package/scripts/test/skill-invocations.bats +0 -535
  69. package/scripts/test/skill-md-dual-tolerant-coverage-rfc-002-t3.bats +0 -374
  70. package/scripts/test/story-oversight-lib.bats +0 -190
  71. package/scripts/test/update-jtbd-references-related-problems.bats +0 -178
  72. package/scripts/test/update-problem-references-section.bats +0 -242
  73. package/scripts/test/update-problem-rfcs-section.bats +0 -242
  74. package/scripts/test/update-references-section-sibling-helpers.bats +0 -80
  75. package/scripts/test/update-rfc-commits-section.bats +0 -77
  76. package/scripts/test/verify-iter-summary.bats +0 -196
  77. package/scripts/test/working-the-problem-traversal.bats +0 -116
  78. package/skills/capture-problem/test/capture-problem-step-1-5b-jtbd-trace.bats +0 -398
  79. package/skills/capture-problem/test/capture-problem.bats +0 -389
  80. package/skills/capture-story/test/capture-story-behavioural.bats +0 -317
  81. package/skills/capture-story-map/test/capture-story-map-behavioural.bats +0 -98
  82. package/skills/close-incident/test/close-incident-contract.bats +0 -146
  83. package/skills/link-incident/test/link-incident-contract.bats +0 -125
  84. package/skills/list-incidents/test/list-incidents-contract.bats +0 -115
  85. package/skills/list-problems/test/list-problems-contract.bats +0 -100
  86. package/skills/list-stories/test/list-stories-contract.bats +0 -127
  87. package/skills/list-story-maps/test/list-story-maps-contract.bats +0 -46
  88. package/skills/manage-incident/test/manage-incident-adr-044-contract.bats +0 -206
  89. package/skills/manage-incident/test/manage-incident-close-forwarder.bats +0 -67
  90. package/skills/manage-incident/test/manage-incident-link-forwarder.bats +0 -67
  91. package/skills/manage-incident/test/manage-incident-list-forwarder.bats +0 -68
  92. package/skills/manage-incident/test/manage-incident-mitigate-forwarder.bats +0 -66
  93. package/skills/manage-incident/test/manage-incident-restore-forwarder.bats +0 -66
  94. package/skills/manage-incident/test/manage-incident.bats +0 -171
  95. package/skills/manage-problem/test/manage-problem-adr-044-step4-derive-first.bats +0 -151
  96. package/skills/manage-problem/test/manage-problem-auto-migrate-step.bats +0 -53
  97. package/skills/manage-problem/test/manage-problem-concern-boundary.bats +0 -64
  98. package/skills/manage-problem/test/manage-problem-effort-buckets.bats +0 -56
  99. package/skills/manage-problem/test/manage-problem-external-root-cause-detection.bats +0 -119
  100. package/skills/manage-problem/test/manage-problem-first-run-intake-prompt.bats +0 -43
  101. package/skills/manage-problem/test/manage-problem-git-mv-restage-reminder.bats +0 -48
  102. package/skills/manage-problem/test/manage-problem-id-collision-guard.bats +0 -39
  103. package/skills/manage-problem/test/manage-problem-list-forwarder.bats +0 -66
  104. package/skills/manage-problem/test/manage-problem-next-id-origin-lookup.bats +0 -50
  105. package/skills/manage-problem/test/manage-problem-no-prose-options.bats +0 -74
  106. package/skills/manage-problem/test/manage-problem-output-formatting.bats +0 -30
  107. package/skills/manage-problem/test/manage-problem-parked-and-cache.bats +0 -73
  108. package/skills/manage-problem/test/manage-problem-readme-refresh-on-transition.bats +0 -62
  109. package/skills/manage-problem/test/manage-problem-readme-tie-break-order.bats +0 -318
  110. package/skills/manage-problem/test/manage-problem-readme-vq-sort-order.bats +0 -206
  111. package/skills/manage-problem/test/manage-problem-release-vehicle-seed.bats +0 -83
  112. package/skills/manage-problem/test/manage-problem-review-forwarder.bats +0 -71
  113. package/skills/manage-problem/test/manage-problem-step-9d-recovery-path.bats +0 -97
  114. package/skills/manage-problem/test/manage-problem-transition-forwarder.bats +0 -96
  115. package/skills/manage-problem/test/manage-problem-transitive-dependencies.bats +0 -365
  116. package/skills/manage-problem/test/manage-problem-verification-detection.bats +0 -64
  117. package/skills/manage-problem/test/manage-problem-verification-pending.bats +0 -114
  118. package/skills/manage-problem/test/manage-problem-work-forwarder.bats +0 -88
  119. package/skills/manage-story/test/manage-story-contract.bats +0 -174
  120. package/skills/manage-story-map/test/manage-story-map-contract.bats +0 -63
  121. package/skills/mitigate-incident/test/mitigate-incident-contract.bats +0 -258
  122. package/skills/reconcile-readme/test/reconcile-readme-contract.bats +0 -201
  123. package/skills/report-upstream/test/report-upstream-contract.bats +0 -394
  124. package/skills/restore-incident/test/restore-incident-contract.bats +0 -170
  125. package/skills/review-problems/test/inbound-channels-cache-shape.bats +0 -127
  126. package/skills/review-problems/test/inbound-discovery-contract.bats +0 -383
  127. package/skills/review-problems/test/jtbd-301-verdict-shape-contract.bats +0 -225
  128. package/skills/review-problems/test/review-problems-contract.bats +0 -235
  129. package/skills/review-problems/test/review-problems-likely-verified-cell-shape.bats +0 -229
  130. package/skills/scaffold-intake/test/scaffold-intake-contract.bats +0 -126
  131. package/skills/scaffold-intake/test/scaffold-intake-fixture.bats +0 -164
  132. package/skills/scaffold-intake/test/scaffold-intake-secrets-absent.bats +0 -87
  133. package/skills/transition-problem/test/transition-problem-contract.bats +0 -285
  134. package/skills/transition-problems/test/transition-problems-contract.bats +0 -331
  135. package/skills/update-upstream/test/update-upstream-contract.bats +0 -375
  136. package/skills/work-problem/test/work-problem-contract.bats +0 -294
  137. package/skills/work-problems/test/work-problems-above-appetite-remediation.bats +0 -139
  138. package/skills/work-problems/test/work-problems-adr-013-rule-6-p352-amendment.bats +0 -166
  139. package/skills/work-problems/test/work-problems-auto-migrate-step.bats +0 -57
  140. package/skills/work-problems/test/work-problems-cost-logging.bats +0 -109
  141. package/skills/work-problems/test/work-problems-deviation-candidate-shape.bats +0 -121
  142. package/skills/work-problems/test/work-problems-first-run-intake-prompt.bats +0 -36
  143. package/skills/work-problems/test/work-problems-inter-iteration-verify.bats +0 -59
  144. package/skills/work-problems/test/work-problems-mid-loop-userpromptsubmit-handler.bats +0 -69
  145. package/skills/work-problems/test/work-problems-no-mid-loop-asking.bats +0 -192
  146. package/skills/work-problems/test/work-problems-p341-pre-all-done-gate.bats +0 -122
  147. package/skills/work-problems/test/work-problems-p342-r007-derive-then-pass-flags.bats +0 -170
  148. package/skills/work-problems/test/work-problems-p342-retro-auto-ticket-carveout.bats +0 -120
  149. package/skills/work-problems/test/work-problems-preflight-failure-handling.bats +0 -248
  150. package/skills/work-problems/test/work-problems-preflight-session-continuity.bats +0 -139
  151. package/skills/work-problems/test/work-problems-preflight.bats +0 -69
  152. package/skills/work-problems/test/work-problems-release-cadence.bats +0 -63
  153. package/skills/work-problems/test/work-problems-step-0-iter-error-staleness.bats +0 -93
  154. package/skills/work-problems/test/work-problems-step-0b-cache-staleness-behavioural.bats +0 -138
  155. package/skills/work-problems/test/work-problems-step-0c-deferred-placeholder-staleness-behavioural.bats +0 -238
  156. package/skills/work-problems/test/work-problems-step-0d-outbound-responses-staleness-behavioural.bats +0 -174
  157. package/skills/work-problems/test/work-problems-step-2-5-routing.bats +0 -80
  158. package/skills/work-problems/test/work-problems-step-2-5b-cross-halt-routing.bats +0 -149
  159. package/skills/work-problems/test/work-problems-step-3-5-jtbd-ratification-predicate.bats +0 -191
  160. package/skills/work-problems/test/work-problems-step-5-bats-polling-discipline.bats +0 -91
  161. package/skills/work-problems/test/work-problems-step-5-delegation.bats +0 -333
  162. package/skills/work-problems/test/work-problems-step-5-idle-timeout-sigterm.bats +0 -421
  163. package/skills/work-problems/test/work-problems-step-5-is-error-transient-halt.bats +0 -278
  164. package/skills/work-problems/test/work-problems-step-5-iter-changeset-required.bats +0 -95
  165. package/skills/work-problems/test/work-problems-step-5-prompt-body-re-grounding.bats +0 -128
  166. package/skills/work-problems/test/work-problems-step-5-stream-timeout-salvage.bats +0 -251
  167. package/skills/work-problems/test/work-problems-step-6-5-always-drain.bats +0 -203
  168. package/skills/work-problems/test/work-problems-step-6-5-cache-refresh-chain.bats +0 -174
  169. package/skills/work-problems/test/work-problems-step-6-5-fix-and-continue.bats +0 -254
  170. package/skills/work-problems/test/work-problems-step-6-5-postrelease-kv-callback.bats +0 -209
  171. package/skills/work-problems/test/work-problems-stop-condition-questions.bats +0 -85
@@ -1,83 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # P378/RFC-030: itil-rfc-oversight-nudge.sh (SessionStart) emits a one-line
4
- # nudge when RFCs lack the human-oversight marker, is silent when none do, and
5
- # self-suppresses under the shared AFK guard (WR_SUPPRESS_OVERSIGHT_NUDGE=1).
6
- # Behavioural — exercises the hook against fixture docs/rfcs/ trees. Clone of
7
- # the architect/jtbd oversight-nudge contract (ADR-066/068).
8
-
9
- setup() {
10
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
11
- HOOK="$REPO_ROOT/packages/itil/hooks/itil-rfc-oversight-nudge.sh"
12
- PLUGIN_ROOT="$REPO_ROOT/packages/itil"
13
- DIR="$(mktemp -d)"
14
- mkdir -p "$DIR/docs/rfcs"
15
- # Hermeticity (P391): the AFK work-problems orchestrator exports
16
- # WR_SUPPRESS_OVERSIGHT_NUDGE=1, which the hook self-suppresses on. Unset it so
17
- # count-emitting tests assert real behaviour; guard-specific tests set it
18
- # per-invocation via `run env WR_SUPPRESS_OVERSIGHT_NUDGE=...`.
19
- unset WR_SUPPRESS_OVERSIGHT_NUDGE
20
- }
21
-
22
- teardown() { rm -rf "$DIR"; }
23
-
24
- mk_unconfirmed() {
25
- { echo "---"; echo "status: proposed"; echo "human-oversight: unconfirmed"; echo "---"; echo "# $1"; } \
26
- > "$DIR/docs/rfcs/$1"
27
- }
28
- mk_confirmed() {
29
- { echo "---"; echo "status: proposed"; echo "human-oversight: confirmed"; echo "---"; echo "# $1"; } \
30
- > "$DIR/docs/rfcs/$1"
31
- }
32
-
33
- run_hook() { run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" "$@" bash "$HOOK"; }
34
-
35
- @test "emits a count line when there are unoversighted RFCs" {
36
- mk_unconfirmed "RFC-201-foo.proposed.md"
37
- mk_unconfirmed "RFC-202-bar.proposed.md"
38
- run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
39
- [ "$status" -eq 0 ]
40
- [[ "$output" == *"2 RFCs lack human oversight"* ]]
41
- [[ "$output" == *"/wr-itil:manage-rfc"* ]]
42
- }
43
-
44
- @test "singular wording for exactly one unoversighted RFC" {
45
- mk_unconfirmed "RFC-201-foo.proposed.md"
46
- run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
47
- [[ "$output" == *"1 RFC lacks human oversight"* ]]
48
- }
49
-
50
- @test "silent when every RFC is confirmed" {
51
- mk_confirmed "RFC-201-foo.proposed.md"
52
- run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
53
- [ "$status" -eq 0 ]
54
- [ -z "$output" ]
55
- }
56
-
57
- @test "excludes superseded and closed RFCs from the count" {
58
- mk_unconfirmed "RFC-201-foo.superseded.md"
59
- mk_unconfirmed "RFC-202-bar.closed.md"
60
- run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
61
- [ "$status" -eq 0 ]
62
- [ -z "$output" ]
63
- }
64
-
65
- @test "shared AFK guard suppresses the nudge entirely" {
66
- mk_unconfirmed "RFC-201-foo.proposed.md"
67
- run env WR_SUPPRESS_OVERSIGHT_NUDGE=1 CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
68
- [ "$status" -eq 0 ]
69
- [ -z "$output" ]
70
- }
71
-
72
- @test "guard value other than 1 does not suppress" {
73
- mk_unconfirmed "RFC-201-foo.proposed.md"
74
- run env WR_SUPPRESS_OVERSIGHT_NUDGE=yes CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
75
- [[ "$output" == *"lack"*"human oversight"* ]] || [[ "$output" == *"lacks human oversight"* ]]
76
- }
77
-
78
- @test "silent when docs/rfcs does not exist (framework not adopted)" {
79
- rm -rf "$DIR/docs/rfcs"
80
- run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
81
- [ "$status" -eq 0 ]
82
- [ -z "$output" ]
83
- }
@@ -1,375 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # @problem P170 — Slice 3 second half (B5.T9): PostToolUse:Bash hook
4
- # detects `git commit` invocations whose HEAD commit message carries
5
- # a `Refs: RFC-<NNN>` trailer, and emits a stderr advisory when the
6
- # corresponding driving-problem ticket's `## RFCs` table is stale.
7
- #
8
- # Architect Q1 verdict: skill-side refresh primary; hook-side advisory
9
- # for arbitrary commits (e.g. feat/fix/chore commits with `Refs:` trailer
10
- # authored outside the RFC skills).
11
- # Architect Q2 verdict: PostToolUse:Bash; advisory-only; silent-on-pass
12
- # per ADR-045 Pattern 1; fail-open per ADR-013 Rule 6.
13
- # Architect Q4 verdict: parse via `git interpret-trailers`; multi-`Refs:`
14
- # trailers emit malformed-per-finding-8 advisory.
15
- #
16
- # Behavioural per ADR-052 + P081: assert on emitted stderr / exit code
17
- # in response to simulated PostToolUse:Bash payload — no structural
18
- # greps on hook source.
19
- #
20
- # @adr ADR-014 (single-commit grain — hook never auto-fixes via follow-up commit)
21
- # @adr ADR-013 Rule 6 (fail-open)
22
- # @adr ADR-045 (silent-on-pass; advisory band ≤300 bytes)
23
- # @adr ADR-051 (load-bearing-from-the-start)
24
- # @adr ADR-060 (Phase 1 item 12 + Confirmation criterion 3)
25
- # @jtbd JTBD-006 (advisory does not block AFK loop — exit 0; stderr only)
26
- # @jtbd JTBD-008 (reverse-trace surface JTBD-008 names — drift detection)
27
-
28
- setup() {
29
- SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
30
- HOOK="$SCRIPT_DIR/itil-rfc-trailer-advisory.sh"
31
- ORIG_DIR="$PWD"
32
- TEST_DIR=$(mktemp -d)
33
- cd "$TEST_DIR"
34
- git init --quiet -b main
35
- git config user.email "test@example.com"
36
- git config user.name "Test"
37
- mkdir -p docs/rfcs docs/problems
38
- echo "seed" > seed.txt
39
- git add seed.txt
40
- git -c commit.gpgsign=false commit --quiet -m "initial"
41
- unset BYPASS_RFC_TRAILER_ADVISORY
42
- }
43
-
44
- teardown() {
45
- cd "$ORIG_DIR"
46
- rm -rf "$TEST_DIR"
47
- unset BYPASS_RFC_TRAILER_ADVISORY
48
- }
49
-
50
- write_rfc() {
51
- local id="$1" slug="$2" status="$3"
52
- local problems="${4:-[P168]}"
53
- cat > "docs/rfcs/RFC-${id}-${slug}.${status}.md" <<EOF
54
- ---
55
- status: ${status}
56
- rfc-id: ${slug}
57
- reported: 2026-05-05
58
- decision-makers: [test]
59
- problems: ${problems}
60
- ---
61
-
62
- # RFC-${id}: ${slug}
63
-
64
- stub
65
- EOF
66
- }
67
-
68
- write_problem_with_rfcs_section() {
69
- local num="$1" rows="$2"
70
- local file="docs/problems/${num}-stub.open.md"
71
- cat > "$file" <<EOF
72
- # Problem ${num}: stub
73
-
74
- **Status**: Open
75
-
76
- ## Description
77
-
78
- stub
79
-
80
- ## Related
81
-
82
- stub
83
-
84
- ## RFCs
85
-
86
- | RFC | Status | Title |
87
- |-----|--------|-------|
88
- ${rows}
89
- EOF
90
- }
91
-
92
- write_problem_without_rfcs_section() {
93
- local num="$1"
94
- cat > "docs/problems/${num}-stub.open.md" <<EOF
95
- # Problem ${num}: stub
96
-
97
- **Status**: Open
98
-
99
- ## Description
100
-
101
- stub
102
-
103
- ## Related
104
-
105
- stub
106
- EOF
107
- }
108
-
109
- run_post_bash_hook() {
110
- local cmd="$1"
111
- local json
112
- json=$(printf '{"tool_name":"Bash","tool_input":{"command":"%s"}}' "$cmd")
113
- echo "$json" | bash "$HOOK"
114
- }
115
-
116
- # ── Existence + executable ──────────────────────────────────────────────────
117
-
118
- @test "hook exists" {
119
- [ -f "$HOOK" ]
120
- }
121
-
122
- @test "hook is executable" {
123
- [ -x "$HOOK" ]
124
- }
125
-
126
- # ── Silent paths ────────────────────────────────────────────────────────────
127
-
128
- @test "non-Bash tool → silent (exit 0; no output)" {
129
- json='{"tool_name":"Write","tool_input":{"file_path":"foo.txt","content":"x"}}'
130
- run bash -c "echo '$json' | bash '$HOOK'"
131
- [ "$status" -eq 0 ]
132
- [ -z "$output" ]
133
- }
134
-
135
- @test "non-commit Bash command → silent" {
136
- run run_post_bash_hook "git status"
137
- [ "$status" -eq 0 ]
138
- [ -z "$output" ]
139
- }
140
-
141
- @test "BYPASS_RFC_TRAILER_ADVISORY=1 → silent regardless of drift" {
142
- write_rfc "001" "foo" "accepted"
143
- write_problem_without_rfcs_section "168"
144
- echo "x" > stub.txt
145
- git add stub.txt
146
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
147
- BYPASS_RFC_TRAILER_ADVISORY=1 run run_post_bash_hook "git commit -m foo"
148
- [ "$status" -eq 0 ]
149
- [ -z "$output" ]
150
- }
151
-
152
- @test "outside git work tree → silent" {
153
- cd "$ORIG_DIR"
154
- TMP_NONGIT=$(mktemp -d)
155
- cd "$TMP_NONGIT"
156
- run run_post_bash_hook "git commit -m foo"
157
- [ "$status" -eq 0 ]
158
- [ -z "$output" ]
159
- cd "$ORIG_DIR"
160
- rm -rf "$TMP_NONGIT"
161
- }
162
-
163
- @test "no docs/rfcs/ → silent (project has not adopted RFC framework)" {
164
- rm -rf docs/rfcs
165
- echo "x" > stub.txt
166
- git add stub.txt
167
- git -c commit.gpgsign=false commit --quiet -m "feat: stub"
168
- run run_post_bash_hook "git commit -m foo"
169
- [ "$status" -eq 0 ]
170
- [ -z "$output" ]
171
- }
172
-
173
- @test "no docs/problems/ → silent (project has not adopted problem framework)" {
174
- rm -rf docs/problems
175
- echo "x" > stub.txt
176
- git add stub.txt
177
- git -c commit.gpgsign=false commit --quiet -m "feat: stub"
178
- run run_post_bash_hook "git commit -m foo"
179
- [ "$status" -eq 0 ]
180
- [ -z "$output" ]
181
- }
182
-
183
- @test "commit without Refs: RFC trailer → silent" {
184
- write_rfc "001" "foo" "accepted"
185
- write_problem_without_rfcs_section "168"
186
- echo "x" > stub.txt
187
- git add stub.txt
188
- git -c commit.gpgsign=false commit --quiet -m "feat: no trailer"
189
- run run_post_bash_hook "git commit -m foo"
190
- [ "$status" -eq 0 ]
191
- [ -z "$output" ]
192
- }
193
-
194
- # ── Advisory paths ──────────────────────────────────────────────────────────
195
-
196
- @test "Refs: RFC-NNN trailer + stale problem (no ## RFCs section) → stderr advisory" {
197
- write_rfc "001" "foo" "accepted"
198
- write_problem_without_rfcs_section "168"
199
- echo "x" > stub.txt
200
- git add stub.txt
201
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
202
- run run_post_bash_hook "git commit -m foo"
203
- [ "$status" -eq 0 ]
204
- [[ "$output" == *"RFC-001"* ]]
205
- [[ "$output" == *"P168"* ]]
206
- }
207
-
208
- @test "Refs: RFC-NNN trailer + stale problem (## RFCs section missing this RFC) → stderr advisory" {
209
- write_rfc "001" "foo" "accepted"
210
- write_problem_with_rfcs_section "168" "| RFC-002 | proposed | other |"
211
- echo "x" > stub.txt
212
- git add stub.txt
213
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
214
- run run_post_bash_hook "git commit -m foo"
215
- [ "$status" -eq 0 ]
216
- [[ "$output" == *"RFC-001"* ]]
217
- [[ "$output" == *"P168"* ]]
218
- }
219
-
220
- @test "Refs: RFC-NNN trailer + current problem ## RFCs (RFC listed) → silent" {
221
- write_rfc "001" "foo" "accepted"
222
- write_problem_with_rfcs_section "168" "| RFC-001 | accepted | foo |"
223
- echo "x" > stub.txt
224
- git add stub.txt
225
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
226
- run run_post_bash_hook "git commit -m foo"
227
- [ "$status" -eq 0 ]
228
- [ -z "$output" ]
229
- }
230
-
231
- # ── Multi-RFC malformed-per-finding-8 ──────────────────────────────────────
232
-
233
- @test "multiple Refs: RFC trailers → malformed advisory (architect Q4 / finding 8)" {
234
- write_rfc "001" "foo" "accepted"
235
- write_rfc "002" "bar" "accepted"
236
- write_problem_with_rfcs_section "168" "| RFC-001 | accepted | foo |"
237
- echo "x" > stub.txt
238
- git add stub.txt
239
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001
240
- Refs: RFC-002"
241
- run run_post_bash_hook "git commit -m foo"
242
- [ "$status" -eq 0 ]
243
- # The advisory names finding-8 / split / mis-scoped vocabulary.
244
- [[ "$output" == *"finding-8"* || "$output" == *"split"* || "$output" == *"mis-scoped"* ]]
245
- }
246
-
247
- # ── Trailer to non-existent RFC ──────────────────────────────────────────────
248
-
249
- @test "Refs: RFC trailer with no matching file → silent (RFC may be in flight elsewhere)" {
250
- write_problem_without_rfcs_section "168"
251
- echo "x" > stub.txt
252
- git add stub.txt
253
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-999"
254
- run run_post_bash_hook "git commit -m foo"
255
- [ "$status" -eq 0 ]
256
- # Fail-open: missing RFC files don't promote to advisory (could be capture-rfc invocation in flight).
257
- [ -z "$output" ]
258
- }
259
-
260
- # ── Advisory budget ─────────────────────────────────────────────────────────
261
-
262
- @test "advisory message stays within ADR-045 advisory band (≤300 bytes)" {
263
- write_rfc "001" "byte-budget-test-with-an-extra-long-slug-to-stress-row-width" "accepted"
264
- write_problem_without_rfcs_section "168"
265
- echo "x" > stub.txt
266
- git add stub.txt
267
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
268
- run run_post_bash_hook "git commit -m foo"
269
- [ "$status" -eq 0 ]
270
- [ -n "$output" ]
271
- # Permit per-line overhead; the advisory should remain readable.
272
- [ "${#output}" -le 600 ]
273
- }
274
-
275
- # ── P274 / P268 leading-executable regression cases ─────────────────────────
276
- #
277
- # The hook must fire on ACTUAL `git commit` invocations, NOT on Bash that
278
- # merely MENTIONS the phrase "git commit" in argument vectors or heredoc
279
- # bodies. Mirrors the P268 regression fixtures in command-detect.bats and
280
- # the P272 sibling fixtures in itil-changeset-discipline.bats.
281
- #
282
- # Setup constructs a drift state (RFC + stale problem) — the hook would
283
- # emit an advisory on any `git commit` after `Refs: RFC-001` lands in HEAD.
284
- # These tests confirm non-commit Bash that mentions "git commit" does NOT
285
- # emit the advisory.
286
-
287
- @test "P274 allow: grep with literal 'git commit' pattern in drift state → silent" {
288
- write_rfc "001" "foo" "accepted"
289
- write_problem_without_rfcs_section "168"
290
- echo "x" > stub.txt
291
- git add stub.txt
292
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
293
- run run_post_bash_hook "grep -r 'git commit' ."
294
- [ "$status" -eq 0 ]
295
- [ -z "$output" ]
296
- }
297
-
298
- @test "P274 allow: sed pattern containing 'git commit' in drift state → silent" {
299
- write_rfc "001" "foo" "accepted"
300
- write_problem_without_rfcs_section "168"
301
- echo "x" > stub.txt
302
- git add stub.txt
303
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
304
- run run_post_bash_hook "sed -n 's/git commit/X/p' stub.txt"
305
- [ "$status" -eq 0 ]
306
- [ -z "$output" ]
307
- }
308
-
309
- @test "P274 allow: echo with literal 'git commit' string in drift state → silent" {
310
- write_rfc "001" "foo" "accepted"
311
- write_problem_without_rfcs_section "168"
312
- echo "x" > stub.txt
313
- git add stub.txt
314
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
315
- run run_post_bash_hook "echo 'run git commit -m foo'"
316
- [ "$status" -eq 0 ]
317
- [ -z "$output" ]
318
- }
319
-
320
- @test "P274 allow: git log --grep with 'git commit' search term in drift state → silent" {
321
- write_rfc "001" "foo" "accepted"
322
- write_problem_without_rfcs_section "168"
323
- echo "x" > stub.txt
324
- git add stub.txt
325
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
326
- run run_post_bash_hook "git log --grep='git commit'"
327
- [ "$status" -eq 0 ]
328
- [ -z "$output" ]
329
- }
330
-
331
- @test "P274 allow: git commit-tree plumbing in drift state → silent (boundary)" {
332
- write_rfc "001" "foo" "accepted"
333
- write_problem_without_rfcs_section "168"
334
- echo "x" > stub.txt
335
- git add stub.txt
336
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
337
- run run_post_bash_hook "git commit-tree HEAD^{tree} -m 'msg'"
338
- [ "$status" -eq 0 ]
339
- [ -z "$output" ]
340
- }
341
-
342
- # ── P274 positive leading-executable cases still emit advisory ──────────────
343
-
344
- @test "P274 advisory: env-var-prefixed git commit in drift state still emits advisory" {
345
- write_rfc "001" "foo" "accepted"
346
- write_problem_without_rfcs_section "168"
347
- echo "x" > stub.txt
348
- git add stub.txt
349
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
350
- run run_post_bash_hook "GIT_AUTHOR_NAME=foo git commit -m foo"
351
- [ "$status" -eq 0 ]
352
- [[ "$output" == *"RFC-001"* ]]
353
- }
354
-
355
- @test "P274 advisory: cd-prefixed git commit in drift state still emits advisory" {
356
- write_rfc "001" "foo" "accepted"
357
- write_problem_without_rfcs_section "168"
358
- echo "x" > stub.txt
359
- git add stub.txt
360
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
361
- run run_post_bash_hook "cd . && git commit -m foo"
362
- [ "$status" -eq 0 ]
363
- [[ "$output" == *"RFC-001"* ]]
364
- }
365
-
366
- @test "P274 advisory: leading-whitespace git commit in drift state still emits advisory" {
367
- write_rfc "001" "foo" "accepted"
368
- write_problem_without_rfcs_section "168"
369
- echo "x" > stub.txt
370
- git add stub.txt
371
- git -c commit.gpgsign=false commit --quiet -m "feat: stub" -m "" -m "Refs: RFC-001"
372
- run run_post_bash_hook " git commit -m foo"
373
- [ "$status" -eq 0 ]
374
- [[ "$output" == *"RFC-001"* ]]
375
- }
@@ -1,122 +0,0 @@
1
- #!/usr/bin/env bats
2
- # Behavioural tests for itil-story-mirror-migration-nudge.sh — the P474 / RFC-059
3
- # self-firing cadence for the story `**Status**:` mirror migration.
4
- #
5
- # Why this exists: the migration is a PATH shim, so it does not surface in
6
- # autocomplete, and its only announcement was the release note. Release notes
7
- # cannot fire in an adopter's repo — prose counts as zero control paths — so
8
- # artefacts predating the fix would keep the defect indefinitely while newly
9
- # captured stories were immune. A maintenance action with no automatic trigger
10
- # does not happen.
11
- #
12
- # Three contract conditions this suite locks, per the JTBD-009 review:
13
- # 1. Nudge-only, never auto-run — JTBD-009's "I review the rewritten artefacts
14
- # before they ship" is a ratified outcome, so the hook must not migrate.
15
- # 2. Names the PATH shim, never a repo-relative `packages/...` path, which
16
- # would only resolve in the source monorepo (P151/P153/P219/P317).
17
- # 3. Silent when clean, so steady state costs nothing.
18
- #
19
- # @problem P474 @adr ADR-049 ADR-090 @story STORY-054
20
-
21
- setup() {
22
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
23
- HOOK="$REPO_ROOT/packages/itil/hooks/itil-story-mirror-migration-nudge.sh"
24
- TMPD="$(mktemp -d)"
25
- mkdir -p "$TMPD/docs/stories/draft" "$TMPD/docs/stories/accepted"
26
- # AFK iters export the shared oversight guard; unset so the positive cases do
27
- # not false-RED inside one (the P391 hermeticity class).
28
- unset WR_SUPPRESS_OVERSIGHT_NUDGE
29
- }
30
- teardown() { unset WR_SUPPRESS_OVERSIGHT_NUDGE; rm -rf "$TMPD"; }
31
-
32
- seed_with_mirror() {
33
- printf -- '---\nstatus: draft\n---\n\n# STORY-901: x\n\n**Status**: draft\n**Reported**: 2026-07-30\n' \
34
- > "$TMPD/docs/stories/draft/STORY-901-x.md"
35
- }
36
- seed_clean() {
37
- printf -- '---\nstatus: draft\n---\n\n# STORY-902: y\n\n**Reported**: 2026-07-30\n' \
38
- > "$TMPD/docs/stories/draft/STORY-902-y.md"
39
- }
40
-
41
- @test "fires when a story still carries the **Status** mirror" {
42
- seed_with_mirror
43
- run env CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
44
- [ "$status" -eq 0 ]
45
- [ -n "$output" ]
46
- [[ "$output" == *"[wr-itil]"* ]]
47
- }
48
-
49
- @test "names the PATH shim so it resolves in an adopter install" {
50
- seed_with_mirror
51
- run env CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
52
- [[ "$output" == *"wr-itil-migrate-story-status-mirror"* ]]
53
- }
54
-
55
- # The recurring adopter-portability class: a repo-relative path resolves only in
56
- # the source monorepo, so source-repo dogfooding hides the breakage.
57
- @test "never emits a repo-relative packages/ path" {
58
- seed_with_mirror
59
- run env CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
60
- [[ "$output" != *"packages/itil"* ]]
61
- [[ "$output" != *"scripts/migrate-story-status-mirror.sh"* ]]
62
- }
63
-
64
- @test "silent when no story carries the mirror" {
65
- seed_clean
66
- run env CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
67
- [ "$status" -eq 0 ]
68
- [ -z "$output" ]
69
- }
70
-
71
- @test "silent when there is no stories directory at all" {
72
- run env CLAUDE_PROJECT_DIR="$(mktemp -d)" bash "$HOOK"
73
- [ "$status" -eq 0 ]
74
- [ -z "$output" ]
75
- }
76
-
77
- @test "counts affected stories rather than reporting a bare flag" {
78
- seed_with_mirror
79
- printf -- '---\nstatus: draft\n---\n\n# STORY-903: z\n\n**Status**: draft\n' \
80
- > "$TMPD/docs/stories/accepted/STORY-903-z.md"
81
- run env CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
82
- [[ "$output" == *"2"* ]]
83
- }
84
-
85
- # Ratified JTBD-009 outcome: the developer reviews the rewrite before it ships.
86
- # A nudge that migrated on its own would breach that, so assert the file is
87
- # untouched — this is the condition that keeps the hook inside the job.
88
- @test "NEVER migrates — the story file is left exactly as found" {
89
- seed_with_mirror
90
- local before; before="$(cat "$TMPD/docs/stories/draft/STORY-901-x.md")"
91
- run env CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
92
- [ "$status" -eq 0 ]
93
- [ "$before" = "$(cat "$TMPD/docs/stories/draft/STORY-901-x.md")" ]
94
- grep -qE '^\*\*Status\*\*: draft' "$TMPD/docs/stories/draft/STORY-901-x.md"
95
- }
96
-
97
- @test "self-suppresses under the shared AFK oversight guard" {
98
- seed_with_mirror
99
- run env WR_SUPPRESS_OVERSIGHT_NUDGE=1 CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
100
- [ "$status" -eq 0 ]
101
- [ -z "$output" ]
102
- }
103
-
104
- @test "only the literal value 1 suppresses" {
105
- seed_with_mirror
106
- run env WR_SUPPRESS_OVERSIGHT_NUDGE=yes CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
107
- [ -n "$output" ]
108
- }
109
-
110
- # README documents the template and legitimately shows the field; scanning it
111
- # would make the nudge fire forever on a clean corpus.
112
- @test "ignores docs/stories/README.md" {
113
- printf -- '# Stories\n\n**Status**: <status>\n' > "$TMPD/docs/stories/README.md"
114
- run env CLAUDE_PROJECT_DIR="$TMPD" bash "$HOOK"
115
- [ "$status" -eq 0 ]
116
- [ -z "$output" ]
117
- }
118
-
119
- @test "registered on SessionStart startup in hooks.json" {
120
- run grep -c 'itil-story-mirror-migration-nudge.sh' "$REPO_ROOT/packages/itil/hooks/hooks.json"
121
- [ "$output" -ge 1 ]
122
- }