@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,398 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # P170 / Phase 3 P3.1 + Phase 4 P4.2 — behavioural fixture for
4
- # capture-problem Step 1.5b JTBD-trace + persona dispatch. Per ADR-060
5
- # § Phase 3 + Phase 4 in-scope amendment (2026-05-13), as amended by
6
- # P287 (2026-06-02 base — type-classification retired) AND ADR-060
7
- # Amendment 2026-06-02 (I12 hard-block REPLACED with derive-then-ratify;
8
- # applies to ALL problems; no type-keyed gating):
9
- #
10
- # - Lexical JTBD-trace detection: description-contains-JTBD-NNN-ID →
11
- # silent-resolve jtbd_trace_value to the matched IDs.
12
- # - --jtbd=JTBD-NNN[,...] flag pre-resolves jtbd_trace_value silently.
13
- # - --persona=<value> flag pre-resolves persona_value silently.
14
- # - Low-confidence (no flag + no lexical detection + no cited-JTBD
15
- # agreement) → P401 CORRECTED shape (user direction 2026-06-29,
16
- # sharpened 2026-07-02): INTERVIEW the human to elicit the real
17
- # who/why (NOT propose an ID) → agent classifies existing-vs-new →
18
- # existing:map+proceed autonomously / no-fit:human-ratifies-CREATION
19
- # of a new persona/JTBD (ADR-068/P288). A real problem is NEVER
20
- # discarded over anchoring uncertainty. Scope-rejection (elicited
21
- # who/why out of scope) is EXTERNAL-report-only, at manage-problem
22
- # ingestion — never here.
23
- # - --no-prompt + low-confidence → preserve the finding: CREATE the
24
- # ticket with the `(unconfirmed — elicitation queued)` anchoring
25
- # sentinel + queue the elicitation (P401 never-discard + JTBD-006
26
- # save-and-continue). It no longer halt-refuses.
27
- # - Skeleton template carries **JTBD**: and **Persona**: body fields.
28
- #
29
- # afk_low_confidence_action predicate (P401 2026-06-29) encodes the AFK
30
- # create-with-unconfirmed-sentinel branch. The historical
31
- # i12_should_block predicate is preserved as a regression guard
32
- # (never returns 0) against re-introduction of the type-keyed hard-block.
33
- #
34
- # Persona enum aligned 2026-06-02 to `docs/jtbd/<persona>/` directory
35
- # names: developer / tech-lead / plugin-developer / plugin-user
36
- # (architect AMEND finding 1 — historical `solo-developer` value was
37
- # stale ADR-060 P4.2 spec text and is corrected in the amendment).
38
- #
39
- # Reference-impl pattern: this fixture exercises the algorithm directly
40
- # via shell helpers; the SKILL.md prose at runtime executes the same
41
- # algorithm via LLM-interpretation. The bats algorithm IS the contract
42
- # the SKILL.md prose binds.
43
-
44
- setup() {
45
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
46
- SKILL_FILE="$REPO_ROOT/packages/itil/skills/capture-problem/SKILL.md"
47
- }
48
-
49
- # Reference implementation of the JTBD-trace lexical detector (matches
50
- # the Step 1.5b prose at SKILL.md). Returns space-separated sorted-unique
51
- # JTBD IDs from the description, OR empty string if none.
52
- detect_jtbd_trace() {
53
- local desc="$1"
54
- echo "$desc" | grep -oE '\bJTBD-[0-9]+\b' | sort -u | tr '\n' ' ' | sed 's/[[:space:]]*$//'
55
- }
56
-
57
- # P287 retirement: the I12 hard-block was retired alongside the type
58
- # axis. This predicate now ALWAYS returns 1 (never blocks) — preserved
59
- # as a regression-guard so future drift that re-introduces a type-keyed
60
- # hard-block surfaces as a test failure.
61
- i12_should_block() {
62
- return 1
63
- }
64
-
65
- # P401 (2026-06-29 / 2026-07-02) — AFK low-confidence action. The
66
- # corrected shape PRESERVES THE FINDING: under --no-prompt + low-
67
- # confidence, capture the ticket with the unconfirmed-anchoring sentinel
68
- # and queue the elicitation. It NO LONGER halt-refuses (that discarded
69
- # legitimate problems over anchoring uncertainty). Returns the action.
70
- # Inputs:
71
- # $1: no_prompt_flag ("1" if --no-prompt set, "" otherwise)
72
- # $2: derivation_resolved ("1" if persona+JTBD resolved by any of
73
- # flag/lexical/cited-JTBD path; "" otherwise)
74
- AFK_SENTINEL="(unconfirmed — elicitation queued)"
75
- afk_low_confidence_action() {
76
- local no_prompt="$1"
77
- local derivation_resolved="$2"
78
- if [ "$no_prompt" = "1" ] && [ -z "$derivation_resolved" ]; then
79
- echo "CREATE_UNCONFIRMED_QUEUE_ELICITATION"
80
- else
81
- echo "PROCEED" # derivation succeeded, or interactive interview fires
82
- fi
83
- }
84
-
85
- # P401 — reference impl for the corrected low-confidence resolution of a
86
- # MAINTAINER-INTERNAL capture. The agent interviews to elicit who/why,
87
- # then classifies the elicited fit. Returns the resolution action:
88
- # "MAP_EXISTING" elicited job/persona matches an existing artefact
89
- # → map autonomously (ADR-068 boundary is creation-only)
90
- # "RATIFY_CREATE_NEW" no existing fit → human ratifies the CREATION of a
91
- # new persona/JTBD (ADR-068/P288), then map
92
- # The problem is NEVER discarded over anchoring uncertainty.
93
- resolve_low_confidence_internal() {
94
- local elicited_fit="$1"
95
- case "$elicited_fit" in
96
- existing) echo "MAP_EXISTING" ;;
97
- none) echo "RATIFY_CREATE_NEW" ;;
98
- *) echo "INTERVIEW" ;; # elicit who/why first
99
- esac
100
- }
101
-
102
- # P401 — every maintainer-internal low-confidence resolution yields a
103
- # ticket (never discard). CREATE_UNCONFIRMED is the AFK sentinel path.
104
- internal_resolution_creates_ticket() {
105
- case "$1" in
106
- MAP_EXISTING|RATIFY_CREATE_NEW|CREATE_UNCONFIRMED_QUEUE_ELICITATION) return 0 ;;
107
- *) return 1 ;;
108
- esac
109
- }
110
-
111
- # P401 — scope-rejection is EXTERNAL-report-only (at manage-problem
112
- # ingestion, NOT capture-problem). Through the elicitation interview the
113
- # maintainer may decide the elicited who/why is out of scope to support
114
- # and decline. This is the ONLY path that yields no ticket, and only for
115
- # external reports. A maintainer-internal capture ALWAYS anchors.
116
- # Inputs: $1 origin ("internal"|"external"); $2 in_scope ("yes"|"no").
117
- external_scope_disposition() {
118
- local origin="$1" in_scope="$2"
119
- if [ "$origin" = "external" ] && [ "$in_scope" = "no" ]; then
120
- echo "DECLINE_SCOPE" # deliberate product-scope decline; no ticket
121
- else
122
- echo "ANCHOR" # internal always anchors; external in-scope anchors
123
- fi
124
- }
125
-
126
- # Reference implementation of --jtbd= flag parser. Accepts CSV; returns
127
- # space-separated IDs (canonicalised) OR empty if the flag wasn't set.
128
- parse_jtbd_flag() {
129
- local arg="$1"
130
- case "$arg" in
131
- --jtbd=*) echo "${arg#--jtbd=}" | tr ',' '\n' | sort -u | tr '\n' ' ' | sed 's/[[:space:]]*$//' ;;
132
- *) echo "" ;;
133
- esac
134
- }
135
-
136
- # Reference implementation of --persona= validator. Returns the value
137
- # if it's in the closed enum; halts (returns 1) otherwise. Enum aligned
138
- # 2026-06-02 to docs/jtbd/<persona>/ directory names (architect AMEND
139
- # finding 1 — `solo-developer` was stale ADR-060 P4.2 text).
140
- validate_persona() {
141
- local val="$1"
142
- case "$val" in
143
- developer|tech-lead|plugin-developer|plugin-user) echo "$val"; return 0 ;;
144
- *) return 1 ;;
145
- esac
146
- }
147
-
148
- # Reference implementation of --no-prompt flag detector. Returns "1" if
149
- # any of the supplied args is --no-prompt; empty otherwise. AFK marker
150
- # per ADR-060 Amendment 2026-06-02 I12 derive-then-ratify contract.
151
- parse_no_prompt_flag() {
152
- for arg in "$@"; do
153
- case "$arg" in
154
- --no-prompt) echo "1"; return 0 ;;
155
- esac
156
- done
157
- echo ""
158
- }
159
-
160
- @test "P3.1 detect_jtbd_trace: description with single JTBD-NNN citation extracts ID" {
161
- result=$(detect_jtbd_trace "Adopters want JTBD-101 to scale down for atomic fixes")
162
- [ "$result" = "JTBD-101" ]
163
- }
164
-
165
- @test "P3.1 detect_jtbd_trace: description with multiple JTBD-NNN citations extracts sorted-unique IDs" {
166
- result=$(detect_jtbd_trace "Composes with JTBD-008 and JTBD-001 governance outcome (also JTBD-008 again)")
167
- [ "$result" = "JTBD-001 JTBD-008" ]
168
- }
169
-
170
- @test "P3.1 detect_jtbd_trace: description with no JTBD citation returns empty" {
171
- result=$(detect_jtbd_trace "The captureProblem hook in packages/itil/hooks has a regex drift")
172
- [ -z "$result" ]
173
- }
174
-
175
- @test "P3.1 detect_jtbd_trace: JTBD-NNN must be word-boundary (not substring)" {
176
- # NOT-JTBD-001 should NOT match because of leading \b boundary check —
177
- # but `\b` matches at hyphen boundary in standard regex. The detector
178
- # treats this conservatively — anything matching \bJTBD-[0-9]+\b is
179
- # accepted. The signal is high-precision; mis-matches at hyphen
180
- # boundaries are still real JTBD-NNN citations from the maintainer's
181
- # perspective.
182
- result=$(detect_jtbd_trace "BANANA-JTBD-001-thing")
183
- [ "$result" = "JTBD-001" ]
184
- }
185
-
186
- @test "P287 i12 hard-block retired: never blocks regardless of inputs (regression guard)" {
187
- # After P287, the I12 hard-block is retired. The predicate must never
188
- # return 0 (block) for any input combination — capture-time JTBD
189
- # anchoring is best-effort, not hard-required. If a future maintainer
190
- # re-introduces a type-keyed hard-block, this test catches it.
191
- ! i12_should_block "user-business" "" "0"
192
- ! i12_should_block "user-business" "JTBD-001" "0"
193
- ! i12_should_block "user-business" "" "1"
194
- ! i12_should_block "technical" "" "0"
195
- ! i12_should_block "anything" "anything" "anything"
196
- }
197
-
198
- @test "P3.1 parse_jtbd_flag: --jtbd=JTBD-NNN parses single ID" {
199
- result=$(parse_jtbd_flag "--jtbd=JTBD-001")
200
- [ "$result" = "JTBD-001" ]
201
- }
202
-
203
- @test "P3.1 parse_jtbd_flag: --jtbd=JTBD-A,JTBD-B parses CSV into sorted-unique list" {
204
- result=$(parse_jtbd_flag "--jtbd=JTBD-008,JTBD-001,JTBD-008")
205
- [ "$result" = "JTBD-001 JTBD-008" ]
206
- }
207
-
208
- @test "P3.1 parse_jtbd_flag: non-jtbd-flag arg returns empty" {
209
- result=$(parse_jtbd_flag "--persona=plugin-user")
210
- [ -z "$result" ]
211
- }
212
-
213
- @test "P4.2 validate_persona: closed enum accepts developer (architect AMEND 2026-06-02 — was solo-developer)" {
214
- result=$(validate_persona "developer")
215
- [ "$result" = "developer" ]
216
- }
217
-
218
- @test "P4.2 validate_persona: closed enum accepts tech-lead" {
219
- validate_persona "tech-lead"
220
- }
221
-
222
- @test "P4.2 validate_persona: closed enum accepts plugin-developer" {
223
- validate_persona "plugin-developer"
224
- }
225
-
226
- @test "P4.2 validate_persona: closed enum accepts plugin-user" {
227
- validate_persona "plugin-user"
228
- }
229
-
230
- @test "P4.2 validate_persona: rejects free-text outside enum" {
231
- ! validate_persona "maintainer"
232
- }
233
-
234
- @test "P4.2 validate_persona: rejects stale solo-developer (architect AMEND 2026-06-02 regression guard)" {
235
- # Pre-Amendment-2026-06-02 ADR-060 P4.2 text named `solo-developer` but
236
- # docs/jtbd/ directory layout uses `developer/`. The amendment reconciled
237
- # the enum. This test guards against drift back to the stale value.
238
- ! validate_persona "solo-developer"
239
- }
240
-
241
- # ---------------------------------------------------------------------------
242
- # P401 (2026-06-29 / 2026-07-02) — corrected low-confidence contract.
243
- # Low-confidence INTERVIEWS to elicit who/why (never proposes an ID); the
244
- # agent classifies existing-vs-new; existing→map+proceed, no-fit→human-
245
- # ratifies-CREATION; a real problem is NEVER discarded over anchoring;
246
- # scope-rejection is external-report-only; AFK captures with the
247
- # unconfirmed-anchoring sentinel + queues the elicitation.
248
- # ---------------------------------------------------------------------------
249
-
250
- @test "P401 low-confidence interviews (elicits who/why) rather than proposing an ID" {
251
- # Before classification, the resolution action is INTERVIEW — the agent
252
- # elicits the real who/why, NOT a candidate JTBD-NNN to ratify.
253
- result=$(resolve_low_confidence_internal "unknown")
254
- [ "$result" = "INTERVIEW" ]
255
- }
256
-
257
- @test "P401 elicited who/why matching an existing artefact maps autonomously" {
258
- # Classification: elicited job matches an existing JTBD/persona → map and
259
- # proceed with no further human ratification (ADR-068 boundary is
260
- # creation-only). Yields a ticket.
261
- result=$(resolve_low_confidence_internal "existing")
262
- [ "$result" = "MAP_EXISTING" ]
263
- internal_resolution_creates_ticket "$result"
264
- }
265
-
266
- @test "P401 elicited who/why with no existing fit routes to human-ratified new-artefact creation" {
267
- # No existing fit → a new persona/JTBD is warranted → human ratifies the
268
- # CREATION (ADR-068/P288), then map. Still yields a ticket (never discard).
269
- result=$(resolve_low_confidence_internal "none")
270
- [ "$result" = "RATIFY_CREATE_NEW" ]
271
- internal_resolution_creates_ticket "$result"
272
- }
273
-
274
- @test "P401 a maintainer-internal problem is NEVER discarded over anchoring uncertainty" {
275
- # Every internal low-confidence resolution — map-existing, ratify-new, or
276
- # the AFK create-with-unconfirmed-sentinel — yields a ticket.
277
- internal_resolution_creates_ticket "MAP_EXISTING"
278
- internal_resolution_creates_ticket "RATIFY_CREATE_NEW"
279
- internal_resolution_creates_ticket "CREATE_UNCONFIRMED_QUEUE_ELICITATION"
280
- }
281
-
282
- @test "P401 AFK low-confidence creates ticket with unconfirmed sentinel + queues elicitation (not halt-refuse)" {
283
- # --no-prompt + derivation-failure → preserve the finding.
284
- result=$(afk_low_confidence_action "1" "")
285
- [ "$result" = "CREATE_UNCONFIRMED_QUEUE_ELICITATION" ]
286
- # The AFK sentinel is a real (non-empty) anchoring value written to the
287
- # ticket — the finding is preserved, not discarded.
288
- [ -n "$AFK_SENTINEL" ]
289
- internal_resolution_creates_ticket "$result"
290
- }
291
-
292
- @test "P401 AFK proceeds normally when derivation succeeded (flags pre-resolved)" {
293
- # AFK orchestrator pattern: pass --no-prompt PLUS --persona + --jtbd to
294
- # skip the sentinel path entirely.
295
- no_prompt=$(parse_no_prompt_flag "--persona=developer" "--jtbd=JTBD-006" "--no-prompt" "fix work-problems iter halt")
296
- [ "$no_prompt" = "1" ]
297
- persona=$(validate_persona "developer")
298
- [ "$persona" = "developer" ]
299
- jtbd=$(parse_jtbd_flag "--jtbd=JTBD-006")
300
- [ "$jtbd" = "JTBD-006" ]
301
- result=$(afk_low_confidence_action "$no_prompt" "1")
302
- [ "$result" = "PROCEED" ]
303
- }
304
-
305
- @test "P401 interactive low-confidence proceeds into the interview (no --no-prompt)" {
306
- result=$(afk_low_confidence_action "" "")
307
- [ "$result" = "PROCEED" ]
308
- }
309
-
310
- @test "P401 scope-rejection is external-report-only; maintainer-internal always anchors" {
311
- # External report whose elicited who/why we do NOT want to support →
312
- # deliberate scope decline (no ticket) — the ONLY no-ticket path.
313
- [ "$(external_scope_disposition external no)" = "DECLINE_SCOPE" ]
314
- # External report in scope → anchors.
315
- [ "$(external_scope_disposition external yes)" = "ANCHOR" ]
316
- # Maintainer-internal ALWAYS anchors, regardless of scope signal —
317
- # internal captures are never scope-rejected.
318
- [ "$(external_scope_disposition internal no)" = "ANCHOR" ]
319
- [ "$(external_scope_disposition internal yes)" = "ANCHOR" ]
320
- }
321
-
322
- @test "P401 parse_no_prompt_flag detects --no-prompt anywhere in args" {
323
- result=$(parse_no_prompt_flag "--persona=developer" "--no-prompt" "description text")
324
- [ "$result" = "1" ]
325
- }
326
-
327
- @test "P401 parse_no_prompt_flag empty when --no-prompt absent" {
328
- result=$(parse_no_prompt_flag "--persona=developer" "description text")
329
- [ -z "$result" ]
330
- }
331
-
332
- @test "SKILL.md: Step 1.5b section header exists for JTBD-trace + persona dispatch" {
333
- grep -qE '^### 1\.5b JTBD-trace \+ persona dispatch' "$SKILL_FILE"
334
- }
335
-
336
- @test "SKILL.md: Step 1.5b names I12 invariant load-bearing identifier" {
337
- grep -qE 'I12 (invariant|hard-block)' "$SKILL_FILE"
338
- }
339
-
340
- @test "SKILL.md: --jtbd= flag declared in flag table" {
341
- grep -qE '\| `--jtbd=JTBD-NNN' "$SKILL_FILE"
342
- }
343
-
344
- @test "SKILL.md: --persona= flag declared in flag table" {
345
- grep -qE '\| `--persona=<value>`' "$SKILL_FILE"
346
- }
347
-
348
- @test "SKILL.md: Step 4 template carries **JTBD**: body field" {
349
- grep -qE '^\*\*JTBD\*\*:' "$SKILL_FILE"
350
- }
351
-
352
- @test "SKILL.md: Step 4 template carries **Persona**: body field" {
353
- grep -qE '^\*\*Persona\*\*:' "$SKILL_FILE"
354
- }
355
-
356
- @test "SKILL.md: Step 1.5b cites ADR-060 Amendment 2026-06-02 (I12 derive-then-ratify)" {
357
- grep -qE 'ADR-060 Amendment 2026-06-02' "$SKILL_FILE"
358
- }
359
-
360
- @test "SKILL.md: Step 1.5b preserves JTBD-301 firewall on plugin-user-side intake" {
361
- grep -qE '[Pp]lugin-user-side.*MUST NOT (prompt|carry)' "$SKILL_FILE"
362
- }
363
-
364
- @test "SKILL.md: Step 1.5b names derive-then-ratify contract" {
365
- grep -qE 'derive-then-ratify' "$SKILL_FILE"
366
- }
367
-
368
- @test "SKILL.md: --no-prompt flag declared in flag table (AFK mode marker)" {
369
- grep -qE '\| `--no-prompt`' "$SKILL_FILE"
370
- }
371
-
372
- @test "SKILL.md: Step 1.5b names P401 never-discard-over-anchoring rule" {
373
- grep -qiE 'never discarded over anchoring uncertainty' "$SKILL_FILE"
374
- }
375
-
376
- @test "SKILL.md: Step 1.5b names interview-to-elicit-who/why (not propose an ID)" {
377
- grep -qiE '[Ii]nterview.*(who|why|elicit)|elicit the real who/why' "$SKILL_FILE"
378
- }
379
-
380
- @test "SKILL.md: Step 1.5b names scope-rejection as external-report-only" {
381
- grep -qiE 'external-report-only' "$SKILL_FILE"
382
- }
383
-
384
- @test "SKILL.md: Step 1.5b names AFK unconfirmed-anchoring sentinel + queued elicitation" {
385
- grep -qiE 'unconfirmed — elicitation queued|unconfirmed-anchoring sentinel' "$SKILL_FILE"
386
- }
387
-
388
- @test "SKILL.md: Step 1.5b routes new persona/JTBD creation to ADR-068/P288 human ratify" {
389
- grep -qE 'ratif.*(creation|CREATION).*(ADR-068|P288)|(ADR-068|P288).*creation' "$SKILL_FILE"
390
- }
391
-
392
- @test "SKILL.md: allowed-tools includes AskUserQuestion (for the low-confidence interview)" {
393
- grep -qE '^allowed-tools:.*AskUserQuestion' "$SKILL_FILE"
394
- }
395
-
396
- @test "SKILL.md: ADR-044 authority taxonomy names direction-setting (category 1) for interview + creation-ratify" {
397
- grep -qE 'direction-setting.*category 1|category 1.*direction-setting' "$SKILL_FILE"
398
- }