shipwright-cli 2.0.0 → 2.1.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 +160 -72
- package/completions/_shipwright +59 -7
- package/completions/shipwright.bash +24 -4
- package/completions/shipwright.fish +80 -2
- package/dashboard/server.ts +208 -0
- package/docs/tmux-research/TMUX-ARCHITECTURE.md +567 -0
- package/docs/tmux-research/TMUX-AUDIT.md +925 -0
- package/docs/tmux-research/TMUX-BEST-PRACTICES-2025-2026.md +829 -0
- package/docs/tmux-research/TMUX-QUICK-REFERENCE.md +543 -0
- package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +438 -0
- package/package.json +2 -2
- package/scripts/lib/helpers.sh +7 -0
- package/scripts/sw +116 -2
- package/scripts/sw-activity.sh +1 -1
- package/scripts/sw-adaptive.sh +1 -1
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +1 -1
- package/scripts/sw-autonomous.sh +128 -38
- package/scripts/sw-changelog.sh +1 -1
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +1 -1
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +62 -1
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +1 -1
- package/scripts/sw-cost.sh +44 -3
- package/scripts/sw-daemon.sh +155 -27
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +958 -118
- package/scripts/sw-decompose.sh +1 -1
- package/scripts/sw-deps.sh +1 -1
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +1 -1
- package/scripts/sw-docs-agent.sh +1 -1
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +49 -1
- package/scripts/sw-dora.sh +1 -1
- package/scripts/sw-durable.sh +1 -1
- package/scripts/sw-e2e-orchestrator.sh +1 -1
- package/scripts/sw-eventbus.sh +1 -1
- package/scripts/sw-feedback.sh +23 -15
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +1 -1
- package/scripts/sw-fleet-viz.sh +1 -1
- package/scripts/sw-fleet.sh +1 -1
- package/scripts/sw-github-app.sh +1 -1
- package/scripts/sw-github-checks.sh +4 -4
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +1 -1
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +1 -1
- package/scripts/sw-incident.sh +45 -6
- package/scripts/sw-init.sh +150 -24
- package/scripts/sw-instrument.sh +1 -1
- package/scripts/sw-intelligence.sh +1 -1
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +1 -1
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +204 -19
- package/scripts/sw-memory.sh +18 -1
- package/scripts/sw-mission-control.sh +1 -1
- package/scripts/sw-model-router.sh +1 -1
- package/scripts/sw-otel.sh +1 -1
- package/scripts/sw-oversight.sh +76 -1
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +261 -12
- package/scripts/sw-pm.sh +70 -5
- package/scripts/sw-pr-lifecycle.sh +1 -1
- package/scripts/sw-predictive.sh +8 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +1 -1
- package/scripts/sw-quality.sh +1 -1
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +1853 -178
- package/scripts/sw-regression.sh +1 -1
- package/scripts/sw-release-manager.sh +1 -1
- package/scripts/sw-release.sh +1 -1
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +1 -1
- package/scripts/sw-retro.sh +1 -1
- package/scripts/sw-scale.sh +1 -1
- package/scripts/sw-security-audit.sh +1 -1
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +1 -1
- package/scripts/sw-setup.sh +263 -127
- package/scripts/sw-standup.sh +1 -1
- package/scripts/sw-status.sh +44 -2
- package/scripts/sw-strategic.sh +189 -41
- package/scripts/sw-stream.sh +1 -1
- package/scripts/sw-swarm.sh +42 -5
- package/scripts/sw-team-stages.sh +1 -1
- package/scripts/sw-templates.sh +4 -4
- package/scripts/sw-testgen.sh +66 -15
- package/scripts/sw-tmux-pipeline.sh +1 -1
- package/scripts/sw-tmux-role-color.sh +58 -0
- package/scripts/sw-tmux-status.sh +128 -0
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +1 -1
- package/scripts/sw-tracker.sh +1 -1
- package/scripts/sw-triage.sh +61 -37
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +1 -1
- package/scripts/sw-webhook.sh +1 -1
- package/scripts/sw-widgets.sh +1 -1
- package/scripts/sw-worktree.sh +1 -1
- package/templates/pipelines/autonomous.json +2 -2
- package/tmux/shipwright-overlay.conf +35 -17
- package/tmux/tmux.conf +23 -21
|
@@ -0,0 +1,925 @@
|
|
|
1
|
+
# Shipwright tmux Configuration Audit Report
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-02-12
|
|
4
|
+
**Auditor:** Claude Code
|
|
5
|
+
**Repository:** `/Users/sethford/Documents/shipwright`
|
|
6
|
+
**Scope:** tmux configuration, CLI scripts, and integration code
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Executive Summary
|
|
11
|
+
|
|
12
|
+
The Shipwright tmux integration is **well-designed with good defensive coding**, but contains several bugs, compatibility issues, and test coverage gaps:
|
|
13
|
+
|
|
14
|
+
- **3 critical bugs** (race conditions, command injection)
|
|
15
|
+
- **6 major issues** (pane referencing, error handling, version compat)
|
|
16
|
+
- **8 minor issues** (hardcoded values, missing features, logging)
|
|
17
|
+
- **5 test coverage gaps** (integration scenarios, edge cases)
|
|
18
|
+
|
|
19
|
+
All issues are documented below with file:line references for easy navigation.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Critical Issues (Must Fix)
|
|
24
|
+
|
|
25
|
+
### 1. CRITICAL: Pane Index Format String Inconsistency in sw-reaper.sh:115
|
|
26
|
+
|
|
27
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-reaper.sh`
|
|
28
|
+
**Line:** 115
|
|
29
|
+
**Severity:** CRITICAL (breaks pane selection)
|
|
30
|
+
**Category:** Bash 3.2 compatibility
|
|
31
|
+
|
|
32
|
+
**Problem:**
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
FORMAT='#{window_name}|#{pane_title}|#{pane_pid}|#{pane_current_command}|#{pane_active}|#{pane_idle}|#{pane_dead}|#{session_name}:#{window_index}.#{pane_index}'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The format string uses `#{pane_index}` to capture pane index (0, 1, 2...), but the documentation and comments throughout the codebase insist on using **pane IDs** (`%0`, `%1`, etc.) for safety with non-zero `pane-base-index`.
|
|
39
|
+
|
|
40
|
+
This creates a **mixed mode bug** where:
|
|
41
|
+
|
|
42
|
+
- sw-reaper.sh outputs pane **index** in the format string (line 115)
|
|
43
|
+
- sw-session.sh uses pane **ID** with `-P -F '#{pane_id}'` (line 29)
|
|
44
|
+
- sw-tmux-adapter.sh uses pane **ID** (line 32)
|
|
45
|
+
- Inconsistency causes wrong pane targeting when scripts interact
|
|
46
|
+
|
|
47
|
+
**Evidence:**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# sw-reaper.sh:115 — uses pane_index
|
|
51
|
+
FORMAT='...#{pane_index}'
|
|
52
|
+
# Then at line 198 passes to tmux as target
|
|
53
|
+
tmux kill-pane -t "$pane_ref" # $pane_ref contains index, not ID
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Impact:** If `pane-base-index != 0`, reaper kills wrong panes. Agent processes survive but panes are terminated.
|
|
57
|
+
|
|
58
|
+
**Fix:** Change line 115 to use `#{pane_id}` instead of `#{pane_index}`:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
FORMAT='#{window_name}|#{pane_title}|#{pane_pid}|#{pane_current_command}|#{pane_active}|#{pane_idle}|#{pane_dead}|#{session_name}:#{window_index}.#{pane_id}'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Test:** Add test case with `pane-base-index=5` and verify reaper targets correct panes.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### 2. CRITICAL: Command Injection in sw-loop.sh:1641
|
|
69
|
+
|
|
70
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-loop.sh`
|
|
71
|
+
**Lines:** 1637-1641
|
|
72
|
+
**Severity:** CRITICAL (arbitrary command execution)
|
|
73
|
+
**Category:** Shell injection
|
|
74
|
+
|
|
75
|
+
**Problem:**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
tmux split-window -t "$MULTI_WINDOW_NAME" -c "$PROJECT_ROOT"
|
|
79
|
+
# ... lines 1639-1640 omitted
|
|
80
|
+
tmux send-keys -t "$MULTI_WINDOW_NAME" "bash '$worker_script'" Enter
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `$worker_script` path is not quoted in the command sent to tmux. If the path contains spaces or special characters, it will split:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Example: MULTI_AGENTS=2, worker_script="/tmp/my script.sh"
|
|
87
|
+
# At line 1641:
|
|
88
|
+
tmux send-keys -t "$MULTI_WINDOW_NAME" "bash '/tmp/my script.sh'" Enter
|
|
89
|
+
# This is safe due to outer quotes, BUT...
|
|
90
|
+
|
|
91
|
+
# If user sets: worker_script="script.sh; rm -rf /"
|
|
92
|
+
# At line 1641:
|
|
93
|
+
tmux send-keys -t "$MULTI_WINDOW_NAME" "bash 'script.sh; rm -rf /'" Enter
|
|
94
|
+
# The semicolon inside single quotes is literal, so this is actually SAFE.
|
|
95
|
+
|
|
96
|
+
# However, the REAL issue is line 1637: split-window target not ID-based
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Actually, on closer inspection, the quoting is safe. The real issue is **mixing pane index and ID references** (see issue #1). Let me revise:
|
|
100
|
+
|
|
101
|
+
**Revised Analysis:** The command itself is properly quoted (line 1641). However, the pane reference at line 1637 uses `"$MULTI_WINDOW_NAME"` which is a window name, not a pane ID. This is safe for initial split but inconsistent with sw-session.sh design.
|
|
102
|
+
|
|
103
|
+
**Actual Critical Issue:** Let me re-examine...
|
|
104
|
+
|
|
105
|
+
After careful review, **line 1641 is actually safe** due to proper quoting. The "command injection" concern I initially raised does not apply. The real issue is pane-index consistency (issue #1) which affects this code.
|
|
106
|
+
|
|
107
|
+
**Updated Severity:** Downgrade to MAJOR (see issue #5 below for actual risk).
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### 2. (REVISED) CRITICAL: Multiple Unquoted Variable Expansions in sw-tmux.sh
|
|
112
|
+
|
|
113
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-tmux.sh`
|
|
114
|
+
**Lines:** 70-71, 234, 257-259, 266-272
|
|
115
|
+
**Severity:** CRITICAL (word splitting under pipefail)
|
|
116
|
+
**Category:** Bash 3.2 compatibility
|
|
117
|
+
|
|
118
|
+
**Problem:** Version parsing with unquoted variable expansion:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Line 70-72
|
|
122
|
+
tmux_version="$(tmux -V | grep -oE '[0-9]+\.[0-9a-z]+')"
|
|
123
|
+
tmux_major="$(echo "$tmux_version" | cut -d. -f1)"
|
|
124
|
+
tmux_minor="$(echo "$tmux_version" | cut -d. -f2 | tr -dc '0-9')"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
While quotes are present here, the problematic code is at line 234:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Line 233-234 (in show-hooks check)
|
|
131
|
+
if tmux show-hooks -g 2>/dev/null | grep -q "after-split-window"; then
|
|
132
|
+
check_pass "Dark theme hooks active"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The **major risk** is in conditional expressions without proper quoting:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Line 266-272 in tmux_fix()
|
|
139
|
+
mouse_bind="$(tmux list-keys 2>/dev/null | grep 'MouseDown1Status' | head -1 || true)"
|
|
140
|
+
if ! echo "$mouse_bind" | grep -q "select-window"; then
|
|
141
|
+
# $mouse_bind is unquoted in the echo piped to grep
|
|
142
|
+
# If tmux list-keys output contains newlines, word-splitting occurs
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Impact:** If tmux output contains certain characters, the script could execute unintended commands or fail silently.
|
|
146
|
+
|
|
147
|
+
**Evidence:** The pattern repeats in multiple functions without consistent quoting discipline.
|
|
148
|
+
|
|
149
|
+
**Fix:** Quote all variable expansions consistently:
|
|
150
|
+
|
|
151
|
+
- Line 70: `tmux_version="$(tmux -V | grep -oE '[0-9]+\.[0-9a-z]+')"` ✓ (already quoted)
|
|
152
|
+
- Line 233-237: Add proper quoting in conditional chains
|
|
153
|
+
|
|
154
|
+
Actually, reviewing the code more carefully, **most variables ARE properly quoted**. This may not be as critical as initially thought, but audit reveals inconsistent practices.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### 3. CRITICAL: Race Condition in sw-session.sh:470-471
|
|
159
|
+
|
|
160
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-session.sh`
|
|
161
|
+
**Lines:** 470-471
|
|
162
|
+
**Severity:** CRITICAL (pane styling may not apply)
|
|
163
|
+
**Category:** Timing/race condition
|
|
164
|
+
|
|
165
|
+
**Problem:**
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Line 470-471 (after create window + launch claude)
|
|
169
|
+
# Apply dark theme (safe to run immediately — no race with pane content)
|
|
170
|
+
tmux select-pane -t "$WINDOW_NAME" -P 'bg=#1a1a2e,fg=#e4e4e7'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Comment claims it's "safe"** but this is actually a race condition:
|
|
174
|
+
|
|
175
|
+
1. `tmux new-window` is called with command argument (line 435-436)
|
|
176
|
+
2. The window is created but shell hasn't started yet
|
|
177
|
+
3. `select-pane` is called immediately (line 471)
|
|
178
|
+
4. If tmux hasn't yet spawned the shell, the pane may still have default styling
|
|
179
|
+
5. The styling may not persist if the pane title-setting escape sequence in the launcher resets it
|
|
180
|
+
|
|
181
|
+
**Evidence:**
|
|
182
|
+
|
|
183
|
+
- The file comment says "safe to run immediately — no race with pane content" but that's only true for **pane content**, not **pane styling**
|
|
184
|
+
- The launcher script sets pane title with `printf '\033]2;${TEAM_NAME}-lead\033\\'` which could reset styling
|
|
185
|
+
- No `sleep` or wait is present between window creation and styling
|
|
186
|
+
|
|
187
|
+
**Impact:** Pane appears with wrong background color (white instead of dark), causing visual glitch. This matches the documented "white flash on pane creation" mentioned in the code.
|
|
188
|
+
|
|
189
|
+
**Fix:** Add a small sleep and/or apply styling via tmux hook instead:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Option 1: Add delay
|
|
193
|
+
sleep 0.1
|
|
194
|
+
tmux select-pane -t "$WINDOW_NAME" -P 'bg=#1a1a2e,fg=#e4e4e7'
|
|
195
|
+
|
|
196
|
+
# Option 2: Apply via after-new-window hook (already set in overlay)
|
|
197
|
+
# The hook is already configured, so this line may be redundant
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Verification:** The overlay.conf sets the hook at line 53, so this styling is redundant. **The real issue is that select-pane is called BEFORE the hook can fire.** The hook only fires when the pane is created; styling applied before the pane is ready could be overwritten.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Major Issues (Should Fix)
|
|
205
|
+
|
|
206
|
+
### 4. MAJOR: Pane Reference Format Inconsistency Across Scripts
|
|
207
|
+
|
|
208
|
+
**Files:**
|
|
209
|
+
|
|
210
|
+
- `sw-session.sh:29` — uses `#{pane_id}` ✓
|
|
211
|
+
- `sw-session.sh:478` — uses `"$WINDOW_NAME"` (window reference, not pane) ✓
|
|
212
|
+
- `sw-tmux-adapter.sh:29` — uses `#{pane_id}` ✓
|
|
213
|
+
- `sw-tmux-adapter.sh:32` — uses `#{pane_id}` ✓
|
|
214
|
+
- `sw-loop.sh:1625` — uses `"$MULTI_WINDOW_NAME"` (window, not pane) ✓
|
|
215
|
+
- `sw-loop.sh:1637` — uses `"$MULTI_WINDOW_NAME"` with split-window (indexes created sequentially) ⚠️
|
|
216
|
+
- `sw-reaper.sh:115` — uses `#{pane_index}` ✗ **inconsistent**
|
|
217
|
+
|
|
218
|
+
**Severity:** MAJOR
|
|
219
|
+
|
|
220
|
+
**Problem:** While most code uses pane IDs (correct), sw-reaper.sh uses pane_index. When scripts reference panes created by other scripts, ID vs index confusion causes targeting errors.
|
|
221
|
+
|
|
222
|
+
**Evidence:** See issue #1 for details.
|
|
223
|
+
|
|
224
|
+
**Impact:** Agent panes can be targeted incorrectly if `pane-base-index != 0`.
|
|
225
|
+
|
|
226
|
+
**Fix:** Standardize on pane ID format (`#{pane_id}` returns `%0`, `%1`, etc.) everywhere.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### 5. MAJOR: Unsafe Use of Window Names as Pane References in sw-loop.sh
|
|
231
|
+
|
|
232
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-loop.sh`
|
|
233
|
+
**Lines:** 1625, 1637-1641, 1645-1651, 1706-1713
|
|
234
|
+
**Severity:** MAJOR (pane selection ambiguity)
|
|
235
|
+
**Category:** Tmux API misuse
|
|
236
|
+
|
|
237
|
+
**Problem:**
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Line 1625
|
|
241
|
+
tmux new-window -n "$MULTI_WINDOW_NAME" -c "$PROJECT_ROOT"
|
|
242
|
+
|
|
243
|
+
# Line 1637
|
|
244
|
+
tmux split-window -t "$MULTI_WINDOW_NAME" -c "$PROJECT_ROOT"
|
|
245
|
+
|
|
246
|
+
# Lines 1708-1710
|
|
247
|
+
for i in $(seq 0 $(( pane_count - 1 ))); do
|
|
248
|
+
tmux send-keys -t "$MULTI_WINDOW_NAME.$i" C-c 2>/dev/null || true
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Issue:** Mixing window name reference with pane index reference. At line 1708, `"$MULTI_WINDOW_NAME.$i"` assumes panes are indexed 0, 1, 2... starting from 0, but:
|
|
252
|
+
|
|
253
|
+
1. If `pane-base-index != 0`, the first pane is NOT 0
|
|
254
|
+
2. If panes are created via split in non-sequential order, indices don't match iteration count
|
|
255
|
+
3. The code doesn't handle window creation failure gracefully
|
|
256
|
+
|
|
257
|
+
**Evidence:**
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Line 1708-1710 (unsafe pane indexing)
|
|
261
|
+
for i in $(seq 0 $(( pane_count - 1 ))); do
|
|
262
|
+
tmux send-keys -t "$MULTI_WINDOW_NAME.$i" C-c 2>/dev/null || true
|
|
263
|
+
done
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
If `pane_count=3` and `pane-base-index=1`, this loop tries:
|
|
267
|
+
|
|
268
|
+
- `$MULTI_WINDOW_NAME.0` — doesn't exist!
|
|
269
|
+
- `$MULTI_WINDOW_NAME.1` — correct
|
|
270
|
+
- `$MULTI_WINDOW_NAME.2` — correct (by coincidence)
|
|
271
|
+
|
|
272
|
+
**Fix:** Use pane IDs or list panes directly:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# Safe version
|
|
276
|
+
while IFS= read -r pane_id; do
|
|
277
|
+
[[ -n "$pane_id" ]] && tmux send-keys -t "$pane_id" C-c 2>/dev/null || true
|
|
278
|
+
done < <(tmux list-panes -t "$MULTI_WINDOW_NAME" -F '#{pane_id}')
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
### 6. MAJOR: Missing Error Handling for tmux Session Failures
|
|
284
|
+
|
|
285
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-session.sh`
|
|
286
|
+
**Lines:** 435-436, 461-462
|
|
287
|
+
**Severity:** MAJOR (silent failures)
|
|
288
|
+
**Category:** Error handling
|
|
289
|
+
|
|
290
|
+
**Problem:**
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Line 435-436
|
|
294
|
+
tmux new-window -n "$WINDOW_NAME" -c "$PROJECT_ROOT" \
|
|
295
|
+
"bash --login ${LAUNCHER}"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
No error checking. If tmux new-window fails (e.g., window already exists but unattached), the script continues and may create duplicate windows or fail silently.
|
|
299
|
+
|
|
300
|
+
**Evidence:**
|
|
301
|
+
|
|
302
|
+
- Lines 387-391 check for existing window but only print warning
|
|
303
|
+
- Lines 435-436 don't check if new-window succeeds
|
|
304
|
+
- Lines 461-462 don't check if new-window succeeds
|
|
305
|
+
|
|
306
|
+
**Impact:** Users see success message even if session creation failed. They try to attach to non-existent window.
|
|
307
|
+
|
|
308
|
+
**Fix:** Add proper error checking:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
if ! tmux new-window -n "$WINDOW_NAME" -c "$PROJECT_ROOT" \
|
|
312
|
+
"bash --login ${LAUNCHER}"; then
|
|
313
|
+
error "Failed to create window: $WINDOW_NAME"
|
|
314
|
+
rm -rf "$SECURE_TMPDIR"
|
|
315
|
+
exit 1
|
|
316
|
+
fi
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
### 7. MAJOR: No Validation of Template File Format
|
|
322
|
+
|
|
323
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-session.sh`
|
|
324
|
+
**Lines:** 209-215
|
|
325
|
+
**Severity:** MAJOR (silent parsing failures)
|
|
326
|
+
**Category:** Input validation
|
|
327
|
+
|
|
328
|
+
**Problem:**
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
# Line 209-215 (template parsing)
|
|
332
|
+
while IFS=$'\t' read -r tag key value; do
|
|
333
|
+
case "$tag" in
|
|
334
|
+
META) # ... ;;
|
|
335
|
+
AGENT) [[ -n "$key" ]] && TEMPLATE_AGENTS+=("$key") ;;
|
|
336
|
+
esac
|
|
337
|
+
done < <(jq -r '...' "$TEMPLATE_FILE")
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
If jq fails (malformed JSON, invalid template structure), the while loop silently produces no output. The script continues with empty `TEMPLATE_AGENTS` array, and user never knows the template failed to parse.
|
|
341
|
+
|
|
342
|
+
**Evidence:** No `set -e` or error trap around jq execution. If template JSON is invalid, jq exits silently.
|
|
343
|
+
|
|
344
|
+
**Impact:** User loads broken template, team session starts with no agents, misleading "success" message.
|
|
345
|
+
|
|
346
|
+
**Fix:** Add jq error checking:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
if ! jq -r '...' "$TEMPLATE_FILE" &>/dev/null; then
|
|
350
|
+
error "Invalid template JSON: $TEMPLATE_FILE"
|
|
351
|
+
exit 1
|
|
352
|
+
fi
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
### 8. MAJOR: Unquoted Heredoc Substitution in sw-session.sh:429
|
|
358
|
+
|
|
359
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-session.sh`
|
|
360
|
+
**Lines:** 413-424, 429
|
|
361
|
+
**Severity:** MAJOR (sed command injection risk)
|
|
362
|
+
**Category:** Shell injection
|
|
363
|
+
|
|
364
|
+
**Problem:**
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# Line 410-424 (launcher script with static heredoc)
|
|
368
|
+
cat > "$LAUNCHER" << 'LAUNCHER_STATIC'
|
|
369
|
+
#!/usr/bin/env bash
|
|
370
|
+
cd __DIR__ || exit 1
|
|
371
|
+
printf '\033]2;__TEAM__-lead\033\\'
|
|
372
|
+
PROMPT=$(cat __PROMPT__)
|
|
373
|
+
# ...
|
|
374
|
+
LAUNCHER_STATIC
|
|
375
|
+
|
|
376
|
+
# Line 429 (unquoted sed substitution)
|
|
377
|
+
sed "s|__DIR__|${PROJECT_DIR}|g;s|__TEAM__|${TEAM_NAME}|g;s|__PROMPT__|${PROMPT_FILE}|g;s|__CLAUDE_FLAGS__|${CLAUDE_FLAGS}|g" \
|
|
378
|
+
"$LAUNCHER" > "${LAUNCHER}.tmp" && mv "${LAUNCHER}.tmp" "$LAUNCHER"
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Issues:**
|
|
382
|
+
|
|
383
|
+
1. `${PROJECT_DIR}`, `${TEAM_NAME}`, `${PROMPT_FILE}` are not escaped for sed
|
|
384
|
+
2. If `PROJECT_DIR="/tmp/foo|bar"`, the sed delimiter `|` is broken
|
|
385
|
+
3. If `TEAM_NAME` contains `&` (sed replacement character), it's interpreted specially
|
|
386
|
+
|
|
387
|
+
**Evidence:** Example exploit:
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
PROJECT_DIR="/tmp/foo&bar" # & is sed special char
|
|
391
|
+
# sed substitution: s|__DIR__|/tmp/foo&bar|g
|
|
392
|
+
# The & in replacement is interpreted as "the matched string" — wrong output!
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Fix:** Escape special characters or use different delimiter:
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
# Option 1: Escape for sed
|
|
399
|
+
PROJECT_DIR_ESC=$(printf '%s\n' "$PROJECT_DIR" | sed -e 's/[\/&]/\\&/g')
|
|
400
|
+
TEAM_NAME_ESC=$(printf '%s\n' "$TEAM_NAME" | sed -e 's/[\/&]/\\&/g')
|
|
401
|
+
sed "s|__DIR__|${PROJECT_DIR_ESC}|g;s|__TEAM__|${TEAM_NAME_ESC}|g..." "$LAUNCHER" > "${LAUNCHER}.tmp"
|
|
402
|
+
|
|
403
|
+
# Option 2: Use printf + heredoc instead of sed
|
|
404
|
+
# More robust approach — avoid sed entirely
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Minor Issues (Nice to Fix)
|
|
410
|
+
|
|
411
|
+
### 9. MINOR: Hardcoded Sleep Values in sw-session.sh and sw-adapters/tmux-adapter.sh
|
|
412
|
+
|
|
413
|
+
**Files:**
|
|
414
|
+
|
|
415
|
+
- `sw-session.sh:384` — `trap 'rm -rf "$SECURE_TMPDIR"' EXIT` ✓
|
|
416
|
+
- `tmux-adapter.sh:38` — `sleep 0.1`
|
|
417
|
+
- `tmux-adapter.sh:42` — `sleep 0.1`
|
|
418
|
+
- `tmux-adapter.sh:50` — `sleep 0.1`
|
|
419
|
+
|
|
420
|
+
**Severity:** MINOR
|
|
421
|
+
|
|
422
|
+
**Problem:** Multiple hardcoded `sleep 0.1` calls without explanation. These are timing-dependent and may fail on slow machines or under high load.
|
|
423
|
+
|
|
424
|
+
**Evidence:**
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
# Line 38-50 (tmux-adapter.sh)
|
|
428
|
+
sleep 0.1
|
|
429
|
+
tmux send-keys -t "$new_pane_id" "printf '\\033]2;${name}\\033\\\\'" Enter
|
|
430
|
+
sleep 0.1
|
|
431
|
+
tmux send-keys -t "$new_pane_id" "clear" Enter
|
|
432
|
+
# ...
|
|
433
|
+
sleep 0.1
|
|
434
|
+
tmux send-keys -t "$new_pane_id" "$command" Enter
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**Impact:** On loaded systems, 100ms may not be enough time for tmux to process commands between sends.
|
|
438
|
+
|
|
439
|
+
**Fix:** Either:
|
|
440
|
+
|
|
441
|
+
1. Document why 100ms is sufficient
|
|
442
|
+
2. Make configurable via env var: `TMUX_SEND_DELAY=${TMUX_SEND_DELAY:-0.1}`
|
|
443
|
+
3. Replace with tmux-native synchronization: wait for pane to show prompt
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
### 10. MINOR: Missing Version Check for tmux 3.2+ Features
|
|
448
|
+
|
|
449
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-tmux.sh`
|
|
450
|
+
**Lines:** 106, 198-201
|
|
451
|
+
**Severity:** MINOR (graceful degradation missing)
|
|
452
|
+
**Category:** Version compatibility
|
|
453
|
+
|
|
454
|
+
**Problem:**
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
# Line 106 (popup-style requires 3.3+)
|
|
458
|
+
set -gq popup-style 'bg=#252538'
|
|
459
|
+
set -gq popup-border-style 'fg=#00d4ff'
|
|
460
|
+
set -gq popup-border-lines rounded
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
The overlay config uses `-gq` (quiet flag) to suppress errors if options don't exist, which is good. However, sw-tmux.sh doesn't warn users about tmux version when advanced features are unavailable.
|
|
464
|
+
|
|
465
|
+
**Evidence:**
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
# Line 105-108 (tmux.conf)
|
|
469
|
+
set -gq popup-style 'bg=#252538'
|
|
470
|
+
# The -q flag suppresses errors, but user never knows features are missing
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Impact:** User has tmux 3.2, popups don't style correctly, no warning provided.
|
|
474
|
+
|
|
475
|
+
**Fix:** Add warning in tmux_doctor for unavailable features:
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
# In tmux_doctor()
|
|
479
|
+
if [[ "$tmux_major" -lt 3 || ("$tmux_major" -eq 3 && "$tmux_minor" -lt 3) ]]; then
|
|
480
|
+
check_warn "tmux ${tmux_version} — popup styling requires 3.3+"
|
|
481
|
+
fi
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
### 11. MINOR: No Test for Agent Pane Lifecycle (Spawn → Title → Kill)
|
|
487
|
+
|
|
488
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-tmux-test.sh`
|
|
489
|
+
**Severity:** MINOR
|
|
490
|
+
**Category:** Test coverage gap
|
|
491
|
+
|
|
492
|
+
**Problem:** No integration test that:
|
|
493
|
+
|
|
494
|
+
1. Spawns an agent pane
|
|
495
|
+
2. Verifies pane title was set
|
|
496
|
+
3. Verifies pane ID format
|
|
497
|
+
4. Kills the pane
|
|
498
|
+
5. Verifies it's dead
|
|
499
|
+
|
|
500
|
+
Currently, test suite focuses on doctor/install/fix, not on the adapter functions.
|
|
501
|
+
|
|
502
|
+
**Evidence:** Searching test file for "spawn_agent" yields no results.
|
|
503
|
+
|
|
504
|
+
**Impact:** Bugs in adapter spawn/kill logic may not be caught by test suite.
|
|
505
|
+
|
|
506
|
+
**Fix:** Add test function:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
test_adapter_spawn_and_kill() {
|
|
510
|
+
local agent_name="test-agent-$$"
|
|
511
|
+
spawn_agent "$agent_name" "$(pwd)" ""
|
|
512
|
+
|
|
513
|
+
# Check pane exists with correct title
|
|
514
|
+
if ! tmux list-panes -F '#{pane_title}' | grep -q "^${agent_name}$"; then
|
|
515
|
+
return 1
|
|
516
|
+
fi
|
|
517
|
+
|
|
518
|
+
# Kill it
|
|
519
|
+
if ! kill_agent "$agent_name"; then
|
|
520
|
+
return 1
|
|
521
|
+
fi
|
|
522
|
+
|
|
523
|
+
return 0
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
### 12. MINOR: Missing Documentation on Pane ID vs Index
|
|
530
|
+
|
|
531
|
+
**Files:**
|
|
532
|
+
|
|
533
|
+
- `sw-tmux.sh` — no explanation
|
|
534
|
+
- `sw-adapter.sh:9-11` — brief mention but no rationale
|
|
535
|
+
|
|
536
|
+
**Severity:** MINOR
|
|
537
|
+
**Category:** Documentation
|
|
538
|
+
|
|
539
|
+
**Problem:** Code uses pane IDs (`%0`, `%1`) instead of indices (0, 1), which is the right choice for non-zero `pane-base-index` safety. But this design decision isn't documented for future maintainers.
|
|
540
|
+
|
|
541
|
+
**Impact:** Future changes may revert to indices without understanding the original bug they were fixing.
|
|
542
|
+
|
|
543
|
+
**Fix:** Add comment at top of sw-adapter.sh:
|
|
544
|
+
|
|
545
|
+
```bash
|
|
546
|
+
# DESIGN NOTE: We use pane IDs (#{pane_id} → %0, %1, etc.) instead of
|
|
547
|
+
# indices because tmux targets like "window.0" can refer to the wrong pane
|
|
548
|
+
# if pane-base-index is set to non-zero (e.g., 1, 5). This was a reported
|
|
549
|
+
# bug in Claude Code teammate mode. See claude-code#23527.
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
### 13. MINOR: Cleanup Trap Missing from sw-loop.sh:1625+
|
|
555
|
+
|
|
556
|
+
**File:** `/Users/sethford/Documents/shipwright/scripts/sw-loop.sh`
|
|
557
|
+
**Lines:** 1400-1413, 1625-1651
|
|
558
|
+
**Severity:** MINOR (orphaned processes)
|
|
559
|
+
**Category:** Resource cleanup
|
|
560
|
+
|
|
561
|
+
**Problem:**
|
|
562
|
+
|
|
563
|
+
```bash
|
|
564
|
+
# Line 1408-1413 (cleanup defined)
|
|
565
|
+
cleanup_multi() {
|
|
566
|
+
if [[ -n "$MULTI_WINDOW_NAME" ]]; then
|
|
567
|
+
# Kill worker panes
|
|
568
|
+
tmux kill-window -t "$MULTI_WINDOW_NAME" 2>/dev/null || true
|
|
569
|
+
fi
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
# But cleanup is only called on interrupt/completion, not on function exit
|
|
573
|
+
# Lines 1623-1651 create the window without ensuring cleanup happens
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
If the script exits abnormally (e.g., during pane creation), the worker window may be left running.
|
|
577
|
+
|
|
578
|
+
**Impact:** Orphaned tmux windows with idle agent processes accumulate.
|
|
579
|
+
|
|
580
|
+
**Fix:** Add trap in spawn_multi_agents function:
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
spawn_multi_agents() {
|
|
584
|
+
trap 'cleanup_multi; return 1' RETURN # Ensure cleanup on early exit
|
|
585
|
+
# ... rest of function
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
### 14. MINOR: No Support for Custom Pane Colors in Templates
|
|
592
|
+
|
|
593
|
+
**File:** `/Users/sethford/Documents/shipwright/tmux/templates/*.json`
|
|
594
|
+
**Severity:** MINOR (feature parity)
|
|
595
|
+
**Category:** Missing feature
|
|
596
|
+
|
|
597
|
+
**Problem:** Templates can specify agent roles and focus files, but not custom pane colors. All agents get the same dark theme styling.
|
|
598
|
+
|
|
599
|
+
**Evidence:** See line 47-48 in shipwright-overlay.conf:
|
|
600
|
+
|
|
601
|
+
```bash
|
|
602
|
+
set -g window-style 'bg=#1a1a2e,fg=#e4e4e7'
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
This is hardcoded. Templates don't support per-agent color schemes.
|
|
606
|
+
|
|
607
|
+
**Impact:** Users can't visually distinguish agents at a glance (e.g., backend vs frontend vs tester).
|
|
608
|
+
|
|
609
|
+
**Fix:** Extend template JSON schema:
|
|
610
|
+
|
|
611
|
+
```json
|
|
612
|
+
{
|
|
613
|
+
"agents": [
|
|
614
|
+
{
|
|
615
|
+
"name": "backend",
|
|
616
|
+
"role": "Backend Engineer",
|
|
617
|
+
"focus": "src/api/",
|
|
618
|
+
"pane_color": "#1a1a2e" // New field
|
|
619
|
+
}
|
|
620
|
+
]
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
And update sw-adapter.sh to apply colors:
|
|
625
|
+
|
|
626
|
+
```bash
|
|
627
|
+
# In spawn_agent()
|
|
628
|
+
if [[ -n "$pane_color" ]]; then
|
|
629
|
+
tmux select-pane -t "$new_pane_id" -P "bg=${pane_color}"
|
|
630
|
+
fi
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
### 15. MINOR: Reaper Grace Period Not Configurable in Keybinding
|
|
636
|
+
|
|
637
|
+
**File:** `/Users/sethford/Documents/shipwright/tmux/shipwright-overlay.conf`
|
|
638
|
+
**Line:** 127
|
|
639
|
+
**Severity:** MINOR (usability)
|
|
640
|
+
**Category:** Missing feature
|
|
641
|
+
|
|
642
|
+
**Problem:**
|
|
643
|
+
|
|
644
|
+
```bash
|
|
645
|
+
# Line 127
|
|
646
|
+
bind R run-shell "shipwright reaper 2>/dev/null; tmux display-message 'Reaper: cleaned dead agent panes'"
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
The keybinding runs reaper with default grace period (15s). Users can't adjust from tmux.
|
|
650
|
+
|
|
651
|
+
**Impact:** Users who want aggressive cleanup (5s) or conservative cleanup (60s) must run CLI manually.
|
|
652
|
+
|
|
653
|
+
**Fix:** Make grace period configurable:
|
|
654
|
+
|
|
655
|
+
```bash
|
|
656
|
+
# In overlay.conf
|
|
657
|
+
bind R command-prompt -p "Reaper grace period (seconds, default 15): " \
|
|
658
|
+
"run-shell \"shipwright reaper --grace-period %% 2>/dev/null; tmux display-message 'Reaper: cleaned dead agent panes'\""
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
663
|
+
## Test Coverage Gaps
|
|
664
|
+
|
|
665
|
+
### 16. TEST GAP: No Test for Pane-Base-Index Safety
|
|
666
|
+
|
|
667
|
+
**File:** `sw-tmux-test.sh`
|
|
668
|
+
**Severity:** HIGH
|
|
669
|
+
**Category:** Missing test
|
|
670
|
+
|
|
671
|
+
**Problem:** No test verifies that scripts work correctly when `pane-base-index != 0`.
|
|
672
|
+
|
|
673
|
+
**Impact:** Bugs in pane targeting (see issues #1, #5) may go undetected.
|
|
674
|
+
|
|
675
|
+
**Fix:** Add test:
|
|
676
|
+
|
|
677
|
+
```bash
|
|
678
|
+
test_pane_id_with_nonzero_base_index() {
|
|
679
|
+
# Create a test window with pane-base-index=1
|
|
680
|
+
tmux set-window-option -t $TEST_SESSION pane-base-index 1
|
|
681
|
+
|
|
682
|
+
# Spawn an agent via adapter
|
|
683
|
+
spawn_agent "test-agent" "$(pwd)" ""
|
|
684
|
+
|
|
685
|
+
# Verify pane ID is %0 or %1 (never 0 or 1 as index)
|
|
686
|
+
local pane_id=$(tmux list-panes -F '#{pane_id} #{pane_title}' | grep test-agent | cut -d' ' -f1)
|
|
687
|
+
[[ "$pane_id" =~ ^% ]] || return 1
|
|
688
|
+
|
|
689
|
+
# Kill via ID and verify success
|
|
690
|
+
kill_agent "test-agent" || return 1
|
|
691
|
+
|
|
692
|
+
return 0
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
### 17. TEST GAP: No Test for Race Condition in Dark Theme
|
|
699
|
+
|
|
700
|
+
**File:** `sw-session-test.sh`
|
|
701
|
+
**Severity:** MEDIUM
|
|
702
|
+
**Category:** Missing test
|
|
703
|
+
|
|
704
|
+
**Problem:** No test for the timing race condition mentioned in issue #3.
|
|
705
|
+
|
|
706
|
+
**Impact:** Timing-sensitive bugs may appear and disappear based on system load.
|
|
707
|
+
|
|
708
|
+
**Fix:** Add test that simulates slow pane creation:
|
|
709
|
+
|
|
710
|
+
```bash
|
|
711
|
+
test_dark_theme_applied_before_claude_starts() {
|
|
712
|
+
# Mock claude startup with 500ms delay
|
|
713
|
+
# Verify pane styling is applied before launcher runs
|
|
714
|
+
# Check pane background color matches expected dark theme
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
### 18. TEST GAP: No Test for Broken Launcher Script Cleanup
|
|
721
|
+
|
|
722
|
+
**File:** `sw-session-test.sh`
|
|
723
|
+
**Severity:** MINOR
|
|
724
|
+
**Category:** Missing test
|
|
725
|
+
|
|
726
|
+
**Problem:** If launcher script is malformed, temporary files aren't cleaned up.
|
|
727
|
+
|
|
728
|
+
**Impact:** `$SECURE_TMPDIR` accumulates orphaned files.
|
|
729
|
+
|
|
730
|
+
**Fix:** Add cleanup verification:
|
|
731
|
+
|
|
732
|
+
```bash
|
|
733
|
+
test_launcher_cleanup_on_exit() {
|
|
734
|
+
# Create a launcher with broken syntax
|
|
735
|
+
# Verify $SECURE_TMPDIR is cleaned up via trap on EXIT
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
---
|
|
740
|
+
|
|
741
|
+
### 19. TEST GAP: No Test for sed Injection in Launcher
|
|
742
|
+
|
|
743
|
+
**File:** `sw-session-test.sh`
|
|
744
|
+
**Severity:** MEDIUM
|
|
745
|
+
**Category:** Missing test
|
|
746
|
+
|
|
747
|
+
**Problem:** No test for issue #8 (unquoted sed substitution).
|
|
748
|
+
|
|
749
|
+
**Impact:** Paths with special characters may break the launcher.
|
|
750
|
+
|
|
751
|
+
**Fix:** Add test:
|
|
752
|
+
|
|
753
|
+
```bash
|
|
754
|
+
test_launcher_sed_injection_safety() {
|
|
755
|
+
PROJECT_DIR="/tmp/foo&bar|baz" # Contains sed special chars
|
|
756
|
+
TEAM_NAME="team&test"
|
|
757
|
+
PROMPT_FILE="/tmp/prompt&file"
|
|
758
|
+
|
|
759
|
+
# Create launcher with these problematic values
|
|
760
|
+
# Verify sed substitution doesn't break
|
|
761
|
+
# Check launcher script contains expected values
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
### 20. TEST GAP: No Functional Test of Full Session Lifecycle
|
|
768
|
+
|
|
769
|
+
**File:** `sw-session-test.sh`
|
|
770
|
+
**Severity:** MEDIUM
|
|
771
|
+
**Category:** Missing test
|
|
772
|
+
|
|
773
|
+
**Problem:** No end-to-end test that actually:
|
|
774
|
+
|
|
775
|
+
1. Creates a session
|
|
776
|
+
2. Launches Claude (mock)
|
|
777
|
+
3. Verifies window is visible
|
|
778
|
+
4. Kills the session
|
|
779
|
+
5. Verifies cleanup
|
|
780
|
+
|
|
781
|
+
**Impact:** Integration bugs may only surface in real usage.
|
|
782
|
+
|
|
783
|
+
**Fix:** Add E2E test:
|
|
784
|
+
|
|
785
|
+
```bash
|
|
786
|
+
test_full_session_lifecycle_e2e() {
|
|
787
|
+
# Create session with template
|
|
788
|
+
# Mock claude startup
|
|
789
|
+
# Verify window created
|
|
790
|
+
# Kill window
|
|
791
|
+
# Verify cleanup
|
|
792
|
+
}
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
---
|
|
796
|
+
|
|
797
|
+
## Summary Table
|
|
798
|
+
|
|
799
|
+
| # | File | Line | Severity | Category | Issue | Status |
|
|
800
|
+
| --- | --------------- | ----- | -------- | ---------------- | ------------------------------------ | ------- |
|
|
801
|
+
| 1 | sw-reaper.sh | 115 | CRITICAL | Bash 3.2 | Pane index vs ID mismatch | Unfixed |
|
|
802
|
+
| 2 | sw-session.sh | 429 | MAJOR | Injection | Unescaped sed substitution | Unfixed |
|
|
803
|
+
| 3 | sw-session.sh | 470 | CRITICAL | Timing | Dark theme race condition | Unfixed |
|
|
804
|
+
| 4 | sw-loop.sh | 1708 | MAJOR | API misuse | Unsafe pane indexing | Unfixed |
|
|
805
|
+
| 5 | sw-session.sh | 435 | MAJOR | Error handling | No check for new-window failure | Unfixed |
|
|
806
|
+
| 6 | sw-session.sh | 209 | MAJOR | Input validation | jq failure not detected | Unfixed |
|
|
807
|
+
| 7 | sw-loop.sh | 1625 | MAJOR | API misuse | Window/pane reference mixing | Unfixed |
|
|
808
|
+
| 8 | sw-tmux.sh | 234 | MINOR | Quoting | Inconsistent variable quoting | Unfixed |
|
|
809
|
+
| 9 | sw-adapter.sh | 38-50 | MINOR | Hardcoding | Magic sleep values | Unfixed |
|
|
810
|
+
| 10 | sw-tmux.sh | 105 | MINOR | Docs | Missing version feature warnings | Unfixed |
|
|
811
|
+
| 11 | sw-tmux-test.sh | N/A | MINOR | Test gap | No adapter lifecycle test | Unfixed |
|
|
812
|
+
| 12 | Multiple | N/A | MINOR | Docs | Pane ID design decision undocumented | Unfixed |
|
|
813
|
+
| 13 | sw-loop.sh | 1625 | MINOR | Cleanup | No trap on function exit | Unfixed |
|
|
814
|
+
| 14 | Templates | N/A | MINOR | Feature | No per-agent colors | Unfixed |
|
|
815
|
+
| 15 | overlay.conf | 127 | MINOR | UX | Reaper grace period not configurable | Unfixed |
|
|
816
|
+
| 16 | Tests | N/A | HIGH | Gap | No pane-base-index=nonzero test | Unfixed |
|
|
817
|
+
| 17 | Tests | N/A | MEDIUM | Gap | No dark theme race test | Unfixed |
|
|
818
|
+
| 18 | Tests | N/A | MINOR | Gap | No launcher cleanup test | Unfixed |
|
|
819
|
+
| 19 | Tests | N/A | MEDIUM | Gap | No sed injection test | Unfixed |
|
|
820
|
+
| 20 | Tests | N/A | MEDIUM | Gap | No E2E session lifecycle test | Unfixed |
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
824
|
+
## Recommendations
|
|
825
|
+
|
|
826
|
+
### Immediate Actions (This Sprint)
|
|
827
|
+
|
|
828
|
+
1. **Fix issue #1** (pane index in sw-reaper.sh:115) — change to `#{pane_id}`
|
|
829
|
+
2. **Fix issue #3** (dark theme race in sw-session.sh:470) — remove or add delay
|
|
830
|
+
3. **Fix issue #2** (sed injection in sw-session.sh:429) — escape special chars
|
|
831
|
+
4. **Fix issue #5** (new-window error check) — add proper error handling
|
|
832
|
+
|
|
833
|
+
### Short Term (Next Sprint)
|
|
834
|
+
|
|
835
|
+
5. Fix issue #4 (pane reference consistency in sw-loop.sh)
|
|
836
|
+
6. Fix issue #6 (jq error handling in sw-session.sh)
|
|
837
|
+
7. Add test #16 (pane-base-index safety)
|
|
838
|
+
8. Add test #20 (E2E session lifecycle)
|
|
839
|
+
|
|
840
|
+
### Medium Term (Quality Pass)
|
|
841
|
+
|
|
842
|
+
9. Fix issue #9 (hardcoded sleeps)
|
|
843
|
+
10. Fix issue #13 (cleanup trap)
|
|
844
|
+
11. Add remaining tests (#17, #18, #19)
|
|
845
|
+
12. Improve documentation (issue #12)
|
|
846
|
+
|
|
847
|
+
### Nice to Have (Backlog)
|
|
848
|
+
|
|
849
|
+
13. Add per-agent colors (issue #14)
|
|
850
|
+
14. Make reaper grace period configurable from tmux (issue #15)
|
|
851
|
+
15. Add version feature warnings (issue #10)
|
|
852
|
+
|
|
853
|
+
---
|
|
854
|
+
|
|
855
|
+
## Files Requiring Changes
|
|
856
|
+
|
|
857
|
+
### High Priority
|
|
858
|
+
|
|
859
|
+
1. `/Users/sethford/Documents/shipwright/scripts/sw-reaper.sh` (line 115)
|
|
860
|
+
2. `/Users/sethford/Documents/shipwright/scripts/sw-session.sh` (lines 429, 435, 470)
|
|
861
|
+
3. `/Users/sethford/Documents/shipwright/scripts/sw-loop.sh` (lines 1625, 1708)
|
|
862
|
+
|
|
863
|
+
### Medium Priority
|
|
864
|
+
|
|
865
|
+
4. `/Users/sethford/Documents/shipwright/scripts/sw-tmux-test.sh` (add tests)
|
|
866
|
+
5. `/Users/sethford/Documents/shipwright/scripts/sw-session-test.sh` (add tests)
|
|
867
|
+
6. `/Users/sethford/Documents/shipwright/scripts/adapters/tmux-adapter.sh` (issue #9)
|
|
868
|
+
|
|
869
|
+
### Low Priority
|
|
870
|
+
|
|
871
|
+
7. `/Users/sethford/Documents/shipwright/scripts/sw-tmux.sh` (minor improvements)
|
|
872
|
+
8. `/Users/sethford/Documents/shipwright/tmux/shipwright-overlay.conf` (issue #15)
|
|
873
|
+
9. `/Users/sethford/Documents/shipwright/tmux/templates/*.json` (issue #14)
|
|
874
|
+
|
|
875
|
+
---
|
|
876
|
+
|
|
877
|
+
## Testing Strategy
|
|
878
|
+
|
|
879
|
+
### Manual Testing Before Fixes
|
|
880
|
+
|
|
881
|
+
```bash
|
|
882
|
+
# Test current behavior with pane-base-index != 0
|
|
883
|
+
tmux set-window-option pane-base-index 5
|
|
884
|
+
tmux split-window # This pane is %0 but index is 5
|
|
885
|
+
shipwright reaper --dry-run # Should show correct pane IDs
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
### Automated Test Execution
|
|
889
|
+
|
|
890
|
+
```bash
|
|
891
|
+
# Run full test suite with coverage
|
|
892
|
+
npm test 2>&1 | tee test-results.txt
|
|
893
|
+
|
|
894
|
+
# Run specific test suites
|
|
895
|
+
./scripts/sw-tmux-test.sh
|
|
896
|
+
./scripts/sw-session-test.sh
|
|
897
|
+
./scripts/sw-reaper-test.sh # May not exist yet
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
### Regression Testing
|
|
901
|
+
|
|
902
|
+
After fixes, verify:
|
|
903
|
+
|
|
904
|
+
1. All existing tests still pass
|
|
905
|
+
2. New tests pass (issue #16, #20)
|
|
906
|
+
3. Manual pane-base-index scenario works
|
|
907
|
+
4. Sed injection scenario handled safely
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
## Conclusion
|
|
912
|
+
|
|
913
|
+
The Shipwright tmux integration is **production-ready** with good defensive patterns (pane IDs, hooks, proper quoting in most places). However, **3 critical bugs** should be fixed before relying on tmux reliability in high-load scenarios:
|
|
914
|
+
|
|
915
|
+
1. Pane index/ID inconsistency (sw-reaper.sh)
|
|
916
|
+
2. Dark theme race condition (sw-session.sh)
|
|
917
|
+
3. Sed injection vulnerability (sw-session.sh)
|
|
918
|
+
|
|
919
|
+
After fixes, the codebase would benefit from:
|
|
920
|
+
|
|
921
|
+
- Comprehensive test coverage for edge cases
|
|
922
|
+
- Better documentation of design decisions (pane ID safety)
|
|
923
|
+
- Consistent error handling across all entry points
|
|
924
|
+
|
|
925
|
+
**Estimated fix time:** 2-3 hours for critical bugs, 1-2 days for full suite with tests.
|