Skip to content

feat: add personal current debate context - #72

Merged
Programcode merged 7 commits into
mainfrom
t3code/implement-current-debate-foundation
Sep 27, 2026
Merged

Programcode merged 7 commits into
mainfrom
t3code/implement-current-debate-foundation

Conversation

@Programcode

@Programcode Programcode commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Implements PR 1 of .hermes/plans/2026-09-25_074255-current-debate-foundation.md: every signed-in user now has a personal Current Debate, and their work is filed under it.

What changes

  • Header: now shows Home, Current debate, Library and History. Tool links moved to Home and the debate dashboard. On mobile the header shows the current motion, and the menu holds everything else.
  • Home and dashboard: Home lists the tools. The dashboard shows the motion, recent activity, the In favor and Against columns, sources, recent work, and a tool grid.
  • Feature strip: every tool shows a strip reading "Working in: " with Change and Use once buttons. One-off mode runs a tool without reading from or saving to a debate.
  • Motion gate: entering a motion that differs from the Current Debate asks whether to create a new debate or use the result once. A debate idle for more than five days asks which debate the work is for.
  • Library and History: both are scoped to the Current Debate. Legacy material with no debate appears as Unsorted.
  • Isolation: Ask, Generate, Crossfire, Sparring and source folders stay inside the debate they started in. That holds when you switch debates, toggle one-off mode, delete a debate, or reload the page.
  • Database: adds debates and current_debates with RLS and composite foreign-key ownership, plus a debate_id on each result table. A private rollout timestamp is recorded when the migration runs.

Destructive migration

20260926090100_legacy_debate_cleanup.sql permanently deletes legacy work that has no debate and hasn't been touched in the 30 days before rollout. "Touched" means created, edited (flowcharts, sparring rounds) or messaged (chats). Anything newer stays and shows as Unsorted. This must not be applied to staging or production without Adam's explicit approval. Nothing in this PR applies it remotely.

Evidence

Screenshots and videos come from the full-stack Playwright journey (e2e/full-stack/current-debate.spec.ts), run against the local Supabase stack and the fake OpenRouter. It covers two debates, homework and zoos: work on the first, switch to the second, add a source, get the different-motion prompt, check the zoo dashboard, then switch back and see only homework in History. It runs on desktop and mobile Chromium.

Local results at 656aae6

  • npm run verify steps:
    • docs, lint and typecheck: clean.
    • Vitest: 108 files, 1075 tests passed.
    • Collaboration: 30 tests passed.
    • Edge Functions: 240 passed.
    • Build: passed.
    • Service-worker offline check: passed.
    • Playwright (desktop and mobile Chromium): 226 passed, 6 skipped.
  • ./.superset/db-gate.sh:
    • pgTAP: 12 files, 336 tests, PASS.
    • Full-stack Playwright: 53 passed, 1 skipped. The skipped test is the existing mobile workspace-collaboration.spec.ts:353.

How verify was run: the first full run hit a 5s timeout in route-modules.test.ts while the machine was under load. That test's cold import takes the same 2.1s on main, and it passed on rerun. The second run stopped at the service-worker check, which still clicked the Attack link this PR removes from the header. The script now reaches Attack from Home, and every step from lint onward was rerun on the final commit. The unit tests, collaboration tests and Edge Function tests last ran one commit earlier, and that commit changes only a CSS class and the service-worker script. The strip unit tests (11) were rerun on the final commit.

Not verified

  • WebKit, staging and live-provider suites weren't run.
  • A Codex second opinion wasn't available, so a second Opus security review covered the migration and RLS instead. Its finding, that the purge judged age by created_at alone, is fixed.

🤖 Generated with Claude Code

Desktop: zoo debate dashboard

Desktop: different-motion prompt

Desktop: Brainstorm working in the homework debate

Desktop: Library scoped to the zoo debate

Desktop: History back in the homework debate

Mobile: zoo debate dashboard

Mobile: different-motion prompt

Mobile: Brainstorm with the wrapped debate strip

desktop-two-debate-journey.webm
mobile-two-debate-journey.webm

Summary by CodeRabbit

  • New Features
    • Added Current Debate controls and a dashboard for viewing debate details, sources, and saved work, with options to create, switch, and manage debates.
    • Debate tools can use the current debate’s motion and keep generated work and sources associated with it. Different motions can be used once without saving them to a debate.
    • Library and History views now show debate-scoped content, with options to review and assign unlinked work.
    • Added an inactivity prompt for debates with no activity for five days.
    • Ask conversations can be saved with their debate context.
    • Simplified navigation around Home, Current Debate, Library, and History.
  • Documentation
    • Updated architecture documentation to describe debate workflows and chat persistence.

