@windyroad/retrospective 0.27.1 → 0.27.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 (33) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/package.json +5 -2
  3. package/hooks/test/retrospective-deferral-census.bats +0 -112
  4. package/hooks/test/retrospective-readme-jtbd-currency.bats +0 -312
  5. package/scripts/test/check-ask-hygiene.bats +0 -262
  6. package/scripts/test/check-autocreate-rfc-scope.bats +0 -250
  7. package/scripts/test/check-briefing-budgets.bats +0 -349
  8. package/scripts/test/check-internal-id-leaks.bats +0 -286
  9. package/scripts/test/check-plugin-maturity-drift.bats +0 -269
  10. package/scripts/test/check-readme-jtbd-currency.bats +0 -165
  11. package/scripts/test/check-skill-md-budgets.bats +0 -336
  12. package/scripts/test/check-tarball-shipped-shims.bats +0 -284
  13. package/scripts/test/check-tickets-deferred-cause.bats +0 -330
  14. package/scripts/test/list-plugin-attribution.bats +0 -149
  15. package/scripts/test/measure-context-budget.bats +0 -298
  16. package/skills/analyze-context/test/analyze-context-skill-contract.bats +0 -142
  17. package/skills/migrate-briefing/test/migrate-briefing-contract.bats +0 -105
  18. package/skills/migrate-briefing/test/migrate-briefing-fixture.bats +0 -242
  19. package/skills/run-retro/test/run-retro-anti-pattern-clause.bats +0 -87
  20. package/skills/run-retro/test/run-retro-codification-candidates.bats +0 -168
  21. package/skills/run-retro/test/run-retro-context-usage-step-2c.bats +0 -156
  22. package/skills/run-retro/test/run-retro-pipeline-instability-scan.bats +0 -122
  23. package/skills/run-retro/test/run-retro-signal-vs-noise.bats +0 -137
  24. package/skills/run-retro/test/run-retro-skill-candidates.bats +0 -116
  25. package/skills/run-retro/test/run-retro-stage-1-fallback-gating.bats +0 -65
  26. package/skills/run-retro/test/run-retro-step-2d-r6-auto-flag.bats +0 -82
  27. package/skills/run-retro/test/run-retro-step-3-tier-3-branch-b-evidence-based.bats +0 -189
  28. package/skills/run-retro/test/run-retro-step-4a-cross-plugin-dispatch.bats +0 -114
  29. package/skills/run-retro/test/run-retro-step-4a-prior-session-evidence-drain.bats +0 -168
  30. package/skills/run-retro/test/run-retro-step-4a-recovery-path.bats +0 -84
  31. package/skills/run-retro/test/run-retro-step-4b-retro-auto-ticket-carveout.bats +0 -115
  32. package/skills/run-retro/test/run-retro-ticket-first-flow.bats +0 -161
  33. package/skills/run-retro/test/run-retro-verification-close-housekeeping.bats +0 -132
