Skip to content

Load only the active locale and open the app offline (T11: PF-01, PF-02, PF-04, PF-05, PF-06, PF-09) - #215

Merged
Programcode merged 11 commits into
mainfrom
fix/t11-perf-offline
Oct 23, 2026
Merged

Programcode merged 11 commits into
mainfrom
fix/t11-perf-offline

Conversation

@Programcode

Copy link
Copy Markdown
Owner

Batch T11 from the 22 Oct QA report: performance and offline. One PR, scoped to PF-01, PF-02, PF-04, PF-05, PF-06, PF-09 and the icon cache header. No migrations, no Edge Function changes, nothing to deploy beyond the frontend.

Measurements

Both builds (origin/main at 508f243, and this branch) were served from dist/ by a local HTTP/2 + gzip server that applies the vercel.json app-shell rewrites. Every Supabase, Jam, Sentry and Vercel request is blocked, and signed-in routes use a stub session. Absolute numbers are local and better than production's; compare the columns, not the absolute values.

Time until the page's own content is on screen, mobile conditions (150 ms RTT, 1.6 Mbps down, 4x CPU slowdown, HTTP cache disabled), median of 3 cold loads. For / this is when React takes over the prerendered page; for the others, when the route's <h1> appears.

Route Before After Change
/ (React takes over) 4.35 s 3.70 s −0.65 s
/login 3.63 s 2.79 s −0.84 s
/app 4.07 s 3.96 s −0.11 s
/app in Spanish 4.07 s 4.01 s −0.06 s (now fetches the es chunk first)
/app/generate 5.39 s 5.01 s −0.38 s
/app/settings 4.03 s 3.91 s −0.12 s
/app/sparring 6.04 s 5.18 s −0.86 s

Assets transferred before the heading appeared: /app 467 → 409 KB, /app/generate 626 → 569 KB, /app/sparring 681 → 625 KB.

