Repository navigation
refactor(ai): add debate-aware context and focused feature handlers - #75
Conversation
The email fixtures hard-coded an expiry of 2026-09-27 12:00 UTC, so four tests began failing once that moment passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Move code without changing behaviour so later prompt and context changes are easy to review. - ai/index.ts stays the HTTP entrypoint and dispatcher. Auth, validation, entitlements, routing, source ranking, and usage accounting run there once, then each feature has its own handler in ai/features/. - Environment and service-role clients live in ai/runtime.ts, provider calls in ai/provider.ts, and input hygiene in ai/request.ts. - Each feature's task prompt and user-prompt builder moved to _shared/prompts/<feature>.ts; shared rules to _shared/prompts/common.ts. _shared/prompts.ts and ai/prompt-builder.ts remain as re-export facades. - The security source-scan test now reads every non-test ai source. Every system and user prompt is byte-identical to the previous build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every ai and ai-stream request can now carry a debateContext of IDs and preferences. The server loads the motion, side, settings, instructions, Library sources, and saved results for that debate, filtering every query by the caller and the one debate, so forged or cross-debate IDs never load. Ownership is checked before quota, routing, or provider work. - The student's material renders in delimited blocks; stored text cannot close them, and the untrusted-material rules close the system prompt. - Sparring stays transcript-only. Its scorecard sees no instructions or saved results. Crossfire uses saved results but not free-form instructions. - Whole saved results drop out when the budget is spent. The response's meta.debateContext reports what reached the model and what did not. - The per-user custom system prompt layer is retired; an old client's customSystemPrompt field is ignored. Admin prompts are unchanged. - Legacy sourceIds still work for clients that do not send debateContext. - A debate_context_pins table stores results the student pins per debate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eature A compact line directly above the primary button in Generate, Attack, Brainstorm, Factoids, Ask, Crossfire, and Sparring says which sources and saved results the next request will use. Review opens a sheet with checkboxes for that request, pins that persist per debate, and honest notes for material that is over the limit, too large, or empty. One-off mode says no debate material will be used or saved. The browser computes the default selection with the same shared rules as the server and sends only IDs. Review choices reset after one successful request, and the last response's usage report shows what reached the model. Sparring requests now use the round's own debate rather than whichever debate is current. The browser no longer sends the retired custom system prompt. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Generate: speech-ready claim, warrant, evidence, and impact with explicit weighing, and a consistent team line across speakers. - Attack: each weakness quotes its exact target, names the broken link, and gives a response the student can say word for word. - Brainstorm: ideas rest on different mechanisms, stakeholders, or values, and skip directions already saved in the debate. - Factoids: exact figures from the source and a limitations field that always labels the uncertainty. - Ask: answers the mode directly and uses only the material it received. - Crossfire: follows up on dodges and counts a concession only when the speaker's own words accept the point. - Sparring: stays in role during the round, and scores with feedback tied to specific speeches and nothing about voice or confidence. - Year ranges and a reference to a file the model cannot see are gone. prompt-examples.ts holds two representative requests per feature, one per side where the feature has sides. A Deno test checks them against each feature's rules, and scripts/prompt-examples.ts compares revisions on one real model. A full-stack journey covers the Review control, and the architecture, backend, and testing docs describe the new layout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Default privileges also gave anon access and UPDATE. A pin is only added or removed, so revoke everything and grant SELECT, INSERT, and DELETE to authenticated users. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…leted debates - Crossfire and Sparring show Review once, before the round. Their turns now keep those choices; the debrief or judging clears them, as does restarting or abandoning the round. Before, the second turn silently restored excluded sources and results. - A Crossfire round near its 100,000-character prompt budget no longer fails. It drops the saved results, and meta.debateContext lists them as omitted for size instead of used. - A debate request that returns 404 (deleted on another device) reloads the debate list. - The full-stack spec now expects the result a request just saved to join the next request's defaults, as the recency rule intends. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Programcode has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (73)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughAI requests now send Current Debate context IDs for server-side loading and feature-specific prompt use. The change adds context review and pinning controls, updates AI request handling and prompt modules, and adds tests, documentation, and prompt tools. ChangesCurrent Debate context and AI requests
Workspace invite test fixtures
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant AIClient
participant AIEndpoint
participant ContextLoader
participant PromptBuilder
participant AIProvider
AIClient->>AIEndpoint: Send debateContext IDs and feature input
AIEndpoint->>ContextLoader: Load caller-scoped debate material
ContextLoader-->>AIEndpoint: Return validated sources and output digests
AIEndpoint->>PromptBuilder: Build feature prompt with debate context
PromptBuilder-->>AIEndpoint: Return system and user prompts
AIEndpoint->>AIProvider: Submit prompt
AIProvider-->>AIEndpoint: Return generated output
AIEndpoint-->>AIClient: Return output and context usage metadata
Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to A timing issue can cause a result that was just unpinned to be selected for a later AI request. The affected material remains within the signed-in user’s debate; the review did not establish a cross-user access path. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
Warning Tools execution failed with the following error: Failed to run tools: Ping-pong health check failed Note 🎁 Summarized by CodeRabbit FreeYour 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 Thanks. On the retained low-severity concern (debate material loads before the request-limit check): we're keeping this ordering on purpose.
A dedicated throttle in front of the loader would add another admission path for a small, bounded read. It's a reasonable follow-up if abuse ever shows up in |
|
The author of this PR is on the CodeRabbit Free Plan. In order to use the Chat feature, please upgrade the PR author to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing. |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
… late A pin load that started before an unpin could bring the unpinned result back, and a failed save restored the whole list from before it, undoing other pin changes. Either way a result the student had unpinned could reach a later request. Pin changes now bump a per-debate counter that a stale load respects, and a failed save undoes only its own change. Found by CodeRabbit's security review on #75. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Fixed CodeRabbit's pin-race finding in 4a6ba4a. A pin change now bumps a per-debate counter, and a load that started before the change no longer overwrites it. A failed save undoes only its own change instead of restoring an older list. Two new tests reproduce both races and fail without the fix. |
|
@coderabbitai review |
|
|
@coderabbitai review |
|
|
@coderabbitai full review |
✅ Action performedFull review finished. |
|
The retained pin-race concern in the security section (still marked "up to 3c96d") is addressed in 4a6ba4a, following the proposed hardening. |
|
@coderabbitai full review 5a53509 fixes two pin-store issues found in an internal review:
|
|
Implements PR 2 from
.hermes/plans/2026-09-25_074255-debate-ai-quality-modules.md: every AI feature now uses the Current Debate, the seven feature prompts are sharper, and the 89 KB AI entrypoint is split into focused modules. The deployedaiandai-streamendpoints keep their names and request shape; the new field is additive.What changes for students
A short line sits directly above the main button in Generate, Attack, Brainstorm, Factoids, Ask, Crossfire, and Sparring, for example "Using 2 sources and 1 saved result: Case (In favor)". Review opens a sheet that lists the debate's Library sources and saved results with checkboxes for the next request (for the whole round in Crossfire and Sparring, where Review appears only before the round starts), pin buttons that keep a result in every request for that debate, and notes for anything over the limit, too large, or empty. After a request, the sheet reports what actually reached the model. One-off mode says that no debate material will be used or saved.
Screenshots come from the local full-stack Playwright run (real local Supabase, fake model provider). They're stored on the evidence-only branch
evidence/debate-aware-ai, so the links work for signed-in collaborators.Generate, desktop and mobile: the context line above the button.
Brainstorm Review sheet, after unchecking one source and pinning the saved case. On desktop, the full-page capture draws the sticky header partway down behind the fixed sheet; that is an artifact of the capture method, not of the layout.
How context works
debateContext: { debateId, side, sourceIds, outputs: [{ kind, id }] }. The default selection is the debate's active sources, then pinned results, then up to three of the newest results of the types that feature uses, for the requested side. The rules live in_shared/debate-context.ts, which both the browser and the server import._shared/debate-context-loader.tsloads the motion, settings, instructions, sources, and result digests with the service role. Every query filters by the caller's user ID and the one debate. A debate the caller doesn't own returns 404 before any quota, routing, or provider work. Forged or cross-debate IDs are never loaded; they're counted as unavailable.meta.debateContextreports what reached the model.customSystemPromptis retired. The browser no longer sends it, and the server ignores it from older clients. Operator prompts inglobal_promptsare unchanged.debate_context_pins(additive migration with RLS, a composite key to the owner's debate, and explicit grants).Prompts
Each feature now has its own module in
_shared/prompts/, with shared rules incommon.ts:Hard-coded year ranges are gone.
prompt-examples.tsholds two representative requests per feature (one per side where the feature has sides). A Deno test checks each against its feature's rules and the fake provider's routing lines.The runner (
scripts/prompt-examples.ts) sent the 14 examples to OpenRouter withopenai/gpt-5.6-luna, once withmain's prompts and once with this branch's. These were live model calls, not fixtures: 28 calls, all HTTP 200, about $0.047 in total. The system prompt grew by 2,100 to 3,000 characters per example (Generate: about 10,600 to 13,600).Two representative differences:
These calls ran on the prompt text at 55e1356. The later commits don't change prompt text.
Code layout
ai/index.tsshrank from 1,930 to about 800 lines and remains the entrypoint and dispatcher. One handler per feature lives inai/features/, withprovider.ts,request.ts,runtime.ts, andtext.tsbeside it._shared/prompts.tsandai/prompt-builder.tsstay as re-export facades. The split landed first as its own commit (11c736c), with every system and user prompt byte-identical tomain.Commit 49e67ab fixes an unrelated test time bomb: the workspace-invite email fixtures hard-coded an expiry of 2026-09-27 12:00 UTC, so four Edge tests started failing on
maintoday.Deploy order (not done here)
The client is backward compatible: it still sends the legacy
sourceIds, and pins fail soft when the table is missing. So Vercel can go first. To turn the feature on:20260927170000_debate_context_pins.sql(additive).aiandai-streamEdge Functions.Until step 2, the new client works against the old functions exactly as today.
Verification
All results below are local, at 4a6ba4a. GitHub Actions don't start on this repository because of the account's billing limit, so no hosted CI result exists for this head. These local runs are the equivalents of the hosted jobs.
./.superset/db-gate.sh(Docker Supabase, pgTAP, quota and concurrency scripts, full-stack Playwright): exit 0. 345 pgTAP tests passed. Playwright: 55 passed, 1 skipped (the mobile variant of a desktop-only workspace-collaboration test).npm run test:edge: 270 passed, 0 failed.npm run verify: exit 0. Docs check, lint, and typecheck are clean. Vitest: 1,093 tests in 109 files passed, 90.3% statement coverage. Collaboration: 5 files passed. Edge: 270 passed. Build and service-worker tests passed. Chromium E2E, desktop and mobile, against a fresh Vite server: 225 passed, 6 skipped, 1 flaky. The flaky test ise2e/home-navigation.spec.ts:89. It asserts with a 100 ms timeout that the onboarding tooltip is gone, and it passed on retry. This branch doesn't touch that spec or onboarding.Fixes from the pre-PR review
crossfire-client.test.tssends two turns and a debrief. It fails without the fix.meta.debateContextlists them inoutputsOmittedForSize, notoutputsUsed. The Edge test inai/debate-context.test.tsreproduced the 500 before the fix.aiandai-streamclients now reload the debate list on that 404, with a unit test that fails without the change.🤖 Generated with Claude Code
Summary by CodeRabbit