RetriggerConfidence Score: 0/5

Do not merge until the cleanup, source ownership, wrong-debate deletion, hydration, and saved-round failures are fixed. The folder-filter concern alone would not block merging.

Fix All in CodexFindings

  1. P1 Cleanup runs during migration ▶
  2. P1 One-off sources enter debates ▶
  3. P1 Workspace research enters personal debates ▶
  4. P1 Library clear targets switched debate ▶
  5. P1 Hydration overwrites new debates ▶
  6. P1 Saved Crossfire rounds cannot reopen ▶
Fix with agent prompt
### Issue 1
supabase/migrations/20260926090100_legacy_debate_cleanup.sql:43-45
If this migration is applied before Adam approves the legacy cleanup, this call immediately deletes qualifying cases, sources, chats, and related messages. There is no separate approval step when applying the migration, so legacy work can be permanently lost. Keep the destructive call out of ordinary migration application until approval is given.

### Issue 2
src/lib/stores/session-store.ts:492
Ask and Factoids save sources without a debate ID. Even with “Use once” enabled, this fallback selects the retained Current Debate, so those sources are saved as personal debate work instead of remaining outside it.

### Issue 3
src/lib/ai/client.ts:1195
Workspace requests pass no personal debate ID, but source autosave substitutes whichever personal debate is active when the response arrives. Discovered workspace sources enter an unrelated personal debate, and switching debates while a request runs changes their destination.

### Issue 4
src/lib/stores/session-store.ts:785
If someone confirms Clear All in debate A and switches to B while authentication is pending, this code reads B’s ID after the wait. It deletes B’s sources instead of the sources the person confirmed deleting in A.

### Issue 5
src/lib/stores/debate-store.ts:141-145
If someone creates or switches to a debate while initial loading is in flight, the older query results replace the newer debate list and Current Debate selection. A newly created debate disappears from the local menu, or a completed switch reverts, so subsequent work can target the wrong debate.

### Issue 6
src/components/features/debate/debate-items.ts:105
Completed Crossfire rounds appear as saved work, but this loader refuses to open them and their dashboard and Unsorted entries have no Load action. Once the live round is gone, its saved transcript and debrief cannot be revisited through those entries.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR adds personal Current Debates, debate-scoped work and sources, and a one-time legacy cleanup. It is not safe to merge yet: applying the cleanup migration can delete legacy work before approval; one-off and workspace sources can enter personal debates; a Library clear can delete the wrong debate’s sources; initial loading can undo newer debate actions; and saved Crossfire rounds cannot be reopened. The Library also offers folders belonging only to other debates, a non-blocking usability concern.

Reviews (1) · Last reviewed commit: "fix: wrap current debate strip actions o..."

Programcode and others added 6 commits September 26, 2026 06:42
Record results without turning a save failure into a generation failure,
add unit tests for the inactivity gate, motion choices, and auto-attach,
add a two-debate full-stack journey, and move stubbed browser specs to the
new header (tools open from Home and the Current Debate dashboard).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review fixes:
- Ask records into the chat it has open, keeps loaded unsorted chats out of
  the Current Debate, starts fresh when the debate changes, and no longer
  hands an in-flight chat to a conversation started after clearing.
- Regenerating a case and continuing a restored Crossfire round use the
  result's own debate, falling back to the Current Debate only when the
  motion matches.
- Renaming or deleting a Library folder moves only the Current Debate's
  sources.
- Deleting a debate drops its local work.
- The one-time legacy cleanup is anchored to the recorded rollout time and
  measures last activity (edits, latest chat message), so later one-off work
  and recently used legacy work survive. pgTAP covers every purged table.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Entering or leaving one-off mode now resets Ask the same way a debate
switch does, so one-off exchanges are never saved into the debate and an
open debate chat does not continue in one-off mode. A reply still in
flight when the scope changes is dropped.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The offline service-worker check now reaches Attack from Home, since
the header no longer lists tools.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 26, 2026 20:59
@vercel

vercel Bot commented Sep 26, 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 Sep 26, 2026 9:52pm UTC

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 Sep 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 27540396-f7e3-4086-b39b-efd3fce39c95

📥 Commits

Reviewing files that changed from the base of the PR and between 656aae6 and 5787c15.

📒 Files selected for processing (7)
  • src/lib/ai/client.ts
  • src/lib/stores/debate-store.ts
  • src/lib/stores/session-store.ts
  • src/pages/LibraryPage.tsx
  • src/test/ai/client-branches.test.ts
  • src/test/pages/LibraryPage.test.tsx
  • src/test/stores/session-store.test.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

