Skip to content

Free no-login debate tools: timer, motion generator, flow sheet, speech calculator - #181

Merged
Programcode merged 6 commits into
mainfrom
seo/free-debate-tools
Oct 22, 2026
Merged

Programcode merged 6 commits into
mainfrom
seo/free-debate-tools

Conversation

@Programcode

@Programcode Programcode commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Four free debate tools that need no login and cost nothing in AI calls. Each has its own prerendered, indexable URL, and a hub page links them. The aim is pages people link to and embed, because Google crawls the guides but does not index them. Each page ends with one soft call to action for the paid app.

URLs

  • /tools: hub with a card per tool (CollectionPage and ItemList JSON-LD)
  • /tools/debate-timer: round timer with format presets
  • /tools/debate-motion-generator: 300 original practice motions
  • /tools/debate-flow-sheet: printable flow sheet
  • /tools/speech-time-calculator: how long is my speech
  • /embed/debate-timer?format=bp: chrome-less timer for iframes (noindex, canonical is /tools/debate-timer)

Each page is prerendered to real HTML through the existing pipeline (PAGE_HEADS, scripts/prerender.mjs, src/prerender/entry.tsx, routeLoaders). Each has its own title, description, self-referencing canonical, and WebApplication + BreadcrumbList + FAQPage JSON-LD. The JSON-LD has price 0, isAccessibleForFree, dateModified, and no ratings or reviews. dist/sitemap.xml lists /tools and the four tools (44 URLs in total) and skips /embed/debate-timer. Built pages run from about 1,150 visible words on the flow sheet and calculator pages to 2,300 on the timer page, which carries every format table. The motion page also lists all 300 motions by topic.

Security review: vercel.json framing change

