@thebassclef/lite 1.0.2 → 1.0.3
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/dist/cli.cjs +24 -1
- package/dist/cli.js +24 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/hooks/session-reflection.d/00-bassclef-hook-connect.sh +63 -0
- package/dist/lite/.claude/hooks/session-reflection.d/05-active-bet.sh +113 -0
- package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +62 -0
- package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +78 -0
- package/dist/lite/.claude/hooks/session-reflection.d/12-session-lock.sh +63 -0
- package/dist/lite/.claude/hooks/session-reflection.d/15-substrate-config-validate.sh +91 -0
- package/dist/lite/.claude/hooks/session-reflection.d/20-artifact-staleness.sh +98 -0
- package/dist/lite/.claude/hooks/session-reflection.d/30-metrics-staleness.sh +61 -0
- package/dist/lite/.claude/hooks/session-reflection.d/35-post-merge-whereami-self-heal.sh +180 -0
- package/dist/lite/.claude/hooks/session-reflection.d/36-whereami-size-check.sh +117 -0
- package/dist/lite/.claude/hooks/session-reflection.d/40-gate-compliance.sh +108 -0
- package/dist/lite/.claude/hooks/session-reflection.d/45-auto-sync-status.sh +99 -0
- package/dist/lite/.claude/hooks/session-reflection.d/50-evolution-issues.sh +44 -0
- package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +187 -0
- package/dist/lite/.claude/hooks/session-reflection.d/56-roadmap-reconcile-gate.sh +88 -0
- package/dist/lite/.claude/hooks/session-reflection.d/60-deferred-actions.sh +212 -0
- package/dist/lite/.claude/hooks/session-reflection.d/70-recent-strategic-artifacts.sh +101 -0
- package/dist/lite/.claude/hooks/session-reflection.d/75-prior-r8-findings.sh +100 -0
- package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +87 -0
- package/dist/lite/.claude/hooks/session-reflection.d/80-workflow-staleness.sh +141 -0
- package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +67 -0
- package/dist/lite/.claude/hooks/session-reflection.d/90-release-backlog.sh +140 -0
- package/dist/lite/.claude/hooks/session-reflection.d/90-secrets-expiry.sh +104 -0
- package/dist/lite/.claude/hooks/session-reflection.d/95-settings-hook-verify.sh +131 -0
- package/dist/lite/.claude/hooks/session-reflection.d/95-telemetry-emit.sh +66 -0
- package/dist/lite/.claude/hooks/session-reflection.d/97-install-class-dispatch.sh +57 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/00-bassclef-hook-connect.test.sh +492 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/05-active-bet.test.sh +336 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/12-session-lock.test.sh +216 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/15-substrate-config-validate.test.sh +50 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/20-artifact-staleness.test.sh +348 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/36-whereami-size-check.test.sh +189 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/45-auto-sync-status.test.sh +263 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/55-orientation-gate.test.sh +595 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/56-roadmap-reconcile-gate.test.sh +227 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/75-prior-r8-findings.test.sh +142 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/90-release-backlog.test.sh +44 -0
- package/dist/lite/.claude/hooks/trace-helper.sh +22 -0
- package/package.json +1 -1
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: standard
|
|
3
|
+
# fixtures: post-rename-adopter bassclef-upstream-self fully-cured-adopter
|
|
4
|
+
# Tier 0 test for .claude/hooks/session-reflection.d/00-bassclef-hook-connect.sh
|
|
5
|
+
#
|
|
6
|
+
# Closes sunj-labs/bassclef-upstream#644 — sync-cure peer path fix.
|
|
7
|
+
#
|
|
8
|
+
# The fragment sources presence/install/bassclef-hook-connect.sh and calls
|
|
9
|
+
# bassclef_hook_connect_apply at every session-start. Runs regardless of
|
|
10
|
+
# sync path (peer, clone, npm, curl). Renames sister fragment
|
|
11
|
+
# 00-settings-drift.sh -> 08-settings-drift.sh so this fragment runs first.
|
|
12
|
+
#
|
|
13
|
+
# Per #644 acceptance + four-lens pre-mortem:
|
|
14
|
+
# - Fixture parent path OUTSIDE ~/src/sunj-labs/ (Feathers characterization)
|
|
15
|
+
# - Merge populates 4 missing POA-shape drift entries
|
|
16
|
+
# - Merge is idempotent (Nygard steady state)
|
|
17
|
+
# - Fragment emits stderr log line on every merge (Ousterhout signal visibility)
|
|
18
|
+
# - Fragment path mirrors L682 clone-path call precisely (Cockburn walking skeleton)
|
|
19
|
+
#
|
|
20
|
+
# Run: bash .claude/hooks/session-reflection.d/tests/00-bassclef-hook-connect.test.sh
|
|
21
|
+
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
# test-list:
|
|
25
|
+
# [ ] Fragment file exists at expected path (RED baseline; goes GREEN after WU-2)
|
|
26
|
+
# [ ] Fragment sources bassclef-hook-connect.sh when BASSCLEF_DIR is set
|
|
27
|
+
# [ ] Fragment silently skips when BASSCLEF_DIR is unset
|
|
28
|
+
# [ ] Fragment skips when adopter has no .claude/ dir
|
|
29
|
+
# [ ] Fragment skips when bassclef settings.json is missing
|
|
30
|
+
# [ ] Merge populates 4 missing POA-shape drift entries
|
|
31
|
+
# [ ] Merge is idempotent — running twice does not duplicate entries
|
|
32
|
+
# [ ] Fragment emits stderr log line on every merge run (Ousterhout signal-visibility)
|
|
33
|
+
# [ ] SKIP_BASSCLEF_HOOK_WIRE=1 bypasses cleanly
|
|
34
|
+
# [ ] Fixture parent path lives OUTSIDE ~/src/sunj-labs/ (Feathers characterization; operator ask)
|
|
35
|
+
# [ ] jq missing — fragment skips cleanly
|
|
36
|
+
# [ ] Bassclef self (remote URL contains sunj-labs/bassclef) — merge skips
|
|
37
|
+
# [ ] Adopter settings.json missing — created with hooks {} then merged
|
|
38
|
+
# [ ] Adopter settings.json malformed — skip without touching
|
|
39
|
+
# [ ] Exit 0 on all paths (session-reflection.d fragments are informational)
|
|
40
|
+
# [ ] Install-class project (CLAUDE_PROJECT_DIR set) resolves BASSCLEF_DIR
|
|
41
|
+
# [ ] Install-class operator (HOME_OVERRIDE set) resolves BASSCLEF_DIR
|
|
42
|
+
# [~] Trap: fragment writes to adopter settings.json via atomic tmp file; deep module handles the trap. Reason: covered upstream.
|
|
43
|
+
|
|
44
|
+
REPO_ROOT="$(cd "$(dirname "$0")/../../../.." && pwd)"
|
|
45
|
+
HOOK="${REPO_ROOT}/.claude/hooks/session-reflection.d/00-bassclef-hook-connect.sh"
|
|
46
|
+
DEEP_MODULE="${REPO_ROOT}/presence/install/bassclef-hook-connect.sh"
|
|
47
|
+
PASS=0
|
|
48
|
+
FAIL=0
|
|
49
|
+
FAILED_TESTS=()
|
|
50
|
+
|
|
51
|
+
assert() {
|
|
52
|
+
local name="$1"
|
|
53
|
+
local result="$2"
|
|
54
|
+
if [ "$result" = "ok" ]; then
|
|
55
|
+
PASS=$((PASS + 1))
|
|
56
|
+
else
|
|
57
|
+
FAIL=$((FAIL + 1))
|
|
58
|
+
FAILED_TESTS+=("$name: $result")
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Build a fixture with parent path OUTSIDE ~/src/sunj-labs/.
|
|
63
|
+
# Returns the adopter dir path; caller must clean up parent.
|
|
64
|
+
build_poa_shape_fixture() {
|
|
65
|
+
local parent="$1"
|
|
66
|
+
local bassclef="$parent/bassclef"
|
|
67
|
+
local adopter="$parent/adopter"
|
|
68
|
+
|
|
69
|
+
mkdir -p "$bassclef/.claude"
|
|
70
|
+
mkdir -p "$bassclef/presence/install"
|
|
71
|
+
mkdir -p "$adopter/.claude"
|
|
72
|
+
|
|
73
|
+
# Copy the real shared function into the fake bassclef — mirrors real layout.
|
|
74
|
+
if [ -f "$DEEP_MODULE" ]; then
|
|
75
|
+
cp "$DEEP_MODULE" "$bassclef/presence/install/bassclef-hook-connect.sh"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Bassclef settings.json — mirrors POA-drift shape from #644.
|
|
79
|
+
# Contains 4 entries POA's settings.json is missing.
|
|
80
|
+
cat > "$bassclef/.claude/settings.json" <<'BASSCLEF_SETTINGS'
|
|
81
|
+
{
|
|
82
|
+
"hooks": {
|
|
83
|
+
"PostToolUse": [
|
|
84
|
+
{
|
|
85
|
+
"matcher": "Read|Grep|Bash",
|
|
86
|
+
"hooks": [
|
|
87
|
+
{"type": "command", "command": "bash .claude/hooks/base-pair-reprime.sh"}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"Stop": [
|
|
92
|
+
{
|
|
93
|
+
"hooks": [
|
|
94
|
+
{"type": "command", "command": "bash .claude/hooks/longrun-prep-compounding-sequence-check.sh"},
|
|
95
|
+
{"type": "command", "command": "bash .claude/hooks/stop-assertion-shape-scan.sh"}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"PreToolUse": [
|
|
100
|
+
{
|
|
101
|
+
"matcher": "Bash",
|
|
102
|
+
"hooks": [
|
|
103
|
+
{"type": "command", "command": "bash .claude/hooks/pre-gh-git-claim-shape-scrub.sh"}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
BASSCLEF_SETTINGS
|
|
110
|
+
|
|
111
|
+
# Adopter starts EMPTY (POA-shape drift — missing all 4 entries).
|
|
112
|
+
cat > "$adopter/.claude/settings.json" <<'ADOPTER_SETTINGS'
|
|
113
|
+
{
|
|
114
|
+
"hooks": {}
|
|
115
|
+
}
|
|
116
|
+
ADOPTER_SETTINGS
|
|
117
|
+
|
|
118
|
+
echo "$adopter"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# --- Test 1 — RED baseline: fragment file must exist (goes GREEN when WU-2 ships)
|
|
122
|
+
if [ -f "$HOOK" ]; then
|
|
123
|
+
assert "01-fragment-file-exists" "ok"
|
|
124
|
+
else
|
|
125
|
+
assert "01-fragment-file-exists" "fragment missing at $HOOK (expected RED baseline)"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# --- Test 2 — Fragment sources deep module when BASSCLEF_DIR is set
|
|
129
|
+
if [ -f "$HOOK" ]; then
|
|
130
|
+
TMP=$(mktemp -d)
|
|
131
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
132
|
+
out=$(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
133
|
+
ec=$?
|
|
134
|
+
rm -rf "$TMP"
|
|
135
|
+
if [ "$ec" = "0" ]; then
|
|
136
|
+
assert "02-fragment-runs-with-bassclef-dir" "ok"
|
|
137
|
+
else
|
|
138
|
+
assert "02-fragment-runs-with-bassclef-dir" "expected exit 0; got $ec ($out)"
|
|
139
|
+
fi
|
|
140
|
+
else
|
|
141
|
+
assert "02-fragment-runs-with-bassclef-dir" "fragment missing at $HOOK"
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# --- Test 3 — Fragment silently skips when BASSCLEF_DIR is unset
|
|
145
|
+
if [ -f "$HOOK" ]; then
|
|
146
|
+
TMP=$(mktemp -d)
|
|
147
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
148
|
+
out=$(cd "$ADOPTER" && CWD="$ADOPTER" bash "$HOOK" 2>&1)
|
|
149
|
+
ec=$?
|
|
150
|
+
rm -rf "$TMP"
|
|
151
|
+
if [ "$ec" = "0" ]; then
|
|
152
|
+
assert "03-skip-when-no-bassclef-dir" "ok"
|
|
153
|
+
else
|
|
154
|
+
assert "03-skip-when-no-bassclef-dir" "expected exit 0; got $ec ($out)"
|
|
155
|
+
fi
|
|
156
|
+
else
|
|
157
|
+
assert "03-skip-when-no-bassclef-dir" "fragment missing at $HOOK"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# --- Test 4 — Fragment skips when adopter has no .claude dir
|
|
161
|
+
if [ -f "$HOOK" ]; then
|
|
162
|
+
TMP=$(mktemp -d)
|
|
163
|
+
mkdir -p "$TMP/bassclef/.claude"
|
|
164
|
+
echo '{"hooks":{}}' > "$TMP/bassclef/.claude/settings.json"
|
|
165
|
+
mkdir -p "$TMP/adopter" # NO .claude subdir
|
|
166
|
+
out=$(cd "$TMP/adopter" && CWD="$TMP/adopter" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
167
|
+
ec=$?
|
|
168
|
+
if [ "$ec" = "0" ] && [ ! -d "$TMP/adopter/.claude" ]; then
|
|
169
|
+
assert "04-skip-when-no-adopter-claude-dir" "ok"
|
|
170
|
+
else
|
|
171
|
+
assert "04-skip-when-no-adopter-claude-dir" "expected exit 0 + no .claude created; got ec=$ec"
|
|
172
|
+
fi
|
|
173
|
+
rm -rf "$TMP"
|
|
174
|
+
else
|
|
175
|
+
assert "04-skip-when-no-adopter-claude-dir" "fragment missing at $HOOK"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# --- Test 5 — Fragment skips when bassclef settings.json missing
|
|
179
|
+
if [ -f "$HOOK" ]; then
|
|
180
|
+
TMP=$(mktemp -d)
|
|
181
|
+
mkdir -p "$TMP/bassclef/.claude"
|
|
182
|
+
mkdir -p "$TMP/adopter/.claude"
|
|
183
|
+
echo '{"hooks":{}}' > "$TMP/adopter/.claude/settings.json"
|
|
184
|
+
out=$(cd "$TMP/adopter" && CWD="$TMP/adopter" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
185
|
+
ec=$?
|
|
186
|
+
content=$(cat "$TMP/adopter/.claude/settings.json")
|
|
187
|
+
if [ "$ec" = "0" ] && [ "$content" = '{"hooks":{}}' ]; then
|
|
188
|
+
assert "05-skip-when-bassclef-settings-missing" "ok"
|
|
189
|
+
else
|
|
190
|
+
assert "05-skip-when-bassclef-settings-missing" "expected exit 0 + unchanged; got ec=$ec"
|
|
191
|
+
fi
|
|
192
|
+
rm -rf "$TMP"
|
|
193
|
+
else
|
|
194
|
+
assert "05-skip-when-bassclef-settings-missing" "fragment missing at $HOOK"
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
# --- Test 6 — Merge populates 4 missing POA-shape drift entries
|
|
198
|
+
if [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1; then
|
|
199
|
+
TMP=$(mktemp -d)
|
|
200
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
201
|
+
out=$(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
202
|
+
ec=$?
|
|
203
|
+
merged_content=$(cat "$ADOPTER/.claude/settings.json")
|
|
204
|
+
count=0
|
|
205
|
+
echo "$merged_content" | grep -q "base-pair-reprime.sh" && count=$((count + 1))
|
|
206
|
+
echo "$merged_content" | grep -q "longrun-prep-compounding-sequence-check.sh" && count=$((count + 1))
|
|
207
|
+
echo "$merged_content" | grep -q "stop-assertion-shape-scan.sh" && count=$((count + 1))
|
|
208
|
+
echo "$merged_content" | grep -q "pre-gh-git-claim-shape-scrub.sh" && count=$((count + 1))
|
|
209
|
+
rm -rf "$TMP"
|
|
210
|
+
if [ "$ec" = "0" ] && [ "$count" = "4" ]; then
|
|
211
|
+
assert "06-merge-populates-4-poa-shape-entries" "ok"
|
|
212
|
+
else
|
|
213
|
+
assert "06-merge-populates-4-poa-shape-entries" "expected 4 entries; got $count (ec=$ec)"
|
|
214
|
+
fi
|
|
215
|
+
else
|
|
216
|
+
assert "06-merge-populates-4-poa-shape-entries" "prereq missing: HOOK / DEEP_MODULE / jq"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# --- Test 7 — Merge is idempotent — running twice does not duplicate entries
|
|
220
|
+
if [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1; then
|
|
221
|
+
TMP=$(mktemp -d)
|
|
222
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
223
|
+
(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" >/dev/null 2>&1)
|
|
224
|
+
(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" >/dev/null 2>&1)
|
|
225
|
+
merged_content=$(cat "$ADOPTER/.claude/settings.json")
|
|
226
|
+
base_pair_count=$(echo "$merged_content" | grep -c "base-pair-reprime.sh")
|
|
227
|
+
compounding_count=$(echo "$merged_content" | grep -c "longrun-prep-compounding-sequence-check.sh")
|
|
228
|
+
rm -rf "$TMP"
|
|
229
|
+
if [ "$base_pair_count" = "1" ] && [ "$compounding_count" = "1" ]; then
|
|
230
|
+
assert "07-merge-idempotent" "ok"
|
|
231
|
+
else
|
|
232
|
+
assert "07-merge-idempotent" "expected 1 of each; got base_pair=$base_pair_count compounding=$compounding_count"
|
|
233
|
+
fi
|
|
234
|
+
else
|
|
235
|
+
assert "07-merge-idempotent" "prereq missing: HOOK / DEEP_MODULE / jq"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
# --- Test 8 — Fragment emits stderr log line on every merge run (Ousterhout)
|
|
239
|
+
if [ -f "$HOOK" ]; then
|
|
240
|
+
TMP=$(mktemp -d)
|
|
241
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
242
|
+
stderr_out=$(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1 >/dev/null)
|
|
243
|
+
rm -rf "$TMP"
|
|
244
|
+
if echo "$stderr_out" | grep -q "bassclef-hook-connect"; then
|
|
245
|
+
assert "08-stderr-log-emitted" "ok"
|
|
246
|
+
else
|
|
247
|
+
assert "08-stderr-log-emitted" "no [bassclef-hook-connect] log on stderr; got: $stderr_out"
|
|
248
|
+
fi
|
|
249
|
+
else
|
|
250
|
+
assert "08-stderr-log-emitted" "fragment missing at $HOOK"
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
# --- Test 9 — SKIP_BASSCLEF_HOOK_WIRE=1 bypasses cleanly
|
|
254
|
+
if [ -f "$HOOK" ]; then
|
|
255
|
+
TMP=$(mktemp -d)
|
|
256
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
257
|
+
out=$(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" SKIP_BASSCLEF_HOOK_WIRE=1 bash "$HOOK" 2>&1)
|
|
258
|
+
ec=$?
|
|
259
|
+
content=$(cat "$ADOPTER/.claude/settings.json")
|
|
260
|
+
rm -rf "$TMP"
|
|
261
|
+
if [ "$ec" = "0" ] && [ "$content" = '{
|
|
262
|
+
"hooks": {}
|
|
263
|
+
}' ]; then
|
|
264
|
+
assert "09-skip-flag-bypasses" "ok"
|
|
265
|
+
else
|
|
266
|
+
assert "09-skip-flag-bypasses" "expected exit 0 + unchanged content; got ec=$ec"
|
|
267
|
+
fi
|
|
268
|
+
else
|
|
269
|
+
assert "09-skip-flag-bypasses" "fragment missing at $HOOK"
|
|
270
|
+
fi
|
|
271
|
+
|
|
272
|
+
# --- Test 10 — Fixture parent path lives OUTSIDE ~/src/sunj-labs/ (Feathers ask)
|
|
273
|
+
if [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1; then
|
|
274
|
+
TMP=$(mktemp -d)
|
|
275
|
+
case "$TMP" in
|
|
276
|
+
"$HOME/src/sunj-labs"*)
|
|
277
|
+
assert "10-fixture-parent-outside-sunj-labs" "TMPDIR $TMP is inside ~/src/sunj-labs/; test setup broken"
|
|
278
|
+
rm -rf "$TMP"
|
|
279
|
+
;;
|
|
280
|
+
*)
|
|
281
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
282
|
+
out=$(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
283
|
+
ec=$?
|
|
284
|
+
merged=$(cat "$ADOPTER/.claude/settings.json")
|
|
285
|
+
rm -rf "$TMP"
|
|
286
|
+
if [ "$ec" = "0" ] && echo "$merged" | grep -q "base-pair-reprime.sh"; then
|
|
287
|
+
assert "10-fixture-parent-outside-sunj-labs" "ok"
|
|
288
|
+
else
|
|
289
|
+
assert "10-fixture-parent-outside-sunj-labs" "merge failed with outside-tree fixture; ec=$ec"
|
|
290
|
+
fi
|
|
291
|
+
;;
|
|
292
|
+
esac
|
|
293
|
+
else
|
|
294
|
+
assert "10-fixture-parent-outside-sunj-labs" "prereq missing: HOOK / DEEP_MODULE / jq"
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# --- Test 11 — jq missing — fragment skips cleanly
|
|
298
|
+
# Simulate jq missing by prepending a PATH dir with a "jq" stub that returns 127.
|
|
299
|
+
# This keeps bash + git available while making jq lookup fail (portable across macOS + Linux).
|
|
300
|
+
if [ -f "$HOOK" ]; then
|
|
301
|
+
TMP=$(mktemp -d)
|
|
302
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
303
|
+
FAKE_BIN="$TMP/fake-bin"
|
|
304
|
+
mkdir -p "$FAKE_BIN"
|
|
305
|
+
# Empty file named jq that is NOT executable — command -v finds it but exec fails.
|
|
306
|
+
# Actually simpler: put a script that exits 127 immediately.
|
|
307
|
+
cat > "$FAKE_BIN/jq" <<'EOF'
|
|
308
|
+
#!/bin/sh
|
|
309
|
+
exit 127
|
|
310
|
+
EOF
|
|
311
|
+
chmod -x "$FAKE_BIN/jq" # not executable — command -v returns non-zero
|
|
312
|
+
out=$(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" PATH="$FAKE_BIN:$PATH:__no_jq__" bash "$HOOK" 2>&1)
|
|
313
|
+
ec=$?
|
|
314
|
+
# Deep module uses `command -v jq` which checks executability — non-executable file returns non-zero.
|
|
315
|
+
rm -rf "$TMP"
|
|
316
|
+
if [ "$ec" = "0" ]; then
|
|
317
|
+
assert "11-jq-missing-graceful" "ok"
|
|
318
|
+
else
|
|
319
|
+
assert "11-jq-missing-graceful" "expected exit 0 when jq absent; got $ec"
|
|
320
|
+
fi
|
|
321
|
+
else
|
|
322
|
+
assert "11-jq-missing-graceful" "fragment missing at $HOOK"
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
# --- Test 12 — Bassclef self (git remote URL contains sunj-labs/bassclef) — skip merge
|
|
326
|
+
if [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1 && command -v git >/dev/null 2>&1; then
|
|
327
|
+
TMP=$(mktemp -d)
|
|
328
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
329
|
+
(cd "$ADOPTER" && git init -q 2>/dev/null && git remote add origin https://github.com/sunj-labs/bassclef 2>/dev/null)
|
|
330
|
+
original=$(cat "$ADOPTER/.claude/settings.json")
|
|
331
|
+
out=$(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
332
|
+
ec=$?
|
|
333
|
+
after=$(cat "$ADOPTER/.claude/settings.json")
|
|
334
|
+
rm -rf "$TMP"
|
|
335
|
+
if [ "$ec" = "0" ] && [ "$original" = "$after" ]; then
|
|
336
|
+
assert "12-bassclef-self-skip" "ok"
|
|
337
|
+
else
|
|
338
|
+
assert "12-bassclef-self-skip" "expected unchanged for bassclef self; ec=$ec"
|
|
339
|
+
fi
|
|
340
|
+
else
|
|
341
|
+
assert "12-bassclef-self-skip" "prereq missing: HOOK / DEEP_MODULE / jq / git"
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
# --- Test 13 — Adopter settings.json missing — created with hooks{} then merged
|
|
345
|
+
if [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1; then
|
|
346
|
+
TMP=$(mktemp -d)
|
|
347
|
+
mkdir -p "$TMP/bassclef/.claude"
|
|
348
|
+
mkdir -p "$TMP/bassclef/presence/install"
|
|
349
|
+
cp "$DEEP_MODULE" "$TMP/bassclef/presence/install/bassclef-hook-connect.sh"
|
|
350
|
+
cat > "$TMP/bassclef/.claude/settings.json" <<'EOF'
|
|
351
|
+
{
|
|
352
|
+
"hooks": {
|
|
353
|
+
"Stop": [
|
|
354
|
+
{
|
|
355
|
+
"hooks": [
|
|
356
|
+
{"type": "command", "command": "bash .claude/hooks/base-pair-reprime.sh"}
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
EOF
|
|
363
|
+
mkdir -p "$TMP/adopter/.claude"
|
|
364
|
+
out=$(cd "$TMP/adopter" && CWD="$TMP/adopter" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
365
|
+
ec=$?
|
|
366
|
+
if [ "$ec" = "0" ] && [ -f "$TMP/adopter/.claude/settings.json" ] && grep -q "base-pair-reprime.sh" "$TMP/adopter/.claude/settings.json"; then
|
|
367
|
+
assert "13-adopter-settings-missing-created" "ok"
|
|
368
|
+
else
|
|
369
|
+
assert "13-adopter-settings-missing-created" "expected settings.json created + merged; ec=$ec"
|
|
370
|
+
fi
|
|
371
|
+
rm -rf "$TMP"
|
|
372
|
+
else
|
|
373
|
+
assert "13-adopter-settings-missing-created" "prereq missing: HOOK / DEEP_MODULE / jq"
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
# --- Test 14 — Adopter settings.json malformed — skip without touching
|
|
377
|
+
if [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1; then
|
|
378
|
+
TMP=$(mktemp -d)
|
|
379
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
380
|
+
echo '{ "hooks": broken' > "$ADOPTER/.claude/settings.json"
|
|
381
|
+
original=$(cat "$ADOPTER/.claude/settings.json")
|
|
382
|
+
out=$(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
383
|
+
ec=$?
|
|
384
|
+
after=$(cat "$ADOPTER/.claude/settings.json")
|
|
385
|
+
rm -rf "$TMP"
|
|
386
|
+
if [ "$ec" = "0" ] && [ "$original" = "$after" ]; then
|
|
387
|
+
assert "14-adopter-malformed-untouched" "ok"
|
|
388
|
+
else
|
|
389
|
+
assert "14-adopter-malformed-untouched" "expected unchanged malformed file; ec=$ec"
|
|
390
|
+
fi
|
|
391
|
+
else
|
|
392
|
+
assert "14-adopter-malformed-untouched" "prereq missing: HOOK / DEEP_MODULE / jq"
|
|
393
|
+
fi
|
|
394
|
+
|
|
395
|
+
# --- Test 15 — Exit 0 on all paths
|
|
396
|
+
if [ -f "$HOOK" ]; then
|
|
397
|
+
TMP=$(mktemp -d)
|
|
398
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
399
|
+
(cd "$ADOPTER" && CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" >/dev/null 2>&1)
|
|
400
|
+
ec1=$?
|
|
401
|
+
(cd "$ADOPTER" && bash "$HOOK" >/dev/null 2>&1)
|
|
402
|
+
ec2=$?
|
|
403
|
+
(cd "$ADOPTER" && CWD="$ADOPTER" bash "$HOOK" >/dev/null 2>&1)
|
|
404
|
+
ec3=$?
|
|
405
|
+
rm -rf "$TMP"
|
|
406
|
+
if [ "$ec1" = "0" ] && [ "$ec2" = "0" ] && [ "$ec3" = "0" ]; then
|
|
407
|
+
assert "15-always-exit-0" "ok"
|
|
408
|
+
else
|
|
409
|
+
assert "15-always-exit-0" "expected all exit 0; got $ec1 / $ec2 / $ec3"
|
|
410
|
+
fi
|
|
411
|
+
else
|
|
412
|
+
assert "15-always-exit-0" "fragment missing at $HOOK"
|
|
413
|
+
fi
|
|
414
|
+
|
|
415
|
+
# --- Test 16 — Install-class project (CLAUDE_PROJECT_DIR set) resolves BASSCLEF_DIR
|
|
416
|
+
if [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1; then
|
|
417
|
+
TMP=$(mktemp -d)
|
|
418
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
419
|
+
out=$(cd "$ADOPTER" && CLAUDE_PROJECT_DIR="$ADOPTER" CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
420
|
+
ec=$?
|
|
421
|
+
merged=$(cat "$ADOPTER/.claude/settings.json")
|
|
422
|
+
rm -rf "$TMP"
|
|
423
|
+
if [ "$ec" = "0" ] && echo "$merged" | grep -q "base-pair-reprime.sh"; then
|
|
424
|
+
assert "16-install-class-project" "ok"
|
|
425
|
+
else
|
|
426
|
+
assert "16-install-class-project" "expected exit 0 + merge under CLAUDE_PROJECT_DIR; ec=$ec"
|
|
427
|
+
fi
|
|
428
|
+
else
|
|
429
|
+
assert "16-install-class-project" "prereq missing: HOOK / DEEP_MODULE / jq"
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
# --- Test 17 — Install-class operator (HOME_OVERRIDE fixture) resolves BASSCLEF_DIR
|
|
433
|
+
if [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1; then
|
|
434
|
+
TMP=$(mktemp -d)
|
|
435
|
+
HOME_FAKE=$(mktemp -d)
|
|
436
|
+
ADOPTER=$(build_poa_shape_fixture "$TMP")
|
|
437
|
+
out=$(cd "$ADOPTER" && HOME="$HOME_FAKE" CWD="$ADOPTER" BASSCLEF_DIR="$TMP/bassclef" bash "$HOOK" 2>&1)
|
|
438
|
+
ec=$?
|
|
439
|
+
merged=$(cat "$ADOPTER/.claude/settings.json")
|
|
440
|
+
rm -rf "$TMP" "$HOME_FAKE"
|
|
441
|
+
if [ "$ec" = "0" ] && echo "$merged" | grep -q "base-pair-reprime.sh"; then
|
|
442
|
+
assert "17-install-class-operator" "ok"
|
|
443
|
+
else
|
|
444
|
+
assert "17-install-class-operator" "expected exit 0 + merge under HOME override; ec=$ec"
|
|
445
|
+
fi
|
|
446
|
+
else
|
|
447
|
+
assert "17-install-class-operator" "prereq missing: HOOK / DEEP_MODULE / jq"
|
|
448
|
+
fi
|
|
449
|
+
|
|
450
|
+
# --- Test 18 — fixture-driven coverage (rides under run-with-fixtures.sh wrapper)
|
|
451
|
+
if [ -n "${FIXTURE_DIR:-}" ] && [ -f "$HOOK" ] && [ -f "$DEEP_MODULE" ] && command -v jq >/dev/null 2>&1; then
|
|
452
|
+
FIXTURE_TMP=$(mktemp -d)
|
|
453
|
+
mkdir -p "$FIXTURE_TMP/bassclef/.claude"
|
|
454
|
+
mkdir -p "$FIXTURE_TMP/bassclef/presence/install"
|
|
455
|
+
# Copy the shared function so the fragment can source it.
|
|
456
|
+
cp "$DEEP_MODULE" "$FIXTURE_TMP/bassclef/presence/install/bassclef-hook-connect.sh"
|
|
457
|
+
cat > "$FIXTURE_TMP/bassclef/.claude/settings.json" <<'EOF'
|
|
458
|
+
{
|
|
459
|
+
"hooks": {
|
|
460
|
+
"Stop": [
|
|
461
|
+
{"hooks": [{"type": "command", "command": "bash .claude/hooks/base-pair-reprime.sh"}]}
|
|
462
|
+
]
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
EOF
|
|
466
|
+
mkdir -p "$FIXTURE_DIR/.claude" 2>/dev/null
|
|
467
|
+
[ -f "$FIXTURE_DIR/.claude/settings.json" ] || echo '{"hooks":{}}' > "$FIXTURE_DIR/.claude/settings.json"
|
|
468
|
+
fixture_out=$(cd "$FIXTURE_DIR" && CWD="$FIXTURE_DIR" BASSCLEF_DIR="$FIXTURE_TMP/bassclef" bash "$HOOK" 2>&1)
|
|
469
|
+
ec=$?
|
|
470
|
+
fixture_name="${FIXTURE_DIR##*/}"
|
|
471
|
+
merged=$(cat "$FIXTURE_DIR/.claude/settings.json" 2>/dev/null || echo "")
|
|
472
|
+
rm -rf "$FIXTURE_TMP"
|
|
473
|
+
if [ "$ec" = "0" ] && echo "$merged" | grep -q "base-pair-reprime.sh"; then
|
|
474
|
+
assert "18-fixture-coverage-${fixture_name}" "ok"
|
|
475
|
+
else
|
|
476
|
+
assert "18-fixture-coverage-${fixture_name}" "fixture merge failed; ec=$ec"
|
|
477
|
+
fi
|
|
478
|
+
fi
|
|
479
|
+
|
|
480
|
+
# --- Summary
|
|
481
|
+
TOTAL=$((PASS + FAIL))
|
|
482
|
+
echo ""
|
|
483
|
+
echo "=== 00-bassclef-hook-connect.sh tests ==="
|
|
484
|
+
echo "Passed: $PASS / $TOTAL"
|
|
485
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
486
|
+
echo "Failed:"
|
|
487
|
+
for t in "${FAILED_TESTS[@]}"; do
|
|
488
|
+
echo " - $t"
|
|
489
|
+
done
|
|
490
|
+
exit 1
|
|
491
|
+
fi
|
|
492
|
+
exit 0
|