@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.
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +5 -2
- package/hooks/test/retrospective-deferral-census.bats +0 -112
- package/hooks/test/retrospective-readme-jtbd-currency.bats +0 -312
- package/scripts/test/check-ask-hygiene.bats +0 -262
- package/scripts/test/check-autocreate-rfc-scope.bats +0 -250
- package/scripts/test/check-briefing-budgets.bats +0 -349
- package/scripts/test/check-internal-id-leaks.bats +0 -286
- package/scripts/test/check-plugin-maturity-drift.bats +0 -269
- package/scripts/test/check-readme-jtbd-currency.bats +0 -165
- package/scripts/test/check-skill-md-budgets.bats +0 -336
- package/scripts/test/check-tarball-shipped-shims.bats +0 -284
- package/scripts/test/check-tickets-deferred-cause.bats +0 -330
- package/scripts/test/list-plugin-attribution.bats +0 -149
- package/scripts/test/measure-context-budget.bats +0 -298
- package/skills/analyze-context/test/analyze-context-skill-contract.bats +0 -142
- package/skills/migrate-briefing/test/migrate-briefing-contract.bats +0 -105
- package/skills/migrate-briefing/test/migrate-briefing-fixture.bats +0 -242
- package/skills/run-retro/test/run-retro-anti-pattern-clause.bats +0 -87
- package/skills/run-retro/test/run-retro-codification-candidates.bats +0 -168
- package/skills/run-retro/test/run-retro-context-usage-step-2c.bats +0 -156
- package/skills/run-retro/test/run-retro-pipeline-instability-scan.bats +0 -122
- package/skills/run-retro/test/run-retro-signal-vs-noise.bats +0 -137
- package/skills/run-retro/test/run-retro-skill-candidates.bats +0 -116
- package/skills/run-retro/test/run-retro-stage-1-fallback-gating.bats +0 -65
- package/skills/run-retro/test/run-retro-step-2d-r6-auto-flag.bats +0 -82
- package/skills/run-retro/test/run-retro-step-3-tier-3-branch-b-evidence-based.bats +0 -189
- package/skills/run-retro/test/run-retro-step-4a-cross-plugin-dispatch.bats +0 -114
- package/skills/run-retro/test/run-retro-step-4a-prior-session-evidence-drain.bats +0 -168
- package/skills/run-retro/test/run-retro-step-4a-recovery-path.bats +0 -84
- package/skills/run-retro/test/run-retro-step-4b-retro-auto-ticket-carveout.bats +0 -115
- package/skills/run-retro/test/run-retro-ticket-first-flow.bats +0 -161
- package/skills/run-retro/test/run-retro-verification-close-housekeeping.bats +0 -132
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
#
|
|
3
|
-
# packages/retrospective/scripts/test/check-tickets-deferred-cause.bats
|
|
4
|
-
#
|
|
5
|
-
# Behavioural tests for `check-tickets-deferred-cause.sh` — the
|
|
6
|
-
# Tickets Deferred cause-allowlist advisory script (P148). Mirrors
|
|
7
|
-
# the fixture-based test pattern of `check-ask-hygiene.bats`.
|
|
8
|
-
#
|
|
9
|
-
# Tests are behavioural per ADR-005 / ADR-037 / P081 — they exercise
|
|
10
|
-
# the script end-to-end against fixture retro summary directories
|
|
11
|
-
# and assert on stdout / stderr / exit shape. No structural greps of
|
|
12
|
-
# the script source itself.
|
|
13
|
-
#
|
|
14
|
-
# @problem P148 (Agent defers ticket creation — broadens Stage 1 fallback gate)
|
|
15
|
-
# @problem P081 (Structural-content tests are wasteful — behavioural preferred)
|
|
16
|
-
# @adr ADR-044 (Decision-Delegation Contract)
|
|
17
|
-
# @adr ADR-040 (Tier 3 advisory-not-fail-closed)
|
|
18
|
-
# @adr ADR-013 Rule 6 (non-interactive fail-safe)
|
|
19
|
-
# @adr ADR-005 / ADR-037 (Plugin testing strategy — behavioural tests)
|
|
20
|
-
# @jtbd JTBD-001 / JTBD-006 / JTBD-201
|
|
21
|
-
|
|
22
|
-
SCRIPT="${BATS_TEST_DIRNAME}/../check-tickets-deferred-cause.sh"
|
|
23
|
-
|
|
24
|
-
setup() {
|
|
25
|
-
TEST_DIR="$(mktemp -d)"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
teardown() {
|
|
29
|
-
rm -rf "$TEST_DIR"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
# ── Pre-checks ──────────────────────────────────────────────────────────────
|
|
33
|
-
|
|
34
|
-
@test "script file exists and is executable" {
|
|
35
|
-
[ -f "$SCRIPT" ]
|
|
36
|
-
[ -x "$SCRIPT" ]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
@test "missing retros dir exits 2 with error message on stderr" {
|
|
40
|
-
run bash "$SCRIPT" "$TEST_DIR/does-not-exist"
|
|
41
|
-
[ "$status" -eq 2 ]
|
|
42
|
-
[[ "$output" == *"retros dir not found"* ]]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@test "empty retros dir exits 0 with empty stdout" {
|
|
46
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
47
|
-
[ "$status" -eq 0 ]
|
|
48
|
-
[ -z "$output" ]
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
@test "default retros-dir argument is docs/retros (when omitted)" {
|
|
52
|
-
cd "$TEST_DIR"
|
|
53
|
-
run bash "$SCRIPT"
|
|
54
|
-
[ "$status" -eq 2 ]
|
|
55
|
-
[[ "$output" == *"docs/retros"* ]]
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
# ── No-defer steady state ───────────────────────────────────────────────────
|
|
59
|
-
|
|
60
|
-
@test "retro file with no Tickets Deferred section emits no output" {
|
|
61
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
62
|
-
## Session Retrospective
|
|
63
|
-
|
|
64
|
-
### Briefing Changes
|
|
65
|
-
- Added: foo
|
|
66
|
-
|
|
67
|
-
### Problems Created/Updated
|
|
68
|
-
- P148: opened
|
|
69
|
-
|
|
70
|
-
### No Action Needed
|
|
71
|
-
- learning captured
|
|
72
|
-
RETRO
|
|
73
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
74
|
-
[ "$status" -eq 0 ]
|
|
75
|
-
[ -z "$output" ]
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
# ── Good fixture (skill_unavailable cause) ──────────────────────────────────
|
|
79
|
-
|
|
80
|
-
@test "good fixture: skill_unavailable cause → 0 violations" {
|
|
81
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
82
|
-
## Session Retrospective
|
|
83
|
-
|
|
84
|
-
### Tickets Deferred
|
|
85
|
-
|
|
86
|
-
| Observation | Cause | Citation |
|
|
87
|
-
|-------------|-------|----------|
|
|
88
|
-
| Polling regex deadlock observation | `skill_unavailable` | Step 2b detection |
|
|
89
|
-
| SIGTERM flush caveat | `skill_unavailable` | Step 4a verification |
|
|
90
|
-
RETRO
|
|
91
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
92
|
-
[ "$status" -eq 0 ]
|
|
93
|
-
[[ "$output" == *"deferred=2"* ]]
|
|
94
|
-
[[ "$output" == *"with_valid_cause=2"* ]]
|
|
95
|
-
[[ "$output" == *"violations=0"* ]]
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
# ── Bad fixture (session_pressure cause — the P148 anti-pattern) ────────────
|
|
99
|
-
|
|
100
|
-
@test "bad fixture: session_pressure cause → all entries are violations" {
|
|
101
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
102
|
-
## Session Retrospective
|
|
103
|
-
|
|
104
|
-
### Tickets Deferred
|
|
105
|
-
|
|
106
|
-
| Observation | Cause | Citation |
|
|
107
|
-
|-------------|-------|----------|
|
|
108
|
-
| Polling regex deadlock | `session_pressure` | Step 2b |
|
|
109
|
-
| SIGTERM flush caveat | `context_heavyweight` | Step 4a |
|
|
110
|
-
RETRO
|
|
111
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
112
|
-
[ "$status" -eq 0 ]
|
|
113
|
-
[[ "$output" == *"deferred=2"* ]]
|
|
114
|
-
[[ "$output" == *"with_valid_cause=0"* ]]
|
|
115
|
-
[[ "$output" == *"violations=2"* ]]
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
# ── Legacy fixture (no Cause column — pre-P148 retro shape) ─────────────────
|
|
119
|
-
|
|
120
|
-
@test "legacy fixture: no Cause column → all entries are violations" {
|
|
121
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
122
|
-
## Session Retrospective
|
|
123
|
-
|
|
124
|
-
### Tickets Deferred
|
|
125
|
-
|
|
126
|
-
| Observation | Citation |
|
|
127
|
-
|-------------|----------|
|
|
128
|
-
| Polling regex deadlock | Step 2b |
|
|
129
|
-
| SIGTERM flush caveat | Step 4a |
|
|
130
|
-
RETRO
|
|
131
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
132
|
-
[ "$status" -eq 0 ]
|
|
133
|
-
[[ "$output" == *"deferred=2"* ]]
|
|
134
|
-
[[ "$output" == *"violations=2"* ]]
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@test "legacy fixture exit code is 0 — advisory contract holds for AFK safety" {
|
|
138
|
-
# JTBD-006 line 32 (extended AFK safety): legacy retros lacking the Cause
|
|
139
|
-
# column would break AFK loops if exit code went non-zero. Fail-closed
|
|
140
|
-
# escalation belongs at a future hook tier per P135 R6 trajectory, not
|
|
141
|
-
# at the script.
|
|
142
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
143
|
-
### Tickets Deferred
|
|
144
|
-
|
|
145
|
-
| Observation | Citation |
|
|
146
|
-
|-------------|----------|
|
|
147
|
-
| obs1 | step1 |
|
|
148
|
-
RETRO
|
|
149
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
150
|
-
[ "$status" -eq 0 ]
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
# ── Mixed fixture ───────────────────────────────────────────────────────────
|
|
154
|
-
|
|
155
|
-
@test "mixed fixture: one valid + one invalid → violations=1, with_valid_cause=1" {
|
|
156
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
157
|
-
### Tickets Deferred
|
|
158
|
-
|
|
159
|
-
| Observation | Cause | Citation |
|
|
160
|
-
|-------------|-------|----------|
|
|
161
|
-
| Valid observation | `skill_unavailable` | Step 2b |
|
|
162
|
-
| Invalid observation | `session_pressure` | Step 4a |
|
|
163
|
-
RETRO
|
|
164
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
165
|
-
[ "$status" -eq 0 ]
|
|
166
|
-
[[ "$output" == *"deferred=2"* ]]
|
|
167
|
-
[[ "$output" == *"with_valid_cause=1"* ]]
|
|
168
|
-
[[ "$output" == *"violations=1"* ]]
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
# ── Empty Cause cell ────────────────────────────────────────────────────────
|
|
172
|
-
|
|
173
|
-
@test "empty Cause cell counts as a violation (cause-required invariant)" {
|
|
174
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
175
|
-
### Tickets Deferred
|
|
176
|
-
|
|
177
|
-
| Observation | Cause | Citation |
|
|
178
|
-
|-------------|-------|----------|
|
|
179
|
-
| Observation with empty cause | | Step 2b |
|
|
180
|
-
RETRO
|
|
181
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
182
|
-
[ "$status" -eq 0 ]
|
|
183
|
-
[[ "$output" == *"violations=1"* ]]
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
# ── Format tolerance ────────────────────────────────────────────────────────
|
|
187
|
-
|
|
188
|
-
@test "Cause cell tolerates surrounding whitespace and bold markers" {
|
|
189
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
190
|
-
### Tickets Deferred
|
|
191
|
-
|
|
192
|
-
| Observation | Cause | Citation |
|
|
193
|
-
|-------------|-------|----------|
|
|
194
|
-
| obs1 | `skill_unavailable` | Step 2b |
|
|
195
|
-
| obs2 | **skill_unavailable** | Step 4a |
|
|
196
|
-
RETRO
|
|
197
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
198
|
-
[ "$status" -eq 0 ]
|
|
199
|
-
[[ "$output" == *"deferred=2"* ]]
|
|
200
|
-
[[ "$output" == *"with_valid_cause=2"* ]]
|
|
201
|
-
[[ "$output" == *"violations=0"* ]]
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
@test "placeholder template row is skipped — not counted as a deferred entry" {
|
|
205
|
-
# The retro summary template includes a placeholder example row in the
|
|
206
|
-
# SKILL.md template (with `<one-line observation summary>` literal text).
|
|
207
|
-
# When a retro is rendered with no real deferred entries, the template
|
|
208
|
-
# row may persist; the script must NOT count it as a real deferred
|
|
209
|
-
# observation.
|
|
210
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
211
|
-
### Tickets Deferred
|
|
212
|
-
|
|
213
|
-
| Observation | Cause | Citation |
|
|
214
|
-
|-------------|-------|----------|
|
|
215
|
-
| <one-line observation summary> | `skill_unavailable` | <retro-step-citation> |
|
|
216
|
-
RETRO
|
|
217
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
218
|
-
[ "$status" -eq 0 ]
|
|
219
|
-
[ -z "$output" ]
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
# ── Multi-file behaviour ────────────────────────────────────────────────────
|
|
223
|
-
|
|
224
|
-
@test "multiple retro files emit per-file lines plus a TOTAL summary line" {
|
|
225
|
-
cat > "$TEST_DIR/2026-04-25-retro.md" <<'RETRO'
|
|
226
|
-
### Tickets Deferred
|
|
227
|
-
|
|
228
|
-
| Observation | Cause | Citation |
|
|
229
|
-
|-------------|-------|----------|
|
|
230
|
-
| good obs | `skill_unavailable` | Step 2b |
|
|
231
|
-
RETRO
|
|
232
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
233
|
-
### Tickets Deferred
|
|
234
|
-
|
|
235
|
-
| Observation | Cause | Citation |
|
|
236
|
-
|-------------|-------|----------|
|
|
237
|
-
| bad obs | `session_pressure` | Step 4a |
|
|
238
|
-
RETRO
|
|
239
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
240
|
-
[ "$status" -eq 0 ]
|
|
241
|
-
[[ "$output" == *"RETRO 2026-04-25"* ]]
|
|
242
|
-
[[ "$output" == *"RETRO 2026-04-29"* ]]
|
|
243
|
-
[[ "$output" == *"TOTAL files=2 deferred=2 with_valid_cause=1 violations=1"* ]]
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
@test "files sorted oldest-first by date prefix" {
|
|
247
|
-
for d in 27 25 26; do
|
|
248
|
-
cat > "$TEST_DIR/2026-04-$d-retro.md" <<RETRO
|
|
249
|
-
### Tickets Deferred
|
|
250
|
-
|
|
251
|
-
| Observation | Cause | Citation |
|
|
252
|
-
|-------------|-------|----------|
|
|
253
|
-
| obs$d | \`skill_unavailable\` | Step$d |
|
|
254
|
-
RETRO
|
|
255
|
-
done
|
|
256
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
257
|
-
[ "$status" -eq 0 ]
|
|
258
|
-
line1="${lines[0]}"
|
|
259
|
-
line2="${lines[1]}"
|
|
260
|
-
line3="${lines[2]}"
|
|
261
|
-
[[ "$line1" == *"2026-04-25"* ]]
|
|
262
|
-
[[ "$line2" == *"2026-04-26"* ]]
|
|
263
|
-
[[ "$line3" == *"2026-04-27"* ]]
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
# ── File-type filtering ─────────────────────────────────────────────────────
|
|
267
|
-
|
|
268
|
-
@test "ask-hygiene trail files are skipped (not retro summaries)" {
|
|
269
|
-
cat > "$TEST_DIR/2026-04-29-ask-hygiene.md" <<'TRAIL'
|
|
270
|
-
### Tickets Deferred
|
|
271
|
-
|
|
272
|
-
| Observation | Cause | Citation |
|
|
273
|
-
|-------------|-------|----------|
|
|
274
|
-
| should be ignored | `session_pressure` | irrelevant |
|
|
275
|
-
TRAIL
|
|
276
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
277
|
-
[ "$status" -eq 0 ]
|
|
278
|
-
[ -z "$output" ]
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
@test "context-analysis files are skipped (not retro summaries)" {
|
|
282
|
-
cat > "$TEST_DIR/2026-04-29-context-analysis.md" <<'CTX'
|
|
283
|
-
### Tickets Deferred
|
|
284
|
-
|
|
285
|
-
| Observation | Cause | Citation |
|
|
286
|
-
|-------------|-------|----------|
|
|
287
|
-
| should be ignored | `session_pressure` | irrelevant |
|
|
288
|
-
CTX
|
|
289
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
290
|
-
[ "$status" -eq 0 ]
|
|
291
|
-
[ -z "$output" ]
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
@test "files without a YYYY-MM-DD date prefix are skipped" {
|
|
295
|
-
cat > "$TEST_DIR/README.md" <<'RM'
|
|
296
|
-
### Tickets Deferred
|
|
297
|
-
|
|
298
|
-
| Observation | Cause | Citation |
|
|
299
|
-
|-------------|-------|----------|
|
|
300
|
-
| should be ignored | `session_pressure` | irrelevant |
|
|
301
|
-
RM
|
|
302
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
303
|
-
[ "$status" -eq 0 ]
|
|
304
|
-
[ -z "$output" ]
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
# ── Cross-shell portability (P124 / P133 lessons) ───────────────────────────
|
|
308
|
-
|
|
309
|
-
@test "script glob iteration uses portable for-loop existence check" {
|
|
310
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
311
|
-
[ "$status" -eq 0 ]
|
|
312
|
-
[[ "$output" != *"*.md"* ]]
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
# ── Read-only contract ──────────────────────────────────────────────────────
|
|
316
|
-
|
|
317
|
-
@test "script is read-only — fixture tree unchanged after run" {
|
|
318
|
-
cat > "$TEST_DIR/2026-04-29-retro.md" <<'RETRO'
|
|
319
|
-
### Tickets Deferred
|
|
320
|
-
|
|
321
|
-
| Observation | Cause | Citation |
|
|
322
|
-
|-------------|-------|----------|
|
|
323
|
-
| obs1 | `skill_unavailable` | Step 2b |
|
|
324
|
-
RETRO
|
|
325
|
-
pre_hash=$(find "$TEST_DIR" -type f -exec cksum {} \; 2>/dev/null | sort | cksum | awk '{print $1}')
|
|
326
|
-
run bash "$SCRIPT" "$TEST_DIR"
|
|
327
|
-
[ "$status" -eq 0 ]
|
|
328
|
-
post_hash=$(find "$TEST_DIR" -type f -exec cksum {} \; 2>/dev/null | sort | cksum | awk '{print $1}')
|
|
329
|
-
[ "$pre_hash" = "$post_hash" ]
|
|
330
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# @problem P153 — Published skills enumerate repo-relative directories.
|
|
4
|
-
# `/wr-retrospective:analyze-context` SKILL.md L56-67 used a `for plugin_dir
|
|
5
|
-
# in packages/*/hooks; do ... done` glob loop that expanded to nothing in
|
|
6
|
-
# adopter trees, silently emitting zero PLUGIN-HOOKS / PLUGIN-SKILLS rows.
|
|
7
|
-
#
|
|
8
|
-
# Fix: extract the loop into a helper script that probes for the source-
|
|
9
|
-
# tree first (preserves dev-session output) and falls back to a `$PATH`-
|
|
10
|
-
# derived plugin-cache walk so adopter sessions resolve too. Same row
|
|
11
|
-
# shape; backward compatible with deep-layer SKILL.md Step 4 sum-check.
|
|
12
|
-
#
|
|
13
|
-
# Contract:
|
|
14
|
-
# list-plugin-attribution.sh [<project-root>]
|
|
15
|
-
#
|
|
16
|
-
# Default project-root is the current working directory.
|
|
17
|
-
#
|
|
18
|
-
# Output (one line per row, terse machine-readable per ADR-038 ≤150 bytes):
|
|
19
|
-
# PLUGIN-HOOKS <plugin> bytes=<N>
|
|
20
|
-
# PLUGIN-SKILLS <plugin> bytes=<N>
|
|
21
|
-
# PLUGIN-ATTRIBUTION not-measured reason=<reason>
|
|
22
|
-
#
|
|
23
|
-
# Exit code: 0 always (advisory, matches measure-context-budget.sh contract).
|
|
24
|
-
#
|
|
25
|
-
# Resolution order:
|
|
26
|
-
# 1. Source-tree mode — if `<project-root>/packages/*/hooks` glob expands
|
|
27
|
-
# to anything, walk it. Same applies to `packages/*/skills`.
|
|
28
|
-
# 2. Cache-fallback mode — sniff `$PATH` entries that match
|
|
29
|
-
# `*/cache/<owner>/<plugin>/<version>/bin`; walk back to each
|
|
30
|
-
# plugin's root and emit the same row shape.
|
|
31
|
-
# 3. Neither — emit `PLUGIN-ATTRIBUTION not-measured
|
|
32
|
-
# reason=no-plugin-source-resolvable`.
|
|
33
|
-
#
|
|
34
|
-
# @adr ADR-049 (Plugin-bundled scripts via `bin/` on `$PATH` —
|
|
35
|
-
# reassessment-criteria clause 3 explicitly anticipates this surface)
|
|
36
|
-
# @adr ADR-038 (Progressive disclosure — per-row byte budget)
|
|
37
|
-
# @adr ADR-026 (Agent output grounding — explicit not-measured sentinels)
|
|
38
|
-
# @adr ADR-005 (Plugin testing strategy)
|
|
39
|
-
# @adr ADR-037 (Skill testing strategy — bats-contract precedent)
|
|
40
|
-
# @jtbd JTBD-301 (Plugin-user) / JTBD-101 (Plugin-developer)
|
|
41
|
-
|
|
42
|
-
setup() {
|
|
43
|
-
SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
44
|
-
SCRIPT="$SCRIPTS_DIR/list-plugin-attribution.sh"
|
|
45
|
-
FIXTURE_DIR="$(mktemp -d)"
|
|
46
|
-
# Minimal PATH for bash + coreutils, but no plugin-cache bin dirs.
|
|
47
|
-
# Tests that exercise cache-fallback explicitly prepend a synthetic
|
|
48
|
-
# cache bin to this MIN_PATH.
|
|
49
|
-
MIN_PATH="/usr/bin:/bin"
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
teardown() {
|
|
53
|
-
rm -rf "$FIXTURE_DIR"
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
# ── Existence + executable ──────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
@test "list-plugin-attribution: script file exists at expected path" {
|
|
59
|
-
[ -f "$SCRIPT" ]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@test "list-plugin-attribution: script is executable" {
|
|
63
|
-
[ -x "$SCRIPT" ]
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
# ── Exit code ───────────────────────────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
@test "list-plugin-attribution: empty fixture exits 0 (advisory)" {
|
|
69
|
-
# Run with an empty PATH so the cache-fallback yields nothing too.
|
|
70
|
-
run env PATH="$MIN_PATH" bash "$SCRIPT" "$FIXTURE_DIR"
|
|
71
|
-
[ "$status" -eq 0 ]
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# ── Source-tree mode ────────────────────────────────────────────────────────
|
|
75
|
-
|
|
76
|
-
@test "list-plugin-attribution: source-tree mode emits PLUGIN-HOOKS row for packages/<plugin>/hooks" {
|
|
77
|
-
mkdir -p "$FIXTURE_DIR/packages/foo/hooks"
|
|
78
|
-
printf '%s' 'echo hi' > "$FIXTURE_DIR/packages/foo/hooks/h.sh"
|
|
79
|
-
run env PATH="$MIN_PATH" bash "$SCRIPT" "$FIXTURE_DIR"
|
|
80
|
-
[ "$status" -eq 0 ]
|
|
81
|
-
echo "$output" | grep -qE '^PLUGIN-HOOKS foo bytes=[0-9]+$'
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
@test "list-plugin-attribution: source-tree mode emits PLUGIN-SKILLS row for packages/<plugin>/skills" {
|
|
85
|
-
mkdir -p "$FIXTURE_DIR/packages/foo/skills/wizard"
|
|
86
|
-
printf '%s' '# Wizard' > "$FIXTURE_DIR/packages/foo/skills/wizard/SKILL.md"
|
|
87
|
-
run env PATH="$MIN_PATH" bash "$SCRIPT" "$FIXTURE_DIR"
|
|
88
|
-
[ "$status" -eq 0 ]
|
|
89
|
-
echo "$output" | grep -qE '^PLUGIN-SKILLS foo bytes=[0-9]+$'
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
@test "list-plugin-attribution: source-tree mode reports nonzero hook bytes when files present" {
|
|
93
|
-
mkdir -p "$FIXTURE_DIR/packages/foo/hooks"
|
|
94
|
-
printf '%s' 'echo hi' > "$FIXTURE_DIR/packages/foo/hooks/h.sh" # 7 bytes
|
|
95
|
-
run env PATH="$MIN_PATH" bash "$SCRIPT" "$FIXTURE_DIR"
|
|
96
|
-
bytes=$(echo "$output" | awk '/^PLUGIN-HOOKS foo bytes=/{ split($3, a, "="); print a[2] }')
|
|
97
|
-
[ "${bytes:-0}" -gt 0 ]
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
@test "list-plugin-attribution: source-tree mode emits one row per plugin (multi-plugin)" {
|
|
101
|
-
mkdir -p "$FIXTURE_DIR/packages/foo/hooks" "$FIXTURE_DIR/packages/bar/hooks"
|
|
102
|
-
printf 'x' > "$FIXTURE_DIR/packages/foo/hooks/h.sh"
|
|
103
|
-
printf 'x' > "$FIXTURE_DIR/packages/bar/hooks/h.sh"
|
|
104
|
-
run env PATH="$MIN_PATH" bash "$SCRIPT" "$FIXTURE_DIR"
|
|
105
|
-
echo "$output" | grep -qE '^PLUGIN-HOOKS foo bytes='
|
|
106
|
-
echo "$output" | grep -qE '^PLUGIN-HOOKS bar bytes='
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
# ── Cache-fallback mode ─────────────────────────────────────────────────────
|
|
110
|
-
|
|
111
|
-
@test "list-plugin-attribution: cache-fallback mode resolves plugins via PATH-derived bin dirs" {
|
|
112
|
-
# Synthesise an adopter-shaped layout: ~/.claude/plugins/cache/<owner>/<plugin>/<version>/
|
|
113
|
-
cache_root="$FIXTURE_DIR/.claude/plugins/cache/wroad/myplug/0.0.1"
|
|
114
|
-
mkdir -p "$cache_root/bin" "$cache_root/hooks" "$cache_root/skills/s1"
|
|
115
|
-
printf '%s' 'exec true' > "$cache_root/bin/wr-myplug-cmd"
|
|
116
|
-
chmod +x "$cache_root/bin/wr-myplug-cmd"
|
|
117
|
-
printf 'hook' > "$cache_root/hooks/h.sh"
|
|
118
|
-
printf 'skill' > "$cache_root/skills/s1/SKILL.md"
|
|
119
|
-
|
|
120
|
-
# CWD has no packages/ dir → forces cache-fallback path.
|
|
121
|
-
empty_cwd="$FIXTURE_DIR/empty"
|
|
122
|
-
mkdir -p "$empty_cwd"
|
|
123
|
-
|
|
124
|
-
run env PATH="$cache_root/bin:$MIN_PATH" bash "$SCRIPT" "$empty_cwd"
|
|
125
|
-
[ "$status" -eq 0 ]
|
|
126
|
-
echo "$output" | grep -qE '^PLUGIN-HOOKS myplug bytes=[0-9]+$'
|
|
127
|
-
echo "$output" | grep -qE '^PLUGIN-SKILLS myplug bytes=[0-9]+$'
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
# ── Neither source nor cache resolves ───────────────────────────────────────
|
|
131
|
-
|
|
132
|
-
@test "list-plugin-attribution: emits not-measured sentinel when nothing resolves" {
|
|
133
|
-
# Empty fixture, empty PATH → zero source + zero cache.
|
|
134
|
-
run env PATH="$MIN_PATH" bash "$SCRIPT" "$FIXTURE_DIR"
|
|
135
|
-
[ "$status" -eq 0 ]
|
|
136
|
-
echo "$output" | grep -qE '^PLUGIN-ATTRIBUTION not-measured reason=no-plugin-source-resolvable$'
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
# ── Output budget ───────────────────────────────────────────────────────────
|
|
140
|
-
|
|
141
|
-
@test "list-plugin-attribution: every output row is under 150 bytes (ADR-038)" {
|
|
142
|
-
mkdir -p "$FIXTURE_DIR/packages/foo/hooks" "$FIXTURE_DIR/packages/foo/skills/s1"
|
|
143
|
-
printf 'x' > "$FIXTURE_DIR/packages/foo/hooks/h.sh"
|
|
144
|
-
printf 'x' > "$FIXTURE_DIR/packages/foo/skills/s1/SKILL.md"
|
|
145
|
-
run env PATH="$MIN_PATH" bash "$SCRIPT" "$FIXTURE_DIR"
|
|
146
|
-
while IFS= read -r line; do
|
|
147
|
-
[ "${#line}" -le 150 ]
|
|
148
|
-
done <<< "$output"
|
|
149
|
-
}
|