Skip to content

Native iOS app: History, announcements and account tools - #143

Merged
cursor[bot] merged 16 commits into
feat/native-iosfrom
native-ios/06a-history-announcements
Oct 22, 2026
Merged

cursor[bot] merged 16 commits into
feat/native-iosfrom
native-ios/06a-history-announcements

Conversation

@Programcode

@Programcode Programcode commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

History, announcements and account tools for the iOS app, matching the website.

What's in it

  • History. A scope picker covers the Current Debate, any other debate, and one-off or unsorted work. The website's tabs follow: cases, chats, attacks, brainstorms, factoids and Sparring rounds.
    • Rows open their documents or the Flowchart. Chats reopen in Ask, and Sparring rounds reopen as their scorecard.
    • Cases and Sparring rounds delete with the website's confirmation.
    • The unsorted view shows the suggested debate and has Add to debate, through the website's owner-checked attach_to_debate function.
    • You get there from See all on the Debate tab or History in You.
  • Announcements. A banner at the top of the Debate tab, with the website's date filter, a "1 of 2" counter and server-side dismiss, so dismissing on one side dismisses on both.
  • You.
    • Download my data: the account export, saved as debatelab-data-<date>.json and shared.
    • Delete all my data: the website's email-typed confirmation in a form sheet. Afterwards it clears session memory and reloads.
    • Terms and Privacy: open in the in-app browser.
  • Model picker. AI model and Reasoning in Generate, Attack, Brainstorm and Factoids, and as chips in Ask.
    • Pro models are greyed out without Pro.
    • Choices write feature_configs in the website's shape through the settings write queue.
  • Moved to core. The announcement schema and date filter now live in packages/core, and the website imports them back in one line.

Out of scope

  • Share links: the website has no code that creates them.
  • Upgrades and promo codes: these wait for in-app purchase.
  • DOCX and Google Docs export, and the onboarding tour.

Checks (local; GitHub Actions is out of minutes)

At this head, each run under the shared memory lock:

  • npm run verify: exit 0. Vitest 316 tests, collaboration 30/30, Deno edge tests, build, Playwright smoke 30/30.
  • Mobile typecheck, npx eslint apps/mobile and npx knip: all exit 0.
  • New tests:
    • owner-check.test.ts: attach and delete-all refuse to run once a different account holds the session. Confirmed to fail without the guard.
    • feature-config.test.ts: refuses a Pro model without Pro, and changes one tool while keeping the others.

Screenshots cover History (all scopes, unsorted), the announcement banner, You, Delete all my data, and the model picker. Shot on iPhone light en, iPhone dark hu and iPad light en.

Staging, seed Pro account:

  • A 6a-test case: created unsorted, added to a debate from History, then deleted. Cases went 2 → 3 → 2.
  • The Generate model was changed in the picker, read back in the website's shape, then restored.
  • The data export returned the expected keys.

Not proven

  • Delete all my data was shown only up to its confirmation, never run.
  • Dismissing an announcement was tested only against made-up announcements.
  • Native-only behaviour, such as action-sheet greying and the share sheet, wasn't checked.

Codex review rounds follow as comments.

🤖 Generated with Claude Code

History, announcements and account tools for the Expo (React Native) app, matching the website. This is not an Xcode project. ios/ and android/ are generated and gitignored, and the checks below were run on Linux.

What's in it

  • History. A scope picker covers the Current Debate, any other debate, and one-off or unsorted work. The website's tabs follow: cases, chats, attacks, brainstorms, factoids and Sparring rounds.
    • Rows open their documents or the Flowchart. Chats reopen in Ask, and Sparring rounds reopen as their scorecard.
    • Cases and Sparring rounds delete with the website's confirmation.
    • The unsorted view shows the suggested debate and has Add to debate, through the website's owner-checked attach_to_debate function.
    • You get there from See all on the Debate tab or History in You.
  • Announcements. A banner at the top of the Debate tab, with the website's date filter, a "1 of 2" counter and server-side dismiss, so dismissing on one side dismisses on both.
  • You.
    • Download my data: the account export, saved as debatelab-data-<date>.json and shared.
    • Delete all my data: the website's email-typed confirmation in a form sheet. Afterwards it clears session memory and reloads.
    • Terms and Privacy: open in the in-app browser.
  • Model picker. AI model and Reasoning in Generate, Attack, Brainstorm and Factoids, and as chips in Ask.
    • Pro models are greyed out without Pro.
    • Choices write feature_configs in the website's shape through the settings write queue.
  • Moved to core. The announcement schema and date filter now live in packages/core, and the website imports them back in one line.

