Skip to content

Commit 5622d24

Browse files
feat: Tab base directory with VS Code style redesign
## Tab Base Directory Feature - Per-tab base directory context (tab:basedir, tab:basedirlock metadata) - Smart auto-detection from terminal OSC 7 sequences - All terminals and widgets inherit the tab's base directory - Tab context menu for configuration - Stale path detection and recovery after restart ## Security & Validation - Path validation for OSC 7 terminal escape sequences - Backend metadata validation to prevent injection attacks - Optimistic locking for metadata updates (race condition fix) - Preset metadata validation ## VS Code Style Tab Redesign - Tab backgrounds use relative white overlays (active/hover/inactive) - Tab stripe moved from left to top horizontal (manual color only) - Status indicated via text color (running=blue, finished=green, stopped=red) - Breadcrumb bar below tabs showing active tab's base directory - App menu moved from tab bar to breadcrumb bar - Transparent button styling with subtle hover effects ## Other Improvements - Tab color customization via context menu - Reactive tab status icons using Jotai atoms - Reusable preset menu builder - showOpenDialog API for file picker dialogs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 31a8714 commit 5622d24

41 files changed

Lines changed: 3831 additions & 105 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGES.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Changes Summary: Race Condition Fixes with Optimistic Locking
2+
3+
## Overview
4+
5+
This implementation addresses race conditions in tab metadata updates (spec-004) by implementing optimistic locking with version checking. The changes prevent TOCTOU (Time-Of-Check-Time-Of-Use) vulnerabilities in concurrent metadata operations.
6+
7+
## Files Modified
8+
9+
### Backend (Go)
10+
11+
#### `pkg/wstore/wstore_dbops.go`
12+
- Added `ErrVersionMismatch` error variable for concurrent modification detection
13+
- Added `ErrObjectLocked` error variable for lock state rejection
14+
15+
#### `pkg/wstore/wstore.go`
16+
- Added `UpdateObjectMetaWithVersion()` function:
17+
- Performs optimistic locking update with version checking
18+
- If `expectedVersion > 0` and doesn't match current version, returns `ErrVersionMismatch`
19+
- If `expectedVersion == 0`, behaves like `UpdateObjectMeta` (no version check)
20+
21+
- Added `UpdateObjectMetaIfNotLocked()` function:
22+
- Atomically checks lock and updates metadata
23+
- Lock is checked INSIDE the transaction, eliminating TOCTOU vulnerability
24+
- Returns `ErrObjectLocked` (wrapped in `ErrVersionMismatch`) if locked
25+
- Returns `ErrVersionMismatch` if version doesn't match
26+
27+
#### `pkg/service/objectservice/objectservice.go`
28+
- Added `UpdateObjectMetaWithVersion()` RPC service method
29+
- Added `UpdateObjectMetaIfNotLocked()` RPC service method
30+
- Both methods include proper metadata annotations for TypeScript binding generation
31+
32+
### Frontend (TypeScript)
33+
34+
#### `frontend/app/view/term/termwrap.ts`
35+
- Added debounce map (`osc7DebounceMap`) for OSC 7 updates per tab
36+
- Added `OSC7_DEBOUNCE_MS = 300` constant for debounce delay
37+
- Added `clearOsc7Debounce()` helper function
38+
- Added `cleanupOsc7DebounceForTab()` exported function for memory leak prevention
39+
- Updated `handleOsc7Command()` to:
40+
- Add null safety check for `tabData?.oid`
41+
- Use debouncing to reduce race condition window
42+
- Use atomic lock-aware update (`UpdateObjectMetaIfNotLocked`) instead of regular update
43+
- Gracefully handle version mismatch and locked state errors
44+
45+
#### `frontend/app/tab/tab.tsx`
46+
- Added `getApi` to imports from `@/app/store/global` (fix for pre-existing missing import)
47+
48+
### Generated Files
49+
50+
#### `frontend/app/store/services.ts`
51+
- Auto-generated new TypeScript methods:
52+
- `UpdateObjectMetaWithVersion(oref, meta, expectedVersion)`
53+
- `UpdateObjectMetaIfNotLocked(oref, meta, lockKey, expectedVersion)`
54+
55+
## Key Features Implemented
56+
57+
### 1. Optimistic Locking
58+
- Uses existing `version` field in WaveObj types
59+
- Version checked inside transaction to prevent TOCTOU
60+
- Atomic increment of version on successful update (already implemented in `DBUpdate`)
61+
62+
### 2. Error Types
63+
- **ErrVersionMismatch**: Indicates concurrent modification detected
64+
- **ErrObjectLocked**: Indicates update rejected due to lock state
65+
- Both errors are wrapped appropriately for consistent error handling
66+
67+
### 3. OSC 7 Debouncing
68+
- 300ms debounce window for rapid directory changes
69+
- Per-tab debounce timers in a Map
70+
- Cleanup function to prevent memory leaks on tab close
71+
72+
### 4. Atomic Lock Checking
73+
- Lock state checked INSIDE database transaction
74+
- Eliminates race condition between lock check and update
75+
- If lock is toggled during update, the update is safely rejected
76+
77+
## Acceptance Criteria Status
78+
79+
- [x] `UpdateObjectMetaWithVersion` added to `wstore.go`
80+
- [x] RPC endpoints added to `objectservice.go`
81+
- [x] OSC 7 debounce map with cleanup function
82+
- [x] Null safety guards in `termwrap.ts`
83+
- [x] `ErrVersionMismatch` error type created
84+
- [x] `ErrObjectLocked` error type created
85+
- [x] TypeScript compilation passes (our files)
86+
- [x] Go compilation passes
87+
- [x] Changes committed
88+
89+
## Testing Notes
90+
91+
To test the implementation:
92+
93+
1. **Version Mismatch Test**: Open two terminals in the same tab, rapidly change directories in both - the race condition should be handled gracefully
94+
95+
2. **Lock Bypass Test**: Toggle the lock while an OSC 7 update is in flight - the update should be rejected if lock is set
96+
97+
3. **Debounce Test**: Rapidly `cd` between directories - only the final directory should be set as basedir
98+
99+
4. **Memory Leak Test**: Open and close multiple tabs - the debounce map should be cleaned up
100+
101+
## Notes
102+
103+
- The spec mentions retry logic for manual updates (handleSetBaseDir, handleToggleLock) - this was NOT implemented as the spec noted it as optional for Phase 4 and the core race condition fixes are functional without it
104+
- Pre-existing TypeScript errors in unrelated files (streamdown.tsx, notificationpopover.tsx) remain unfixed as they are not related to this implementation

0 commit comments

Comments
 (0)