@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,374 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # @rfc RFC-002 T3 — Bats fixture audit + dual-tolerant assertions
4
- # @adr ADR-031 (Problem-ticket directory layout — per-state subdirs)
5
- # @adr ADR-051 (load-bearing-from-the-start — each SKILL-prescribed
6
- # enumeration pipeline ships with a behavioural enforcement test
7
- # exercised against per-state-layout synthetic fixtures, not later
8
- # by graceful drift discovery during the T5 migration cutover)
9
- # @adr ADR-052 (behavioural-bats default — these tests run the actual
10
- # shell pipelines the SKILL.md sites prescribe against synthetic
11
- # fixtures and assert observable enumeration. They do NOT structurally
12
- # grep SKILL.md prose for the dual-pattern string, which would be
13
- # P081-class structural-test-disguised-as-behavioural and was
14
- # explicitly excluded from T2 per architect finding 3)
15
- # @adr ADR-014 (single-purpose: one mechanical contract — the
16
- # SKILL-prescribed pipelines compose with per-state-layout fixtures)
17
- # @adr ADR-060 (Phase 1 Slice 5 forward-dogfood — T3 commit grain)
18
- # @problem P069 (driving — flat layout unskimmable; the migration this
19
- # contract guards is the relief)
20
- # @problem P081 (no structural-grep on SKILL.md content — this test
21
- # is the behavioural alternative)
22
- # @jtbd JTBD-001 (extended scope — multi-commit RFC-grain coordinated
23
- # change governance; T3 is one of 11 RFC-002 sub-tasks; behavioural
24
- # coverage is how per-edit governance stays trustworthy across the
25
- # migration window)
26
- # @jtbd JTBD-006 (work-backlog-AFK — dual-tolerant pipelines preserve
27
- # AFK-loop continuity during the T2-to-T6 migration window; without
28
- # this contract, mid-migration AFK iterations silently miss tickets
29
- # in the un-migrated layout half)
30
- # @jtbd JTBD-008 (decompose-fix-into-coordinated-changes — RFC-002 T3
31
- # is the load-bearing test artefact for the coordinated-change
32
- # sub-workstream; visible as an RFC-002-T3 entity rather than
33
- # diffusing across 14 existing files per JTBD review)
34
- # @jtbd JTBD-101 (extend-the-suite — adopter projects consuming
35
- # @windyroad/itil at the T2-shipped state must enumerate correctly
36
- # against their flat-layout tickets AND post-auto-migration per-state
37
- # tickets; T3 proves both halves)
38
- #
39
- # Contract: every SKILL.md call site updated in T2 (commit `0795e91`,
40
- # 14 SKILL.md surfaces) prescribes a shell pipeline of canonical shape:
41
- #
42
- # ls docs/problems/*.<state>.md docs/problems/<state>/*.md 2>/dev/null
43
- # ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null
44
- # ls docs/problems/<ID>-*.md docs/problems/*/<ID>-*.md 2>/dev/null
45
- #
46
- # T2's `dual-tolerant-glob-rfc-002-t2.bats` exercises the canonical
47
- # pattern shapes generically. T3 extends that coverage to the
48
- # end-to-end SKILL-prescribed PIPELINES — the next-ID compute pipeline
49
- # (`ls X Y | sed | grep -oE | sort -n | tail -1`), the multi-state
50
- # union form (4-pathspec for open + known-error backlog scan), the
51
- # verifying-state filter as run-retro Step 4a dispatches it, and the
52
- # brace-expansion ID + state-set form report-upstream uses. Each test
53
- # runs the pipeline against three synthetic fixture shapes (flat-only,
54
- # per-state-only, mixed) and asserts observable enumeration.
55
- #
56
- # T6 (post-T5 verification) drops the flat-layout half. This test
57
- # updates at T6 to single-pattern (per-state only), NOT removed — the
58
- # contract narrows but the behavioural enforcement remains.
59
- #
60
- # CONTRACT NOTE: when one half of the dual-pattern has zero matches
61
- # in the current fixture (single-layout fixtures), `ls X Y 2>/dev/null`
62
- # exits nonzero — the unmatched literal pathname propagates to ls's
63
- # argv and `2>/dev/null` only suppresses the stderr noise, not the
64
- # exit code. SKILL.md call sites MUST treat STDOUT emptiness as the
65
- # canonical "no tickets" signal, NOT exit code zero. Test assertions
66
- # probe stdout content via `run` (which absorbs the exit code into
67
- # `$status`); `$status` is asserted only in the empty-fixture and
68
- # missing-ID cases where nonzero exit is the intended contract.
69
-
70
- setup() {
71
- REPO_ROOT="$(mktemp -d)"
72
- cd "$REPO_ROOT"
73
- mkdir -p docs/problems
74
- }
75
-
76
- teardown() {
77
- cd /
78
- rm -rf "$REPO_ROOT"
79
- }
80
-
81
- # ── fixture builders ─────────────────────────────────────────────────────────
82
-
83
- build_flat_layout() {
84
- cat > docs/problems/100-foo.open.md <<'EOF'
85
- # Problem 100: Foo
86
- **Status**: Open
87
- **WSJF**: 5.0
88
- EOF
89
- cat > docs/problems/101-bar.known-error.md <<'EOF'
90
- # Problem 101: Bar
91
- **Status**: Known Error
92
- **WSJF**: 4.0
93
- EOF
94
- cat > docs/problems/102-baz.verifying.md <<'EOF'
95
- # Problem 102: Baz
96
- **Status**: Verification Pending
97
- EOF
98
- cat > docs/problems/103-qux.parked.md <<'EOF'
99
- # Problem 103: Qux
100
- **Status**: Parked
101
- EOF
102
- cat > docs/problems/104-quux.closed.md <<'EOF'
103
- # Problem 104: Quux
104
- **Status**: Closed
105
- EOF
106
- }
107
-
108
- build_per_state_layout() {
109
- mkdir -p docs/problems/open docs/problems/known-error docs/problems/verifying docs/problems/parked docs/problems/closed
110
- cat > docs/problems/open/200-foo2.md <<'EOF'
111
- # Problem 200: Foo2
112
- **Status**: Open
113
- **WSJF**: 6.0
114
- EOF
115
- cat > docs/problems/known-error/201-bar2.md <<'EOF'
116
- # Problem 201: Bar2
117
- **Status**: Known Error
118
- **WSJF**: 3.5
119
- EOF
120
- cat > docs/problems/verifying/202-baz2.md <<'EOF'
121
- # Problem 202: Baz2
122
- **Status**: Verification Pending
123
- EOF
124
- cat > docs/problems/parked/203-qux2.md <<'EOF'
125
- # Problem 203: Qux2
126
- **Status**: Parked
127
- EOF
128
- cat > docs/problems/closed/204-quux2.md <<'EOF'
129
- # Problem 204: Quux2
130
- **Status**: Closed
131
- EOF
132
- }
133
-
134
- build_mixed_layout() {
135
- build_flat_layout
136
- build_per_state_layout
137
- }
138
-
139
- # ── Pipeline 1: Next-ID compute (manage-problem Step 3 + capture-problem Step 3)
140
- #
141
- # SKILL.md prescribes the recursive local_max formula:
142
- # ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null \
143
- # | sed 's|.*/||' \
144
- # | grep -oE '^[0-9]+' \
145
- # | sort -n | tail -1
146
- #
147
- # Architect finding 2 (T2): the recursive enumeration MUST contribute
148
- # tickets from BOTH layouts to max-ID, otherwise a per-state ticket at
149
- # ID 204 is invisible to a flat-only-enumerating capture-problem and
150
- # the next ID re-allocates an already-taken slot. T3 exercises the
151
- # pipeline against per-state-only AND mixed fixtures to prove the
152
- # dual-pathspec composes with the downstream sed/grep/sort pipeline.
153
- # ──────────────────────────────────────────────────────────────────────────────
154
-
155
- @test "next-ID pipeline: flat-only fixture yields max ID 104" {
156
- build_flat_layout
157
- run bash -c "ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null | sed 's|.*/||' | grep -oE '^[0-9]+' | sort -n | tail -1"
158
- [ "$output" = "104" ]
159
- }
160
-
161
- @test "next-ID pipeline: per-state-only fixture yields max ID 204" {
162
- build_per_state_layout
163
- run bash -c "ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null | sed 's|.*/||' | grep -oE '^[0-9]+' | sort -n | tail -1"
164
- [ "$output" = "204" ]
165
- }
166
-
167
- @test "next-ID pipeline: mixed fixture yields max ID 204 (recursive enumeration spans both layouts)" {
168
- # Architect finding 2: a per-state ticket at ID 204 MUST contribute
169
- # to max-ID even when flat-layout 104 also exists. Drop the per-state
170
- # half of the dual-pathspec and this test fails — capture-problem
171
- # would re-allocate ID 105 instead of advancing to 205.
172
- build_mixed_layout
173
- run bash -c "ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null | sed 's|.*/||' | grep -oE '^[0-9]+' | sort -n | tail -1"
174
- [ "$output" = "204" ]
175
- }
176
-
177
- @test "next-ID pipeline: empty fixture yields empty result" {
178
- run bash -c "ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null | sed 's|.*/||' | grep -oE '^[0-9]+' | sort -n | tail -1"
179
- [ -z "$output" ]
180
- }
181
-
182
- # ── Pipeline 2: Open + known-error multi-state union
183
- # (work-problems Step 1, list-problems live scan)
184
- #
185
- # SKILL.md prescribes the 4-pathspec form:
186
- # ls docs/problems/*.open.md docs/problems/*.known-error.md \
187
- # docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null
188
- #
189
- # This is wider than T2's single-state filter — it unions two states
190
- # across two layouts in one ls invocation. The prove-out shape: the
191
- # union enumerates open + known-error from BOTH layouts and excludes
192
- # verifying / parked / closed from BOTH layouts.
193
- # ──────────────────────────────────────────────────────────────────────────────
194
-
195
- @test "open+known-error union: per-state-only fixture enumerates 200 and 201" {
196
- build_per_state_layout
197
- run bash -c 'ls docs/problems/*.open.md docs/problems/*.known-error.md docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null'
198
- [[ "$output" == *"open/200-foo2.md"* ]]
199
- [[ "$output" == *"known-error/201-bar2.md"* ]]
200
- }
201
-
202
- @test "open+known-error union: per-state-only fixture excludes verifying/parked/closed" {
203
- build_per_state_layout
204
- run bash -c 'ls docs/problems/*.open.md docs/problems/*.known-error.md docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null'
205
- [[ "$output" != *"202-baz2"* ]]
206
- [[ "$output" != *"203-qux2"* ]]
207
- [[ "$output" != *"204-quux2"* ]]
208
- }
209
-
210
- @test "open+known-error union: mixed fixture enumerates all four (100, 101, 200, 201)" {
211
- build_mixed_layout
212
- run bash -c 'ls docs/problems/*.open.md docs/problems/*.known-error.md docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null'
213
- [[ "$output" == *"100-foo.open.md"* ]]
214
- [[ "$output" == *"101-bar.known-error.md"* ]]
215
- [[ "$output" == *"open/200-foo2.md"* ]]
216
- [[ "$output" == *"known-error/201-bar2.md"* ]]
217
- }
218
-
219
- @test "open+known-error union: mixed fixture excludes verifying/parked/closed from BOTH layouts" {
220
- build_mixed_layout
221
- run bash -c 'ls docs/problems/*.open.md docs/problems/*.known-error.md docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null'
222
- [[ "$output" != *"102-baz"* ]]
223
- [[ "$output" != *"103-qux"* ]]
224
- [[ "$output" != *"104-quux"* ]]
225
- [[ "$output" != *"202-baz2"* ]]
226
- [[ "$output" != *"203-qux2"* ]]
227
- [[ "$output" != *"204-quux2"* ]]
228
- }
229
-
230
- # ── Pipeline 3: Verifying-state filter (run-retro Step 4a)
231
- #
232
- # SKILL.md prescribes:
233
- # ls docs/problems/*.verifying.md docs/problems/verifying/*.md 2>/dev/null
234
- #
235
- # run-retro Step 4a uses this to surface verification-close candidates
236
- # from the session-context evidence. T3 proves the pipeline finds the
237
- # verifying ticket in BOTH layouts independently.
238
- # ──────────────────────────────────────────────────────────────────────────────
239
-
240
- @test "verifying-state pipeline: flat-only fixture finds 102" {
241
- build_flat_layout
242
- run bash -c 'ls docs/problems/*.verifying.md docs/problems/verifying/*.md 2>/dev/null'
243
- [[ "$output" == *"102-baz.verifying.md"* ]]
244
- }
245
-
246
- @test "verifying-state pipeline: per-state-only fixture finds 202" {
247
- build_per_state_layout
248
- run bash -c 'ls docs/problems/*.verifying.md docs/problems/verifying/*.md 2>/dev/null'
249
- [[ "$output" == *"verifying/202-baz2.md"* ]]
250
- }
251
-
252
- @test "verifying-state pipeline: mixed fixture finds 102 AND 202" {
253
- build_mixed_layout
254
- run bash -c 'ls docs/problems/*.verifying.md docs/problems/verifying/*.md 2>/dev/null'
255
- [[ "$output" == *"102-baz.verifying.md"* ]]
256
- [[ "$output" == *"verifying/202-baz2.md"* ]]
257
- }
258
-
259
- # ── Pipeline 4: ID-anchored ticket lookup
260
- # (manage-problem ticket-by-ID, link-incident, close-incident,
261
- # transition-problem Step 2, capture-problem Step 2 dup-detect)
262
- #
263
- # SKILL.md prescribes:
264
- # ls docs/problems/<ID>-*.md docs/problems/*/<ID>-*.md 2>/dev/null
265
- #
266
- # T3 exercises lookup of a known-existing ID across both layouts and
267
- # the missing-ID case (asserts empty stdout + nonzero exit).
268
- # ──────────────────────────────────────────────────────────────────────────────
269
-
270
- @test "ID-anchored pipeline: per-state-only fixture finds 200 in subdir" {
271
- build_per_state_layout
272
- run bash -c 'ls docs/problems/200-*.md docs/problems/*/200-*.md 2>/dev/null'
273
- [[ "$output" == *"open/200-foo2.md"* ]]
274
- }
275
-
276
- @test "ID-anchored pipeline: mixed fixture finds 100 (flat) and 200 (per-state)" {
277
- build_mixed_layout
278
- run bash -c 'ls docs/problems/100-*.md docs/problems/*/100-*.md 2>/dev/null'
279
- [[ "$output" == *"100-foo.open.md"* ]]
280
- run bash -c 'ls docs/problems/200-*.md docs/problems/*/200-*.md 2>/dev/null'
281
- [[ "$output" == *"open/200-foo2.md"* ]]
282
- }
283
-
284
- @test "ID-anchored pipeline: missing ID yields empty stdout + nonzero exit" {
285
- build_per_state_layout
286
- run bash -c 'ls docs/problems/999-*.md docs/problems/*/999-*.md 2>/dev/null'
287
- [ -z "$output" ]
288
- [ "$status" -ne 0 ]
289
- }
290
-
291
- # ── Pipeline 5: Brace-expansion ID + state-set (report-upstream)
292
- #
293
- # SKILL.md prescribes:
294
- # ls docs/problems/<ID>-*.{open,known-error,verifying,closed}.md \
295
- # docs/problems/*/<ID>-*.md 2>/dev/null
296
- #
297
- # The flat half restricts to a state-set (excludes parked); the
298
- # per-state half is unrestricted (no state filter). T3 proves the
299
- # brace expansion composes with the per-state pathspec without false
300
- # positives across the migration window.
301
- # ──────────────────────────────────────────────────────────────────────────────
302
-
303
- @test "brace-id-state pipeline: per-state-only fixture finds 200 via per-state half" {
304
- build_per_state_layout
305
- run bash -c 'ls docs/problems/200-*.{open,known-error,verifying,closed}.md docs/problems/*/200-*.md 2>/dev/null'
306
- [[ "$output" == *"open/200-foo2.md"* ]]
307
- }
308
-
309
- @test "brace-id-state pipeline: mixed fixture finds flat 100 (open) and per-state 200 (open)" {
310
- build_mixed_layout
311
- run bash -c 'ls docs/problems/100-*.{open,known-error,verifying,closed}.md docs/problems/*/100-*.md 2>/dev/null'
312
- [[ "$output" == *"100-foo.open.md"* ]]
313
- run bash -c 'ls docs/problems/200-*.{open,known-error,verifying,closed}.md docs/problems/*/200-*.md 2>/dev/null'
314
- [[ "$output" == *"open/200-foo2.md"* ]]
315
- }
316
-
317
- @test "brace-id-state pipeline: parked ticket excluded from flat half but matched in per-state half" {
318
- # Flat 103 is parked (excluded by the brace state-set); per-state 203
319
- # is parked but the per-state pathspec is state-unrestricted, so it
320
- # MUST surface. This is the contract architect finding 2 codifies.
321
- build_mixed_layout
322
- run bash -c 'ls docs/problems/103-*.{open,known-error,verifying,closed}.md docs/problems/*/103-*.md 2>/dev/null'
323
- [[ "$output" != *"103-qux.parked.md"* ]]
324
- run bash -c 'ls docs/problems/203-*.{open,known-error,verifying,closed}.md docs/problems/*/203-*.md 2>/dev/null'
325
- [[ "$output" == *"parked/203-qux2.md"* ]]
326
- }
327
-
328
- # ── Pipeline 6: Closed-ticket ID-anchored lookup
329
- # (review-problems Step 5: closed-section rendering uses
330
- # `docs/problems/*.closed.md docs/problems/closed/*.md`)
331
- # ──────────────────────────────────────────────────────────────────────────────
332
-
333
- @test "closed-ticket pipeline: mixed fixture enumerates flat 104 and per-state 204" {
334
- build_mixed_layout
335
- run bash -c 'ls docs/problems/*.closed.md docs/problems/closed/*.md 2>/dev/null'
336
- [[ "$output" == *"104-quux.closed.md"* ]]
337
- [[ "$output" == *"closed/204-quux2.md"* ]]
338
- }
339
-
340
- @test "closed-ticket pipeline: per-state-only fixture excludes other-state subdirs" {
341
- build_per_state_layout
342
- run bash -c 'ls docs/problems/*.closed.md docs/problems/closed/*.md 2>/dev/null'
343
- [[ "$output" == *"closed/204-quux2.md"* ]]
344
- [[ "$output" != *"open/200"* ]]
345
- [[ "$output" != *"known-error/201"* ]]
346
- [[ "$output" != *"verifying/202"* ]]
347
- [[ "$output" != *"parked/203"* ]]
348
- }
349
-
350
- # ── Pipeline 7: Parked-state filter (review-problems Step 3 parked section)
351
- # ──────────────────────────────────────────────────────────────────────────────
352
-
353
- @test "parked-state pipeline: mixed fixture enumerates flat 103 and per-state 203" {
354
- build_mixed_layout
355
- run bash -c 'ls docs/problems/*.parked.md docs/problems/parked/*.md 2>/dev/null'
356
- [[ "$output" == *"103-qux.parked.md"* ]]
357
- [[ "$output" == *"parked/203-qux2.md"* ]]
358
- }
359
-
360
- # ── Composition: ls-with-2>/dev/null exit-code semantics
361
- # (mirrors T2's empty-fixture contract; proves the SKILL.md contract
362
- # remains "stdout content is the signal, not exit code")
363
- # ──────────────────────────────────────────────────────────────────────────────
364
-
365
- @test "all pipelines: empty fixture produces empty stdout across every shape" {
366
- run bash -c "ls docs/problems/*.md docs/problems/*/*.md 2>/dev/null | sed 's|.*/||' | grep -oE '^[0-9]+' | sort -n | tail -1"
367
- [ -z "$output" ]
368
- run bash -c 'ls docs/problems/*.open.md docs/problems/*.known-error.md docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null'
369
- [ -z "$output" ]
370
- run bash -c 'ls docs/problems/*.verifying.md docs/problems/verifying/*.md 2>/dev/null'
371
- [ -z "$output" ]
372
- run bash -c 'ls docs/problems/100-*.md docs/problems/*/100-*.md 2>/dev/null'
373
- [ -z "$output" ]
374
- }
@@ -1,190 +0,0 @@
1
- #!/usr/bin/env bats
2
- # Behavioural test for lib/story-oversight.sh — ADR-090 lazy-fingerprint helpers.
3
- # The hash EXCLUDES the marker lines (so writing the marker is idempotent), and
4
- # "ratified" = confirmed marker AND stored hash matches current content. Any
5
- # content edit drifts the hash → not ratified (ADR-090 drift-invalidation).
6
- #
7
- # @adr ADR-090
8
- # @problem P404 (Phase 2)
9
-
10
- setup() {
11
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
12
- LIB="${REPO_ROOT}/packages/itil/lib/story-oversight.sh"
13
- TMPD="$(mktemp -d)"
14
- # shellcheck source=/dev/null
15
- source "$LIB"
16
- }
17
- teardown() { rm -rf "$TMPD"; }
18
-
19
- @test "oversight_content_hash: identical content → identical hash" {
20
- printf '# body\nline two\n' > "$TMPD/a"; printf '# body\nline two\n' > "$TMPD/b"
21
- [ "$(oversight_content_hash "$TMPD/a")" = "$(oversight_content_hash "$TMPD/b")" ]
22
- }
23
-
24
- @test "oversight_content_hash: adding/changing the marker lines does NOT change the hash" {
25
- printf -- '---\nstatus: accepted\n---\n# body\n' > "$TMPD/f"
26
- before="$(oversight_content_hash "$TMPD/f")"
27
- printf -- '---\nstatus: accepted\nhuman-oversight: confirmed\noversight-hash: %064d\n---\n# body\n' 0 > "$TMPD/f"
28
- [ "$before" = "$(oversight_content_hash "$TMPD/f")" ]
29
- }
30
-
31
- @test "oversight_content_hash: a real content edit DOES change the hash" {
32
- printf '# body\n' > "$TMPD/f"; before="$(oversight_content_hash "$TMPD/f")"
33
- printf '# body EDITED\n' > "$TMPD/f"
34
- [ "$before" != "$(oversight_content_hash "$TMPD/f")" ]
35
- }
36
-
37
- @test "oversight_content_hash: ticking an acceptance-criterion checkbox does NOT drift (lifecycle progress)" {
38
- printf -- '---\nstatus: accepted\n---\n- [ ] a criterion\n' > "$TMPD/f"; before="$(oversight_content_hash "$TMPD/f")"
39
- printf -- '---\nstatus: accepted\n---\n- [x] a criterion\n' > "$TMPD/f"
40
- [ "$before" = "$(oversight_content_hash "$TMPD/f")" ]
41
- }
42
-
43
- @test "oversight_content_hash: advancing frontmatter status: does NOT drift (lifecycle)" {
44
- printf -- '---\nstatus: accepted\n---\n# body\n' > "$TMPD/f"; before="$(oversight_content_hash "$TMPD/f")"
45
- printf -- '---\nstatus: done\n---\n# body\n' > "$TMPD/f"
46
- [ "$before" = "$(oversight_content_hash "$TMPD/f")" ]
47
- }
48
-
49
- @test "oversight_content_hash: changing criterion TEXT DOES drift (substance)" {
50
- printf -- '---\nstatus: accepted\n---\n- [ ] original\n' > "$TMPD/f"; before="$(oversight_content_hash "$TMPD/f")"
51
- printf -- '---\nstatus: accepted\n---\n- [ ] a DIFFERENT criterion\n' > "$TMPD/f"
52
- [ "$before" != "$(oversight_content_hash "$TMPD/f")" ]
53
- }
54
-
55
- @test "oversight_content_hash: advancing slice data-status does NOT drift (HTML map lifecycle)" {
56
- printf '<a class="slice" data-story-id="STORY-1" data-status="draft">x</a>\n' > "$TMPD/m.html"; before="$(oversight_content_hash "$TMPD/m.html")"
57
- printf '<a class="slice" data-story-id="STORY-1" data-status="done">x</a>\n' > "$TMPD/m.html"
58
- [ "$before" = "$(oversight_content_hash "$TMPD/m.html")" ]
59
- }
60
-
61
- @test "is_story_map_ratified: confirmed + matching hash → ratified (md)" {
62
- printf -- '---\nstatus: accepted\n---\n# body\n' > "$TMPD/f"
63
- h="$(oversight_content_hash "$TMPD/f")"
64
- printf -- '---\nstatus: accepted\nhuman-oversight: confirmed\noversight-hash: %s\n---\n# body\n' "$h" > "$TMPD/f"
65
- run is_story_map_ratified "$TMPD/f"; [ "$status" -eq 0 ]
66
- }
67
-
68
- @test "is_story_map_ratified: confirmed but STALE hash (drifted) → not ratified" {
69
- printf -- '---\nhuman-oversight: confirmed\noversight-hash: %064d\n---\n# body EDITED AFTER RATIFY\n' 1 > "$TMPD/f"
70
- run is_story_map_ratified "$TMPD/f"; [ "$status" -ne 0 ]
71
- }
72
-
73
- @test "is_story_map_ratified: confirmed with NO hash (legacy hand-ratified) → not ratified" {
74
- printf -- '---\nhuman-oversight: confirmed\n---\n# body\n' > "$TMPD/f"
75
- run is_story_map_ratified "$TMPD/f"; [ "$status" -ne 0 ]
76
- }
77
-
78
- @test "is_story_map_ratified: unconfirmed → not ratified" {
79
- printf -- '---\nhuman-oversight: unconfirmed\n---\n# body\n' > "$TMPD/f"
80
- run is_story_map_ratified "$TMPD/f"; [ "$status" -ne 0 ]
81
- }
82
-
83
- @test "is_story_map_ratified: HTML map confirmed + matching hash → ratified" {
84
- printf '<h1>map</h1>\n' > "$TMPD/m.html"
85
- h="$(oversight_content_hash "$TMPD/m.html")"
86
- printf '<meta name="human-oversight" content="confirmed">\n<meta name="oversight-hash" content="%s">\n<h1>map</h1>\n' "$h" > "$TMPD/m.html"
87
- run is_story_map_ratified "$TMPD/m.html"; [ "$status" -eq 0 ]
88
- }
89
-
90
- # --- P474 / STORY-055: the extraction must not move a single hash --------------
91
- #
92
- # `oversight_content_hash` and `oversight_content_hash_excluding_stories` shared a
93
- # verbatim grep|sed filter, which is why the P474 mirror had to be removed twice
94
- # and why a third copy could have been missed. The filter is now extracted.
95
- #
96
- # The extraction boundary is the whole risk. The two functions' INPUT paths are
97
- # NOT equivalent and must stay that way: one passes the file to grep directly,
98
- # the other round-trips through $(cat) — which strips all trailing newlines —
99
- # then printf '%s\n' re-adds exactly one. So trailing blank lines are preserved
100
- # by the first and collapsed by the second. Extract the filter only; unify the
101
- # input paths "for symmetry" and the hash silently changes for any artefact with
102
- # anomalous trailing whitespace, un-ratifying every stored fingerprint at once.
103
- #
104
- # `_ref_*` below are VERBATIM copies of the pre-extraction implementation. They
105
- # exist to be compared against, so do NOT refactor them to call the new helper —
106
- # that would make the equivalence assertions circular and worthless.
107
-
108
- _ref_hash() {
109
- grep -vE '^(human-oversight|oversight-hash|oversight-basis|status):|<meta[^>]*name="(human-oversight|oversight-hash|oversight-basis|status)"' "$1" \
110
- | sed -E 's/- \[[ xX]\]/- [ ]/g; s/data-status="[^"]*"/data-status=""/g' \
111
- | shasum -a 256 | awk '{print $1}'
112
- }
113
-
114
- _ref_hash_excluding() {
115
- local f="$1"; shift
116
- local filtered id
117
- filtered="$(cat "$f")"
118
- for id in "$@"; do
119
- [ -n "$id" ] || continue
120
- filtered="$(printf '%s\n' "$filtered" | grep -vF "data-story-id=\"${id}\"" || true)"
121
- done
122
- printf '%s\n' "$filtered" \
123
- | grep -vE '^(human-oversight|oversight-hash|oversight-basis|status):|<meta[^>]*name="(human-oversight|oversight-hash|oversight-basis|status)"' \
124
- | sed -E 's/- \[[ xX]\]/- [ ]/g; s/data-status="[^"]*"/data-status=""/g' \
125
- | shasum -a 256 | awk '{print $1}'
126
- }
127
-
128
- @test "extraction: byte-identical to the frozen reference across edge shapes" {
129
- # No final newline.
130
- printf -- '---\nstatus: draft\n---\nbody' > "$TMPD/a"
131
- # Multiple trailing blank lines — the shape the input-path divergence hinges on.
132
- printf -- '---\nstatus: draft\n---\nbody\n\n\n\n' > "$TMPD/b"
133
- # CRLF.
134
- printf -- '---\r\nstatus: draft\r\n---\r\nbody\r\n' > "$TMPD/c"
135
- # Empty file.
136
- : > "$TMPD/d"
137
- # A criterion tick and a data-status, both normalised.
138
- printf -- '- [x] done\n<a data-status="done">x</a>\n' > "$TMPD/e"
139
- # Every excluded key present, in both encodings.
140
- printf -- 'human-oversight: confirmed\noversight-hash: deadbeef\noversight-basis: pure-decomposition\nstatus: accepted\n<meta name="status" content="draft">\nbody\n' > "$TMPD/f"
141
- # Only a newline.
142
- printf '\n' > "$TMPD/g"
143
-
144
- for n in a b c d e f g; do
145
- [ "$(oversight_content_hash "$TMPD/$n")" = "$(_ref_hash "$TMPD/$n")" ] || {
146
- echo "oversight_content_hash diverged on fixture $n"; false
147
- }
148
- done
149
- }
150
-
151
- @test "extraction: map variant byte-identical, including the id-exclusion loop" {
152
- printf -- '<meta name="status" content="draft">\n<a data-story-id="STORY-05">short</a>\n<a data-story-id="STORY-054">long</a>\n<a class="slice" data-status="done">s</a>\nbody\n\n\n' \
153
- > "$TMPD/m.html"
154
- # Zero ids, one id, and the prefix-collision pair — STORY-05 must not strip
155
- # STORY-054's card, which is what the closing-quote anchor buys.
156
- [ "$(oversight_content_hash_excluding_stories "$TMPD/m.html")" = "$(_ref_hash_excluding "$TMPD/m.html")" ]
157
- [ "$(oversight_content_hash_excluding_stories "$TMPD/m.html" STORY-054)" = "$(_ref_hash_excluding "$TMPD/m.html" STORY-054)" ]
158
- [ "$(oversight_content_hash_excluding_stories "$TMPD/m.html" STORY-05)" = "$(_ref_hash_excluding "$TMPD/m.html" STORY-05)" ]
159
- [ "$(oversight_content_hash_excluding_stories "$TMPD/m.html" STORY-05 STORY-054)" = "$(_ref_hash_excluding "$TMPD/m.html" STORY-05 STORY-054)" ]
160
- # And the divergence itself is preserved rather than accidentally unified.
161
- [ "$(oversight_content_hash_excluding_stories "$TMPD/m.html")" != "$(oversight_content_hash "$TMPD/m.html")" ]
162
- }
163
-
164
- # Golden hashes are pinned over FIXTURES, never over live artefacts. Pinning to
165
- # the corpus would redden this suite on every later legitimate artefact edit —
166
- # ADR-052's refactor-blocking failure mode. Their job is to catch the frozen
167
- # reference itself rotting, which the equivalence cases above cannot.
168
- @test "extraction: pinned golden hashes over a canonical fixture" {
169
- printf -- '---\nstatus: draft\nhuman-oversight: confirmed\n---\n\n# STORY-900: canonical\n\n**Reported**: 2026-07-30\n\n- [ ] a criterion\n' \
170
- > "$TMPD/golden.md"
171
- [ "$(oversight_content_hash "$TMPD/golden.md")" = "1a18f2501f7092b64316a1566795e64860f90893c2b4d0d36cd9a783ada21414" ]
172
- }
173
-
174
- @test "extraction: the excluded-key accessor agrees with the operative pattern both ways" {
175
- local keys; keys="$(oversight_excluded_keys)"
176
- [ -n "$keys" ]
177
- # Every accessor key appears as an alternation branch in the operative filter.
178
- for k in $keys; do
179
- grep -qF -- "$k" "$LIB" || { echo "accessor key '$k' absent from the lib pattern"; false; }
180
- done
181
- # And every branch in the operative pattern appears in the accessor — the
182
- # direction that catches a key ADDED to the regex but not to the accessor,
183
- # which would leave the corpus lint blind to it.
184
- local branch
185
- for branch in $(grep -oE '\^\(([a-z-]+\|)+[a-z-]+\):' "$LIB" | head -1 | tr -d '^():' | tr '|' ' '); do
186
- printf '%s\n' "$keys" | grep -qx -- "$branch" || {
187
- echo "pattern branch '$branch' missing from oversight_excluded_keys()"; false
188
- }
189
- done
190
- }