Skip to content

Design audit 3/4: AI tools and practice tools - #128

Merged
Programcode merged 14 commits into
mainfrom
design-audit/ai-tools
Oct 11, 2026
Merged

Programcode merged 14 commits into
mainfrom
design-audit/ai-tools

Conversation

@Programcode

Copy link
Copy Markdown
Owner

Third of four stacked PRs for the 2026-10-09 design audit. Base: #126 (Shell).

What changed

Ask

  • New text scrolls the chat only when you're already at the bottom, inside the chat and never the window. Otherwise a "Jump to latest" pill appears. Before, every 30ms token called scrollIntoView({behavior:'smooth'}), which pulled the whole page down mid-read.
  • The stored reply keeps the streaming bubble's id, so the answer no longer remounts (and flickers) when it finishes.
  • The box stays editable while the answer streams. Send becomes Stop, which keeps the partial answer. A failure stays above the box with a Retry that doesn't post the question twice.
  • One honest status line ("Working…" or "Searching the web…", plus elapsed time) replaces the typing dots, the streaming glow and the token fade. Sending no longer animates your own bubble.
  • The four switches and the model picker move into an Options popover. The chat height uses dvh, so the box isn't hidden behind the phone's toolbar.

Generate, Attack, Brainstorm, Factoids

  • Every request takes an AbortSignal, and the form shows Cancel while it runs.
  • One spinner and one status line replace five different loaders, the shimmer on Generate's button and a progress bar that didn't measure anything.
  • Errors are an inline panel with Retry, not a toast that disappears. Error copy is localized.
  • The success toasts are gone; the result itself is the confirmation, and on phones it scrolls into view.
  • One ResultHeader: title, meta, actions and the AI caveat. Generate marks evidence Unverified when no web research backs it. Researched sources sit in a disclosure under the header.
  • "Discuss" builds its prompt through i18n and sends it from Ask on the streaming path. Markdown has an inline mode, and links are restricted to safe protocols.
  • Deleted: StreamingIndicator, AnimatedSection, StaggeredContainer, AutoSavedSourcesUndo, the stagger and message keyframes. The form cards drop their duplicate titles, and the no-debate notice appears once.

Timer, Sparring, Crossfire

  • One RoundClock for all three: Inter tabular figures, amber in the last 30 seconds, red at zero. Before, the Timer turned calm again at 0:00. A screen reader hears 1:00, 0:30 and 0:00 instead of every tick.
  • Timer:
    • the ring sweeps linearly (it was clip-path 1000ms ease per tick, so it surged and stopped every second);
    • the clock counts milliseconds, so pausing no longer gives back up to a second (unit test);
    • a wake lock while running, and sound plus vibration at 0:00;
    • R and Escape reset only while paused, and Reset offers Undo;
    • keyboard hints are hidden on touch.
  • Crossfire: the clock ticks inside its own component instead of re-rendering the page twice a second. The header shows the resolution. The composer stays editable while the AI works (only Send waits), and only turns that just arrived fade in.
  • Sparring: one spinner instead of a ping, a pulse and bouncing dots. The opponent's latest speech is pinned above the composer, so you can answer it without scrolling up.

Flowchart

  • Edges are static, and rebuttals are dashed instead of marching.
  • Deleting a node or a saved flowchart acts at once with Undo, which finishes the delete rule from PR2.
  • Auto layout glides in 250ms with fitView.
  • The mouse wheel scrolls the page instead of zooming the canvas, and new nodes appear at the centre of the visible canvas.
  • Handles and controls are touch-sized, and the page has a real h1.

Changes from the audit's literal After

None. Every PR3 row follows the recommended After, and each is ticked in the plan.

Verification