@@ -1,336 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # @problem P097 — SKILL.md files mix runtime-necessary steps with maintainer-
4
- # facing rationale, bloating every skill invocation. ADR-054 codifies the
5
- # classification taxonomy ([runtime] / [reference] / [deprecated]), the
6
- # sibling REFERENCE.md lazy-load pattern, and the byte budgets (WARN ≥
7
- # 8192, MUST_SPLIT ≥ 16384). This bats fixture covers the advisory
8
- # detector script that surfaces SKILL.md files exceeding the budget.
9
- #
10
- # Contract: `check-skill-md-budgets.sh [<root-dir>]` is a diagnose-only
11
- # advisory script. It walks `<root-dir>/packages/*/skills/*/SKILL.md` and
12
- # `<root-dir>/.claude/skills/*/SKILL.md` (default `<root-dir>` is `.`),
13
- # measures byte size per file, and reports each SKILL.md whose size is at
14
- # or above the WARN threshold (default 8192 bytes, overridable via
15
- # SKILL_MD_WARN_BYTES env var).
16
- #
17
- # Exit codes:
18
- # 0 = always (advisory only — overflow is signal, not failure)
19
- # 2 = parse error (root dir missing or unreadable)
20
- #
21
- # Output format on overflow (one line per file, terse machine-readable
22
- # per ADR-038 progressive-disclosure budget):
23
- # OVER <plugin>/<skill> bytes=<N> threshold=<N>
24
- #
25
- # Files at >= MUST_SPLIT (default 16384, overridable via SKILL_MD_MUST_SPLIT_BYTES)
26
- # also emit a second line:
27
- # MUST_SPLIT <plugin>/<skill> reason=<code>
28
- #
29
- # This mirrors the OVER / MUST_SPLIT pair shape from `check-briefing-budgets.sh`
30
- # (P099 / P145 / ADR-040) deliberately so adopters learn one concept across
31
- # two surfaces.
32
- #
33
- # Output ordering (deterministic for stable retro-summary diffs):
34
- # 1. All OVER lines, sorted by `<plugin>/<skill>` identifier.
35
- # 2. Then all MUST_SPLIT lines, sorted by identifier.
36
- #
37
- # Output is empty (no lines) when no SKILL.md exceeds the WARN threshold.
38
- # REFERENCE.md sibling files (per ADR-054) are excluded from the scan —
39
- # they are intentionally lazy-loaded and not subject to the runtime budget.
40
- #
41
- # Read-only — does NOT mutate any SKILL.md file. Extraction priority is
42
- # surfaced to the maintainer; rotation is opportunistic per ADR-052
43
- # migration shape.
44
- #
45
- # This fixture is BEHAVIOURAL per ADR-052 — it asserts script output on
46
- # temp-fixture skill trees, NOT script source content. No greps of
47
- # check-skill-md-budgets.sh source.
48
- #
49
- # @jtbd JTBD-001 (Enforce Governance Without Slowing Down — solo dev;
50
- # read-only, no interactive friction on the happy path)
51
- # @jtbd JTBD-006 (Progress the Backlog While I'm Away — AFK-safe advisory)
52
- # @jtbd JTBD-101 (Extend the Suite with Clear Patterns — reusable
53
- # advisory-script + bats + ADR-amendment shape for context-budget surfaces)
54
- #
55
- # Cross-reference:
56
- # P097: docs/problems/097-skill-md-runtime-size-mixes-policy-with-runtime-steps.*.md
57
- # ADR-054 — SKILL.md runtime budget policy (taxonomy + sibling pattern + budget)
58
- # ADR-040 — Session-start briefing surface (Tier 3 OVER / MUST_SPLIT precedent)
59
- # ADR-038 — Progressive disclosure (per-row byte budget on diff output)
60
- # ADR-052 — Behavioural-tests-default (this fixture's pattern)
61
- # ADR-005 — Plugin testing strategy (script-level bats governance)
62
-
63
- setup() {
64
- SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
65
- SCRIPT="$SCRIPTS_DIR/check-skill-md-budgets.sh"
66
- FIXTURE_ROOT="$(mktemp -d)"
67
- }
68
-
69
- teardown() {
70
- rm -rf "$FIXTURE_ROOT"
71
- }
72
-
73
- # Helper: write a SKILL.md with N bytes of body content under a fixture
74
- # plugin/skill path. Creates the directory tree as needed.
75
- write_skill() {
76
- local plugin="$1"
77
- local skill="$2"
78
- local target_bytes="$3"
79
- local skill_dir="$FIXTURE_ROOT/packages/$plugin/skills/$skill"
80
- mkdir -p "$skill_dir"
81
- local path="$skill_dir/SKILL.md"
82
- : > "$path"
83
- printf '# Skill\n\n' >> "$path"
84
- local header_size
85
- header_size=$(wc -c < "$path" | tr -d ' ')
86
- local body_target=$(( target_bytes - header_size ))
87
- if [ "$body_target" -gt 0 ]; then
88
- local line="- step text padded out to a known length for byte-budget testing. "
89
- local line_size=${#line}
90
- line+=$'\n'
91
- local line_count=$(( (body_target + line_size) / (line_size + 1) ))
92
- local i=0
93
- while [ "$i" -lt "$line_count" ]; do
94
- printf '%s' "$line" >> "$path"
95
- i=$(( i + 1 ))
96
- done
97
- fi
98
- }
99
-
100
- # Helper: write a project-local SKILL.md under .claude/skills/<skill>/SKILL.md
101
- # (the project-local skill surface, in scope per ADR-054 §Phase 1 deliverable).
102
- write_local_skill() {
103
- local skill="$1"
104
- local target_bytes="$2"
105
- local skill_dir="$FIXTURE_ROOT/.claude/skills/$skill"
106
- mkdir -p "$skill_dir"
107
- local path="$skill_dir/SKILL.md"
108
- : > "$path"
109
- printf '# Skill\n\n' >> "$path"
110
- local header_size
111
- header_size=$(wc -c < "$path" | tr -d ' ')
112
- local body_target=$(( target_bytes - header_size ))
113
- if [ "$body_target" -gt 0 ]; then
114
- local line="- step text padded out to a known length for byte-budget testing. "
115
- local line_size=${#line}
116
- line+=$'\n'
117
- local line_count=$(( (body_target + line_size) / (line_size + 1) ))
118
- local i=0
119
- while [ "$i" -lt "$line_count" ]; do
120
- printf '%s' "$line" >> "$path"
121
- i=$(( i + 1 ))
122
- done
123
- fi
124
- }
125
-
126
- # ── Existence + executable ──────────────────────────────────────────────────
127
-
128
- @test "check-skill-md-budgets: script exists" {
129
- [ -f "$SCRIPT" ]
130
- }
131
-
132
- @test "check-skill-md-budgets: script is executable" {
133
- [ -x "$SCRIPT" ]
134
- }
135
-
136
- # ── Default-threshold behaviour (WARN 8192, MUST_SPLIT 16384) ───────────────
137
-
138
- @test "check-skill-md-budgets: empty tree produces no output and exits 0" {
139
- mkdir -p "$FIXTURE_ROOT/packages"
140
- run "$SCRIPT" "$FIXTURE_ROOT"
141
- [ "$status" -eq 0 ]
142
- [ -z "$output" ]
143
- }
144
-
145
- @test "check-skill-md-budgets: all skills under WARN produces no output" {
146
- write_skill "alpha" "small" 1024
147
- write_skill "beta" "medium" 4096
148
- write_skill "gamma" "near-warn" 7000
149
- run "$SCRIPT" "$FIXTURE_ROOT"
150
- [ "$status" -eq 0 ]
151
- [ -z "$output" ]
152
- }
153
-
154
- @test "check-skill-md-budgets: skill at WARN band emits OVER line with bytes + threshold" {
155
- write_skill "alpha" "warn-band" 10000
156
- run "$SCRIPT" "$FIXTURE_ROOT"
157
- [ "$status" -eq 0 ]
158
- echo "$output" | grep -E "^OVER alpha/warn-band bytes=[0-9]+ threshold=8192$"
159
- }
160
-
161
- @test "check-skill-md-budgets: skill exactly at WARN threshold emits OVER (>= boundary)" {
162
- local skill_dir="$FIXTURE_ROOT/packages/alpha/skills/edge"
163
- mkdir -p "$skill_dir"
164
- printf '%.0s.' $(seq 1 8192) > "$skill_dir/SKILL.md"
165
- run "$SCRIPT" "$FIXTURE_ROOT"
166
- [ "$status" -eq 0 ]
167
- echo "$output" | grep -E "^OVER alpha/edge bytes=8192 threshold=8192$"
168
- }
169
-
170
- @test "check-skill-md-budgets: skill at WARN band but under MUST_SPLIT emits OVER only (no MUST_SPLIT)" {
171
- write_skill "alpha" "warn-only" 12000
172
- run "$SCRIPT" "$FIXTURE_ROOT"
173
- [ "$status" -eq 0 ]
174
- echo "$output" | grep -E "^OVER alpha/warn-only bytes=[0-9]+ threshold=8192"
175
- ! echo "$output" | grep -q "MUST_SPLIT"
176
- }
177
-
178
- @test "check-skill-md-budgets: skill at exactly MUST_SPLIT emits OVER + MUST_SPLIT" {
179
- local skill_dir="$FIXTURE_ROOT/packages/alpha/skills/exactly-must-split"
180
- mkdir -p "$skill_dir"
181
- printf '%.0s.' $(seq 1 16384) > "$skill_dir/SKILL.md"
182
- run "$SCRIPT" "$FIXTURE_ROOT"
183
- [ "$status" -eq 0 ]
184
- echo "$output" | grep -E "^OVER alpha/exactly-must-split bytes=16384 threshold=8192"
185
- echo "$output" | grep -E "^MUST_SPLIT alpha/exactly-must-split reason=ratio-exceeds-must-split$"
186
- }
187
-
188
- @test "check-skill-md-budgets: skill well over MUST_SPLIT emits both lines" {
189
- write_skill "alpha" "very-bloated" 80000
190
- run "$SCRIPT" "$FIXTURE_ROOT"
191
- [ "$status" -eq 0 ]
192
- echo "$output" | grep -E "^OVER alpha/very-bloated bytes=[0-9]+ threshold=8192"
193
- echo "$output" | grep -E "^MUST_SPLIT alpha/very-bloated reason=ratio-exceeds-must-split$"
194
- }
195
-
196
- @test "check-skill-md-budgets: only over-threshold skills appear in output" {
197
- write_skill "alpha" "under" 2000
198
- write_skill "beta" "over-warn" 10000
199
- write_skill "gamma" "over-must-split" 20000
200
- run "$SCRIPT" "$FIXTURE_ROOT"
201
- [ "$status" -eq 0 ]
202
- echo "$output" | grep -E "^OVER beta/over-warn bytes=[0-9]+ threshold=8192"
203
- echo "$output" | grep -E "^OVER gamma/over-must-split bytes=[0-9]+ threshold=8192"
204
- ! echo "$output" | grep -q "alpha/under"
205
- }
206
-
207
- # ── Sibling REFERENCE.md exclusion (ADR-054) ────────────────────────────────
208
-
209
- @test "check-skill-md-budgets: REFERENCE.md sibling is excluded from the scan" {
210
- write_skill "alpha" "with-ref" 1000
211
- # Write a bloated REFERENCE.md that would otherwise trip MUST_SPLIT
212
- printf '%.0s.' $(seq 1 50000) > "$FIXTURE_ROOT/packages/alpha/skills/with-ref/REFERENCE.md"
213
- run "$SCRIPT" "$FIXTURE_ROOT"
214
- [ "$status" -eq 0 ]
215
- # No output — only SKILL.md is measured, not REFERENCE.md
216
- [ -z "$output" ]
217
- }
218
-
219
- # ── Project-local .claude/skills discovery ──────────────────────────────────
220
-
221
- @test "check-skill-md-budgets: .claude/skills/* SKILL.md files are scanned" {
222
- write_local_skill "install-updates" 12000
223
- run "$SCRIPT" "$FIXTURE_ROOT"
224
- [ "$status" -eq 0 ]
225
- # The project-local prefix is .claude in the identifier
226
- echo "$output" | grep -E "^OVER .claude/install-updates bytes=[0-9]+ threshold=8192"
227
- }
228
-
229
- @test "check-skill-md-budgets: project-local + plugin-skill outputs both appear" {
230
- write_skill "alpha" "plugin-skill" 10000
231
- write_local_skill "local-skill" 12000
232
- run "$SCRIPT" "$FIXTURE_ROOT"
233
- [ "$status" -eq 0 ]
234
- echo "$output" | grep -E "^OVER .claude/local-skill"
235
- echo "$output" | grep -E "^OVER alpha/plugin-skill"
236
- }
237
-
238
- # ── Configurable thresholds via env vars ────────────────────────────────────
239
-
240
- @test "check-skill-md-budgets: SKILL_MD_WARN_BYTES env var overrides default" {
241
- write_skill "alpha" "small-by-default" 4096
242
- # Default 8192: no output. With env var 2000: over threshold.
243
- SKILL_MD_WARN_BYTES=2000 run "$SCRIPT" "$FIXTURE_ROOT"
244
- [ "$status" -eq 0 ]
245
- echo "$output" | grep -E "^OVER alpha/small-by-default bytes=[0-9]+ threshold=2000"
246
- }
247
-
248
- @test "check-skill-md-budgets: SKILL_MD_MUST_SPLIT_BYTES env var overrides default" {
249
- write_skill "alpha" "moderate" 9000
250
- # Default WARN 8192: emits OVER. Default MUST_SPLIT 16384: no MUST_SPLIT.
251
- # With MUST_SPLIT override 8500: should emit MUST_SPLIT too.
252
- SKILL_MD_MUST_SPLIT_BYTES=8500 run "$SCRIPT" "$FIXTURE_ROOT"
253
- [ "$status" -eq 0 ]
254
- echo "$output" | grep -E "^OVER alpha/moderate"
255
- echo "$output" | grep -E "^MUST_SPLIT alpha/moderate reason=ratio-exceeds-must-split$"
256
- }
257
-
258
- @test "check-skill-md-budgets: env var threshold of 0 emits every skill (sanity)" {
259
- write_skill "alpha" "tiny-one" 100
260
- write_skill "beta" "tiny-two" 200
261
- SKILL_MD_WARN_BYTES=0 run "$SCRIPT" "$FIXTURE_ROOT"
262
- [ "$status" -eq 0 ]
263
- echo "$output" | grep -q "OVER alpha/tiny-one"
264
- echo "$output" | grep -q "OVER beta/tiny-two"
265
- }
266
-
267
- # ── Argument and error handling ─────────────────────────────────────────────
268
-
269
- @test "check-skill-md-budgets: defaults to current directory when no arg provided" {
270
- cd "$FIXTURE_ROOT"
271
- write_skill "alpha" "default-arg" 12000
272
- run "$SCRIPT"
273
- [ "$status" -eq 0 ]
274
- echo "$output" | grep -E "^OVER alpha/default-arg bytes=[0-9]+ threshold=8192"
275
- }
276
-
277
- @test "check-skill-md-budgets: missing root dir exits 2 with parse error on stderr" {
278
- run "$SCRIPT" "$FIXTURE_ROOT/does-not-exist"
279
- [ "$status" -eq 2 ]
280
- echo "$output" | grep -iE "not found|missing|does not exist"
281
- }
282
-
283
- @test "check-skill-md-budgets: ignores non-SKILL.md files in skill dirs" {
284
- local skill_dir="$FIXTURE_ROOT/packages/alpha/skills/with-extras"
285
- mkdir -p "$skill_dir"
286
- # Bloated non-SKILL.md files should not trip the scan
287
- printf '%.0s.' $(seq 1 50000) > "$skill_dir/NOTES.md"
288
- printf '%.0s.' $(seq 1 50000) > "$skill_dir/scratch.txt"
289
- # Small SKILL.md under threshold
290
- printf '%.0s.' $(seq 1 1000) > "$skill_dir/SKILL.md"
291
- run "$SCRIPT" "$FIXTURE_ROOT"
292
- [ "$status" -eq 0 ]
293
- [ -z "$output" ]
294
- }
295
-
296
- # ── Output stability ────────────────────────────────────────────────────────
297
-
298
- @test "check-skill-md-budgets: output is sorted by identifier for stable diffs" {
299
- write_skill "zeta" "one" 10000
300
- write_skill "alpha" "one" 10000
301
- write_skill "middle" "one" 10000
302
- run "$SCRIPT" "$FIXTURE_ROOT"
303
- [ "$status" -eq 0 ]
304
- first_line=$(echo "$output" | head -1)
305
- last_line=$(echo "$output" | tail -1)
306
- echo "$first_line" | grep -q "alpha/one"
307
- echo "$last_line" | grep -q "zeta/one"
308
- }
309
-
310
- @test "check-skill-md-budgets: mixed OVER + MUST_SPLIT output uses block ordering" {
311
- # Three OVER files; two of them also MUST_SPLIT. Output must be
312
- # deterministic so retro summary diffs stay stable across cycles.
313
- # Contract: OVER block (sorted by identifier) followed by MUST_SPLIT
314
- # block (sorted by identifier).
315
- write_skill "zeta" "over-only" 10000
316
- write_skill "alpha" "must-split" 20000
317
- write_skill "middle" "must-split" 25000
318
- run "$SCRIPT" "$FIXTURE_ROOT"
319
- [ "$status" -eq 0 ]
320
- # Three OVER lines — alpha first, middle next, zeta last
321
- over_lines=$(echo "$output" | grep "^OVER ")
322
- [ "$(echo "$over_lines" | wc -l | tr -d ' ')" = "3" ]
323
- echo "$over_lines" | sed -n '1p' | grep -q "alpha/must-split"
324
- echo "$over_lines" | sed -n '2p' | grep -q "middle/must-split"
325
- echo "$over_lines" | sed -n '3p' | grep -q "zeta/over-only"
326
- # Two MUST_SPLIT lines — alpha first, middle second; zeta-over-only NOT present
327
- must_lines=$(echo "$output" | grep "^MUST_SPLIT ")
328
- [ "$(echo "$must_lines" | wc -l | tr -d ' ')" = "2" ]
329
- echo "$must_lines" | sed -n '1p' | grep -q "alpha/must-split"
330
- echo "$must_lines" | sed -n '2p' | grep -q "middle/must-split"
331
- ! echo "$must_lines" | grep -q "zeta/over-only"
332
- # All OVER lines come before any MUST_SPLIT line (block ordering)
333
- first_must_line_no=$(echo "$output" | grep -n "^MUST_SPLIT " | head -1 | cut -d: -f1)
334
- last_over_line_no=$(echo "$output" | grep -n "^OVER " | tail -1 | cut -d: -f1)
335
- [ "$last_over_line_no" -lt "$first_must_line_no" ]
336
- }
@@ -1,284 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # Behavioural fixture for the tarball-shipped-shims advisory detector — per
4
- # WR-P154 (P137 namespace-prefix detector must run against npm pack output
5
- # not source tree) and the WR-ADR-049 / WR-ADR-052 / WR-ADR-055 cluster.
6
- #
7
- # Contract: `check-tarball-shipped-shims.sh [<root-dir>]` is a diagnose-only
8
- # advisory script. It walks workspaces under `<root-dir>/packages/*/`, runs
9
- # `npm pack --dry-run --json` per workspace to enumerate the file set that
10
- # ships, then asserts that every WR-ADR-049-grammar bin shim
11
- # (`bin/wr-<plugin>-<name>`) in the tarball has its `exec`'d
12
- # `scripts/<name>.sh` target also in the tarball.
13
- #
14
- # Surface: shipped publish-manifest integrity for bin/scripts/ shim
15
- # resolvability — different correctness axis than check-internal-id-leaks.sh
16
- # (which measures source-tree namespace-prefix drift).
17
- #
18
- # WR-ADR-049-grammar shims that this script considers:
19
- # bin/wr-<plugin>-<name>
20
- # Non-grammar bins (e.g. `bin/install.mjs`, `bin/check-deps.sh`,
21
- # `bin/windyroad-<plugin>` legacy installers) are skipped — they don't
22
- # follow the script-resolution-via-bin-on-PATH ADR-049 contract this
23
- # script enforces.
24
- #
25
- # Exit codes:
26
- # 0 = always (advisory only — drift is signal, not failure)
27
- # 2 = parse error (root dir missing/unreadable, or npm unavailable)
28
- #
29
- # Output format on drift (terse machine-readable per WR-ADR-038):
30
- # TARBALL_DRIFT package=<name> shim=<bin/wr-...> target=<scripts/...> tarball-status=missing
31
- #
32
- # Followed by a final summary line (always emitted when any drift exists):
33
- # TOTAL packages=<N> with_drift=<M> missing_targets=<K>
34
- #
35
- # Output is empty (no lines) when no shipped artefact carries broken
36
- # shims — silent-on-pass per WR-ADR-045 hook injection budget.
37
- #
38
- # Output ordering (deterministic for stable retro-summary diffs):
39
- # TARBALL_DRIFT lines sorted by `<package>/<shim>` identifier.
40
- # TOTAL line last.
41
- #
42
- # Read-only — does NOT mutate any artefact. Per WR-ADR-052, this fixture
43
- # is BEHAVIOURAL — it asserts script output on temp-fixture trees, NOT
44
- # script source content. No greps of check-tarball-shipped-shims.sh source.
45
- #
46
- # @problem P154 (P137 namespace-prefix detector must run against
47
- # npm pack output not source tree)
48
- # @problem P140 (Step 6.5 fix-and-continue — same prevention surface)
49
- # @adr ADR-049 (Plugin script resolution via bin/ on PATH)
50
- # @adr ADR-038 (Progressive disclosure — terse machine-readable signal)
51
- # @adr ADR-045 (Hook injection budget — silent-on-pass)
52
- # @adr ADR-052 (Behavioural-tests-default — fixture pattern)
53
- # @adr ADR-055 (Plugin-published namespace-prefixed permalinks —
54
- # sibling adopter-context decision)
55
- # @jtbd JTBD-302 (Trust That the README Describes the Plugin I Just
56
- # Installed — executable correctness axis of adopter-facing content)
57
- # @jtbd JTBD-101 (Extend the Suite with New Plugins — secondary
58
- # plugin-developer feedback surface)
59
-
60
- setup() {
61
- SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
62
- SCRIPT="$SCRIPTS_DIR/check-tarball-shipped-shims.sh"
63
- FIXTURE_ROOT="$(mktemp -d)"
64
- }
65
-
66
- teardown() {
67
- rm -rf "$FIXTURE_ROOT"
68
- }
69
-
70
- # Helper: write a workspace package.json with a controlled `files` array.
71
- # Args: <plugin> <files-json-array>
72
- # Example: write_package_json alpha '["bin/", "scripts/"]'
73
- write_package_json() {
74
- local plugin="$1"
75
- local files="$2"
76
- local plugin_dir="$FIXTURE_ROOT/packages/$plugin"
77
- mkdir -p "$plugin_dir"
78
- cat > "$plugin_dir/package.json" <<EOF
79
- {
80
- "name": "@test/$plugin",
81
- "version": "0.1.0",
82
- "files": $files
83
- }
84
- EOF
85
- }
86
-
87
- # Helper: write an WR-ADR-049-grammar bin shim that exec-s a scripts/ target.
88
- # Args: <plugin> <name> (shim becomes bin/wr-<plugin>-<name>, exec-s ../scripts/<name>.sh)
89
- write_adr049_shim() {
90
- local plugin="$1"
91
- local name="$2"
92
- local bin_dir="$FIXTURE_ROOT/packages/$plugin/bin"
93
- mkdir -p "$bin_dir"
94
- cat > "$bin_dir/wr-$plugin-$name" <<EOF
95
- #!/usr/bin/env bash
96
- exec "\$(dirname "\$0")/../scripts/$name.sh" "\$@"
97
- EOF
98
- chmod +x "$bin_dir/wr-$plugin-$name"
99
- }
100
-
101
- # Helper: write a script body matching the shim's exec target.
102
- write_script() {
103
- local plugin="$1"
104
- local name="$2"
105
- local script_dir="$FIXTURE_ROOT/packages/$plugin/scripts"
106
- mkdir -p "$script_dir"
107
- cat > "$script_dir/$name.sh" <<'EOF'
108
- #!/usr/bin/env bash
109
- echo "fixture script body"
110
- EOF
111
- chmod +x "$script_dir/$name.sh"
112
- }
113
-
114
- # Helper: write a non-WR-ADR-049-grammar bin entry (e.g. legacy installer).
115
- # These should be silently ignored by the script.
116
- write_non_grammar_bin() {
117
- local plugin="$1"
118
- local name="$2"
119
- local bin_dir="$FIXTURE_ROOT/packages/$plugin/bin"
120
- mkdir -p "$bin_dir"
121
- cat > "$bin_dir/$name" <<'EOF'
122
- #!/usr/bin/env bash
123
- echo "legacy installer"
124
- EOF
125
- chmod +x "$bin_dir/$name"
126
- }
127
-
128
- # ── Existence + executable ──────────────────────────────────────────────────
129
-
130
- @test "check-tarball-shipped-shims: script exists" {
131
- [ -f "$SCRIPT" ]
132
- }
133
-
134
- @test "check-tarball-shipped-shims: script is executable" {
135
- [ -x "$SCRIPT" ]
136
- }
137
-
138
- # ── Empty / clean trees ─────────────────────────────────────────────────────
139
-
140
- @test "check-tarball-shipped-shims: empty tree (no packages dir) produces no output and exits 0" {
141
- run "$SCRIPT" "$FIXTURE_ROOT"
142
- [ "$status" -eq 0 ]
143
- [ -z "$output" ]
144
- }
145
-
146
- @test "check-tarball-shipped-shims: packages dir with no workspaces produces no output and exits 0" {
147
- mkdir -p "$FIXTURE_ROOT/packages"
148
- run "$SCRIPT" "$FIXTURE_ROOT"
149
- [ "$status" -eq 0 ]
150
- [ -z "$output" ]
151
- }
152
-
153
- @test "check-tarball-shipped-shims: workspace with no WR-ADR-049 shims produces no output" {
154
- write_package_json "alpha" '["bin/"]'
155
- write_non_grammar_bin "alpha" "windyroad-alpha"
156
- run "$SCRIPT" "$FIXTURE_ROOT"
157
- [ "$status" -eq 0 ]
158
- [ -z "$output" ]
159
- }
160
-
161
- @test "check-tarball-shipped-shims: clean workspace (shim + target both ship) produces no output" {
162
- write_package_json "alpha" '["bin/", "scripts/"]'
163
- write_adr049_shim "alpha" "good"
164
- write_script "alpha" "good"
165
- run "$SCRIPT" "$FIXTURE_ROOT"
166
- [ "$status" -eq 0 ]
167
- [ -z "$output" ]
168
- }
169
-
170
- # ── Drift detection — the iter-20 P033 sibling-finding shape ────────────────
171
-
172
- @test "check-tarball-shipped-shims: shim present in tarball, target NOT in tarball — flagged" {
173
- # The canonical broken shape: scripts/ exists on disk, shim references it,
174
- # but package.json#files omits scripts/ so the target isn't shipped.
175
- write_package_json "alpha" '["bin/"]'
176
- write_adr049_shim "alpha" "broken"
177
- write_script "alpha" "broken"
178
- run "$SCRIPT" "$FIXTURE_ROOT"
179
- [ "$status" -eq 0 ]
180
- echo "$output" | grep -E "^TARBALL_DRIFT package=@test/alpha shim=bin/wr-alpha-broken target=scripts/broken.sh tarball-status=missing$"
181
- }
182
-
183
- @test "check-tarball-shipped-shims: TOTAL summary emitted on any drift" {
184
- write_package_json "alpha" '["bin/"]'
185
- write_adr049_shim "alpha" "broken"
186
- write_script "alpha" "broken"
187
- run "$SCRIPT" "$FIXTURE_ROOT"
188
- [ "$status" -eq 0 ]
189
- echo "$output" | grep -E "^TOTAL packages=1 with_drift=1 missing_targets=1$"
190
- }
191
-
192
- @test "check-tarball-shipped-shims: multiple shims in one workspace — each missing target flagged" {
193
- write_package_json "alpha" '["bin/"]'
194
- write_adr049_shim "alpha" "alpha-one"
195
- write_script "alpha" "alpha-one"
196
- write_adr049_shim "alpha" "alpha-two"
197
- write_script "alpha" "alpha-two"
198
- run "$SCRIPT" "$FIXTURE_ROOT"
199
- [ "$status" -eq 0 ]
200
- echo "$output" | grep -E "^TARBALL_DRIFT package=@test/alpha shim=bin/wr-alpha-alpha-one target=scripts/alpha-one.sh tarball-status=missing$"
201
- echo "$output" | grep -E "^TARBALL_DRIFT package=@test/alpha shim=bin/wr-alpha-alpha-two target=scripts/alpha-two.sh tarball-status=missing$"
202
- echo "$output" | grep -E "^TOTAL packages=1 with_drift=1 missing_targets=2$"
203
- }
204
-
205
- @test "check-tarball-shipped-shims: drift across multiple packages aggregates correctly" {
206
- write_package_json "alpha" '["bin/"]'
207
- write_adr049_shim "alpha" "a-broken"
208
- write_script "alpha" "a-broken"
209
- write_package_json "beta" '["bin/"]'
210
- write_adr049_shim "beta" "b-broken"
211
- write_script "beta" "b-broken"
212
- run "$SCRIPT" "$FIXTURE_ROOT"
213
- [ "$status" -eq 0 ]
214
- echo "$output" | grep -E "^TOTAL packages=2 with_drift=2 missing_targets=2$"
215
- }
216
-
217
- # ── Mixed clean + drift workspaces ──────────────────────────────────────────
218
-
219
- @test "check-tarball-shipped-shims: clean workspace + broken workspace — only broken flagged, TOTAL counts only with_drift" {
220
- write_package_json "alpha" '["bin/", "scripts/"]'
221
- write_adr049_shim "alpha" "good"
222
- write_script "alpha" "good"
223
- write_package_json "beta" '["bin/"]'
224
- write_adr049_shim "beta" "broken"
225
- write_script "beta" "broken"
226
- run "$SCRIPT" "$FIXTURE_ROOT"
227
- [ "$status" -eq 0 ]
228
- ! echo "$output" | grep -q "alpha"
229
- echo "$output" | grep -E "^TARBALL_DRIFT package=@test/beta shim=bin/wr-beta-broken target=scripts/broken.sh tarball-status=missing$"
230
- echo "$output" | grep -E "^TOTAL packages=1 with_drift=1 missing_targets=1$"
231
- }
232
-
233
- # ── Determinism ─────────────────────────────────────────────────────────────
234
-
235
- @test "check-tarball-shipped-shims: TARBALL_DRIFT lines sorted deterministically by package/shim" {
236
- write_package_json "zeta" '["bin/"]'
237
- write_adr049_shim "zeta" "z-shim"
238
- write_script "zeta" "z-shim"
239
- write_package_json "alpha" '["bin/"]'
240
- write_adr049_shim "alpha" "a-shim"
241
- write_script "alpha" "a-shim"
242
- write_package_json "mu" '["bin/"]'
243
- write_adr049_shim "mu" "m-shim"
244
- write_script "mu" "m-shim"
245
- run "$SCRIPT" "$FIXTURE_ROOT"
246
- [ "$status" -eq 0 ]
247
- local first
248
- first=$(echo "$output" | grep '^TARBALL_DRIFT' | head -1)
249
- echo "$first" | grep -q "package=@test/alpha"
250
- local last
251
- last=$(echo "$output" | grep '^TARBALL_DRIFT' | tail -1)
252
- echo "$last" | grep -q "package=@test/zeta"
253
- }
254
-
255
- # ── Mixed grammar — non-WR-ADR-049 bins ignored ─────────────────────────────
256
-
257
- @test "check-tarball-shipped-shims: legacy bin (non-WR-ADR-049 grammar) alongside grammar shim — only grammar shim checked" {
258
- write_package_json "alpha" '["bin/"]'
259
- write_non_grammar_bin "alpha" "windyroad-alpha"
260
- write_adr049_shim "alpha" "broken"
261
- write_script "alpha" "broken"
262
- run "$SCRIPT" "$FIXTURE_ROOT"
263
- [ "$status" -eq 0 ]
264
- ! echo "$output" | grep -q "windyroad-alpha"
265
- echo "$output" | grep -E "^TARBALL_DRIFT package=@test/alpha shim=bin/wr-alpha-broken target=scripts/broken.sh tarball-status=missing$"
266
- }
267
-
268
- # ── Pre-check error path ────────────────────────────────────────────────────
269
-
270
- @test "check-tarball-shipped-shims: missing root dir exits 2" {
271
- run "$SCRIPT" "/nonexistent/path/$$"
272
- [ "$status" -eq 2 ]
273
- }
274
-
275
- # ── Silent-on-pass invariant ────────────────────────────────────────────────
276
-
277
- @test "check-tarball-shipped-shims: no TOTAL line emitted when output is empty" {
278
- write_package_json "alpha" '["bin/", "scripts/"]'
279
- write_adr049_shim "alpha" "good"
280
- write_script "alpha" "good"
281
- run "$SCRIPT" "$FIXTURE_ROOT"
282
- [ "$status" -eq 0 ]
283
- [ -z "$output" ]
284
- }