Adam approved this in the thread before work started. Framing is allowed on the embed only, and after review that means exactly /embed/debate-timer.

  • X-Frame-Options: DENY and the main CSP (with frame-ancestors 'none') move from /(.*) to /((?!embed/debate-timer$).*). The other headers (nosniff, Referrer-Policy, Permissions-Policy, HSTS) stay on /(.*) for every path.
  • A new /embed/debate-timer rule sends frame-ancestors * with no X-Frame-Options, X-Robots-Tag: noindex, nofollow, and a tighter CSP than the main site: default-src, script-src and connect-src are 'self' only, frame-src and worker-src are 'none', and form-action is 'none'. There is no Jam, Supabase, Sentry or PostHog.
  • Vercel's docs give no order for two header rules that set the same key, and a later rule cannot delete a header. So the two sources are complementary, and no path matches both. Vercel compiles sources with path-to-regexp, strict and case-sensitive (sourceToRegex in @vercel/routing-utils 6.6.0). I compiled both sources with that function. Only /embed/debate-timer matches the embed rule. Everything else matches only the deny rule, including /embed, /embed/nope, /embed/debate-timer/, /embed/debate-timer/index.html and /EMBED/debate-timer. A future embed needs its path added to both rules.
  • src/test/vercel-deployment-config.test.ts now resolves headers per path. It fails if two matching rules set the same header. It asserts that /, /index.html, /app, /app/settings, /login, /share/abc, /tools, /tools/debate-timer, a guide, /404.html, /app-shell.html, /embed, /embedded/x, /EMBED/debate-timer and /assets/* all send DENY and frame-ancestors 'none'. Only /embed/debate-timer is frameable, and it keeps the shared headers. I checked that the test fails against the old vercel.json and when the embed rule also sets X-Frame-Options. The existing security-header assertions all still run, now against the resolved headers for /.
  • public/sw.js now ignores /embed/ requests. Without that, the service worker's navigation handler would store an iframe's embed page as the offline /index.html, and could answer an embed with the cached app shell, which refuses to be framed.

The embed is a second Vite HTML entry (embed/debate-timer/index.html, src/embed/debate-timer.tsx), not a route in the app. It never loads the app bundle, the auth provider, Jam, Vercel Analytics or the service worker. In the browser it made no request to another origin. Its JavaScript is 87.6 kB gzipped (React, i18next, icons and the timer). A small dev and preview middleware in vite.config.ts maps /embed/debate-timer to its index.html, which Vercel does on its own.

Timer

  • The presets are Public Forum, Lincoln-Douglas, Policy, British Parliamentary, World Schools, Asian Parliamentary, the app's School 4-6-6-3, and custom formats. Custom formats can add, remove, rename and reorder speeches, and are shared as ?format=custom&custom=…, a validated base64url payload with limits on length and values.
  • Protected time and the POI window show as a strip with a moving marker and a badge. A knock sounds when POIs open and close, a beep with a minute left, and a double beep at time. When the knock at 6:00 coincides with one minute left, only the knock plays. There is a mute toggle.
  • Each side's prep bank runs on its own. Starting one pauses the speech and the other bank, and an empty bank stops at zero.
  • Overtime counts up in red (+0:12), with BP's and AP's 15-second grace period labelled.
  • Fullscreen and projector mode, keyboard shortcuts (Space, R while paused, arrow keys), screen wake lock, and a round that survives a reload. Clocks are stored as wall-clock times, so a running speech is still running after a reload.
  • Prep banks reuse src/lib/timer-clock.ts unchanged; nothing is forked. The speech clock is a small count-up stopwatch, because timer-clock stops at zero by design and overtime needs negative time. ClockAnnouncer, clockTone and the clock colours come from RoundClock. I moved playSignal (adding a knock) and the wake-lock effect out of TimerPage.tsx into src/lib/timer-signals.ts and src/hooks/use-screen-wake-lock.ts. /app/timer behaves as before, and its diff is the import swap only.

Sources for the format times

Every number comes from a document I or a research pass fetched. I checked the starred numbers myself against the PDF text.

  • PF, LD, Policy: NSDA High School Unified Manual. Prep is 3 minutes per team in PF*, 4 per debater in LD* and 8 per team in Policy*. PF crossfires are 3 minutes and summaries 3 minutes, the post-2020 numbers.
  • BP: WUDC Debating & Judging Manual, 2024 revision. It has 7-minute speeches, signals at 1:00, 6:00 and 7:00, POIs between 1 and 6 minutes, and nothing judged after 7:15*.
  • World Schools: WSDC Debate Rules, August 2026. Speeches are 8 minutes and replies 4*. The reply comes from the first or second speaker, Opposition first*, and POIs fall between the first and seventh minute*. "No POIs in replies" is explicit in the NSDA USWSDI manual.
  • Asian Parliamentary: UADC 2023 Debating and Judging Handbook, which has 7-minute speeches, 4-minute replies, POIs from 1:00 to 6:00 and protected replies. There is no single official AP rulebook, and the page says so.
  • School 4-6-6-3 is the existing in-app data. The page says it is not a league format.

Each format's table on the page cites its source and says what varies, such as PF's old 2/2 summary and prep, LD's 4-or-5 minutes of prep, and the 4-minute rebuttals some Policy circuits still use.

Motion generator

  • src/lib/free-tools/practice-motions.ts has 300 motions written for this PR, 25 per topic across 12 topics. They split 96 novice, 108 intermediate and 96 advanced. By type there are 120 policy, 72 value, 48 actor, 30 regret and 30 prefer. 31 have an info slide. None is copied from a tournament archive. Each motion has hand-written "This House" and "Resolved:" wording. I read all 300 for balance and school suitability.
  • You can draw one, or three for a veto drill (drawing again avoids the ones just shown); filters for topic, difficulty and type; THW or Resolved wording; copy; permalinks (?m=edu-004, filters kept in the URL); and the full bank as a list grouped by topic in the prerendered HTML.
  • The call to action, "Prep both sides of this motion in DebateLab", links to /login. The app has no parameter that carries a motion through sign-in (LoginPage reads none, and RequireAuth only keeps the path), so I didn't invent one. Follow-up below.

Flow sheet and calculator

  • On the flow sheet, the format picks the columns, one per speech, labelled by speaker and coloured by side. Paper is A4 or Letter in landscape or portrait, with optional motion, round and judge lines. The strip at the bottom has a box per crossfire or CX period, or a POI and decision-notes box for parliamentary formats. Printing goes through window.print() with an @page size set from the picker. Checked with Chromium's print-to-PDF: one page each for Letter landscape and A4 portrait.
  • The calculator counts words and estimates duration at 110, 130 and 160 wpm plus a custom rate. The page calls these rates working approximations, not research figures. It also lists which speech slots the speech fits ("PF 4:00 constructive / rebuttal, 0:32 spare", "LD 3:00 2AR, 0:28 over"), using the timer's format data, and has a word-target table per slot. It runs only in the browser, and the page says so.

Site integration

  • The footer gets one added link, "Free debate tools", to /tools.
  • Each tool links to the hub, to one or two existing guides (/how-to-flow-in-debate, /debate-prep-time-management, /debate-motion-analysis, /debate-topics-for-students and others), and to the other tools.
  • The tool UI strings live in a separate tools namespace (src/locales/tools/{en,es,hu}.json), and src/locales/*.json is untouched. i18n-keys.test.ts now resolves namespaces, and i18n.test.ts checks parity for the new catalogs. The article copy, URLs and prerendered HTML stay English, with no hreflang.
  • Analytics adds one event, free_tool_used, through the existing allowlisted captureProductEvent, with closed sets for freeTool, action (start, draw, print, calculate) and format. No new vendor. The embed sends nothing.
  • Tool JavaScript is lazy-loaded, and no tool module is in the static graph of index.html or LandingPage. Two bundle changes are worth knowing. The tools catalogs load with the rest of i18n, about 5.5 kB gzipped across three languages, so the footer label works on the landing page. And with a second HTML entry, Rollup moves React, i18next and the other shared libraries out of the main entry into a shared chunk (button-*.js). The main entry is now two files, about 371 kB gzipped together.
  • docs/operations.md describes the framing rules, the tool prerender and the embed entry. docs/testing.md describes the new spec, and the verify-debatelab feature index notes that the tools are covered by e2e/free-tools.spec.ts. The skill's recipes need the backend stack, and these pages have no backend.
  • The calls to action use only the verified pricing facts: a 3-day trial, no card, then $5 a month plus tax. There are no stats, user counts, testimonials or ratings. Product claims (sparring in PF, LD and 4-6-6-3 with a verdict, case generation per side, an editable flowchart) are checked against src/.

Verification

Local runs on this VPS at 632aee06. Two later commits followed: 795579e0 sizes the embed snippet, and f682816a fixes the interim review findings listed in the PR comments. f682816a was checked with ESLint and a scoped tsc on the touched files, the affected unit tests, and e2e/free-tools.spec.ts on both projects. The full ladder was not rerun because the VPS is out of memory. These are local passes, not CI. Every GitHub Actions job on this PR failed within 2 seconds with no logs, which is the Actions-minutes problem AGENTS.md describes, so there is no CI result yet.

  • npm run verify passed end to end: docs check, lint (ESLint, knip, repo rules), typecheck on all three tsconfigs, 343 unit tests in 40 files, 30 collaboration tests, 356 edge tests, build with prerender, sitemap and the bundle-boundary check, test:sw, and the desktop @smoke browser set (42 passed, including the new tool specs).
  • e2e/free-tools.spec.ts and e2e/public-pages.spec.ts on desktop and mobile Chromium: 54 passed.
  • Full desktop and mobile Playwright suite (npm run test:e2e, the extra part of verify:full): 260 passed, 5 flaky, 5 skipped, 2 failed. The first attempt was invalid because the box's memory guard killed the dev server and Chromium mid-run. The failures are not from this PR:
    • e2e/search-quality.spec.ts (authenticated Factoids source panel) fails on desktop and mobile, and fails the same way at this branch's base 4f9e0a0e and on current main (34fb5856).
    • e2e/home-navigation.spec.ts:90 (onboarding tooltip within 100 ms) failed once on mobile under load and passed when rerun alone.
    • The flaky tests were all mobile navigation-performance timing tests.
  • Built output: every dist/tools/*/index.html has its h1, intro, how-to, tables and FAQ as HTML before any JavaScript runs, plus its JSON-LD. dist/sitemap.xml lists the five URLs with lastmod, and the sitemap script logs that it skipped /embed/debate-timer.
  • Header sources compiled with @vercel/routing-utils sourceToRegex, as described above.
  • On the real Vercel preview of 632aee06 (staging project, read-only HEAD requests, before the rule narrowed to the exact embed path; Vercel rate-limited the preview build for f682816a), /, /app/settings, /login, /tools, /tools/debate-timer, /online-debate-timer, /embed and /EMBED/debate-timer send X-Frame-Options: DENY and frame-ancestors 'none'. /embed/debate-timer (200) and /embed/nope (404) send frame-ancestors * with no X-Frame-Options and X-Robots-Tag: noindex, nofollow. HSTS, nosniff and Permissions-Policy are on every path.
  • I framed the preview's embed from a page on another origin in Chromium. It rendered, ran, and made no request to any origin but its own. A second iframe of /tools/debate-timer was refused with a frame-ancestors CSP violation.
  • Visual check on desktop (1280 px) and mobile (390 px), light and dark: the timer in BP overtime (+0:12, grace), the BP POI-open state on a phone, the embed in dark mode, the motion generator with a three-motion permalink, and the flow sheet and calculator. Print preview through Chromium's PDF output: PF on Letter landscape and LD on A4 portrait, each one page. GitHub's CLI can't attach images to a PR, so the screenshots are in the T3 thread that opened this PR.
  • Not run: WebKit, staging, verify-debatelab recipes (they need the backend stack, and these pages have none), and the agentic suite (see follow-ups). The box's memory guard killed tsc twice while other worktrees were busy. Each step above passed on a rerun when memory was free.