JavaScript per route (gzip -9; the entry and its static imports, plus the route's own chunks, with the signed-in layout counted for /app routes):

Before After
Entry main-*.js 1003 KB raw / 308 KB gz 680 KB raw / 203 KB gz
Entry + static imports 1242 KB / 386 KB gz 904 KB / 276 KB gz
/ 435 KB gz 298 KB gz
/login 394 KB gz 287 KB gz
/app 391 KB gz 326 KB gz
/app/generate 540 KB gz 476 KB gz
/app/settings 405 KB gz 340 KB gz
/app/sparring 597 KB gz 533 KB gz
/app/library 417 KB gz 353 KB gz
/app/history 409 KB gz 344 KB gz
/app/timer 393 KB gz 328 KB gz

Lighthouse 13.5.0 (performance only, median of 3, DevTools throttling, fresh profile per run). Scores and TBT improved on most routes, but treat this table with care. Lighthouse picks the startup shell's text, or late "Loading…" text, as LCP. /app/settings reads about 40 s in both builds because a blocked Supabase call keeps that text changing. On the baseline, Chrome reported Generate's and Sparring's route chunks as 0 bytes, finishing faster than the throttled link allows, which flatters the "before" column there. The timing table above is the comparison to rely on.

Route Score before → after LCP before → after TBT before → after
/ 67 → 72 2.68 → 2.57 s 1065 → 975 ms
/login 67 → 74 5.03 → 4.25 s 507 → 441 ms
/app 61 → 65 4.90 → 5.03 s 699 → 542 ms
/app/generate 56 → 54 5.29 → 6.18 s 949 → 933 ms
/app/settings 48 → 53 (40 s, see above) 726 → 495 ms
/app/sparring 54 → 53 5.44 → 6.58 s 1109 → 887 ms

Desktop Lighthouse (simulated throttling) is 99–100 on every route in both builds. The QA baseline (34fb585, before C1: mobile 51–73, LCP 5.4–11.9 s on signed-in routes) was measured on production, so it is not comparable with these local runs.

What changed, by issue

PF-02: the entry shipped all three locales

  • src/i18n.ts: English stays in the entry; Spanish and Hungarian (app and /tools strings) are dynamic chunks loaded by a small loader. The first render waits for the chosen language (appLanguageReady() in mountApp), so an ES or HU visitor goes straight from the startup shell to translated text. If the language is changed twice quickly, the later choice wins over strings still loading; i18next's own backend would let the late load win and would treat a failed read as done for good. If the strings can't load (offline and never cached), the app stays on the current language.
  • English is kept in the entry rather than split into a separate "fallback chunk": it is the default, so splitting it would add a round trip for every English visitor. The cost is that a Spanish or Hungarian cold load pays one extra request (about 30 KB gzip) before first render.
  • Prerendered public pages still render in English at build time and switch on load, as before.
  • One page needs every language: T9's flowchart (Fix Flowchart QA issues (T9: FC-01–06, PF-03) #206) recognises starter text saved in any language. Its route loader (/app/flowchart in src/lib/route-modules.ts) now fetches all catalogs before the page renders. Without that, a chart saved in Hungarian and opened in English kept its Hungarian starter nodes, and four of T9's tests caught it. They now load through the route loader.
  • scripts/check-build-boundaries.mjs now fails the build if the es/hu catalogs come back into the entry or the landing, login, home, generate or attack static graphs.
  • framer-motion's 118 KB use-reduced-motion chunk no longer loads on /. The landing Reveal is now a CSS transition with an IntersectionObserver, and reduced motion keeps the fade without the slide. Charts (recharts), PDF and DOCX export were already off the initial routes (the boundary check enforces this).
  • I measured the 16 tiny icon modulepreloads and left them as they are. One shared lucide chunk (39 KB raw) cut the landing page from 23 chunks to 6, but added about 3.5 KB gzip to every route. It also made the embeddable timer download the whole icon set instead of 600 bytes. Telling the embed's icons apart isn't possible from the module graph, because lucide's barrel file imports every icon. The tiny chunks load in parallel through the modulepreloads, so they add requests but no extra round trip.

PF-01: 5 to 12 s LCP on phones

  • AppLayout (header, navigation, Current Debate dialogs with the select/popover/form code) is lazy, so / and /login no longer load app-only code. The boundary check now enforces that as well.
  • mountApp starts loading the page chunk for the current path, and the layout under /app, while the entry is still starting up. Before, that request only started once React rendered down to the lazy() boundary, after AuthProvider's session check.
  • Not changed, and probably the largest remaining cost on a real phone: RequireAuth waits for AuthProvider's getUser round trip before rendering any /app page. That gate exists so a revoked session never renders. The page chunk now downloads during that wait instead of after it.

PF-04 and PF-09: offline app pages failed, and every navigation was cached as /index.html

  • The build writes this build's precache list and cache id into dist/sw.js (serviceWorkerPrecache in vite.config.ts), generated from the Vite manifest, not a hand-kept list. It covers the shell's scripts and styles, the signed-in layout, Home, Library, History, Timer and Settings, the es/hu strings, and Inter's Latin fonts. A missing manifest key fails the build. This is 88 files, 1.7 MB raw, about 520 KB compressed. Hashed assets may come from the HTTP cache, so the page that registered the worker doesn't download them twice.
  • In public/sw.js, navigations to the paths vercel.json rewrites to app-shell.html (/app*, /login, /share/*; /expocit/sources is rewritten too but its prerendered file wins, so it counts as a public page) go to the network first and, offline, get the precached shell. Public pages are stored under their own path, and offline a page is only answered with itself, or with the offline page if it was never visited. Before, every navigation response was stored as /index.html, so /app offline could come back as Terms.
  • /auth/callback, the Supabase paths (including the staging-preview proxy) and /_vercel/ (Analytics and Speed Insights) stay network-only. Before, the /_vercel/ scripts were cached forever.
  • Caches are named debate-lab-v6-<build id>-{shell,runtime,pages}. The next build's worker deletes them on activation, because a page cached by an old build references assets the new build no longer has. Install fails, and the browser retries later, if the two HTML documents or any precached asset can't be stored. Updates still wait for the normal lifecycle (no skipWaiting).
  • The offline indicator ("Offline mode", role="status") shows on /app. The timer works offline: its sounds use Web Audio, so there are no files to cache.
  • vite preview now applies the vercel.json rewrites, so the offline test and local measurements load app-shell.html for app routes, as production does. E2E runs on the dev server, so it is unaffected.
  • scripts/test-service-worker-offline.mjs (npm run test:sw) found a bug in the test itself: its page.waitForFunction(async ...) calls, including the ones already on main, passed at once without checking anything, because Playwright treats the returned Promise as truthy. They now poll from Node. The test now proves:
    • Home, Library, History, Settings and Timer open offline as fresh navigations after one visit to /app, without being visited first;
    • the timer runs offline;
    • the offline indicator shows;
    • switching to Spanish offline works and sets <html lang="es">;
    • a visited public page comes back offline as itself, and an unvisited one gets the offline page;
    • /auth/callback and the API paths never enter Cache Storage;
    • the earlier checks still pass (upgrade from an old worker, recovery UI for an unvisited route, intent-prefetched route offline, offline logo).
  • I checked that the new test fails against main's worker (with only its cache name changed to the new pattern): "Timed out waiting for the precache and the visited Terms page".
  • Related fix: src/lib/reload-stale-chunk.ts no longer reloads for a failed chunk while navigator.onLine is false. Offline, choosing a language that was never cached would have triggered the stale-deploy reload, and without a worker that replaces the page with the browser's offline error.

PF-05: offline, user_roles (and every read) was retried

  • Reproduced on the built app: opening /app/settings offline sent about 70 Supabase requests in 30 s. Each GET went out 4 times (supabase-js's own retry: 1 s, 2 s, 4 s), even though the user_roles query has retry: false, and user_settings went out 18 times.
  • src/lib/supabase/client.ts: every request is still sent. A REST, Functions or Storage read that fails while navigator.onLine is false is ended as an abort, the one failure postgrest-js doesn't retry, so it goes out once instead of four times. Online, the retries on transient failures are unchanged, and a browser that wrongly reports offline still sends every request. Writes and Auth are untouched.
  • TanStack Query refetches failed queries on online. Its own reconnect refetch needs an offline event first, and a page opened offline never gets one. This is how Settings' user_roles check resumes.
  • After the fix, the same reproduction sends each read once while offline (the 3 retries per read are gone), and Settings' failed queries load again when the connection returns. The tests are in src/test/supabase/offline-reads.test.ts, and the first one fails without the change (4 attempts).
  • The store hydrations (hydrateStores in AuthProvider) still don't rerun on reconnect, as on main. Offline the app shows the locally persisted data. Rehydrating on online would touch AuthProvider's account-switch logic, so I left it out.
  • Upgrade Now offline: T4 (Make billing states agree between Settings and the AI functions (T4: QB-02, QB-13, QB-14, QB-15, QB-08, QB-17, SC-03) #207) merged while this was in progress, so it's done here and kept to what the brief allows. In PricingModal.tsx the button is disabled while offline, with a short notice under it (aria-describedby), and it re-enables on online. That needed one new string, pricingModal.offlineUpgrade, in all three catalogs (the key-parity test requires all three). T13 may want to reword it.

PF-06: plain "Opening " loading states

  • Main had already fixed this for route chunks. The shared Suspense fallback has been a per-route skeleton (RouteSkeleton) since perf: smooth route loading and navigation #51 (9127589). "Opening " is its screen-reader label (sr-only), not visible text.
  • RequireAuth's full-screen spinner during the session check was still plain. It now shows the route's skeleton inside a <main> landmark, and so does the new lazy-layout fallback, so /app routes show the skeleton while AuthProvider checks the session and the layout loads.

Icon cache header (fold-in from C3)

  • The 30-day rule in vercel.json now covers apple-touch-icon.png, icon-192.png, icon-512.png, icon-1024.png and icon-maskable-512.png. The test reads public/, so a new icon file without a matching rule fails it (checked by removing icon-1024 from the rule). hero-visual is a .jpg in the repo; it was already covered.

Left open, owned by other batches

  • Generate ships React Flow (126 KB minified, 40 KB gz) for one enum. src/lib/flowchart.ts (from T9, Fix Flowchart QA issues (T9: FC-01–06, PF-03) #206) imports MarkerType from reactflow, which pulls all of @reactflow/core into the Generate route. Using the string 'arrowclosed' (typed as MarkerType) there would drop it. check-build-boundaries doesn't catch this, because its pattern looks for node_modules/reactflow/ and the code lives in @reactflow/core. Generate is T8's area, so I left it alone; widening the pattern should go with that fix.
  • Sparring ships recharts (365 KB raw) up front for the end-of-round scorecard (SparringScorecard.tsx, statically imported). Lazy-loading the scorecard is a Sparring component change (T8).
  • zod is in the entry because AuthProvider imports the sparring store and the crossfire storage for sign-out resets, and both import zod schemas. Making those lazy would change the sign-in and sign-out reset path, for about 15 KB gzip. Not done here.

Verification

Local, on the final head (CI minutes are used up, so these are local passes, not CI passes).

npm run verify (full ladder) at 02e6509c, exit 0:

  • docs:check, lint (eslint, knip and repo rules) and typecheck passed
  • npm test: 81 files, 586 tests passed
  • test:collaboration: 5 files, 30 tests passed
  • test:edge: 457 passed, 0 failed
  • build: vite build, prerender, sitemap, check-site (48 HTML files, no problems), check-build-boundaries (now also rejects the es/hu catalogs and AppLayout in initial graphs)
  • test:sw: {"upgradeActivated":true,"precachedRoutesOffline":["/","/app","/app/library","/app/history","/app/settings","/app/timer"],"authCallbackNeverCached":true,"timerRunsOffline":true,"languageSwitchOffline":true,"publicPagesKeepTheirOwnHtml":true,"offlineIndicator":true,"unvisitedRouteShowsRecovery":true,"intentPrefetchedRouteOffline":true,"offlineLogo":true,"apiCacheEntries":0}
  • e2e smoke (desktop Chromium, @smoke): 43 passed

Checks against the built app in Chromium (stub session, every non-local request blocked or answered locally):

  • Offline after one visit to /app: Home, Library, History, Settings and Timer open, the timer runs, the offline indicator shows, and switching to Spanish works (all inside test:sw).
  • Cold loads with the stored language set to EN, ES and HU on /app, /app/settings and /: app routes go from the startup shell to the skeleton and page in the chosen language, with no English text and no raw keys in between. Prerendered public pages still show their English HTML first and then switch, as on main; per-language public pages are T12's job (IN-04).
  • Upgrade Now: enabled online, disabled with the notice offline (aria-describedby set), and enabled again when back online.
  • Offline Settings: each Supabase read goes out once (about 20 requests in 30 s, down from about 70, and user_roles 1 instead of 4). Failed queries run again when the connection returns.
  • Sign-in redirect into /app: the layout and Home chunks start within 5 ms of each other.

Tests added or changed: src/test/i18n.test.ts (language race), src/test/supabase/offline-reads.test.ts, src/test/lib/reload-stale-chunk.test.ts (offline), src/test/lib/flowchart.test.ts and src/test/pages/FlowchartPage.test.tsx (load through the route loader), src/test/vercel-deployment-config.test.ts (icons read from public/, service-worker assertions), scripts/test-service-worker-offline.mjs. Each new assertion was checked to fail without its fix.

verify-debatelab recipes against the full local stack (Vite dev server, local Supabase Auth and Postgres, Edge Functions, fake model provider), with the shared stack lock taken by verify.sh up and released by down, at 02e6509c:

  • drive all --project desktop-chromium: 56 passed, 2 skipped (7.6 min)
  • drive all --project mobile-chromium: 56 passed, 2 skipped (9.0 min). The skips on both are the two Workspaces autosave recipes, which need up --collab; Workspaces is hidden in production.

This covers real sign-in (demo code, email link, callback), the lazy signed-in layout, the RequireAuth skeleton and every tool page. The recipes run on the dev server, where the service worker is not registered; test:sw covers the worker.

Review rounds

  • Round 1 (fresh-context Claude subagent, claude-opus-5-5, read the full diff): no High findings; 2 Medium, 5 Low.
    • M1, an offline read held until online kept the previous account's token and could replay after sign-out: fixed by not holding reads at all.
    • M2, a browser that wrongly reports offline would never send held reads, and the onlineManager seeding would pause queries forever: fixed the same way, and the seeding is removed.
    • L3, no timeout on the first render's language wait: now capped at 3 s.
    • L4, the build id ignored theme-init.js and the icons, and an offline public page could get the install-time copy over a newer visit: both fixed.
    • L5, / opened offline could turn into the error page: its chunk is now precached, and test:sw asserts the precache contents.
    • L6, the /auth/callback cache check could not fail: test:sw now visits the callback first.
    • L7, a language chosen offline was never retried: it is now applied on online. The related flowchart memo is covered under L-A below.
  • Round 2 (fresh context, claude-opus-5-5): no High or Medium findings, and all seven round-1 findings confirmed resolved. Three Lows:
    • L-B, after a client-side sign-in the layout and Home loaded one after the other: fixed, both now start together.
    • L-C, the offline no-retry rule missed reads through the dev:preview staging proxy: fixed.
    • hero-visual.jpg and placeholder.svg were left out of the worker's build id: fixed.
    • L-A, left open: starterTexts in src/lib/flowchart.ts (T9's file) is memoised once per session. If a catalog fails to load before the first flowchart render (offline before the worker installed), a chart saved in that language keeps its original starter text until a reload. The fix is in T9's file: build the memo only once every catalog is present.

Needs a manual check in a real browser

  • On a real phone over mobile data: cold-load /app signed in, and confirm LCP improved on the deployed build (the local harness can't reproduce Supabase latency).
  • Install the PWA, visit /app once, go offline (airplane mode), and open Library, History, Timer and Settings from the home screen icon.
  • Safari/WebKit: the service-worker path was only exercised in Chromium.

Migrations: none. Edge Functions to deploy: none.

🤖 Generated with Claude Code

Programcode and others added 10 commits October 23, 2026 04:43
C3 (#199) added icon-1024.png and the 192/512/maskable manifest icons,
which still fell back to revalidating on every request. The test now
reads public/ so a new icon size can't miss the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Spanish and Hungarian strings are their own chunks, fetched when chosen;
  English stays in the entry as the default and fallback. The first render
  waits for the chosen language, and a later choice wins over one still
  loading.
- AppLayout (header, navigation, debate dialogs) loads only under /app.
  mountApp starts the page chunk and the layout while the entry starts,
  instead of when React first reaches them.
- The landing reveal is a CSS transition, so / no longer loads
  framer-motion's 118 KB chunk.
- The session check and layout load show the route's skeleton instead of a
  full-screen spinner.
- check-build-boundaries fails if the es/hu catalogs or AppLayout re-enter
  the entry or the landing, login, home, generate or attack graphs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The build writes a precache list into sw.js from the Vite manifest: the
shell's scripts and styles, the signed-in layout, Home, Library, History,
Timer and Settings, the es/hu strings and Inter's Latin fonts. A missing
manifest key fails the build.

Offline, app routes get the precached app-shell.html (the paths
vercel.json rewrites) and a public page gets only its own HTML, stored
under its own path. Before, every navigation was stored as /index.html,
so any route could come back as the last page visited. /auth/callback,
Supabase paths and /_vercel/ stay network-only. Caches carry the build's
id and the next build's worker deletes them.

vite preview applies the vercel.json rewrites, so the offline test loads
what production serves. The test's async waitForFunction calls passed at
once without checking (Playwright treats the returned Promise as truthy);
they now poll.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offline, supabase-js retried every failed read three more times, so
opening Settings sent about 70 doomed requests in 30 seconds (user_roles
four times, user_settings 18). REST, Functions and Storage reads now wait
for the 'online' event and go out once; an abort still ends them. Writes
fail at once as before, and Auth is left alone because auth-js holds its
session lock during a user lookup.

TanStack Query also starts with the browser's real online state, so a page
opened offline pauses its queries instead of running them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Choosing a language whose strings were never cached, offline, failed its
import and triggered the stale-deploy reload, which without a service
worker replaces the page with the browser's offline error. Offline the
chunk is unreachable, not stale, so the error stands and the app keeps
the current language.

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

/expocit/sources is prerendered and Vercel serves that file before the
rewrite, so offline it now gets its own stored HTML instead of the shell
(whose page chunk is not precached). vite preview serves prerendered
pages before the rewrites too. The non-prerendered first render now also
runs if waiting for the language rejects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Checkout needs the network. Offline the button stays disabled with a short
notice under it (one new string, pricingModal.offlineUpgrade, in all three
catalogs) and re-enables on the 'online' event. Done here now that T4
(#207) has merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
T9 (#206) recognises flowchart starter text saved in any language by
comparing it with each catalog. With Spanish and Hungarian loaded only on
demand, a chart saved in Hungarian and opened in English kept its
Hungarian starter text. The flowchart route's loader now fetches every
catalog before the page renders. Only that page (and a hover prefetch of
it) pays for them, and they are precached for offline.

The flowchart tests now load through the route loader; without this
change four of them fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Offline Supabase reads are no longer held until 'online'. A held read kept
  the token of whoever started it and could replay after sign-out, and a
  browser that wrongly reports offline would never send it. Every request
  is now sent; one that fails while the browser is offline ends as an
  abort, which postgrest-js does not retry. TanStack Query is no longer
  seeded from navigator.onLine; failed queries refetch on 'online' instead.
- A language chosen offline before its strings were cached is applied when
  the connection returns.
- The first render waits at most 3 s for the language's strings.
- The service worker's build id also covers the unhashed public files it
  serves cache-first (theme-init.js, icons), and an offline public page
  prefers the copy saved on the last visit over the one stored at install.
- The landing page's code is precached with its HTML, so / opened offline
  is not replaced by the error page.
- test:sw now visits /auth/callback before checking it is never cached,
  and checks the precached files sit in the install-time shell cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- After a client-side navigation into /app (sign-in, the callback), the
  page chunk now starts with the layout chunk instead of after it.
- The offline no-retry rule also matches reads through the dev:preview
  staging proxy path.
- hero-visual.jpg and placeholder.svg also feed the worker's build id.

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

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 23, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

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

Next included review available in 24 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 87603cd and 6659cb6.

📒 Files selected for processing: 28
  • docs/architecture.md
  • docs/operations.md
  • docs/testing.md
  • public/sw.js
  • scripts/check-build-boundaries.mjs
  • scripts/test-service-worker-offline.mjs
  • src/App.tsx
  • src/components/auth/RequireAuth.tsx
  • src/components/features/subscription/PricingModal.tsx
  • src/components/landing/Reveal.tsx
  • src/i18n.ts
  • src/index.css
  • src/lib/reload-stale-chunk.ts
  • src/lib/route-modules.ts
  • src/lib/supabase/client.ts
  • src/locales/en.json
  • src/locales/es.json
  • src/locales/hu.json
  • src/main.tsx
  • src/test/a11y/names-and-headings.test.tsx
  • src/test/i18n.test.ts
  • src/test/lib/flowchart.test.ts
  • src/test/lib/reload-stale-chunk.test.ts
  • src/test/pages/FlowchartPage.test.tsx
  • src/test/supabase/offline-reads.test.ts
  • src/test/vercel-deployment-config.test.ts
  • vercel.json
  • vite.config.ts
  • 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
Programcode merged commit 504ea07 into main Oct 23, 2026
3 of 11 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