@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,187 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # P375 — itil-deferral-cadence-gate.sh PostToolUse:Write|Edit|MultiEdit
4
- # advisory hook. The ratified Option-C "authoring-time enforcement gate"
5
- # (core slice, advisory rollout per ADR-057 staged-rollout + the P375
6
- # architect review 2026-06-28).
7
- #
8
- # The gate fires at AUTHORING time on the NEWLY-authored text (Edit
9
- # new_string / Write content / MultiEdit concatenated new_strings —
10
- # diff-aware so descriptive prose already on disk never re-triggers) of
11
- # a SHIPPED authoring surface (SKILL.md, docs/decisions/*.md ADRs,
12
- # docs/rfcs/*.md RFCs, hook *.sh). When the new text introduces an
13
- # uncadenced-deferral phrasing (`deferred to <on-demand re-entry>`,
14
- # `pending review`, `re-rate at next`, `(deferred …`, `next review`,
15
- # `when ready`, `lands in Slice N`) WITHOUT a cadence annotation naming
16
- # a SELF-FIRING trigger within the +/-5 line window, it emits a stderr
17
- # advisory citing P375 + the remedy.
18
- #
19
- # THE LOAD-BEARING P375 REFINEMENT vs the P234 sibling
20
- # (itil-fictional-defer-detect.sh): a bare named on-demand skill
21
- # (/wr-foo:bar) or a bare ticket ID (Pnnn / RFC-nnn / ADR-nnn) does NOT
22
- # satisfy the cadence requirement — naming an on-demand re-entry point
23
- # and treating it as a cadence IS the exact conflation P375 captures
24
- # ("BUT NOTHING TRIGGERS THAT WORK!!!"). Only a self-firing-CLASS
25
- # citation satisfies: a hook *.sh, SessionStart, PreToolUse/PostToolUse,
26
- # .github/workflows/ (CI), cron, or a work-problems Step-0x / pre-flight
27
- # reference. The existing P234 hook WRONGLY accepts skills + ticket IDs;
28
- # this hook does not.
29
- #
30
- # Core-slice boundary (architect refinement 1, named loudly): the gate
31
- # checks the cadence annotation names a self-firing-CLASS trigger — it
32
- # does NOT validate that the named trigger actually exists / fires (the
33
- # transitive-reachability graph is a deferred later slice). docs/problems/
34
- # tickets are EXCLUDED (they descriptively narrate deferrals — highest
35
- # false-positive surface, already covered by the ADR-084 census).
36
- #
37
- # Advisory only — NEVER blocks (exit 0 always), per ADR-040/045
38
- # declarative-first + ADR-013 Rule 6 fail-open. Behavioural per ADR-052
39
- # (asserts emitted stderr, never source-greps the hook). Hermetic per
40
- # P391 (unsets inherited suppress vars in setup).
41
-
42
- setup() {
43
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
44
- HOOK="$REPO_ROOT/packages/itil/hooks/itil-deferral-cadence-gate.sh"
45
- # P391 hermeticity: AFK iters export suppress vars that would poison
46
- # the advisory-fires assertions. Strip them for the test process.
47
- unset WR_SUPPRESS_DEFERRAL_CADENCE_GATE
48
- unset WR_SUPPRESS_OVERSIGHT_NUDGE
49
- }
50
-
51
- # Emit a PostToolUse stdin payload for an Edit tool call.
52
- # $1 = file_path, $2 = new_string (the newly-authored text).
53
- emit_edit() {
54
- jq -n --arg p "$1" --arg n "$2" '{
55
- session_id: "deferral-cadence-test",
56
- tool_name: "Edit",
57
- tool_input: { file_path: $p, old_string: "X", new_string: $n },
58
- tool_response: { success: true }
59
- }'
60
- }
61
-
62
- # Emit a PostToolUse stdin payload for a Write tool call.
63
- emit_write() {
64
- jq -n --arg p "$1" --arg c "$2" '{
65
- session_id: "deferral-cadence-test",
66
- tool_name: "Write",
67
- tool_input: { file_path: $p, content: $c },
68
- tool_response: { success: true }
69
- }'
70
- }
71
-
72
- fires() { [[ "$output" == *"P375"* ]] || [[ "$stderr" == *"P375"* ]]; }
73
- silent() { [[ "$output" != *"P375"* ]] && [[ "$stderr" != *"P375"* ]]; }
74
-
75
- # --- Positive: uncadenced deferral authored → advisory fires ---
76
-
77
- @test "fire: SKILL.md deferral citing a bare on-demand skill (the P375 conflation) fires" {
78
- # This is the case the P234 sibling WRONGLY allows — a named on-demand
79
- # skill is NOT a self-firing cadence.
80
- run bash "$HOOK" <<<"$(emit_edit packages/foo/skills/bar/SKILL.md \
81
- 'Full scope deferred to /wr-itil:manage-rfc accepted transition.')"
82
- [ "$status" -eq 0 ]
83
- fires
84
- }
85
-
86
- @test "fire: ADR new_string deferral with no citation at all fires" {
87
- run bash "$HOOK" <<<"$(emit_edit docs/decisions/099-thing.proposed.md \
88
- 'Considered Options and Consequences deferred — flesh out later.')"
89
- [ "$status" -eq 0 ]
90
- fires
91
- }
92
-
93
- @test "fire: RFC deferral citing only a bare ticket ID (not a cadence) fires" {
94
- # A ticket ID is an audit annotation, not a self-firing trigger.
95
- run bash "$HOOK" <<<"$(emit_edit docs/rfcs/RFC-099-thing.proposed.md \
96
- 'Scope deferred to next review per P375.')"
97
- [ "$status" -eq 0 ]
98
- fires
99
- }
100
-
101
- @test "fire: Write of a hook .sh whose content carries an uncadenced deferral fires" {
102
- run bash "$HOOK" <<<"$(emit_write packages/foo/hooks/foo.sh \
103
- '# TODO pending review when ready; not wired yet.')"
104
- [ "$status" -eq 0 ]
105
- fires
106
- }
107
-
108
- @test "fire: deferral with a cadence comment that names an ON-DEMAND skill still fires" {
109
- # The annotation carrier is fine but its value must name a self-firing
110
- # CLASS — naming an on-demand skill inside the comment does not help.
111
- run bash "$HOOK" <<<"$(emit_edit packages/foo/skills/bar/SKILL.md \
112
- 'Tasks deferred. <!-- cadence: /wr-itil:manage-rfc accepted -->')"
113
- [ "$status" -eq 0 ]
114
- fires
115
- }
116
-
117
- # --- Negative: cadenced deferral or out-of-scope → silent ---
118
-
119
- @test "allow: deferral citing a self-firing hook .sh in window exits silent" {
120
- run bash "$HOOK" <<<"$(emit_edit packages/foo/skills/bar/SKILL.md \
121
- 'Re-rate deferred; surfaced every session by retrospective-deferral-census.sh.')"
122
- [ "$status" -eq 0 ]
123
- silent
124
- }
125
-
126
- @test "allow: deferral with an explicit cadence annotation naming PostToolUse exits silent" {
127
- run bash "$HOOK" <<<"$(emit_edit docs/decisions/099-thing.proposed.md \
128
- 'Section deferred. <!-- cadence: PostToolUse itil-foo-gate.sh fires on author -->')"
129
- [ "$status" -eq 0 ]
130
- silent
131
- }
132
-
133
- @test "allow: deferral citing SessionStart self-firing surface exits silent" {
134
- run bash "$HOOK" <<<"$(emit_edit docs/rfcs/RFC-099-thing.proposed.md \
135
- 'Drain deferred; the SessionStart nudge re-surfaces it every session.')"
136
- [ "$status" -eq 0 ]
137
- silent
138
- }
139
-
140
- @test "allow: deferral citing a CI workflow exits silent" {
141
- run bash "$HOOK" <<<"$(emit_edit packages/foo/skills/bar/SKILL.md \
142
- 'Validation deferred to .github/workflows/check.yml on every push.')"
143
- [ "$status" -eq 0 ]
144
- silent
145
- }
146
-
147
- @test "allow: deferral in a docs/problems/ ticket (excluded surface) exits silent" {
148
- run bash "$HOOK" <<<"$(emit_edit docs/problems/open/099-thing.open.md \
149
- 'Severity deferred to investigation; expand at next review.')"
150
- [ "$status" -eq 0 ]
151
- silent
152
- }
153
-
154
- @test "allow: deferral phrasing in a non-authoring file (.ts) exits silent" {
155
- run bash "$HOOK" <<<"$(emit_edit packages/foo/src/index.ts \
156
- '// deferred to next review — TODO')"
157
- [ "$status" -eq 0 ]
158
- silent
159
- }
160
-
161
- @test "allow: authoring-surface edit with NO deferral phrasing exits silent" {
162
- run bash "$HOOK" <<<"$(emit_edit packages/foo/skills/bar/SKILL.md \
163
- 'This step validates the marker and proceeds to Step 2.')"
164
- [ "$status" -eq 0 ]
165
- silent
166
- }
167
-
168
- # --- Robustness ---
169
-
170
- @test "fail-open: empty stdin exits 0 silent" {
171
- run bash "$HOOK" <<<""
172
- [ "$status" -eq 0 ]
173
- silent
174
- }
175
-
176
- @test "fail-open: malformed JSON stdin exits 0 silent" {
177
- run bash "$HOOK" <<<"{not json"
178
- [ "$status" -eq 0 ]
179
- silent
180
- }
181
-
182
- @test "suppress: WR_SUPPRESS_DEFERRAL_CADENCE_GATE=1 silences the advisory" {
183
- run env WR_SUPPRESS_DEFERRAL_CADENCE_GATE=1 bash "$HOOK" <<<"$(emit_edit \
184
- packages/foo/skills/bar/SKILL.md 'deferred to /wr-itil:manage-rfc accepted')"
185
- [ "$status" -eq 0 ]
186
- silent
187
- }
@@ -1,292 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # P234 Phase 1: itil-fictional-defer-detect.sh PostToolUse:Write|Edit
4
- # hook detects "fictional defer" rationales in `docs/retros/*.md` writes
5
- # — defer-rationale phrases (`next retro`, `next session`, `defer
6
- # pending`, `defer with cause:`, `deferred per`) that lack a
7
- # SCHEDULED-FUTURE-SURFACE citation in surrounding context.
8
- #
9
- # Detection signal (per ticket Investigation Task 2 two-axis test):
10
- # 1. tool_name is Write OR Edit AND file_path matches docs/retros/*.md
11
- # 2. Written file contains a defer-rationale phrase (case-insensitive)
12
- # 3. Within +/-5 lines of the match there is NO citation of a
13
- # SCHEDULED-FUTURE-SURFACE (ticket ID P\d{3} / STORY-\d{3} / R\d{3},
14
- # skill invocation /wr-[a-z-]+:[a-z-]+, hook script path .sh, CI
15
- # workflow path .github/workflows/, dated ADR ADR-\d{3} ... YYYY-MM-DD)
16
- # 4. Match is NOT on the exception allowlist (e.g. `deferred per Branch B`).
17
- #
18
- # When all four hold, the hook emits a stderr advisory citing P234 + the
19
- # SCHEDULED-FUTURE-SURFACE definition. Advisory only — never blocks
20
- # (exit 0 always). Mirrors the itil-rfc-trailer-advisory.sh PostToolUse
21
- # precedent (stderr + exit 0) and the itil-mid-loop-ask-detect.sh
22
- # detection-pattern precedent (per-surface configuration at top).
23
- #
24
- # Per ADR-005 / ADR-052 — bats live under packages/<plugin>/hooks/test/
25
- # and assert on emitted stderr text, not source-content. Per P081 — no
26
- # source-grep on hook text.
27
-
28
- setup() {
29
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
30
- HOOK="$REPO_ROOT/packages/itil/hooks/itil-fictional-defer-detect.sh"
31
- TMPDIR_="$(mktemp -d)"
32
- RETRO_DIR="$TMPDIR_/docs/retros"
33
- mkdir -p "$RETRO_DIR"
34
- RETRO_FILE="$RETRO_DIR/2026-05-17-session-3.md"
35
- }
36
-
37
- teardown() {
38
- rm -rf "$TMPDIR_"
39
- }
40
-
41
- # Helper: emit PostToolUse stdin payload for a Write tool call.
42
- emit_write_payload() {
43
- local file_path="$1"
44
- jq -n --arg p "$file_path" '{
45
- session_id: "fictional-defer-test",
46
- tool_name: "Write",
47
- tool_input: { file_path: $p, content: "(content already on disk)" },
48
- tool_response: { success: true }
49
- }'
50
- }
51
-
52
- # Helper: emit PostToolUse stdin payload for an Edit tool call.
53
- emit_edit_payload() {
54
- local file_path="$1"
55
- jq -n --arg p "$file_path" '{
56
- session_id: "fictional-defer-test",
57
- tool_name: "Edit",
58
- tool_input: { file_path: $p, old_string: "x", new_string: "y" },
59
- tool_response: { success: true }
60
- }'
61
- }
62
-
63
- run_hook_with_write() {
64
- emit_write_payload "$RETRO_FILE" | bash "$HOOK"
65
- }
66
-
67
- run_hook_with_edit() {
68
- emit_edit_payload "$RETRO_FILE" | bash "$HOOK"
69
- }
70
-
71
- # --- Positive detection: fictional defer ---
72
-
73
- @test "detect: defer-to-next-retro with no scheduled-future-surface emits advisory" {
74
- # Faithful reproduction of the P234 worked-example fictional-defer
75
- # class — the defer-rationale prose carries no SCHEDULED-FUTURE-
76
- # SURFACE citation; no ticket ID, no skill invocation, no dated ADR
77
- # appears in the +/-5 line window around the defer phrase.
78
- cat > "$RETRO_FILE" <<'EOF'
79
- # Session 3 Retro
80
-
81
- ## Signal-vs-Noise Pass
82
-
83
- Deferred this retro per session-length constraint (16+ briefing
84
- entries across 13 topic files would require ~30 min of per-entry
85
- scoring). Next retro should run a full pass.
86
- EOF
87
- run run_hook_with_write
88
- [ "$status" -eq 0 ]
89
- [[ "$stderr" == *"P234"* ]] || [[ "$output" == *"P234"* ]]
90
- }
91
-
92
- @test "detect: deferred-pending-design-judgement with no scheduled-future-surface emits advisory" {
93
- cat > "$RETRO_FILE" <<'EOF'
94
- # Session 4 Retro
95
-
96
- ## Topic File Rotation Candidates
97
-
98
- | File | Action |
99
- |------|--------|
100
- | governance-workflow.md | deferred pending design judgement (cascade case) |
101
- | hooks-and-gates.md | deferred pending complexity review |
102
- EOF
103
- run run_hook_with_write
104
- [ "$status" -eq 0 ]
105
- [[ "$stderr" == *"P234"* ]] || [[ "$output" == *"P234"* ]]
106
- }
107
-
108
- @test "detect: defer-with-cause-context-budget with no surface emits advisory" {
109
- cat > "$RETRO_FILE" <<'EOF'
110
- # Session 5 Retro
111
-
112
- ## Codification Candidates
113
-
114
- Deferred with cause: context budget pressure. Next session should
115
- revisit when fresh context is available.
116
- EOF
117
- run run_hook_with_edit
118
- [ "$status" -eq 0 ]
119
- [[ "$stderr" == *"P234"* ]] || [[ "$output" == *"P234"* ]]
120
- }
121
-
122
- # --- Negative paths: legitimate citations (silent exit) ---
123
-
124
- @test "allow: defer citing P-ticket within +/-5 lines exits silent" {
125
- cat > "$RETRO_FILE" <<'EOF'
126
- # Session 3 Retro
127
-
128
- ## Signal-vs-Noise Pass
129
-
130
- Deferred per [[P235]] (briefing SVN backlog: 146 entries across 17
131
- topic files). Next retro will surface P235 if it has been promoted
132
- to actionable.
133
- EOF
134
- run run_hook_with_write
135
- [ "$status" -eq 0 ]
136
- [[ "$stderr" != *"P234"* ]]
137
- [[ "$output" != *"P234"* ]]
138
- }
139
-
140
- @test "allow: defer citing skill invocation within +/-5 lines exits silent" {
141
- cat > "$RETRO_FILE" <<'EOF'
142
- # Session 4 Retro
143
-
144
- ## Tickets Deferred
145
-
146
- Deferred pending /wr-itil:work-problems Step 6.5 above-appetite
147
- release-loop check.
148
- EOF
149
- run run_hook_with_edit
150
- [ "$status" -eq 0 ]
151
- [[ "$stderr" != *"P234"* ]]
152
- [[ "$output" != *"P234"* ]]
153
- }
154
-
155
- @test "allow: defer citing hook script path within +/-5 lines exits silent" {
156
- cat > "$RETRO_FILE" <<'EOF'
157
- # Session 5 Retro
158
-
159
- ## Codification Candidates
160
-
161
- Defer pending packages/itil/hooks/itil-fictional-defer-detect.sh
162
- extension to also cover the assistant-output review channel.
163
- EOF
164
- run run_hook_with_write
165
- [ "$status" -eq 0 ]
166
- [[ "$stderr" != *"P234"* ]]
167
- [[ "$output" != *"P234"* ]]
168
- }
169
-
170
- @test "allow: defer citing dated ADR within +/-5 lines exits silent" {
171
- cat > "$RETRO_FILE" <<'EOF'
172
- # Session 6 Retro
173
-
174
- ## Codification Candidates
175
-
176
- Deferred pending ADR-044 confirmation criterion 3 graduation
177
- (2026-05-25). Reassess after the criterion lands.
178
- EOF
179
- run run_hook_with_write
180
- [ "$status" -eq 0 ]
181
- [[ "$stderr" != *"P234"* ]]
182
- [[ "$output" != *"P234"* ]]
183
- }
184
-
185
- # --- Exception allowlist ---
186
-
187
- @test "allow: deferred-per-Branch-B allowlist phrase exits silent" {
188
- cat > "$RETRO_FILE" <<'EOF'
189
- # Session 3 Retro
190
-
191
- ## Topic File Rotation Candidates
192
-
193
- | File | Action |
194
- |------|--------|
195
- | governance-workflow.md (ratio 1.5x) | leave-as-is — deferred per Branch B |
196
- | hooks-and-gates.md (ratio 1.3x) | leave-as-is — deferred per Branch B |
197
- EOF
198
- run run_hook_with_write
199
- [ "$status" -eq 0 ]
200
- [[ "$stderr" != *"P234"* ]]
201
- [[ "$output" != *"P234"* ]]
202
- }
203
-
204
- # --- Path / tool short-circuits ---
205
-
206
- @test "allow: tool_name != Write/Edit exits silent" {
207
- cat > "$RETRO_FILE" <<'EOF'
208
- Deferred this retro per session-length constraint. Next retro should run.
209
- EOF
210
- payload=$(jq -n --arg p "$RETRO_FILE" '{
211
- session_id: "x",
212
- tool_name: "Bash",
213
- tool_input: { command: "ls" },
214
- tool_response: { stdout: "" }
215
- }')
216
- run bash -c "echo '$payload' | bash '$HOOK'"
217
- [ "$status" -eq 0 ]
218
- [[ "$stderr" != *"P234"* ]]
219
- [[ "$output" != *"P234"* ]]
220
- }
221
-
222
- @test "allow: file_path outside docs/retros/ exits silent" {
223
- OTHER="$TMPDIR_/docs/problems/foo.md"
224
- mkdir -p "$(dirname "$OTHER")"
225
- cat > "$OTHER" <<'EOF'
226
- Deferred this retro per session-length constraint. Next retro should run a full pass.
227
- EOF
228
- payload=$(jq -n --arg p "$OTHER" '{
229
- session_id: "x",
230
- tool_name: "Write",
231
- tool_input: { file_path: $p, content: "" },
232
- tool_response: { success: true }
233
- }')
234
- run bash -c "echo '$payload' | bash '$HOOK'"
235
- [ "$status" -eq 0 ]
236
- [[ "$stderr" != *"P234"* ]]
237
- [[ "$output" != *"P234"* ]]
238
- }
239
-
240
- @test "allow: missing file_path exits silent" {
241
- payload=$(jq -n '{
242
- session_id: "x",
243
- tool_name: "Write",
244
- tool_input: {},
245
- tool_response: { success: true }
246
- }')
247
- run bash -c "echo '$payload' | bash '$HOOK'"
248
- [ "$status" -eq 0 ]
249
- [[ "$stderr" != *"P234"* ]]
250
- [[ "$output" != *"P234"* ]]
251
- }
252
-
253
- # --- Crash safety ---
254
-
255
- @test "allow: malformed JSON input does not crash the hook" {
256
- run bash -c "echo 'not-json' | bash '$HOOK'"
257
- [ "$status" -eq 0 ]
258
- # Either silent OR a single advisory — but never a non-zero exit.
259
- }
260
-
261
- @test "allow: non-existent retro file exits silent" {
262
- GHOST="$TMPDIR_/docs/retros/does-not-exist.md"
263
- payload=$(jq -n --arg p "$GHOST" '{
264
- session_id: "x",
265
- tool_name: "Write",
266
- tool_input: { file_path: $p, content: "" },
267
- tool_response: { success: true }
268
- }')
269
- run bash -c "echo '$payload' | bash '$HOOK'"
270
- [ "$status" -eq 0 ]
271
- [[ "$stderr" != *"P234"* ]]
272
- [[ "$output" != *"P234"* ]]
273
- }
274
-
275
- # --- Advisory budget per ADR-045 ---
276
-
277
- @test "advisory output stays under ADR-045 1000-byte honour-system ceiling" {
278
- cat > "$RETRO_FILE" <<'EOF'
279
- # Session 3 Retro
280
-
281
- ## Signal-vs-Noise Pass
282
-
283
- Deferred this retro per session-length constraint. Next retro
284
- should run a full pass.
285
- EOF
286
- emit_write_payload "$RETRO_FILE" > "$TMPDIR_/payload.json"
287
- # Capture combined stdout+stderr; advisory channel is stderr per
288
- # PostToolUse precedent (itil-rfc-trailer-advisory.sh).
289
- combined=$(bash "$HOOK" < "$TMPDIR_/payload.json" 2>&1)
290
- [ -n "$combined" ]
291
- [ "${#combined}" -lt 1000 ]
292
- }
@@ -1,129 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # P345/RFC-044/STORY-038: itil-fix-title-lifecycle-advisory.sh — post-commit
4
- # ADVISORY for fix-titled commits whose named P<NNN> ticket is still Open on
5
- # disk (no paired lifecycle transition in the commit). Advisory-only per
6
- # ADR-092: never blocks, exit 0 on every path. Behavioural (ADR-052).
7
-
8
- setup() {
9
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
10
- HOOK="$REPO_ROOT/packages/itil/hooks/itil-fix-title-lifecycle-advisory.sh"
11
- DIR="$(mktemp -d)"; cd "$DIR"
12
- git init -q; git config user.email t@e.x; git config user.name t
13
- mkdir -p docs/problems/open docs/problems/known-error
14
- echo x > seed; git add -A; git commit -qm "chore: seed"
15
- }
16
- teardown() { cd /; rm -rf "$DIR"; }
17
-
18
- # Feed the hook a PostToolUse Bash payload for a `git commit` command.
19
- run_hook() {
20
- printf '{"tool_name":"Bash","tool_input":{"command":"git commit -m x"}}' | bash "$HOOK"
21
- }
22
-
23
- commit() { git commit -q --allow-empty -m "$1"; }
24
-
25
- @test "advises when a fix-titled commit names a still-Open ticket" {
26
- echo t > docs/problems/open/501-widget-breaks.md
27
- commit "fix(itil): P501 stop the widget breaking"
28
- run run_hook
29
- [ "$status" -eq 0 ]
30
- [[ "$output" == *"P501"* ]]
31
- [[ "$output" == *"Open"* ]]
32
- [[ "$output" == *"transition"* ]]
33
- }
34
-
35
- @test "silent when the named ticket is known-error (transition already happened)" {
36
- echo t > docs/problems/known-error/501-widget-breaks.md
37
- commit "fix(itil): P501 stop the widget breaking"
38
- run run_hook
39
- [ "$status" -eq 0 ]
40
- [ -z "$output" ]
41
- }
42
-
43
- @test "silent when the named ticket does not exist on disk" {
44
- commit "fix(itil): P999 phantom ticket"
45
- run run_hook
46
- [ "$status" -eq 0 ]
47
- [ -z "$output" ]
48
- }
49
-
50
- @test "silent on a non-fix-typed subject naming a P token" {
51
- echo t > docs/problems/open/501-widget-breaks.md
52
- commit "docs(problems): P501 add investigation notes"
53
- run run_hook
54
- [ "$status" -eq 0 ]
55
- [ -z "$output" ]
56
- }
57
-
58
- @test "silent on a fix-typed subject with no P token" {
59
- echo t > docs/problems/open/501-widget-breaks.md
60
- commit "fix(itil): tidy the widget"
61
- run run_hook
62
- [ "$status" -eq 0 ]
63
- [ -z "$output" ]
64
- }
65
-
66
- @test "advises on the bare fix: form too" {
67
- echo t > docs/problems/open/502-gadget-stuck.md
68
- commit "fix: P502 unstick the gadget"
69
- run run_hook
70
- [ "$status" -eq 0 ]
71
- [[ "$output" == *"P502"* ]]
72
- }
73
-
74
- @test "total emission stays at or under 300 bytes with many still-Open tickets" {
75
- for n in 511 512 513 514 515 516; do
76
- echo t > "docs/problems/open/${n}-thing-${n}.md"
77
- done
78
- commit "fix(itil): P511 P512 P513 P514 P515 P516 mega sweep"
79
- run run_hook
80
- [ "$status" -eq 0 ]
81
- [ -n "$output" ]
82
- [ "${#output}" -le 300 ]
83
- }
84
-
85
- @test "bypass env var suppresses the advisory" {
86
- echo t > docs/problems/open/501-widget-breaks.md
87
- commit "fix(itil): P501 stop the widget breaking"
88
- run env BYPASS_FIX_TITLE_LIFECYCLE_ADVISORY=1 bash -c 'printf "{\"tool_name\":\"Bash\",\"tool_input\":{\"command\":\"git commit -m x\"}}" | bash "$0"' "$HOOK"
89
- [ "$status" -eq 0 ]
90
- [ -z "$output" ]
91
- }
92
-
93
- @test "silent on non-Bash tool payloads" {
94
- echo t > docs/problems/open/501-widget-breaks.md
95
- commit "fix(itil): P501 stop the widget breaking"
96
- run bash -c 'printf "{\"tool_name\":\"Write\",\"tool_input\":{}}" | bash "$0"' "$HOOK"
97
- [ "$status" -eq 0 ]
98
- [ -z "$output" ]
99
- }
100
-
101
- @test "silent on non-commit Bash commands" {
102
- echo t > docs/problems/open/501-widget-breaks.md
103
- commit "fix(itil): P501 stop the widget breaking"
104
- run bash -c 'printf "{\"tool_name\":\"Bash\",\"tool_input\":{\"command\":\"git log --oneline\"}}" | bash "$0"' "$HOOK"
105
- [ "$status" -eq 0 ]
106
- [ -z "$output" ]
107
- }
108
-
109
- @test "fail-open silent when docs/problems is absent" {
110
- rm -rf docs/problems
111
- commit "fix(itil): P501 stop the widget breaking"
112
- run run_hook
113
- [ "$status" -eq 0 ]
114
- [ -z "$output" ]
115
- }
116
-
117
- @test "fail-open silent outside a git work tree" {
118
- NOGIT="$(mktemp -d)"; cd "$NOGIT"
119
- run run_hook
120
- [ "$status" -eq 0 ]
121
- [ -z "$output" ]
122
- cd /; rm -rf "$NOGIT"
123
- }
124
-
125
- @test "fail-open silent on malformed JSON input" {
126
- run bash -c 'printf "not json at all" | bash "$0"' "$HOOK"
127
- [ "$status" -eq 0 ]
128
- [ -z "$output" ]
129
- }