All runs are local. GitHub Actions is out of minutes: every job fails in about 2 seconds with "recent account payments have failed".

  • npm run lint, typecheck, npm test (276, including the new timer-clock test), build and docs:check all pass at a168d14. The touch-size fix in 4fee6f5 changes only CSS, and its effect was measured in the browser.
  • verify.sh drive all: 30 passed and 2 skipped on desktop-chromium, and the same on mobile-chromium. The skips are workspace-autosave, which needs --collab. Recipes updated in this PR: account (flowchart Undo instead of a confirm) and library (now on the Shell base).
  • verify.sh e2e run (agentic): 174 passed, 4 skipped, 1 failed and 1 interrupted when my 30-minute background limit cut the run off.
    • The failure was the Spanish landing badge "Prep IA diaria", which has an English word in it. The copy predates this PR and is fixed in the Landing PR.
    • The first attempt had 85 engine failures because Playwright's ffmpeg was missing on the machine. I installed it and reran.
    • Re-recorded replays are committed.
  • Targeted e2e specs for the changed flows pass: sparring, voice-rounds (10/10), animation-polish and quality-backlog. The GeneratePage and SparringVoice unit tests are updated.
  • Browser measurements, before and after:
    • Timer ring: clip-path 1000ms cubic-bezier(0.4,0,0.2,1) per tick, now 1000ms linear.
    • Timer digits: Space Mono with −3.6px tracking, now Inter tabular with −1.44px.
    • Generate at rest: one infinite shimmer 3000ms, now none.
    • Pressing Enter in Ask: message-enter 300ms plus streaming-glow-pulse 1500ms, now neither.
    • Under reduced motion the Select still fades instead of popping.
  • Screenshots of all nine tool pages at 1440px and 375px in light and dark, plus Hungarian at 375px, all at the current head: 45 states with no horizontal overflow. Hungarian shows no clipped labels. The probe's one hit is the Timer's visually hidden "Időmérő kijelző" label.
  • Touch targets with a coarse pointer at 375px:
    • Generate has none under 44px.
    • Sparring's and Crossfire's only small controls are a switch and a checkbox; both have invisible hit areas.
    • The flowchart's zoom controls were still 26px, because React Flow's stylesheet beat the PR's rule. Fixed in 4fee6f5 and re-measured at 44px.
    • What remains on the flowchart are the SVG edges, which carry their own wide hit area, and React Flow's attribution link.

Not verified: real touch hardware (wake lock, vibration), WebKit and Safari, live AI output (fake provider only), staging and production.

🤖 Generated with Claude Code

Programcode and others added 7 commits October 9, 2026 12:49
…eader

Fixes the AI tools rows of the 2026-10-09 design audit (Ask and the four
form tools; the practice tools follow in the next commit).

- Ask: the stored reply keeps the streaming bubble's id, so it doesn't
  remount mid-read. New text scrolls only a reader at the bottom, inside
  the chat, never the window; otherwise "Jump to latest" appears. The box
  stays editable while answering, Send becomes Stop (which keeps the
  partial), and failures stay above the box with a Retry that doesn't
  duplicate the question. The four switches and the model picker move into
  an Options popover. The status is one true line ("Searching the web…",
  "Working…") plus elapsed time, and the height uses dvh.
- Attack, Brainstorm, Factoids, Generate: requests take an AbortSignal and
  show Cancel. One spinner and one status line replace five loops and a
  fake progress bar. Errors are an inline panel with Retry. The success
  toasts are gone, results scroll into view on phones, and the empty
  states use the Lucide EmptyState.
- One ResultHeader: title, meta, actions, and the AI caveat. Generate marks
  evidence "Unverified" when no web research backs it. Researched sources
  sit in a disclosure under the header.
- "Discuss" prompts are built line by line through i18n and sent from Ask
  on the streaming path. Markdown gets an inline mode and safe links.
- Deleted: StreamingIndicator, AnimatedSection, StaggeredContainer,
  AutoSavedSourcesUndo and the unused showUndo prop, the stagger and
  message keyframes, the typing dots, the streaming glow and token fade.
  The caret blinks by opacity only. Error copy is localized, the form
  cards drop their duplicate titles, and the no-debate notice appears once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes the Timer, Crossfire, Sparring and Flowchart rows of the 2026-10-09
design audit.

- One RoundClock for Timer, Sparring and Crossfire: Inter tabular figures,
  amber in the last 30 seconds, red at zero (the Timer no longer goes calm
  at 0:00). A ClockAnnouncer speaks only at 1:00, 0:30 and 0:00 instead of
  every tick.
