Repository navigation
Repair persistent sessions and integrate page control fixes - #601
Merged
NewWorldVenture merged 32 commits intoSep 27, 2026
Merged
Conversation
All 398 page.tsx routes are listed in thirteen lane files under docs/audit/pages/, each lane mapped to the A-unit that owns the same paths (docs/audit/COORDINATION.md), so Claude sessions and Codex can audit in parallel without editing the same file. A lane is claimed before it is worked; README.md holds the protocol and what PASS means. finalaudit.md carries a generated roll-up (per-lane counts and every page's status). scripts/page-audit-register.mjs regenerates it from the lane files, so a conflict there is resolved by re-running, never by merging. tests/every-page-is-in-the-page-register.test.ts fails if a page is missing, listed twice, carries an unknown status, or the roll-up is stale. scripts/page-audit-crawl.mjs drives Chromium over every route and records status, redirects, console/page errors, failed same-origin requests, raw catalogue keys, the error boundary and overflow at 390 px. scripts/page-audit-session.mjs signs a disposable account in through the real login form against a LOCAL Supabase only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
C1-S9-94 — /dashboard/calendar threw "cannot add postgres_changes callbacks ... after subscribe()" and dropped a section to its error boundary. The browser client is a singleton and realtime-js channel() returns the EXISTING channel for a topic already open, so two readers of one table shared a channel: the second's .on() came after the first's .subscribe(), and whichever unmounted first removed it from under the other. lib/realtime/own-channel.ts gives each postgres_changes subscription its own topic (the server filters on the .on() config, not the name); all ten sites use it. Presence keeps its shared topic, since presence meets on it. A guard reproduces realtime-js's dedupe in a fake and is red on the shared name. C1-S9-95 — every blog post's share links hydrated with a different href than the server sent: window.location.origin in the browser, a hard-coded host on the server. They now use canonicalUrl on both sides, which is also the address a share should carry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
C1-S9-96 — /dashboard/vacations/new opens "New trip" on arrival and failed hydration. Modal gated its portal on `typeof document`, which is false on the client's first render (the hydration render), so the server sent nothing and hydration produced a portal; React threw the tree away and rebuilt it client-side. useHydrated reads the server snapshot while hydrating, so the portal waits one commit. The command bar takes the same gate. A guard fails on any portal gated on a typeof check, and is red on the old Modal. C1-S9-97 — every missing record under /dashboard rendered AppNotFound's defaults, which were English literals in its parameter list, where no catalogue scanner looks; the wallet passed a literal label too. Four keys in the seven base catalogues; rendered in German by a test that is red on the old component. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
use-realtime-query and app-context now import lib/realtime/own-channel, and the fixtures that mount them in a page list every module they load. The module-graph guard (tests/medications-fixture-module-graph.test.ts) named all eight; each lists the file now. The ten fixture specs that load the hook pass locally (218 tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
Found by the page audit crawl, signed in as a super administrator whose family is on the free plan: - /dashboard/autopilot said "100% probability the day runs smoothly", "0" risk alerts and "All clear" after POST /api/autopilot/scan answered 403. A refused scan left no open suggestions, and the forecast read that absence as good news. The forecast, the risk count and the all-clear now wait for a scan that ran. A failed one shows "—" and says so, with a retry. - /dashboard/briefing said "Failed to generate briefing" in English to a refusal the route had worded in the reader's language. The route's reason (wait, plan, try again) now reaches the screen, and the session's own two fallbacks are translated at render. The 403 itself: requireFeature lets a super administrator onto every feature page to preview it (F16 records the same bypass for the family write path), and none of the endpoints behind those pages did. So refuseUnlessEntitled and the on-demand scan now let a super administrator through too, asked only after the family has been refused. The nightly cron still runs each family on its real plan. Guards (red on the old code, each checked): the route gate and the scan route pass a super admin below the tier and answer 503 when the account cannot be read; the Autopilot screen shows no forecast after a 403, a 500 or before the first scan finishes, and does after a clean one; a refused briefing keeps the route's reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
Two conflicts, both additive: messages-module keeps ownChannel (C1-S9-94) and main's family-media imports; hydration-ledger's fixture lists both own-channel.ts and main's lib/ui/a11y.ts. main's new tests/a-dialog-does-not-steal-the-caret.test.ts calls Modal as a function under mocked hooks, and Modal now also reads useSyncExternalStore (useHydrated, C1-S9-96). The harness models that hook as a client render after hydration does — it returns the client snapshot — and asserts exactly what it did before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
…n its sentence The page audit crawl, signed in at 390px, found 17 routes that scrolled sideways, from 8px (/dashboard/sports) to 647px (/admin/support-tickets). A DOM bisection per route found four causes: - A responsive grid with no mobile column count. Its single column is an implicit `auto` track, never narrower than its content's min-content, so a table or a filter row widened the page. Fixed with grid-cols-1 (minmax(0,1fr)), and minmax(0,1fr) in the lg templates: six admin pages, social settings, marketplace community, outcomes. - A row of controls that could not wrap. PageHeader wrapped its own slot but not the button row 31 modules pass into it (photos, recipes). It wraps that row now. Behavior, guardian and the kitchen display wrap their own. - A column tile laid out as a row. `.stat-card` is a flex row, and sports and school stack an icon over three lines of text inside one. Those tiles are columns now, and their English labels come from 13 new keys. - Text that could not wrap. The feedback admin's GitHub notice is now one wrapping sentence rather than loose flex items. An inline link's truncate did nothing, so the link is a block. 0px on all 17 afterwards, with the same probe. Found alongside: all seven catalogues defined three locator keys twice (from a Sep 26 commit). JSON.parse keeps the last copy, so the geofence failure had lost "The alert setting has not changed". The later copies are removed, and tests/no-catalogue-defines-a-key-twice.test.ts scans lines, not the parsed object (red on main's catalogues). tests/a-page-fits-a-phone.test.ts pins each cause, and ratchets the 330 remaining responsive-but-implicit grids so the count can only fall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
…anguage
The i18n gate covers app/(marketing). Thirteen public and sign-in pages sit
outside it, and still carried English:
- a browser-tab title on /login, /signup, /kid-login, /auth/recovery,
/auth/complete, /auth/step-up, /join, /offline, /gift/[token],
/pay/[handle], /reviews, /reviews/new and /s/[slug]
- the join link's "This invite link is missing its token."
- the review page's "Review us on {platform}" buttons and its star labels
The titles come from generateMetadata and catalogue keys. Each review button
is one whole sentence per platform, because "on the App Store" and
"on Google" take different words in German or French. The star labels live
in the scope the page ships to the browser. That adds 21 keys to the seven
base catalogues.
Left as they are: three strings the server writes into rows or an email with
no reader locale (the gift notification, the welcome email subject, the
imported-event note). All three are I18N-001, which belongs to the owner.
tests/auth-callback-completion-page.test.ts now awaits generateMetadata and
asserts the title as well as noindex and no-referrer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
…larms scripts/page-audit-static.mjs follows each page's imports to the app/ and components/ files that render on it, and counts their hardcoded copy with the repo's own scanner. It is the machine half of the README's "read the page" step. Shared chrome and the UI kit are reported once, not on all 354 pages. The crawler now: - counts the signed-in app's own not-found copy (appNotFound.title) as a not-found, as well as the site's 404 - treats a 429 from the two tracking beacons as environmental: they rate-limit by IP by design, and a crawl is hundreds of views from one address - ignores catalogue-key spellings inside code, pre, kbd and samp The last one was /admin/social/providers. It lists TikTok's required OAuth scopes, one of which is `video.publish`, the same spelling as a catalogue key. The scopes are identifiers, so the page now marks them up as <code>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
Rows: - FIXED, each naming its finding and commit: calendar, autopilot, briefing, behavior, vacations/new, blog/[slug], the 17 routes that overflowed at 390px, and the 13 public and sign-in pages whose titles and copy were English. Where a fixed page still carries hardcoded copy, its row names the count under C1-S9-101. - IN PROGRESS, with crawl and static evidence: the rest of the PUBLIC and AUTH lanes, which Claude-1 holds. The read is still pending. Lanes Claude-1 does not hold were claimed for this edit and released FREE in the same commit. finalaudit.md: - C1-S9-93 to C1-S9-100: the register, and the seven fixes the crawl led to. - C1-S9-101 (OPEN, HIGH): 2,765 hardcoded English strings across 596 files in the signed-in modules, counted per lane. - C1-S9-102 (OPEN, LOW): notFound() under the app loading boundary answers 200. PRODUCTION READY stays NO. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
…nguage Every blog post's save button had four English labels, and the subscribe form's failure message was English. components/blog sat outside every gated i18n surface. It is gated now. Five keys in seven catalogues. The save button's keys use a blogHeartButton prefix, added to the marketing client scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
occasionLabel() returned English, and both the public gift page and the parent's gift screen (occasion picker included) printed it. The public page also fell back to "a child" and "a family" when a name was unknown. It is now occasionLabelKey(), and each caller words the key with its own t. Eight keys in seven catalogues. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
…flags English prose The i18n scanner reads source by pattern, and misses copy in a ternary or a setter argument (C1-S9-103). With --locale de-DE the crawler sets the locale cookie and reports every visible text node, aria-label, placeholder or title that has two or more English function words. Code and aria-hidden text are skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
… that were not true C1-S9-105. Controls that looked like controls and had no handler: - Invite Admin (/admin/admins) and New Ticket (/admin/support-tickets) open forms that call their existing, previously unused actions. - Export downloads a CSV from a new super-admin route. It reads every ticket, paged. A failed read is a 502, the export is audit-logged, and a formula-leading cell is written as text. - The ticket menu's "Assign agent" had no action at all. It is now "Assign to me", backed by a new assignTicketToMeAction. - "Ask AI" on sports and school opens that page's AI insight. - Each row's "more options" on both tables is now a confirmed delete, with a read-back. - The six sports tabs changed only their highlight. Each now shows its own sections, and the two with nothing behind them are gone. - School's tab labels, weekday names (via Intl) and due-in text (via Intl.RelativeTimeFormat) follow the reader's language. tests/a-button-does-something.test.ts scans every page and component for a button with no action, and carries its own self-test. C1-S9-106. The /admin/admins Settings tab showed four switches that did nothing. Two of them said "Require 2FA for all admins" and "Admin session timeout" were ON, and neither is enforced anywhere. They are now read-only statements of what the console enforces today. Whether to require two-step sign-in and an idle timeout for admins is recorded for the owner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
main now carries session_01KP9rt5's page audit (#591): a per-page "Every page" table in finalaudit.md with lanes A–F, fixes for the same defects this branch found, and its own approaches to two of them. Conflicts, resolved: - Grids on six admin pages and outcomes: this side's grid-cols-1 plus minmax(0,1fr), a superset of main's minmax, and what tests/a-page-fits-a-phone.test.ts pins. - Public page titles: this side's generateMetadata. The pageTitle.* values drop " · Bubaly", because main's root template now adds the brand. - /gift/[token]: main's dead-link card, with this side's translated title and fallbacks. - AppNotFound: main's comment and its notFound.goToDashboard key. This side's parallel appNotFound.goToDashboard is removed, and its test reads the adopted wording. - Seven catalogues: merged key by key three ways, written in main's key order. Composed rather than chosen: - Autopilot. main's preview skips the automatic scan. An unscanned preview now says "no scan has run yet" and offers Re-scan, rather than sitting on "Scanning your family…". Re-scan works for a super admin through C1-S9-98's route bypass. C1-S9-107, one register: - Claude-1's docs/audit/pages/ lane files and roll-up are removed. - scripts/page-audit-register.mjs lists routes and checks main's table: every page.tsx has exactly one row. The test says so. - Lanes A–F are all claimed elsewhere (A by session_01TRY21 in #594). This branch's fixes on their pages are in a hand-off table above the register, not edited into their rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
main's port of C1-S9-94 (854d8dd) added the same import this branch had, and the merge kept both. tsc: TS2300 duplicate identifier. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
… titles off a phone Found by rendering every page in German (page-audit-crawl --locale de-DE). The i18n gate had called these surfaces clean, because the copy sat where it does not look: - The closing call to action's defaults, in a parameter list, closed /, /faq, /privacy, /terms, /cookies and /acceptable-use on English. - /ai's call-to-action subtitle was an English attribute. - Two /privacy paragraphs were literals inside an array of keys. - /reviews/new fell back to English default copy. All are catalogue keys now: eight keys in seven catalogues. "Datenschutzerklärung" and "Nutzungsbedingungen" are single words wider than a 390 px phone. The legal title now hyphenates by <html lang>, with a break fallback, so all four legal pages measure 0 px in German. Left OPEN as content: the FAQ answers come from marketing_aeo_questions, which 0279 translates only in part. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
…ices" says why it can't open
/dashboard/billing showed English in every locale:
- the plan-choice button ("Current plan", "Choose Family+", "Switch billing
to annual")
- the status badge, and the plan names, descriptions and feature lists
- the tabs and stat labels
- the fifteen spending categories
All of it now comes from catalogue keys, in labelKey/nameKey fields.
Categories stay stored as the English word, because it is data, and are shown
through categoryLabel().
openPortal() read the route's JSON, found no url, and did nothing, so every
refusal the route words (not a parent, no billing account, unavailable) was
dropped. It now toasts that reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
…mplate creates /dashboard/chores showed English in every locale: - the status badges, tab labels and approval toast - every "N pts" - the ten chore templates A template is worse than a label: picking one wrote its English title and description into the family's new chore. Templates now hold keys, and each is worded when shown and when picked, so the chore is created in the picker's language. 33 keys in seven catalogues. Scanner findings for chores-module drop from 36 to 3 (type-alias fragments). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
NewWorldVenture
marked this pull request as ready for review
September 27, 2026 18:31
NewWorldVenture
pushed a commit
that referenced
this pull request
Sep 27, 2026
19 files conflicted, almost all where both sides had translated the same thing: - Auth and public page titles: main's C1-S9-100 translated them too. Main's version is kept, and the duplicate imports both sides added are dropped. - /reviews/new star labels and the chores approval toast: same text, and main's keys are kept. - The auth-callback test keeps main's note, with the port's mock that passes parameters through. - Catalogues: the union. Six keys that both sides added twice are kept once, with main's wording where the two differed. - i18n ratchet: both histories kept. The merged tree measures 2,208, banked as the ceiling. tsc clean. Lint: 0 errors, 15 warnings. Full suite in UTC: 21,381 tests in 1,698 files, with the 12 duplicate-key failures now fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXw2nu25BjyRfA6Fg3YiMS
NewWorldVenture
pushed a commit
that referenced
this pull request
Sep 27, 2026
…ct-45d8k6 Main integrated this branch's PR585/598 work. Its side is kept wherever both touched the same thing: - route-feature-gate: main's isSuperAdmin(db), with 503 when the read fails; route-plan-gate.test.ts is main's copy, which pins that 503. - a-client-write-reads-what-it-changed, a-seeded-settings-row…: main's versions of the same translated assertions. - Catalogues: main's order and values (incl. its appNotFound wording) plus this branch's 540 keys main lacks; nothing of main's removed. - finalaudit.md: C1-S9-115…126 kept after main's entries. - Ratchet: main's 2650 lowered to 2349 by C1-S9-114…126. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stale SDK session events or reads can arrive after logout, an account switch or token rotation. This change checks their token against current cookies before replacing the active identity, preventing an old result from restoring the wrong session. Reconciliation stays outside the SDK callback lock; legitimate rotation and temporary read failures preserve the established login.
This also integrates the remaining reviewed page/control repairs from #598 at4e2abf1 with main e96e745, which already contains #585 and #603. Initially open dialogs now install focus, keyboard and scroll behavior when their hydrated portal appears. Endpoint plan overrides retain the authenticated principal and report lookup outages as503. Responsive, translation, administrator ticket and export repairs are retained, along with all current catalogue keys. Shared navigation matches current main exactly; no SQL changes are included.
The durable meal test acknowledges the real save notification through its visible Dismiss button before proceeding, retaining database checks and its120-second limit. Seven existing controlled-browser fixtures now pair their synthetic cookie token with their SDK session; all assertions are retained, and a mismatched token still fails the real session guard. Source scans work on Windows and Linux.
Final52d validation passes: 20,856 unit tests across1,638 files in each of three hosted time zones; production build247; full generated types; lint; Database and Mobile jobs. Hosted run36339544177 finishes with a clean1,786/1,786 end-to-end browser matrix in18.6 minutes, with no failed or flaky cases. The separate canonical local run also passes20,856 UTC units/build/types/lint with all5,221 raw files/modes unchanged. The session repair retains six original failing controls and17 final browser passes. Earlier failures remain recorded in the audit rather than being erased by this clean run.
The five audit documents preserve all earlier failures, permanent IDs and Claude's attributed findings. RegisterB includes14,214 items after reconciling one previously unlisted existing test. Production readiness remains NO. Framework upgrade #602, newer #598 follow-ups and the reproduced Inbox selection/draft defects remain separate; no complete provider, physical-device or private-media workflow acceptance is claimed.