jfl 0.1.1 → 0.2.0
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/README.md +77 -7
- package/clawdbot-plugin/clawdbot.plugin.json +20 -0
- package/clawdbot-plugin/index.js +555 -0
- package/clawdbot-plugin/index.ts +582 -0
- package/clawdbot-skill/SKILL.md +33 -336
- package/clawdbot-skill/index.ts +491 -321
- package/clawdbot-skill/skill.json +4 -13
- package/dist/commands/clawdbot.d.ts +11 -0
- package/dist/commands/clawdbot.d.ts.map +1 -0
- package/dist/commands/clawdbot.js +215 -0
- package/dist/commands/clawdbot.js.map +1 -0
- package/dist/commands/gtm-process-update.d.ts +10 -0
- package/dist/commands/gtm-process-update.d.ts.map +1 -0
- package/dist/commands/gtm-process-update.js +101 -0
- package/dist/commands/gtm-process-update.js.map +1 -0
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +203 -15
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/openclaw.d.ts +56 -0
- package/dist/commands/openclaw.d.ts.map +1 -0
- package/dist/commands/openclaw.js +700 -0
- package/dist/commands/openclaw.js.map +1 -0
- package/dist/commands/service-validate.d.ts +12 -0
- package/dist/commands/service-validate.d.ts.map +1 -0
- package/dist/commands/service-validate.js +611 -0
- package/dist/commands/service-validate.js.map +1 -0
- package/dist/commands/services-create.d.ts +15 -0
- package/dist/commands/services-create.d.ts.map +1 -0
- package/dist/commands/services-create.js +1452 -0
- package/dist/commands/services-create.js.map +1 -0
- package/dist/commands/services-sync-agents.d.ts +23 -0
- package/dist/commands/services-sync-agents.d.ts.map +1 -0
- package/dist/commands/services-sync-agents.js +207 -0
- package/dist/commands/services-sync-agents.js.map +1 -0
- package/dist/commands/services.d.ts +7 -1
- package/dist/commands/services.d.ts.map +1 -1
- package/dist/commands/services.js +347 -22
- package/dist/commands/services.js.map +1 -1
- package/dist/commands/update.js +0 -0
- package/dist/commands/validate-settings.d.ts +37 -0
- package/dist/commands/validate-settings.d.ts.map +1 -0
- package/dist/commands/validate-settings.js +197 -0
- package/dist/commands/validate-settings.js.map +1 -0
- package/dist/index.js +155 -60
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-generator.d.ts.map +1 -1
- package/dist/lib/agent-generator.js +94 -1
- package/dist/lib/agent-generator.js.map +1 -1
- package/dist/lib/openclaw-registry.d.ts +48 -0
- package/dist/lib/openclaw-registry.d.ts.map +1 -0
- package/dist/lib/openclaw-registry.js +181 -0
- package/dist/lib/openclaw-registry.js.map +1 -0
- package/dist/lib/openclaw-sdk.d.ts +107 -0
- package/dist/lib/openclaw-sdk.d.ts.map +1 -0
- package/dist/lib/openclaw-sdk.js +208 -0
- package/dist/lib/openclaw-sdk.js.map +1 -0
- package/dist/lib/peer-agent-generator.d.ts +44 -0
- package/dist/lib/peer-agent-generator.d.ts.map +1 -0
- package/dist/lib/peer-agent-generator.js +286 -0
- package/dist/lib/peer-agent-generator.js.map +1 -0
- package/dist/lib/service-detector.d.ts +1 -1
- package/dist/lib/service-detector.d.ts.map +1 -1
- package/dist/lib/service-detector.js +118 -5
- package/dist/lib/service-detector.js.map +1 -1
- package/dist/lib/service-gtm.d.ts +157 -0
- package/dist/lib/service-gtm.d.ts.map +1 -0
- package/dist/lib/service-gtm.js +786 -0
- package/dist/lib/service-gtm.js.map +1 -0
- package/dist/lib/service-mcp-base.d.ts +10 -1
- package/dist/lib/service-mcp-base.d.ts.map +1 -1
- package/dist/lib/service-mcp-base.js +20 -1
- package/dist/lib/service-mcp-base.js.map +1 -1
- package/dist/mcp/service-peer-mcp.d.ts +36 -0
- package/dist/mcp/service-peer-mcp.d.ts.map +1 -0
- package/dist/mcp/service-peer-mcp.js +220 -0
- package/dist/mcp/service-peer-mcp.js.map +1 -0
- package/dist/mcp/service-registry-mcp.js +0 -0
- package/dist/utils/settings-validator.d.ts +4 -1
- package/dist/utils/settings-validator.d.ts.map +1 -1
- package/dist/utils/settings-validator.js +25 -1
- package/dist/utils/settings-validator.js.map +1 -1
- package/package.json +2 -1
- package/template/.claude/service-settings.json +32 -0
- package/template/.claude/settings.json +10 -0
- package/template/.claude/skills/end/SKILL.md +1780 -0
- package/template/.jfl/config.json +2 -1
- package/template/.mcp.json +1 -7
- package/template/CLAUDE.md +1042 -248
- package/template/CLAUDE.md.bak +1187 -0
- package/template/scripts/commit-gtm.sh +56 -0
- package/template/scripts/commit-product.sh +68 -0
- package/template/scripts/migrate-to-branch-sessions.sh +201 -0
- package/template/scripts/session/auto-commit.sh +4 -3
- package/template/scripts/session/jfl-doctor.sh +222 -83
- package/template/scripts/session/session-cleanup.sh +109 -21
- package/template/scripts/session/session-end.sh +26 -13
- package/template/scripts/session/session-init.sh +280 -98
- package/template/scripts/session/test-critical-infrastructure.sh +293 -0
- package/template/scripts/session/test-experience-level.sh +336 -0
- package/template/scripts/session/test-session-cleanup.sh +268 -0
- package/template/scripts/session/test-session-sync.sh +320 -0
- package/template/scripts/where-am-i.sh +78 -0
- package/template/templates/service-agent/.claude/settings.json +32 -0
- package/template/templates/service-agent/CLAUDE.md +334 -0
- package/template/templates/service-agent/knowledge/ARCHITECTURE.md +115 -0
- package/template/templates/service-agent/knowledge/DEPLOYMENT.md +199 -0
- package/template/templates/service-agent/knowledge/RUNBOOK.md +412 -0
- package/template/templates/service-agent/knowledge/SERVICE_SPEC.md +77 -0
- package/dist/commands/session-mgmt.d.ts +0 -33
- package/dist/commands/session-mgmt.d.ts.map +0 -1
- package/dist/commands/session-mgmt.js +0 -404
- package/dist/commands/session-mgmt.js.map +0 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Critical Infrastructure Tests - Work Loss Prevention
|
|
4
|
+
#
|
|
5
|
+
# Tests the three pillars that prevent work loss:
|
|
6
|
+
# 1. Signal handling (Ctrl+C, crashes)
|
|
7
|
+
# 2. Unmerged branch detection
|
|
8
|
+
# 3. Crash reconciliation
|
|
9
|
+
#
|
|
10
|
+
# @purpose Rigorous testing of work-loss-prevention infrastructure
|
|
11
|
+
|
|
12
|
+
set -e
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
REPO_DIR="$(git rev-parse --path-format=absolute --git-common-dir)"
|
|
16
|
+
REPO_DIR="${REPO_DIR%/.git}"
|
|
17
|
+
WORKTREES_DIR="$REPO_DIR/worktrees"
|
|
18
|
+
|
|
19
|
+
# Colors
|
|
20
|
+
RED='\033[0;31m'
|
|
21
|
+
GREEN='\033[0;32m'
|
|
22
|
+
YELLOW='\033[1;33m'
|
|
23
|
+
BLUE='\033[0;34m'
|
|
24
|
+
NC='\033[0m'
|
|
25
|
+
|
|
26
|
+
TESTS_PASSED=0
|
|
27
|
+
TESTS_FAILED=0
|
|
28
|
+
|
|
29
|
+
# Test helper functions
|
|
30
|
+
pass() {
|
|
31
|
+
echo -e "${GREEN}✓${NC} $1"
|
|
32
|
+
TESTS_PASSED=$((TESTS_PASSED + 1))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fail() {
|
|
36
|
+
echo -e "${RED}✗${NC} $1"
|
|
37
|
+
TESTS_FAILED=$((TESTS_FAILED + 1))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
section() {
|
|
41
|
+
echo ""
|
|
42
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
43
|
+
echo -e "${BLUE}$1${NC}"
|
|
44
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
cleanup_test_branches() {
|
|
48
|
+
# Clean up any test branches from previous runs
|
|
49
|
+
git branch -D test-merged-branch 2>/dev/null || true
|
|
50
|
+
git branch -D test-unmerged-branch 2>/dev/null || true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# ==============================================================================
|
|
54
|
+
# Test 1: Signal Handling in Auto-Commit
|
|
55
|
+
# ==============================================================================
|
|
56
|
+
|
|
57
|
+
test_signal_handling() {
|
|
58
|
+
section "Test 1: Signal Handling (auto-commit graceful shutdown)"
|
|
59
|
+
|
|
60
|
+
# Check for signal trap in auto-commit.sh
|
|
61
|
+
if grep -q "trap graceful_shutdown" "$SCRIPT_DIR/auto-commit.sh"; then
|
|
62
|
+
pass "Signal trap registered for graceful shutdown"
|
|
63
|
+
else
|
|
64
|
+
fail "No signal trap found in auto-commit.sh"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Check for graceful_shutdown function
|
|
68
|
+
if grep -q "graceful_shutdown()" "$SCRIPT_DIR/auto-commit.sh"; then
|
|
69
|
+
pass "graceful_shutdown() function exists"
|
|
70
|
+
else
|
|
71
|
+
fail "graceful_shutdown() function missing"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Check that stop_daemon sends SIGTERM (not hard kill)
|
|
75
|
+
if grep -A10 "stop_daemon()" "$SCRIPT_DIR/auto-commit.sh" | grep -q "kill -TERM"; then
|
|
76
|
+
pass "stop_daemon() sends SIGTERM for graceful shutdown"
|
|
77
|
+
else
|
|
78
|
+
fail "stop_daemon() uses hard kill instead of graceful shutdown"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Anti-test: Verify old behavior is removed
|
|
82
|
+
if grep -A10 "stop_daemon()" "$SCRIPT_DIR/auto-commit.sh" | grep -q "kill \"\$pid\" 2>/dev/null$"; then
|
|
83
|
+
fail "Old hard-kill behavior still present"
|
|
84
|
+
else
|
|
85
|
+
pass "Old hard-kill behavior removed"
|
|
86
|
+
fi
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# ==============================================================================
|
|
90
|
+
# Test 2: Doctor Script - Unmerged Branch Detection
|
|
91
|
+
# ==============================================================================
|
|
92
|
+
|
|
93
|
+
test_unmerged_detection() {
|
|
94
|
+
section "Test 2: Unmerged Branch Detection"
|
|
95
|
+
|
|
96
|
+
cd "$REPO_DIR"
|
|
97
|
+
|
|
98
|
+
# Create test branches
|
|
99
|
+
cleanup_test_branches
|
|
100
|
+
|
|
101
|
+
# Create merged branch (0 commits ahead)
|
|
102
|
+
git branch test-merged-branch HEAD 2>/dev/null || true
|
|
103
|
+
|
|
104
|
+
# Create unmerged branch (1 commit ahead)
|
|
105
|
+
git checkout -b test-unmerged-branch 2>/dev/null || git checkout test-unmerged-branch
|
|
106
|
+
echo "test change" >> .jfl/test-file.txt
|
|
107
|
+
git add .jfl/test-file.txt
|
|
108
|
+
git commit -m "test: unmerged commit" >/dev/null 2>&1 || true
|
|
109
|
+
git checkout main 2>/dev/null
|
|
110
|
+
|
|
111
|
+
# Run doctor and capture output
|
|
112
|
+
output=$("$SCRIPT_DIR/jfl-doctor.sh" --verbose 2>&1 || true)
|
|
113
|
+
|
|
114
|
+
# Test: Check code has MERGED label (may not appear in output if no merged orphans exist)
|
|
115
|
+
if grep -q "✓ MERGED (safe to delete):" "$SCRIPT_DIR/jfl-doctor.sh"; then
|
|
116
|
+
pass "Merged branches labeled as safe in code"
|
|
117
|
+
else
|
|
118
|
+
fail "Merged branches not properly labeled in code"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# Test: UNMERGED branch shows up as do NOT delete
|
|
122
|
+
if echo "$output" | grep -q "⚠️ UNMERGED (do NOT delete):"; then
|
|
123
|
+
pass "Unmerged branches labeled as dangerous"
|
|
124
|
+
else
|
|
125
|
+
fail "Unmerged branches not properly labeled"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Test: Check code formats commit counts correctly
|
|
129
|
+
if grep -q "commits NOT in main" "$SCRIPT_DIR/jfl-doctor.sh"; then
|
|
130
|
+
pass "Unmerged commit count format present in code"
|
|
131
|
+
else
|
|
132
|
+
fail "Unmerged commit count format missing"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Anti-test: Verify unmerged branches are NEVER deleted in --fix mode
|
|
136
|
+
before_count=$(git branch --list 'test-*' | wc -l | tr -d ' ')
|
|
137
|
+
"$SCRIPT_DIR/jfl-doctor.sh" --fix >/dev/null 2>&1 || true
|
|
138
|
+
after_count=$(git branch --list 'test-*' | wc -l | tr -d ' ')
|
|
139
|
+
|
|
140
|
+
if git rev-parse test-unmerged-branch >/dev/null 2>&1; then
|
|
141
|
+
pass "Unmerged branch NOT deleted by --fix mode"
|
|
142
|
+
else
|
|
143
|
+
fail "DANGER: --fix mode deleted unmerged branch!"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Cleanup
|
|
147
|
+
cleanup_test_branches
|
|
148
|
+
rm -f .jfl/test-file.txt
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# ==============================================================================
|
|
152
|
+
# Test 3: Crash Reconciliation
|
|
153
|
+
# ==============================================================================
|
|
154
|
+
|
|
155
|
+
test_crash_reconciliation() {
|
|
156
|
+
section "Test 3: Crash Reconciliation (uncommitted work detection)"
|
|
157
|
+
|
|
158
|
+
# Check that session-init scans for uncommitted work
|
|
159
|
+
if grep -q "Check for uncommitted work in stale sessions" "$SCRIPT_DIR/session-init.sh"; then
|
|
160
|
+
pass "Crash reconciliation code present in session-init"
|
|
161
|
+
else
|
|
162
|
+
fail "No crash reconciliation in session-init"
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# Check for the prompt
|
|
166
|
+
if grep -q "This work needs to be saved before continuing" "$SCRIPT_DIR/session-init.sh"; then
|
|
167
|
+
pass "User prompt for uncommitted work exists"
|
|
168
|
+
else
|
|
169
|
+
fail "No prompt for uncommitted work"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# Check for auto-commit option
|
|
173
|
+
if grep -q "Auto-commit all and continue" "$SCRIPT_DIR/session-init.sh"; then
|
|
174
|
+
pass "Auto-commit option available"
|
|
175
|
+
else
|
|
176
|
+
fail "No auto-commit option in crash recovery"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Anti-test: Verify it doesn't just skip uncommitted work
|
|
180
|
+
if grep -q "git status --porcelain" "$SCRIPT_DIR/session-init.sh"; then
|
|
181
|
+
pass "Checks for uncommitted changes properly"
|
|
182
|
+
else
|
|
183
|
+
fail "Doesn't check for uncommitted changes"
|
|
184
|
+
fi
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# ==============================================================================
|
|
188
|
+
# Test 4: REPO_DIR Resolution (worktree-aware)
|
|
189
|
+
# ==============================================================================
|
|
190
|
+
|
|
191
|
+
test_repo_dir_resolution() {
|
|
192
|
+
section "Test 4: REPO_DIR Resolution (works from worktrees)"
|
|
193
|
+
|
|
194
|
+
# Check that doctor uses git to find main repo
|
|
195
|
+
if grep -q "git rev-parse --path-format=absolute --git-common-dir" "$SCRIPT_DIR/jfl-doctor.sh"; then
|
|
196
|
+
pass "Doctor script uses git to find main repo"
|
|
197
|
+
else
|
|
198
|
+
fail "Doctor script doesn't resolve repo correctly"
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# Test from main repo
|
|
202
|
+
cd "$REPO_DIR"
|
|
203
|
+
detected_repo=$("$SCRIPT_DIR/jfl-doctor.sh" 2>&1 | grep -c "jfl doctor" || echo "0")
|
|
204
|
+
if [[ "$detected_repo" -gt 0 ]]; then
|
|
205
|
+
pass "Doctor runs from main repo"
|
|
206
|
+
else
|
|
207
|
+
fail "Doctor fails from main repo"
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
# Test from worktree (if any exist)
|
|
211
|
+
if [[ -d "$WORKTREES_DIR" ]]; then
|
|
212
|
+
first_worktree=$(ls "$WORKTREES_DIR" | head -1)
|
|
213
|
+
if [[ -n "$first_worktree" ]]; then
|
|
214
|
+
cd "$WORKTREES_DIR/$first_worktree"
|
|
215
|
+
detected_repo=$("$SCRIPT_DIR/jfl-doctor.sh" 2>&1 | grep -c "jfl doctor" || echo "0")
|
|
216
|
+
if [[ "$detected_repo" -gt 0 ]]; then
|
|
217
|
+
pass "Doctor runs from worktree"
|
|
218
|
+
else
|
|
219
|
+
fail "Doctor fails from worktree"
|
|
220
|
+
fi
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
cd "$REPO_DIR"
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# ==============================================================================
|
|
228
|
+
# Test 5: Session Sync (pulls main into worktree)
|
|
229
|
+
# ==============================================================================
|
|
230
|
+
|
|
231
|
+
test_session_sync() {
|
|
232
|
+
section "Test 5: Session Sync (main branch updates)"
|
|
233
|
+
|
|
234
|
+
# Check if session-sync exists
|
|
235
|
+
if [[ -f "$SCRIPT_DIR/session-sync.sh" ]]; then
|
|
236
|
+
pass "session-sync.sh exists"
|
|
237
|
+
|
|
238
|
+
# Check if it pulls main
|
|
239
|
+
if grep -q "git pull\|git fetch" "$SCRIPT_DIR/session-sync.sh"; then
|
|
240
|
+
pass "session-sync pulls from remote"
|
|
241
|
+
else
|
|
242
|
+
fail "session-sync doesn't pull from remote"
|
|
243
|
+
fi
|
|
244
|
+
else
|
|
245
|
+
fail "session-sync.sh missing"
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# Check if session-init calls session-sync
|
|
249
|
+
if [[ -f "$SCRIPT_DIR/session-init.sh" ]]; then
|
|
250
|
+
if grep -q "session-sync" "$SCRIPT_DIR/session-init.sh"; then
|
|
251
|
+
pass "session-init calls session-sync"
|
|
252
|
+
else
|
|
253
|
+
fail "session-init doesn't call session-sync"
|
|
254
|
+
fi
|
|
255
|
+
fi
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
# ==============================================================================
|
|
259
|
+
# Run All Tests
|
|
260
|
+
# ==============================================================================
|
|
261
|
+
|
|
262
|
+
main() {
|
|
263
|
+
echo ""
|
|
264
|
+
echo "╔══════════════════════════════════════════════════════════╗"
|
|
265
|
+
echo "║ JFL Critical Infrastructure Tests ║"
|
|
266
|
+
echo "║ Work Loss Prevention ║"
|
|
267
|
+
echo "╚══════════════════════════════════════════════════════════╝"
|
|
268
|
+
|
|
269
|
+
test_signal_handling
|
|
270
|
+
test_unmerged_detection
|
|
271
|
+
test_crash_reconciliation
|
|
272
|
+
test_repo_dir_resolution
|
|
273
|
+
test_session_sync
|
|
274
|
+
|
|
275
|
+
# Summary
|
|
276
|
+
echo ""
|
|
277
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
278
|
+
echo -e "${BLUE}Test Results${NC}"
|
|
279
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
280
|
+
echo -e "${GREEN}Passed:${NC} $TESTS_PASSED"
|
|
281
|
+
if [[ $TESTS_FAILED -gt 0 ]]; then
|
|
282
|
+
echo -e "${RED}Failed:${NC} $TESTS_FAILED"
|
|
283
|
+
echo ""
|
|
284
|
+
echo -e "${RED}CRITICAL INFRASTRUCTURE HAS ISSUES${NC}"
|
|
285
|
+
exit 1
|
|
286
|
+
else
|
|
287
|
+
echo ""
|
|
288
|
+
echo -e "${GREEN}✓ All critical infrastructure tests passed${NC}"
|
|
289
|
+
exit 0
|
|
290
|
+
fi
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
main "$@"
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Experience-Level Testing for Critical Infrastructure
|
|
4
|
+
#
|
|
5
|
+
# Tests ACTUAL USER WORKFLOWS, not just code paths.
|
|
6
|
+
# Simulates real scenarios that would cause work loss.
|
|
7
|
+
#
|
|
8
|
+
# @purpose Test critical infrastructure from user's perspective
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
REPO_DIR="$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null || echo ".")"
|
|
14
|
+
REPO_DIR="${REPO_DIR%/.git}"
|
|
15
|
+
|
|
16
|
+
# Colors
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
GREEN='\033[0;32m'
|
|
19
|
+
YELLOW='\033[1;33m'
|
|
20
|
+
BLUE='\033[0;34m'
|
|
21
|
+
NC='\033[0m'
|
|
22
|
+
|
|
23
|
+
TESTS_PASSED=0
|
|
24
|
+
TESTS_FAILED=0
|
|
25
|
+
|
|
26
|
+
pass() {
|
|
27
|
+
echo -e "${GREEN}✓${NC} $1"
|
|
28
|
+
TESTS_PASSED=$((TESTS_PASSED + 1))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fail() {
|
|
32
|
+
echo -e "${RED}✗${NC} $1"
|
|
33
|
+
TESTS_FAILED=$((TESTS_FAILED + 1))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
section() {
|
|
37
|
+
echo ""
|
|
38
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
39
|
+
echo -e "${BLUE}$1${NC}"
|
|
40
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# ==============================================================================
|
|
44
|
+
# Experience Test 1: Ctrl+C During Auto-Commit
|
|
45
|
+
# ==============================================================================
|
|
46
|
+
|
|
47
|
+
test_ctrl_c_experience() {
|
|
48
|
+
section "Experience Test 1: Ctrl+C During Auto-Commit"
|
|
49
|
+
|
|
50
|
+
echo "Scenario: User presses Ctrl+C while auto-commit is running"
|
|
51
|
+
echo "Expected: Final commit happens, no work lost"
|
|
52
|
+
echo ""
|
|
53
|
+
|
|
54
|
+
# Check that signal handling is in place
|
|
55
|
+
if grep -q "trap graceful_shutdown SIGINT SIGTERM" "$SCRIPT_DIR/auto-commit.sh"; then
|
|
56
|
+
pass "Signal trap will catch Ctrl+C"
|
|
57
|
+
else
|
|
58
|
+
fail "No signal trap - Ctrl+C will lose work"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Check that graceful shutdown commits
|
|
62
|
+
if grep -A10 "graceful_shutdown()" "$SCRIPT_DIR/auto-commit.sh" | grep -q "do_commit"; then
|
|
63
|
+
pass "Graceful shutdown runs final commit"
|
|
64
|
+
else
|
|
65
|
+
fail "Graceful shutdown doesn't commit - work lost"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Check that it also commits submodules
|
|
69
|
+
if grep -A10 "graceful_shutdown()" "$SCRIPT_DIR/auto-commit.sh" | grep -q "commit_submodules"; then
|
|
70
|
+
pass "Graceful shutdown commits submodules too"
|
|
71
|
+
else
|
|
72
|
+
fail "Submodule changes lost on Ctrl+C"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
echo ""
|
|
76
|
+
echo "User Experience: Press Ctrl+C → See 'saving final changes' → Changes committed → Clean exit"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# ==============================================================================
|
|
80
|
+
# Experience Test 2: Crashed Session, Fresh Start
|
|
81
|
+
# ==============================================================================
|
|
82
|
+
|
|
83
|
+
test_crash_recovery_experience() {
|
|
84
|
+
section "Experience Test 2: Crashed Session Recovery"
|
|
85
|
+
|
|
86
|
+
echo "Scenario: Session crashed yesterday, user starts new session today"
|
|
87
|
+
echo "Expected: Prompted to save uncommitted work from crashed session"
|
|
88
|
+
echo ""
|
|
89
|
+
|
|
90
|
+
# Check that session-init scans for uncommitted work
|
|
91
|
+
if grep -q "Check for uncommitted work in stale sessions" "$SCRIPT_DIR/session-init.sh"; then
|
|
92
|
+
pass "Session start checks for abandoned work"
|
|
93
|
+
else
|
|
94
|
+
fail "No check for crashed session work - silently lost"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Check that user is prompted, not auto-committed silently
|
|
98
|
+
if grep -q "Options:" "$SCRIPT_DIR/session-init.sh" && grep -q "Choose \[1-3\]" "$SCRIPT_DIR/session-init.sh"; then
|
|
99
|
+
pass "User is prompted to review abandoned work"
|
|
100
|
+
else
|
|
101
|
+
fail "No user prompt - work handled without consent"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Check that auto-commit is an option
|
|
105
|
+
if grep -q "Auto-commit all and continue" "$SCRIPT_DIR/session-init.sh"; then
|
|
106
|
+
pass "User can auto-commit with one choice"
|
|
107
|
+
else
|
|
108
|
+
fail "No quick auto-commit option"
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
echo ""
|
|
112
|
+
echo "User Experience: Start session → See 'Found 2 sessions with uncommitted work' → Choose option → Work saved → Continue"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# ==============================================================================
|
|
116
|
+
# Experience Test 3: Someone Else Pushed, I Start Session
|
|
117
|
+
# ==============================================================================
|
|
118
|
+
|
|
119
|
+
test_team_sync_experience() {
|
|
120
|
+
section "Experience Test 3: Team Member Pushed Changes"
|
|
121
|
+
|
|
122
|
+
echo "Scenario: Teammate pushed to main, I start a new session"
|
|
123
|
+
echo "Expected: My session starts with their latest changes"
|
|
124
|
+
echo ""
|
|
125
|
+
|
|
126
|
+
# Check that session-sync exists and is tracked
|
|
127
|
+
if git ls-files "$SCRIPT_DIR/session-sync.sh" >/dev/null 2>&1; then
|
|
128
|
+
pass "session-sync.sh is tracked in git (all worktrees get it)"
|
|
129
|
+
else
|
|
130
|
+
fail "session-sync.sh not tracked - worktrees won't have sync"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Check that it's called on session start
|
|
134
|
+
if grep -q "session-sync" "$SCRIPT_DIR/session-init.sh" 2>/dev/null || \
|
|
135
|
+
grep -q "session-sync" "$REPO_DIR/.claude/settings.json" 2>/dev/null; then
|
|
136
|
+
pass "Session start triggers sync"
|
|
137
|
+
else
|
|
138
|
+
fail "Sync not triggered on session start"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Check that sync pulls main
|
|
142
|
+
if grep -q "git pull origin" "$SCRIPT_DIR/session-sync.sh" 2>/dev/null; then
|
|
143
|
+
pass "Sync pulls latest from origin"
|
|
144
|
+
else
|
|
145
|
+
fail "Sync doesn't pull - user starts with stale code"
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
echo ""
|
|
149
|
+
echo "User Experience: Start session → Syncing repos → Up to date → See teammate's changes → Continue"
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# ==============================================================================
|
|
153
|
+
# Experience Test 4: I Have Unmerged Work, Someone Runs Doctor
|
|
154
|
+
# ==============================================================================
|
|
155
|
+
|
|
156
|
+
test_unmerged_work_safety() {
|
|
157
|
+
section "Experience Test 4: Protecting Unmerged Work"
|
|
158
|
+
|
|
159
|
+
echo "Scenario: My branch has 4 commits, teammate runs 'doctor --fix'"
|
|
160
|
+
echo "Expected: My work is NOT deleted"
|
|
161
|
+
echo ""
|
|
162
|
+
|
|
163
|
+
# Check that doctor separates merged vs unmerged
|
|
164
|
+
if grep -q "⚠️ UNMERGED (do NOT delete)" "$SCRIPT_DIR/jfl-doctor.sh"; then
|
|
165
|
+
pass "Doctor clearly labels unmerged branches"
|
|
166
|
+
else
|
|
167
|
+
fail "Doctor doesn't distinguish merged vs unmerged"
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# Check that --fix never deletes unmerged (check code, not output)
|
|
171
|
+
# The key is: only $merged_list is deleted, never $unmerged_list
|
|
172
|
+
if grep -A20 "if \[\[ \$unmerged_orphans -gt 0 \]\]" "$SCRIPT_DIR/jfl-doctor.sh" | \
|
|
173
|
+
grep -q "UNMERGED.*do NOT delete"; then
|
|
174
|
+
pass "--fix mode labels unmerged branches correctly"
|
|
175
|
+
else
|
|
176
|
+
fail "DANGER: --fix might delete unmerged work"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Check that only merged branches are deleted
|
|
180
|
+
if grep -B5 "git branch -D" "$SCRIPT_DIR/jfl-doctor.sh" | grep -q "merged_orphans -gt 0"; then
|
|
181
|
+
pass "Only deletes branches that are fully merged"
|
|
182
|
+
else
|
|
183
|
+
fail "Deletion logic might be unsafe"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
echo ""
|
|
187
|
+
echo "User Experience: Teammate runs doctor --fix → My unmerged branch preserved → I can continue work"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# ==============================================================================
|
|
191
|
+
# Experience Test 5: Network Fails During Sync
|
|
192
|
+
# ==============================================================================
|
|
193
|
+
|
|
194
|
+
test_network_failure_graceful() {
|
|
195
|
+
section "Experience Test 5: Network Failure Handling"
|
|
196
|
+
|
|
197
|
+
echo "Scenario: Network goes down during session sync"
|
|
198
|
+
echo "Expected: Graceful warning, session continues with local state"
|
|
199
|
+
echo ""
|
|
200
|
+
|
|
201
|
+
# Check for network error handling
|
|
202
|
+
if grep -q "Could not fetch.*no network" "$SCRIPT_DIR/session-sync.sh" 2>/dev/null; then
|
|
203
|
+
pass "Network failures handled gracefully"
|
|
204
|
+
else
|
|
205
|
+
fail "Network failure might crash session"
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
# Check that sync doesn't block on network failure
|
|
209
|
+
if grep -A5 "git fetch origin" "$SCRIPT_DIR/session-sync.sh" 2>/dev/null | grep -q "|| {"; then
|
|
210
|
+
pass "Fetch failures don't block session"
|
|
211
|
+
else
|
|
212
|
+
fail "Session might hang on network failure"
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# Check that user is warned but can continue
|
|
216
|
+
if grep -q "WARNING.*network" "$SCRIPT_DIR/session-sync.sh" 2>/dev/null; then
|
|
217
|
+
pass "User warned about network issues"
|
|
218
|
+
else
|
|
219
|
+
fail "Silent network failure - user confused"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
echo ""
|
|
223
|
+
echo "User Experience: Start session → 'WARNING: Could not fetch (no network?)' → Continue working offline"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# ==============================================================================
|
|
227
|
+
# Experience Test 6: Uncommitted Changes + Behind Origin
|
|
228
|
+
# ==============================================================================
|
|
229
|
+
|
|
230
|
+
test_uncommitted_and_behind() {
|
|
231
|
+
section "Experience Test 6: Uncommitted Changes + Behind Origin"
|
|
232
|
+
|
|
233
|
+
echo "Scenario: I have uncommitted work, main is behind origin"
|
|
234
|
+
echo "Expected: Sync refuses to pull, warns me, preserves my work"
|
|
235
|
+
echo ""
|
|
236
|
+
|
|
237
|
+
# Check for uncommitted + behind detection
|
|
238
|
+
if grep -q "has uncommitted changes AND is behind" "$SCRIPT_DIR/session-sync.sh" 2>/dev/null; then
|
|
239
|
+
pass "Detects dangerous situation (uncommitted + behind)"
|
|
240
|
+
else
|
|
241
|
+
fail "Doesn't detect uncommitted + behind - might lose work"
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# Check that it exits with error
|
|
245
|
+
if grep -A5 "uncommitted changes AND is behind" "$SCRIPT_DIR/session-sync.sh" 2>/dev/null | grep -q "exit 1\|FAILURES="; then
|
|
246
|
+
pass "Blocks sync to prevent data loss"
|
|
247
|
+
else
|
|
248
|
+
fail "Doesn't block sync - work could be lost"
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
# Check that it tells user what to do
|
|
252
|
+
if grep -A5 "uncommitted changes AND is behind" "$SCRIPT_DIR/session-sync.sh" 2>/dev/null | grep -q "commit or stash"; then
|
|
253
|
+
pass "Guides user to resolve safely"
|
|
254
|
+
else
|
|
255
|
+
fail "No guidance - user stuck"
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
echo ""
|
|
259
|
+
echo "User Experience: Start session → 'ERROR: uncommitted changes AND behind' → Commit first → Retry → Success"
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
# ==============================================================================
|
|
263
|
+
# CI/CD Integration Check
|
|
264
|
+
# ==============================================================================
|
|
265
|
+
|
|
266
|
+
test_ci_integration() {
|
|
267
|
+
section "CI/CD Integration"
|
|
268
|
+
|
|
269
|
+
echo "Checking if tests are integrated into CI/CD pipeline"
|
|
270
|
+
echo ""
|
|
271
|
+
|
|
272
|
+
# Check for GitHub Actions workflow
|
|
273
|
+
if [[ -f "$REPO_DIR/.github/workflows/test-critical-infrastructure.yml" ]]; then
|
|
274
|
+
pass "GitHub Actions workflow exists for tests"
|
|
275
|
+
else
|
|
276
|
+
echo -e "${YELLOW}⚠${NC} No GitHub Actions workflow (recommended)"
|
|
277
|
+
echo " Create .github/workflows/test-critical-infrastructure.yml"
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
# Check for pre-commit hook
|
|
281
|
+
if [[ -f "$REPO_DIR/.git/hooks/pre-commit" ]] || \
|
|
282
|
+
[[ -f "$REPO_DIR/.husky/pre-commit" ]]; then
|
|
283
|
+
pass "Pre-commit hook exists"
|
|
284
|
+
else
|
|
285
|
+
echo -e "${YELLOW}⚠${NC} No pre-commit hook (recommended)"
|
|
286
|
+
echo " Run tests before commit to catch issues early"
|
|
287
|
+
fi
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
# ==============================================================================
|
|
291
|
+
# Run All Experience Tests
|
|
292
|
+
# ==============================================================================
|
|
293
|
+
|
|
294
|
+
main() {
|
|
295
|
+
echo ""
|
|
296
|
+
echo "╔══════════════════════════════════════════════════════════╗"
|
|
297
|
+
echo "║ Experience-Level Testing ║"
|
|
298
|
+
echo "║ Testing Actual User Workflows ║"
|
|
299
|
+
echo "╚══════════════════════════════════════════════════════════╝"
|
|
300
|
+
|
|
301
|
+
test_ctrl_c_experience
|
|
302
|
+
test_crash_recovery_experience
|
|
303
|
+
test_team_sync_experience
|
|
304
|
+
test_unmerged_work_safety
|
|
305
|
+
test_network_failure_graceful
|
|
306
|
+
test_uncommitted_and_behind
|
|
307
|
+
test_ci_integration
|
|
308
|
+
|
|
309
|
+
# Summary
|
|
310
|
+
echo ""
|
|
311
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
312
|
+
echo -e "${BLUE}Test Results${NC}"
|
|
313
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
314
|
+
echo -e "${GREEN}Passed:${NC} $TESTS_PASSED"
|
|
315
|
+
if [[ $TESTS_FAILED -gt 0 ]]; then
|
|
316
|
+
echo -e "${RED}Failed:${NC} $TESTS_FAILED"
|
|
317
|
+
echo ""
|
|
318
|
+
echo -e "${RED}EXPERIENCE-LEVEL ISSUES FOUND${NC}"
|
|
319
|
+
echo "Fix these before releasing to users"
|
|
320
|
+
exit 1
|
|
321
|
+
else
|
|
322
|
+
echo ""
|
|
323
|
+
echo -e "${GREEN}✓ All user experience paths protected${NC}"
|
|
324
|
+
echo ""
|
|
325
|
+
echo "Verified workflows:"
|
|
326
|
+
echo " • Ctrl+C doesn't lose work"
|
|
327
|
+
echo " • Crashed sessions recovered"
|
|
328
|
+
echo " • Team changes synced automatically"
|
|
329
|
+
echo " • Unmerged work never deleted"
|
|
330
|
+
echo " • Network failures handled gracefully"
|
|
331
|
+
echo " • Uncommitted + behind detected safely"
|
|
332
|
+
exit 0
|
|
333
|
+
fi
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
main "$@"
|