@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.
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +7 -2
- package/agents/test/fixtures/proceed-new-genuinely-new.md +0 -43
- package/agents/test/fixtures/proceed-new-subtle-sibling.md +0 -47
- package/agents/test/fixtures/regression-p347-vs-p346.md +0 -54
- package/agents/test/hang-off-check.bats +0 -225
- package/hooks/test/block-list.bats +0 -153
- package/hooks/test/command-detect.bats +0 -181
- package/hooks/test/itil-assistant-output-gate.bats +0 -114
- package/hooks/test/itil-assistant-output-review.bats +0 -224
- package/hooks/test/itil-bash-polling-antipattern-detect.bats +0 -154
- package/hooks/test/itil-changeset-discipline.bats +0 -603
- package/hooks/test/itil-claude-space-protection.bats +0 -294
- package/hooks/test/itil-commit-trailer-transition-advisory.bats +0 -80
- package/hooks/test/itil-correction-detect.bats +0 -189
- package/hooks/test/itil-deferral-cadence-gate.bats +0 -187
- package/hooks/test/itil-fictional-defer-detect.bats +0 -292
- package/hooks/test/itil-fix-title-lifecycle-advisory.bats +0 -129
- package/hooks/test/itil-mid-loop-ask-detect.bats +0 -220
- package/hooks/test/itil-no-implement-draft-gate.bats +0 -210
- package/hooks/test/itil-pending-questions-surface.bats +0 -307
- package/hooks/test/itil-readme-refresh-discipline.bats +0 -642
- package/hooks/test/itil-rfc-oversight-nudge.bats +0 -83
- package/hooks/test/itil-rfc-trailer-advisory.bats +0 -375
- package/hooks/test/itil-story-mirror-migration-nudge.bats +0 -122
- package/hooks/test/manage-problem-enforce-create.bats +0 -424
- package/hooks/test/p057-staging-trap-detect.bats +0 -231
- package/hooks/test/pre-publish-intake-gate.bats +0 -144
- package/hooks/test/runtime-sid-marker.bats +0 -90
- package/hooks/test/session-id.bats +0 -370
- package/scripts/migrate-problems-add-type.sh +0 -116
- package/scripts/test/catchup-scan.bats +0 -274
- package/scripts/test/check-afk-accept-eligible.bats +0 -310
- package/scripts/test/check-fail-soft-skip-discipline.bats +0 -172
- package/scripts/test/check-fix-rfc-trace.bats +0 -164
- package/scripts/test/check-locale-discipline.bats +0 -291
- package/scripts/test/check-problems-readme-budget.bats +0 -192
- package/scripts/test/check-rfc-has-stories.bats +0 -69
- package/scripts/test/check-rfc-rejected-alternatives.bats +0 -108
- package/scripts/test/check-rfc-stories-ratified.bats +0 -82
- package/scripts/test/check-upstream-responses.bats +0 -503
- package/scripts/test/classify-readme-drift.bats +0 -392
- package/scripts/test/derive-first-dispatch.bats +0 -274
- package/scripts/test/derive-release-vehicle.bats +0 -454
- package/scripts/test/detect-unratified-stories-maps.bats +0 -60
- package/scripts/test/dual-tolerant-glob-rfc-002-t2.bats +0 -309
- package/scripts/test/effort-tally.bats +0 -170
- package/scripts/test/evaluate-relevance.bats +0 -773
- package/scripts/test/kind-a-commands.bats +0 -46
- package/scripts/test/mark-create-gate.bats +0 -59
- package/scripts/test/mark-story-oversight-confirmed.bats +0 -53
- package/scripts/test/migrate-problems-add-type.bats +0 -280
- package/scripts/test/migrate-story-status-mirror.bats +0 -167
- package/scripts/test/no-type-regression-guard.bats +0 -74
- package/scripts/test/oversight-mirror-corpus-lint.bats +0 -150
- package/scripts/test/plugin-exercise-index.bats +0 -386
- package/scripts/test/plugin-maturity-doc-lint.bats +0 -470
- package/scripts/test/plugin-maturity-populate.bats +0 -631
- package/scripts/test/plugin-maturity-render.bats +0 -469
- package/scripts/test/plugin-validate-ci-gate.bats +0 -208
- package/scripts/test/reconcile-readme.bats +0 -1007
- package/scripts/test/reconcile-rfcs.bats +0 -496
- package/scripts/test/reconcile-stories.bats +0 -173
- package/scripts/test/reconcile-story-maps.bats +0 -74
- package/scripts/test/release-watch-poll-loop.bats +0 -180
- package/scripts/test/resolve-governance-plugin-dirs.bats +0 -78
- package/scripts/test/rfc-stories-extension.bats +0 -175
- package/scripts/test/skill-invocations.bats +0 -535
- package/scripts/test/skill-md-dual-tolerant-coverage-rfc-002-t3.bats +0 -374
- package/scripts/test/story-oversight-lib.bats +0 -190
- package/scripts/test/update-jtbd-references-related-problems.bats +0 -178
- package/scripts/test/update-problem-references-section.bats +0 -242
- package/scripts/test/update-problem-rfcs-section.bats +0 -242
- package/scripts/test/update-references-section-sibling-helpers.bats +0 -80
- package/scripts/test/update-rfc-commits-section.bats +0 -77
- package/scripts/test/verify-iter-summary.bats +0 -196
- package/scripts/test/working-the-problem-traversal.bats +0 -116
- package/skills/capture-problem/test/capture-problem-step-1-5b-jtbd-trace.bats +0 -398
- package/skills/capture-problem/test/capture-problem.bats +0 -389
- package/skills/capture-story/test/capture-story-behavioural.bats +0 -317
- package/skills/capture-story-map/test/capture-story-map-behavioural.bats +0 -98
- package/skills/close-incident/test/close-incident-contract.bats +0 -146
- package/skills/link-incident/test/link-incident-contract.bats +0 -125
- package/skills/list-incidents/test/list-incidents-contract.bats +0 -115
- package/skills/list-problems/test/list-problems-contract.bats +0 -100
- package/skills/list-stories/test/list-stories-contract.bats +0 -127
- package/skills/list-story-maps/test/list-story-maps-contract.bats +0 -46
- package/skills/manage-incident/test/manage-incident-adr-044-contract.bats +0 -206
- package/skills/manage-incident/test/manage-incident-close-forwarder.bats +0 -67
- package/skills/manage-incident/test/manage-incident-link-forwarder.bats +0 -67
- package/skills/manage-incident/test/manage-incident-list-forwarder.bats +0 -68
- package/skills/manage-incident/test/manage-incident-mitigate-forwarder.bats +0 -66
- package/skills/manage-incident/test/manage-incident-restore-forwarder.bats +0 -66
- package/skills/manage-incident/test/manage-incident.bats +0 -171
- package/skills/manage-problem/test/manage-problem-adr-044-step4-derive-first.bats +0 -151
- package/skills/manage-problem/test/manage-problem-auto-migrate-step.bats +0 -53
- package/skills/manage-problem/test/manage-problem-concern-boundary.bats +0 -64
- package/skills/manage-problem/test/manage-problem-effort-buckets.bats +0 -56
- package/skills/manage-problem/test/manage-problem-external-root-cause-detection.bats +0 -119
- package/skills/manage-problem/test/manage-problem-first-run-intake-prompt.bats +0 -43
- package/skills/manage-problem/test/manage-problem-git-mv-restage-reminder.bats +0 -48
- package/skills/manage-problem/test/manage-problem-id-collision-guard.bats +0 -39
- package/skills/manage-problem/test/manage-problem-list-forwarder.bats +0 -66
- package/skills/manage-problem/test/manage-problem-next-id-origin-lookup.bats +0 -50
- package/skills/manage-problem/test/manage-problem-no-prose-options.bats +0 -74
- package/skills/manage-problem/test/manage-problem-output-formatting.bats +0 -30
- package/skills/manage-problem/test/manage-problem-parked-and-cache.bats +0 -73
- package/skills/manage-problem/test/manage-problem-readme-refresh-on-transition.bats +0 -62
- package/skills/manage-problem/test/manage-problem-readme-tie-break-order.bats +0 -318
- package/skills/manage-problem/test/manage-problem-readme-vq-sort-order.bats +0 -206
- package/skills/manage-problem/test/manage-problem-release-vehicle-seed.bats +0 -83
- package/skills/manage-problem/test/manage-problem-review-forwarder.bats +0 -71
- package/skills/manage-problem/test/manage-problem-step-9d-recovery-path.bats +0 -97
- package/skills/manage-problem/test/manage-problem-transition-forwarder.bats +0 -96
- package/skills/manage-problem/test/manage-problem-transitive-dependencies.bats +0 -365
- package/skills/manage-problem/test/manage-problem-verification-detection.bats +0 -64
- package/skills/manage-problem/test/manage-problem-verification-pending.bats +0 -114
- package/skills/manage-problem/test/manage-problem-work-forwarder.bats +0 -88
- package/skills/manage-story/test/manage-story-contract.bats +0 -174
- package/skills/manage-story-map/test/manage-story-map-contract.bats +0 -63
- package/skills/mitigate-incident/test/mitigate-incident-contract.bats +0 -258
- package/skills/reconcile-readme/test/reconcile-readme-contract.bats +0 -201
- package/skills/report-upstream/test/report-upstream-contract.bats +0 -394
- package/skills/restore-incident/test/restore-incident-contract.bats +0 -170
- package/skills/review-problems/test/inbound-channels-cache-shape.bats +0 -127
- package/skills/review-problems/test/inbound-discovery-contract.bats +0 -383
- package/skills/review-problems/test/jtbd-301-verdict-shape-contract.bats +0 -225
- package/skills/review-problems/test/review-problems-contract.bats +0 -235
- package/skills/review-problems/test/review-problems-likely-verified-cell-shape.bats +0 -229
- package/skills/scaffold-intake/test/scaffold-intake-contract.bats +0 -126
- package/skills/scaffold-intake/test/scaffold-intake-fixture.bats +0 -164
- package/skills/scaffold-intake/test/scaffold-intake-secrets-absent.bats +0 -87
- package/skills/transition-problem/test/transition-problem-contract.bats +0 -285
- package/skills/transition-problems/test/transition-problems-contract.bats +0 -331
- package/skills/update-upstream/test/update-upstream-contract.bats +0 -375
- package/skills/work-problem/test/work-problem-contract.bats +0 -294
- package/skills/work-problems/test/work-problems-above-appetite-remediation.bats +0 -139
- package/skills/work-problems/test/work-problems-adr-013-rule-6-p352-amendment.bats +0 -166
- package/skills/work-problems/test/work-problems-auto-migrate-step.bats +0 -57
- package/skills/work-problems/test/work-problems-cost-logging.bats +0 -109
- package/skills/work-problems/test/work-problems-deviation-candidate-shape.bats +0 -121
- package/skills/work-problems/test/work-problems-first-run-intake-prompt.bats +0 -36
- package/skills/work-problems/test/work-problems-inter-iteration-verify.bats +0 -59
- package/skills/work-problems/test/work-problems-mid-loop-userpromptsubmit-handler.bats +0 -69
- package/skills/work-problems/test/work-problems-no-mid-loop-asking.bats +0 -192
- package/skills/work-problems/test/work-problems-p341-pre-all-done-gate.bats +0 -122
- package/skills/work-problems/test/work-problems-p342-r007-derive-then-pass-flags.bats +0 -170
- package/skills/work-problems/test/work-problems-p342-retro-auto-ticket-carveout.bats +0 -120
- package/skills/work-problems/test/work-problems-preflight-failure-handling.bats +0 -248
- package/skills/work-problems/test/work-problems-preflight-session-continuity.bats +0 -139
- package/skills/work-problems/test/work-problems-preflight.bats +0 -69
- package/skills/work-problems/test/work-problems-release-cadence.bats +0 -63
- package/skills/work-problems/test/work-problems-step-0-iter-error-staleness.bats +0 -93
- package/skills/work-problems/test/work-problems-step-0b-cache-staleness-behavioural.bats +0 -138
- package/skills/work-problems/test/work-problems-step-0c-deferred-placeholder-staleness-behavioural.bats +0 -238
- package/skills/work-problems/test/work-problems-step-0d-outbound-responses-staleness-behavioural.bats +0 -174
- package/skills/work-problems/test/work-problems-step-2-5-routing.bats +0 -80
- package/skills/work-problems/test/work-problems-step-2-5b-cross-halt-routing.bats +0 -149
- package/skills/work-problems/test/work-problems-step-3-5-jtbd-ratification-predicate.bats +0 -191
- package/skills/work-problems/test/work-problems-step-5-bats-polling-discipline.bats +0 -91
- package/skills/work-problems/test/work-problems-step-5-delegation.bats +0 -333
- package/skills/work-problems/test/work-problems-step-5-idle-timeout-sigterm.bats +0 -421
- package/skills/work-problems/test/work-problems-step-5-is-error-transient-halt.bats +0 -278
- package/skills/work-problems/test/work-problems-step-5-iter-changeset-required.bats +0 -95
- package/skills/work-problems/test/work-problems-step-5-prompt-body-re-grounding.bats +0 -128
- package/skills/work-problems/test/work-problems-step-5-stream-timeout-salvage.bats +0 -251
- package/skills/work-problems/test/work-problems-step-6-5-always-drain.bats +0 -203
- package/skills/work-problems/test/work-problems-step-6-5-cache-refresh-chain.bats +0 -174
- package/skills/work-problems/test/work-problems-step-6-5-fix-and-continue.bats +0 -254
- package/skills/work-problems/test/work-problems-step-6-5-postrelease-kv-callback.bats +0 -209
- 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
|
-
}
|