- Timer: the ring sweeps linearly; the clock works in milliseconds, so a
  pause no longer gives up to a second back (unit test); a wake lock
  while running; sound and vibration together at 0:00; R and Escape
  reset only while paused, and the reset button offers Undo; keyboard
  hints are hidden on touch; solid card; no pulsing digits.
- Crossfire: the clock ticks inside its own component instead of
  re-rendering the page twice a second; the header shows the resolution;
  the composer stays editable while the AI works (only Send waits); only
  turns that just arrived fade in.
- Sparring: one spinner instead of a ping, a pulse and bouncing dots; the
  opponent's latest speech is pinned above the composer.
- Flowchart: static edges, with rebuttals dashed; deleting a node or a
  saved flowchart acts at once with Undo; auto layout uses fitView with
  a 250ms glide; wheel scrolls the page; new nodes appear at the visible
  centre; touch-sized handles and controls; a real h1.
- One ListeningIndicator for Ask, Generate and voice rounds, with no
  pulsing buttons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- sparring.spec reads the opponent's reply from the transcript and checks
  it is pinned above the composer.
- The flow-edit recipe proves a node deletes at once and Undo restores
  it; the feature map says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inned copy

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
React Flow's stylesheet loads after ours and sizes the buttons content-box, so
the coarse-pointer rule needs the extra class and border-box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 13:08
@vercel

vercel Bot commented Oct 9, 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 11, 2026 12:22pm UTC
debatelab-staging Ready Ready Preview Oct 11, 2026 12:22pm 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 Oct 9, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Free
  • Run ID: bba07f72-e67d-4363-b105-dedc8bd0de94

📥 Commits

Reviewing files that changed from the base of the PR and between fbbb8da and 80dc67b.