Follow-ups after #156 merges

  • Add the tools to public/llms.txt.
  • Link /online-debate-timer to /tools/debate-timer.
  • Add a tools link to the guide shell.

Other follow-ups:

  • A ?motion= handoff through sign-in, so the motion CTA can open DebateLab with the motion set.
  • Spanish and Hungarian motion sets (English only for now).
  • agentic/pages.e2e.ts does not cover /tools. In Spanish and Hungarian the judge would flag the English article copy, which is English by design. An English-only pass could be added.
  • With ?lang=es|hu the embed translates its controls, but speech names stay English (format data).

Cut

Nothing. All four tools shipped.

🤖 Generated with Claude Code

Programcode and others added 3 commits October 22, 2026 11:58
…lculator

Four public tools under /tools, prerendered with their own heads and
JSON-LD, plus an embeddable timer at /embed/debate-timer. vercel.json
allows framing on /embed/* only; every other path still refuses it.

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

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

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

Copy link
Copy Markdown

Review in Change Stack →

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 4 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
📥 Commits

Reviewing files that changed from the base of the PR and between 632aee0 and 79229bb.

📒 Files selected for processing: 17
  • docs/operations.md
  • e2e/free-tools.spec.ts
  • public/sw.js
  • src/App.tsx
  • src/components/free-tools/CustomFormatEditor.tsx
  • src/components/free-tools/DebateTimer.tsx
  • src/components/free-tools/MotionGenerator.tsx
  • src/components/free-tools/SpeechTimeCalculator.tsx
  • src/locales/tools/en.json
  • src/locales/tools/es.json
  • src/locales/tools/hu.json
  • src/pages/TimerPage.tsx
  • src/pages/tools/DebateTimerToolPage.tsx
  • src/pages/tools/ToolsHubPage.tsx
  • src/test/service-worker-embed.test.ts
  • src/test/vercel-deployment-config.test.ts
  • vercel.json
ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 4f9e0a0 and 632aee0.

📒 Files selected for processing: 50
  • .claude/skills/verify-debatelab/features/README.md
  • docs/operations.md
  • docs/testing.md
  • e2e/free-tools.spec.ts
  • embed/debate-timer/index.html
  • public/sw.js
  • src/App.tsx
  • src/components/free-tools/CustomFormatEditor.tsx
  • src/components/free-tools/DebateTimer.tsx
  • src/components/free-tools/FlowSheet.tsx
  • src/components/free-tools/FormatReference.tsx
  • src/components/free-tools/MotionGenerator.tsx
  • src/components/free-tools/SpeechTimeCalculator.tsx
  • src/components/free-tools/ToolPageLayout.tsx
  • src/components/free-tools/timer-session.ts
  • src/components/landing/Footer.tsx
  • src/embed/debate-timer.tsx
  • src/hooks/use-screen-wake-lock.ts
  • src/i18n.ts
  • src/lib/free-tools/custom-format.ts
  • src/lib/free-tools/formats.ts
  • src/lib/free-tools/motion-bank.ts
  • src/lib/free-tools/motion-types.ts
  • src/lib/free-tools/practice-motions.ts
  • src/lib/free-tools/round-timer.ts
  • src/lib/free-tools/speech-time.ts
  • src/lib/observability.ts
  • src/lib/route-modules.ts
  • src/lib/seo/pages.ts
  • src/lib/seo/tool-pages.ts
  • src/lib/timer-signals.ts
  • src/locales/tools/en.json
  • src/locales/tools/es.json
  • src/locales/tools/hu.json
  • src/pages/TimerPage.tsx
  • src/pages/tools/DebateTimerToolPage.tsx
  • src/pages/tools/FlowSheetToolPage.tsx
  • src/pages/tools/MotionGeneratorToolPage.tsx
  • src/pages/tools/SpeechTimeCalculatorToolPage.tsx
  • src/pages/tools/ToolsHubPage.tsx
  • src/prerender/entry.tsx
  • src/prerender/tool-pages.tsx
  • src/test/free-tools/motion-bank.test.ts
  • src/test/free-tools/round-timer.test.ts
  • src/test/free-tools/speech-time.test.ts
  • src/test/i18n-keys.test.ts
  • src/test/i18n.test.ts
  • src/test/vercel-deployment-config.test.ts
  • vercel.json
  • vite.config.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

This change adds a /tools hub and four debate tools: a round timer, motion generator, flow sheet, and speech-time calculator. It adds shared debate-format data and utilities, prerendered tool pages, localization, and a standalone embeddable timer with separate deployment rules.

Changes: Free debate tools

Layer / File(s) Summary
Format data and calculation engines
src/lib/free-tools/*, src/test/free-tools/*
Adds built-in and custom debate formats, timer and speech-time calculations, and a categorized practice-motion catalog with filtering, drawing, and permalink utilities.
Timer state and controls
src/components/free-tools/DebateTimer.tsx, src/components/free-tools/CustomFormatEditor.tsx, src/components/free-tools/timer-session.ts, src/lib/timer-signals.ts, src/hooks/use-screen-wake-lock.ts, src/pages/TimerPage.tsx, e2e/free-tools.spec.ts
Adds a configurable timer with persisted sessions, custom formats, prep banks, keyboard controls, audio cues, and wake-lock handling. The existing timer page now uses shared cue and wake-lock modules.
Other tool experiences and page content
src/components/free-tools/FlowSheet.tsx, src/components/free-tools/FormatReference.tsx, src/components/free-tools/MotionGenerator.tsx, src/components/free-tools/SpeechTimeCalculator.tsx, src/components/free-tools/ToolPageLayout.tsx, src/pages/tools/*, src/lib/observability.ts, e2e/free-tools.spec.ts
Adds the flow-sheet, motion-generator, and speech-time tools, shared tool-page components, and their explanatory pages. Tool interactions record the configured product events.
Tool routes, prerendering, and localization
src/App.tsx, src/lib/route-modules.ts, src/lib/seo/*, src/prerender/*, src/i18n.ts, src/locales/tools/*, src/test/i18n*.test.ts, src/components/landing/Footer.tsx, e2e/free-tools.spec.ts, docs/operations.md, docs/testing.md, .claude/skills/verify-debatelab/features/README.md
Registers the tool routes and prerender entries, adds SEO metadata and structured data, loads English, Spanish, and Hungarian tool translations, and links to the hub from the footer. Adds related tests and documentation.
Standalone timer embed and deployment rules
embed/debate-timer/index.html, src/embed/debate-timer.tsx, vite.config.ts, public/sw.js, vercel.json, src/test/vercel-deployment-config.test.ts, e2e/free-tools.spec.ts, docs/operations.md
Adds a standalone timer embed with its own build entry. Embed paths bypass service-worker handling and use separate framing, CSP, and noindex headers. Tests cover the embed page and deployment rules.

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

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant ViteEmbedMiddleware
  participant EmbedHTML
  participant EmbedEntry
  participant DebateTimer
  Browser->>ViteEmbedMiddleware: Request /embed/debate-timer
  ViteEmbedMiddleware->>EmbedHTML: Resolve embed index.html
  EmbedHTML->>EmbedEntry: Load timer module
  EmbedEntry->>DebateTimer: Render embedded timer
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.

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

- The custom format editor's minute fields no longer fail the browser's step
  check, so Use this format applies; an e2e test covers it. Formats too long
  to share are refused with a message instead of vanishing on reload.
- Only /embed/debate-timer may be framed, not every /embed/ path.
- On the tool page the timer's keys act only once it has focus, so Space
  scrolls the article.
- The calculator's fit pace follows the custom rate; quieter live regions;
  narrower privacy copy; a behavioural service worker test replaces a
  source-text check.

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

vercel Bot commented Oct 22, 2026

Copy link
Copy Markdown

Deployment failed for project debatelab-staging with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/adam-gaborjanis-projects?upgradeToPro=build-rate-limit

@vercel

vercel Bot commented Oct 22, 2026

Copy link
Copy Markdown

Deployment failed for project debate-navigator with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/adam-gaborjanis-projects?upgradeToPro=build-rate-limit

@Programcode

Copy link
Copy Markdown
Owner Author

Interim review of 795579e0, done by an Opus review agent because Codex is at its usage cap until about 17:05 Budapest. A Codex round is scheduled for after that. Fixed in f682816a:

  • High: the custom format editor could not apply anything. The minute fields had min 10/60 and step 0.25, so the browser's step check rejected every whole-minute value and the form never submitted. The fields now accept any value within bounds and keep their own text while you edit, so clearing a field no longer snaps it to 10 seconds. A new e2e test applies a custom format, checks the share URL, and reloads. It fails on the old editor.
  • Medium: the calculator's "fits at" pace went stale after editing the custom rate. It now stores the pace id instead of the number.
  • Low: /embed/(.*) made every embed path frameable, including the 404 page. The rules are now /((?!embed/debate-timer$).*) and /embed/debate-timer. Compiled with @vercel/routing-utils sourceToRegex, /embed/nope, /embed/debate-timer/ and /embed/debate-timer/index.html now get DENY.
  • Low: a custom format longer than the 4,000-character share limit applied, but was lost on reload. The editor now refuses it and shows a message.
  • Low: on the long tool page, Space and the arrow keys drove the timer from anywhere. They now act only once the timer has focus (clicking it gives it focus), so Space scrolls the article. The embed keeps page-wide keys.
  • Low: the privacy copy said "Nothing is sent to a server", but the first Start sends a free_tool_used event. The copy now says the saved round stays in the browser.
  • Low: noisy live regions. The word count is no longer live, and the motion generator announces only the drawn motions.
  • Low: the service worker test matched source text. src/test/service-worker-embed.test.ts now runs public/sw.js with stub globals and checks that embed navigations are not answered. It fails without the bypass.

Dismissed:

  • Format, side and speech names stay English in the Spanish and Hungarian UI (for example, the embed with ?lang=hu). This is the format data, and the brief keeps it English. The PR description lists it as a follow-up.

Checks for f682816a: ESLint and a scoped tsc on the touched files, 63 unit tests across the affected files, and e2e/free-tools.spec.ts on desktop and mobile Chromium (26 passed). The full lint, typecheck and build were not rerun, because the VPS is out of memory and its guard is killing those jobs.

@Programcode

Copy link
Copy Markdown
Owner Author

Codex review (gpt-6.1-sol, xhigh) of the merged head 79229bb4 found two things. Both are valid, and both are fixed in follow-up #188, which is not merged:

  • Medium: at the first or last speech, the timer's arrow keys reset and paused a running speech.
  • Low: the hub copy promised saved drafts, but only the timer saves anything.

This branch was successfully deployed

2 active (1 outdated) deployments
Preview – debate-navigator — 79229bb4 Deployed Oct 22, 2026 by vercel[bot]
Preview – debatelab-staging — 795579e0 Deployed Oct 22, 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