Migrate frontend from Vite+Hono to Next.js App Router#458
Merged
Conversation
Phase 1-3 of Next.js + SSE migration plan: Backend streaming module: - ActiveSessionController for transport-agnostic session management - SessionStreamer for SDK event transformation - Types for SessionEvent, PendingPrompt, PromptResponse Next.js app structure: - SSE chat endpoint (POST /api/chat) - Permission/question resolution endpoints - Abort endpoint - Webpack config for .js extension resolution Frontend integration: - useChat hook for SSE-based chat communication - Discussion component supports transport="sse" prop - Unified interface for both WebSocket and SSE transports WebSocket handler update deferred for separate commit. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WebSocket handler now delegates to ActiveSessionController for all streaming logic, becoming a thin transport layer. The controller owns AI state (SDK connection, pending prompts, token tracking) while the handler just subscribes to events and maps them to WebSocket messages. Removed ~440 lines of streaming code from websocket-handler.ts. Satisfies REQ-6 from session-viewport separation spec. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Next.js build output should not be version controlled. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1: Move React components to Next.js - Move all components from frontend/src/components/ to nextjs/components/ - Move contexts, hooks, styles to nextjs/ - Create App.tsx wrapper and update layout.tsx Phase 2: Migrate all REST API routes to Next.js - Create 30 API route files covering all endpoints - Vault management, files, capture, search, meetings, cards, config - Add vault-helpers.ts for common route utilities - Update backend package.json exports for new modules - Create spaced-repetition/index.ts for module re-exports Phase 3: Cleanup - Delete frontend/ workspace entirely - Update root package.json: remove frontend, update scripts - Update pre-commit hook to check nextjs instead of frontend - dev/build/start now point to Next.js directly WebSocket proxy retained for streaming until full SSE migration. Known issues (to fix in follow-up): - Test files reference bun:test which isn't available in Next.js tsconfig - Lint errors from moved test files - Some import paths need updating Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move fonts, images, icons, and manifest from deleted frontend/public/ to nextjs/public/ for proper static file serving. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace all WebSocket usage with REST/SSE: BrowseMode: - Remove useWebSocket import and hook call - Remove disconnect effect and error handling effect - All file operations already use REST API hooks PairWritingMode: - Remove sendMessage, lastMessage, connectionStatus props - Use useChat hook for quick/advisory actions via SSE - Discussion component now uses transport="sse" PairWritingEditor: - Remove sendMessage and lastMessage props - Delegate quick actions to parent via callback Chat API fixes: - Send vault info (vaultPath, vaultName) in request instead of fetching - Initialize SDK provider in controller singleton - Remove non-existent /api/vaults/:vaultId fetch Config endpoints: - Add REST endpoints for extraction prompt and card generator - Components now fetch their own data via REST Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Return immediately after receiving 'result' event (terminal event) to avoid errors when SDK process exits - Increase SSE stream close delay and track closing state to prevent duplicate close attempts The SDK subprocess may exit after completing a response, which throws an error if we try to continue iterating the event stream. Since 'result' is the terminal event, we can safely return at that point. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The SDK event flow is: stream_event* → assistant → result - stream_event: partial content deltas (for real-time streaming) - assistant: complete message with all content blocks - result: statistics and terminal signal Previously we only processed stream_event deltas and missed the 'assistant' event entirely. Now we use the assistant event's complete content as the authoritative source, ensuring no content is lost. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Strips out useWebSocket hook and its tests. Discussion component now uses useChat (SSE-based) instead of direct WebSocket messaging. Session streamer patched to emit content missed by stream_events. Components updated to use REST API client imports. Known issues remain. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e-commit Phase 1 - Fix ESLint + TypeScript config: - Create nextjs/eslint.config.mjs (flat config, ignores .next/, disables type-checked rules in test files) - Exclude test files from nextjs/tsconfig.json (bun:test unresolvable) - Remove unnecessary type assertion in session-streamer.ts - Update nextjs lint script for flat config format Phase 2 - Remove dead code: - Strip all Hono REST routes from backend/src/server.ts (now in Next.js) - Create nextjs/instrumentation.ts for scheduler init (extraction + cards) - Update launch.sh and systemd service to run Next.js instead of Hono - Delete 12 dead backend test files for removed Hono routes Phase 3 - Fix remaining lint/type errors: - Fix test imports from "../@/lib/api/types" to "@/lib/api/types" - Create nextjs/test-helpers.ts for test utilities - Remove debug console.log from useCapture, useHome, reducer - Fix floating promise, unnecessary assertions, unused variables - Update CLAUDE.md to reflect Next.js + SSE architecture Pre-commit hook now passes: backend typecheck/lint/tests, nextjs typecheck/lint/build, shared typecheck/lint all green. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Session resume was silently dropping message history because session_ready was only emitted for new sessions. On resume, the frontend received no previous messages and showed an empty chat. Now both new and resumed sessions emit session_ready. Resumed sessions include the full conversation history loaded from session metadata. Slash commands are also sent on resume (previously only on new sessions). Also fix Next.js dev startup error where webpack followed the instrumentation.ts import chain into cron's child_process dependency. Uses require() with variable paths to stay opaque to webpack's static analysis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the browser tab went to background or the user navigated away, the SSE stream would close but the controller kept emitting events. Each event hit the closed stream controller and logged a full stack trace, repeating for every remaining chunk. Add cancel() handler on ReadableStream to unsubscribe on client disconnect. First write failure now also triggers cleanup instead of just logging, preventing the error cascade. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ev mode Two issues fixed: 1. useChat.ts: Translate prompt_pending SSE events to the tool_permission_request / ask_user_question_request types that Discussion components expect. The controller emits prompt_pending (with nested prompt object) but the frontend was built for the old WebSocket protocol event names. 2. controller.ts: Move singleton to globalThis so it survives webpack HMR in Next.js dev mode. Without this, module reloading creates a fresh controller with no session state, causing "Session mismatch" 409 errors when resolving pending questions or permissions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two issues: 1. daily-prep/today route was never ported from Hono to Next.js, causing 404 on the Ground tab. 2. The gitignore pattern `vaults/` matched `nextjs/app/api/vaults/` too, preventing all vault-scoped API routes from being tracked. Changed to `/vaults/` to only match the top-level data directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
POST /directories and DELETE /directories/:path were in the Hono backend but never ported to Next.js, breaking file browser directory operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Directory delete now fetches real contents from the API for the confirmation preview instead of showing empty placeholder. Removed the archive context menu action from the file browser (just noise, users can move directories manually). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The health system (HealthCollector, HealthPanel, useHealth, health schemas, health API routes) was infrastructure built for WebSocket-based health reporting but never wired to actual health checks. No production code ever called .report() on the collector. Remove the entire system across all three workspaces (~2300 lines deleted). Also replace require() with dynamic import() using webpackIgnore magic comments in instrumentation.ts to eliminate the webpack "Critical dependency" build warning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All Next.js test files were still using MockWebSocket infrastructure from the pre-migration Hono backend. Since the app now uses REST API calls and SSE streaming, the tests needed to match production behavior. - Add happy-dom setup (bunfig.toml, test-setup.ts) for DOM environment - Remove MockWebSocket class and WS message assertions from 12 test files - Rewrite Discussion.test.tsx for SSE: mock fetch + createSSEResponse pattern - Rewrite VaultSelect.test.tsx for REST: POST /api/vaults/:id/sessions - Fix storage key mismatch (was "memory-loop-vault-id", actually "memory-loop:vaultId") - Inject slash commands after vault selection (SELECT_VAULT resets state) 1944 tests passing, 0 failures, 91.24% line coverage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…race Resume button was broken: it set pendingSessionId in context but nobody loaded the conversation history. Now RecentActivity calls the same POST /api/vaults/:vaultId/sessions endpoint that VaultSelect uses on login, populating messages and session ID before switching to Discussion. Discussion captures the pending session ID on mount and passes it to useChat as initialSessionId so subsequent messages resume correctly instead of creating new sessions. Also fixed SDK initialization race where API routes using the SDK (inspiration, vault setup, card/extraction triggers) would fail with SdkNotInitializedError if accessed before the first chat. Made initializeSdkProvider() idempotent and added ensureSdk() utility for routes that need the SDK independently of the chat controller. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The TLS system in backend/server.ts was built for Hono's Bun.serve() and became unreachable when production moved to `next start`. Scripts and docs were setting TLS env vars that nothing consumed. - Remove TLS_CERT/TLS_KEY/TLS_PASSPHRASE from scripts, service, .env - Rename HOST to HOSTNAME (what Next.js actually reads) - Add NODE_ENV=production to launch.sh and service file - Rewrite https-setup.md for reverse proxy (nginx, caddy) - Use bun run --cwd instead of cd in launch.sh Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Next.js migration is complete. The frontend uses REST+SSE exclusively, making the entire Hono/WebSocket layer dead code. This removes ~7,700 lines across 25 deleted files: websocket-handler, Hono server entry points, all Hono routes (replaced by Next.js API routes), middleware, and WebSocket-only handlers (pair-writing, memory, card-generator). Backend is now a pure library with no HTTP server of its own. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PairWritingMode and Discussion each created their own useChat instance, so Quick/Advisory actions sent from PairWritingMode never appeared in Discussion's conversation. Fix by adding a sendMessageRef prop to Discussion that exposes its sendChatMessage function. PairWritingMode passes a ref and routes all actions through it, ensuring both user messages and AI responses flow through Discussion's pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On session resume, the backend emits session_ready with stored message history before appending the new user message. The frontend was blindly replacing its messages array with this stale history, wiping the user message that was just added locally. Now only applies server history when local state is empty (initial resume), not during active send. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The app-version was showing "0.1.0" (package.json fallback) instead of the git commit hash. Inject NEXT_PUBLIC_APP_VERSION from git rev-parse at build time via next.config.ts, matching the original behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CI referenced the deleted frontend/ workspace. Updated to nextjs/ for test runs and coverage uploads. Added lcov coverage reporter to nextjs bunfig.toml. Included nextjs in root test and test:coverage scripts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
rjroy
added a commit
that referenced
this pull request
Feb 6, 2026
All .lore/ reference docs, README, and copilot-instructions still referenced the pre-migration Hono/WebSocket architecture. This updates every stale reference to reflect the current Next.js App Router + SSE stack (PR #458). Changes across 55 files: - Rewrite all frontend/src/ paths to nextjs/ - Replace WebSocket protocol sections with REST/SSE endpoints - Remove references to deleted files (websocket-handler.ts, Hono handlers, server.ts) - Archive 12 outdated .lore/ documents (WebSocket-era designs, diagrams, specs, research) - Rename 3 auto-generated plan files to descriptive names - Delete unused test-vault-widgets fixture (15 files) - Update lore-agents registry with 4 new agents - Fix frontmatter statuses and tags across 10 documents Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rjroy
added a commit
that referenced
this pull request
Feb 6, 2026
All .lore/ reference docs, README, and copilot-instructions still referenced the pre-migration Hono/WebSocket architecture. This updates every stale reference to reflect the current Next.js App Router + SSE stack (PR #458). Changes across 55 files: - Rewrite all frontend/src/ paths to nextjs/ - Replace WebSocket protocol sections with REST/SSE endpoints - Remove references to deleted files (websocket-handler.ts, Hono handlers, server.ts) - Archive 12 outdated .lore/ documents (WebSocket-era designs, diagrams, specs, research) - Rename 3 auto-generated plan files to descriptive names - Delete unused test-vault-widgets fixture (15 files) - Update lore-agents registry with 4 new agents - Fix frontmatter statuses and tags across 10 documents Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
app/api/)What changed
Architecture: The backend workspace is now a library (domain logic only, no HTTP server). Next.js API routes import directly from it. Communication is REST for stateless operations, SSE (POST
/api/chat) for streaming AI responses. The frontend, API, and scheduled tasks all run in one Next.js process.Deleted: Hono server (
server.ts,index.ts), WebSocket handler (839 lines), all Hono routes (11 files), Hono middleware, WebSocket-only handlers (pair-writing, memory, card-generator), health collector, Vite config, old frontend entry point. ~25 files removed from backend alone.Added: Next.js app shell (
app/layout.tsx,app/page.tsx), 40+ API route handlers, SSE streaming infrastructure (lib/sse.ts,lib/controller.ts),useChathook (SSE client),instrumentation.tsfor scheduled tasks, ActiveSessionController + SessionStreamer for SDK orchestration.Fixed (this session):
sendMessageRefpropsession_readyno longer overwrites locally-added user messages on resumeTest plan
🤖 Generated with Claude Code