@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,392 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # @problem P149 — manage-problem Step 0 reconcile halt-on-drift directive
4
- # doesn't distinguish uncommitted-rename-rooted drift (same-session pending,
5
- # in-flow P094/P062 refresh will land it in the upcoming commit per ADR-014)
6
- # from committed cross-session drift (must halt and route to
7
- # /wr-itil:reconcile-readme).
8
- #
9
- # Contract: `classify-readme-drift.sh <drift-stdout-file> [<problems-dir>]`
10
- # reads the captured stdout of reconcile-readme.sh exit-1 output AND the
11
- # working-tree state via `git status --porcelain`, then classifies the drift.
12
- #
13
- # Output (stdout, one classification line):
14
- # INLINE_REFRESH covered=<N> — every drift ID is the destination
15
- # of a staged rename in the working
16
- # tree; defer to in-flow P094/P062
17
- # refresh per ADR-014.
18
- # HALT_ROUTE_RECONCILE uncovered=<N> — at least one drift ID is NOT
19
- # covered by a working-tree rename;
20
- # committed cross-session drift OR
21
- # mixed; route to
22
- # /wr-itil:reconcile-readme.
23
- #
24
- # Exit codes:
25
- # 0 = INLINE_REFRESH
26
- # 1 = HALT_ROUTE_RECONCILE
27
- # 2 = parse error (drift-stdout-file missing or empty)
28
- #
29
- # @adr ADR-014 (single-commit grain — the carve-out preserves it for the
30
- # in-flow path while keeping cross-session drift safety)
31
- # @adr ADR-005 (Plugin testing strategy — behavioural bats per P081)
32
- # @jtbd JTBD-006 (Progress the Backlog While I'm Away — AFK loop continuity)
33
- # @jtbd JTBD-001 (Enforce Governance Without Slowing Down — single-commit grain)
34
-
35
- setup() {
36
- SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
37
- SCRIPT="$SCRIPTS_DIR/classify-readme-drift.sh"
38
- FIXTURE_DIR="$(mktemp -d)"
39
- cd "$FIXTURE_DIR"
40
- git init -q
41
- git config user.email test@example.com
42
- git config user.name "Test"
43
- mkdir -p docs/problems
44
- }
45
-
46
- teardown() {
47
- cd /
48
- rm -rf "$FIXTURE_DIR"
49
- }
50
-
51
- # ── Existence + executable ──────────────────────────────────────────────────
52
-
53
- @test "classify-readme-drift: script exists" {
54
- [ -f "$SCRIPT" ]
55
- }
56
-
57
- @test "classify-readme-drift: script is executable" {
58
- [ -x "$SCRIPT" ]
59
- }
60
-
61
- # ── Exit 0 (INLINE_REFRESH): every drift ID covered by staged rename ────────
62
-
63
- @test "classify-readme-drift: single drift ID covered by staged rename → INLINE_REFRESH" {
64
- # Simulate the P148 scenario: prior session staged a rename for a ticket
65
- # transition; reconcile-readme observes the README is stale (still claims
66
- # the old status) and emits a DRIFT line. The in-flow P094/P062 refresh
67
- # at Step 5/Step 7 will reconcile the README in the same commit per
68
- # ADR-014.
69
- echo "stub" > docs/problems/148-foo.open.md
70
- git add docs/problems/148-foo.open.md
71
- git commit -q -m "init"
72
- git mv docs/problems/148-foo.open.md docs/problems/148-foo.verifying.md
73
-
74
- cat > drift.txt <<'EOF'
75
- DRIFT P148 wsjf-rankings: claims=open actual=verifying
76
- MISSING P148 verification-queue: actual=verifying
77
- EOF
78
-
79
- run "$SCRIPT" drift.txt docs/problems
80
- [ "$status" -eq 0 ]
81
- echo "$output" | grep -q "INLINE_REFRESH"
82
- echo "$output" | grep -q "covered=1"
83
- }
84
-
85
- @test "classify-readme-drift: multiple drift IDs all covered by staged renames → INLINE_REFRESH" {
86
- echo "stub" > docs/problems/100-aaa.open.md
87
- echo "stub" > docs/problems/101-bbb.open.md
88
- git add docs/problems/
89
- git commit -q -m "init"
90
-
91
- git mv docs/problems/100-aaa.open.md docs/problems/100-aaa.known-error.md
92
- git mv docs/problems/101-bbb.open.md docs/problems/101-bbb.verifying.md
93
-
94
- cat > drift.txt <<'EOF'
95
- DRIFT P100 wsjf-rankings: claims=open actual=known-error
96
- DRIFT P101 wsjf-rankings: claims=open actual=verifying
97
- MISSING P101 verification-queue: actual=verifying
98
- EOF
99
-
100
- run "$SCRIPT" drift.txt docs/problems
101
- [ "$status" -eq 0 ]
102
- echo "$output" | grep -q "INLINE_REFRESH"
103
- echo "$output" | grep -q "covered=2"
104
- }
105
-
106
- @test "classify-readme-drift: rename-with-modification (RM) is recognised as covered" {
107
- # `git mv` followed by an `Edit` to the renamed file's body shows up as
108
- # `RM` in `git status --porcelain` (rename + unstaged modification). The
109
- # carve-out must treat both `R ` and `RM` as covered.
110
- cat > docs/problems/200-xyz.open.md <<'EOF'
111
- # Problem 200: XYZ
112
- **Status**: Open
113
- EOF
114
- git add docs/problems/200-xyz.open.md
115
- git commit -q -m "init"
116
-
117
- git mv docs/problems/200-xyz.open.md docs/problems/200-xyz.verifying.md
118
- # Modify the renamed file's content (without re-staging) — emits RM.
119
- cat >> docs/problems/200-xyz.verifying.md <<'EOF'
120
-
121
- ## Fix Released
122
- Deployed in v0.99.0.
123
- EOF
124
-
125
- cat > drift.txt <<'EOF'
126
- DRIFT P200 wsjf-rankings: claims=open actual=verifying
127
- EOF
128
-
129
- run "$SCRIPT" drift.txt docs/problems
130
- [ "$status" -eq 0 ]
131
- echo "$output" | grep -q "INLINE_REFRESH"
132
- echo "$output" | grep -q "covered=1"
133
- }
134
-
135
- # ── P306: same-ID D+A pair coverage (substantial-body in-flight rename) ─────
136
- # When `git mv` is followed by a substantial body edit, git's rename-detection
137
- # may not match the old/new paths and emits a delete+add pair instead of an R
138
- # entry. The classifier must recognise a same-ID D+A pair as same-session
139
- # coverage — equivalent to an R/RM entry — and return INLINE_REFRESH.
140
-
141
- @test "classify-readme-drift: same-ID D+A pair (substantial-body rename) → INLINE_REFRESH" {
142
- # Seed an open ticket, commit, then `git mv` + substantial-body edit. With
143
- # rename-detection thresholds, git may emit `D ` + `A ` (or `??`) rather
144
- # than `R `. We force the D+A shape by writing wholly different content to
145
- # the destination path.
146
- cat > docs/problems/306-foo.open.md <<'EOF'
147
- # Problem 306: Foo
148
-
149
- **Status**: Open
150
-
151
- Original body — short.
152
- EOF
153
- git add docs/problems/306-foo.open.md
154
- git commit -q -m "init"
155
-
156
- # Create the new file at the verifying path with substantially different
157
- # body BEFORE removing the old path (avoid empty-dir prune). git's
158
- # rename-detection then sees delete + add as distinct entries rather than
159
- # an R-rename, because the bodies are wholly different.
160
- cat > docs/problems/306-foo.verifying.md <<'EOF'
161
- # Problem 306: Foo
162
-
163
- **Status**: Verification Pending
164
-
165
- Wholly rewritten body so git rename-detection does not match the source.
166
- This is a multi-paragraph substantive rewrite that exercises the D+A path.
167
-
168
- ## Fix Released
169
-
170
- Deployed in vX.Y.Z. Adds a behavioural fixture that exercises the
171
- classifier across the D+A coverage gap that P306 captured.
172
- EOF
173
- git rm -q docs/problems/306-foo.open.md
174
- git add docs/problems/306-foo.verifying.md
175
-
176
- cat > drift.txt <<'EOF'
177
- DRIFT P306 wsjf-rankings: claims=open actual=verifying
178
- MISSING P306 verification-queue: actual=verifying
179
- EOF
180
-
181
- run "$SCRIPT" drift.txt docs/problems
182
- [ "$status" -eq 0 ]
183
- echo "$output" | grep -q "INLINE_REFRESH"
184
- echo "$output" | grep -q "covered=1"
185
- }
186
-
187
- @test "classify-readme-drift: same-ID D+A pair with untracked add (D + ??) → INLINE_REFRESH" {
188
- # Variant: the new path is untracked (not yet `git add`-ed). git status
189
- # emits `D ` for the old path and `??` for the new path. The classifier
190
- # must still recognise the same-ID pair as same-session coverage.
191
- cat > docs/problems/307-bar.open.md <<'EOF'
192
- # Problem 307: Bar
193
- **Status**: Open
194
- EOF
195
- git add docs/problems/307-bar.open.md
196
- git commit -q -m "init"
197
-
198
- cat > docs/problems/307-bar.verifying.md <<'EOF'
199
- # Problem 307: Bar
200
-
201
- **Status**: Verification Pending
202
-
203
- Untracked add side of the D+?? pair.
204
- EOF
205
- git rm -q docs/problems/307-bar.open.md
206
-
207
- cat > drift.txt <<'EOF'
208
- DRIFT P307 wsjf-rankings: claims=open actual=verifying
209
- EOF
210
-
211
- run "$SCRIPT" drift.txt docs/problems
212
- [ "$status" -eq 0 ]
213
- echo "$output" | grep -q "INLINE_REFRESH"
214
- echo "$output" | grep -q "covered=1"
215
- }
216
-
217
- @test "classify-readme-drift: D-only (no matching A for same ID) → HALT_ROUTE_RECONCILE" {
218
- # Negative case: a delete without a corresponding add for the same ID is
219
- # NOT a rename — it is a genuine deletion. Must HALT.
220
- cat > docs/problems/308-baz.open.md <<'EOF'
221
- # Problem 308: Baz
222
- **Status**: Open
223
- EOF
224
- git add docs/problems/308-baz.open.md
225
- git commit -q -m "init"
226
-
227
- git rm -q docs/problems/308-baz.open.md
228
-
229
- cat > drift.txt <<'EOF'
230
- DRIFT P308 wsjf-rankings: claims=open actual=verifying
231
- EOF
232
-
233
- run "$SCRIPT" drift.txt docs/problems
234
- [ "$status" -eq 1 ]
235
- echo "$output" | grep -q "HALT_ROUTE_RECONCILE"
236
- echo "$output" | grep -q "uncovered=1"
237
- }
238
-
239
- @test "classify-readme-drift: mismatched D + A (different IDs) → HALT_ROUTE_RECONCILE" {
240
- # Negative case: a delete for one ID + an add for a different ID is NOT a
241
- # rename of either ticket — both are uncovered.
242
- cat > docs/problems/309-alpha.open.md <<'EOF'
243
- # Problem 309: Alpha
244
- **Status**: Open
245
- EOF
246
- git add docs/problems/309-alpha.open.md
247
- git commit -q -m "init"
248
-
249
- cat > docs/problems/310-beta.open.md <<'EOF'
250
- # Problem 310: Beta
251
- **Status**: Open
252
- EOF
253
- git rm -q docs/problems/309-alpha.open.md
254
- git add docs/problems/310-beta.open.md
255
-
256
- cat > drift.txt <<'EOF'
257
- DRIFT P309 wsjf-rankings: claims=open actual=verifying
258
- EOF
259
-
260
- run "$SCRIPT" drift.txt docs/problems
261
- [ "$status" -eq 1 ]
262
- echo "$output" | grep -q "HALT_ROUTE_RECONCILE"
263
- }
264
-
265
- # ── Exit 1 (HALT_ROUTE_RECONCILE): committed cross-session drift ────────────
266
-
267
- @test "classify-readme-drift: single drift ID not covered by any rename → HALT_ROUTE_RECONCILE" {
268
- # Simulate the P118 originating scenario: a prior session committed a
269
- # ticket transition without staging the README refresh. The README is
270
- # stale; the working tree is clean (no pending rename). Halt and route
271
- # to /wr-itil:reconcile-readme.
272
- cat > docs/problems/074-foo.closed.md <<'EOF'
273
- # Problem 074: Foo
274
- **Status**: Closed
275
- EOF
276
- git add docs/problems/074-foo.closed.md
277
- git commit -q -m "init"
278
-
279
- cat > drift.txt <<'EOF'
280
- DRIFT P074 wsjf-rankings: claims=open actual=closed
281
- EOF
282
-
283
- run "$SCRIPT" drift.txt docs/problems
284
- [ "$status" -eq 1 ]
285
- echo "$output" | grep -q "HALT_ROUTE_RECONCILE"
286
- echo "$output" | grep -q "uncovered=1"
287
- }
288
-
289
- @test "classify-readme-drift: mixed (some covered, some uncovered) → HALT_ROUTE_RECONCILE" {
290
- # Mixed case: one ID has a staged rename, another is committed drift.
291
- # The architect's confirmation: mixed routes to halt (the safe path),
292
- # because reconcile-readme will resolve both, and the in-flow refresh
293
- # only handles the rename'd one.
294
- cat > docs/problems/074-foo.closed.md <<'EOF'
295
- # Problem 074: Foo
296
- **Status**: Closed
297
- EOF
298
- cat > docs/problems/148-bar.open.md <<'EOF'
299
- # Problem 148: Bar
300
- **Status**: Open
301
- EOF
302
- git add docs/problems/
303
- git commit -q -m "init"
304
-
305
- git mv docs/problems/148-bar.open.md docs/problems/148-bar.verifying.md
306
-
307
- cat > drift.txt <<'EOF'
308
- DRIFT P074 wsjf-rankings: claims=open actual=closed
309
- DRIFT P148 wsjf-rankings: claims=open actual=verifying
310
- EOF
311
-
312
- run "$SCRIPT" drift.txt docs/problems
313
- [ "$status" -eq 1 ]
314
- echo "$output" | grep -q "HALT_ROUTE_RECONCILE"
315
- # Only P074 is uncovered.
316
- echo "$output" | grep -q "uncovered=1"
317
- }
318
-
319
- @test "classify-readme-drift: clean working tree (no renames) → HALT_ROUTE_RECONCILE for any drift" {
320
- cat > docs/problems/050-zzz.verifying.md <<'EOF'
321
- # Problem 050: Zzz
322
- **Status**: Verification Pending
323
- EOF
324
- git add docs/problems/050-zzz.verifying.md
325
- git commit -q -m "init"
326
-
327
- cat > drift.txt <<'EOF'
328
- MISSING P050 verification-queue: actual=verifying
329
- EOF
330
-
331
- run "$SCRIPT" drift.txt docs/problems
332
- [ "$status" -eq 1 ]
333
- echo "$output" | grep -q "HALT_ROUTE_RECONCILE"
334
- echo "$output" | grep -q "uncovered=1"
335
- }
336
-
337
- # ── Exit 2: parse errors ────────────────────────────────────────────────────
338
-
339
- @test "classify-readme-drift: missing drift-stdout-file → exit 2" {
340
- run "$SCRIPT" /nonexistent/drift.txt docs/problems
341
- [ "$status" -eq 2 ]
342
- echo "$output" | grep -qi "PARSE_ERROR"
343
- }
344
-
345
- @test "classify-readme-drift: empty drift-stdout-file → exit 2" {
346
- : > drift.txt
347
- run "$SCRIPT" drift.txt docs/problems
348
- [ "$status" -eq 2 ]
349
- echo "$output" | grep -qi "PARSE_ERROR"
350
- }
351
-
352
- @test "classify-readme-drift: no arguments → exit 2 with usage" {
353
- run "$SCRIPT"
354
- [ "$status" -eq 2 ]
355
- echo "$output" | grep -qi "USAGE"
356
- }
357
-
358
- # ── Default problems-dir resolution ─────────────────────────────────────────
359
-
360
- @test "classify-readme-drift: defaults to ./docs/problems when problems-dir omitted" {
361
- echo "stub" > docs/problems/300-default.open.md
362
- git add docs/problems/300-default.open.md
363
- git commit -q -m "init"
364
- git mv docs/problems/300-default.open.md docs/problems/300-default.verifying.md
365
-
366
- cat > drift.txt <<'EOF'
367
- DRIFT P300 wsjf-rankings: claims=open actual=verifying
368
- EOF
369
-
370
- run "$SCRIPT" drift.txt
371
- [ "$status" -eq 0 ]
372
- echo "$output" | grep -q "INLINE_REFRESH"
373
- }
374
-
375
- # ── Non-git working tree (defensive — outside a repo) ───────────────────────
376
-
377
- @test "classify-readme-drift: outside git repo with drift → HALT_ROUTE_RECONCILE (no rename evidence)" {
378
- # If the script is invoked from outside a git repo (defensive — should not
379
- # happen in the SKILL.md flow but safest fail-closed), there is no rename
380
- # evidence, so all drift is treated as uncovered.
381
- NON_GIT="$(mktemp -d)"
382
- cd "$NON_GIT"
383
- mkdir -p docs/problems
384
- cat > drift.txt <<'EOF'
385
- DRIFT P074 wsjf-rankings: claims=open actual=closed
386
- EOF
387
- run "$SCRIPT" drift.txt docs/problems
388
- [ "$status" -eq 1 ]
389
- echo "$output" | grep -q "HALT_ROUTE_RECONCILE"
390
- cd /
391
- rm -rf "$NON_GIT"
392
- }
@@ -1,274 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- bats_require_minimum_version 1.5.0
4
-
5
- # Behavioural assertions for packages/itil/lib/derive-first-dispatch.sh —
6
- # the shared derive-first dispatch helper extracted in P132 Phase 2a-iii-A.
7
- #
8
- # The helper centralises the dispatch mechanism shipped across three
9
- # declaration-skill surfaces (manage-incident Step 4, manage-problem
10
- # Step 4, create-adr Step 2 — Phase 2a-iii-B 4th adopter). Each caller
11
- # passes surface-specific signal definitions; the helper owns:
12
- #
13
- # - Slug derivation (Title) from prose
14
- # - RISK-POLICY matrix lookup (Severity / Priority)
15
- # - I2-isomorphic stderr advisory format
16
- #
17
- # P287 (2026-06-02) retired the two-sided lexical classifier (formerly
18
- # used by capture-problem Step 1.5 Type classification — technical vs
19
- # user-business). The type axis was removed as redundant with RFC/Story
20
- # persona-anchoring per ADR-060 Phase 4; the function had no remaining
21
- # consumer.
22
- #
23
- # @problem P132 (agents over-ask in interactive sessions — Phase 2a-iii-A
24
- # shared helper extraction)
25
- # @problem P185 (capture-problem Step 1.5 worked-example precedent — retired by P287)
26
- # @problem P287 (type-classification retirement — lexical_classify_two_sided removed)
27
- # @adr ADR-044 (Decision-Delegation Contract — derive-first framework
28
- # resolution boundary)
29
- # @adr ADR-026 (cost-source grounding — stderr advisory shape)
30
- # @adr ADR-052 (behavioural-by-default — these are runtime behaviour
31
- # assertions on the helper functions, NOT structural greps)
32
- # @jtbd JTBD-001 (enforce governance without slowing down — primary)
33
- # @jtbd JTBD-101 (extend the suite with consistent patterns)
34
-
35
- setup() {
36
- LIB_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../lib" && pwd)"
37
- HELPER="${LIB_DIR}/derive-first-dispatch.sh"
38
- [ -f "$HELPER" ]
39
- # shellcheck disable=SC1090
40
- source "$HELPER"
41
- PKG_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../.." && pwd)"
42
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
43
- ARCHITECT_PKG_ROOT="$REPO_ROOT/packages/architect"
44
- }
45
-
46
- # ----------------------------------------------------------------------
47
- # Stderr advisory contract (I2-isomorphic format across all 3 surfaces).
48
- # Format: <skill>: derived <field>=<value> from <source>; <reversibility>
49
- # ----------------------------------------------------------------------
50
-
51
- @test "emit_stderr_advisory writes single canonical line to stderr" {
52
- run -0 bash -c '
53
- source "'"$HELPER"'"
54
- emit_stderr_advisory manage-problem title my-slug "description tokens" \
55
- "re-invoke with the desired title or rename the file if the slug is wrong"
56
- '
57
- # stderr captured in $output via run; assert structure
58
- [[ "$output" == *"manage-problem: derived title=my-slug from description tokens; re-invoke with the desired title"* ]]
59
- }
60
-
61
- @test "emit_stderr_advisory uses default reversibility clause when omitted" {
62
- run -0 bash -c '
63
- source "'"$HELPER"'"
64
- emit_stderr_advisory manage-incident severity "9 (Medium)" "RISK-POLICY matrix"
65
- '
66
- [[ "$output" == *"manage-incident: derived severity=9 (Medium) from RISK-POLICY matrix;"* ]]
67
- [[ "$output" == *"re-invoke"* ]] || [[ "$output" == *"update"* ]]
68
- }
69
-
70
- @test "emit_stderr_advisory shape is I2-isomorphic across surfaces (same sentence structure)" {
71
- run -0 bash -c '
72
- source "'"$HELPER"'"
73
- emit_stderr_advisory capture-problem type technical "description signals" "re-invoke with --type=user-business to override"
74
- emit_stderr_advisory manage-incident title incident-slug "description" "re-invoke or rename"
75
- emit_stderr_advisory manage-problem priority "9 (Medium)" "RISK-POLICY matrix" "re-invoke or update if mis-rated"
76
- '
77
- # Each surface emits the same sentence shape: <skill>: derived <field>=<value> from <source>; <clause>
78
- line_count=$(printf '%s\n' "$output" | grep -c "^[a-z-]*: derived ")
79
- [ "$line_count" -eq 3 ]
80
- }
81
-
82
- # ----------------------------------------------------------------------
83
- # Kebab-case slug derivation from prose.
84
- # ----------------------------------------------------------------------
85
-
86
- @test "derive_kebab_slug produces kebab-case from prose" {
87
- run -0 bash -c '
88
- source "'"$HELPER"'"
89
- derive_kebab_slug "Agent over-asks during interactive sessions"
90
- '
91
- [[ "$output" == *"agent"* ]]
92
- [[ "$output" == *"over"* ]] || [[ "$output" == *"asks"* ]]
93
- [[ "$output" != *" "* ]]
94
- [[ "$output" != *"_"* ]]
95
- }
96
-
97
- @test "derive_kebab_slug drops stopwords" {
98
- run -0 bash -c '
99
- source "'"$HELPER"'"
100
- derive_kebab_slug "The agent is asking the user a question"
101
- '
102
- # stopwords like "the", "a", "is" must NOT appear as standalone tokens
103
- [[ "$output" != *"-the-"* ]]
104
- [[ "$output" != "the-"* ]]
105
- [[ "$output" == *"agent"* ]]
106
- }
107
-
108
- @test "derive_kebab_slug caps token count (default 8)" {
109
- run -0 bash -c '
110
- source "'"$HELPER"'"
111
- derive_kebab_slug "one two three four five six seven eight nine ten eleven twelve"
112
- '
113
- token_count=$(printf '%s\n' "$output" | tr '-' '\n' | wc -l | tr -d ' ')
114
- [ "$token_count" -le 8 ]
115
- }
116
-
117
- @test "derive_kebab_slug accepts custom token count" {
118
- run -0 bash -c '
119
- source "'"$HELPER"'"
120
- derive_kebab_slug "alpha beta gamma delta epsilon zeta" 3
121
- '
122
- token_count=$(printf '%s\n' "$output" | tr '-' '\n' | wc -l | tr -d ' ')
123
- [ "$token_count" -le 3 ]
124
- }
125
-
126
- # ----------------------------------------------------------------------
127
- # P287 (2026-06-02) retirement: lexical_classify_two_sided removed.
128
- # Regression guard — the function must NOT exist in the helper.
129
- # ----------------------------------------------------------------------
130
-
131
- @test "P287: lexical_classify_two_sided is removed from the helper (regression guard)" {
132
- # The function name must not appear as a function definition.
133
- ! grep -E '^lexical_classify_two_sided\(\)' "$HELPER"
134
- }
135
-
136
- @test "P287: helper still exports the three surviving functions" {
137
- # emit_stderr_advisory, derive_kebab_slug, risk_policy_matrix_lookup
138
- # serve manage-incident severity, manage-problem priority, and
139
- # create-adr title derivation — they must stay.
140
- grep -E '^emit_stderr_advisory\(\)' "$HELPER"
141
- grep -E '^derive_kebab_slug\(\)' "$HELPER"
142
- grep -E '^risk_policy_matrix_lookup\(\)' "$HELPER"
143
- }
144
-
145
- # ----------------------------------------------------------------------
146
- # RISK-POLICY matrix lookup (manage-incident / manage-problem mechanism).
147
- # Returns:
148
- # <score>|<label>|impact=<L>+likelihood=<L> — clear single-cell match
149
- # AMBIGUOUS|<reason> — multi-band or zero match
150
- # ----------------------------------------------------------------------
151
-
152
- @test "risk_policy_matrix_lookup returns clear cell on unambiguous impact + likelihood signals" {
153
- run -0 bash -c '
154
- source "'"$HELPER"'"
155
- impact_high=("\\b(down|outage|data loss|unavailable)\\b")
156
- impact_mod=("\\b(slow|latency|degraded)\\b")
157
- impact_low=("\\b(typo|cosmetic)\\b")
158
- likelihood_high=("\\b(every request|reproducible|always)\\b")
159
- likelihood_med=("\\b(intermittent|flaky)\\b")
160
- likelihood_low=("\\b(one-off|single)\\b")
161
- risk_policy_matrix_lookup "service is down on every request" impact_high impact_mod impact_low likelihood_high likelihood_med likelihood_low
162
- '
163
- # Expect impact=high(5) + likelihood=high(5) -> score=25, label=Very High
164
- [[ "$output" == "25|"* ]] || [[ "$output" == "20|"* ]] || [[ "$output" == "15|"* ]]
165
- [[ "$output" == *"High"* ]] || [[ "$output" == *"Very High"* ]]
166
- }
167
-
168
- @test "risk_policy_matrix_lookup returns AMBIGUOUS on multi-band impact" {
169
- run -0 bash -c '
170
- source "'"$HELPER"'"
171
- impact_high=("\\b(down)\\b")
172
- impact_mod=("\\b(slow)\\b")
173
- impact_low=("\\b(typo)\\b")
174
- likelihood_high=("\\b(every request)\\b")
175
- likelihood_med=("\\b(intermittent)\\b")
176
- likelihood_low=("\\b(one-off)\\b")
177
- risk_policy_matrix_lookup "service is down and slow with typo" impact_high impact_mod impact_low likelihood_high likelihood_med likelihood_low
178
- '
179
- [[ "$output" == "AMBIGUOUS|"* ]]
180
- }
181
-
182
- @test "risk_policy_matrix_lookup returns AMBIGUOUS when no signals match" {
183
- run -0 bash -c '
184
- source "'"$HELPER"'"
185
- impact_high=("\\b(down)\\b")
186
- impact_mod=("\\b(slow)\\b")
187
- impact_low=("\\b(typo)\\b")
188
- likelihood_high=("\\b(every request)\\b")
189
- likelihood_med=("\\b(intermittent)\\b")
190
- likelihood_low=("\\b(one-off)\\b")
191
- risk_policy_matrix_lookup "totally bland text" impact_high impact_mod impact_low likelihood_high likelihood_med likelihood_low
192
- '
193
- [[ "$output" == "AMBIGUOUS|"* ]]
194
- }
195
-
196
- @test "risk_policy_matrix_lookup label band aligns with RISK-POLICY.md (Medium = 5-9)" {
197
- # Verify a specific clear-cell mapping produces the RISK-POLICY-canonical label.
198
- # impact=mod (3) * likelihood=high (5) = 15 -> "High" band (10-16)
199
- run -0 bash -c '
200
- source "'"$HELPER"'"
201
- impact_high=("\\b(down)\\b")
202
- impact_mod=("\\b(slow)\\b")
203
- impact_low=("\\b(typo)\\b")
204
- likelihood_high=("\\b(every request)\\b")
205
- likelihood_med=("\\b(intermittent)\\b")
206
- likelihood_low=("\\b(one-off)\\b")
207
- risk_policy_matrix_lookup "the service is slow on every request" impact_high impact_mod impact_low likelihood_high likelihood_med likelihood_low
208
- '
209
- [[ "$output" == "15|High|"* ]]
210
- }
211
-
212
- # ----------------------------------------------------------------------
213
- # Cross-skill consistency: all 4 SKILL.md surfaces reference the helper
214
- # as the shared dispatch mechanism. The I2-isomorphic stderr advisory
215
- # format is locked-in by reference to derive-first-dispatch.sh.
216
- #
217
- # Phase 2a-iii-B (2026-05-16): 4th adopter wr-architect:create-adr added.
218
- # Helper canonical source moved to packages/shared/ per ADR-017 sync
219
- # pattern; per-package lib/ copies in packages/itil/lib/ and
220
- # packages/architect/lib/ stay byte-identical via scripts/sync-derive-first-dispatch.sh.
221
- # ----------------------------------------------------------------------
222
-
223
- @test "manage-incident Step 4 cross-references derive-first-dispatch.sh helper" {
224
- run grep -c "derive-first-dispatch\\.sh\\|packages/itil/lib/derive-first-dispatch" \
225
- "${PKG_ROOT}/skills/manage-incident/SKILL.md"
226
- [ "$status" -eq 0 ]
227
- [ "$output" -ge 1 ]
228
- }
229
-
230
- @test "manage-problem Step 4 cross-references derive-first-dispatch.sh helper" {
231
- run grep -c "derive-first-dispatch\\.sh\\|packages/itil/lib/derive-first-dispatch" \
232
- "${PKG_ROOT}/skills/manage-problem/SKILL.md"
233
- [ "$status" -eq 0 ]
234
- [ "$output" -ge 1 ]
235
- }
236
-
237
- @test "create-adr Step 2 cross-references derive-first-dispatch.sh helper (Phase 2a-iii-B 4th adopter)" {
238
- # The 4th adopter (architect package) sources from its own per-package
239
- # lib/ copy (NOT cross-package from itil) per ADR-017.
240
- run grep -c "derive-first-dispatch\\.sh\\|packages/architect/lib/derive-first-dispatch" \
241
- "${ARCHITECT_PKG_ROOT}/skills/create-adr/SKILL.md"
242
- [ "$status" -eq 0 ]
243
- [ "$output" -ge 1 ]
244
- }
245
-
246
- @test "helper file documents its four caller surfaces (audit trail)" {
247
- # The helper's header comment must name the four SKILL.md surfaces it
248
- # serves so the audit trail is recoverable from the helper itself.
249
- # Phase 2a-iii-B adds create-adr as the 4th adopter.
250
- run grep -E "capture-problem" "$HELPER"
251
- [ "$status" -eq 0 ]
252
- run grep -E "manage-incident" "$HELPER"
253
- [ "$status" -eq 0 ]
254
- run grep -E "manage-problem" "$HELPER"
255
- [ "$status" -eq 0 ]
256
- run grep -E "create-adr" "$HELPER"
257
- [ "$status" -eq 0 ]
258
- }
259
-
260
- @test "per-package lib/ copies are byte-identical to canonical packages/shared/ source (ADR-017)" {
261
- # Phase 2a-iii-B + ADR-017: canonical at packages/shared/, synced copies
262
- # in per-package lib/. The sync script (scripts/sync-derive-first-dispatch.sh)
263
- # in --check mode is the CI guard; this test asserts the post-condition.
264
- local shared_src="${REPO_ROOT}/packages/shared/derive-first-dispatch.sh"
265
- local itil_copy="${REPO_ROOT}/packages/itil/lib/derive-first-dispatch.sh"
266
- local architect_copy="${REPO_ROOT}/packages/architect/lib/derive-first-dispatch.sh"
267
- [ -f "$shared_src" ]
268
- [ -f "$itil_copy" ]
269
- [ -f "$architect_copy" ]
270
- run diff -q "$shared_src" "$itil_copy"
271
- [ "$status" -eq 0 ]
272
- run diff -q "$shared_src" "$architect_copy"
273
- [ "$status" -eq 0 ]
274
- }