Skip to content

Conversation

@eenlars
Copy link
Owner

@eenlars eenlars commented Feb 5, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Standalone Mode: New self-contained local development environment that operates without external dependencies.
    • Local Workspace Management: Create and manage workspaces directly within the application.
    • Enhanced error handling for workspace conflicts with user-friendly messages.
  • Documentation

    • Updated Getting Started guide with mode selection and setup instructions.
  • Tests

    • Expanded E2E test coverage for site limits and workspace operations.

eenlars and others added 3 commits February 5, 2026 22:21
Enables running Claude Bridge locally without Supabase/Redis:
- Add BRIDGE_ENV=standalone mode
- Auto-login with any credentials (no database)
- Local workspace management (~/.claude-bridge/workspaces/)
- In-memory session store (no Redis)
- Supabase clients throw in standalone mode
- Setup script: bun run setup:standalone

Limitations: single user, no persistence, no OAuth integrations
- Add security comments for weak session value (intentional for local dev)
- Add memory leak warning for in-memory session store
- Document why process.env.BRIDGE_ENV is used at module scope
- Document why require() is used instead of await import()
- Handle null return from getRedisUrl() in standalone mode
- Add null checks in stream-buffer.ts for Redis operations
- Skip Redis/Supabase health checks in standalone mode
- Update GETTING_STARTED.md with standalone mode documentation
- Add tests for /api/workspaces/local endpoint (15 tests)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused @ts-expect-error in alive-tagger jsx-dev-runtime
- Fix circular import in Sandbox.tsx (macOS case-insensitive ./sandbox vs ./Sandbox)
- Fix redis nullable type in stream-buffer test
- Remove unused imports (fetcher, useQueryClient)
- Fix import type style (React, UseQueryResult)
- Remove unused variables in workspace local test
- Fix hardcoded bun paths in hooks/scripts for macOS compatibility

Co-Authored-By: Lars <lars@eenlars.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

Warning

Rate limit exceeded

@eenlars has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 7 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This pull request introduces standalone mode for Claude Bridge, allowing the application to run as a self-contained environment without requiring external services like Supabase or Redis. The changes add environment-based branching throughout the codebase to detect and handle standalone mode, while maintaining backward compatibility with existing deployments.

Changes