📒 Files selected for processing (75)
  • .claude/skills/verify-debatelab/features/README.md
  • .claude/skills/verify-debatelab/features/ai-tools.md
  • .claude/skills/verify-debatelab/features/case-generation.md
  • .claude/skills/verify-debatelab/recipes/account.spec.ts
  • .claude/skills/verify-debatelab/recipes/ai-tools.spec.ts
  • .claude/skills/verify-debatelab/recipes/case-generation.spec.ts
  • agentic/replay/0b6684c4cf8f85ea83d9d2c90b3638a2f99b41bd44866a41d64915470d42a880.json
  • agentic/replay/126b78cf76461974e31c525473531fea764059779def23ffec087cecc00a0950.json
  • agentic/replay/2296281fbea5e96beb19e409eaaf03e9257953f151152980f9f33713c14bf2c3.json
  • agentic/replay/23755b48030e2ba88ae7593c0e65506ecd8b0b7d98a4006cfda221eda5edb068.json
  • agentic/replay/495abc7af387dc9131381dcd246cd42ee762f88ed3d578f31d3211625788e805.json
  • agentic/replay/4b19b5a96c8088da6cfdc7035b87036fd9cc0ac6dbdd9fc8d128176a57f60dfd.json
  • agentic/replay/6944dfabce5ae31228a20c2c28d91e6b10f370683c1bdd64349ee6ebd73846e3.json
  • agentic/replay/729490fb7619dcddf35c0c60cedc58d2bc05aff8f07aad051c9ef2cd457f9799.json
  • agentic/replay/79a9ee2259d8d16afd564cc66fa33f4b3906e33910768c64dc5c2d643baa1ea1.json
  • agentic/replay/d636e78bd57e1346e9ad878bdd394cbf70ccb746bdcfcc35af4c0a7d2cf6795e.json
  • agentic/replay/d73284119fd8669a81b6ad8f2680b21f11103285e5b28beed208c736f63fd98c.json
  • agentic/replay/d837eeb2b0665a23180f65fb205e2ea67f8b44f1a1a6a400067d474f1b21cb5f.json
  • agentic/replay/e0c912b52a6de1dadb5287bb54346d1fac813a9d7ac70a1742898dc3abdf4a99.json
  • agentic/replay/e753f1e71c4d4ed90310f668e44ebdc847f876ca5ec6d2ade3d672c29aedcaf9.json
  • e2e/animation-polish.spec.ts
  • e2e/authed-pages.spec.ts
  • e2e/full-stack/ai-tools.spec.ts
  • e2e/live/research.spec.ts
  • e2e/navigation-performance.spec.ts
  • e2e/quality-backlog.spec.ts
  • e2e/sparring.spec.ts
  • e2e/voice-rounds.spec.ts
  • plans/2026-10-09-design-audit.md
  • src/components/features/AutoSavedSourcesUndo.tsx
  • src/components/features/ExportToolbar.tsx
  • src/components/features/ask/AskChat.tsx
  • src/components/features/attack/AttackForm.tsx
  • src/components/features/attack/AttackResults.tsx
  • src/components/features/brainstorm/BrainstormForm.tsx
  • src/components/features/brainstorm/BrainstormResults.tsx
  • src/components/features/debate/DebateContextControl.tsx
  • src/components/features/factoids/FactoidsForm.tsx
  • src/components/features/factoids/FactoidsResults.tsx
  • src/components/features/generate/GenerateForm.tsx
  • src/components/features/generate/GenerateResults.tsx
  • src/components/features/practice/RoundClock.tsx
  • src/components/features/sparring/SparringRound.tsx
  • src/components/features/sparring/SparringSetup.tsx
  • src/components/features/tools/ResearchedSources.tsx
  • src/components/features/tools/ResultHeader.tsx
  • src/components/features/tools/discuss.ts
  • src/components/features/tools/tool-run.tsx
  • src/components/features/voice/ListeningIndicator.tsx
  • src/components/features/voice/VoiceMicControl.tsx
  • src/components/ui/animated-section.tsx
  • src/components/ui/empty-state.tsx
  • src/components/ui/loading-spinner.tsx
  • src/components/ui/markdown.tsx
  • src/components/ui/streaming-indicator.tsx
  • src/index.css
  • src/lib/ai/client.ts
  • src/lib/ai/error-display.ts
  • src/lib/stores/session-store.ts
  • src/lib/timer-clock.ts
  • src/locales/en.json
  • src/locales/es.json
  • src/locales/hu.json
  • src/pages/AskPage.tsx
  • src/pages/AttackPage.tsx
  • src/pages/BrainstormPage.tsx
  • src/pages/CrossfirePage.tsx
  • src/pages/FactoidsPage.tsx
  • src/pages/FlowchartPage.tsx
  • src/pages/GeneratePage.tsx
  • src/pages/TimerPage.tsx
  • src/test/pages/GeneratePage.test.tsx
  • src/test/pages/SparringVoice.test.tsx
  • src/test/timer/timer-clock.test.ts
  • src/test/tools/tool-run.test.tsx

  • 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 and others added 2 commits October 9, 2026 17:00
- A form tool's request belongs to the tool, not one mount of its page, so Cancel
  still works after leaving and coming back (unit test). Every page checks the signal
  after the request returns, so a canceled run never publishes or saves.
- Timer Undo returns to the paused remainder and stops the clock, instead of writing
  a paused clock under a running timer, which froze it.
- Flowchart node Undo belongs to the chart it was deleted from: loading another chart
  dismisses it. A saved-chart delete writes first and throws on failure, so Undo is
  only offered once the row is gone.
- Ask renders stored and streaming messages as one keyed list, so the finished reply
  keeps its bubble instead of remounting.

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

Copy link
Copy Markdown
Owner Author

Codex review round 1 (on 4fee6f5) found six issues. All are fixed in bac0f45:

  1. Cancel could still publish a result. If the request resolved after Cancel, the result appeared anyway. Each tool page now checks the signal right after the request returns, before setting or saving anything.
  2. Cancel lost the request after navigation. The request now belongs to the tool, not to one mount of its page. A new unit test, src/test/tools/tool-run.test.tsx, fails on the old hook and passes on the new one.
  3. Timer Undo could freeze the clock. If you reset and then started again, Undo wrote a paused clock under a running timer. Undo now also stops the run. In the browser: paused at 3:58, Reset, Start, Undo shows 3:58 paused, and Start resumes to 3:56 two seconds later.
  4. Flowchart node Undo could land in another chart. Loading a chart now dismisses the node's Undo, and Undo checks it is still on the chart it came from.
  5. Saved-chart Undo could duplicate a chart when the delete failed. The delete now writes first and throws on failure, so Undo is offered only once the row is gone.
  6. The finished Ask reply remounted. Stored and streaming messages now render as one keyed list. In the browser, the DOM node captured while streaming is still the one on screen after the reply finishes.

