Skip to content

feat: REST API Migration for Stateless Operations#390

Merged
rjroy merged 41 commits intomainfrom
ref/375-rest-api
Jan 22, 2026
Merged

feat: REST API Migration for Stateless Operations#390
rjroy merged 41 commits intomainfrom
ref/375-rest-api

Conversation

@rjroy
Copy link
Copy Markdown
Owner

@rjroy rjroy commented Jan 22, 2026

Summary

Migrates 36 stateless WebSocket operations to REST API endpoints, resolving the select_vault race condition and improving frontend testability.

  • Backend: 9 REST route files under backend/src/routes/ with vault resolution middleware and consistent error handling
  • Frontend: 8 React hooks using REST API with dependency injection for testable fetch
  • Protocol: WebSocket now only handles streaming operations (AI chat, pair writing, session establishment)
  • Tests: 489 tests covering routes, hooks, and end-to-end integration

Changes

Backend Routes Created

  • /api/vaults/:vaultId/files/* - File browser operations
  • /api/vaults/:vaultId/capture - Note capture
  • /api/vaults/:vaultId/goals, /inspiration, /tasks - Home dashboard
  • /api/vaults/:vaultId/search/* - File and content search
  • /api/vaults/:vaultId/meetings/* - Meeting management
  • /api/vaults/:vaultId/config/* - Configuration
  • /api/vaults/:vaultId/memory - Memory operations
  • /api/vaults/:vaultId/sessions/:sessionId - Session management

Frontend Hooks Created

  • useFileBrowser - Directory listing, file CRUD, rename/move
  • useCapture - Note capture, recent notes/activity
  • useHome - Goals, inspiration, tasks
  • useSearch - File search, content search, snippets
  • useConfig - Pinned assets, vault config, health issues
  • useMeetings - Meeting lifecycle
  • useMemory - Memory get/save
  • useSessions - Session deletion

Protocol Cleanup

  • Removed 30+ migrated message types from WebSocket protocol
  • WebSocket retained for: AI streaming, pair writing, extraction prompts, health reports

Test plan

  • All 489 tests pass (backend, frontend, shared)
  • TypeScript compilation passes
  • ESLint passes
  • Integration tests validate REST-only operation
  • Performance tests confirm <200ms file ops, <500ms search
  • Manual testing of all tabs (Ground, Capture, Think, Recall)
  • Verify AI chat still streams correctly via WebSocket

Closes #375

🤖 Generated with Claude Code

rjroy and others added 30 commits January 21, 2026 13:05
- Create Hono middleware that resolves :vaultId path parameter to VaultInfo
- Validate vault ID format to prevent path traversal (REQ-NF-6)
- Return 404 JSON error for unknown vaults (REQ-F-55)
- Export helper functions for downstream route handlers
- Add comprehensive test suite (25 tests)

Implements: REQ-F-3, REQ-F-55, REQ-NF-6

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create error handler that maps FileBrowserError to HTTP status codes
- Return consistent { error: { code, message } } JSON format (REQ-NF-3)
- Map: PATH_TRAVERSAL→403, FILE/DIR_NOT_FOUND→404, VALIDATION→400
- Unknown errors return 500 with safe message (no stack traces)
- Log errors server-side with method, path, and context
- Add comprehensive test suite (21 tests)

Implements: REQ-F-55, REQ-F-56, REQ-F-57, REQ-F-58, REQ-F-59, REQ-NF-3

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create route index with vault-scoped Hono router
- Apply vault resolution middleware to /api/vaults/:vaultId/* routes
- Register global error handler with app.onError()
- CORS inherited from existing /api/* configuration
- Add integration tests (15 tests) verifying middleware and error handling

Implements: REQ-F-3, REQ-F-4
Depends on: TASK-001, TASK-002

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enhance meeting-store.ts with diagnostic/testing functions:
- getAllActiveMeetings() for debugging visibility
- clearAllMeetings() for test cleanup

Add comprehensive unit tests covering:
- Basic store operations (get, set, clear, has)
- Entry count incrementing
- Single meeting per vault enforcement
- Meeting lifecycle scenarios
- WebSocket reconnection simulation
- Edge cases

31 tests passing, 65 assertions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create search-cache.ts module for REST API search efficiency:
- Map<vaultId, CacheEntry> for vault-scoped caching
- getOrCreateIndex(vaultId, vaultPath) with lazy initialization
- LRU eviction when >10 vaults cached (configurable)
- TTL-based invalidation (default 5 minutes)
- Cache stats for monitoring

Add REST-compatible search functions to search-handlers.ts:
- searchFilesRest() for file name search
- searchContentRest() for content search
- getSnippetsRest() for context snippets
- SearchResultWithTiming<T> interface

29 tests passing, 73 assertions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create frontend REST API infrastructure:

frontend/src/api/types.ts:
- ApiErrorResponse matching { error: { code, message } } format
- ApiError class with typed accessors and helper methods
- isApiErrorResponse() type guard
- ApiResult<T>, ApiRequestOptions, ApiClientConfig interfaces

frontend/src/api/client.ts:
- createApiClient() factory with dependency injection for fetch
- Methods: get(), post(), put(), patch(), delete(), request()
- Status code to ErrorCode mapping (404→FILE_NOT_FOUND, etc.)
- vaultPath() helper for building vault-scoped endpoints
- Handles AbortSignal for request cancellation

39 tests passing, 78 assertions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create REST endpoints for search operations:
- GET /api/vaults/:vaultId/search/files?q= - File name search
- GET /api/vaults/:vaultId/search/content?q= - Content search
- GET /api/vaults/:vaultId/search/snippets?path=&q= - Context snippets

Uses search-cache.ts for cached SearchIndexManager instances.

30 tests passing, 97 assertions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create REST endpoints for file browser operations:
- GET /files?path= - Directory listing
- POST /files - Create file
- GET/PUT/PATCH/DELETE /files/:path - File operations
- POST/DELETE /directories - Directory operations
- GET /directories/:path/contents - Directory contents preview
- POST /archive/:path/archive - Archive file/directory

51 tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create REST endpoints for note capture:
- POST /capture - Capture text to daily note
- GET /recent-notes - Get recent captured notes
- GET /recent-activity - Get combined recent activity

26 tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create REST endpoints for home dashboard:
- GET /goals - Get vault goals
- GET /inspiration - Get inspiration (contextual + quote)
- GET /tasks - Get all tasks
- PATCH /tasks - Toggle task completion

25 tests passing (2 skipped - require SDK).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create REST endpoints for meeting management:
- POST /meetings - Start a meeting
- DELETE /meetings/current - Stop current meeting
- GET /meetings/current - Get meeting state

Uses meeting-store.ts for state persistence across requests.

20 tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create REST endpoints for vault configuration:
- GET/PUT /pinned-assets - Manage pinned assets
- PATCH /config - Update vault config
- POST /setup - Setup vault
- DELETE /health-issues/:issueId - Dismiss health issue

25 tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create REST endpoints for memory and session management:
- GET/PUT /memory - Memory file operations
- DELETE /sessions/:sessionId - Delete session

Includes bug fix for memory file path resolution.

20 tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Import and mount all domain route modules:
- File browser (files, directories, archive)
- Capture and recent activity
- Home dashboard (goals, inspiration, tasks)
- Meetings
- Search
- Config
- Memory and sessions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1 and Phase 2 complete (13 of 18 tasks, 72%)
357 tests across all REST API components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create useFileBrowser hook for file operations via REST API:
- listDirectory, readFile, writeFile, deleteFile
- createFile, createDirectory, deleteDirectory
- renameFile, moveFile

Hook uses dependency injection for fetch (testable without mock.module).
42 tests cover all operations, error handling, and loading states.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create three React hooks for REST API operations:

useCapture: captureNote, getRecentNotes, getRecentActivity
useHome: getGoals, getInspiration, getTasks, toggleTask
useSearch: searchFiles, searchContent, getSnippets

All hooks use dependency injection for testable fetch.
Reuses shared types from @memory-loop/shared.
61 tests across the three hooks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 3 Frontend Migration complete:
- TASK-014: File Browser Hooks (42 tests)
- TASK-015: Domain Hooks (61 tests)

15 of 18 tasks complete (83%). 460 total tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove handlers for operations now served by REST:
- File browser: list_directory, read_file, write_file, etc.
- Capture: capture_note, get_recent_notes, get_recent_activity
- Home: get_goals, get_inspiration, get_tasks, toggle_task
- Search: search_files, search_content, get_snippets
- Config: get_pinned_assets, set_pinned_assets, etc.
- Memory: get_memory, save_memory
- Session: delete_session, Meeting operations

WebSocket now only handles:
- Session establishment (select_vault, new_session, resume_session)
- AI streaming (discussion_message, response_*, tool_*)
- Interactive prompts (tool_permission_*, ask_user_question_*)
- Pair writing (quick_action, advisory_action)
- Extraction prompt operations
- ping/pong keepalive

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive integration tests validating REST-only operation:
- File browser without WebSocket (read, write, navigate)
- Capture without WebSocket (POST note, verify response)
- Search without WebSocket (files, content, snippets)
- Error handling (404, 400, 403 responses)
- Performance (<200ms file ops, <500ms search)
- No select_vault bug (direct vault access works)
- REQ-F-64: REST uses same business logic as WebSocket

29 tests, 138 assertions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove WebSocket message types now handled by REST API:
- File browser: list_directory, read_file, write_file, etc.
- Capture: capture_note, get_recent_notes, get_recent_activity
- Home: get_goals, get_inspiration, get_tasks, toggle_task
- Search: search_files, search_content, get_snippets
- Config: get_pinned_assets, set_pinned_assets, etc.
- Memory: get_memory, save_memory
- Session: delete_session
- Meeting: start_meeting, stop_meeting, get_meeting_state

WebSocket protocol now only handles:
- Session establishment (select_vault, new_session, resume_session)
- AI streaming (discussion_message, response_*, tool_*)
- Interactive prompts (tool_permission_*, ask_user_question_*)
- Pair writing, extraction prompt operations
- Health reports, ping/pong

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Additional hooks to complete the REST API migration:
- useConfig: pinned assets, vault config, setup, health issues
- useMeetings: start, stop, get meeting state
- useMemory: get/save memory
- useSessions: delete session

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update all components to use REST hooks instead of WebSocket:
- App.tsx: useMeetings, useConfig hooks
- BrowseMode.tsx: useFileBrowser, useSearch, useHome, useConfig
- HomeView.tsx: useCapture, useHome, useSessions
- MemoryEditor.tsx: useMemory hook
- NoteCapture.tsx: useCapture, useMeetings
- MoveDialog.tsx: useFileBrowser
- VaultSelect.tsx: direct API calls for setup/config

SessionContext simplified: removed handlers for migrated operations.
useWebSocket: removed search methods (now REST-based).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Delete tests for WebSocket patterns that no longer exist:
- BrowseMode test files (8 files)
- MemoryEditor.test.tsx
- NoteCapture.test.tsx
- VaultSelect.test.tsx

These tests relied on WebSocket message mocking which is no longer
applicable after REST migration. New tests use REST hooks with
dependency injection for fetch.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Delete handler files now replaced by REST routes:
- browser-handlers.ts (replaced by routes/files.ts)
- home-handlers.ts (replaced by routes/home.ts)
- meeting-handlers.ts (replaced by routes/meetings.ts)

Update handlers/index.ts to remove deleted exports.
Update memory-handlers.ts and search-handlers.ts for REST usage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All 18 tasks complete (100%). 489 total tests.

Migration includes:
- 30+ message types moved from WebSocket to REST
- 8 REST hooks for frontend
- 9 route files for backend
- Protocol schemas cleaned up
- Obsolete handlers and tests removed

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 22, 2026

rjroy and others added 11 commits January 22, 2026 09:04
REST API calls should not wait for WebSocket connection - that was
the whole point of the migration. Now data loads immediately when
vault is available.

Also properly resets data loading state when vault changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Simplified the data loading effect to trigger on vault.id change.
Added console logs for debugging.
Removed ref-based tracking that was causing issues.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When options.fetch is undefined, spreading { fetch: undefined }
overwrites the default fetch with undefined, causing "n.fetch is not
a function" errors in production.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The effects were depending on the entire hook objects (fileBrowser,
homeApi, configApi) which are new objects on every render. This
caused infinite loops when effects triggered state changes.

Fixed by destructuring the specific functions needed and using those
in the dependency arrays instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The effect that fetches pinned assets was calling getPinnedAssets() but
discarding the result. Now actually calls setPinnedAssets(paths) to
populate the pinned files in the Recall tab.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Complete the REST API migration by removing WebSocket connections from
components that no longer need them:

- HomeView: Remove useWebSocket, select_vault effect (loads via REST)
- NoteCapture: Remove useWebSocket, select_vault effect (submits via REST)
- BrowseMode: Remove hasSessionReady gating, session_ready handler
  - File tree, pinned assets, and tasks now load immediately
  - WebSocket kept only for PairWritingMode passthrough

Achieves spec criterion: "WebSocket is only required when entering
Discussion mode."

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…VaultSelect

Add 100 new tests covering key behaviors:
- MemoryEditor: loading, size limits, save/reset, accessibility
- NoteCapture: drafts, meeting mode, keyboard shortcuts
- VaultSelect: vault list, selection, error states, session resume
- BrowseMode: view modes, tree collapse, mobile overlay

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…seMode, NoteCapture, and VaultSelect components
@rjroy rjroy merged commit 1320b82 into main Jan 22, 2026
2 of 3 checks passed
@rjroy rjroy deleted the ref/375-rest-api branch January 22, 2026 19:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WebSocket over use

1 participant