@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,82 +0,0 @@
1
- #!/usr/bin/env bats
2
- # Behavioural test for check-rfc-stories-ratified.sh (ADR-090 — an RFC may
3
- # reference only RATIFIED stories). DRIFT-AWARE via the shared lazy-fingerprint
4
- # lib: a listed story counts only when it is `confirmed` AND its stored
5
- # oversight-hash matches current content. Composes with check-rfc-has-stories
6
- # (ADR-089): has-stories checks >=1 exists; this checks each listed one is ratified.
7
- #
8
- # @adr ADR-090
9
- # @problem P404 (implement ADR-089 + ADR-090) — Phase 2
10
-
11
- setup() {
12
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
13
- SCRIPT="${REPO_ROOT}/packages/itil/scripts/check-rfc-stories-ratified.sh"
14
- MARK="${REPO_ROOT}/packages/itil/scripts/mark-story-oversight-confirmed.sh"
15
- TMPD="$(mktemp -d)"
16
- mkdir -p "$TMPD/stories/accepted"
17
- }
18
- teardown() { rm -rf "$TMPD"; }
19
-
20
- mkstory() { # $1=id $2=confirmed|unconfirmed|none
21
- local f="$TMPD/stories/accepted/$1-x.md"
22
- case "$2" in
23
- confirmed) printf -- '---\nstatus: accepted\n---\n# %s\n' "$1" > "$f"; bash "$MARK" "$f" ;;
24
- unconfirmed) printf -- '---\nstatus: accepted\nhuman-oversight: unconfirmed\n---\n# %s\n' "$1" > "$f" ;;
25
- none) printf -- '---\nstatus: accepted\n---\n# %s\n' "$1" > "$f" ;;
26
- esac
27
- }
28
- mkrfc() { printf -- '---\nstatus: proposed\nstories: [%s]\n---\n# rfc\n' "$1" > "$TMPD/RFC.proposed.md"; }
29
-
30
- @test "check-rfc-stories-ratified: empty stories: [] passes vacuously (exit 0)" {
31
- printf -- '---\nstatus: proposed\nstories: []\n---\n# rfc\n' > "$TMPD/RFC.proposed.md"
32
- run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
33
- [ "$status" -eq 0 ]
34
- }
35
-
36
- @test "check-rfc-stories-ratified: a ratified (confirmed+hash) story is ACCEPTED (exit 0)" {
37
- mkstory STORY-020 confirmed
38
- mkrfc STORY-020
39
- run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
40
- [ "$status" -eq 0 ]
41
- }
42
-
43
- @test "check-rfc-stories-ratified: an unconfirmed story is REJECTED" {
44
- mkstory STORY-020 unconfirmed
45
- mkrfc STORY-020
46
- run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
47
- [ "$status" -ne 0 ]
48
- [[ "$output" == *"STORY-020"* ]]
49
- }
50
-
51
- @test "check-rfc-stories-ratified: a story with NO oversight marker is REJECTED" {
52
- mkstory STORY-020 none
53
- mkrfc STORY-020
54
- run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
55
- [ "$status" -ne 0 ]
56
- [[ "$output" == *"STORY-020"* ]]
57
- }
58
-
59
- @test "check-rfc-stories-ratified: a confirmed-but-DRIFTED story is REJECTED" {
60
- mkstory STORY-020 confirmed
61
- printf '\nedit after ratify\n' >> "$TMPD/stories/accepted/STORY-020-x.md"
62
- mkrfc STORY-020
63
- run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
64
- [ "$status" -ne 0 ]
65
- [[ "$output" == *"STORY-020"* ]]
66
- }
67
-
68
- @test "check-rfc-stories-ratified: a missing story file is REJECTED" {
69
- mkrfc STORY-099
70
- run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
71
- [ "$status" -ne 0 ]
72
- [[ "$output" == *"STORY-099"* ]]
73
- }
74
-
75
- @test "check-rfc-stories-ratified: mixed — one ratified one unconfirmed is REJECTED" {
76
- mkstory STORY-020 confirmed
77
- mkstory STORY-021 unconfirmed
78
- mkrfc "STORY-020, STORY-021"
79
- run bash "$SCRIPT" "$TMPD/RFC.proposed.md" "$TMPD/stories"
80
- [ "$status" -ne 0 ]
81
- [[ "$output" == *"STORY-021"* ]]
82
- }
@@ -1,503 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # @problem P249 — no process for issue reporters to check for responses
4
- # (symmetric gap to ADR-062 inbound discovery). Phase 1: us-as-upstream-
5
- # reporter side — scan local tickets for `## Reported Upstream` back-
6
- # link sections (written by `/wr-itil:report-upstream` Step 7), poll
7
- # each upstream issue via `gh issue view`, diff against cache, surface
8
- # new comments / state changes / label changes.
9
- #
10
- # Contract: `check-upstream-responses.sh [--problems-dir <dir>]
11
- # [--cache-file <path>] [--audit-log <path>] [--force-recheck]
12
- # [--ticket P<NNN>] [--gh-bin <path>]` is a foreground governance
13
- # script that polls upstream issues, writes cache + audit-log, and
14
- # emits a structured one-line-per-ticket report to stdout.
15
- #
16
- # Read-soft externally: only `gh issue view` (read-only) — no
17
- # `gh issue comment` / `gh issue create`. Does NOT trip ADR-028
18
- # external-comms gate. AFK-safe.
19
- #
20
- # Cache: `docs/problems/.outbound-responses-cache.json` (mirrors
21
- # ADR-062 inbound cache shape under same dir per ADR-031 §"Cache
22
- # files live under docs/problems/"). Committed for replay
23
- # determinism; rebuild via --force-recheck.
24
- #
25
- # Audit-log: `docs/audits/outbound-responses-log.md` (mirrors
26
- # ADR-062 inbound audit-log under docs/audits/ per CLAUDE.md P131).
27
- #
28
- # Exit codes:
29
- # 0 = success (zero or more new responses surfaced; check stdout
30
- # for per-ticket lines)
31
- # 1 = error (cache file malformed, upstream URL malformed, gh CLI
32
- # missing, problems-dir not found)
33
- # 2 = partial — some upstream polls failed (network / 404); the
34
- # successful ones are still written to cache + audit-log
35
- #
36
- # Structured stdout format (≤ 150 bytes per line per ADR-038
37
- # progressive-disclosure budget):
38
- # NEW P<NNN> <url> state=<state> new-comments=<N>
39
- # STATE P<NNN> <url> state=<old>→<new>
40
- # LABEL P<NNN> <url> labels-added=<csv> labels-removed=<csv>
41
- # NONE P<NNN> <url> no-change-since=<last-checked>
42
- # SKIP P<NNN> reason=no-reported-upstream-section
43
- # FAIL P<NNN> <url> reason=<gh-error-short>
44
- #
45
- # @adr ADR-014 (governance skills commit their own work — cache +
46
- # audit-log ride one commit per pass)
47
- # @adr ADR-024 (back-link source of truth — `## Reported Upstream`
48
- # URL field is what this script reads)
49
- # @adr ADR-031 (cache file placement under docs/problems/)
50
- # @adr ADR-032 (foreground synchronous skill — no AskUserQuestion)
51
- # @adr ADR-038 (progressive disclosure — per-row byte budget on diff)
52
- # @adr ADR-049 (script invoked via wr-itil-check-upstream-responses
53
- # bin shim from SKILL.md)
54
- # @adr ADR-062 (inbound discovery pattern — this script is the
55
- # outbound symmetric counterpart)
56
- # @jtbd JTBD-004 (cross-repo coordination — primary anchor)
57
- # @jtbd JTBD-006 (AFK-safe surface)
58
- # @jtbd JTBD-001 (governance without slowing down — eliminates
59
- # manual upstream polling)
60
- # @jtbd JTBD-201 (audit trail — outbound-responses-log.md replay)
61
-
62
- setup() {
63
- SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
64
- SCRIPT="$SCRIPTS_DIR/check-upstream-responses.sh"
65
- FIXTURE_DIR="$(mktemp -d)"
66
- PROBLEMS_DIR="$FIXTURE_DIR/problems"
67
- AUDITS_DIR="$FIXTURE_DIR/audits"
68
- GHFAKE_DIR="$FIXTURE_DIR/ghfake"
69
- mkdir -p "$PROBLEMS_DIR" "$AUDITS_DIR" "$GHFAKE_DIR"
70
- CACHE_FILE="$PROBLEMS_DIR/.outbound-responses-cache.json"
71
- AUDIT_LOG="$AUDITS_DIR/outbound-responses-log.md"
72
- }
73
-
74
- teardown() {
75
- rm -rf "$FIXTURE_DIR"
76
- }
77
-
78
- # ── Helper: install a fake `gh` binary that prints a canned JSON
79
- # ── response keyed by upstream URL. Tests prime $GHFAKE_DIR/<url-hash>
80
- # ── with the JSON they want returned.
81
-
82
- make_fake_gh() {
83
- cat > "$GHFAKE_DIR/gh" <<'EOF'
84
- #!/usr/bin/env bash
85
- # Fake gh shim. Recognised invocations:
86
- # gh issue view <url> --json comments,state,labels,updatedAt
87
- # Looks up canned response by url-hash from $GHFAKE_DATA dir.
88
- if [ "$1" = "issue" ] && [ "$2" = "view" ]; then
89
- URL="$3"
90
- HASH=$(echo -n "$URL" | shasum | awk '{print $1}')
91
- if [ -f "$GHFAKE_DATA/$HASH.json" ]; then
92
- cat "$GHFAKE_DATA/$HASH.json"
93
- exit 0
94
- else
95
- echo "could not resolve issue: $URL" >&2
96
- exit 1
97
- fi
98
- fi
99
- echo "fake-gh: unrecognised invocation: $*" >&2
100
- exit 1
101
- EOF
102
- chmod +x "$GHFAKE_DIR/gh"
103
- }
104
-
105
- prime_gh_response() {
106
- local url="$1"
107
- local payload="$2"
108
- local hash
109
- hash=$(echo -n "$url" | shasum | awk '{print $1}')
110
- printf '%s' "$payload" > "$GHFAKE_DIR/$hash.json"
111
- }
112
-
113
- # ── Existence + executable ──────────────────────────────────────────────────
114
-
115
- @test "check-upstream-responses: script exists" {
116
- [ -f "$SCRIPT" ]
117
- }
118
-
119
- @test "check-upstream-responses: script is executable" {
120
- [ -x "$SCRIPT" ]
121
- }
122
-
123
- # ── Behavioural: ticket with `## Reported Upstream` is polled ───────────────
124
-
125
- @test "ticket with Reported Upstream section is polled and surfaces NEW state on first check" {
126
- cat > "$PROBLEMS_DIR/100-foo.open.md" <<'EOF'
127
- # Problem 100: Foo
128
-
129
- **Status**: Open
130
-
131
- ## Description
132
-
133
- Some description.
134
-
135
- ## Reported Upstream
136
-
137
- - **URL**: https://github.com/example/repo/issues/42
138
- - **Reported**: 2026-05-01
139
- - **Template used**: bug_report.yml
140
- - **Disclosure path**: public issue
141
- - **Cross-reference confirmed**: yes
142
- EOF
143
-
144
- make_fake_gh
145
- prime_gh_response "https://github.com/example/repo/issues/42" '{"state":"OPEN","comments":[{"id":1,"author":{"login":"someone"},"createdAt":"2026-05-10T10:00:00Z","body":"thanks"}],"labels":[{"name":"triage"}],"updatedAt":"2026-05-10T10:00:00Z"}'
146
-
147
- export GHFAKE_DATA="$GHFAKE_DIR"
148
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
149
- [ "$status" -eq 0 ]
150
- echo "$output" | grep -qE "^NEW *P100"
151
- echo "$output" | grep -q "https://github.com/example/repo/issues/42"
152
- }
153
-
154
- # ── Behavioural: ticket without Reported Upstream is skipped ────────────────
155
-
156
- @test "ticket without Reported Upstream section is skipped" {
157
- cat > "$PROBLEMS_DIR/101-bar.open.md" <<'EOF'
158
- # Problem 101: Bar
159
-
160
- **Status**: Open
161
-
162
- ## Description
163
-
164
- No upstream link here.
165
- EOF
166
-
167
- make_fake_gh
168
- export GHFAKE_DATA="$GHFAKE_DIR"
169
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
170
- [ "$status" -eq 0 ]
171
- # P101 should not appear in output at all (no Reported Upstream → silently skipped).
172
- ! echo "$output" | grep -q "P101"
173
- }
174
-
175
- # ── Behavioural: cache fresh → no new responses surfaced ────────────────────
176
-
177
- @test "ticket with cached state matching upstream surfaces NONE" {
178
- cat > "$PROBLEMS_DIR/102-baz.open.md" <<'EOF'
179
- # Problem 102: Baz
180
-
181
- **Status**: Open
182
-
183
- ## Reported Upstream
184
-
185
- - **URL**: https://github.com/example/repo/issues/77
186
- - **Reported**: 2026-05-01
187
- - **Template used**: structured default
188
- - **Disclosure path**: public issue
189
- - **Cross-reference confirmed**: yes
190
- EOF
191
-
192
- # Seed cache to match upstream state.
193
- cat > "$CACHE_FILE" <<'EOF'
194
- {
195
- "last_checked": "2026-05-15T00:00:00Z",
196
- "tickets": {
197
- "P102": {
198
- "upstream_url": "https://github.com/example/repo/issues/77",
199
- "last_checked_at": "2026-05-15T00:00:00Z",
200
- "last_seen_state": "OPEN",
201
- "last_seen_comment_count": 3,
202
- "last_seen_labels": ["triage"],
203
- "last_seen_updated_at": "2026-05-14T12:00:00Z"
204
- }
205
- }
206
- }
207
- EOF
208
-
209
- make_fake_gh
210
- prime_gh_response "https://github.com/example/repo/issues/77" '{"state":"OPEN","comments":[{"id":1},{"id":2},{"id":3}],"labels":[{"name":"triage"}],"updatedAt":"2026-05-14T12:00:00Z"}'
211
-
212
- export GHFAKE_DATA="$GHFAKE_DIR"
213
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
214
- [ "$status" -eq 0 ]
215
- echo "$output" | grep -qE "^NONE *P102"
216
- }
217
-
218
- # ── Behavioural: new comments surface as NEW with delta count ───────────────
219
-
220
- @test "ticket with new comments since last check surfaces NEW with count" {
221
- cat > "$PROBLEMS_DIR/103-qux.open.md" <<'EOF'
222
- # Problem 103: Qux
223
-
224
- **Status**: Open
225
-
226
- ## Reported Upstream
227
-
228
- - **URL**: https://github.com/example/repo/issues/88
229
- - **Reported**: 2026-05-01
230
- - **Template used**: bug_report.yml
231
- - **Disclosure path**: public issue
232
- - **Cross-reference confirmed**: yes
233
- EOF
234
-
235
- cat > "$CACHE_FILE" <<'EOF'
236
- {
237
- "last_checked": "2026-05-15T00:00:00Z",
238
- "tickets": {
239
- "P103": {
240
- "upstream_url": "https://github.com/example/repo/issues/88",
241
- "last_checked_at": "2026-05-15T00:00:00Z",
242
- "last_seen_state": "OPEN",
243
- "last_seen_comment_count": 1,
244
- "last_seen_labels": [],
245
- "last_seen_updated_at": "2026-05-14T12:00:00Z"
246
- }
247
- }
248
- }
249
- EOF
250
-
251
- make_fake_gh
252
- prime_gh_response "https://github.com/example/repo/issues/88" '{"state":"OPEN","comments":[{"id":1},{"id":2},{"id":3}],"labels":[],"updatedAt":"2026-05-17T08:00:00Z"}'
253
-
254
- export GHFAKE_DATA="$GHFAKE_DIR"
255
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
256
- [ "$status" -eq 0 ]
257
- echo "$output" | grep -qE "^NEW *P103"
258
- echo "$output" | grep -q "new-comments=2"
259
- }
260
-
261
- # ── Behavioural: state change surfaces STATE marker ─────────────────────────
262
-
263
- @test "ticket with state change OPEN to CLOSED surfaces STATE marker" {
264
- cat > "$PROBLEMS_DIR/104-baz.open.md" <<'EOF'
265
- # Problem 104: Baz
266
-
267
- **Status**: Open
268
-
269
- ## Reported Upstream
270
-
271
- - **URL**: https://github.com/example/repo/issues/99
272
- - **Reported**: 2026-05-01
273
- - **Template used**: structured default
274
- - **Disclosure path**: public issue
275
- - **Cross-reference confirmed**: yes
276
- EOF
277
-
278
- cat > "$CACHE_FILE" <<'EOF'
279
- {
280
- "last_checked": "2026-05-15T00:00:00Z",
281
- "tickets": {
282
- "P104": {
283
- "upstream_url": "https://github.com/example/repo/issues/99",
284
- "last_checked_at": "2026-05-15T00:00:00Z",
285
- "last_seen_state": "OPEN",
286
- "last_seen_comment_count": 2,
287
- "last_seen_labels": [],
288
- "last_seen_updated_at": "2026-05-14T12:00:00Z"
289
- }
290
- }
291
- }
292
- EOF
293
-
294
- make_fake_gh
295
- prime_gh_response "https://github.com/example/repo/issues/99" '{"state":"CLOSED","comments":[{"id":1},{"id":2}],"labels":[],"updatedAt":"2026-05-17T08:00:00Z"}'
296
-
297
- export GHFAKE_DATA="$GHFAKE_DIR"
298
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
299
- [ "$status" -eq 0 ]
300
- echo "$output" | grep -qE "^STATE *P104"
301
- echo "$output" | grep -q "state=OPEN.*CLOSED"
302
- }
303
-
304
- # ── Behavioural: cache file is created/updated after a pass ─────────────────
305
-
306
- @test "cache file is written with the latest state after the pass" {
307
- cat > "$PROBLEMS_DIR/105-quux.open.md" <<'EOF'
308
- # Problem 105: Quux
309
-
310
- **Status**: Open
311
-
312
- ## Reported Upstream
313
-
314
- - **URL**: https://github.com/example/repo/issues/55
315
- - **Reported**: 2026-05-01
316
- - **Template used**: structured default
317
- - **Disclosure path**: public issue
318
- - **Cross-reference confirmed**: yes
319
- EOF
320
-
321
- make_fake_gh
322
- prime_gh_response "https://github.com/example/repo/issues/55" '{"state":"OPEN","comments":[{"id":1},{"id":2}],"labels":[{"name":"triage"},{"name":"bug"}],"updatedAt":"2026-05-17T08:00:00Z"}'
323
-
324
- export GHFAKE_DATA="$GHFAKE_DIR"
325
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
326
- [ "$status" -eq 0 ]
327
- [ -f "$CACHE_FILE" ]
328
- # Cache file contains P105 entry with the current state.
329
- grep -q '"P105"' "$CACHE_FILE"
330
- grep -q '"upstream_url": *"https://github.com/example/repo/issues/55"' "$CACHE_FILE"
331
- grep -q '"last_seen_state": *"OPEN"' "$CACHE_FILE"
332
- }
333
-
334
- # ── Behavioural: audit-log file is appended after the pass ──────────────────
335
-
336
- @test "audit-log file is appended with a timestamped pass heading" {
337
- cat > "$PROBLEMS_DIR/106-corge.open.md" <<'EOF'
338
- # Problem 106: Corge
339
-
340
- **Status**: Open
341
-
342
- ## Reported Upstream
343
-
344
- - **URL**: https://github.com/example/repo/issues/66
345
- - **Reported**: 2026-05-01
346
- - **Template used**: structured default
347
- - **Disclosure path**: public issue
348
- - **Cross-reference confirmed**: yes
349
- EOF
350
-
351
- make_fake_gh
352
- prime_gh_response "https://github.com/example/repo/issues/66" '{"state":"OPEN","comments":[],"labels":[],"updatedAt":"2026-05-17T08:00:00Z"}'
353
-
354
- export GHFAKE_DATA="$GHFAKE_DIR"
355
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
356
- [ "$status" -eq 0 ]
357
- [ -f "$AUDIT_LOG" ]
358
- # Audit-log contains a heading line matching the ISO timestamp pattern + pass summary.
359
- grep -qE '^## [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z' "$AUDIT_LOG"
360
- }
361
-
362
- # ── Behavioural: --ticket filter restricts to one ticket ────────────────────
363
-
364
- @test "--ticket filter restricts polling to the named ticket only" {
365
- cat > "$PROBLEMS_DIR/107-grault.open.md" <<'EOF'
366
- # Problem 107: Grault
367
-
368
- ## Reported Upstream
369
-
370
- - **URL**: https://github.com/example/repo/issues/107
371
- - **Reported**: 2026-05-01
372
- - **Template used**: structured default
373
- - **Disclosure path**: public issue
374
- - **Cross-reference confirmed**: yes
375
- EOF
376
-
377
- cat > "$PROBLEMS_DIR/108-garply.open.md" <<'EOF'
378
- # Problem 108: Garply
379
-
380
- ## Reported Upstream
381
-
382
- - **URL**: https://github.com/example/repo/issues/108
383
- - **Reported**: 2026-05-01
384
- - **Template used**: structured default
385
- - **Disclosure path**: public issue
386
- - **Cross-reference confirmed**: yes
387
- EOF
388
-
389
- make_fake_gh
390
- prime_gh_response "https://github.com/example/repo/issues/107" '{"state":"OPEN","comments":[],"labels":[],"updatedAt":"2026-05-17T08:00:00Z"}'
391
- prime_gh_response "https://github.com/example/repo/issues/108" '{"state":"OPEN","comments":[],"labels":[],"updatedAt":"2026-05-17T08:00:00Z"}'
392
-
393
- export GHFAKE_DATA="$GHFAKE_DIR"
394
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh" --ticket P107
395
- [ "$status" -eq 0 ]
396
- echo "$output" | grep -q "P107"
397
- ! echo "$output" | grep -q "P108"
398
- }
399
-
400
- # ── Behavioural: dual-tolerant subdir layout (RFC-002) ──────────────────────
401
-
402
- @test "ticket in per-state subdir is discovered and polled" {
403
- mkdir -p "$PROBLEMS_DIR/open"
404
- cat > "$PROBLEMS_DIR/open/109-waldo.md" <<'EOF'
405
- # Problem 109: Waldo
406
-
407
- ## Reported Upstream
408
-
409
- - **URL**: https://github.com/example/repo/issues/109
410
- - **Reported**: 2026-05-01
411
- - **Template used**: structured default
412
- - **Disclosure path**: public issue
413
- - **Cross-reference confirmed**: yes
414
- EOF
415
-
416
- make_fake_gh
417
- prime_gh_response "https://github.com/example/repo/issues/109" '{"state":"OPEN","comments":[],"labels":[],"updatedAt":"2026-05-17T08:00:00Z"}'
418
-
419
- export GHFAKE_DATA="$GHFAKE_DIR"
420
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
421
- [ "$status" -eq 0 ]
422
- echo "$output" | grep -q "P109"
423
- }
424
-
425
- # ── Behavioural: gh failure for one URL doesn't kill the pass ───────────────
426
-
427
- @test "gh failure on one URL is surfaced as FAIL; pass continues for other tickets" {
428
- cat > "$PROBLEMS_DIR/110-broken.open.md" <<'EOF'
429
- # Problem 110: Broken
430
-
431
- ## Reported Upstream
432
-
433
- - **URL**: https://github.com/example/repo/issues/110
434
- - **Reported**: 2026-05-01
435
- - **Template used**: structured default
436
- - **Disclosure path**: public issue
437
- - **Cross-reference confirmed**: yes
438
- EOF
439
-
440
- cat > "$PROBLEMS_DIR/111-working.open.md" <<'EOF'
441
- # Problem 111: Working
442
-
443
- ## Reported Upstream
444
-
445
- - **URL**: https://github.com/example/repo/issues/111
446
- - **Reported**: 2026-05-01
447
- - **Template used**: structured default
448
- - **Disclosure path**: public issue
449
- - **Cross-reference confirmed**: yes
450
- EOF
451
-
452
- make_fake_gh
453
- # Only prime 111; 110 will fail.
454
- prime_gh_response "https://github.com/example/repo/issues/111" '{"state":"OPEN","comments":[],"labels":[],"updatedAt":"2026-05-17T08:00:00Z"}'
455
-
456
- export GHFAKE_DATA="$GHFAKE_DIR"
457
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh"
458
- # Exit 2 = partial (some polls failed).
459
- [ "$status" -eq 2 ]
460
- echo "$output" | grep -qE "^FAIL *P110"
461
- echo "$output" | grep -q "P111"
462
- }
463
-
464
- # ── Behavioural: --force-recheck ignores cache and re-emits NEW ─────────────
465
-
466
- @test "--force-recheck re-emits ticket as new even when cache matches" {
467
- cat > "$PROBLEMS_DIR/112-fresh.open.md" <<'EOF'
468
- # Problem 112: Fresh
469
-
470
- ## Reported Upstream
471
-
472
- - **URL**: https://github.com/example/repo/issues/112
473
- - **Reported**: 2026-05-01
474
- - **Template used**: structured default
475
- - **Disclosure path**: public issue
476
- - **Cross-reference confirmed**: yes
477
- EOF
478
-
479
- cat > "$CACHE_FILE" <<'EOF'
480
- {
481
- "last_checked": "2026-05-17T00:00:00Z",
482
- "tickets": {
483
- "P112": {
484
- "upstream_url": "https://github.com/example/repo/issues/112",
485
- "last_checked_at": "2026-05-17T00:00:00Z",
486
- "last_seen_state": "OPEN",
487
- "last_seen_comment_count": 0,
488
- "last_seen_labels": [],
489
- "last_seen_updated_at": "2026-05-17T00:00:00Z"
490
- }
491
- }
492
- }
493
- EOF
494
-
495
- make_fake_gh
496
- prime_gh_response "https://github.com/example/repo/issues/112" '{"state":"OPEN","comments":[],"labels":[],"updatedAt":"2026-05-17T00:00:00Z"}'
497
-
498
- export GHFAKE_DATA="$GHFAKE_DIR"
499
- run "$SCRIPT" --problems-dir "$PROBLEMS_DIR" --cache-file "$CACHE_FILE" --audit-log "$AUDIT_LOG" --gh-bin "$GHFAKE_DIR/gh" --force-recheck
500
- [ "$status" -eq 0 ]
501
- # With --force-recheck, the line is emitted as NEW regardless of cache match.
502
- echo "$output" | grep -qE "^NEW *P112"
503
- }