@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,469 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# @problem P238 — Phase 3b renderer behavioural confirmation.
|
|
4
|
-
# @problem P087 — parent: plugin maturity battle-hardening signal.
|
|
5
|
-
#
|
|
6
|
-
# Contract under test: `packages/itil/scripts/plugin-maturity-render.sh`
|
|
7
|
-
# reads `plugin.json` `maturity:` field (per Phase 3a populate output)
|
|
8
|
-
# and writes a prose-woven rollup badge into each plugin's README.md
|
|
9
|
-
# value-framing lead prose AND populates a per-skill `Maturity` column
|
|
10
|
-
# in the existing `## Skills` table. Idempotent — re-running with
|
|
11
|
-
# unchanged inputs produces byte-identical README output.
|
|
12
|
-
#
|
|
13
|
-
# Anti-patterns enforced (ADR-063 §README badge rendering format):
|
|
14
|
-
# - NO standalone `## Maturity` section
|
|
15
|
-
# - NO header block immediately after H1 before any prose framing
|
|
16
|
-
# - NO shields.io URL or inline SVG (markdown text only)
|
|
17
|
-
#
|
|
18
|
-
# Bootstrapping rendering: rollup carries compound form during the
|
|
19
|
-
# suite-bootstrap window; per-skill column carries band name only
|
|
20
|
-
# (compound stays at rollup, never at cell).
|
|
21
|
-
#
|
|
22
|
-
# @adr ADR-063 (Plugin maturity presentation layer — Phase 3b contract)
|
|
23
|
-
# @adr ADR-053 (Plugin maturity taxonomy — Bootstrapping clause rendering)
|
|
24
|
-
# @adr ADR-051 (JTBD-anchored README — prose-weaving precedent)
|
|
25
|
-
# @adr ADR-049 (Shim grammar — `wr-itil-plugin-maturity-render` on $PATH)
|
|
26
|
-
# @adr ADR-052 (Behavioural tests default)
|
|
27
|
-
# @adr ADR-013 Rule 6 (non-interactive fail-safe — exit 0 always)
|
|
28
|
-
|
|
29
|
-
setup() {
|
|
30
|
-
SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
31
|
-
SCRIPT="$SCRIPTS_DIR/plugin-maturity-render.sh"
|
|
32
|
-
FIXTURE_DIR="$(mktemp -d)"
|
|
33
|
-
PROJECT_ROOT="$FIXTURE_DIR/project"
|
|
34
|
-
mkdir -p "$PROJECT_ROOT/packages"
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
teardown() {
|
|
38
|
-
rm -rf "$FIXTURE_DIR"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
# Helper: write a synthetic plugin layout with plugin.json + README.md
|
|
42
|
-
make_plugin() {
|
|
43
|
-
local name="$1"
|
|
44
|
-
local plugin_json="$2"
|
|
45
|
-
local readme="$3"
|
|
46
|
-
local pkg="$PROJECT_ROOT/packages/$name"
|
|
47
|
-
mkdir -p "$pkg/.claude-plugin"
|
|
48
|
-
printf '%s\n' "$plugin_json" > "$pkg/.claude-plugin/plugin.json"
|
|
49
|
-
printf '%s\n' "$readme" > "$pkg/README.md"
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
# ── Pre-checks ──────────────────────────────────────────────────────────────
|
|
53
|
-
|
|
54
|
-
@test "script file exists and is executable" {
|
|
55
|
-
[ -f "$SCRIPT" ]
|
|
56
|
-
[ -x "$SCRIPT" ]
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
@test "missing packages dir exits 0 with stderr comment" {
|
|
60
|
-
run bash "$SCRIPT" --project-root="$FIXTURE_DIR/does-not-exist"
|
|
61
|
-
[ "$status" -eq 0 ]
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
@test "opt-out marker present: skips all writes" {
|
|
65
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Alpha"}}' \
|
|
66
|
-
"# @windyroad/stub
|
|
67
|
-
|
|
68
|
-
**Stub plugin description.**
|
|
69
|
-
|
|
70
|
-
## Skills
|
|
71
|
-
|
|
72
|
-
| Skill | Purpose |
|
|
73
|
-
|-------|---------|
|
|
74
|
-
| /wr-stub:thing | Does a thing |
|
|
75
|
-
"
|
|
76
|
-
mkdir -p "$PROJECT_ROOT/.claude"
|
|
77
|
-
touch "$PROJECT_ROOT/.claude/.skill-metrics-opt-out"
|
|
78
|
-
local before; before="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
79
|
-
|
|
80
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
81
|
-
[ "$status" -eq 0 ]
|
|
82
|
-
|
|
83
|
-
local after; after="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
84
|
-
[ "$before" = "$after" ]
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
# ── Confirmation #1 (ADR-063): rollup badge prose-woven into lead prose ─────
|
|
88
|
-
|
|
89
|
-
@test "rollup badge: inserts prose-woven Maturity span into bold lead prose line" {
|
|
90
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Alpha"}}' \
|
|
91
|
-
"# @windyroad/stub
|
|
92
|
-
|
|
93
|
-
**Stub plugin description.** Some more prose.
|
|
94
|
-
|
|
95
|
-
## Skills
|
|
96
|
-
|
|
97
|
-
| Skill | Purpose |
|
|
98
|
-
|-------|---------|
|
|
99
|
-
| /wr-stub:thing | Does a thing |
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
103
|
-
[ "$status" -eq 0 ]
|
|
104
|
-
|
|
105
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
106
|
-
# Prose-woven Maturity span appears, italicised, in the lead-prose area
|
|
107
|
-
[[ "$out" == *"*Maturity: Alpha"* ]]
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
@test "rollup badge: bootstrapping window renders compound form with invocation count" {
|
|
111
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Experimental","bootstrapping":true,"rollup_invocations_30d":796}}' \
|
|
112
|
-
"# @windyroad/stub
|
|
113
|
-
|
|
114
|
-
**Stub plugin description.**
|
|
115
|
-
|
|
116
|
-
## Skills
|
|
117
|
-
|
|
118
|
-
| Skill | Purpose |
|
|
119
|
-
|-------|---------|
|
|
120
|
-
| /wr-stub:thing | Does a thing |
|
|
121
|
-
"
|
|
122
|
-
|
|
123
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
124
|
-
[ "$status" -eq 0 ]
|
|
125
|
-
|
|
126
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
127
|
-
[[ "$out" == *"Experimental"* ]]
|
|
128
|
-
[[ "$out" == *"suite-bootstrap window"* ]]
|
|
129
|
-
[[ "$out" == *"796 invocations"* ]]
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
@test "rollup badge: post-bootstrap renders band name only (no compound)" {
|
|
133
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Beta"}}' \
|
|
134
|
-
"# @windyroad/stub
|
|
135
|
-
|
|
136
|
-
**Stub plugin description.**
|
|
137
|
-
|
|
138
|
-
## Skills
|
|
139
|
-
|
|
140
|
-
| Skill | Purpose |
|
|
141
|
-
|-------|---------|
|
|
142
|
-
| /wr-stub:thing | Does a thing |
|
|
143
|
-
"
|
|
144
|
-
|
|
145
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
146
|
-
[ "$status" -eq 0 ]
|
|
147
|
-
|
|
148
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
149
|
-
[[ "$out" == *"*Maturity: Beta.*"* ]]
|
|
150
|
-
[[ "$out" != *"suite-bootstrap"* ]]
|
|
151
|
-
[[ "$out" != *"invocations"* ]]
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
# ── P269: AND-gated compound predicate — null rollup_invocations_30d → bare ─
|
|
155
|
-
|
|
156
|
-
@test "rollup badge: bootstrapping=true + rollup_invocations_30d=null → bare-band (hook-only plugin)" {
|
|
157
|
-
# P269: when populate writes bootstrapping=true but rollup_invocations_30d
|
|
158
|
-
# is null (hook-only plugin — no countable surfaces), the renderer's
|
|
159
|
-
# AND-gated compound predicate at plugin-maturity-render.sh:146 must fall
|
|
160
|
-
# through to bare-band. `0 invocations / 30d` would lie; bare-band is the
|
|
161
|
-
# honest fall-through.
|
|
162
|
-
make_plugin "hookonly" '{"name":"wr-hookonly","version":"0.1.0","description":"Hook-only","maturity":{"schema_version":"2.0","band":"Experimental","bootstrapping":true,"rollup_invocations_30d":null}}' \
|
|
163
|
-
"# @windyroad/hookonly
|
|
164
|
-
|
|
165
|
-
**Hook-only plugin description.**
|
|
166
|
-
|
|
167
|
-
## Skills
|
|
168
|
-
|
|
169
|
-
| Skill | Purpose |
|
|
170
|
-
|-------|---------|
|
|
171
|
-
| /wr-hookonly:thing | Does a thing |
|
|
172
|
-
"
|
|
173
|
-
|
|
174
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
175
|
-
[ "$status" -eq 0 ]
|
|
176
|
-
|
|
177
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/hookonly/README.md")"
|
|
178
|
-
[[ "$out" == *"*Maturity: Experimental.*"* ]]
|
|
179
|
-
[[ "$out" != *"suite-bootstrap"* ]]
|
|
180
|
-
[[ "$out" != *"invocations"* ]]
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
# ── P269: AND-gated compound predicate — bootstrapping=false → bare-band ────
|
|
184
|
-
|
|
185
|
-
@test "rollup badge: bootstrapping=false + rollup_invocations_30d=integer → bare-band (post-sunset)" {
|
|
186
|
-
# P269: when bootstrapping has lapsed (suite_oldest_days >= 60), populate
|
|
187
|
-
# writes bootstrapping=false. Even with a non-null rollup_invocations_30d,
|
|
188
|
-
# the AND-gated predicate falls through to bare-band — the compound
|
|
189
|
-
# rendering is scoped to the bootstrapping window per ADR-053 §Bootstrapping
|
|
190
|
-
# clause Phase 3 rendering requirement.
|
|
191
|
-
make_plugin "stedp" '{"name":"wr-stedp","version":"0.1.0","description":"Steady","maturity":{"schema_version":"2.0","band":"Beta","bootstrapping":false,"rollup_invocations_30d":1500}}' \
|
|
192
|
-
"# @windyroad/stedp
|
|
193
|
-
|
|
194
|
-
**Steady-state plugin description.**
|
|
195
|
-
|
|
196
|
-
## Skills
|
|
197
|
-
|
|
198
|
-
| Skill | Purpose |
|
|
199
|
-
|-------|---------|
|
|
200
|
-
| /wr-stedp:thing | Does a thing |
|
|
201
|
-
"
|
|
202
|
-
|
|
203
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
204
|
-
[ "$status" -eq 0 ]
|
|
205
|
-
|
|
206
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/stedp/README.md")"
|
|
207
|
-
[[ "$out" == *"*Maturity: Beta.*"* ]]
|
|
208
|
-
[[ "$out" != *"suite-bootstrap"* ]]
|
|
209
|
-
[[ "$out" != *"invocations"* ]]
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
# ── Confirmation #2 (ADR-063): per-skill Maturity column populated ──────────
|
|
213
|
-
|
|
214
|
-
@test "per-skill column: adds Maturity column to existing Skills table" {
|
|
215
|
-
make_plugin "stub" '{
|
|
216
|
-
"name":"wr-stub","version":"0.1.0","description":"Stub",
|
|
217
|
-
"maturity":{"schema_version":"2.0","band":"Alpha","skills":{
|
|
218
|
-
"thing":{"schema_version":"2.0","band":"Alpha","computed_at":"2026-05-18T00:00:00Z","evidence":{"invocations_30d":50,"days_shipped":30,"closed_tickets_window":5,"breaking_change_age_days":null}},
|
|
219
|
-
"widget":{"schema_version":"2.0","band":"Experimental","computed_at":"2026-05-18T00:00:00Z","evidence":{"invocations_30d":2,"days_shipped":5,"closed_tickets_window":0,"breaking_change_age_days":null}}
|
|
220
|
-
}}
|
|
221
|
-
}' \
|
|
222
|
-
"# @windyroad/stub
|
|
223
|
-
|
|
224
|
-
**Stub plugin description.**
|
|
225
|
-
|
|
226
|
-
## Skills
|
|
227
|
-
|
|
228
|
-
| Skill | Purpose |
|
|
229
|
-
|-------|---------|
|
|
230
|
-
| /wr-stub:thing | Does a thing |
|
|
231
|
-
| /wr-stub:widget | Widgets things |
|
|
232
|
-
"
|
|
233
|
-
|
|
234
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
235
|
-
[ "$status" -eq 0 ]
|
|
236
|
-
|
|
237
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
238
|
-
# Header row gains Maturity column
|
|
239
|
-
[[ "$out" == *"| Skill | Purpose | Maturity |"* ]]
|
|
240
|
-
# Cells populated
|
|
241
|
-
[[ "$out" == *"/wr-stub:thing"*"| Alpha |"* ]]
|
|
242
|
-
[[ "$out" == *"/wr-stub:widget"*"| Experimental |"* ]]
|
|
243
|
-
# Per-skill cell carries band name ONLY (no compound)
|
|
244
|
-
[[ "$out" != *"| Alpha (suite"* ]]
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
# ── Idempotency: second run produces no diff ────────────────────────────────
|
|
248
|
-
|
|
249
|
-
@test "idempotency: second run against unchanged plugin.json produces byte-equal README" {
|
|
250
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Alpha"}}' \
|
|
251
|
-
"# @windyroad/stub
|
|
252
|
-
|
|
253
|
-
**Stub plugin description.**
|
|
254
|
-
|
|
255
|
-
## Skills
|
|
256
|
-
|
|
257
|
-
| Skill | Purpose |
|
|
258
|
-
|-------|---------|
|
|
259
|
-
| /wr-stub:thing | Does a thing |
|
|
260
|
-
"
|
|
261
|
-
|
|
262
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
263
|
-
[ "$status" -eq 0 ]
|
|
264
|
-
local first; first="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
265
|
-
|
|
266
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
267
|
-
[ "$status" -eq 0 ]
|
|
268
|
-
local second; second="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
269
|
-
|
|
270
|
-
[ "$first" = "$second" ]
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
@test "idempotency: existing Maturity badge gets replaced (not appended)" {
|
|
274
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Beta"}}' \
|
|
275
|
-
"# @windyroad/stub
|
|
276
|
-
|
|
277
|
-
**Stub plugin description.** *Maturity: Alpha.*
|
|
278
|
-
|
|
279
|
-
## Skills
|
|
280
|
-
|
|
281
|
-
| Skill | Purpose |
|
|
282
|
-
|-------|---------|
|
|
283
|
-
| /wr-stub:thing | Does a thing |
|
|
284
|
-
"
|
|
285
|
-
|
|
286
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
287
|
-
[ "$status" -eq 0 ]
|
|
288
|
-
|
|
289
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
290
|
-
# Beta replaced Alpha (no duplication)
|
|
291
|
-
[[ "$out" == *"*Maturity: Beta"* ]]
|
|
292
|
-
[[ "$out" != *"*Maturity: Alpha"* ]]
|
|
293
|
-
# Single Maturity span (no duplication)
|
|
294
|
-
local count
|
|
295
|
-
count="$(grep -oE '\*Maturity: [^*]+\*' "$PROJECT_ROOT/packages/stub/README.md" | wc -l)"
|
|
296
|
-
[ "$count" -eq 1 ]
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
# ── Anti-pattern: no standalone ## Maturity section emitted ─────────────────
|
|
300
|
-
|
|
301
|
-
@test "anti-pattern: renderer never emits a standalone ## Maturity section" {
|
|
302
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Alpha"}}' \
|
|
303
|
-
"# @windyroad/stub
|
|
304
|
-
|
|
305
|
-
**Stub plugin description.**
|
|
306
|
-
|
|
307
|
-
## Skills
|
|
308
|
-
|
|
309
|
-
| Skill | Purpose |
|
|
310
|
-
|-------|---------|
|
|
311
|
-
| /wr-stub:thing | Does a thing |
|
|
312
|
-
"
|
|
313
|
-
|
|
314
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
315
|
-
[ "$status" -eq 0 ]
|
|
316
|
-
|
|
317
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
318
|
-
[[ "$out" != *"## Maturity"* ]]
|
|
319
|
-
[[ "$out" != *"# Maturity"* ]]
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
@test "anti-pattern: no shields.io badge URL emitted" {
|
|
323
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Alpha"}}' \
|
|
324
|
-
"# @windyroad/stub
|
|
325
|
-
|
|
326
|
-
**Stub plugin description.**
|
|
327
|
-
|
|
328
|
-
## Skills
|
|
329
|
-
|
|
330
|
-
| Skill | Purpose |
|
|
331
|
-
|-------|---------|
|
|
332
|
-
| /wr-stub:thing | Does a thing |
|
|
333
|
-
"
|
|
334
|
-
|
|
335
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
336
|
-
[ "$status" -eq 0 ]
|
|
337
|
-
|
|
338
|
-
local out; out="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
339
|
-
[[ "$out" != *"shields.io"* ]]
|
|
340
|
-
[[ "$out" != *"img.shields"* ]]
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
# ── Fail-safe: missing maturity field is a no-op (Phase 3a not yet run) ─────
|
|
344
|
-
|
|
345
|
-
@test "fail-safe: plugin.json without maturity: field is skipped" {
|
|
346
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub"}' \
|
|
347
|
-
"# @windyroad/stub
|
|
348
|
-
|
|
349
|
-
**Stub plugin description.**
|
|
350
|
-
|
|
351
|
-
## Skills
|
|
352
|
-
|
|
353
|
-
| Skill | Purpose |
|
|
354
|
-
|-------|---------|
|
|
355
|
-
| /wr-stub:thing | Does a thing |
|
|
356
|
-
"
|
|
357
|
-
local before; before="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
358
|
-
|
|
359
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
360
|
-
[ "$status" -eq 0 ]
|
|
361
|
-
|
|
362
|
-
local after; after="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
363
|
-
[ "$before" = "$after" ]
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
# ── Fail-safe: plugin without README is skipped ─────────────────────────────
|
|
367
|
-
|
|
368
|
-
@test "fail-safe: plugin without README.md is silently skipped" {
|
|
369
|
-
local pkg="$PROJECT_ROOT/packages/no-readme"
|
|
370
|
-
mkdir -p "$pkg/.claude-plugin"
|
|
371
|
-
echo '{"name":"wr-no-readme","version":"0.1.0","description":"No README","maturity":{"schema_version":"1.0","band":"Alpha"}}' > "$pkg/.claude-plugin/plugin.json"
|
|
372
|
-
|
|
373
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
374
|
-
[ "$status" -eq 0 ]
|
|
375
|
-
[ ! -f "$pkg/README.md" ]
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
# ── No-AskUserQuestion: ADR-044 silent-framework carve-out ──────────────────
|
|
379
|
-
|
|
380
|
-
@test "ADR-044: renderer never invokes AskUserQuestion per re-render" {
|
|
381
|
-
# Negative-presence behavioural assertion per ADR-052 §carve-out — the
|
|
382
|
-
# renderer is mechanical (band already computed by Phase 3a) and must
|
|
383
|
-
# not surface a consent gate per re-render. Scans combined stdout +
|
|
384
|
-
# stderr output for any AskUserQuestion-token spelling, case-insensitive
|
|
385
|
-
# per architect adjustment G to plugin-maturity-populate.bats.
|
|
386
|
-
make_plugin "silentp" '{"name":"wr-silentp","version":"0.1.0","description":"Silent","maturity":{"schema_version":"1.0","band":"Alpha"}}' \
|
|
387
|
-
"# @windyroad/silentp
|
|
388
|
-
|
|
389
|
-
**Silent plugin description.**
|
|
390
|
-
|
|
391
|
-
## Skills
|
|
392
|
-
|
|
393
|
-
| Skill | Purpose |
|
|
394
|
-
|-------|---------|
|
|
395
|
-
| /wr-silentp:thing | Does a thing |
|
|
396
|
-
"
|
|
397
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
398
|
-
[ "$status" -eq 0 ]
|
|
399
|
-
printf '%s' "$output" | grep -i -E 'askuserquestion|<askuser' && return 1 || true
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
# ── No-network primitive: ADR-035 privacy posture ───────────────────────────
|
|
403
|
-
|
|
404
|
-
@test "ADR-035: script body invokes no network primitive" {
|
|
405
|
-
# Negative-presence behavioural assertion — the renderer reads
|
|
406
|
-
# plugin.json + README.md from filesystem only; never reaches a
|
|
407
|
-
# network endpoint. Mirrors plugin-maturity-populate.bats.
|
|
408
|
-
run grep -E "(curl|wget|nc -|netcat|ssh |scp |rsync|http\.client|urllib|requests)" "$SCRIPT"
|
|
409
|
-
[ "$status" -ne 0 ]
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
# ── Multi-plugin: renders each plugin independently ─────────────────────────
|
|
413
|
-
|
|
414
|
-
@test "multi-plugin: renders each packages/<plugin>/README.md independently" {
|
|
415
|
-
make_plugin "alpha" '{"name":"wr-alpha","version":"0.1.0","description":"Alpha","maturity":{"schema_version":"1.0","band":"Alpha"}}' \
|
|
416
|
-
"# @windyroad/alpha
|
|
417
|
-
|
|
418
|
-
**Alpha plugin description.**
|
|
419
|
-
|
|
420
|
-
## Skills
|
|
421
|
-
|
|
422
|
-
| Skill | Purpose |
|
|
423
|
-
|-------|---------|
|
|
424
|
-
| /wr-alpha:thing | Does a thing |
|
|
425
|
-
"
|
|
426
|
-
make_plugin "bravo" '{"name":"wr-bravo","version":"0.1.0","description":"Bravo","maturity":{"schema_version":"1.0","band":"Beta"}}' \
|
|
427
|
-
"# @windyroad/bravo
|
|
428
|
-
|
|
429
|
-
**Bravo plugin description.**
|
|
430
|
-
|
|
431
|
-
## Skills
|
|
432
|
-
|
|
433
|
-
| Skill | Purpose |
|
|
434
|
-
|-------|---------|
|
|
435
|
-
| /wr-bravo:thing | Does a thing |
|
|
436
|
-
"
|
|
437
|
-
|
|
438
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT"
|
|
439
|
-
[ "$status" -eq 0 ]
|
|
440
|
-
|
|
441
|
-
local out_a; out_a="$(cat "$PROJECT_ROOT/packages/alpha/README.md")"
|
|
442
|
-
local out_b; out_b="$(cat "$PROJECT_ROOT/packages/bravo/README.md")"
|
|
443
|
-
[[ "$out_a" == *"*Maturity: Alpha"* ]]
|
|
444
|
-
[[ "$out_b" == *"*Maturity: Beta"* ]]
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
# ── Dry-run: prints diff to stdout, does not write ──────────────────────────
|
|
448
|
-
|
|
449
|
-
@test "dry-run: --dry-run flag prints intended diff to stdout without modifying README" {
|
|
450
|
-
make_plugin "stub" '{"name":"wr-stub","version":"0.1.0","description":"Stub","maturity":{"schema_version":"1.0","band":"Alpha"}}' \
|
|
451
|
-
"# @windyroad/stub
|
|
452
|
-
|
|
453
|
-
**Stub plugin description.**
|
|
454
|
-
|
|
455
|
-
## Skills
|
|
456
|
-
|
|
457
|
-
| Skill | Purpose |
|
|
458
|
-
|-------|---------|
|
|
459
|
-
| /wr-stub:thing | Does a thing |
|
|
460
|
-
"
|
|
461
|
-
local before; before="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
462
|
-
|
|
463
|
-
run bash "$SCRIPT" --project-root="$PROJECT_ROOT" --dry-run
|
|
464
|
-
[ "$status" -eq 0 ]
|
|
465
|
-
[[ "$output" == *"Maturity: Alpha"* ]]
|
|
466
|
-
|
|
467
|
-
local after; after="$(cat "$PROJECT_ROOT/packages/stub/README.md")"
|
|
468
|
-
[ "$before" = "$after" ]
|
|
469
|
-
}
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# @problem P263 — CI pre-publish manifest-validity gate. Adds the
|
|
4
|
-
# `claude plugin validate` (non-strict) loop ADR-063
|
|
5
|
-
# §Confirmation #11 names as the post-bats coverage
|
|
6
|
-
# layer that catches P258-class manifest breakages
|
|
7
|
-
# (recognised top-level key with wrong-typed content)
|
|
8
|
-
# without rejecting the ADR-063 `maturity:` safe-
|
|
9
|
-
# extension pattern that --strict would reject.
|
|
10
|
-
#
|
|
11
|
-
# Contract: `plugin-validate-ci-gate.sh` (invoked via ADR-049 shim
|
|
12
|
-
# `wr-itil-plugin-validate-ci-gate`) walks every
|
|
13
|
-
# `packages/*/.claude-plugin/plugin.json` in CWD, invokes `claude
|
|
14
|
-
# plugin validate <plugin_dir>` (NON-strict) per plugin, and exits
|
|
15
|
-
# non-zero if ANY plugin's manifest fails validation. The loop does
|
|
16
|
-
# NOT short-circuit on first failure — every plugin is exercised so
|
|
17
|
-
# the CI failure surfaces every defect at once.
|
|
18
|
-
#
|
|
19
|
-
# Behavioural test strategy (ADR-052): a programmable stub `claude`
|
|
20
|
-
# on PATH simulates `claude plugin validate`. The stub exits 1 when
|
|
21
|
-
# the manifest carries a top-level `hooks:` key whose body holds a
|
|
22
|
-
# `schema_version` sub-key (the P258 reproduction class — recognised
|
|
23
|
-
# key, wrong-typed content); exits 0 otherwise (including the ADR-063
|
|
24
|
-
# top-level `maturity:` safe-extension shape — unrecognised key, warn-
|
|
25
|
-
# only under non-strict). The stub captures the load-bearing
|
|
26
|
-
# behavioural contract of `claude plugin validate` non-strict for
|
|
27
|
-
# the two manifest shapes P263's design tension turns on. The real
|
|
28
|
-
# CLI is exercised at the CI workflow layer (`.github/workflows/
|
|
29
|
-
# ci.yml`) — split per ADR-052 (bats covers script-owned loop +
|
|
30
|
-
# failure-aggregation behaviour with a faithful test double; CI
|
|
31
|
-
# exercises the real CLI surface end-to-end).
|
|
32
|
-
#
|
|
33
|
-
# @adr ADR-063 Amendment 2026-05-18 §Confirmation #11 (the gate
|
|
34
|
-
# criterion this script satisfies)
|
|
35
|
-
# @adr ADR-049 (bin/ on PATH shim — `wr-itil-plugin-validate-ci-gate`)
|
|
36
|
-
# @adr ADR-052 (Behavioural tests default — stub-claude faithfully
|
|
37
|
-
# models the validator's non-strict exit contract for
|
|
38
|
-
# the two manifest shapes P263 turns on)
|
|
39
|
-
# @adr ADR-014 (single-commit grain — script + shim + bats + CI +
|
|
40
|
-
# ADR amendment + changeset land together)
|
|
41
|
-
# @adr ADR-022 (KE WSJF multiplier — informs prioritisation)
|
|
42
|
-
# @jtbd JTBD-101 (Extend the Suite with New Plugins — primary
|
|
43
|
-
# alignment; named Desired Outcome at L19 strengthened
|
|
44
|
-
# by this gate)
|
|
45
|
-
# @jtbd JTBD-202 (Pre-Flight Governance Checks Before Release —
|
|
46
|
-
# secondary alignment; gate fires in Quality Gates job)
|
|
47
|
-
|
|
48
|
-
setup() {
|
|
49
|
-
SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
50
|
-
SCRIPT="$SCRIPTS_DIR/plugin-validate-ci-gate.sh"
|
|
51
|
-
SHIM="$(cd "$SCRIPTS_DIR/../bin" && pwd)/wr-itil-plugin-validate-ci-gate"
|
|
52
|
-
FIXTURE_DIR="$(mktemp -d)"
|
|
53
|
-
STUB_DIR="$FIXTURE_DIR/stub-bin"
|
|
54
|
-
mkdir -p "$STUB_DIR"
|
|
55
|
-
cd "$FIXTURE_DIR"
|
|
56
|
-
mkdir -p packages
|
|
57
|
-
write_stub_claude
|
|
58
|
-
export PATH="$STUB_DIR:$PATH"
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
teardown() {
|
|
62
|
-
cd /
|
|
63
|
-
rm -rf "$FIXTURE_DIR"
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
# Programmable stub for `claude plugin validate <plugin_dir>`. Faithful
|
|
67
|
-
# model of the non-strict exit contract for the two manifest shapes
|
|
68
|
-
# P263 turns on:
|
|
69
|
-
# - Top-level `hooks:` carrying a `schema_version` sub-key (the P258
|
|
70
|
-
# reproduction — recognised key with wrong-typed content) → exit 1.
|
|
71
|
-
# - Anything else (including ADR-063 top-level `maturity:` safe-
|
|
72
|
-
# extension shape, where `maturity:` is unrecognised → warn-only
|
|
73
|
-
# under non-strict) → exit 0.
|
|
74
|
-
write_stub_claude() {
|
|
75
|
-
cat > "$STUB_DIR/claude" <<'STUB'
|
|
76
|
-
#!/usr/bin/env bash
|
|
77
|
-
[[ "$1" == "plugin" && "$2" == "validate" ]] || { echo "stub: unsupported args $*" >&2; exit 2; }
|
|
78
|
-
plugin_dir="$3"
|
|
79
|
-
manifest="$plugin_dir/.claude-plugin/plugin.json"
|
|
80
|
-
[ -f "$manifest" ] || { echo "stub: manifest not found at $manifest" >&2; exit 1; }
|
|
81
|
-
# Detect P258 reproduction: top-level "hooks": { ... "schema_version": ... }
|
|
82
|
-
if python3 -c "
|
|
83
|
-
import json, sys
|
|
84
|
-
with open(sys.argv[1]) as f:
|
|
85
|
-
m = json.load(f)
|
|
86
|
-
for key in ('hooks', 'skills', 'agents', 'commands'):
|
|
87
|
-
v = m.get(key)
|
|
88
|
-
if isinstance(v, dict):
|
|
89
|
-
for sub in v.values():
|
|
90
|
-
if isinstance(sub, dict) and 'schema_version' in sub:
|
|
91
|
-
sys.exit(1)
|
|
92
|
-
sys.exit(0)
|
|
93
|
-
" "$manifest"; then
|
|
94
|
-
echo "stub: $plugin_dir OK"
|
|
95
|
-
exit 0
|
|
96
|
-
else
|
|
97
|
-
echo "stub: $plugin_dir Validation errors: hooks: Invalid input" >&2
|
|
98
|
-
exit 1
|
|
99
|
-
fi
|
|
100
|
-
STUB
|
|
101
|
-
chmod +x "$STUB_DIR/claude"
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
# Helper: write a synthetic plugin manifest at packages/<name>/.claude-plugin/plugin.json
|
|
105
|
-
write_plugin() {
|
|
106
|
-
local name="$1"
|
|
107
|
-
local body="$2"
|
|
108
|
-
mkdir -p "packages/$name/.claude-plugin"
|
|
109
|
-
printf '%s\n' "$body" > "packages/$name/.claude-plugin/plugin.json"
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
# ── Existence / executable ──────────────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
@test "plugin-validate-ci-gate: canonical script exists" {
|
|
115
|
-
[ -f "$SCRIPT" ]
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
@test "plugin-validate-ci-gate: canonical script is executable" {
|
|
119
|
-
[ -x "$SCRIPT" ]
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
@test "plugin-validate-ci-gate: ADR-049 shim exists" {
|
|
123
|
-
[ -f "$SHIM" ]
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
@test "plugin-validate-ci-gate: ADR-049 shim is executable" {
|
|
127
|
-
[ -x "$SHIM" ]
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
@test "plugin-validate-ci-gate: shim is thin (single exec line to canonical body)" {
|
|
131
|
-
grep -qE '^exec .*plugin-validate-ci-gate\.sh' "$SHIM"
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
# ── Fixture A: positive — ADR-063 safe-extension shape passes ───────────────
|
|
135
|
-
|
|
136
|
-
@test "plugin-validate-ci-gate: ADR-063 maturity: top-level safe-extension shape → exit 0" {
|
|
137
|
-
write_plugin "alpha" '{
|
|
138
|
-
"name": "alpha",
|
|
139
|
-
"version": "1.0.0",
|
|
140
|
-
"description": "test plugin",
|
|
141
|
-
"maturity": {
|
|
142
|
-
"schema_version": "2.0",
|
|
143
|
-
"band": "Experimental"
|
|
144
|
-
}
|
|
145
|
-
}'
|
|
146
|
-
|
|
147
|
-
run "$SCRIPT"
|
|
148
|
-
[ "$status" -eq 0 ]
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
@test "plugin-validate-ci-gate: multiple plugins all ADR-063-shaped → exit 0" {
|
|
152
|
-
write_plugin "alpha" '{"name":"alpha","version":"1.0.0","description":"t","maturity":{"schema_version":"2.0","band":"Experimental"}}'
|
|
153
|
-
write_plugin "beta" '{"name":"beta","version":"1.0.0","description":"t","maturity":{"schema_version":"2.0","band":"Stable"}}'
|
|
154
|
-
write_plugin "gamma" '{"name":"gamma","version":"1.0.0","description":"t"}'
|
|
155
|
-
|
|
156
|
-
run "$SCRIPT"
|
|
157
|
-
[ "$status" -eq 0 ]
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
# ── Fixture B: negative — P258 reproduction shape fails ─────────────────────
|
|
161
|
-
|
|
162
|
-
@test "plugin-validate-ci-gate: P258 reproduction (top-level hooks: with schema_version) → exit 1" {
|
|
163
|
-
write_plugin "broken" '{
|
|
164
|
-
"name": "broken",
|
|
165
|
-
"version": "1.0.0",
|
|
166
|
-
"description": "test plugin with P258 mistake",
|
|
167
|
-
"hooks": {
|
|
168
|
-
"some-hook": {
|
|
169
|
-
"schema_version": "1.0",
|
|
170
|
-
"band": "Experimental"
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}'
|
|
174
|
-
|
|
175
|
-
run "$SCRIPT"
|
|
176
|
-
[ "$status" -ne 0 ]
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
@test "plugin-validate-ci-gate: mixed pass/fail loops every plugin (no short-circuit) → exit 1" {
|
|
180
|
-
write_plugin "alpha" '{"name":"alpha","version":"1.0.0","description":"t","maturity":{"schema_version":"2.0","band":"Experimental"}}'
|
|
181
|
-
write_plugin "broken" '{"name":"broken","version":"1.0.0","description":"t","hooks":{"h":{"schema_version":"1.0","band":"Experimental"}}}'
|
|
182
|
-
write_plugin "gamma" '{"name":"gamma","version":"1.0.0","description":"t"}'
|
|
183
|
-
|
|
184
|
-
run "$SCRIPT"
|
|
185
|
-
[ "$status" -ne 0 ]
|
|
186
|
-
# All three plugins must be exercised — no short-circuit on first failure.
|
|
187
|
-
echo "$output" | grep -q "alpha"
|
|
188
|
-
echo "$output" | grep -q "broken"
|
|
189
|
-
echo "$output" | grep -q "gamma"
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
# ── No-plugins case (CI is always run against the monorepo tree, but the
|
|
193
|
-
# fail-safe matters for adopter-tree invocation per ADR-049 portability).
|
|
194
|
-
|
|
195
|
-
@test "plugin-validate-ci-gate: no plugins under packages/ → exit 0 (nothing to validate)" {
|
|
196
|
-
# No packages/<name>/.claude-plugin/plugin.json written.
|
|
197
|
-
run "$SCRIPT"
|
|
198
|
-
[ "$status" -eq 0 ]
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
# ── Shim PATH-resolution behaviour (ADR-049 contract) ──────────────────────
|
|
202
|
-
|
|
203
|
-
@test "plugin-validate-ci-gate: invoking shim runs the canonical body" {
|
|
204
|
-
write_plugin "alpha" '{"name":"alpha","version":"1.0.0","description":"t","maturity":{"schema_version":"2.0","band":"Experimental"}}'
|
|
205
|
-
|
|
206
|
-
run "$SHIM"
|
|
207
|
-
[ "$status" -eq 0 ]
|
|
208
|
-
}
|