Also in 8db4a3b: the Timer's Reset button was announced as "R to reset" (the keyboard hint). It now has a real name in en, es and hu. That label came from main.

Local checks at 8db4a3b: lint, typecheck, npm test (278), build and docs:check all pass. Codex round 2 is reviewing 8db4a3b.

…ell header is the banner

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

- A saved chart's Delete is disabled while its delete is in flight, so a double click
  can't offer two Undos (which would save two copies).
- Signed in, a failed user lookup now fails the delete instead of skipping the server
  row and offering an Undo that would duplicate it. Signed out stays local-only.

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

Copy link
Copy Markdown
Owner Author

Codex round 2 (on 8db4a3b) confirmed the six round-1 fixes and found two gaps in the saved-flowchart delete. Both are fixed in a41e963:

  1. A double click could delete twice and offer two Undos. Delete now writes before removing the chart locally, and a second click arriving while the first delete was in flight got its own Undo toast. Two Undos would save two copies. The row's Delete button is now disabled while its delete is in flight.
  2. A failed user lookup skipped the server delete. If the auth read failed while you were signed in, the delete ran locally only and still offered Undo. It now fails instead, so you get the error toast and no Undo. Signed out, charts stay local-only as before.

Local checks at a41e963: lint, typecheck, npm test (278) and build all pass. Codex round 3 is reviewing a41e963.

…s clickable while busy

A disabled button has pointer-events: none, so a second click fell through to the row
and loaded the chart. The button is now aria-disabled and dimmed, and the handler
already ignores repeat clicks. getSession errors no longer read as signed out.

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

Copy link
Copy Markdown
Owner Author

Codex round 3 (on a41e963) found two gaps in the round-2 fixes. Both are fixed in cca52c1:

  1. A failed session read counted as signed out. getSession() errors made the delete local-only while it still offered Undo. The error is now thrown, so you get the failure toast and no Undo.
  2. The disabled Delete button let a second click through to the row underneath, because the shared button style sets pointer-events: none. That click loaded the chart. The button now stays clickable but marked busy (aria-disabled, dimmed), and its handler ignores repeat clicks.

Local checks at cca52c1: lint, typecheck, npm test (278) and build all pass. Codex round 4 is reviewing cca52c1.

The row was a role=button wrapping the Delete button, so a click or an Enter/Space
on a busy Delete could reach the row and load the chart being deleted. The title is
now its own load button beside Delete, and Delete is disabled while its delete runs.

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

Copy link
Copy Markdown
Owner Author

Codex round 4 (on cca52c1) confirmed the session-error fix and found one gap. While a delete was running, Enter or Space on the Delete button could still reach the clickable row and load the chart being deleted. Fixed in 0770b94 at the root: the row was a role=button wrapping the Delete button. Now the title is its own Load button beside Delete, and Delete is disabled while its delete runs. Nothing is nested, so nothing can leak from one button to the other. This is the same pattern PR2 used for Library rows.

Checked at 0770b94:

  • lint, typecheck, npm test (278) and build all pass.
  • verify.sh drive recipes/account: 5 passed on desktop-chromium and 5 on mobile-chromium. That recipe saves a flowchart and loads it back.
  • Browser probe: three presses of Enter/Space on a saved chart's Delete gave one Deleted toast and no load. The canvas was unchanged, the dialog stayed open, and the chart was gone.

@Programcode

Copy link
Copy Markdown
Owner Author

Codex round 5 (on 0770b94): no findings. All review findings on this PR are resolved.

@Programcode
Programcode changed the base branch from design-audit/shell to main October 11, 2026 13:08
@Programcode
Programcode merged commit 287ac36 into main Oct 11, 2026
8 of 18 checks passed
@Programcode
Programcode deleted the design-audit/ai-tools branch October 11, 2026 13:12

This branch was successfully deployed

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