Skip to content

Split the client AI module into per-feature files - #145

Merged
Programcode merged 3 commits into
mainfrom
refactor/split-ai-client
Oct 16, 2026
Merged

Programcode merged 3 commits into
mainfrom
refactor/split-ai-client

Conversation

@Programcode

@Programcode Programcode commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Why

src/lib/ai/client.ts was 1,585 lines with every AI feature in one file. The server side was split per feature in #75. This does the same for the client, as item 11 of plans/2026-10-01-next-15.md.

What changed

  • The shared code moves to four files next to client.ts. request.ts holds the one transport, callSupabaseAI. errors.ts holds the error classes, sources.ts the discovered-source parsing and autosave, and text.ts the string helpers.
  • Each feature gets its own file in src/lib/ai/features/, with the same seven names the server uses.
  • client.ts now only re-exports. It lists the same 39 names, so no caller or test changes. A lint rule stops app code from importing the files behind it.

Scope

  • This is a move only. Every function body is byte-identical to the original. The one in-body edit adds export to 11 helpers that other files now need.
  • Two known smells stay as they are because fixing them would change behavior. Sparring validates its input twice, and callSupabaseAI has an unreachable TimeoutError branch.
  • session-store.ts is out of scope, as the plan says.

Tradeoffs

streaming.ts and error-display.ts still import from ./client, not from the new files. Three page tests mock the whole of @/lib/ai/client. If error-display.ts imported errors.ts directly, those tests would get the real error class instead of the mock.

Blast Radius

The split touches every AI feature in the browser, but only the module layout changes. If something is wrong, it shows up as a broken import at build time or a failed AI request in the app. The payload check below rules out the second for every flow the verify recipes drive.

Verification

  • Recorded every request body the fake model provider receives across verify-debatelab drive all. Two runs on main and one run on this branch give the same 140 requests, byte-identical after masking UUIDs, timestamps and HMAC signatures: sha256 ca3e696c… all three times. As a check that the comparison can fail, a one-word change to the client's default factoids region changed the recorded payload while its recipe still passed.
  • drive all: 66 passed, 4 skipped on both main and this branch, desktop and mobile Chromium, fake provider.
  • npm test (31 files, 280 tests, no test file changed), typecheck, lint, build and docs:check all pass locally. A partition script confirms each of the 79 original declarations appears exactly once with an unchanged body.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Reliability Improvements
    • AI-generated responses are more consistently checked and cleaned before being shown, helping prevent malformed results.
    • AI errors are handled more consistently, including clearer handling of timeouts, access limits, and provider failures.
    • Ask requests now have safeguards for oversized messages and conversation history.
  • Source Handling
    • Discovered sources can be saved to the relevant debate or workspace, and factoid results include only verified citations.

