Complete implementation of 7 slices addressing E2E testing gaps:
Slice 0+1: Wire Actions + ReasonPrompt
- FocusView now uses useActions hook instead of direct act() calls
- Added pendingAction state pattern for skip/defer/complete actions
- ReasonPrompt integration with proper confirm/cancel flow
- Tags support in DecisionEntry interface
Slice 2: Drag Reorder UI
- Installed @dnd-kit (core, sortable, utilities)
- QueueView with DndContext, SortableContext, verticalListSortingStrategy
- SortableQueueItem wrapper component using useSortable hook
- pendingReorder state with ReasonPrompt for reorder reasons
- Cmd+Up/Down keyboard shortcuts for accessibility
- Fixed: Store item ID in PendingReorder to avoid stale queue reference
Slice 3: System Tray Integration
- tray.rs with TrayState, setup_tray, toggle_window_visibility
- Menu with Show/Quit items
- Left-click toggles window visibility
- update_tray_badge command updates tooltip with item count
- Frontend wiring in AppShell
Slice 4: E2E Test Updates
- Fixed test selectors for InboxView, Queue badge
- Exposed inbox store for test seeding
Slice 5: Staleness Visualization
- Already implemented in computeStaleness() with tests
Slice 6: Quick Wiring
- onStartBatch callback wired to QueueView
- SyncStatus rendered in nav area
- SettingsView renders Settings component
Slice 7: State Persistence
- settings-store with hydrate/update methods
- Tauri backend integration via read_settings/write_settings
- AppShell hydrates settings on mount
Bug fixes from code review:
- close_bead now has error isolation (try/catch) so decision logging
and queue advancement continue even if bead close fails
- PendingReorder stores item ID to avoid stale queue reference
E2E tests for all ACs (tests/e2e/followup-acs.spec.ts):
- AC-F1: Drag reorder (4 tests)
- AC-F2: ReasonPrompt integration (7 tests)
- AC-F5: Staleness visualization (3 tests)
- AC-F6: Batch mode (2 tests)
- AC-F7: SyncStatus (2 tests)
- ReasonPrompt behavior (3 tests)
Tests: 388 frontend + 119 Rust + 32 E2E all passing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The MappingKey::escape_project now replaces / with :: so
'issue:g/p:42' becomes 'issue:g::p:42'.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add DebugView component to show raw lore status data for visual
verification that the data pipeline works end-to-end:
- Frontend -> Tauri IPC -> Rust backend -> lore CLI -> parsed data
New files:
- src/components/DebugView.tsx - Debug component with health indicator
- src/hooks/useLoreData.ts - TanStack Query hook for lore status
- tests/components/DebugView.test.tsx - Component tests
- tests/hooks/useLoreData.test.ts - Hook tests
Modified:
- src/App.tsx - Add QueryClientProvider wrapper
- src/stores/nav-store.ts - Add 'debug' ViewId
- src/components/AppShell.tsx - Add Debug nav tab and view routing
- tests/components/AppShell.test.tsx - Update tests for new nav
Implements the InboxView container that wraps the existing Inbox component
with store integration and keyboard navigation.
Key features:
- Filters and displays only untriaged inbox items from store
- Keyboard navigation (j/k or arrow keys) between items
- Triage actions (accept, defer, archive) that update store state
- Inbox zero celebration state with animation
- Real-time count updates in both view header and nav badge
- Keyboard shortcut hints in footer
TDD: Tests written first, then implementation to pass them.
Files:
- src/components/InboxView.tsx: New container component
- src/stores/inbox-store.ts: New Zustand store for inbox state
- src/components/Inbox.tsx: Added focusIndex prop for keyboard nav
- src/components/AppShell.tsx: Wire up InboxView and inbox count badge
- src/lib/types.ts: Added archived and snoozedUntil fields to InboxItem
- tests/components/Inbox.test.tsx: Added InboxView test suite
- tests/helpers/fixtures.ts: Added makeInboxItem helper
Acceptance criteria met:
- Only untriaged items shown
- Inbox zero state with animation
- Keyboard navigation works
- Triage actions update state
- Count updates in real-time
Add suggestion state when no focus is set but items exist in queue:
- FocusView now shows SuggestionCard when current is null but queue has items
- SuggestionCard displays suggested item with 'Set as focus' button
- Clicking 'Set as focus' promotes the suggestion to current focus
- Auto-advances to next item after completing current focus
- Shows empty state celebration when all items are complete
TDD: 14 tests covering focus, suggestion, empty states, and actions
Add query hooks for lore and bridge status with automatic invalidation
on lore-data-changed and sync-status events. Include mutations for
sync_now and reconcile operations that invalidate relevant queries.
- createQueryClient factory with appropriate defaults
- useLoreStatus hook with 30s staleTime and event invalidation
- useBridgeStatus hook with 30s staleTime and event invalidation
- useSyncNow mutation with query invalidation on success
- useReconcile mutation with query invalidation on success
- Centralized query keys for consistent invalidation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
QueueView now supports:
- Filtering items via CommandPalette (Cmd+K)
- Hide snoozed items by default (showSnoozed prop)
- Show snooze count indicator when items are hidden
- Support batch mode entry for sections with 2+ items
- Filter by type prop for programmatic filtering
Added snoozedUntil field to FocusItem type and updated fixtures.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ErrorBoundary class component to catch React render errors
- Show fallback UI with error details (stack in dev mode) and recovery buttons
- Add ErrorDisplay component for showing structured McError messages
- Support all McErrorCode types with contextual messages and install guides
- Implement retry/dismiss actions for recoverable errors
- Add 16 comprehensive tests covering both components
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add InvariantError class and assertion utilities for runtime validation:
- invariant(condition, message) - throws if condition is falsy, acts as type guard
- assertNever(value) - for exhaustive switch statement checking
- Support for lazy message evaluation to avoid expensive string operations
These helpers enable defensive programming with clear error messages when
invariants are violated.
Closes bd-1w5
Shows THE ONE THING with:
- Focus item title, type badge, project, and age
- Quick actions: Start, Defer (1h), Skip
- Queue and inbox counts
- Link to open full window
- Empty state when nothing focused
Includes 18 tests covering all states and interactions.
bd-wlg
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Displays sync state with:
- Green dot for synced (with relative time)
- Spinner for syncing
- Amber dot for stale (auto-detected after 15min)
- Red dot for error (with retry button)
- Gray dot for offline
Includes 23 tests covering all states, time formatting,
button visibility, and click handlers.
bd-2or
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add text search across all focus items
- Support filter commands: type: and stale:
- Keyboard navigation with arrow keys + Enter
- Click to select items
- Escape or backdrop click to close
- 17 tests covering search, filters, keyboard nav, and empty state
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Log swallowed errors in file watcher and window operations (lib.rs, watcher.rs)
- Propagate recovery errors from bridge::recover_pending to SyncResult.errors
so the frontend can display them instead of silently dropping failures
- Fix useTauriEvent/useTauriEvents race condition where cleanup fires before
async listen() resolves, leaking the listener (cancelled flag pattern)
- Guard computeStaleness against invalid date strings (NaN -> 'normal'
instead of incorrectly returning 'urgent')
- Strengthen isMcError type guard to check field types, not just presence
- Log warning when data directory resolution falls back to '.' (state.rs, bridge.rs)
- Add test for computeStaleness with invalid date inputs
- Create ReasonPrompt dialog for capturing optional reasons
- Add quick tag buttons (Blocking, Urgent, Context switch, etc.)
- Support keyboard navigation (Escape to cancel)
- Handle text input with trimming and null for empty
- Different titles for different actions (set_focus, defer, skip)
- All 10 tests pass
Closes bd-2p0
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add InboxItem types and TriageAction/DeferDuration types
- Create Inbox component with:
- Accept/Defer/Archive actions for each item
- Keyboard shortcuts (A/D/X) for fast triage
- Defer duration picker popup
- Inbox Zero celebration state
- Type-specific badges with colors
- Add comprehensive tests for all functionality
Closes bd-qvc
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds test coverage for the navigation store's localStorage persistence
and ensures clean state between tests.
Changes:
- nav-store.test.ts: Add persistence test verifying activeView is saved
- Clear localStorage in beforeEach to prevent test pollution
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds real-time updates when lore syncs new GitLab data by watching
the lore.db file for changes.
React hook (src/hooks/useTauriEvents.ts):
- useTauriEvent(): Subscribe to a single Tauri event with auto-cleanup
- useTauriEvents(): Subscribe to multiple events with a handler map
- Typed payloads for each event type:
- global-shortcut-triggered: toggle-window | quick-capture
- lore-data-changed: void (refresh trigger)
- sync-status: started | completed | failed
- error-notification: code + message
Rust watcher (src-tauri/src/watcher.rs):
- Watches lore's data directory for lore.db modifications
- Uses notify crate with 2-second poll interval
- Emits "lore-data-changed" event to frontend on file change
- Handles atomic writes by watching parent directory
- Gracefully handles missing lore.db (logs warning, skips watcher)
Test coverage:
- Hook subscription and cleanup behavior
- Focus store test fix: clear localStorage before each test
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set up comprehensive testing infrastructure for both unit and E2E tests:
Unit Testing (Vitest):
- vitest.config.ts: jsdom environment, globals enabled
- Path alias @tauri-apps/api -> tests/mocks/tauri-api.ts
- Excludes tests/e2e/** to prevent Playwright collision
- V8 coverage configured for src/**/*.{ts,tsx}
- tests/setup.ts: @testing-library/jest-dom matchers
Tauri API Mocking:
- tests/mocks/tauri-api.ts: Mock implementation of @tauri-apps/api
- invoke(): Returns configurable mock responses
- listen()/emit(): Event system stubs
- setMockResponse()/resetMocks(): Test helpers
- Enables testing React components without Tauri runtime
Component Tests:
- tests/components/App.test.tsx: Verifies App shell renders
- "Mission Control" heading
- "What should you be doing right now?" tagline
- "THE ONE THING will appear here" placeholder
E2E Testing (Playwright):
- playwright.config.ts: Chromium + WebKit (Tauri uses WebKit on macOS)
- Runs Vite dev server before tests
- HTML reporter, trace on retry
- tests/e2e/app.spec.ts: Smoke tests for deployed app
- Heading visible, tagline visible, dark mode applied
This dual-layer testing strategy (Vitest for speed, Playwright for
integration) follows the testing trophy: many unit, fewer E2E.