Cohort / File(s) Summary
Standalone Health Checks
apps/web/app/api/health/route.ts
Added lazy Redis initialization guard and standalone-mode early returns for health checks, returning connected status with standalone mode details instead of performing real health checks when infrastructure unavailable.
Authentication & Session Management
apps/web/app/api/login/route.ts, apps/web/features/auth/lib/auth.ts, apps/web/features/auth/lib/sessionStore.ts
Introduced standalone login flow using predefined test user, bypassing standard authentication. Refactored sessionStore to use conditional factory pattern: in-memory store for standalone mode, Supabase-backed store for production. Added STANDALONE import from shared config.
Workspace Management
apps/web/app/api/workspaces/local/route.ts, apps/web/app/api/workspaces/local/__tests__/route.test.ts, apps/web/features/workspace/lib/standalone-workspace.ts, apps/web/features/chat/lib/workspaceRetriever.ts
New local workspaces API with GET (list) and POST (create) endpoints for standalone mode. Comprehensive test suite validating authentication, path traversal protection, duplicate handling, and happy paths. New standalone-workspace utility module providing workspace directory management, existence checks, and creation with validation.
Stream Buffering & Redis
apps/web/lib/stream/stream-buffer.ts, packages/redis/src/index.ts, packages/env/src/server.ts
Added graceful degradation when Redis unavailable: lazy initialization guard, new isStreamBufferingAvailable() helper, and early no-op returns in buffering functions. Modified createRedisClient to return null in standalone mode. Updated getRedisUrl() to return string | null and null for standalone.
Supabase Integration Guards
apps/web/lib/supabase/app.ts, apps/web/lib/supabase/iam.ts
Added explicit standalone mode checks that throw descriptive errors, preventing Supabase client instantiation when running in standalone mode.
Configuration & Environment
packages/env/src/schema.ts, packages/shared/src/config.ts, packages/shared/src/index.ts, package.json, scripts/setup-standalone.sh
Added BRIDGE_ENV environment variable (with values: local, dev, staging, production, standalone) to schema and runtime mapping. Introduced STANDALONE constant with workspace defaults, test user credentials, and session value. Added setup:standalone npm script and Bash setup script for initializing standalone environment with default workspace directory and .env.local configuration.
Error Handling
apps/web/lib/error-codes.ts
Added WORKSPACE_EXISTS error code with message formatting support, enabling proper error reporting when workspace creation conflicts occur.
Documentation
docs/GETTING_STARTED.md
Reorganized getting started guide introducing mode-based setup (Standalone vs Local) with separate sections, environment variable tables, and limitations. Contains unresolved merge conflict markers (<<<<<<< HEAD, =======, >>>>>>> 426f8ac) indicating incomplete merge.
Conversation Architecture Proposal
docs/proposals/conversation-tree-architecture.md
Added extensive proposal documenting immutable tree-based conversation model with core operations (addNode, navigateToSibling, editMessage, prepareForRegeneration) enabling branching, editing, regeneration, and time-travel debugging of conversations.
E2E Test Updates
apps/web/e2e-tests/site-limits.spec.ts, apps/web/e2e-tests/utilities-validation.spec.ts
Re-enabled Site Limits test suite with expanded deployment quota and limit validation. Added detailed assertions for error details structure and user-friendly messages. Minor reformatting of test skip modifiers with no functional changes.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant LoginAPI as /api/login
    participant StandaloneAuth as Standalone Auth
    participant SessionStore as Session Store
    participant Response

    Client->>LoginAPI: POST /api/login
    LoginAPI->>LoginAPI: Check BRIDGE_ENV
    alt Standalone Mode
        LoginAPI->>StandaloneAuth: Load standalone utilities
        StandaloneAuth->>StandaloneAuth: Ensure default workspace exists
        StandaloneAuth->>SessionStore: Set session with TEST_USER.ID
        SessionStore->>SessionStore: Store in-memory
        LoginAPI->>Response: Return userId + workspaces
    else Production/Local Mode
        LoginAPI->>LoginAPI: Standard authentication flow
        LoginAPI->>Response: Return user session
    end
    Response->>Client: HTTP 200 with session
Loading
sequenceDiagram
    participant Client
    participant WorkspaceAPI as /api/workspaces/local
    participant SessionValidation as Session Check
    participant WorkspaceUtils as Workspace Utilities
    participant FileSystem as File System

    Client->>WorkspaceAPI: GET/POST with session cookie
    WorkspaceAPI->>WorkspaceAPI: Check BRIDGE_ENV standalone
    WorkspaceAPI->>SessionValidation: Verify session cookie
    SessionValidation->>SessionValidation: Compare against SESSION_VALUE
    alt Valid Session
        alt GET - List Workspaces
            WorkspaceAPI->>WorkspaceUtils: getStandaloneWorkspaces()
            WorkspaceUtils->>FileSystem: Read workspace directory
            FileSystem->>WorkspaceUtils: Return workspace names
            WorkspaceUtils->>WorkspaceAPI: Return list with basePath
        else POST - Create Workspace
            WorkspaceAPI->>WorkspaceAPI: Validate input (Zod schema)
            WorkspaceAPI->>WorkspaceUtils: Check workspace exists
            alt Not Exists
                WorkspaceUtils->>FileSystem: Create workspace directory
                FileSystem->>WorkspaceUtils: Success
                WorkspaceUtils->>WorkspaceAPI: Return workspace info
            else Already Exists
                WorkspaceAPI->>WorkspaceAPI: Return WORKSPACE_EXISTS error (409)
            end
        end
    else Invalid/Missing Session
        WorkspaceAPI->>WorkspaceAPI: Return 401 Unauthorized
    end
    WorkspaceAPI->>Client: HTTP response with requestId
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly Related PRs

  • Improve stream buffering and reconnection #3: Overlaps on stream-buffer modifications including lazy Redis initialization, isStreamBufferingAvailable() helper, and Redis client handling for standalone degradation.
  • Worktrees #12: Modifies the same workspaceRetriever.ts file; main PR adds standalone-mode workspace resolution while concurrent changes involve async refactoring and worktree resolution.

