@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,309 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # @rfc RFC-002 T2 — Dual-tolerant SKILL.md glob updates
4
- # @adr ADR-031 (Problem-ticket directory layout)
5
- # @adr ADR-051 (load-bearing-from-the-start — the dual-tolerant glob
6
- # contract ships with a behavioural enforcement test, not later by
7
- # graceful drift)
8
- # @adr ADR-052 (behavioural-bats default — this test exercises the
9
- # glob shape against synthetic fixtures and asserts observable
10
- # enumeration behaviour; it does NOT structurally grep SKILL.md
11
- # prose for the dual-pattern string, which would be P081-class
12
- # structural-test-disguised-as-behavioural)
13
- # @adr ADR-014 (single-purpose: one mechanical contract — dual-glob
14
- # enumeration parity across both layouts)
15
- # @problem P069 (driving — flat layout unskimmable; the migration this
16
- # contract guards is the relief)
17
- # @problem P081 (no structural-grep on SKILL.md content — this test
18
- # is the behavioural alternative)
19
- # @jtbd JTBD-001 (extended scope — multi-commit RFC-grain coordinated
20
- # change; the test is the cross-skill invariant the SKILL.md edits
21
- # share)
22
- # @jtbd JTBD-006 (work-backlog-AFK — dual-tolerant globs preserve
23
- # AFK-loop continuity across the migration window; without this
24
- # contract, mid-migration loop iterations silently miss tickets in
25
- # the un-migrated layout)
26
- #
27
- # Contract: SKILL.md enumeration globs of `docs/problems/<state>/...`
28
- # during the RFC-002 migration window MUST match BOTH the flat layout
29
- # (`docs/problems/<NNN>-<title>.<state>.md`) AND the per-state subdir
30
- # layout (`docs/problems/<state>/<NNN>-<title>.md`). The dual-tolerant
31
- # pattern shape is:
32
- #
33
- # ls docs/problems/*.<state>.md docs/problems/<state>/*.md 2>/dev/null
34
- #
35
- # (with `2>/dev/null` swallowing the no-match error from whichever
36
- # half of the OR currently has zero matches.)
37
- #
38
- # This test exercises the canonical dual-tolerant pattern shapes (state-
39
- # filtered enumeration, ID-anchored lookup, all-state-all-tickets) on
40
- # synthetic fixtures of three shapes (flat-only, per-state-only, mixed).
41
- # Each cross-product asserts non-empty enumeration of every present
42
- # ticket and zero false-positive enumeration of absent tickets.
43
- #
44
- # CONTRACT NOTE: when one half of the dual-glob has zero matches in the
45
- # current fixture (single-layout fixtures), `ls X Y 2>/dev/null` exits
46
- # nonzero — the unmatched literal pathname propagates to ls's argv and
47
- # `2>/dev/null` only suppresses the stderr noise, not the exit code.
48
- # This is CORRECT behaviour — SKILL.md call sites MUST treat STDOUT
49
- # emptiness as the canonical "no tickets" signal, NOT exit code zero.
50
- # Test assertions therefore probe stdout content, not `$status`, except
51
- # in the empty-fixture and missing-ID cases where nonzero exit is the
52
- # intended contract.
53
- #
54
- # T6 (post-T5 verification) drops the flat-layout half. This test
55
- # updates at T6 to single-pattern, NOT removed — the contract becomes
56
- # "per-state layout enumerates correctly" but remains behavioural.
57
-
58
- setup() {
59
- REPO_ROOT="$(mktemp -d)"
60
- cd "$REPO_ROOT"
61
- mkdir -p docs/problems
62
- }
63
-
64
- teardown() {
65
- cd /
66
- rm -rf "$REPO_ROOT"
67
- }
68
-
69
- # ── fixture builders ─────────────────────────────────────────────────────────
70
-
71
- build_flat_layout() {
72
- cat > docs/problems/100-foo.open.md <<'EOF'
73
- # Problem 100: Foo
74
- **Status**: Open
75
- EOF
76
- cat > docs/problems/101-bar.known-error.md <<'EOF'
77
- # Problem 101: Bar
78
- **Status**: Known Error
79
- EOF
80
- cat > docs/problems/102-baz.verifying.md <<'EOF'
81
- # Problem 102: Baz
82
- **Status**: Verification Pending
83
- EOF
84
- cat > docs/problems/103-qux.parked.md <<'EOF'
85
- # Problem 103: Qux
86
- **Status**: Parked
87
- EOF
88
- cat > docs/problems/104-quux.closed.md <<'EOF'
89
- # Problem 104: Quux
90
- **Status**: Closed
91
- EOF
92
- }
93
-
94
- build_per_state_layout() {
95
- mkdir -p docs/problems/open docs/problems/known-error docs/problems/verifying docs/problems/parked docs/problems/closed
96
- cat > docs/problems/open/200-foo2.md <<'EOF'
97
- # Problem 200: Foo2
98
- **Status**: Open
99
- EOF
100
- cat > docs/problems/known-error/201-bar2.md <<'EOF'
101
- # Problem 201: Bar2
102
- **Status**: Known Error
103
- EOF
104
- cat > docs/problems/verifying/202-baz2.md <<'EOF'
105
- # Problem 202: Baz2
106
- **Status**: Verification Pending
107
- EOF
108
- cat > docs/problems/parked/203-qux2.md <<'EOF'
109
- # Problem 203: Qux2
110
- **Status**: Parked
111
- EOF
112
- cat > docs/problems/closed/204-quux2.md <<'EOF'
113
- # Problem 204: Quux2
114
- **Status**: Closed
115
- EOF
116
- }
117
-
118
- build_mixed_layout() {
119
- build_flat_layout
120
- build_per_state_layout
121
- }
122
-
123
- # ── Pattern A: state-filtered enumeration ────────────────────────────────────
124
-
125
- @test "dual-tolerant state-filtered glob: flat-only fixture enumerates open ticket" {
126
- build_flat_layout
127
- run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
128
- # ls exits nonzero because the per-state half has no match — stdout
129
- # content is the canonical signal, not exit code.
130
- [[ "$output" == *"100-foo.open.md"* ]]
131
- }
132
-
133
- @test "dual-tolerant state-filtered glob: per-state-only fixture enumerates open ticket" {
134
- build_per_state_layout
135
- run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
136
- [[ "$output" == *"open/200-foo2.md"* ]]
137
- }
138
-
139
- @test "dual-tolerant state-filtered glob: mixed fixture enumerates BOTH layouts simultaneously" {
140
- build_mixed_layout
141
- run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
142
- [ "$status" -eq 0 ]
143
- [[ "$output" == *"100-foo.open.md"* ]]
144
- [[ "$output" == *"open/200-foo2.md"* ]]
145
- }
146
-
147
- @test "dual-tolerant state-filtered glob: mixed fixture, known-error" {
148
- build_mixed_layout
149
- run bash -c 'ls docs/problems/*.known-error.md docs/problems/known-error/*.md 2>/dev/null'
150
- [ "$status" -eq 0 ]
151
- [[ "$output" == *"101-bar.known-error.md"* ]]
152
- [[ "$output" == *"known-error/201-bar2.md"* ]]
153
- }
154
-
155
- @test "dual-tolerant state-filtered glob: mixed fixture, verifying" {
156
- build_mixed_layout
157
- run bash -c 'ls docs/problems/*.verifying.md docs/problems/verifying/*.md 2>/dev/null'
158
- [ "$status" -eq 0 ]
159
- [[ "$output" == *"102-baz.verifying.md"* ]]
160
- [[ "$output" == *"verifying/202-baz2.md"* ]]
161
- }
162
-
163
- @test "dual-tolerant state-filtered glob: mixed fixture, parked" {
164
- build_mixed_layout
165
- run bash -c 'ls docs/problems/*.parked.md docs/problems/parked/*.md 2>/dev/null'
166
- [ "$status" -eq 0 ]
167
- [[ "$output" == *"103-qux.parked.md"* ]]
168
- [[ "$output" == *"parked/203-qux2.md"* ]]
169
- }
170
-
171
- @test "dual-tolerant state-filtered glob: state filter excludes other-state tickets in flat layout" {
172
- build_flat_layout
173
- run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
174
- [[ "$output" != *"known-error"* ]]
175
- [[ "$output" != *"verifying"* ]]
176
- [[ "$output" != *"parked"* ]]
177
- [[ "$output" != *"closed"* ]]
178
- }
179
-
180
- @test "dual-tolerant state-filtered glob: state filter excludes other-state subdirs in per-state layout" {
181
- build_per_state_layout
182
- run bash -c 'ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null'
183
- # No known-error/, verifying/, parked/, closed/ subdir contents leak into open enumeration.
184
- [[ "$output" != *"201-bar2.md"* ]]
185
- [[ "$output" != *"202-baz2.md"* ]]
186
- [[ "$output" != *"203-qux2.md"* ]]
187
- [[ "$output" != *"204-quux2.md"* ]]
188
- }
189
-
190
- # ── Pattern B: ID-anchored lookup (any state) ────────────────────────────────
191
-
192
- @test "dual-tolerant ID-anchored glob: flat-only fixture finds ticket by ID" {
193
- build_flat_layout
194
- run bash -c 'ls docs/problems/100-*.md docs/problems/*/100-*.md 2>/dev/null'
195
- # Single-layout fixture: ls exits nonzero on the unmatched half;
196
- # stdout content is the contract signal.
197
- [[ "$output" == *"100-foo.open.md"* ]]
198
- }
199
-
200
- @test "dual-tolerant ID-anchored glob: per-state-only fixture finds ticket by ID" {
201
- build_per_state_layout
202
- run bash -c 'ls docs/problems/200-*.md docs/problems/*/200-*.md 2>/dev/null'
203
- [[ "$output" == *"open/200-foo2.md"* ]]
204
- }
205
-
206
- @test "dual-tolerant ID-anchored glob: mixed fixture finds tickets across both layouts" {
207
- build_mixed_layout
208
- run bash -c 'ls docs/problems/100-*.md docs/problems/*/100-*.md 2>/dev/null'
209
- [[ "$output" == *"100-foo.open.md"* ]]
210
- run bash -c 'ls docs/problems/200-*.md docs/problems/*/200-*.md 2>/dev/null'
211
- [[ "$output" == *"open/200-foo2.md"* ]]
212
- }
213
-
214
- @test "dual-tolerant ID-anchored glob: missing ID returns empty (status nonzero from ls)" {
215
- build_flat_layout
216
- set +e
217
- result=$(ls docs/problems/999-*.md docs/problems/*/999-*.md 2>/dev/null)
218
- rc=$?
219
- set -e
220
- [ -z "$result" ]
221
- [ "$rc" -ne 0 ]
222
- }
223
-
224
- # ── Pattern C: all-states-all-tickets (next-ID compute, count) ───────────────
225
-
226
- @test "dual-tolerant all-tickets glob: flat-only enumerates every ticket regardless of state" {
227
- build_flat_layout
228
- run bash -c 'ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null'
229
- # Single-layout fixture: stdout is contract signal, not $status.
230
- [[ "$output" == *"100-foo.open.md"* ]]
231
- [[ "$output" == *"101-bar.known-error.md"* ]]
232
- [[ "$output" == *"102-baz.verifying.md"* ]]
233
- [[ "$output" == *"103-qux.parked.md"* ]]
234
- [[ "$output" == *"104-quux.closed.md"* ]]
235
- }
236
-
237
- @test "dual-tolerant all-tickets glob: per-state-only enumerates every ticket regardless of state" {
238
- build_per_state_layout
239
- run bash -c 'ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null'
240
- [[ "$output" == *"open/200-foo2.md"* ]]
241
- [[ "$output" == *"known-error/201-bar2.md"* ]]
242
- [[ "$output" == *"verifying/202-baz2.md"* ]]
243
- [[ "$output" == *"parked/203-qux2.md"* ]]
244
- [[ "$output" == *"closed/204-quux2.md"* ]]
245
- }
246
-
247
- @test "dual-tolerant all-tickets glob: mixed fixture enumerates ALL tickets in BOTH layouts" {
248
- build_mixed_layout
249
- run bash -c 'ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null'
250
- [ "$status" -eq 0 ]
251
- for ticket in 100-foo.open.md 101-bar.known-error.md 102-baz.verifying.md 103-qux.parked.md 104-quux.closed.md \
252
- open/200-foo2.md known-error/201-bar2.md verifying/202-baz2.md parked/203-qux2.md closed/204-quux2.md; do
253
- [[ "$output" == *"${ticket}"* ]]
254
- done
255
- }
256
-
257
- @test "dual-tolerant all-tickets next-ID compute: highest ID across both layouts" {
258
- # Critical for capture-problem next-ID compute (architect finding 2):
259
- # the next-ID surface MUST recurse so flat-layout 104 and per-state 204
260
- # both contribute to max-ID; missing the per-state half re-allocates
261
- # already-taken IDs.
262
- build_mixed_layout
263
- result=$(ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null \
264
- | sed 's/.*\///' \
265
- | grep -oE '^[0-9]+' \
266
- | sort -n \
267
- | tail -1)
268
- [ "$result" = "204" ]
269
- }
270
-
271
- # ── Pattern D: pathspec-pair shell-glob equivalence ──────────────────────────
272
- # git log accepts multiple pathspecs; the dual-tolerant filter is two
273
- # pathspecs side-by-side. We validate this against the working-tree
274
- # semantics that git uses (the same shell-glob shape).
275
-
276
- @test "dual-tolerant pathspec pair: each pathspec enumerates its layout half independently" {
277
- build_mixed_layout
278
- flat_count=$(ls docs/problems/*.md 2>/dev/null | wc -l | tr -d ' ')
279
- subdir_count=$(ls docs/problems/*/*.md 2>/dev/null | wc -l | tr -d ' ')
280
- [ "$flat_count" -ge 5 ]
281
- [ "$subdir_count" -ge 5 ]
282
- }
283
-
284
- # ── Pattern E: brace-expansion ID + state-set (report-upstream) ──────────────
285
-
286
- @test "dual-tolerant ID + state-set lookup: flat brace expansion + per-state lookup" {
287
- build_mixed_layout
288
- # Old shape: ls docs/problems/${ID}-*.{open,known-error,verifying,closed}.md
289
- # Dual-tolerant: add docs/problems/*/${ID}-*.md as a sibling pathspec.
290
- run bash -c 'ls docs/problems/100-*.{open,known-error,verifying,closed}.md docs/problems/*/100-*.md 2>/dev/null'
291
- [[ "$output" == *"100-foo.open.md"* ]]
292
- run bash -c 'ls docs/problems/200-*.{open,known-error,verifying,closed}.md docs/problems/*/200-*.md 2>/dev/null'
293
- [[ "$output" == *"open/200-foo2.md"* ]]
294
- }
295
-
296
- # ── Composition: empty-tree fixture exit-code semantics ──────────────────────
297
-
298
- @test "dual-tolerant glob: empty fixture produces empty output and ls exits nonzero" {
299
- # Critical for null-safe `2>/dev/null` semantics — `ls` on a
300
- # non-matching glob exits nonzero. SKILL.md call sites must rely on
301
- # `2>/dev/null` to suppress the stderr noise but still treat empty
302
- # stdout as the canonical "no tickets" signal.
303
- set +e
304
- result=$(ls docs/problems/*.open.md docs/problems/open/*.md 2>/dev/null)
305
- rc=$?
306
- set -e
307
- [ -z "$result" ]
308
- [ "$rc" -ne 0 ]
309
- }
@@ -1,170 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # ADR-067 / P248: effort-tally.sh attributes .afk-run-state/iter*.json actuals
4
- # back to their source ticket (pNNN filename token) and emits the per-ticket
5
- # tally. Behavioural — exercises the script against fixture iter-JSON trees.
6
-
7
- setup() {
8
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
9
- SCRIPT="$REPO_ROOT/packages/itil/scripts/effort-tally.sh"
10
- DIR="$(mktemp -d)"
11
- mkdir -p "$DIR/.afk-run-state"
12
- }
13
-
14
- teardown() { rm -rf "$DIR"; }
15
-
16
- mk_iter() { # mk_iter <filename> <cost> <duration_ms> <input_tokens>
17
- cat > "$DIR/.afk-run-state/$1" <<EOF
18
- {"total_cost_usd": $2, "duration_ms": $3, "usage": {"input_tokens": $4, "output_tokens": 0, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0}}
19
- EOF
20
- }
21
-
22
- @test "attributes a single iter to its ticket via the pNNN filename token" {
23
- mk_iter "iter1-p087.json" 12.50 600000 1000000
24
- run bash "$SCRIPT" "$DIR/.afk-run-state"
25
- [ "$status" -eq 0 ]
26
- [[ "$output" == *"P087"* ]]
27
- [[ "$output" == *"cost_usd=12.50"* ]]
28
- [[ "$output" == *"minutes=10.0"* ]]
29
- }
30
-
31
- @test "sums multiple iters for the same ticket" {
32
- mk_iter "iter1-p087.json" 10.00 300000 500000
33
- mk_iter "iter2-p087.json" 5.00 300000 500000
34
- run bash "$SCRIPT" "$DIR/.afk-run-state"
35
- [[ "$output" == *"P087 | iters=2 | cost_usd=15.00"* ]]
36
- }
37
-
38
- @test "authoritative cost comes from total_cost_usd; tokens flagged best-effort with ~" {
39
- mk_iter "iter1-p100.json" 7.00 60000 2000000
40
- run bash "$SCRIPT" "$DIR/.afk-run-state"
41
- [[ "$output" == *"cost_usd=7.00"* ]]
42
- [[ "$output" == *"tokens=~2.0M"* ]]
43
- }
44
-
45
- @test "tickets are sorted by descending cost" {
46
- mk_iter "iter1-p010.json" 3.00 60000 100000
47
- mk_iter "iter1-p020.json" 30.00 60000 100000
48
- run bash "$SCRIPT" "$DIR/.afk-run-state"
49
- # P020 (30.00) must appear before P010 (3.00)
50
- [[ "$(echo "$output" | grep -n P020 | cut -d: -f1)" -lt "$(echo "$output" | grep -n P010 | cut -d: -f1)" ]]
51
- }
52
-
53
- @test "handles JSON-array (event-stream) shape, not just a single object" {
54
- cat > "$DIR/.afk-run-state/iter1-p050.json" <<'EOF'
55
- [{"type":"system"},{"type":"result","total_cost_usd":9.00,"duration_ms":120000,"usage":{"input_tokens":3000000,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0}}]
56
- EOF
57
- run bash "$SCRIPT" "$DIR/.afk-run-state"
58
- [[ "$output" == *"P050"* ]]
59
- [[ "$output" == *"cost_usd=9.00"* ]]
60
- }
61
-
62
- @test "files without a pNNN token are ignored" {
63
- mk_iter "drain-push.json" 99.00 60000 100000
64
- cp "$DIR/.afk-run-state/drain-push.json" "$DIR/.afk-run-state/work-problems-session-totals.json"
65
- run bash "$SCRIPT" "$DIR/.afk-run-state"
66
- [ -z "$output" ]
67
- }
68
-
69
- @test "files without total_cost_usd are skipped" {
70
- echo '{"pid": 1234, "start": 999}' > "$DIR/.afk-run-state/iter1-p077.json"
71
- run bash "$SCRIPT" "$DIR/.afk-run-state"
72
- [ -z "$output" ]
73
- }
74
-
75
- @test "missing afk dir exits 0 with no output" {
76
- run bash "$SCRIPT" "$DIR/nonexistent"
77
- [ "$status" -eq 0 ]
78
- [ -z "$output" ]
79
- }
80
-
81
- # --- render / write modes (ADR-067 item 2 + 2a source flag) ---
82
-
83
- mk_ticket() { # mk_ticket <filename> <status> e.g. mk_ticket 087-foo.md Open
84
- cat > "$DIR/$1" <<EOF
85
- # Problem 087: Example
86
-
87
- **Status**: $2
88
- **Priority**: 6 (Medium)
89
- **Effort**: M
90
-
91
- ## Description
92
-
93
- Body.
94
-
95
- ## Related
96
-
97
- - none
98
- EOF
99
- }
100
-
101
- @test "--render prints an Effort Tally section with authoritative cost + best-effort tokens" {
102
- mk_ticket "087-foo.md" Open
103
- mk_iter "iter1-p087.json" 12.50 600000 2000000
104
- run bash "$SCRIPT" --render "$DIR/087-foo.md" "$DIR/.afk-run-state"
105
- [ "$status" -eq 0 ]
106
- [[ "$output" == *"## Effort Tally"* ]]
107
- [[ "$output" == *"AUTO-GENERATED"* ]]
108
- [[ "$output" == *'$12.50'* ]]
109
- [[ "$output" == *"~2.0M"* ]]
110
- }
111
-
112
- @test "--render buckets an Open ticket under RCA" {
113
- mk_ticket "087-foo.md" Open
114
- mk_iter "iter1-p087.json" 5.00 60000 100000
115
- run bash "$SCRIPT" --render "$DIR/087-foo.md" "$DIR/.afk-run-state"
116
- [[ "$output" == *"RCA"* ]]
117
- [[ "$output" != *"| RFC |"* ]]
118
- }
119
-
120
- @test "--render buckets a Known Error ticket under RFC" {
121
- mk_ticket "087-foo.md" "Known Error"
122
- mk_iter "iter1-p087.json" 5.00 60000 100000
123
- run bash "$SCRIPT" --render "$DIR/087-foo.md" "$DIR/.afk-run-state"
124
- [[ "$output" == *"RFC"* ]]
125
- [[ "$output" != *"| RCA |"* ]]
126
- }
127
-
128
- @test "--render defaults source to afk-backfill; --source live-iter flips it" {
129
- mk_ticket "087-foo.md" Open
130
- mk_iter "iter1-p087.json" 5.00 60000 100000
131
- run bash "$SCRIPT" --render "$DIR/087-foo.md" "$DIR/.afk-run-state"
132
- [[ "$output" == *"source: afk-backfill"* ]]
133
- run bash "$SCRIPT" --render --source live-iter "$DIR/087-foo.md" "$DIR/.afk-run-state"
134
- [[ "$output" == *"source: live-iter"* ]]
135
- }
136
-
137
- @test "--write injects the section into the ticket and is idempotent" {
138
- mk_ticket "087-foo.md" Open
139
- mk_iter "iter1-p087.json" 5.00 60000 100000
140
- run bash "$SCRIPT" --write "$DIR/087-foo.md" "$DIR/.afk-run-state"
141
- [ "$status" -eq 0 ]
142
- grep -q "## Effort Tally" "$DIR/087-foo.md"
143
- # original body preserved
144
- grep -q "^## Description" "$DIR/087-foo.md"
145
- # idempotent: second run produces no diff
146
- cp "$DIR/087-foo.md" "$DIR/087-foo.before"
147
- run bash "$SCRIPT" --write "$DIR/087-foo.md" "$DIR/.afk-run-state"
148
- run diff "$DIR/087-foo.before" "$DIR/087-foo.md"
149
- [ "$status" -eq 0 ]
150
- # exactly one section (no duplication)
151
- [ "$(grep -c '^## Effort Tally' "$DIR/087-foo.md")" -eq 1 ]
152
- }
153
-
154
- @test "--write lazy-empties: a ticket with zero iters gets no section" {
155
- mk_ticket "099-bar.md" Open
156
- run bash "$SCRIPT" --write "$DIR/099-bar.md" "$DIR/.afk-run-state"
157
- [ "$status" -eq 0 ]
158
- ! grep -q "## Effort Tally" "$DIR/099-bar.md"
159
- }
160
-
161
- @test "--write removes a stale section when iters disappear (lazy-empty on re-run)" {
162
- mk_ticket "087-foo.md" Open
163
- mk_iter "iter1-p087.json" 5.00 60000 100000
164
- bash "$SCRIPT" --write "$DIR/087-foo.md" "$DIR/.afk-run-state"
165
- grep -q "## Effort Tally" "$DIR/087-foo.md"
166
- rm "$DIR/.afk-run-state/iter1-p087.json"
167
- run bash "$SCRIPT" --write "$DIR/087-foo.md" "$DIR/.afk-run-state"
168
- [ "$status" -eq 0 ]
169
- ! grep -q "## Effort Tally" "$DIR/087-foo.md"
170
- }