Walkthrough

The application adds Current Debate controls and a dashboard, debate-scoped storage for sources and saved work, and one-off workflows. Tool requests, History, Library, and database records now use debate associations. Tests and architecture documentation cover these changes.

Changes

Current Debate

Layer / File(s) Summary
Database ownership and legacy cleanup
supabase/migrations/*, supabase/tests/current_debates_test.sql, supabase/README.md
Adds debate and current-debate tables, associations on saved records, ownership checks, attachment operations, activity updates, and a one-time cleanup for inactive legacy records.
Debate and session persistence
src/lib/stores/debate-store.ts, src/lib/stores/session-store.ts, src/lib/stores/sparring-store.ts, src/lib/supabase/AuthProvider.tsx, src/pages/SettingsPage.tsx, src/test/stores/*
Adds debate hydration and lifecycle state. Sources, saved results, chats, and sparring sessions retain debate associations.
Debate controls and scoped views
src/components/features/debate/*, src/components/layout/AppLayout.tsx, src/App.tsx, src/lib/route-modules.ts, src/pages/CurrentDebatePage.tsx, src/pages/HomePage.tsx, src/pages/HistoryPage.tsx, src/pages/LibraryPage.tsx, src/locales/*.json, docs/architecture.md
Adds current-debate controls and dashboard, debate selection in History, and debate-scoped Library views. Adds translations and architecture documentation.
Debate-aware tool workflows
src/lib/ai/client.ts, src/lib/debate/*, src/components/features/{brainstorm,generate,sparring}/*, src/components/features/AutoSavedSourcesUndo.tsx, src/pages/{Ask,Attack,Brainstorm,Crossfire,Factoids,Flowchart,Generate,Sparring}Page.tsx
Tool requests pass debate scope to AI calls and filter sources by debate. Eligible results and practice sessions are recorded against debates. Motion gates, one-off handling, and motion prefill are added.
End-to-end and unit coverage
e2e/*, scripts/test-service-worker-offline.mjs, src/test/*
Updates tests for revised navigation and debate selection. Adds coverage for debate-scoped requests, records, persistence, and database associations.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant GeneratePage
  participant useDebateMotionGate
  participant AIClient
  participant SessionStore
  User->>GeneratePage: Submit motion
  GeneratePage->>useDebateMotionGate: Resolve debate target
  useDebateMotionGate-->>GeneratePage: Return debateId or one-off target
  GeneratePage->>AIClient: Generate case with debateId
  AIClient-->>GeneratePage: Return generated case
  GeneratePage->>SessionStore: Record case when debateId exists
Loading

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 5787c

Database checks limit work to its owner, but deleting a debate can leave a local copy of its work after an in-flight operation completes. The rollout also permanently removes qualifying older work across accounts. These lifecycle and deletion guarantees merit design review.

Retained concerns

  • Low · security · inferred: An attach response arriving after debate deletion can reintroduce deleted work into the browser’s persisted cache. Server-side deletion still holds, but local deletion is not guaranteed until successful reconciliation.
Security review details

Security Blast Radius

  • inferred — The demonstrated attach and AI lookup controls constrain a signed-in caller to their own database rows. The cleanup has a different scope: it deletes qualifying unattached legacy rows across users when the migration runs.

Security Findings and Attack Paths

  • inferred — A user who supplies IDs from another debate they own can still have those sources included in AI input, because the endpoint checks user ownership but receives no debate identity. This endpoint behavior predates the PR and ordinary client selection now narrows sources; it is not evidence of newly expanded cross-user access.

Trust Boundaries and Controls

  • observed — The privileged AI database client does not rely on caller-provided ownership: it validates the bearer token and explicitly filters source lookup by the resulting user ID and active status. The attach RPC likewise derives identity from the authenticated caller rather than trusting its debate ID argument.

Resilience and Maintainability Implications

  • inferred — Conditional updates make competing attach calls single-winner and prevent reassignment, but a retry after an ambiguous successful response returns an already-attached error. Cache reconciliation after deletion is also sensitive to response ordering.

Hardening Proposals

  • proposed — If debate isolation must hold against modified clients, include an authorized debate identity in the AI request and enforce it when resolving source IDs, while preserving the authenticated user filter.
  • proposed — Reconcile in-flight attach and chat completions against the current debate set before updating persisted local work; verify delayed responses during deletion and recovery after a failed hydration.

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.

Comment thread src/lib/stores/session-store.ts Resolved
Comment thread src/lib/ai/client.ts Resolved
Comment thread src/lib/stores/session-store.ts Resolved
Comment thread src/lib/stores/debate-store.ts Resolved
@greptile-apps

greptile-apps Bot commented Sep 26, 2026

Copy link
Copy Markdown

Comments Outside Diff

These findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.

  • P1 Ordinary migration execution deletes legacy work without separate approval ▶

    • Bug
      • Applying the migration immediately removes qualifying legacy work. The local repro lost a case, source, chat, and chat message solely by executing the migration.
    • Cause
      • The unconditional SELECT private.purge_legacy_debate_material(...) at supabase/migrations/20260926090100_legacy_debate_cleanup.sql:43-45 invokes the DELETE statements defined earlier in the file.
    • Fix
      • Remove the purge invocation from the ordinary migration. Require an explicitly approved, separately run cleanup after reviewing affected rows and arranging a backup.
  • P1 Use once sources are saved into the Current Debate ▶

    • Bug
      • Ask and Factoids omit debateId when calling addSource. Even in Use once mode, both calls insert a source with the retained Current Debate ID, contrary to the mode's no-debate scope.
    • Cause
      • src/lib/stores/session-store.ts:492 falls back to useDebateStore.getState().currentDebateId without checking oneOff; activeDebateId() would return null in this state.
    • Fix
      • Resolve an omitted source debate ID through the one-off-aware debate scope, and handle the resulting no-debate save explicitly rather than silently attaching it to the Current Debate.
  • P1 Clearing A’s library deletes B’s sources after a debate switch ▶

    • Bug
      • The user confirms Clear All while viewing A, switches to B before authentication resolves, and the resulting DELETE is filtered to debate B. B’s local sources are removed too.
    • Cause
      • src/lib/stores/session-store.ts:780–788 awaits getUserId() before reading currentDebateId at line 785, so the operation uses the debate current after the wait rather than at confirmation.
    • Fix
      • Capture and validate the debate ID when the clear action is confirmed, then pass that ID through the authenticated delete and local-state update.
  • P1 Saved completed Crossfire rounds cannot be reopened ▶

    • Bug
      • Dashboard and Unsorted display saved Crossfire entries without a Load action, leaving their saved transcript and debrief inaccessible from those entries.
    • Cause
      • src/components/features/debate/debate-items.ts:105 returns false for Crossfire. The dashboard hides its Load button at src/pages/CurrentDebatePage.tsx:44–56, as does Unsorted at src/components/features/debate/UnsortedWork.tsx:66–68; the generic tool route does not select the saved round.
    • Fix
      • Provide a saved-round loader or viewer and expose an Open action for Crossfire entries.
  • P2 Folders ignore debate scope src/pages/LibraryPage.tsx:326 ▶

    The Library offers account-wide folders even though its sources are limited to the Current Debate. Selecting a folder used only by another debate produces an empty view with folder actions. This is a non-blocking usability concern: the irrelevant filter can mislead users and waste time.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

  • P2 Other debates’ folders appear in the current debate’s Library ▶

    • Bug
      • A user can select a folder belonging only to another debate. The current debate’s source disappears, the other debate’s source remains correctly hidden, and the page offers folder actions over an empty result.
    • Cause
      • At src/pages/LibraryPage.tsx:325-326, folders includes account-wide sourceFolders; the source list and visible-source filter are scoped to the current debate at lines 177 and 328-329.
    • Fix
      • Scope offered folder filters to folders relevant to the current debate, and avoid presenting actions for folders that have no current-debate sources unless that behavior is explicitly intended.

- Ask and Factoids no longer save into the retained debate in one-off mode.
- Workspace research is not autosaved into a personal debate.
- Clear All deletes the debate it was confirmed in, even after a switch.
- A debate load that started before a create or switch reloads instead of
  overwriting it.
- The Library hides folders only other debates use.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@greptile-apps greptile-apps Bot 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.

Programcode has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@Programcode

Copy link
Copy Markdown
Owner Author

Pushed 5787c15 in response to Greptile's review. It fixes one-off source saves, workspace autosave scope, the Clear All race, the hydrate race, and folders shared across debates (the Library now hides folders only other debates use). The cleanup migration and Crossfire reopening findings are answered inline. Local runs at 5787c15: 1079 unit tests passed, 226 browser tests passed with 6 skipped, 336 pgTAP tests passed, and 53 full-stack browser tests passed with 1 skipped. @greptileai please re-review.

@greptile-apps greptile-apps Bot 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.

Programcode has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@Programcode

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

This branch was successfully deployed

1 active deployment
Preview — 5787c157 Deployed Sep 26, 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