Poem

🐰 A rabbit hops through modes so free,
Standalone now—no DB!
With local workspaces sprouting bright,
And sessions kept in memory's light,
Bridge now runs both ways, what delight! 🌱

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat/standalone local mode' directly reflects the main objective of the pull request, which introduces comprehensive standalone mode functionality across multiple components.
Docstring Coverage ✅ Passed Docstring coverage is 81.58% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/standalone-local-mode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

Type Check Failed

Please run bun run type-check locally and fix all TypeScript errors before pushing.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
docs/GETTING_STARTED.md (2)

119-125: ⚠️ Potential issue | 🟡 Minor

Align the local API key variable name.

The Local Mode config uses ANTH_API_SECRET, but the later table says ANTHROPIC_API_KEY. Pick one name and use it consistently to avoid misconfiguration.

✅ Proposed fix (use ANTHROPIC_API_KEY consistently)
-ANTH_API_SECRET=sk-ant-your-key-here
+ANTHROPIC_API_KEY=sk-ant-your-key-here

Also applies to: 169-174


132-136: ⚠️ Potential issue | 🟡 Minor

Local dev command conflicts with documented setup.

Local Mode says to start with bun run web, but the documented local setup uses bun run dev. Please align these steps so onboarding matches the actual workflow. Based on learnings: Local development setup: install dependencies with bun install, run bun run setup, add .env.local with required keys, then run bun run dev.

🔧 Suggested change
-```bash
-bun run web
-```
+```bash
+bun run dev
+```
apps/web/lib/stream/stream-buffer.ts (1)

421-439: ⚠️ Potential issue | 🔴 Critical

Missing null check for Redis in ackStreamCursor will cause runtime error.

All other buffer operations have early returns when redis is null, but ackStreamCursor attempts to call redis.eval() without checking. This will throw a TypeError in standalone mode when Redis is unavailable.