Programcode and others added 3 commits October 16, 2026 14:37
Move src/lib/ai/client.ts into errors, request, sources and text, plus
one file per feature under features/, mirroring supabase/functions/ai.
Code moves byte for byte; 11 formerly private helpers gain an export
so siblings can import them. client.ts is now an explicit re-export
barrel with the same 39 names, so no caller changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a no-restricted-imports rule so app, e2e and agentic code cannot
import ai/request, ai/errors, ai/sources, ai/text or ai/features/*
directly. That restores the privacy the helpers had before the split.
Point the docs and one fixture comment at the new layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The import guard missed specifiers with an extension (the repo allows
`.ts` imports), trailing slashes, and dynamic import(). It also matched
the server's same-named modules under supabase/functions/ai/. Dynamic
imports now go through no-restricted-syntax with the same pattern.

The docs now name streaming.ts as the second transport and request.ts
as a place where responses are validated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
debate-navigator Ready Ready Preview Oct 16, 2026 2:40pm UTC
debatelab-staging Ready Ready Preview Oct 16, 2026 2:40pm UTC

Copilot AI balanced review requested due to automatic review settings October 16, 2026 14:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Free
  • Run ID: 49dd8e25-897f-41e4-a2a9-75a13622567e
📥 Commits

Reviewing files that changed from the base of the PR and between 5af8a46 and 2cec375.

📒 Files selected for processing: 17
  • AGENTS.md
  • docs/architecture.md
  • docs/standards/boundaries.md
  • e2e/fixtures/ai-responses.ts
  • eslint.config.js
  • src/lib/ai/client.ts
  • src/lib/ai/errors.ts
  • src/lib/ai/features/ask.ts
  • src/lib/ai/features/attack.ts
  • src/lib/ai/features/brainstorm.ts
  • src/lib/ai/features/crossfire.ts
  • src/lib/ai/features/factoids.ts
  • src/lib/ai/features/generate.ts
  • src/lib/ai/features/sparring.ts
  • src/lib/ai/request.ts
  • src/lib/ai/sources.ts
  • src/lib/ai/text.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

The AI client implementation is divided into shared request, error, source, and text modules and feature-specific modules. The client facade re-exports those APIs. ESLint rules and documentation describe the internal import boundary and module layout.

Changes: AI Client and Feature APIs

Layer / File(s) Summary
Shared AI errors, sources, and text utilities
src/lib/ai/errors.ts, src/lib/ai/sources.ts, src/lib/ai/text.ts
Added typed AI errors and classification, source parsing and autosaving, and UTF-8 byte and generated-output utilities.
AI request transport
src/lib/ai/request.ts
Added authenticated request handling, context and abort handling, error reporting, body-size limits, and Supabase error-payload parsing.
Ask and generated-content features
src/lib/ai/features/ask.ts, src/lib/ai/features/attack.ts, src/lib/ai/features/brainstorm.ts, src/lib/ai/features/factoids.ts, src/lib/ai/features/generate.ts
Added Ask input bounds and response validation, plus attack, brainstorm, factoids, and case-generation operations with feature-specific response processing.
Crossfire and sparring operations
src/lib/ai/features/crossfire.ts, src/lib/ai/features/sparring.ts
Added Crossfire turn, debrief, and signal requests, and sparring turn generation and session judging with input and response validation.
Client facade and import boundary
src/lib/ai/client.ts, eslint.config.js, AGENTS.md, docs/architecture.md, docs/standards/boundaries.md, e2e/fixtures/ai-responses.ts
The client module now re-exports APIs from their modules. ESLint restricts direct imports of internal AI modules. Documentation and a fixture comment reference the updated module layout.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant generateCase
  participant callSupabaseAI
  participant SupabaseAIFunction
  participant autosaveDiscoveredSources
  generateCase->>callSupabaseAI: submit feature input
  callSupabaseAI->>SupabaseAIFunction: send authenticated AI request
  SupabaseAIFunction-->>callSupabaseAI: return AI response
  callSupabaseAI-->>generateCase: return result and discovered sources
  generateCase->>autosaveDiscoveredSources: save parsed discovered sources
Loading
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

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

@Programcode

Copy link
Copy Markdown
Owner Author

GitHub Actions did not run on this PR. Every job stopped after 2 seconds with "recent account payments have failed". These are the local results at head 2cec375f. They are local passes, not CI passes.

  • npm run verify: exit 0.
  • ./.superset/db-gate.sh: exit 1. 65 passed, 6 failed, 1 skipped.
  • The same gate on main (5af8a46e) gives the same result: the same 6 tests fail, 65 pass, 1 is skipped.

All 6 failures are the same 3 specs on desktop and mobile Chromium: e2e/full-stack/case-generation, current-debate and debate-context. Each one waits for a Library textbox named "Source title...". Since commit 2dc73ff (design audit, shell) the fields are labelled "Title" and "Content". That audit merged while Actions was not running jobs. This PR touches neither the Library page nor those specs. The fix belongs in its own PR.

@Programcode

Copy link
Copy Markdown
Owner Author

Codex review (gpt-6.1-sol, high) of head 2cec375: no actionable findings. Two earlier internal rounds (Opus and GPT) found gaps in the lint guard: specifiers with extensions, dynamic import(), and false positives on the server's modules. Commit 2cec375 fixes them.

@Programcode
Programcode merged commit d733459 into main Oct 16, 2026
8 of 18 checks passed
@Programcode
Programcode deleted the refactor/split-ai-client branch October 16, 2026 15:22

This branch was successfully deployed

2 active deployments
Preview – debate-navigator — 2cec375f Deployed Oct 16, 2026 by vercel[bot]
Preview – debatelab-staging — 2cec375f Deployed Oct 16, 2026 by vercel[bot]
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.

2 participants