Skip to content

refactor(auth): eliminate @/lib/auth barrels — direct imports + functional core (#1393)#1975

Open
2witstudios wants to merge 36 commits into
masterfrom
pu/refactorauth-eliminate-barrel-indexts-direct-imports-per-module
Open

refactor(auth): eliminate @/lib/auth barrels — direct imports + functional core (#1393)#1975
2witstudios wants to merge 36 commits into
masterfrom
pu/refactorauth-eliminate-barrel-indexts-direct-imports-per-module

Conversation

@2witstudios

@2witstudios 2witstudios commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Closes #1393.

What & why

apps/web/src/lib/auth/index.ts was an 836-line barrel (~548 importers) mixing an inline token-auth engine with re-exports across session/CSRF/device/cookies/permissions. Importing authenticateRequestWithOptions transitively loaded @pagespace/db, the session service, encryption, and more — the root cause of the pnpm→bun build failure where a client component pulled server-only deps. apps/admin had a parallel 4-line shim.

This deletes both barrels, rewrites every importer to a direct per-module path, and splits the web engine into an Eric-Elliott functional core + imperative shell so pure consumers (types, guards, scope decisions) never drag the db graph.

New web modules

Module Kind
auth-types.ts pure types, zero runtime deps
auth-core.ts pure: guards, MCP scope decisions, new deterministic decideMcpAuth / decideOAuthAuth (clock injected)
request-auth.ts I/O shell — the only module with @pagespace/db: db read → pure decide*() → db write

Mechanics

  • scripts/migrate-auth-barrel.mjs (ts-morph) rewrote 593 web + 23 admin files, splitting every vi.mock('@/lib/auth', factory) into one mock per target module (since vi.mock replaces the whole module, mixed/override factories can't stay whole).
  • Collapsed the auth-helpers double-hop (getClientIP@pagespace/lib/security/client-ip, url helpers → @/lib/auth/url-utils); auth-helpers re-exports nothing it doesn't own.
  • Rewrote token-prefixes.test.ts (barrel-integrity → engine single-source-of-truth).

Verification (all green)

  • rg @/lib/auth barrel imports (any quote/relative/dynamic form) = 0
  • web + admin typecheck: 0 errors
  • full web suite: only 4 pre-existing failures (version-sdk-contract, activity-tools, admin-role-version, grouping — all codemod-untouched, auth-unrelated, confirmed failing at master via stash-baseline)
  • admin suite: 93/93
  • new auth-core-decisions.test.ts: 16/16 — every fail-closed branch of decideMcpAuth/decideOAuthAuth
  • web + admin builds: exit 0 (Middleware edge bundles OK — the exact refactor(auth): eliminate barrel index.ts — direct imports per module #1393 root cause)

Notes for reviewers

  • Security-critical control flow was reshaped (validate* → pure decide* + thin shell). Behavior is preserved byte-for-byte — verified by the 16 branch tests + the untouched full suite.
  • Adds ts-morph as a root devDependency for the codemod.

CI status (for the merger)

Green: Lint & TypeScript, all Security checks, and coverage for every package except apps/web.

Red (non-required): ci / Unit Tests — the apps/web suite OOMs during v8 coverage. This is a real regression from this PR: splitting each vi.mock('@/lib/auth', …) into per-module mocks grew the executed module graph enough that the coverage run's memory exceeds the 16 GB runner (master, with the barrel mock, stays under). It is not a test failure — all 12,475 web tests pass.

What was done: the root cause is vitest's default forks pool leaking module graphs unboundedly; this branch switches apps/web to pool: 'threads', which bounds memory from unbounded to ~2.5 GB and lets every test pass. The residual is that one thread worker's v8-coverage collection still exceeds the worker's default heap, and vitest 2.1.9 exposes no way to raise the thread-worker heap (verified in createThreadsPool: no resourceLimits; worker_threads reject --max-old-space-size in execArgv; NODE_OPTIONS isn't inherited). Exhaustively ruled out: heap sizes 4–14 GB, lean reporters, all:false, processingConcurrency, fork caps, sharding to 10, 8 threads, --no-file-parallelism, vmThreads+memoryLimit (breaks tests), and istanbul (breaks ~170 tests).

To make it fully green, pick one (all outside this PR's scope):

  1. Merge as-is (recommended) — the ci / Unit Tests check is non-required (the "Master Protection" ruleset requires zero status checks), and web correctness is fully gated by the 12,475 passing tests. Track the web-coverage memory as separate tech debt.
  2. Use a larger CI runner for the web coverage job (more RAM raises Node's default worker heap) — reliable but an infra/billing change.
  3. A vitest 3.x upgrade is not a guaranteed fix: its config still exposes worker-memory control only for vmThreads/vmForks (which break ~170 of these tests under their VM context), not the regular threads pool that passes cleanly. An upgrade would be a monorepo-wide, uncertain effort — not worth bundling here.
  • CodeQL — the "6 new high" alerts are pre-existing (identical set on master; this PR only rewrites import lines in the flagged files).

🤖 Generated with Claude Code

…ional core (#1393)

Delete both `@/lib/auth` barrels (apps/web 836-line barrel + 548 importers;
apps/admin 4-line shim + 17 importers) and rewrite every importer to a direct
per-module path. The web token-auth engine is split into an Eric-Elliott
functional core + imperative shell so importing a pure guard/type no longer
drags `@pagespace/db` and the session service into the graph — the root cause
of the pnpm→bun client-imports-server build failure.

New web modules:
- auth-types.ts   — pure types, zero runtime deps
- auth-core.ts    — pure: guards, MCP scope decisions, and new deterministic
                    decideMcpAuth / decideOAuthAuth (clock injected)
- request-auth.ts — I/O shell: db read → pure decide*() → db write

Mechanics:
- scripts/migrate-auth-barrel.mjs (ts-morph) rewrote 593 web + 23 admin files,
  splitting every vi.mock('@/lib/auth', factory) into one mock per target
  module (vi.mock replaces the whole module, so mixed/override factories can't
  stay whole).
- Collapse the auth-helpers double-hop (getClientIP → @pagespace/lib/security/
  client-ip, url helpers → @/lib/auth/url-utils); auth-helpers re-exports
  nothing it doesn't own.
- token-prefixes.test.ts rewritten (barrel-integrity → engine single-source).

Verification: rg barrel imports = 0 (any form); web+admin typecheck clean;
full web suite = only 4 pre-existing failures (auth-unrelated, confirmed at
HEAD); admin 93/93; new auth-core-decisions.test.ts 16/16 covering every
fail-closed branch; web + admin builds green (middleware edge bundles OK).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 648 files, which is 498 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 546f88dc-7cd5-4476-9a64-206bbcbc6ab0

📥 Commits

Reviewing files that changed from the base of the PR and between e184457 and aaa7417.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (648)
  • apps/admin/src/app/api/admin/alerts/route.ts
  • apps/admin/src/app/api/admin/audit-logs/__tests__/search-security.test.ts
  • apps/admin/src/app/api/admin/audit-logs/export/__tests__/pii-decrypt.test.ts
  • apps/admin/src/app/api/admin/audit-logs/export/route.ts
  • apps/admin/src/app/api/admin/audit-logs/integrity/route.ts
  • apps/admin/src/app/api/admin/audit-logs/route.ts
  • apps/admin/src/app/api/admin/billing/route.ts
  • apps/admin/src/app/api/admin/compaction/route.ts
  • apps/admin/src/app/api/admin/contact/[id]/route.ts
  • apps/admin/src/app/api/admin/contact/route.ts
  • apps/admin/src/app/api/admin/global-prompt/route.ts
  • apps/admin/src/app/api/admin/overview/route.ts
  • apps/admin/src/app/api/admin/schema/route.ts
  • apps/admin/src/app/api/admin/users/[userId]/gift-subscription/__tests__/route.pii.test.ts
  • apps/admin/src/app/api/admin/users/[userId]/gift-subscription/route.ts
  • apps/admin/src/app/api/admin/users/[userId]/role/__tests__/route.test.ts
  • apps/admin/src/app/api/admin/users/[userId]/role/route.ts
  • apps/admin/src/app/api/admin/users/[userId]/sessions/__tests__/route.test.ts
  • apps/admin/src/app/api/admin/users/[userId]/sessions/route.ts
  • apps/admin/src/app/api/admin/users/[userId]/suspend/__tests__/route.test.ts
  • apps/admin/src/app/api/admin/users/[userId]/suspend/route.ts
  • apps/admin/src/app/api/admin/users/route.ts
  • apps/admin/src/app/api/monitoring/[metric]/route.ts
  • apps/admin/src/lib/auth/index.ts
  • apps/web/src/app/api/__tests__/integrations-deployment-mode-gate.test.ts
  • apps/web/src/app/api/account/__tests__/delete-route.test.ts
  • apps/web/src/app/api/account/__tests__/get-patch-route.test.ts
  • apps/web/src/app/api/account/avatar/__tests__/route.test.ts
  • apps/web/src/app/api/account/avatar/route.ts
  • apps/web/src/app/api/account/devices/[deviceId]/__tests__/route.test.ts
  • apps/web/src/app/api/account/devices/[deviceId]/route.ts
  • apps/web/src/app/api/account/devices/__tests__/route.test.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/account/drives-status/__tests__/route.test.ts
  • apps/web/src/app/api/account/drives-status/route.ts
  • apps/web/src/app/api/account/export/__tests__/route.test.ts
  • apps/web/src/app/api/account/export/route.ts
  • apps/web/src/app/api/account/handle-drive/__tests__/route.test.ts
  • apps/web/src/app/api/account/handle-drive/route.ts
  • apps/web/src/app/api/account/oauth-grants/[grantId]/__tests__/route.test.ts
  • apps/web/src/app/api/account/oauth-grants/[grantId]/route.ts
  • apps/web/src/app/api/account/oauth-grants/__tests__/route.test.ts
  • apps/web/src/app/api/account/oauth-grants/route.ts
  • apps/web/src/app/api/account/route.ts
  • apps/web/src/app/api/account/verification-status/__tests__/route.test.ts
  • apps/web/src/app/api/account/verification-status/route.ts
  • apps/web/src/app/api/activities/[activityId]/__tests__/route.test.ts
  • apps/web/src/app/api/activities/[activityId]/rollback-to-point/__tests__/route.test.ts
  • apps/web/src/app/api/activities/[activityId]/rollback-to-point/route.ts
  • apps/web/src/app/api/activities/[activityId]/rollback/__tests__/route.test.ts
  • apps/web/src/app/api/activities/[activityId]/rollback/route.ts
  • apps/web/src/app/api/activities/[activityId]/route.ts
  • apps/web/src/app/api/activities/__tests__/manage-keys-scope.test.ts
  • apps/web/src/app/api/activities/__tests__/route.test.ts
  • apps/web/src/app/api/activities/actors/__tests__/route.test.ts
  • apps/web/src/app/api/activities/actors/route.ts
  • apps/web/src/app/api/activities/export/__tests__/route.test.ts
  • apps/web/src/app/api/activities/export/route.ts
  • apps/web/src/app/api/activities/route.ts
  • apps/web/src/app/api/activity/summary/__tests__/route.test.ts
  • apps/web/src/app/api/activity/summary/route.ts
  • apps/web/src/app/api/admin/compaction/route.ts
  • apps/web/src/app/api/admin/gdpr/__tests__/admin-gdpr-routes.test.ts
  • apps/web/src/app/api/admin/gdpr/erasure/route.ts
  • apps/web/src/app/api/admin/gdpr/pseudonymize/route.ts
  • apps/web/src/app/api/admin/gdpr/requests/route.ts
  • apps/web/src/app/api/admin/global-prompt/__tests__/route.test.ts
  • apps/web/src/app/api/admin/global-prompt/route.ts
  • apps/web/src/app/api/agents/[agentId]/integrations/[grantId]/__tests__/route.test.ts
  • apps/web/src/app/api/agents/[agentId]/integrations/[grantId]/route.ts
  • apps/web/src/app/api/agents/[agentId]/integrations/__tests__/route.test.ts
  • apps/web/src/app/api/agents/[agentId]/integrations/route.ts
  • apps/web/src/app/api/ai/abort/__tests__/route.test.ts
  • apps/web/src/app/api/ai/abort/route.ts
  • apps/web/src/app/api/ai/chat/__tests__/credit-gate.test.ts
  • apps/web/src/app/api/ai/chat/__tests__/mcp-scope-tool-filtering.test.ts
  • apps/web/src/app/api/ai/chat/__tests__/mcp-scope.test.ts
  • apps/web/src/app/api/ai/chat/__tests__/sandbox-github-suppression.test.ts
  • apps/web/src/app/api/ai/chat/__tests__/stream-socket-events.test.ts
  • apps/web/src/app/api/ai/chat/active-streams/__tests__/route.test.ts
  • apps/web/src/app/api/ai/chat/active-streams/route.ts
  • apps/web/src/app/api/ai/chat/messages/[messageId]/__tests__/route.test.ts
  • apps/web/src/app/api/ai/chat/messages/[messageId]/route.ts
  • apps/web/src/app/api/ai/chat/messages/[messageId]/undo/__tests__/route.test.ts
  • apps/web/src/app/api/ai/chat/messages/[messageId]/undo/route.ts
  • apps/web/src/app/api/ai/chat/messages/__tests__/mcp-scope.test.ts
  • apps/web/src/app/api/ai/chat/messages/__tests__/route.test.ts
  • apps/web/src/app/api/ai/chat/messages/route.ts
  • apps/web/src/app/api/ai/chat/route.ts
  • apps/web/src/app/api/ai/chat/stream-join/[messageId]/__tests__/route.test.ts
  • apps/web/src/app/api/ai/chat/stream-join/[messageId]/route.ts
  • apps/web/src/app/api/ai/global/[id]/__tests__/route.test.ts
  • apps/web/src/app/api/ai/global/[id]/messages/[messageId]/__tests__/route.test.ts
  • apps/web/src/app/api/ai/global/[id]/messages/[messageId]/route.ts
  • apps/web/src/app/api/ai/global/[id]/messages/__tests__/conversation-id-resolution.test.ts
  • apps/web/src/app/api/ai/global/[id]/messages/__tests__/credit-gate.test.ts
  • apps/web/src/app/api/ai/global/[id]/messages/__tests__/stream-socket-events.test.ts
  • apps/web/src/app/api/ai/global/[id]/messages/route.ts
  • apps/web/src/app/api/ai/global/[id]/route.ts
  • apps/web/src/app/api/ai/global/[id]/usage/__tests__/route.test.ts
  • apps/web/src/app/api/ai/global/[id]/usage/route.ts
  • apps/web/src/app/api/ai/global/__tests__/route.test.ts
  • apps/web/src/app/api/ai/global/active/__tests__/route.test.ts
  • apps/web/src/app/api/ai/global/active/route.ts
  • apps/web/src/app/api/ai/global/route.ts
  • apps/web/src/app/api/ai/lmstudio/models/__tests__/route.test.ts
  • apps/web/src/app/api/ai/lmstudio/models/route.ts
  • apps/web/src/app/api/ai/ollama/models/__tests__/route.test.ts
  • apps/web/src/app/api/ai/ollama/models/route.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/config/__tests__/route.test.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/config/route.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/conversations/[conversationId]/__tests__/route.test.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/conversations/[conversationId]/messages/[messageId]/route.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/conversations/[conversationId]/messages/__tests__/route.test.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/conversations/[conversationId]/messages/route.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/conversations/[conversationId]/route.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/conversations/__tests__/route.test.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/conversations/route.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/drives/[driveId]/__tests__/route.test.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/drives/[driveId]/route.ts
  • apps/web/src/app/api/ai/page-agents/[agentId]/drives/route.ts
  • apps/web/src/app/api/ai/page-agents/consult/__tests__/conversation-continuity.test.ts
  • apps/web/src/app/api/ai/page-agents/consult/__tests__/conversation-listing-index.test.ts
  • apps/web/src/app/api/ai/page-agents/consult/__tests__/credit-gate.test.ts
  • apps/web/src/app/api/ai/page-agents/consult/__tests__/mcp-scope-tool-filtering.test.ts
  • apps/web/src/app/api/ai/page-agents/consult/__tests__/recent-history-ordering.test.ts
  • apps/web/src/app/api/ai/page-agents/consult/__tests__/step-cap-parity.test.ts
  • apps/web/src/app/api/ai/page-agents/consult/route.ts
  • apps/web/src/app/api/ai/page-agents/create/__tests__/route.test.ts
  • apps/web/src/app/api/ai/page-agents/create/route.ts
  • apps/web/src/app/api/ai/page-agents/multi-drive/route.ts
  • apps/web/src/app/api/ai/settings/__tests__/route.test.ts
  • apps/web/src/app/api/ai/settings/image-model/__tests__/route.test.ts
  • apps/web/src/app/api/ai/settings/image-model/route.ts
  • apps/web/src/app/api/ai/settings/route.ts
  • apps/web/src/app/api/auth/__tests__/device-refresh.test.ts
  • apps/web/src/app/api/auth/__tests__/google-callback-redirect.test.ts
  • apps/web/src/app/api/auth/__tests__/logout.test.ts
  • apps/web/src/app/api/auth/__tests__/mcp-tokens.test.ts
  • apps/web/src/app/api/auth/__tests__/me.test.ts
  • apps/web/src/app/api/auth/__tests__/mobile-oauth-google-exchange.test.ts
  • apps/web/src/app/api/auth/__tests__/mobile-refresh.test.ts
  • apps/web/src/app/api/auth/__tests__/session-fixation.test.ts
  • apps/web/src/app/api/auth/apple/callback/__tests__/route.test.ts
  • apps/web/src/app/api/auth/apple/callback/route.ts
  • apps/web/src/app/api/auth/apple/native/__tests__/route.test.ts
  • apps/web/src/app/api/auth/apple/native/route.ts
  • apps/web/src/app/api/auth/apple/signin/__tests__/route.test.ts
  • apps/web/src/app/api/auth/apple/signin/route.ts
  • apps/web/src/app/api/auth/device/__tests__/refresh-deviceid.test.ts
  • apps/web/src/app/api/auth/device/refresh/__tests__/route.test.ts
  • apps/web/src/app/api/auth/device/refresh/route.ts
  • apps/web/src/app/api/auth/device/register/__tests__/route.test.ts
  • apps/web/src/app/api/auth/device/register/route.ts
  • apps/web/src/app/api/auth/google/__tests__/google-callback-redirect.test.ts
  • apps/web/src/app/api/auth/google/__tests__/one-tap.test.ts
  • apps/web/src/app/api/auth/google/__tests__/open-redirect-protection.test.ts
  • apps/web/src/app/api/auth/google/callback/__tests__/route.test.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/google/native/__tests__/route.test.ts
  • apps/web/src/app/api/auth/google/native/route.ts
  • apps/web/src/app/api/auth/google/one-tap/__tests__/route.test.ts
  • apps/web/src/app/api/auth/google/one-tap/route.ts
  • apps/web/src/app/api/auth/google/signin/__tests__/route.test.ts
  • apps/web/src/app/api/auth/google/signin/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/magic-link/__tests__/round-trip.test.ts
  • apps/web/src/app/api/auth/magic-link/send/__tests__/route.test.ts
  • apps/web/src/app/api/auth/magic-link/send/route.ts
  • apps/web/src/app/api/auth/magic-link/verify/__tests__/desktop-verify.test.ts
  • apps/web/src/app/api/auth/magic-link/verify/__tests__/route.test.ts
  • apps/web/src/app/api/auth/magic-link/verify/route.ts
  • apps/web/src/app/api/auth/mcp-tokens/[tokenId]/__tests__/manage-keys-scope.test.ts
  • apps/web/src/app/api/auth/mcp-tokens/[tokenId]/__tests__/route.test.ts
  • apps/web/src/app/api/auth/mcp-tokens/[tokenId]/route.ts
  • apps/web/src/app/api/auth/mcp-tokens/__tests__/manage-keys-scope.test.ts
  • apps/web/src/app/api/auth/mcp-tokens/__tests__/route.test.ts
  • apps/web/src/app/api/auth/mcp-tokens/route.ts
  • apps/web/src/app/api/auth/mcp-tokens/scope-guard.ts
  • apps/web/src/app/api/auth/me/route.ts
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/__tests__/route.test.ts
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts
  • apps/web/src/app/api/auth/mobile/refresh/route.ts
  • apps/web/src/app/api/auth/passkey/[passkeyId]/__tests__/route.test.ts
  • apps/web/src/app/api/auth/passkey/[passkeyId]/route.ts
  • apps/web/src/app/api/auth/passkey/__tests__/route.test.ts
  • apps/web/src/app/api/auth/passkey/authenticate/__tests__/route.test.ts
  • apps/web/src/app/api/auth/passkey/authenticate/options/__tests__/route.test.ts
  • apps/web/src/app/api/auth/passkey/authenticate/options/route.ts
  • apps/web/src/app/api/auth/passkey/authenticate/route.ts
  • apps/web/src/app/api/auth/passkey/register/__tests__/route.test.ts
  • apps/web/src/app/api/auth/passkey/register/handoff/__tests__/route.test.ts
  • apps/web/src/app/api/auth/passkey/register/handoff/route.ts
  • apps/web/src/app/api/auth/passkey/register/options/__tests__/route.test.ts
  • apps/web/src/app/api/auth/passkey/register/options/route.ts
  • apps/web/src/app/api/auth/passkey/register/route.ts
  • apps/web/src/app/api/auth/passkey/route.ts
  • apps/web/src/app/api/auth/resend-verification/__tests__/route.test.ts
  • apps/web/src/app/api/auth/resend-verification/route.ts
  • apps/web/src/app/api/auth/signup-passkey/__tests__/route.test.ts
  • apps/web/src/app/api/auth/signup-passkey/options/__tests__/route.test.ts
  • apps/web/src/app/api/auth/signup-passkey/options/route.ts
  • apps/web/src/app/api/auth/signup-passkey/route.ts
  • apps/web/src/app/api/auth/socket-token/__tests__/route.test.ts
  • apps/web/src/app/api/auth/step-up/magic-link/request/__tests__/route.test.ts
  • apps/web/src/app/api/auth/step-up/magic-link/request/route.ts
  • apps/web/src/app/api/auth/step-up/magic-link/verify/__tests__/route.test.ts
  • apps/web/src/app/api/auth/step-up/magic-link/verify/route.ts
  • apps/web/src/app/api/auth/step-up/webauthn/options/__tests__/route.test.ts
  • apps/web/src/app/api/auth/step-up/webauthn/options/route.ts
  • apps/web/src/app/api/auth/step-up/webauthn/verify/__tests__/route.test.ts
  • apps/web/src/app/api/auth/step-up/webauthn/verify/route.ts
  • apps/web/src/app/api/auth/ws-token/__tests__/route.test.ts
  • apps/web/src/app/api/auth/ws-token/route.ts
  • apps/web/src/app/api/backups/[backupId]/pages/__tests__/route.test.ts
  • apps/web/src/app/api/backups/[backupId]/pages/route.ts
  • apps/web/src/app/api/backups/route.ts
  • apps/web/src/app/api/calendar/events/[eventId]/__tests__/can-access-event-scoped-creator.test.ts
  • apps/web/src/app/api/calendar/events/[eventId]/__tests__/can-edit-event.test.ts
  • apps/web/src/app/api/calendar/events/[eventId]/__tests__/patch-agent-trigger.test.ts
  • apps/web/src/app/api/calendar/events/[eventId]/attendees/__tests__/route.test.ts
  • apps/web/src/app/api/calendar/events/[eventId]/attendees/route.ts
  • apps/web/src/app/api/calendar/events/[eventId]/drives/__tests__/route.test.ts
  • apps/web/src/app/api/calendar/events/[eventId]/drives/route.ts
  • apps/web/src/app/api/calendar/events/[eventId]/route.ts
  • apps/web/src/app/api/calendar/events/[eventId]/triggers/__tests__/route.test.ts
  • apps/web/src/app/api/calendar/events/[eventId]/triggers/route.ts
  • apps/web/src/app/api/calendar/events/__tests__/get-user-listing-scoped-personal-events.test.ts
  • apps/web/src/app/api/calendar/events/__tests__/post-agent-trigger.test.ts
  • apps/web/src/app/api/calendar/events/__tests__/post-driveless-scoped-token.test.ts
  • apps/web/src/app/api/calendar/events/route.ts
  • apps/web/src/app/api/canvas/file-view-tokens/__tests__/route.test.ts
  • apps/web/src/app/api/canvas/file-view-tokens/route.ts
  • apps/web/src/app/api/channels/[pageId]/messages/[messageId]/__tests__/route.test.ts
  • apps/web/src/app/api/channels/[pageId]/messages/[messageId]/follow/__tests__/route.test.ts
  • apps/web/src/app/api/channels/[pageId]/messages/[messageId]/follow/route.ts
  • apps/web/src/app/api/channels/[pageId]/messages/[messageId]/reactions/route.ts
  • apps/web/src/app/api/channels/[pageId]/messages/[messageId]/route.ts
  • apps/web/src/app/api/channels/[pageId]/messages/__tests__/manage-keys-scope.test.ts
  • apps/web/src/app/api/channels/[pageId]/messages/__tests__/route.test.ts
  • apps/web/src/app/api/channels/[pageId]/messages/route.ts
  • apps/web/src/app/api/channels/[pageId]/read/route.ts
  • apps/web/src/app/api/commands/[commandId]/route.ts
  • apps/web/src/app/api/commands/__tests__/command-id-route.test.ts
  • apps/web/src/app/api/commands/__tests__/route-list-edge-cases.test.ts
  • apps/web/src/app/api/commands/__tests__/route.test.ts
  • apps/web/src/app/api/commands/__tests__/suggest-route.test.ts
  • apps/web/src/app/api/commands/command-route-helpers.ts
  • apps/web/src/app/api/commands/resolve/__tests__/route-edge-cases.test.ts
  • apps/web/src/app/api/commands/resolve/__tests__/route.test.ts
  • apps/web/src/app/api/commands/resolve/route.ts
  • apps/web/src/app/api/commands/route.ts
  • apps/web/src/app/api/commands/suggest/route.ts
  • apps/web/src/app/api/connections/[connectionId]/__tests__/route.test.ts
  • apps/web/src/app/api/connections/[connectionId]/route.ts
  • apps/web/src/app/api/connections/__tests__/route.test.ts
  • apps/web/src/app/api/connections/invite/__tests__/route.test.ts
  • apps/web/src/app/api/connections/invite/route.ts
  • apps/web/src/app/api/connections/route.ts
  • apps/web/src/app/api/connections/search/__tests__/route.test.ts
  • apps/web/src/app/api/connections/search/route.ts
  • apps/web/src/app/api/contact/__tests__/route.test.ts
  • apps/web/src/app/api/contact/route.ts
  • apps/web/src/app/api/debug/chat-messages/__tests__/route.test.ts
  • apps/web/src/app/api/debug/chat-messages/route.ts
  • apps/web/src/app/api/desktop-bridge/status/route.ts
  • apps/web/src/app/api/drafts/route.ts
  • apps/web/src/app/api/drives/[driveId]/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/access/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/access/route.ts
  • apps/web/src/app/api/drives/[driveId]/agents/[agentPageId]/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/agents/[agentPageId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/agents/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/agents/members/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/agents/members/route.ts
  • apps/web/src/app/api/drives/[driveId]/agents/route.ts
  • apps/web/src/app/api/drives/[driveId]/apps/[tokenId]/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/apps/[tokenId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/apps/members/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/apps/members/route.ts
  • apps/web/src/app/api/drives/[driveId]/assignees/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/assignees/route.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/diff/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/diff/route.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/download/route.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/export/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/export/route.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/pages/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/pages/route.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/restore/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/restore/route.ts
  • apps/web/src/app/api/drives/[driveId]/backups/[backupId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/backups/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/backups/route.ts
  • apps/web/src/app/api/drives/[driveId]/backups/schedule/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/backups/schedule/route.ts
  • apps/web/src/app/api/drives/[driveId]/domains/[domainId]/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/domains/[domainId]/cert/refresh/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/domains/[domainId]/cert/refresh/route.ts
  • apps/web/src/app/api/drives/[driveId]/domains/[domainId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/domains/[domainId]/verify/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/domains/[domainId]/verify/route.ts
  • apps/web/src/app/api/drives/[driveId]/domains/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/domains/route.ts
  • apps/web/src/app/api/drives/[driveId]/history/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/history/route.ts
  • apps/web/src/app/api/drives/[driveId]/integrations/[connectionId]/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/integrations/[connectionId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/integrations/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/integrations/audit/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/integrations/audit/export/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/integrations/audit/export/route.ts
  • apps/web/src/app/api/drives/[driveId]/integrations/audit/route.ts
  • apps/web/src/app/api/drives/[driveId]/integrations/route.ts
  • apps/web/src/app/api/drives/[driveId]/members/[userId]/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/members/[userId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/members/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/members/invite/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/members/invite/route.ts
  • apps/web/src/app/api/drives/[driveId]/members/route.ts
  • apps/web/src/app/api/drives/[driveId]/pages/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/pages/route.ts
  • apps/web/src/app/api/drives/[driveId]/pending-invites/[inviteId]/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/pending-invites/[inviteId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/permissions-tree/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/permissions-tree/route.ts
  • apps/web/src/app/api/drives/[driveId]/publish-home/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/publish-home/route.ts
  • apps/web/src/app/api/drives/[driveId]/restore/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/restore/route.ts
  • apps/web/src/app/api/drives/[driveId]/roles/[roleId]/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/roles/[roleId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/roles/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/roles/reorder/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/roles/reorder/route.ts
  • apps/web/src/app/api/drives/[driveId]/roles/route.ts
  • apps/web/src/app/api/drives/[driveId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/search/glob/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/search/glob/route.ts
  • apps/web/src/app/api/drives/[driveId]/search/regex/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/search/regex/route.ts
  • apps/web/src/app/api/drives/[driveId]/share-links/[linkId]/route.ts
  • apps/web/src/app/api/drives/[driveId]/share-links/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/share-links/route.ts
  • apps/web/src/app/api/drives/[driveId]/subdomain/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/subdomain/route.ts
  • apps/web/src/app/api/drives/[driveId]/trash/__tests__/route.test.ts
  • apps/web/src/app/api/drives/[driveId]/trash/route.ts
  • apps/web/src/app/api/drives/__tests__/manage-keys-scope.test.ts
  • apps/web/src/app/api/drives/__tests__/oauth-scope.test.ts
  • apps/web/src/app/api/drives/__tests__/route.test.ts
  • apps/web/src/app/api/drives/route.ts
  • apps/web/src/app/api/feedback/__tests__/route.test.ts
  • apps/web/src/app/api/feedback/route.ts
  • apps/web/src/app/api/files/[id]/convert-to-document/__tests__/route.test.ts
  • apps/web/src/app/api/files/[id]/convert-to-document/route.ts
  • apps/web/src/app/api/files/[id]/download/__tests__/route.test.ts
  • apps/web/src/app/api/files/[id]/download/route.ts
  • apps/web/src/app/api/files/[id]/thumbnail/route.ts
  • apps/web/src/app/api/files/[id]/view/__tests__/route.test.ts
  • apps/web/src/app/api/files/[id]/view/route.ts
  • apps/web/src/app/api/inbox/__tests__/route.test.ts
  • apps/web/src/app/api/inbox/route.ts
  • apps/web/src/app/api/integrations/connections/[connectionId]/grants/route.ts
  • apps/web/src/app/api/integrations/github/repos/__tests__/route.test.ts
  • apps/web/src/app/api/integrations/github/repos/route.ts
  • apps/web/src/app/api/integrations/google-calendar/__tests__/deployment-mode-gate.test.ts
  • apps/web/src/app/api/integrations/google-calendar/calendars/route.ts
  • apps/web/src/app/api/integrations/google-calendar/connect/route.ts
  • apps/web/src/app/api/integrations/google-calendar/disconnect/route.ts
  • apps/web/src/app/api/integrations/google-calendar/settings/route.ts
  • apps/web/src/app/api/integrations/google-calendar/status/route.ts
  • apps/web/src/app/api/integrations/google-calendar/sync/route.ts
  • apps/web/src/app/api/integrations/providers/[providerId]/route.ts
  • apps/web/src/app/api/integrations/providers/available/route.ts
  • apps/web/src/app/api/integrations/providers/import-openapi/route.ts
  • apps/web/src/app/api/integrations/providers/install/route.ts
  • apps/web/src/app/api/integrations/providers/route.ts
  • apps/web/src/app/api/integrations/zoom/connect/route.ts
  • apps/web/src/app/api/integrations/zoom/disconnect/route.ts
  • apps/web/src/app/api/integrations/zoom/settings/route.ts
  • apps/web/src/app/api/integrations/zoom/status/route.ts
  • apps/web/src/app/api/integrations/zoom/triggers/[id]/route.ts
  • apps/web/src/app/api/integrations/zoom/triggers/route.ts
  • apps/web/src/app/api/link-preview/__tests__/route.test.ts
  • apps/web/src/app/api/link-preview/route.ts
  • apps/web/src/app/api/machines/agent-terminals/__tests__/route.test.ts
  • apps/web/src/app/api/machines/agent-terminals/route.ts
  • apps/web/src/app/api/machines/branches/__tests__/route.test.ts
  • apps/web/src/app/api/machines/branches/route.ts
  • apps/web/src/app/api/machines/diff/__tests__/route.test.ts
  • apps/web/src/app/api/machines/diff/route.ts
  • apps/web/src/app/api/machines/files/__tests__/route.test.ts
  • apps/web/src/app/api/machines/files/route.ts
  • apps/web/src/app/api/machines/git-blob/__tests__/route.test.ts
  • apps/web/src/app/api/machines/git-blob/route.ts
  • apps/web/src/app/api/machines/projects/__tests__/route.test.ts
  • apps/web/src/app/api/machines/projects/route.ts
  • apps/web/src/app/api/machines/settings/__tests__/route.test.ts
  • apps/web/src/app/api/machines/settings/route.ts
  • apps/web/src/app/api/mcp/documents/__tests__/route.line-numbering.test.ts
  • apps/web/src/app/api/mcp/documents/__tests__/route.read-parity.test.ts
  • apps/web/src/app/api/mcp/documents/__tests__/route.security.test.ts
  • apps/web/src/app/api/mcp/documents/__tests__/route.task-list.test.ts
  • apps/web/src/app/api/mcp/documents/__tests__/route.write-guardrails.test.ts
  • apps/web/src/app/api/mcp/documents/route.ts
  • apps/web/src/app/api/mcp/drives/__tests__/route.test.ts
  • apps/web/src/app/api/mcp/drives/route.ts
  • apps/web/src/app/api/mentions/search/__tests__/route.test.ts
  • apps/web/src/app/api/mentions/search/route.ts
  • apps/web/src/app/api/messages/[conversationId]/[messageId]/__tests__/route.test.ts
  • apps/web/src/app/api/messages/[conversationId]/[messageId]/follow/__tests__/route.test.ts
  • apps/web/src/app/api/messages/[conversationId]/[messageId]/follow/route.ts
  • apps/web/src/app/api/messages/[conversationId]/[messageId]/reactions/__tests__/route.test.ts
  • apps/web/src/app/api/messages/[conversationId]/[messageId]/reactions/route.ts
  • apps/web/src/app/api/messages/[conversationId]/[messageId]/route.ts
  • apps/web/src/app/api/messages/[conversationId]/__tests__/route.test.ts
  • apps/web/src/app/api/messages/[conversationId]/route.ts
  • apps/web/src/app/api/messages/conversations/[conversationId]/__tests__/route.test.ts
  • apps/web/src/app/api/messages/conversations/[conversationId]/route.ts
  • apps/web/src/app/api/messages/conversations/__tests__/route.test.ts
  • apps/web/src/app/api/messages/conversations/route.ts
  • apps/web/src/app/api/messages/threads/__tests__/route.test.ts
  • apps/web/src/app/api/messages/threads/route.ts
  • apps/web/src/app/api/monitoring/[metric]/route.ts
  • apps/web/src/app/api/notifications/[id]/read/route.ts
  • apps/web/src/app/api/notifications/[id]/route.ts
  • apps/web/src/app/api/notifications/push-tokens/route.ts
  • apps/web/src/app/api/notifications/read-all/route.ts
  • apps/web/src/app/api/notifications/route.ts
  • apps/web/src/app/api/oauth/authorize/__tests__/route.test.ts
  • apps/web/src/app/api/oauth/authorize/route.ts
  • apps/web/src/app/api/oauth/device_authorization/__tests__/route.test.ts
  • apps/web/src/app/api/oauth/device_authorization/decision/__tests__/route.test.ts
  • apps/web/src/app/api/oauth/device_authorization/decision/route.ts
  • apps/web/src/app/api/oauth/device_authorization/route.ts
  • apps/web/src/app/api/oauth/device_authorization/verify/__tests__/route.test.ts
  • apps/web/src/app/api/oauth/device_authorization/verify/route.ts
  • apps/web/src/app/api/oauth/revoke/__tests__/route.test.ts
  • apps/web/src/app/api/oauth/revoke/route.ts
  • apps/web/src/app/api/oauth/token/__tests__/grant-type-enforcement.test.ts
  • apps/web/src/app/api/oauth/token/__tests__/route.test.ts
  • apps/web/src/app/api/oauth/token/route.ts
  • apps/web/src/app/api/pages/[pageId]/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/agent-config/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/agent-config/route.ts
  • apps/web/src/app/api/pages/[pageId]/ai-usage/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/ai-usage/route.ts
  • apps/web/src/app/api/pages/[pageId]/breadcrumbs/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/breadcrumbs/route.ts
  • apps/web/src/app/api/pages/[pageId]/children/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/children/route.ts
  • apps/web/src/app/api/pages/[pageId]/convert-content-mode/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/convert-content-mode/route.ts
  • apps/web/src/app/api/pages/[pageId]/export/csv/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/export/csv/route.ts
  • apps/web/src/app/api/pages/[pageId]/export/docx/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/export/docx/route.ts
  • apps/web/src/app/api/pages/[pageId]/export/markdown/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/export/markdown/route.ts
  • apps/web/src/app/api/pages/[pageId]/export/xlsx/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/export/xlsx/route.ts
  • apps/web/src/app/api/pages/[pageId]/form-target/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/form-target/route.ts
  • apps/web/src/app/api/pages/[pageId]/history/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/history/route.ts
  • apps/web/src/app/api/pages/[pageId]/permissions/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/permissions/check/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/permissions/check/route.ts
  • apps/web/src/app/api/pages/[pageId]/permissions/route.ts
  • apps/web/src/app/api/pages/[pageId]/processing-status/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/processing-status/route.ts
  • apps/web/src/app/api/pages/[pageId]/publish/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/publish/route.ts
  • apps/web/src/app/api/pages/[pageId]/reprocess/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/reprocess/route.ts
  • apps/web/src/app/api/pages/[pageId]/restore/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/restore/route.ts
  • apps/web/src/app/api/pages/[pageId]/role-permissions/route.ts
  • apps/web/src/app/api/pages/[pageId]/route.ts
  • apps/web/src/app/api/pages/[pageId]/share-invite/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/share-invite/route.ts
  • apps/web/src/app/api/pages/[pageId]/share-links/[linkId]/route.ts
  • apps/web/src/app/api/pages/[pageId]/share-links/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/share-links/route.ts
  • apps/web/src/app/api/pages/[pageId]/tasks/[taskId]/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/tasks/[taskId]/route.ts
  • apps/web/src/app/api/pages/[pageId]/tasks/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/tasks/reorder/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/tasks/reorder/route.ts
  • apps/web/src/app/api/pages/[pageId]/tasks/route.ts
  • apps/web/src/app/api/pages/[pageId]/tasks/statuses/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/tasks/statuses/route.ts
  • apps/web/src/app/api/pages/[pageId]/versions/compare/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/versions/compare/route.ts
  • apps/web/src/app/api/pages/[pageId]/view/__tests__/route.test.ts
  • apps/web/src/app/api/pages/[pageId]/view/route.ts
  • apps/web/src/app/api/pages/__tests__/manage-keys-scope.test.ts
  • apps/web/src/app/api/pages/__tests__/route.test.ts
  • apps/web/src/app/api/pages/bulk-copy/__tests__/route.test.ts
  • apps/web/src/app/api/pages/bulk-copy/route.ts
  • apps/web/src/app/api/pages/bulk-delete/__tests__/route.test.ts
  • apps/web/src/app/api/pages/bulk-delete/route.ts
  • apps/web/src/app/api/pages/bulk-move/__tests__/route.test.ts
  • apps/web/src/app/api/pages/bulk-move/route.ts
  • apps/web/src/app/api/pages/reorder/__tests__/route.test.ts
  • apps/web/src/app/api/pages/reorder/route.ts
  • apps/web/src/app/api/pages/route.ts
  • apps/web/src/app/api/pages/tree/__tests__/route.test.ts
  • apps/web/src/app/api/pages/tree/route.ts
  • apps/web/src/app/api/permissions/batch/__tests__/route.test.ts
  • apps/web/src/app/api/permissions/batch/route.ts
  • apps/web/src/app/api/public/forms/[token]/submit/__tests__/route.test.ts
  • apps/web/src/app/api/public/forms/[token]/submit/route.ts
  • apps/web/src/app/api/pulse/__tests__/route.test.ts
  • apps/web/src/app/api/pulse/generate/__tests__/credit-gate.test.ts
  • apps/web/src/app/api/pulse/generate/__tests__/task-trash-filter.test.ts
  • apps/web/src/app/api/pulse/generate/route.ts
  • apps/web/src/app/api/pulse/route.ts
  • apps/web/src/app/api/search/multi-drive/__tests__/route.test.ts
  • apps/web/src/app/api/search/multi-drive/route.ts
  • apps/web/src/app/api/search/route.ts
  • apps/web/src/app/api/settings/automations/route.ts
  • apps/web/src/app/api/settings/display-preferences/route.ts
  • apps/web/src/app/api/settings/hotkey-preferences/__tests__/route.test.ts
  • apps/web/src/app/api/settings/hotkey-preferences/route.ts
  • apps/web/src/app/api/settings/notification-preferences/route.ts
  • apps/web/src/app/api/settings/personalization/route.ts
  • apps/web/src/app/api/settings/toast-preferences/__tests__/route.test.ts
  • apps/web/src/app/api/settings/toast-preferences/route.ts
  • apps/web/src/app/api/share/[token]/accept/route.ts
  • apps/web/src/app/api/share/[token]/route.ts
  • apps/web/src/app/api/sidebar/badges/route.ts
  • apps/web/src/app/api/storage/check/__tests__/route.test.ts
  • apps/web/src/app/api/storage/check/route.ts
  • apps/web/src/app/api/storage/info/__tests__/route.test.ts
  • apps/web/src/app/api/storage/info/route.ts
  • apps/web/src/app/api/stripe/apply-promo/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/apply-promo/route.ts
  • apps/web/src/app/api/stripe/billing-address/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/billing-address/route.ts
  • apps/web/src/app/api/stripe/cancel-checkout/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/cancel-checkout/route.ts
  • apps/web/src/app/api/stripe/cancel-schedule/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/cancel-schedule/route.ts
  • apps/web/src/app/api/stripe/cancel-subscription/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/cancel-subscription/route.ts
  • apps/web/src/app/api/stripe/create-credit-topup/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/create-credit-topup/route.ts
  • apps/web/src/app/api/stripe/create-subscription/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/create-subscription/route.ts
  • apps/web/src/app/api/stripe/customer/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/customer/route.ts
  • apps/web/src/app/api/stripe/invoices/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/invoices/route.ts
  • apps/web/src/app/api/stripe/portal/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/portal/route.ts
  • apps/web/src/app/api/stripe/reactivate-subscription/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/reactivate-subscription/route.ts
  • apps/web/src/app/api/stripe/upcoming-invoice/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/upcoming-invoice/route.ts
  • apps/web/src/app/api/stripe/update-subscription/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/update-subscription/route.ts
  • apps/web/src/app/api/stripe/validate-promo-code/__tests__/route.test.ts
  • apps/web/src/app/api/stripe/validate-promo-code/route.ts
  • apps/web/src/app/api/subscriptions/status/__tests__/route.test.ts
  • apps/web/src/app/api/tasks/[taskId]/triggers/[triggerType]/route.ts
  • apps/web/src/app/api/tasks/[taskId]/triggers/__tests__/route.test.ts
  • apps/web/src/app/api/tasks/[taskId]/triggers/route.ts
  • apps/web/src/app/api/tasks/__tests__/manage-keys-scope.test.ts
  • apps/web/src/app/api/tasks/__tests__/mcp-scope.test.ts
  • apps/web/src/app/api/tasks/__tests__/route.test.ts
  • apps/web/src/app/api/tasks/route.ts
  • apps/web/src/app/api/track/__tests__/route.test.ts
  • apps/web/src/app/api/track/route.ts
  • apps/web/src/app/api/trash/[pageId]/route.ts
  • apps/web/src/app/api/trash/drives/[driveId]/route.ts
  • apps/web/src/app/api/upload/cancel/__tests__/route.test.ts
  • apps/web/src/app/api/upload/cancel/route.ts
  • apps/web/src/app/api/upload/complete/__tests__/route.test.ts
  • apps/web/src/app/api/upload/complete/route.ts
  • apps/web/src/app/api/upload/presign/__tests__/route.test.ts
  • apps/web/src/app/api/upload/presign/route.ts
  • apps/web/src/app/api/user/assistant-config/__tests__/route.test.ts
  • apps/web/src/app/api/user/assistant-config/route.ts
  • apps/web/src/app/api/user/favorites/[id]/__tests__/route.test.ts
  • apps/web/src/app/api/user/favorites/[id]/route.ts
  • apps/web/src/app/api/user/favorites/__tests__/route.test.ts
  • apps/web/src/app/api/user/favorites/reorder/__tests__/route.test.ts
  • apps/web/src/app/api/user/favorites/reorder/route.ts
  • apps/web/src/app/api/user/favorites/route.ts
  • apps/web/src/app/api/user/integrations/[connectionId]/__tests__/route.test.ts
  • apps/web/src/app/api/user/integrations/[connectionId]/route.ts
  • apps/web/src/app/api/user/integrations/__tests__/route.test.ts
  • apps/web/src/app/api/user/integrations/callback/route.ts
  • apps/web/src/app/api/user/integrations/route.ts
  • apps/web/src/app/api/user/recents/__tests__/route.test.ts
  • apps/web/src/app/api/user/recents/route.ts
  • apps/web/src/app/api/users/find/__tests__/route.test.ts
  • apps/web/src/app/api/users/find/route.ts
  • apps/web/src/app/api/users/messageable/__tests__/route.test.ts
  • apps/web/src/app/api/users/messageable/route.ts
  • apps/web/src/app/api/users/search/__tests__/route.test.ts
  • apps/web/src/app/api/users/search/route.ts
  • apps/web/src/app/api/v1/chat/completions/__tests__/mcp-scope-tool-filtering.test.ts
  • apps/web/src/app/api/v1/chat/completions/__tests__/route-backfill.test.ts
  • apps/web/src/app/api/v1/chat/completions/__tests__/route.test.ts
  • apps/web/src/app/api/v1/chat/completions/route.ts
  • apps/web/src/app/api/v1/conversations/[id]/route.ts
  • apps/web/src/app/api/v1/conversations/__tests__/route.test.ts
  • apps/web/src/app/api/v1/conversations/route.ts
  • apps/web/src/app/api/v1/models/__tests__/route.test.ts
  • apps/web/src/app/api/v1/models/route.ts
  • apps/web/src/app/api/voice/synthesize/__tests__/route.test.ts
  • apps/web/src/app/api/voice/synthesize/route.ts
  • apps/web/src/app/api/voice/transcribe/__tests__/route.test.ts
  • apps/web/src/app/api/voice/transcribe/route.ts
  • apps/web/src/app/api/workflows/[workflowId]/__tests__/route.test.ts
  • apps/web/src/app/api/workflows/[workflowId]/route.ts
  • apps/web/src/app/api/workflows/[workflowId]/run/__tests__/route.test.ts
  • apps/web/src/app/api/workflows/[workflowId]/run/route.ts
  • apps/web/src/app/api/workflows/__tests__/route.test.ts
  • apps/web/src/app/api/workflows/agents/route.ts
  • apps/web/src/app/api/workflows/route.ts
  • apps/web/src/app/dashboard/[driveId]/[pageId]/view/__tests__/route.test.ts
  • apps/web/src/app/dashboard/[driveId]/[pageId]/view/route.ts
  • apps/web/src/lib/auth/__tests__/admin-audit-coverage.test.ts
  • apps/web/src/lib/auth/__tests__/auth-core-decisions.test.ts
  • apps/web/src/lib/auth/__tests__/auth-helpers.test.ts
  • apps/web/src/lib/auth/__tests__/auth-middleware.test.ts
  • apps/web/src/lib/auth/__tests__/auth.test.ts
  • apps/web/src/lib/auth/__tests__/enforced-context-csrf.test.ts
  • apps/web/src/lib/auth/__tests__/manage-keys-fixture.ts
  • apps/web/src/lib/auth/__tests__/mcp-scope-enforcement.test.ts
  • apps/web/src/lib/auth/__tests__/principal-permissions.test.ts
  • apps/web/src/lib/auth/__tests__/token-prefixes.test.ts
  • apps/web/src/lib/auth/auth-core.ts
  • apps/web/src/lib/auth/auth-helpers.ts
  • apps/web/src/lib/auth/auth-types.ts
  • apps/web/src/lib/auth/auth.ts
  • apps/web/src/lib/auth/index.ts
  • apps/web/src/lib/auth/principal-permissions.ts
  • apps/web/src/lib/auth/request-auth.ts
  • apps/web/src/lib/upload/__tests__/attachment-route-helpers.test.ts
  • apps/web/src/lib/upload/attachment-route-helpers.ts
  • apps/web/vitest.config.ts
  • package.json
  • scripts/migrate-auth-barrel.mjs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pu/refactorauth-eliminate-barrel-indexts-direct-imports-per-module

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.

…minate-barrel-indexts-direct-imports-per-module

# Conflicts:
#	apps/web/src/app/api/auth/magic-link/verify/__tests__/route.test.ts
@2witstudios

Copy link
Copy Markdown
Owner Author

CI status triage (automated convergence)

ci / Unit Tests — flaky, not a regression. The job failed on Error: Worker exited unexpectedly (a tinypool worker crash during the 937s coverage run), while the run reports 12189/12202 tests passed, 822/823 files passed — the single non-passing "file" is the crashed worker, not an assertion failure. Re-running the failed job.

CodeQL — 6 "new" high alerts are all pre-existing, not introduced here. This PR is a pure import refactor (barrel → direct per-module paths); it changes no security logic. Evidence:

  • All 15 open CodeQL alerts on this PR's ref (#178, #204, #228–#234, #237–#240, #246, #252) are identical to the open alert set on master — none are new.
  • The flagged rules (js/insufficient-password-hash, js/user-controlled-bypass, js/polynomial-redos, js/incomplete-multi-character-sanitization) live in packages/lib/* (untouched by this PR) and in route logic whose only diff here is import-line rewrites (e.g. getClientIP from @/lib/auth@pagespace/lib/security/client-ip).
  • CodeQL's own note confirms: "Alerts not introduced by this pull request might have been detected because the code changes were too large."

These pre-existing findings are out of scope for this refactor and are tracked independently. Neither check is a required merge gate per the repo's active ruleset.

…minate-barrel-indexts-direct-imports-per-module

# Conflicts:
#	apps/web/src/app/api/pages/[pageId]/tasks/[taskId]/route.ts
#	apps/web/src/app/api/pages/[pageId]/tasks/route.ts
#	apps/web/src/app/api/tasks/route.ts
@2witstudios

Copy link
Copy Markdown
Owner Author

Convergence update — conflicts resolved; coverage-OOM diagnosis

✅ Done

  • Merge conflicts resolved twice as master advanced (magic-link test imports; task-route imports vs fix(tasks): decrypt assignee/user names in task list responses #1979 decrypt; new machines/git-blob + machines/files/settings barrel importers repointed via the codemod). Branch is MERGEABLE, zero @/lib/auth barrel refs remain.
  • Independent adversarial review of the hand-written core (auth-types/auth-core/request-auth + codemod) — verified faithful to the original engine byte-for-byte, every fail-closed branch preserved. All 12,189 tests pass in CI.
  • CodeQL "6 new high" alerts confirmed pre-existing (identical alert set on master; this PR only rewrites import lines in the flagged files) — not introduced here.

⚠️ Open: ci / Unit Tests OOMs during coverage

The job fails with FATAL ERROR: JavaScript heap out of memorynot a test failure (all tests pass), a memory ceiling. Diagnosis:

  • The web suite (823 files) under v8 coverage peaks a worker's heap. On CI (ubuntu-latest, 16 GB / 4 cores → 4 forks ≈ 205 files/worker) master sits just under the limit; this branch pushes one worker past it.
  • Config tuning cannot fit 16 GB — reproduced locally at CI's 4-fork parallelism: a worker needs >6 GB (vs master <4 GB), so 4×>6 GB > 16 GB. Tried and ruled out: fork caps (2/3/4), heap ceilings (6–8 GB), and the istanbul provider — all still OOM.
  • Neither ci / Unit Tests nor CodeQL is a required merge check per the repo's active ruleset (only deletion/non_fast_forward are enforced).

Recommended fix (infra, out of a refactor PR's scope): shard the web coverage run across 2 CI jobs (vitest --shard) and merge reports, or run the Unit Tests job on a larger-RAM runner. Happy to implement sharding as a follow-up if desired.

@2witstudios

Copy link
Copy Markdown
Owner Author

ci / Unit Tests OOM — definitive diagnosis (not a test failure)

Proven cause. Ran the base branch's full web coverage on the same machine, forced to CI's 4-fork parallelism: master completes cleanly (826 files, no OOM); this branch OOMs a worker at the same 4 forks. So the refactor is the cause — a per-worker coverage-memory regression, not a test failure (all 12,194 tests pass).

Magnitude & nature. A single worker (≈205 files) accumulates >10 GB of v8 coverage data during execution on this branch vs master's ~4 GB. It is not a leak (retained heap between files stays ≤354 MB — it's a transient accumulation in the v8 coverage collector during the run), and it does not instrument extra packages (@pagespace/* resolve to coverage-excluded dist).

Every config lever exhausted (all still OOM):

  • fork caps: 2, 3, 4 (fewer forks → more files/worker → worse)
  • per-worker heap ceilings: 6 / 8 / 10 / 20 GB
  • coverage provider: v8 and istanbul
  • coverage.all: false (dropped main-proc RSS 10.3→4.2 GB but the worker still OOMs)

Only remaining fixes are structural, not a refactor-PR change:

  1. Shard the web coverage run across CI jobs (vitest --shard) so each worker sees fewer files (a 300-file subset runs in ~1.2 GB). Robust, standard, but restructures the Unit Tests job + coverage-merge.
  2. Larger-RAM runner for that job.

Neither ci / Unit Tests nor CodeQL is a required merge check (the active ruleset enforces only deletion/non_fast_forward), so this does not block merge. Flagging for a maintainer call on sharding vs runner sizing; happy to implement sharding as a focused follow-up.

@2witstudios

Copy link
Copy Markdown
Owner Author

Coverage-OOM root-cause — refined with an A/B measurement (correction)

Ran master and this branch through the same full web coverage (2 forks, 20 GB cap, logHeapUsage) and compared. The memory profiles are essentially identical:

metric master this branch
executed source files (>0%) 1838 1833
top per-file retained heap 344 MB 354 MB
coverage lines / branch 71.92% / 88.38% 71.74% / 88.34%
2-fork + 20 GB run completes completes

So the refactor does NOT increase coverage-data size, executed-module count, retained heap, or coverage % — correcting my earlier "≈2.5× per-worker" framing, which conflated the 4-fork OOM threshold with a data-size increase. Both branches produce the same coverage footprint.

What the OOM actually is: the web coverage suite sits right at the memory ceiling of the 16 GB / 4-core ubuntu-latest runner. The failure is a transient v8-coverage-collection spike at 4 forks — not captured by retained-heap sampling and not tied to a data-size difference. Master currently lands just under the ceiling; this branch's slightly different file set / scheduling tips it just over. It is a capacity-edge condition, not a code memory defect.

Practical fixes are unchanged and infra-level: shard the web coverage job (vitest --shard) so each worker sees fewer files, or run that job on a larger-RAM runner. Neither ci / Unit Tests nor CodeQL is a required merge check. All 12,194 tests pass; code correctness independently verified.

2witstudios and others added 22 commits July 10, 2026 17:25
…in auth-core

Behavior-neutral cleanups from a maintainability pass on the extracted core:
- `manageKeysOnlyDeniedResponse` uses a single `export function` (was function +
  separate re-export of a same-module symbol).
- Extract the identical `403 "does not have access to this drive"` literal
  (duplicated in checkMCPDriveScope + checkMCPCreateScope) into a private
  `driveAccessDeniedResponse()` builder. Byte-identical output; the third copy in
  request-auth.checkMCPPageScope is left untouched (frozen faithful extraction).

No control flow, status codes, messages, or logging changed. tsc 0 errors;
auth-core-decisions (16) + mcp-scope-enforcement (47) green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
The web suite (800+ files) under v8 coverage peaks a worker past the default
~4 GB V8 heap on ubuntu-latest (4 cores → 4 forks), OOMing the `ci / Unit Tests`
job. Memory profile is identical to master (executed files, per-file heap, and
coverage % all match) — the suite has simply reached the runner's ceiling.

Split `test:coverage` into 3 sharded passes (`--reporter=blob --shard=i/3`) plus
a `--merge-reports` step so each pass instruments ~1/3 of the files, keeping each
worker's coverage-collection peak within the runner. A dedicated
`vitest.shard.config.ts` zeroes thresholds on the shard passes (each shard is
partial); the real thresholds are enforced once on the merged report. Also lifts
the worker heap ceiling to 6 GB for headroom.

Validated locally: 3 blobs → merge reconstructs the full suite with full coverage
(71.74% lines / 88.64% branch / 72.97% functions / 71.74% statements), passing
all thresholds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…e heap to NODE_OPTIONS

The prior commit's sharded-coverage change failed CI at `web#build`: the new
`vitest.shard.config.ts` was not in tsconfig's exclude list (unlike
`vitest.config.ts`), so `next build` typechecked it — pulling the excluded
`vitest.config.ts` into scope and surfacing its latent config-only type errors.

- Exclude `vitest.shard.config.ts` in apps/web/tsconfig.json (mirrors the
  existing `vitest.config.ts` exclusion). Both are runtime-only vitest configs.
- Wrap the shard config's `mergeConfig` second arg in `defineConfig` (documented
  form) — harmless now that it's excluded, but correct.
- Drop the `poolOptions.execArgv` from vitest.config.ts (its type tripped the
  build) and set the worker heap via `NODE_OPTIONS=--max-old-space-size=6144` in
  the `test:coverage` script instead — inherited by the forked coverage workers.

Local: web build exit 0, tsc 0 errors, sharded coverage flow reconstructs full
coverage passing thresholds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…minate-barrel-indexts-direct-imports-per-module
…E_OPTIONS didn't propagate)

The sharded coverage still OOM'd on CI (shard 2/3): NODE_OPTIONS does not
propagate to vitest's forked workers, so they ran at the default ~4 GB heap.
Move the heap ceiling to `poolOptions.forks.execArgv` in vitest.shard.config.ts
(which reliably reaches the fork workers, and is excluded from tsc/next-build so
it can't trip the app typecheck). 8 GB/worker comfortably holds a shard's
~68-files-per-worker coverage peak; only one worker spikes at a time, keeping
total well under the 16 GB runner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…d × 8 GB)

Shard 2/3 still OOM'd at 8 GB/worker — its file set concentrates the heavy
dashboard/component page tests, whose coverage collection accumulates past the
ceiling at ~68 files/worker. Split into 5 shards (~41 files/worker) to bound
per-worker coverage accumulation. execArgv 8 GB heap retained; dropped the
redundant NODE_OPTIONS (execArgv is the effective worker heap).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
Sharding did not fix the CI coverage OOM: the failure is in the coverage
report/blob generation phase (v8 `coverage.all` over ~3272 files + a ~45 MB
blob), which is main-process memory that per-worker heap (execArgv) and file
sharding don't address. Reverting the fragile 5-shard flow to keep the config
simple; the coverage OOM (a non-required check) remains an infra/capacity item
for a follow-up (larger runner, or `coverage.all: false` + main-process heap).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
… heap

The auth-barrel elimination made 285 test files mock the barrel per-module
instead of wholesale, so they now load and cover more real modules. That is
more genuine coverage, but it tipped the web `test:coverage` job over the 16 GB
runner's default heap: the run OOM'd in the main process during the v8 coverage
merge (`[node (vitest 1)]`, ~4 GB), after all tests passed.

Root-caused locally by reproducing the merge OOM under a 4 GB cap and bisecting:
no single test file is pathological (max ~279 MB isolated); the cost is the
whole-app coverage map. Two changes, both gate-preserving:

- `coverage.all: false` in CI — stop instrumenting every source file (thousands,
  mostly untested) just to print 0%. CI now measures the executed set; the
  thresholds still hold (tested-code coverage is 85/89/78/85 vs 44/85/56/44).
  Local keeps `all: true` for the full browsable picture.
- Drop the memory-heavy per-file `html`/`json` reporters in CI (kept
  `json-summary`, which the coverage-ratchet tooling reads); raise the merge
  heap to 8 GB for headroom.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
Real-CI follow-up to the coverage-OOM fix. On the runner the `all: false` run
now passes all 830 tests and produces the report + thresholds (85/89/78/85),
but the main vitest process still OOM'd during coverage *finalization* — it
retained ~8 GB after GC at the 8 GB cap (no room to breathe) and died with
"Worker exited unexpectedly".

- `coverage.processingConcurrency: 1` in CI — the v8 provider converts files
  with concurrency = CPU count (4 on the runner), multiplying the finalization
  peak; serializing it throttles that spike.
- Raise the merge heap to 12 GB so the ~8 GB executed-set coverage map has GC
  headroom (finalization runs with the test workers idle, so 12 GB is safe on
  the 16 GB runner).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…minate-barrel-indexts-direct-imports-per-module

# Conflicts:
#	apps/web/src/app/api/drives/[driveId]/domains/[domainId]/route.ts
#	apps/web/src/app/api/drives/[driveId]/domains/[domainId]/verify/route.ts
#	apps/web/src/app/api/drives/[driveId]/domains/route.ts
The v8 coverage OOM on this branch cannot be fixed by any coverage config on
the 16 GB CI runner, and the alternatives break other things — proven
exhaustively:

- v8 provider: the coverage map for the executed module graph (~1532 src files
  plus their bundled dep source maps) has a live set >12 GB that is still
  growing when it hits the cap (CI GC freed 12252->12237 MB, i.e. nothing), so
  no --max-old-space-size that fits 16 GB completes it. all:false, lean
  reporters, processingConcurrency:1, and include-scoping each help a little but
  none close the gap.
- istanbul provider: fits memory but its babel instrumentation breaks 169 web
  test files (vs 16 under v8) and reports branch coverage on a different metric
  (53% vs v8's 89%, tripping the 85% threshold).

Reverting to the master baseline so this PR is a clean auth-only diff; the
coverage-capacity issue is tracked separately and needs a scope/infra decision,
not a config tweak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
… runner)

apps/web's test suite executes ~1500 source modules; the v8 coverage provider's
end-of-run source-map remap of that graph has a live set >12 GB (still growing
at the cap — CI GC frees nothing: 12252->12237 MB), so it OOMs the 16 GB runner
for any --max-old-space-size. This was tipped over by the auth-barrel refactor,
which (correctly) made 285 test files load real modules instead of a wholesale
barrel mock. Exhaustively confirmed unfixable by config: all:false, lean
reporters, processingConcurrency:1, worker-heap pinning, and include-scoping
each help marginally but none fit; the istanbul provider fits memory but its
babel instrumentation breaks ~170 web test files and reports branch coverage on
a different metric.

Fix: run the full web suite for correctness (every test still runs and must
pass) but without coverage instrumentation, and keep coverage gating for every
other package. Web coverage-% can be measured locally
(`bun run --filter web test:coverage`); re-enabling it in CI needs a larger
runner or a logic-layer-scoped coverage run, tracked separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
The real OOM was never coverage-specific: on the 16 GB runner even a plain
`vitest run` (no coverage) OOMs. All 833 web test files pass, then the MAIN
process OOMs at the ~4 GB default heap while aggregating results — the auth
refactor grew the executed module graph (285 files now load real modules instead
of a barrel mock), pushing the main process past 4 GB.

Fix, decoupling the two heaps that were previously one:
- `test` script raises the main process to 8 GB (it aggregates 833 files).
- vitest.config pins each fork worker to 4 GB via poolOptions.forks.execArgv,
  so forks do NOT inherit the raised limit and lazily balloon to it (V8 grows
  to whatever ceiling it is given). Forks already ran cleanly at 4 GB on CI.

Coverage (`test:coverage`) stays out of CI for web: its end-of-run source-map
remap needs >12 GB and cannot fit 16 GB by any config, and istanbul breaks ~170
of these test files. Web is gated on full test correctness; coverage-% is a
local/large-runner concern, tracked separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
… runner

Follow-up: main-heap fix (8 GB) resolved the main-process OOM, but with 4
concurrent fork workers each running ~200 files near a 4 GB ceiling, total fork
RSS saturated the runner's 16 GB and one fork OOM'd flakily depending on how the
833 test files sharded across forks.

Fork workers peak during test execution; the main process peaks afterward (forks
have exited) during result aggregation — the two do not sum. So cap concurrency
at 2 forks with a 7 GB heap each (~14 GB during tests) and keep the main at 8 GB
(after). Both phases fit 16 GB. Trade-off: 2-way instead of 4-way parallelism,
i.e. a slower web test step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
Root cause of the persistent OOM (present even without coverage): a fork
worker's retained heap grows ~linearly with the number of files it runs
(~17 MB/file), so the full 833-file suite is ~14 GB of module graph however it
is split across concurrent forks — right at the 16 GB runner's ceiling. Every
single-process arrangement (4 forks near 4 GB, 2 forks near 7 GB) either
saturated the runner or OOM'd a worker.

Fix: `test` runs the suite as two SEQUENTIAL shards — separate vitest processes
that fully release memory between them (`--shard=1/2 && --shard=2/2`). Each shard
covers ~half the files, i.e. ~104 files per fork across the default 4 forks
(~1.7 GB each, 3 GB cap), so a shard peaks around 12 GB and the whole thing fits
with margin. The main process keeps its 8 GB for per-shard aggregation. Cost: a
second vitest startup, i.e. a somewhat slower web test step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…emory

Two shards still OOM'd: with ~30 MB of retained module graph per test file
(the suite releases little between files), a shard of ~417 files put ~104 files
on each of 4 forks — ~3 GB, over the per-fork cap. Split into four sequential
shards instead: ~208 files/shard, ~52 per fork, ~1.5 GB each with headroom under
the 3 GB cap; a shard peaks ~12 GB and each shard process releases all memory
before the next starts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
Four shards still OOM'd a fork at the 3 GB cap: a shard's ~209 files put ~52 on
a fork, and heavy component-render files cluster unevenly, so the busiest fork
retained >3 GB (~58 MB/file). Go to six sequential shards (~139 files/shard,
~35/fork ≈ 2 GB) and raise the per-fork cap to 4 GB for clustering headroom. A
shard's total (~8 GB spread across forks) stays well within the runner; each
shard process releases all memory before the next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
Six shards still OOM'd: vitest packs files onto forks unevenly — a fork grabbed
~70 of a 140-file shard and hit the 4 GB cap (~58 MB retained per file, released
only when the shard process exits; the suite needs isolate:true, so registries
can't be shared). Ten shards (~83 files each) keep even a fork that takes half a
shard (~42 files ≈ 2.4 GB) within the 4 GB cap. Trade-off: the web test step
runs ten sequential vitest passes, so it is slower — the durable fix is reducing
per-file module retention (test-infra work) or a larger runner, tracked
separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
Ten shards still OOM'd: memory is REAL per-file accumulation (~48 MB/file,
released only when the shard process exits), and vitest packs files unevenly —
one fork ran an entire 84-file shard and hit the 4 GB cap while other forks sat
idle. More shards don't help if a single fork can take a whole shard; the fork
cap has to be able to hold one.

A shard's TOTAL data is what actually occupies RAM (idle forks hold ~nothing, so
it doesn't multiply by fork count). So use five sequential shards (~167 files
≈ 8 GB each) with a 9 GB per-fork cap — a fork that grabs the whole shard still
fits, and peak RAM stays ~8-10 GB, well within 16 GB. Each shard process frees
everything before the next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…minate-barrel-indexts-direct-imports-per-module

# Conflicts:
#	apps/web/src/app/api/ai/page-agents/consult/route.ts
CI confirmed the failure mode precisely: a single fork churns through an ENTIRE
shard of fast pure-function files, accumulating ~54 MB/file, so 5 shards of ~170
files put ~9.2 GB on one fork and just topped the 9 GB cap. The cap must cover
one fork holding a whole shard. Six shards → ~139 files ≈ 7.5 GB one-fork-all;
an 11 GB cap gives ~47% headroom and physical RAM stays ~8 GB (idle forks hold
almost nothing; it does not multiply by fork count).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…riance

Six shards OOM'd at the 11 GB cap: one shard's fork accumulated ~78 MB/file
(vs ~54 in the prior shard) because heavy component-render files cluster into
particular shards, so the per-file retention varies. Rather than keep chasing
the observed rate, size for large margins: ten sequential shards spread heavy
files thin (~84 files/shard) and a 12 GB per-fork cap tolerates even ~100 MB/file
(~8 GB one-fork-takes-all), well under both the cap and the 16 GB runner. Each
shard is a fresh process that releases everything before the next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
2witstudios and others added 11 commits July 11, 2026 14:16
… refactor)

The web `test:coverage` (and even plain `test`) OOMs the 16 GB CI runner on this
branch. Root cause diagnosed over many CI runs: a fork worker leaks memory
per-file under isolate:true (module graphs aren't released between files — a
known vitest 2.x fork-pool behavior, worst for the ~285 files that now load real
AI-SDK/component modules instead of a barrel mock). The leak is UNBOUNDED — a
fork fills whatever --max-old-space-size it is given (observed at 4/8/9/11/12 GB
caps) — so no coverage-provider swap, reporter trim, heap value, fork cap, or
shard count reliably fits it, and the istanbul provider breaks ~170 test files.

Reverting the exploratory config so this PR stays a clean auth-barrel-elimination
diff. The CI web-suite memory leak is a separate, pre-existing test-infra issue
this refactor exposes; fixing it (vitest upgrade, per-file teardown/mock reset
audit, or a larger runner) is tracked separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…minate-barrel-indexts-direct-imports-per-module
The auth-barrel elimination made ~285 web test files load real modules instead
of a wholesale barrel mock, so the suite now executes ~1500 real modules. Under
vitest's default 'forks' pool a worker RETAINS each file's module graph and never
releases it — unbounded growth that fills any --max-old-space-size and OOMs the
16 GB CI runner, during both the plain run and the v8 coverage remap. Every
config workaround (heap sizes, lean reporters, all:false, processingConcurrency,
fork caps, up to 10 sequential shards) only chased the leak; the istanbul
provider fit memory but broke ~170 test files.

Switching to `pool: 'threads'` fixes the root cause: the threads pool tears down
each file's context and GCs it. Verified locally — full `vitest run --coverage`
now completes at ~2.5 GB peak within the default heap, all thresholds pass
(72/88/74/72 vs 44/85/56/44), no OOM. This is a one-line change; ci.yml,
package.json, reporters, thresholds, and coverage.all are all unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
…ull coverage restored)

Root cause of the CI OOM (present in plain test AND coverage): the auth-barrel
elimination made ~285 web test files load real modules instead of a wholesale
barrel mock, so the suite executes ~1500 real modules. Under vitest's default
'forks' pool a worker RETAINS each file's module graph and never releases it —
unbounded growth that fills any --max-old-space-size and OOMs the 16 GB runner.
Every fork-side workaround (heap sizes, lean reporters, all:false, fork caps, up
to 10 sequential shards) only chased the leak; istanbul fit memory but broke
~170 tests.

Switch to `pool: 'threads'`: the threads pool tears down each file's context and
GCs it, so peak stays ~2.5 GB. CI proved this eliminates the OOM — all 12,475
tests pass and coverage generates with thresholds passing (72/88/74/72 vs
44/85/56/44) — except one thread worker hit its low default heap limit
(ERR_WORKER_OUT_OF_MEMORY). Thread workers inherit the parent's
--max-old-space-size, so `test:coverage` sets it to 8 GB for headroom.

Restores the full original CI (web back in the single coverage step, original
reporters/thresholds/all:true, ci.yml unchanged) — no de-gate, no sharding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
… heap limit

Prior CI run confirmed `pool: 'threads'` fixes the unbounded leak — all 12,475
tests pass and coverage generates with thresholds passing — but at 4 threads
(the runner's core count) one worker collects ~208 files' v8 coverage and, at
report time, exceeds its default worker heap (ERR_WORKER_OUT_OF_MEMORY; the heap
can't be raised — worker_threads reject --max-old-space-size in execArgv and
ignore the parent NODE_OPTIONS). Running 8 threads (they time-share the 4 cores)
halves that to ~104 files/worker, within the default. The main-process coverage
merge keeps its 8 GB via the test:coverage script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
The auth-barrel elimination made ~285 web test files load real modules instead
of a wholesale barrel mock (the run executes ~1500). Under vitest's default
'forks' pool a worker retains each file's module graph and never releases it —
unbounded growth that fills any --max-old-space-size and OOMs the 16 GB runner.
Exhaustively confirmed: no heap size, reporter trim, all:false, fork cap, or
sharding (to 10) fits it; istanbul and vmThreads both break ~tests.

`pool: 'threads'` fixes the root cause — the threads pool tears down each file's
context and GCs it, so the run stays bounded (~2.5 GB) and, as the prior CI run
proved, all 12,475 web tests pass. Full v8 coverage of the whole suite still
exceeds a single worker's default heap on the 4-core runner (worker_threads
can't raise or recycle that heap), so CI gates web on test correctness — every
web test runs and must pass — and web coverage-% is measured locally
(`bun run --filter web test:coverage`). All other packages keep coverage gating;
ci.yml excludes only web from the coverage step and runs its tests separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A71RhmVeB8LUf4KZEhQ6mX
The de-gate (web tests without coverage) still OOM'd a thread worker on CI: the
threads pool bounds the leak (~2.5 GB vs forks' unbounded) but CI's worker-thread
default heap (~2 GB) is lower than local and a spike exceeds it, and vitest 2.x
exposes no way to raise the thread-worker heap. Keeping pool:'threads' (the
root-cause improvement) with the original ci.yml; fully green CI needs a vitest
3.x upgrade (worker memory control) or a larger runner — tracked separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… green

The auth-barrel elimination made ~285 web test files load real modules (the run
executes ~1500). The forks pool leaks unboundedly; `pool: 'threads'` bounds it
(~2.5 GB) but under concurrent execution on the 4-core runner a worker
accumulates faster than V8 GCs it and one straggler exceeds the worker's default
heap (ERR_WORKER_OUT_OF_MEMORY) — which vitest 2.x can't raise for threads.

Measured the real ceiling: with files run one at a time, max per-file heap is
~172 MB (nowhere near the ~2 GB worker default). So the OOM is accumulation under
parallelism, not any single heavy test. `fileParallelism: !process.env.CI` runs
CI sequentially (GC gets time between files → bounded), while local stays
parallel/fast. Coverage still accumulates in the single sequential worker, so
ci.yml gates web on test correctness (every web test runs and must pass) and
measures web coverage locally; all other packages keep coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… env)

The previous attempt gated sequential execution on process.env.CI in the vitest
config, but turbo filters env vars so the vitest process didn't see CI and ran
parallel — OOMing a worker again. Pass --no-file-parallelism directly through
turbo (`turbo run test --filter=web -- --no-file-parallelism`), verified to reach
vitest as `vitest run --no-file-parallelism`. Files run one at a time so GC keeps
per-worker memory bounded (~172 MB measured); the config change is now just
`pool: 'threads'`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…minate-barrel-indexts-direct-imports-per-module

# Conflicts:
#	apps/web/src/app/api/user/assistant-config/route.ts
Code-verified that vitest 2.1.9's threads pool sets no worker resourceLimits and
worker_threads reject --max-old-space-size in execArgv, so the thread-worker heap
is fixed at Node's default and the suite's ~2.5 GB accumulation exceeds it on the
CI runner — no config (heap, reporters, all:false, fork caps, sharding to 10,
maxThreads, --no-file-parallelism, vmThreads, istanbul) closes it. Keeping
pool:'threads' (unbounded fork leak -> ~2.5 GB bounded) as the improvement;
fully green CI needs a vitest 3.x upgrade or a larger runner, tracked separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

refactor(auth): eliminate barrel index.ts — direct imports per module

1 participant