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.
Files changed (112) hide show
  1. package/README.md +77 -7
  2. package/clawdbot-plugin/clawdbot.plugin.json +20 -0
  3. package/clawdbot-plugin/index.js +555 -0
  4. package/clawdbot-plugin/index.ts +582 -0
  5. package/clawdbot-skill/SKILL.md +33 -336
  6. package/clawdbot-skill/index.ts +491 -321
  7. package/clawdbot-skill/skill.json +4 -13
  8. package/dist/commands/clawdbot.d.ts +11 -0
  9. package/dist/commands/clawdbot.d.ts.map +1 -0
  10. package/dist/commands/clawdbot.js +215 -0
  11. package/dist/commands/clawdbot.js.map +1 -0
  12. package/dist/commands/gtm-process-update.d.ts +10 -0
  13. package/dist/commands/gtm-process-update.d.ts.map +1 -0
  14. package/dist/commands/gtm-process-update.js +101 -0
  15. package/dist/commands/gtm-process-update.js.map +1 -0
  16. package/dist/commands/onboard.d.ts.map +1 -1
  17. package/dist/commands/onboard.js +203 -15
  18. package/dist/commands/onboard.js.map +1 -1
  19. package/dist/commands/openclaw.d.ts +56 -0
  20. package/dist/commands/openclaw.d.ts.map +1 -0
  21. package/dist/commands/openclaw.js +700 -0
  22. package/dist/commands/openclaw.js.map +1 -0
  23. package/dist/commands/service-validate.d.ts +12 -0
  24. package/dist/commands/service-validate.d.ts.map +1 -0
  25. package/dist/commands/service-validate.js +611 -0
  26. package/dist/commands/service-validate.js.map +1 -0
  27. package/dist/commands/services-create.d.ts +15 -0
  28. package/dist/commands/services-create.d.ts.map +1 -0
  29. package/dist/commands/services-create.js +1452 -0
  30. package/dist/commands/services-create.js.map +1 -0
  31. package/dist/commands/services-sync-agents.d.ts +23 -0
  32. package/dist/commands/services-sync-agents.d.ts.map +1 -0
  33. package/dist/commands/services-sync-agents.js +207 -0
  34. package/dist/commands/services-sync-agents.js.map +1 -0
  35. package/dist/commands/services.d.ts +7 -1
  36. package/dist/commands/services.d.ts.map +1 -1
  37. package/dist/commands/services.js +347 -22
  38. package/dist/commands/services.js.map +1 -1
  39. package/dist/commands/update.js +0 -0
  40. package/dist/commands/validate-settings.d.ts +37 -0
  41. package/dist/commands/validate-settings.d.ts.map +1 -0
  42. package/dist/commands/validate-settings.js +197 -0
  43. package/dist/commands/validate-settings.js.map +1 -0
  44. package/dist/index.js +155 -60
  45. package/dist/index.js.map +1 -1
  46. package/dist/lib/agent-generator.d.ts.map +1 -1
  47. package/dist/lib/agent-generator.js +94 -1
  48. package/dist/lib/agent-generator.js.map +1 -1
  49. package/dist/lib/openclaw-registry.d.ts +48 -0
  50. package/dist/lib/openclaw-registry.d.ts.map +1 -0
  51. package/dist/lib/openclaw-registry.js +181 -0
  52. package/dist/lib/openclaw-registry.js.map +1 -0
  53. package/dist/lib/openclaw-sdk.d.ts +107 -0
  54. package/dist/lib/openclaw-sdk.d.ts.map +1 -0
  55. package/dist/lib/openclaw-sdk.js +208 -0
  56. package/dist/lib/openclaw-sdk.js.map +1 -0
  57. package/dist/lib/peer-agent-generator.d.ts +44 -0
  58. package/dist/lib/peer-agent-generator.d.ts.map +1 -0
  59. package/dist/lib/peer-agent-generator.js +286 -0
  60. package/dist/lib/peer-agent-generator.js.map +1 -0
  61. package/dist/lib/service-detector.d.ts +1 -1
  62. package/dist/lib/service-detector.d.ts.map +1 -1
  63. package/dist/lib/service-detector.js +118 -5
  64. package/dist/lib/service-detector.js.map +1 -1
  65. package/dist/lib/service-gtm.d.ts +157 -0
  66. package/dist/lib/service-gtm.d.ts.map +1 -0
  67. package/dist/lib/service-gtm.js +786 -0
  68. package/dist/lib/service-gtm.js.map +1 -0
  69. package/dist/lib/service-mcp-base.d.ts +10 -1
  70. package/dist/lib/service-mcp-base.d.ts.map +1 -1
  71. package/dist/lib/service-mcp-base.js +20 -1
  72. package/dist/lib/service-mcp-base.js.map +1 -1
  73. package/dist/mcp/service-peer-mcp.d.ts +36 -0
  74. package/dist/mcp/service-peer-mcp.d.ts.map +1 -0
  75. package/dist/mcp/service-peer-mcp.js +220 -0
  76. package/dist/mcp/service-peer-mcp.js.map +1 -0
  77. package/dist/mcp/service-registry-mcp.js +0 -0
  78. package/dist/utils/settings-validator.d.ts +4 -1
  79. package/dist/utils/settings-validator.d.ts.map +1 -1
  80. package/dist/utils/settings-validator.js +25 -1
  81. package/dist/utils/settings-validator.js.map +1 -1
  82. package/package.json +2 -1
  83. package/template/.claude/service-settings.json +32 -0
  84. package/template/.claude/settings.json +10 -0
  85. package/template/.claude/skills/end/SKILL.md +1780 -0
  86. package/template/.jfl/config.json +2 -1
  87. package/template/.mcp.json +1 -7
  88. package/template/CLAUDE.md +1042 -248
  89. package/template/CLAUDE.md.bak +1187 -0
  90. package/template/scripts/commit-gtm.sh +56 -0
  91. package/template/scripts/commit-product.sh +68 -0
  92. package/template/scripts/migrate-to-branch-sessions.sh +201 -0
  93. package/template/scripts/session/auto-commit.sh +4 -3
  94. package/template/scripts/session/jfl-doctor.sh +222 -83
  95. package/template/scripts/session/session-cleanup.sh +109 -21
  96. package/template/scripts/session/session-end.sh +26 -13
  97. package/template/scripts/session/session-init.sh +280 -98
  98. package/template/scripts/session/test-critical-infrastructure.sh +293 -0
  99. package/template/scripts/session/test-experience-level.sh +336 -0
  100. package/template/scripts/session/test-session-cleanup.sh +268 -0
  101. package/template/scripts/session/test-session-sync.sh +320 -0
  102. package/template/scripts/where-am-i.sh +78 -0
  103. package/template/templates/service-agent/.claude/settings.json +32 -0
  104. package/template/templates/service-agent/CLAUDE.md +334 -0
  105. package/template/templates/service-agent/knowledge/ARCHITECTURE.md +115 -0
  106. package/template/templates/service-agent/knowledge/DEPLOYMENT.md +199 -0
  107. package/template/templates/service-agent/knowledge/RUNBOOK.md +412 -0
  108. package/template/templates/service-agent/knowledge/SERVICE_SPEC.md +77 -0
  109. package/dist/commands/session-mgmt.d.ts +0 -33
  110. package/dist/commands/session-mgmt.d.ts.map +0 -1
  111. package/dist/commands/session-mgmt.js +0 -404
  112. 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 "$@"