Out of scope

  • Share links: the website has no code that creates them.
  • Upgrades and promo codes: these wait for in-app purchase.
  • DOCX and Google Docs export, and the onboarding tour.

Synced onto feat/native-ios

Merged origin/feat/native-ios at 036b76ba (push notifications, PR #140) into this branch as 048a1e11. A rebase would have replayed earlier merge commits already on this branch, so this is a merge commit. Sibling PR #144 (iPad layouts) was still open and conflicting, so it did not land first.

Conflicts, all keep-both:

  • apps/mobile/AGENTS.md: this branch's offline-cache notes, including that filing a judged Sparring round drops the one-off History copy, plus the base's push-notification rules.
  • apps/mobile/src/app/(tabs)/(you)/you.tsx: History, download, delete, and Terms, plus the base's Notifications card.
  • src/locales/{en,es,hu}.json: historyDesc and deleteDataDesc plus the notifications strings.
  • apps/mobile/scripts/screenshots.ts: the base want() filter, with History, announcements, account, and model groups added.

History, announcements, account tools, and the model picker sit on parts that are already on feat/native-ios (Sparring, offline cache, entitlements, push). They do not depend on the iPad layouts in PR #144.

Bugs fixed on this sync

  • Model picker race. The 5 second fallback in readFeatureConfigAfterWrites copied feature_configs and then waited on the entitlement. If the save finished in that gap, pending was cleared and the tool kept the model the save had just replaced. The fallback now reads the row again when the pending choice disappears during that read. Covered by feature-config-read.test.ts.
  • Sparring attach. After a judged round was filed into its debate, attachSparring dropped the debate's History copy but not History's one-off copy, so the round still showed under unsorted work offline. It now drops debateListScopes(null, 'sparring') the same way Add to debate does. Covered by sparring-attach.test.ts.

Checks on 048a1e11 (Linux, Expo, Node 22)

Passed:

  • npm ci
  • npm run -w @debatelab/mobile typecheck (app and scripts)
  • npm run typecheck
  • npm run docs:check
  • npm run lint (eslint, knip, repo rules)
  • npm test: 50 files, 352 tests. One unrelated backend-status hook timed out when git commit signing was on; the same file and the full suite passed with commit.gpgsign=false for that run only. The test was not changed.
  • npx expo export --platform web and npx expo export --platform ios both produced bundles. The iOS output was deleted and was not committed.
  • Playwright against the web export: /, /sign-in, /history, /you, /delete-data, and /generate returned 200 with no page errors and no console errors. Signed-out protected routes redirect to sign-in. The sign-in screen shows "Sign in to DebateLab".
  • npm run build (website). Chunk-size warnings only.

npx expo-doctor (1.21.5): 18/21 checks passed, exit 1. npx expo install --check exits 1 for the same patch drift. apps/mobile/package.json is unchanged against origin/feat/native-ios, so these are already on the base:

  • expo-audio wants expo-asset as a direct dependency; it is only present transitively.
  • Duplicate React 19.2.3 (app) and React 18.3.1 (website). That split is intentional. Do not dedupe.
  • Expo patch versions are behind the doctor's expected patches (expo 57.0.27 vs ~57.0.31, and the same drift for crypto, linking, network, notifications, router, sharing, splash-screen, widgets). This PR does not bump the SDK.

GitHub Actions jobs on this head failed in about two seconds with no logs (log not found). That is the account being out of Actions minutes, not a code failure. A local pass is not a CI pass. Vercel production preview failed on the build rate limit; the staging preview succeeded. CodeRabbit succeeded.

Not proven

  • No STAGING_DEMO_CODE in this environment, so signed-in History, announcements, download, the delete sheet, and the model picker were not driven. Delete all my data was never run.
  • Dismissing an announcement was tested only against made-up announcements.
  • Native-only behaviour (the action sheet greying Pro models, and the share sheet) needs Expo Go or a dev build on a device. No Xcode or simulator run was required or done: the app is Expo.

Codex review rounds follow as comments.

Open in Web Open in Cursor 

Programcode and others added 9 commits October 14, 2026 12:41
History mirrors the website's History page: saved cases, chats, attacks,
brainstorms, factoids and Sparring rounds for the Current Debate or any other
debate, and one-off work with Library sources outside every debate, each with
the suggested debate and Add to debate through attach_to_debate. Rows open
their documents or the Flowchart, chats reopen in Ask, and cases swipe to
delete with the website's confirmation. Sparring rounds stay read-only until
Sparring is in the app. The Debate tab's See all and a row in You open it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app lists the account's active announcements through the announcements
Edge Function and shows them one at a time at the top of the Debate tab, with
the website's position counter and dismiss. Dismissals are stored on the
server, so they are shared with the website. The startsAt/expiresAt filter
and the response schema move to packages/core so both clients use one copy;
the website's behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Download my data asks the account function for its export, as the website
does, and shares it as debatelab-data-<date>.json. Delete all my data runs
clear_current_user_data after the website's email-typed confirmation in a
form sheet, then forgets this session's results and drafts, turns one-off mode
off and reloads the debates. Both account-wide writes refuse to run once a
different account holds the session. Terms and Privacy open the public
website's pages in the in-app browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each form gets the website's ModelSelector as two native rows, AI model and
Reasoning, and Ask gets them as chips beside its options. Pro models show
but stay unselectable without Pro access, and reasoning stays at Low for Auto
and for students without Pro, as on the website. A change writes the whole
feature_configs object in the website's canonical shape through the settings
provider's serial queue, filling the defaults when nothing was saved, and a
failed write rolls back only its own field. Tools read their model through
the same queue, so they run with the model just picked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tool reads its model behind pending settings writes so it runs with the
model just picked. A write that hangs would have held Generate, Attack,
Brainstorm, Factoids and Ask back with it; after five seconds the read now
goes ahead on its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The screenshot script gains a SCREENS filter to run only some groups, and
groups for History (Current Debate, chats, the scope menu, another debate and
the unsorted view from made-up rows), the announcement banner from two
made-up announcements with a local dismissal, You down to its data rows with
Download my data (read-only, iphone-light-en) and the Delete all my data sheet,
never confirmed, and the Generate model picker with its menu open, never
chosen. The Delete all my data field's email placeholder is masked too.

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in typed Sparring (PR #138). Conflicts in apps/mobile/AGENTS.md and
scripts/screenshots.ts keep both sides: the route list names history.tsx,
delete-data.tsx and sparring/, and Sparring screenshots become their own
SCREENS group next to History, announcements, account and model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
History's Sparring tab now works as the website's HistoryPage does: a row
opens the round's scorecard at sparring/[id] with History as its back label,
and swiping deletes it after the website's confirmation through
deleteSparringRound, which filters on the user. Deleting the finished round
the Sparring screen still holds forgets it there too. The empty tab starts a
round in the app. Unsorted rounds keep Add to debate through attachToDebate,
which passes the student's side and checks the signed-in account; the
screenshot script now shoots the Sparring tab.

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

vercel Bot commented Oct 14, 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 3:53pm UTC
debatelab-staging Ready Ready Preview Oct 22, 2026 3:53pm 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 14, 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.

@Programcode

Copy link
Copy Markdown
Owner Author

Codex review r1 at 0e391e2 (gpt-6.1-sol, high): 3 findings, accepted, fixes in progress.

  1. High (account.ts:47): Delete all my data checks account A but the parameterless RPC authenticates with whatever session supabase-js holds when it sends, so an account switch in between could delete B's data.
  2. Medium (settings.tsx:292): the 5 s fallback reads the server copy past a pending model change, so a tool (and Ask's cached config) can use the previous model.
  3. Medium (delete-data.tsx:63): after a successful delete, a failed debates refresh leaves the erased debates in local state.
    Codex found ownership filters, RPC policies, settings shape, Pro gating, announcements, the core move and locale parity sound.

Programcode and others added 6 commits October 14, 2026 21:46
clearAllData checked that the session was still the confirmed account, but
supabase-js read the session again when it sent the RPC. An account signed
in between the two would have had its data deleted. The request now carries
the access token from the session that passed the check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tool waits up to 5 seconds for earlier settings writes, then reads its
config anyway. That read returned the row's previous model while the new
pick was still saving. The settings provider now keeps each tool's pending
model and reasoning and applies them over the fallback read (leaving out a
Pro model the student can't use). Ask also drops its cached config when a
save fails, so it never keeps a model that wasn't saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After Delete all my data the sheet only reloaded the debates. When that
reload failed, the deleted debates and the Current Debate stayed on screen
and new work still targeted them. The debate context now drops the account's
debates and Current Debate first (and any load already running can't bring
them back), then reloads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The model picker, settings writes and default model now read Pro access
from effective_entitlement through readEntitlement, as feat does, instead
of the user_settings tier and Stripe rows. extra.websiteUrl moves from the
deleted app.json into app.config.ts. Both history and permissions strings
are kept in en, es and hu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Debate tab imports both the announcements hook and feat's copy
holder. The screenshot harness keeps SCREENS= groups and feat's
ONLY=offline run.

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

History's debate lists and its work outside every debate read through
cachedRead, so they answer from a saved copy offline, and the screen
holds its copies with useCopyHolder. Every write that already drops a
debate's counts and lists now drops that debate's History copy too, and
work outside every debate drops the one-off copy. Adding work to a debate
from History drops the item's own copy and the lists on both sides, and
Delete all my data drops every saved copy before the debates reload.

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

Copy link
Copy Markdown
Owner Author

Round-1 fixes: efe5580 (Delete all my data sends the RPC with the confirmed session's own token; test with a real supabase-js client fails without it), 1c147e7 (after the 5 s wait, tools apply the pending model pick on top of the saved config; Ask drops a config cached with a pick that failed to save; test fails without it), f949f96 (forgetDebates clears the debate list and Current Debate after Delete all even if the reload fails). Then merged feat/native-ios twice (80dbb4f entitlements + app.config.ts; 37b2433 offline reading): Pro gating now goes only through readEntitlement, extra.websiteUrl moved into app.config.ts, and 8f55dcd wires History, Add to debate and Delete all into the offline cache. Local at 8f55dcd: npm run verify green (343 vitest, edge 371, 30 smoke), mobile typecheck, eslint, knip pass. Codex r2 next.

@Programcode

Copy link
Copy Markdown
Owner Author

Codex review r2 at 8f55dcd (gpt-6.1-sol, high): r1's three fixes and both merges checked (token pinning across refresh/account switch, all 15 delete-all cache groups, entitlement gating, config only adds extra.websiteUrl). Two new Medium findings, accepted, fix in progress:

  1. feature-config-read.ts:23: if the save completes while the 5 s fallback is awaiting the entitlement, the fallback can still return the old model.
  2. debates.ts:301 / sparring.ts:102: automatic Sparring attachment doesn't drop the one-off History copy, so offline the round still shows outside every debate.

Keep push notifications on You alongside History, announcements, and account
tools. A model read that overlaps a finished save reads the row again, and
filing a judged Sparring round drops the one-off History copy.

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

@cursor
cursor Bot merged commit 815bc80 into feat/native-ios Oct 22, 2026
6 of 17 checks passed
@cursor cursor Bot mentioned this pull request Oct 22, 2026

This branch was successfully deployed

2 active (1 outdated) deployments
Preview – debatelab-staging — 048a1e11 Deployed Oct 22, 2026 by vercel[bot]
Preview – debate-navigator — 8f55dcd9 Deployed Oct 14, 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.

3 participants