🐛 Proposed fix
 export async function ackStreamCursor(
   requestId: string,
   userId: string,
   lastSeenSeq: number,
 ): Promise<{ lastReadSeq: number } | null> {
   const redis = getRedis()
+  if (!redis) return null // Standalone mode
+
   const key = `${BUFFER_KEY_PREFIX}${requestId}`

   const result = await redis.eval(ACK_SCRIPT, 1, key, userId, lastSeenSeq.toString())
apps/web/app/api/health/route.ts (1)

99-101: ⚠️ Potential issue | 🟡 Minor

Reset function doesn't reset the initialization flag.

_resetHealthCheckRedis() sets healthCheckRedis to null but doesn't reset healthCheckRedisInitialized. After calling this function in tests, subsequent calls to getHealthCheckRedis() will return null without attempting to create a new client because the flag remains true.

🐛 Proposed fix
 // For testing: reset singleton
 export function _resetHealthCheckRedis() {
   healthCheckRedis = null
+  healthCheckRedisInitialized = false
 }
🤖 Fix all issues with AI agents
In `@apps/web/e2e-tests/site-limits.spec.ts`:
- Around line 133-190: The test "deployment succeeds when quota is increased"
currently skips whenever setQuotaResponse.ok() or increaseResponse.ok() are
false, which hides server errors; change the logic in the handlers for
setQuotaResponse and increaseResponse so you only call test.skip() when
response.status() === 404, and otherwise assert the call succeeded (e.g.,
expect(setQuotaResponse.ok()).toBe(true) /
expect(increaseResponse.ok()).toBe(true)) to fail the test on 500/401 errors;
update the checks around setQuotaResponse and increaseResponse in this test
function to reflect this behavior.
- Around line 192-231: The quota setup may silently fail and leave stale state;
after calling authenticatedPage.request.post("/api/test/set-quota") and handling
the 404 skip branch, add an explicit assertion that setQuotaResponse is
successful (e.g. expect(setQuotaResponse.ok()).toBe(true) or check status ===
200) before proceeding to deploy; reference setQuotaResponse and the
authenticatedPage.request.post("/api/test/set-quota") call in the test "error
message includes currentCount from user's actual site count" so the test fails
fast when quota setup didn't actually apply.
- Line 27: The test suite is permanently disabled by test.describe.skip; change
the suite header so it only skips when appropriate—either replace
test.describe.skip("Site Limits", ...) with test.describe("Site Limits", ...) to
enable the suite (the individual tests already handle 404 skips) or wrap the
suite in a conditional that checks an explicit env flag (e.g.,
process.env.RUN_SITE_LIMITS or a helper like shouldRunSiteLimits()) before
calling test.describe, ensuring the suite runs only when intended.
- Around line 89-131: The test currently skips only on a 404 but does not assert
that the /api/test/set-quota call actually succeeded, allowing false positives;
after the existing 404 skip guard add an explicit assertion that
setQuotaResponse.ok() is true (e.g. expect(setQuotaResponse.ok()).toBeTruthy())
so the test fails when the quota setup returns any non‑success response;
reference the setQuotaResponse variable and the /api/test/set-quota POST call
and keep the existing test.skip behavior for 404s.

In `@apps/web/e2e-tests/utilities-validation.spec.ts`:
- Around line 45-47: Replace presence checks that use toBeVisible with
toBeAttached for DOM-only assertions: change await
expect(authenticatedPage.locator(TEST_SELECTORS.workspaceReady)).toBeVisible({
timeout: 2000 }) and the similar lines for TEST_SELECTORS.messageInput and
TEST_SELECTORS.sendButton to use toBeAttached with the same timeouts; ensure the
longest timeout (workspaceReady) is awaited first, followed by the shorter
presence checks (messageInput, sendButton). Also apply the same change for the
analogous assertions referenced at lines 82-83 (use toBeAttached there as well).

In `@apps/web/features/chat/lib/workspaceRetriever.ts`:
- Around line 115-149: The suggestion message is hardcoded to
"~/.claude-bridge/workspaces/" which can be wrong when WORKSPACE_BASE is
overridden; update the error details returned in the standalone block (where
standaloneWorkspaceExists and getStandaloneWorkspacePath are used) to compute
the actual base path (use getStandaloneWorkspacePath or
process.env.WORKSPACE_BASE) and include that dynamic path in the suggestion
instead of the hardcoded string so NextResponse.json
(ErrorCodes.WORKSPACE_NOT_FOUND) returns the real create-directory hint for the
current configuration.

In `@apps/web/features/workspace/lib/standalone-workspace.ts`:
- Around line 39-42: getStandaloneWorkspacePath currently builds a path from an
unvalidated name which can allow path traversal; update
getStandaloneWorkspacePath to validate the resolved path by calling
getStandaloneWorkspaceBase() then joining with name and "user", then use
isPathWithinWorkspace(resolvedPath, base) (or a similar helper) and throw a
"Path traversal attack detected" error if validation fails; this ensures any
callers (e.g., workspaceRetriever.ts) can't pass malicious names and mirrors the
checks performed in createStandaloneWorkspace.

In `@docs/GETTING_STARTED.md`:
- Around line 156-175: Remove the leftover git merge conflict markers (<<<<<<<,
=======, >>>>>>>) and retain the intended content by keeping both the
"Standalone Mode" and "Local Mode" sections with their tables; ensure the
duplicated separators are gone and the tables are formatted correctly so the
document renders cleanly (look for the conflict block surrounding the
"Standalone Mode" and "Local Mode" headings and delete the marker lines only).

In `@docs/proposals/conversation-tree-architecture.md`:
- Line 401: The recommendation line contains a project name typo: replace the
incorrect "alive-brug" string in the paragraph starting "Recommendation: Adopt
this as the core conversation model for alive-brug." with the correct
project/repo name used in this PR (e.g., the canonical repo name), ensuring the
updated sentence reads "Recommendation: Adopt this as the core conversation
model for <correct-project-name>." Search for the exact "alive-brug" token in
conversation-tree-architecture.md and update it wherever it appears to the
correct project name to avoid confusion.
- Line 4: The document currently embeds a local absolute path
"/Users/here/CODE_FOLDER/mini-projects/zukofrontend/src/lib/conversation/tree.ts"
which leaks developer environment details; replace that string in
docs/proposals/conversation-tree-architecture.md with a repo-relative path like
"src/lib/conversation/tree.ts" or a generic reference such as
"src/lib/conversation/tree.ts (repository path)" so the doc remains portable and
non-identifying.
🧹 Nitpick comments (5)
docs/GETTING_STARTED.md (1)

29-175: Update CLAUDE.md to reflect the new mode-based workflow.

This PR introduces a new standalone/local onboarding flow and environment variables; the template guideline requires updating CLAUDE.md for significant workflow/architecture changes. Based on learnings: Update CLAUDE.md file whenever creating a new site from this template, making significant architectural changes, adding new frameworks/libraries/major features, or changing file structure/development workflow.

scripts/setup-standalone.sh (1)

10-10: Unused variable RED can be removed.

The RED color variable is defined but never used in the script. Consider removing it or using it for error messages.

🧹 Remove unused variable
 # Colors for output
-RED='\033[0;31m'
 GREEN='\033[0;32m'
 YELLOW='\033[1;33m'
 BLUE='\033[0;34m'
 NC='\033[0m' # No Color
apps/web/app/api/login/route.ts (1)

44-62: Inconsistent environment variable access pattern.

Line 45 uses process.env.BRIDGE_ENV directly while line 65 uses env.STREAM_ENV from the imported @webalive/env/server. For consistency and to benefit from any validation/typing provided by the env module, consider using the env object consistently.

Additionally, ensureDefaultWorkspace() is called without error handling. If workspace creation fails, the login will proceed with potentially no workspaces available.

♻️ Suggested improvements
-  // Standalone mode - auto-login with any credentials (for local development)
-  if (process.env.BRIDGE_ENV === "standalone") {
+  // Standalone mode - auto-login with any credentials (for local development)
+  if (env.BRIDGE_ENV === "standalone") {
     // Import workspace utilities
     const { getStandaloneWorkspaces, ensureDefaultWorkspace } = await import(
       "@/features/workspace/lib/standalone-workspace"
     )
 
     // Ensure at least one workspace exists
-    ensureDefaultWorkspace()
+    try {
+      ensureDefaultWorkspace()
+    } catch (error) {
+      console.error("[Login] Failed to ensure default workspace:", error)
+    }
apps/web/app/api/health/route.ts (1)

111-122: Inconsistent standalone mode detection approaches.

The Redis check relies on redis being null (implicit detection via getRedisUrl() returning null), while the database check explicitly checks process.env.BRIDGE_ENV === "standalone". This inconsistency could lead to subtle bugs if these conditions diverge.

Consider using explicit BRIDGE_ENV checks in both places for clarity, or document why the approaches differ.

Also applies to: 158-168

apps/web/app/api/workspaces/local/route.ts (1)

46-60: Duplicated session validation logic.

The session cookie validation code is identical in both GET and POST handlers. Consider extracting this to a shared helper function to reduce duplication and ensure consistent behavior.

♻️ Proposed refactor
// Add helper at top of file after imports
async function validateStandaloneSession(requestId: string): Promise<NextResponse | null> {
  const jar = await cookies()
  const sessionCookie = jar.get(COOKIE_NAMES.SESSION)

  if (!sessionCookie?.value || sessionCookie.value !== STANDALONE.SESSION_VALUE) {
    return NextResponse.json(
      {
        ok: false,
        error: ErrorCodes.NO_SESSION,
        message: getErrorMessage(ErrorCodes.NO_SESSION),
        requestId,
      },
      { status: 401 },
    )
  }
  return null // Valid session
}

// Then in handlers:
const authError = await validateStandaloneSession(requestId)
if (authError) return authError

Also applies to: 98-112

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

Type Check Failed

Please run bun run type-check locally and fix all TypeScript errors before pushing.

- Add path traversal validation to getStandaloneWorkspacePath
- Use dynamic workspace base path in error suggestions
- Remove merge conflict markers from GETTING_STARTED.md
- Fix stream-buffer test Redis null type
- Fix local path leak in conversation-tree-architecture.md
- Format e2e test files
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.

1 participant