Skip to content

iOS app: catch up with main and restyle as DebateOwl - #253

Merged
Programcode merged 242 commits into
feat/native-iosfrom
native-ios/catch-up-debateowl
Oct 28, 2026
Merged

Programcode merged 242 commits into
feat/native-iosfrom
native-ios/catch-up-debateowl

Conversation

@Programcode

@Programcode Programcode commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Brings the iOS app up to date with main and restyles it as DebateOwl. Base is feat/native-ios. Don't merge yet. The open questions for Adam are at the end.

Catching up with main

origin/main (222 commits, through #251) is merged into the branch with a normal merge (baec2758). These 22 files conflicted.

  • Billing (_shared/entitlements.ts and its test, use-subscription.ts, settings-store.ts, SettingsPage.tsx). Main's resolveBillingState stays the one place that decides a plan. Main added payment grace, extended access and subscription_ended; the branch's effective_entitlement() SQL function predates them. Rather than let the SQL win, resolveBillingState now also takes App Store rows. App Store Pro ranks with Stripe Pro, and the later end shows. App Store Basic (20 a day) comes after legacy free and the payment grace, before a trial. Revoked, upgraded and expired rows grant nothing, and Apple's billing grace extends a row. The AI functions, the website and the app all read user_settings, subscriptions and app_store_subscriptions and resolve them the same way. A database without the App Store table answers PGRST205, which counts as no App Store rows, so web and functions keep working before that migration ships. I ported the pgTAP scenarios for the App Store rules to billing-state.test.ts.
  • client.ts, streaming.ts, sparring-store.ts, SparringPage.tsx take main's versions, since main split the AI client into per-feature files. The branch's extractions come back on top in later commits (see "Moved to core").
  • ai/index.ts keeps main's re-indented block plus the branch's sparring_scored push. notifyScored became optional so main's new judge tests run unchanged.
  • Edge tests take main's versions. The branch's tests that mocked /rpc/effective_entitlement now mock the table reads like main's; the testing/entitlement.ts helper is gone.
  • Locales went through a key-level three-way merge. No key changed on both sides.
  • package.json test:edge takes main's flags plus the branch's App Store and Expo env names.
  • package-lock.json is main's lockfile with the mobile workspace's resolved versions laid over it, so the Expo SDK 57 set is unchanged. I restored two packages the Expo CLI got from Tailwind 3's hoisted tree (arg, micromatch).
  • Docs (architecture.md, operations.md, supabase/README.md) keep main's text plus the branch's install-command, App Store and push sections, reworded for the TypeScript plan resolver.

Other fixes the merge needed:

  • 40 keys main deleted would have shown as raw keys in the app. They now point at main's current keys, such as the one progress line per tool and the empty-state lines, or live under mobile.* in en/es/hu where the website no longer has them.
  • temperature is gone from the app's tool settings, as on the website.
  • The app's web preview couldn't read the plan because supabase.web.ts lacked the new helper. 3ee7cf9e fixes it.

Moved to core

  • packages/core/src/supabase/billing.ts is the plan reader web and app share. It sits on contract/billing-state.ts, a re-export of the Deno resolver in the existing contract/ pattern.
  • ai/feature-configs.ts holds the tool defaults, model normalisation and the canonical check, taken from the settings store.
  • A Codex child moved the AI parsers, merged in f79ace8a. They are the Generate, Brainstorm and Factoids transforms, discovered-source and assessment parsing, retention and the autosave row, SSE framing, source-url.ts (URL matching), Sparring round moves and timers, and the quota session key. The web imports them with no behaviour change. Tests moved with the code (packages/core/src/**/*.test.ts now runs in vitest).

Case structure v2 is not in this PR. #244 is still a draft, conflicts with main and has failing CI, so there is no v2 on main to move into core or render. Its schema and case-view.ts already live in packages/core, so the app can pick them up once #244 lands. The case screenshots show the current (v1) format.

App catch-up with main

DebateOwl restyle

  • Name DebateOwl, slug debateowl, links to https://debateowl.com.
  • Colours come from the website's tokens in light and dark (plume, ink, gold, mint, coral). Old Tailwind values map to the website's owl remap of the same family and step.
  • Fredoka for titles, the motion and button labels, Nunito for text (expo-font via @expo-google-fonts).
  • Buttons are the website's tactile ones, a blue face on a ledge that sinks when pressed. Sentence-case labels come from main's catalogs, and a long label wraps to a second line instead of being cut off. Pinned action bars (case, attack, scorecard) sit on a strip of the page colour with a fade above it. Cards and fields have 2pt edges, and fields turn blue when focused. Switches use the brand fill.
  • Success is mint with a check mark (sign-in "code sent"). The You tab's plan badges are the website's outline badges: Pro gold, trial and Basic blue, extended access and legacy free mint with a check mark, ended plans coral.
  • Ollie appears in the app icon (the website's icon-1024.png), the splash (pointing), the sign-in mark, empty states (reading, surprised, sleepy, point), tool progress, the Ask coach avatar and the Sparring opponent. The PNGs come from the website's SVGs via scripts/render-owls.ts.
  • On iPad the Library list takes the full width until a source opens, as the website's Library does, and Ask's option chips wrap. On iPhone the sideways chip rows fade at the edge.
  • Titles and choices that Hungarian or Spanish cut off (picker values, collapsed items, recent work, flowchart edge labels) wrap to two lines.
  • No streak exists in the app or on the website, so the feather streak icon appears nowhere.
  • No feature was added or removed.

Review polish (second round)

From the review of this PR:

  1. A pinned button label no longer gets an ellipsis. Labels wrap to a second centred line, and on the case screen "Attack this case" gets the wider button. It fits in en, hu and es at 390pt.
  2. Pinned action bars (case, attack, Brainstorm, Factoids, scorecard) sit on a strip of the page colour with a fade above it. The page and any notice clear the bar's measured height.
  3. Plan badges match the website's outline badges (15% fill, 30% edge, darker text). Pro is gold, as on the website; the earlier "mint plan badges" in this description was wrong. The website's Basic badge now uses the trial blue instead of an unmapped teal.
  4. On iPad the Library list takes the full width (in an 880pt column) until a source opens. Then it keeps 40% of the width beside the source, and titles and dates wrap.
  5. On iPad Ask's option chips wrap. On iPhone the chip rows (options and citations) fade at the edge only while they overflow, into the surface they sit on.

The full screenshot pass found and fixed more of the same kind: picker values, collapsed document items, recent work, source rows and flowchart edge labels that Hungarian or Spanish cut off now take two lines, and switches use the brand fill with a white thumb.

Tests

All results are local runs on Adam's VPS (Node 22.23.2), not CI.

  • npm run verify at the final head 1ef3b2e3 passed. That covers docs, lint (eslint, knip, repo rules), typecheck (web, agentic, core and the app), vitest (121 files, 790 tests, including apps/mobile and packages/core), collaboration (30), Deno edge tests (543), build and site checks, the service worker check, and the Playwright desktop smoke set (47). An earlier npm ci plus verify at 6fb2664e also passed.
  • The database gate (./.superset/db-gate.sh --db-only) on the merged migration order passed 33 pgTAP files and 655 tests, including app_store_entitlements_test.sql and push_tokens_test.sql, plus the quota, Opus-default, admin-drift and workspace concurrency scripts.
  • The two Codex review fixes after 6fb2664e added use-subscription tests (11 pass), and the Crossfire quota test fails when the round id is dropped.
  • The app's npm run -w @debatelab/mobile typecheck and export:web pass, and the screenshot script exits 0 at every size (it fails on any page error or unexpected console error).
  • Codex review round 1 at 6fb2664e raised two findings, both fixed. A failed App Store read on the website now leaves the plan unknown instead of "Expired" (3bb421db, with tests), and the owl loader stays still under Reduce Motion (a9f5a0ad). Round 2 at a9f5a0ad raised one finding, which I dismissed. When the plan query fails, Settings falls back to an "Expired" card. Main already does this for every failed read, such as a failed settings read. The harm the indeterminate-read rule records (fix: finish UI cleanup and OpenRouter model catalog #47), a persisted downgrade of the model choice, can't happen: the settings store decides Pro through readProModelAccess, which returns unknown for a partial read. Showing an "unknown" plan in Settings needs a new card state and strings, so it's listed under gaps as a follow-up.
  • GitHub Actions did not run. Every job on this PR ended within 3 seconds with no steps, which the repo guide describes as the account being out of Actions minutes, not a code failure. CI is not green and has not been tried.
  • Not run were WebKit, the staging browser suite, live AI (verify-debatelab --live) and anything on a real iOS device or simulator.

Screenshots

These come from the web export through apps/mobile/scripts/screenshots.ts (staging, the seeded Pro account, nothing written), at 390, 820 and 1180 points.

  • before: ~/.t3/scratch/ios-reskin-shots/before/, light, English.
  • after: ~/.t3/scratch/ios-reskin-shots/after/, light and dark, English, Hungarian and Spanish. 648 shots from 1ef3b2e3, all checked by eye. clipped.json there lists every text a shot cuts off; only two remain, both by design: the motion as Ask's one-line header subtitle on iPhone, and the opponent's speech in Sparring, collapsed to four lines until tapped.

The script gained an 11-inch iPad Air size (820×1180 and 1180×820), Spanish, SHOTS_DIR, current sentence-case labels, a filter for the expected 404 from a project without the App Store table, and the clipped-text report: each shot lists the texts React Native Web draws with an overflowing ellipsis or line clamp.

Known gaps and flags

  • Bundle id still com.adam.debatelab, and the URL scheme is still debatelab. A new id means a new App Store Connect app, so that's Adam's call.
  • Slug is now debateowl. On eas init day this decides the Expo project name (@adamgaborjani/debateowl).
  • Migrations. The branch's four 20261014* migrations are older than main's 2026-10-16 to 2026-10-24 ones, so a project that already has main's needs supabase db push --include-all. The database gate passes on the merged order. effective_entitlement() is defined but no longer called. A follow-up migration can drop it.
  • Web Settings and the App Store. The website doesn't know an App Store subscriber is billed by Apple. An App Store Pro account would see "Set up billing" and Basic would see "Upgrade to Pro". No real account can hit this yet, because in-app purchase isn't live and the products are placeholders. hasAppStoreSubscription is plumbed for when it is.
  • Unknown plan in web Settings. When the plan read fails, useSubscription falls back to an expired snapshot, so Settings says "Expired" instead of "couldn't load". This predates the branch and covers every failed read; a follow-up can add an unknown state to the Settings card.
  • Live streaming of tool answers (Stream every AI answer live, token by token #149) is not in the app. Generate, Attack, Brainstorm and Factoids still wait for the whole answer, which the server still supports. Drain live AI streams until the body closes #204 and T18: keep AI streams alive, recover dropped results, fix Auto Pro charge, refunds, Sparring judge errors #224's run recovery apply to those live streams only.
  • Not proven on a device. Native tabs, large titles, sheets and the button press were not seen on iOS. EAS Simulator needs an EAS project (eas init), which is a build-day step, so it wasn't used.
  • npx expo install is blocked by the repo's allowScripts policy, so the two font packages were installed from the root with npm install -w.
  • The Android icons were regenerated with the owl, though this is an iOS app.

Open questions for Adam

  • Keep com.adam.debatelab for the store, or move to a DebateOwl bundle id before the first TestFlight build?
  • When should eas init run (it creates @adamgaborjani/debateowl)? It also unlocks EAS Simulator for a native check of this restyle.

🤖 Generated with Claude Code

Programcode and others added 30 commits October 9, 2026 05:41
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes the Foundations rows of the 2026-10-09 design audit.

- Delete the global toast keyframe that froze Sonner, so toasts stack,
  expand and leave again.
- Rewire the colour tokens: muted text is a real lower tier, the ghost
  hover, popover, primary, destructive and input colours meet contrast,
  and dark shadows show.
- Delete the global micro-interaction layer (tooltip, menu items, tab
  underline, icon hijacks, marching ants, switch bounce, progress width,
  .tabular-nums, input focus glow). Each overlay primitive now has one
  curve, exits faster than it enters, and scales from its trigger.
- Gate hover on devices that hover, make press feedback 0.97 over 100ms,
  and give buttons, Select triggers, checkboxes and close buttons 44px hit
  areas on touch.
- Reduced motion fades instead of freezing; reduced transparency and more
  contrast turn blurred surfaces solid; safe-area insets and the tap
  highlight are handled.
- One focus outline, a single theme crossfade, and light and dark
  theme-color metas.

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

Codex review r1 on #124:
- Ask's Save and the Factoids link icon were opacity-0 until hover, which
  hoverOnlyWhenSupported made permanent on touch. They now rest at 60%
  and go full on hover or focus, as the audit's accessibility row asks.
- The 44px checkbox hit area overlapped the next row in the debate context
  list. It is 36px, and those rows are at least 44px on touch, so each
  label stays the larger target.

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

Codex review r2 on #124: switching theme twice before the first transition
was ready left its ready promise rejected and unhandled (AbortError:
Transition was skipped). The final theme was already correct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The audit replaced the blanket reduced-motion kill with one that keeps
opacity and colour transitions. The spec asserted a 0s transition on the
options panel; it now asserts the panel transitions nothing that moves
(no transform, height, grid rows or 'all').

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes the Shell rows of the 2026-10-09 design audit.

- Header: "Home" is now "Tools", and "Current debate" is a link that is
  active on the debate page and every tool route, with its own menu
  chevron. The brand is a span, Home's h1 describes the page, the nav pill
  and glow are gone, the edge appears only on scroll, and icon controls get
  tooltips. Language is a two-tap popover.
- The mobile menu is a modal panel with a scrim and a 180ms fade. The theme
  toggle leaves the mobile header. On phones, side sheets become Vaul
  bottom sheets.
- One delete rule: Undo when the app can put the item back exactly
  (Library sources); a destructive confirm otherwise; admin role changes
  confirm.
- Library: the title is the row's button, labels are tied to their
  fields, errors are inline, the duplicate filters and stat tiles are
  gone, and the merge confirm is a dialog. History keeps its tab in the
  URL, and lists scroll with the page on phones.
- Pending states for sign-out, delete-all, debate switch and delete, file
  import and announcement dismiss. The announcement renders from cache.
- 404, legal pages, Sources, Login and the route error screen all have a
  way home. Shared items render as a readable view, and Import is gone.
- Shared PageHeader and tool links, a text-xs floor, tinted tier badges,
  localized login errors, Escape continues past the inactivity gate, and
  the limit modal gets "Not now".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Tools rename, the Current debate link and the Library form changed what
the recorded agent steps click.

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

tailwindcss-animate's animate-in sets a 150ms default in the variant layer,
which outranks a plain duration-* class. Measured after the change: mobile
menu 180ms in and 120ms out; tooltip 120ms out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The panel was placed 4rem under the top, so the Current Debate row covered
its first item. AppLayout now publishes the header's bottom edge as
--app-header-bottom and the panel opens there (measured: header 118px,
menu 118px on an iPhone 13).

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

- navigation-performance waits for the Tools page h1.
- The onboarding schedule check instruments the new target-first start.
- Hungarian parity checks the inline required-field error, and the toast
  close label through a flowchart node delete.

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

- Library Undo re-inserts the same row (id and created_at), so a per-request source
  exclusion still applies and the source keeps its place. The library recipe now checks
  the restored row against the original.
- The mobile menu closes when the window widens past lg, where it is hidden but would
  still hold the page modal.
- Phone bottom sheets move focus inside on open, like the side sheet.
- A refused announcement dismiss restores the session cache as well as the banner.
- Folder merge and admin role changes keep their confirmation open while the write is
  pending and close only on success; a failed merge says so in the dialog.
- Read-only results (a shared case) show no regenerate buttons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…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>
Claude Opus 5 requests failed with "No endpoints found that can handle the
requested parameters". The catalog marked Opus 5 as accepting temperature,
but none of its 11 OpenRouter endpoints do, and require_parameters: true
drops every endpoint that lacks a sent parameter.

Temperature is now gone from every request: the catalog flag, the ai and
ai-stream payloads, the research sub-calls, the JSON repair retry, and the
client's saved feature settings. Saved settings that still carry a
temperature get rewritten once through the existing canonical check.
Older clients that still send temperature are ignored by the server.

The routing test now fails if any catalog model's request carries
temperature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add Claude Haiku 5.5 (Basic), Claude Opus 5.5 and Claude Sonnet 5.5 (Pro)
to the catalog and retire Claude Sonnet 5 and Claude Opus 5. Haiku 5.5 is
the Basic default and Opus 5.5 the Pro default. Auto Fast now prefers and
falls back to Haiku; Auto Pro prefers and falls back to Opus.

Older clients that still send a retired ID resolve to Opus 5.5 on the
server and in the settings store instead of getting a 400.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dropping temperature makes every saved feature_configs non-canonical, so
every existing user now takes the one-time rewrite path. That write sent
the whole snapshot read at hydration, so a model choice saved in the
meantime could be reverted on the server. The rewrite now applies only
while updated_at still matches the read, and only if the user has not
changed settings locally since hydration. A skipped cleanup retries on
the next hydration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ranked report of 188 independently verified findings, grouped into 17
fix workstreams, plus per-area appendices with evidence and fixes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr1aJw5ciMutxBnMzfiyS1
GPT-5.6 Luna becomes GPT-6 Luna, GPT-5.6 Sol becomes GPT-6.1 Sol, and
DeepSeek V4 Flash becomes DeepSeek V4.1 Flash. GPT-6 has no Terra, so
GPT-5.6 Terra retires into GPT-6.1 Sol. The catalog is back to ten
models.

Retired IDs resolve in the settings store and at both Edge Function
boundaries. GPT-5.6 Luna, the old Basic default, moves to the new Basic
default, Claude Haiku 5.5. A retired Basic ID never resolves to a Pro
model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Live verify rewrites every upstream call to the Muse Spark pin, so the
provider call's model is the pin, not Opus 5.5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ell header is the banner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Programcode and others added 18 commits October 27, 2026 07:24
* Add a bottom tab bar on phone widths

Phones below 768px navigate with Tools, Current debate, Library, and Settings. The header menu stays from 768px until the desktop nav, so iPad portrait still includes History.

Co-authored-by: Gáborjáni-Szabó Ádám <Programcode@users.noreply.github.com>

* Keep phone toasts, the keyboard, and Ask clear of the tab bar

The toast offset now includes the bar up to 767px. The keyboard listener runs only below 768px, ignores pinch-zoom, and Ask sizes itself from the chrome above the page so a banner cannot push the composer under the bar.

Co-authored-by: Gáborjáni-Szabó Ádám <Programcode@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Gáborjáni-Szabó Ádám <Programcode@users.noreply.github.com>
…ack.org links (#247)

- /debatelab-pricing, -vs-chatgpt, -vs-debateclub and -vs-windebate are now /debateowl-*.
  One vercel.json rule 301s each old path, also under /hu/ or /es/ and with a trailing
  slash, to the new page in one hop; Vercel keeps the query.
- Generator config, answers, guides, footer, landing guide hub, locales, llms.txt,
  humans.txt and the seo watch list use the new slugs; pages regenerated, CONTENT_DATE
  moved to 2026-10-27 because every guide links the new pricing page.
- gsc_submit.py targets debateowl.com (URL-prefix or domain property) and inspects the
  live pages instead of the retired brand slugs.
- Tests cover the new redirect with the path-to-regexp form Vercel compiles.
…arison tables (#249)

* SEO/AEO: one DebateOwl entity, icons on every page, speech-times and comparison tables

- Organization JSON-LD is DebateOwl (logo 512, sameAs, Pi-Stack as parentOrganization); meta author DebateOwl
- WebSite/SoftwareApplication alternateName "Debate Owl"; WebSite inLanguage en/hu/es
- Guide pages get favicon/192/apple-touch icon links, meta author, byline and a DebateOwl home link
- /online-debate-timer: speech-times table generated from src/lib/free-tools/formats.ts
- Tables on /debateowl-vs-chatgpt, vs-windebate, vs-debateclub and the case generator
- Case generator and coach answers open with DebateOwl; definitions + DefinedTerm, new FAQs
- 'Formerly DebateLab' in the footer and llms.txt; Bingbot in robots AI group
- check-site enforces the entity and icon links; tests pin the timer table to the formats

* Add Bing Webmaster Tools msvalidate.01 meta tag to the shared head and guide pages
On a phone the first screen now holds Ollie, the H1, one line, the
Start trial button and three checked chips. The hero copy is the first
sentence of the old subheadline; the announcement, second button and
plan boxes are gone, and the product preview shows from lg up.

What's new, Active Grounding and Use cases are removed: they repeated
the feature list. Features are icon tiles, How it works and Pricing
carry one short line per item, the short answer folds to three lines
on phones, and the guide links become chip rows that scroll sideways
on phones. Every link, anchor, heading level and JSON-LD source string
stays. Unused keys leave all three catalogs; hu and es get the same
shorter copy.

At 390 px the English page goes from about 15,250 px to 5,120 px.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Titles follow "Page name | DebateOwl" on every indexable page in English,
Hungarian and Spanish, and the home page is "DebateOwl: AI debate prep and
practice". Each meta description is one sentence saying what the page does;
the free tool pages lead with "Free" and no longer call DebateOwl a paid
app. og: and twitter: tags carry the same strings, and the static guides
gain the twitter title and description they lacked. The guides' heads now
live only in PAGES in generate_seo_content.py.

The logo and name link to the home page in the page's own language and are
labelled "DebateOwl home". Terms, privacy and sources get a BreadcrumbList
like the guides and tool pages already have.

The favicon gets a simplified small-size owl (flat colours, bigger eyes, no
tile) for the 16, 32 and 48 px frames; its source is
src/assets/owl/owl-favicon.svg.

Every landing trial button says "Start free trial" (hu "Ingyenes próba
indítása", es "Comenzar prueba gratis"), matching the pricing card.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Brings feat/native-ios up to date with main (222 commits, through #251).

Billing: main's resolveBillingState stays the one place that decides a plan,
and it now also takes App Store rows (Pro ranks with Stripe Pro, Basic after
legacy free and the payment grace). The server, the website and the app read
the same user_settings, subscriptions and app_store_subscriptions rows, so
effective_entitlement() is no longer called. Its App Store rules are pinned
in billing-state.test.ts.

Web files keep main's behaviour: client.ts, streaming.ts, the sparring store
and page, and the settings store take main's versions, with the branch's
account-switch guard kept in hydration. Tool defaults and the feature-config
normalizer now live once in packages/core, and the app drops temperature as
the website did.

The lockfile is main's with the mobile workspace's resolved versions laid
over it, so the Expo SDK 57 set is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The web preview swaps supabase.ts for supabase.web.ts, which had no
billingQuery, so the You tab's plan never loaded there. The helper now
lives in its own module on top of whichever client the platform gets.

The screenshot script also shoots an 11-inch iPad Air (820 and 1180
points wide) and takes SHOTS_DIR to write a set elsewhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use core transforms, source retention, URL matching and SSE framing in both clients. Preserve web behavior and keep platform side effects in each app. Align mobile research autosave, Crossfire quota keys and Sparring clocks with the web.

Co-Authored-By: Codex <noreply@openai.com>
The app takes the website's DebateOwl look: its colour tokens (plume blue,
ink neutrals, gold, mint, coral) in light and dark, Fredoka for titles and
button labels and Nunito for text, the tactile buttons that sink onto
their ledge, 2px card and field edges, and Ollie in the places the
website shows him: the app icon, splash and sign-in mark, empty states,
tool progress, the Ask coach and the Sparring opponent. The owl PNGs are
rendered from the website's SVGs by scripts/render-owls.ts.

The app is named DebateOwl (slug debateowl) and links to debateowl.com.
The bundle id stays com.adam.debatelab and the URL scheme debatelab.

Labels follow main: the 40 strings main removed now point at the
website's current keys (the one progress line per tool, the empty-state
descriptions as form subtitles, counts with plurals), or live under
mobile.* in all three languages where the website no longer has them.
The You tab's plan card shows main's billing states, dates and wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last colours from the old sky and slate palette move to the
website's owl remap of the same family and step (src/index.css), and
the timer ring keeps to plume blue, as the website has no gradients.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main (#201) charges Crossfire per round: the website sends the round's
id beside every turn and the debrief, and the server's quota ledger
charges the first. The app now sends the same id, so a student's round
costs the same quota on the phone as on the website. Sparring already
was, through its sessionId.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…app's guide

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owl beside a progress line is decoration; only a labelled owl
loader is a progress indicator itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The website's per-feature AI files now take their parsers from
packages/core: the Generate, Brainstorm and Factoids transforms, source
and assessment parsing, research retention and the autosave row, SSE
framing, source URL matching, the Sparring round moves and the quota
session key. The app follows main with them: research autosave runs one
batch at a time and refreshes a page the debate already has, a Sparring
turn's clock starts when the student starts, and Crossfire sends its
round id through the shared key check.

Both branches had added the Crossfire quota session; the merged request
keeps one, which leaves signals out by operation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main fixed this on the website in #187: a reply cut off at its cap can
send a newline before failing, and the page then kept an empty answer
instead of asking the structured endpoint, which retries with a larger
cap. The app had the same check and now follows main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the App Store rows cannot be read and the rest prove no access,
Settings showed a definite Expired with nothing left today, which is
wrong for an App Store subscriber and was cached without a retry. The
query now fails, so the plan stays unknown and retries, as the AI
functions refuse to decide in the same case. A missing App Store table
still counts as no App Store rows. (Codex review r1, finding 1.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(Codex review r1, finding 2.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 28, 2026 10:02
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • 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.

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 insufficient remaining quota.

Programcode and others added 8 commits October 28, 2026 10:28
Button labels wrap to a second centred line instead of being cut off, and
on the case screen "Attack this case" gets the wider button. The pinned
bar now sits on a strip of the page colour with the page fading out above
it, so text no longer shows between or behind the buttons, and the page
and notices clear the bar's measured height.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The You tab's badges take the website's outline badges: the state's
colour at 15% behind, 30% as the edge and its darker text. Pro is gold, as
on the website; mint is for extended access and legacy free, with a check
mark. The website's Basic badge uses the trial blue instead of an
unmapped teal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like the website's Library, the list fills the width (in an 880pt column)
with nothing open; opening a source splits the screen, with the list
keeping 40% of it. Source titles wrap to two lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On iPad every option chip shows, wrapping as needed. On iPhone the row
still scrolls sideways and now fades at its edge, as do the citation
chips under an answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picker values, collapsed document items, recent work and source rows and
flowchart edge labels take two lines instead of one. Switches use the
brand fill with a white thumb, as on the website.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each shot lists the texts React Native Web draws cut off (an ellipsis or a
line clamp that overflows) and the run writes them to clipped.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The citation chips under an Ask answer faded into the page colour over
Ask's card colour, which drew a dark block in dark mode, and the fade
showed even when every chip fit. The row now measures itself and fades
only while it continues off screen, into the colour it sits on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
React Native Web draws a switched-on thumb in its own teal; iOS already
takes the white thumbColor. A Library row's date line wraps instead of
being cut off beside an open source on iPad.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Programcode
Programcode merged commit 353f85f into feat/native-ios Oct 28, 2026
3 of 14 checks passed
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