diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c835f4b --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +docs/performance/**/*.json linguist-generated=true diff --git a/.gitignore b/.gitignore index 028f697..520b407 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,9 @@ agent-audit-report.json /scripts/* !/scripts/revoke-legacy-health-tokens.mjs !/scripts/benchmark-performance.ts +!/scripts/benchmark-browser.cjs +!/scripts/verify-duel-performance.cjs +!/scripts/verify-entry-performance.cjs # scratch & local cache /.scratch/ supabase/.temp/ diff --git a/docs/performance.md b/docs/performance.md index 645f6a1..4266a32 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -1,5 +1,45 @@ # App loading and interaction performance +## 2026-10-12 follow-up + +Compared production builds against `e7b284e` with Chrome 148 on the same machine. The local fixture has 730 prior days across 20 seeded Habits: 14,600 Check-ins, about 2.3 MB. These results measure local client work. The mobile scenario uses a 390×844 viewport and 4× CPU throttling; it does not represent a physical phone or hosted Supabase latency. + +The first open of a deferred view waited about 300 ms after its JavaScript was available. The installed React DOM production runtime throttles Suspense fallback replacement. Deferred views now use a guarded import resource and stable external-store snapshots, keeping the deployment check before activation. Tab and Settings imports also warm on pointer entry or keyboard focus. The initial page still loads its existing frame and Today view. + +The same loader also removes the remaining delay from default account entry and first-run onboarding. With a browser-local heading probe after document navigation, account entry falls from 379.7 to 80.6 ms and onboarding from 496.9 to 197.1 ms (five samples per flow, mock requests at 50 ms). Signup/sign-in switching and saving a first habit plan pass. The original locator-based timing was superseded because retry polling added a fixed delay to the observation. + +First-open medians, five samples per view, document reload before every sample, HTTP cache retained, and two animation frames after content becomes ready: + +| View | Before | After | +| --- | ---: | ---: | +| Progress | 331.3 ms | 53.6 ms | +| Duel | 331.3 ms | 44.8 ms | +| Settings | 334.8 ms | 41.3 ms | +| Activity | 333.4 ms | 40.2 ms | +| Attach Proof | 350.2 ms | 39.1 ms | +| Shortcuts | 352.9 ms | 48.3 ms | + +Repeated interactions alternate build order, with 20 Check-in/undo pairs per build and scenario. Timing includes the real state update, one observed synchronous localStorage write, and two animation frames. The saved state is parsed after timing: + +| Scenario | Action | Before | After | +| --- | --- | ---: | ---: | +| Desktop | Weekly Check-in | 48.6 ms | 41.4 ms | +| Desktop | Undo | 53.3 ms | 45.4 ms | +| Mobile viewport, 4× CPU | Weekly Check-in | 196.3 ms | 172.0 ms | +| Mobile viewport, 4× CPU | Undo | 149.8 ms | 123.1 ms | + +Today streaks reuse the existing Check-in index, selected-day sums stay memoized, and badges are computed when the Trophy Cabinet mounts. Weekly Duel totals aggregate history once. Activity parses each timestamp once before sorting and skips search-string construction for an empty query, while retaining full-history search, counts, stable ties, and backdated records. Local persistence remains synchronous. + +Production copies configured for a localhost Supabase mock verify the actual client, store effect, Phoenix subscriptions, and paginated reads. With 2,500 Check-ins, a burst of 20 realtime events drops from 140 requests to 7; exact-count requests drop from 140 to 5. Events during an active read produce one serialized follow-up. A 400-row server cap still returns all 2,500 records, with 11 requests and 5 exact counts per reload. These request counts describe the mock, not hosted latency. When a total is missing or nonnumeric, pagination continues through the empty page so a short server-capped response cannot truncate history. + +That verification reproduced a read/write race on the original build: a read started before a local Check-in briefly replaced its optimistic state. The store now rejects reads when the pending-write queue changes during loading and requests a fresh read. The same browser reproduction keeps the Habit checked throughout. Initial loading, Check-in/undo writes, Activity counts, and isolation from local history also pass in the mock. + +`npm run check` passes 165 tests, typecheck, and lint; `npm run build` passes. Browser checks cover photo upload and undo, proof gallery, historical calendar navigation and Today reset, shortcut focus trapping and cleanup, light/dark themes, offline Check-in/undo and warmed views, and persisted history after reload. The measured desktop and mobile viewports have no horizontal overflow. Deployment mismatch, recovery reload limits, unavailable version endpoints, offline activation, duplicate records, rest days, and streak caps have focused tests. + +Reproduction commands and raw measurements are in [the October 12 evidence directory](performance/2026-10-12/README.md). Run `npx tsx scripts/benchmark-performance.ts` for calculation medians, including shared-index construction and all active Today streaks. The calculation benchmark is separate from the browser timings above. + +## 2026-10-10 pass + Measured against main at `779dd85` on 2026-10-10, using production builds, Node 20.20.2 and Chrome on the same machine. The fixture contains 730 days for all 20 seeded habits, or 14,600 check-ins. These local measurements isolate client work; they do not measure authenticated Supabase latency or mobile hardware. Run the calculation benchmark with `npx tsx scripts/benchmark-performance.ts`. Each result is the median of five runs after one warm-up. Before the change, the weekly check-in diff used the store's `find` for each rebalanced record. The new benchmark uses the store's positional comparison, since rebalancing preserves array order. diff --git a/docs/performance/2026-10-12/README.md b/docs/performance/2026-10-12/README.md new file mode 100644 index 0000000..7194597 --- /dev/null +++ b/docs/performance/2026-10-12/README.md @@ -0,0 +1,79 @@ +# October 12 performance evidence + +Baseline application source is `e7b284e`. Measurements use production builds and synthetic history in isolated browser contexts. The current source includes guarded deferred views, indexed streaks, coalesced Duel reloads, and rejection of reads that overlap local writes. + +## Reproduce local browser measurements + +Serve the baseline and current production builds on separate localhost ports, with Supabase environment variables unset. Keep the current checkout on its branch. For the baseline, extract a source snapshot with `git archive e7b284e`; run `npm ci`, `npm run build`, then `npm run start -- --port 43127` in that snapshot. Run the current build on port 43130. The scripts accept different URLs with `--baseline` and `--current`. + +Install browser tooling outside the app's dependencies: + +```sh +npm install --prefix .scratch/browser-tools playwright +.scratch/browser-tools/node_modules/.bin/playwright install chromium +``` + +Run from the repository root: + +```sh +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/benchmark-browser.cjs --mode first-open --samples 5 --output .scratch/first-open.json +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/benchmark-browser.cjs --mode interactions --samples 20 --output .scratch/interactions.json +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/benchmark-browser.cjs --mode interactions --mobile --cpu 4 --samples 20 --output .scratch/interactions-mobile.json +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/benchmark-browser.cjs --mode offline --output .scratch/offline.json +``` + +Use `--chrome /path/to/chrome` to select an installed browser. This run used `/usr/bin/google-chrome`, version 148.0.7778.178. Timing ends after actual content or persistence and two animation frames. First-open samples reload the document but retain the HTTP cache. Interaction samples alternate build order and parse the saved state after timing. The mobile option changes the viewport; CPU throttling is synthetic. + +## Reproduce Duel reload and write-race checks + +Use separate baseline/current production snapshots configured for the mock below. They must not share the anonymous builds used above, because public environment variables are embedded during build: + +```sh +NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321 NEXT_PUBLIC_SUPABASE_ANON_KEY=mock-publishable-key npm run build +NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321 NEXT_PUBLIC_SUPABASE_ANON_KEY=mock-publishable-key npm run start -- --port 43131 +``` + +Serve the corresponding current snapshot on port 43132. No Supabase server is required: Playwright handles the localhost HTTP responses and Phoenix WebSocket protocol. The fixture contains 2,500 Check-ins; responses wait 50 ms. `npm test` in the repository root creates the compiled seed module used by the harness. + +```sh +npm test +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/verify-duel-performance.cjs --page-cap 1000 --output .scratch/duel-reload.json +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/verify-duel-performance.cjs --page-cap 400 --output .scratch/duel-cap400.json +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/verify-duel-performance.cjs --race --output .scratch/duel-race.json +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/verify-duel-performance.cjs --loading-only --history-days 730 --samples 5 --page-cap 400 --output .scratch/duel-loading-cap400.json +NODE_PATH="$PWD/.scratch/browser-tools/node_modules" node scripts/verify-entry-performance.cjs --smoke --output .scratch/entry-performance.json +``` + +The reload checks assert full history, counts, filtered queries, twenty-event bursts, one follow-up for events during loading, actual Check-in/undo writes, and unchanged local history. The race check observes the Habit button while a read overlaps a local write: the baseline becomes unchecked temporarily; the current build remains checked. These tests verify client behavior and request amplification against a mock. They do not establish hosted database latency or server-side authorization. + +Known-count history now loads in batches of at most three requests. Responses retain their requested order; a changed page size discards later speculative responses and resumes serial reads from the contiguous prefix. Unknown totals remain serial through an empty page. The loading-only scenario uses 14,600 records and five alternating samples in fresh browser contexts. Its browser-local probe ends when Daily Ritual and the Check-in control appear, followed by two animation frames; Activity then verifies the complete record count. This includes localhost JavaScript loading and rendering with 50 ms mock responses. + +The entry check uses empty auth for the default account screen, then a synthetic account with an empty Duel for first-run onboarding. It measures the heading with a browser-local probe and two animation frames after document navigation. `--smoke` also switches signup/sign-in modes and saves a new habit plan through the mock. No real account is created. + +## Recorded artifacts + +- [First opens](first-open.json): five samples per view and build from `benchmark-browser.cjs`. +- [First opens with a mobile viewport and 4× CPU](first-open-mobile.json): five samples per view and build under synthetic throttling. +- [Repeated interactions](interactions.json): twenty alternating Check-in/undo pairs per build, desktop and synthetic mobile scenarios. +- [Offline](offline.json): local mutation/undo, warmed views, and persisted history after reconnect/reload. +- [Duel reloads](duel-reload.json): pagination and request counts with a 1,000-row page cap. +- [Duel reloads with a 400-row cap](duel-cap400.json): no history truncation with a lower server limit. +- [Read/write race](duel-race.json): the same UI reproduction on baseline and current source. +- [Account entry and onboarding](entry-performance.json): five navigation samples per flow and build, plus mode switching and a saved first plan. +- [Large authenticated history loading](duel-loading-cap400.json): five samples per build, full 14,600-record history, and a 400-row server cap. + +Final source `69d22fe` passes 170 tests, type checking, lint, and anonymous/configured production builds. The final loading repeat measures medians of 2,365.4 ms before and 1,018.5 ms after. Final Duel fixtures also retain 2,500 records under both caps, reduce normal bursts from 140 to 7 requests and capped bursts from 220 to 11, and keep optimistic Check-ins checked during an overlapping read. + +## Independent review and remaining work + +[Standards](standards-review.md) and [Spec](spec-review.md) independently review source `69d22fe`. The [final trail review](trail-review.md) extends the [earlier trail review](trail-review-before-paging.md). Reports are retained with their original findings; the parent responses below explain the disposition. + +No hard standards or demonstrated correctness failures were reported. Optional code cleanup remains deferred. Two independent deferred wrappers share the JavaScript module cache; moving a wrapper into the imported modal would need care to preserve its lazy boundary. Pointer/focus warming directly supports the authorized loading goal and retains the deployment guard. The removed Apple Health poll always received HTTP 410 at baseline and exited before changing state; functioning wearable paths remain intact. + +Hosted database/RLS and physical mobile testing remain unavailable. Mobile measurements use emulation. In-flight read cancellation and further calculation proposals remain follow-up work; their suggested savings are unimplemented estimates. Existing offset pagination does not promise a consistent database snapshot during concurrent remote inserts or deletions. + +The final trail reviewer checked the refreshed browser artifact but attributed its newer medians to historical row 53. That row records the earlier 2,385.8→1,034.4 ms sample; row 55 records the final 2,365.4→1,018.5 ms repeat. The append-only trail preserves both measurements. Documentation packaging and final handoff actions follow the source review boundary. + +Earlier instrumentation that polled and parsed the entire stored state inside the timed interaction was superseded. The accepted interaction artifact observes one real storage write and parses its value after timing. The mock also exposes `Content-Range` to the browser and waits for quiescence across pagination gaps. These corrections are recorded in the decision trail. + +The [decision trail](decisions.tsv) records accepted and abandoned work. Earlier diagnosis logs and quota-interrupted child reports remain local under `.scratch/performance-overnight`; the accepted measurements above and their reusable harnesses are committed here. diff --git a/docs/performance/2026-10-12/decisions.tsv b/docs/performance/2026-10-12/decisions.tsv new file mode 100644 index 0000000..a3a6aa3 --- /dev/null +++ b/docs/performance/2026-10-12/decisions.tsv @@ -0,0 +1,60 @@ +ts phase decision why evidence result +2026-10-12T00:07:36Z frame Set the run deadline to 07:00 Europe/London and verified the stop timer User requested work until 7am and the workspace timezone is Europe/London .scratch/performance-overnight/state.json stop scheduled for 2026-10-12T06:00:00Z +2026-10-12T00:08:00Z baseline Baseline checks and production build passed Need a clean baseline before accepting optimizations .scratch/performance-overnight/baseline-check.log; .scratch/performance-overnight/baseline-build.log 134 tests passed; typecheck and lint passed; build passed +2026-10-12T00:10:01Z baseline Measured production Check-ins and navigation with 14600 historical Check-ins Use the real browser to isolate remaining interaction costs .scratch/performance-overnight/baseline-browser.json weekly Check-in median 81.3 ms; undo median 60.1 ms; Progress warm median 32.7 ms +2026-10-12T00:12:17Z baseline Preserved the baseline source and production build in a detached worktree Rebuild main without losing the original browser comparison target .scratch/performance-overnight/baseline-app; .scratch/performance-overnight/baseline-isolated-server.log baseline available on port 43127; main stays on main +2026-10-12T00:12:52Z orchestration Delegated three separate read-only audits to Gemini Pro Find measured bottlenecks before assigning implementation ownership .scratch/performance-overnight/state.json calculations, store/sync and UI/loading audits running +2026-10-12T00:14:30Z scope Restricted calculation implementation to indexed streaks, exact badges and one-pass Duel points Audit prototypes and unverified claims need real differential checks before accepting gains .scratch/performance-overnight/calculations-r1.md; .scratch/performance-overnight/ui-r1.md calculation implementation and evidence review delegated; no CSS or animation changes approved +2026-10-12T00:16:12Z scope Kept synchronous persistence and narrowed store work to reload coalescing and dead polling Deferred writes change durability; count removal can miss server-capped pages; existing health issue needs coordination .scratch/performance-overnight/store-r1.md; .scratch/performance-overnight/notes.md store-r2 assigned; risky suggestions excluded from this slice +2026-10-12T00:18:38Z review Accepted Gemini evidence review flags and narrowed UI work to exact Activity ordering and intent preloads Audits contained incorrect fetch claims and unsafe truncation; real cold navigation and caller integration remain to verify .scratch/performance-overnight/trail-review-r1.md; .scratch/performance-overnight/notes.md unsafe proposals excluded; three implementation tasks active; gains not yet accepted +2026-10-12T00:24:40Z baseline Captured five first-view navigation samples per view after document reload Earlier cold probes were excluded; needed valid repeatable baseline for loading work .scratch/performance-overnight/baseline-first-navigation.json Progress333.2ms; Duel334.7ms; Settings334.1ms; HTTP cache retained +2026-10-12T00:24:40Z integration Assigned portable calculation tests and live indexed-streak/lazy-badge integration Scratch baseline imports break clean CI and private indexes need actual caller integration .scratch/performance-overnight/calculations-r2.md; src/lib/calculations-r2.test.ts; .scratch/performance-overnight/notes.md calculation cleanup and store/Today integration running; R2 gains not yet accepted +2026-10-12T00:33:07Z repair Continued parent work after Gemini quota interrupted three tasks and an alternate-model attempt Partial files failed checks; provider terminal status is not implementation success .scratch/performance-overnight/round3-check.log; .scratch/performance-overnight/round3-build.log; .scratch/performance-overnight/state.json repaired syntax and portable tests; retries held until04:20UTC; repair checks running +2026-10-12T00:39:00Z verification Repaired partial Gemini edits and integrated indexed streaks plus lazy badge calculations Provider quota ended tasks before valid output; hot-path benefits require actual caller integration .scratch/performance-overnight/integration-check.log; .scratch/performance-overnight/integration-build.log; .scratch/performance-overnight/repair-differential.log; .scratch/performance-overnight/integration-browser.json 152 tests and build pass; local Check-in median67.5ms; broader browser verification pending +2026-10-12T00:47:17Z loading Replaced lazy tab rendering with a subscribed resolved-component snapshot Production first opens waited300ms despite cached chunks; keep guarded imports and render as soon as ready .scratch/performance-overnight/deferred-first-navigation.json; .scratch/performance-overnight/deferred-check.log; .scratch/performance-overnight/deferred-build.log 157 tests/build pass; first opens Progress33.3ms Duel39.8ms Settings30.4ms; broader flows pending +2026-10-12T01:25:11Z deferred-views Use the guarded external-store loader for Activity, proof dialogs, and shortcuts The measured first opens also incurred the same React fallback delay src/components/DeferredView.tsx; .scratch/performance-overnight/deferred-extra-first-open.json passed +2026-10-12T01:25:11Z intent-preload Warm tab and Settings imports on pointer entry or keyboard focus Intent can overlap chunk loading while the actual open still runs the deployment guard src/components/Header.tsx; src/lib/deferred-resource.test.ts; src/lib/load-current-page.test.ts 160 tests and production build passed +2026-10-12T01:25:11Z browser-fallback Use isolated headless Chrome after the preview host disconnected and alternate GUI inventory was empty The explicit unavailable error prohibited preview retries and browser verification still needed to finish .scratch/performance-overnight/headless-verification.cjs; .scratch/performance-overnight/headless-verification.json 60 comparative first-open samples and smoke passed +2026-10-12T01:25:11Z verification Correct the harness dialog locator to the actual accessible name Shortcuts The initial smoke timeout used Keyboard shortcuts while the app exposes Shortcuts .scratch/performance-overnight/headless-verification-initial.json; .scratch/performance-overnight/headless-smoke.log corrected smoke passed +2026-10-12T01:25:11Z visual-check Inspect full-page light and dark screenshots after interaction smoke Confirm the actual production UI remains readable and intact .scratch/performance-overnight/headless-dark.png; .scratch/performance-overnight/headless-light.png inspected; no new visual regression found +2026-10-12T01:28:12Z realtime Reduce the reloader API and require its error handler Only schedule, cancellation, and cancelled status are needed by the store; test-only idle and running APIs added complexity src/lib/coalesced-reload.ts; src/lib/coalesced-reload.test.ts; .scratch/performance-overnight/reloader-check.log 162 tests, lint, typecheck, and production build passed +2026-10-12T01:38:59Z authenticated-reload Verify the real production Duel subscription and mutation paths with a localhost mock Unit tests alone did not cover the store effect, client subscription, and paginated query wiring .scratch/performance-overnight/authenticated-reload.cjs; .scratch/performance-overnight/authenticated-reload.json; .scratch/performance-overnight/authenticated-reload-r5.log baseline and current passed; twenty-event burst now one reload instead of twenty; history and writes preserved +2026-10-12T01:38:59Z verification Correct mock request and accessibility assumptions before accepting Duel results POST carries duel_id in the row; Activity is a tab; the timeline heading includes its count .scratch/performance-overnight/authenticated-reload.log; .scratch/performance-overnight/authenticated-reload-r2.log; .scratch/performance-overnight/authenticated-reload-r4.log; .scratch/performance-overnight/authenticated-reload-r5.log harness errors fixed; production application source unchanged +2026-10-12T01:39:00Z measurement Replace repeated JSON parsing while waiting for persistence with a one-write observer Polling the 2.3MB state added artificial work inside the timed interaction .scratch/performance-overnight/interleaved-interactions-storage-polling.json; .scratch/performance-overnight/interleaved-interactions.cjs old timing retained but superseded; corrected comparison running +2026-10-12T01:43:00Z authenticated-counts Expose Content-Range in the cross-origin mock and assert exact-count elimination The first mock hid that response header from the browser, forcing the existing unknown-count fallback .scratch/performance-overnight/authenticated-reload-hidden-count-header.json; .scratch/performance-overnight/authenticated-reload.json; .scratch/performance-overnight/authenticated-reload-r6.log corrected mock passed; each current reload requests five exact counts instead of seven +2026-10-12T01:43:00Z interaction-measurement Accept the alternating comparison after removing state-polling overhead Twenty measured check-in and undo pairs per build and scenario reduce order bias and retain real persistence in the timing .scratch/performance-overnight/interleaved-interactions.json; .scratch/performance-overnight/interleaved-interactions-r2.log desktop medians 48.55 to 41.4ms and 53.25 to 45.35ms; 4x CPU medians 196.25 to 172ms and 149.85 to 123.1ms; no errors or viewport overflow +2026-10-12T01:44:24Z authenticated-cap Wait for quiescence across all pages before beginning the next scenario The 400-row cap created gaps between requests, so an active-request count of zero did not mean a reload had finished .scratch/performance-overnight/authenticated-reload-cap400.log; .scratch/performance-overnight/authenticated-reload.cjs corrected harness running; possible existing read/write race queued for a separate reproduction +2026-10-12T01:47:53Z authenticated-race Reject reads that overlap a queued local write and request one fresh reload The production mock reproduced checked, unchecked, then checked again on both builds when a read overlapped a check-in .scratch/performance-overnight/authenticated-race-before.json; src/lib/store.tsx fix implemented; before baseline flicker 69ms and serialized current flicker 170ms; verification pending +2026-10-12T01:53:18Z race-verification Verify the write barrier against the same real production reproduction The checked state must remain visible while a fresh read catches up with the queued write .scratch/performance-overnight/authenticated-race-after.json; .scratch/performance-overnight/authenticated-race-after.log; .scratch/performance-overnight/race-check.log; .scratch/performance-overnight/race-build.log baseline still flickers; current stays checked; 162 tests and production build passed +2026-10-12T01:53:18Z pagination Verify the actual browser query path with a 400-row server cap The count optimization must retain history when a backend returns fewer rows than requested .scratch/performance-overnight/authenticated-reload-cap400.json; .scratch/performance-overnight/authenticated-reload-cap400-r2.log both retain 2500 records; current burst uses eleven requests and five exact counts instead of 220 requests and counts +2026-10-12T01:58:45Z offline-verification Verify offline mutation, undo, warmed tabs, and persisted history on both production builds The deferred loader and calculation changes must retain local offline use scripts/benchmark-browser.cjs; .scratch/performance-overnight/offline-browser.json; .scratch/performance-overnight/offline-browser.log passed; both retain 14600 rows after reconnect and reload +2026-10-12T02:08:46Z index-reuse Use the existing first-record Habit/date index for partner proof and Check-in lookup Both Array.find calls repeat a history scan whose exact duplicate-first behavior is already indexed src/lib/store.tsx; src/lib/check-in-index.ts; src/lib/check-in-index.test.ts implemented without another index or changed selection semantics; full packaging checks pending +2026-10-12T02:21:30Z pagination-test Exercise pagination through the actual Supabase HTTP client without a client type cast The prior mock reimplemented the method chain and could miss wire-level count behavior src/lib/duel-sync.test.ts; .scratch/performance-overnight/real-client-pagination.log Node20 lacks native WebSocket; injected a transport that fails if HTTP-only pagination tries to connect; focused checks running +2026-10-12T02:25:16Z count-boundary Validate finite counts and end unknown-count pagination only on an empty page The real SDK returns NaN for wildcard totals; short pages may be server caps rather than the end of history src/lib/duel-sync.ts; src/lib/duel-sync.test.ts; .scratch/performance-overnight/real-client-pagination-r2.log implemented; unknown-count and missing-header capped-history tests added; focused verification pending +2026-10-12T02:25:16Z test-instrument Use a nonretryable mock page error for the propagation assertion The SDK retries 503 responses, so counting exactly two calls was a false harness assumption .scratch/performance-overnight/real-client-pagination-r2.log; src/lib/duel-sync.test.ts mock changed to400; production retry behavior untouched +2026-10-12T02:35:09Z evidence-packaging Move the canonical trail into the committed evidence directory and retain a local symlink This unattended change needs a reviewable trail plus reusable scripts and accepted raw measurements docs/performance/2026-10-12/decisions.tsv; docs/performance/2026-10-12/README.md; scripts/benchmark-browser.cjs; scripts/verify-duel-performance.cjs one canonical log; accepted evidence copied; early diagnosis artifacts remain local +2026-10-12T02:42:27Z packaging Commit the sync, view-loading, unknown-count, and history work as separate changes on main Separate commits let reviewers assess and revert each behavior independently fbc0c76; f8d7837; 0b5db4b; 3862085; .scratch/performance-overnight/packaged-check.log; .scratch/performance-overnight/packaged-build.log 165 tests, lint, typecheck, and production build passed; fresh anonymous and mock servers started +2026-10-12T02:59:55Z pull-request Open and immediately register draft PR64 while keeping the checkout on main CI and review need a concrete diff during the remaining overnight work https://github.com/Maseeek/do-it/pull/64; f2dab27 linked successfully; Gemini final review pending quota reset +2026-10-12T03:13:41Z preview-access Use the protected-preview access workflow and try the mapped CLI fallback The preview redirects to Vercel SSO; the connected token cannot read the project team .scratch/performance-overnight/vercel-whoami.json; .scratch/performance-overnight/vercel-whoami.log; https://github.com/Maseeek/do-it/pull/64 MCP403 for maseeeks-projects; CLI has no credentials; no protection settings changed; continue local verification +2026-10-12T03:20:56Z entry-measurement Probe entry-screen readiness inside the browser instead of timing locator retries Locator waits quantized both different flows around864ms; the actual heading needs a local readiness probe .scratch/performance-overnight/entry-gates-before-locator-polling.json; .scratch/performance-overnight/entry-gates.cjs corrected method running; default entry is account creation, not sign-in; no accounts created +2026-10-12T03:24:55Z entry-loader Use guarded deferred views for account entry and first-run onboarding Corrected production probes show380ms and497ms on the remaining lazy gates; SSR response contains only the existing CSR fallback .scratch/performance-overnight/entry-gates-before.json; .scratch/performance-overnight/auth-ssr.html; src/app/page.tsx implemented; same components and props; build and verification running +2026-10-12T03:24:55Z audit Preserve the Activity habit-ID mapping after checking the baseline The baseline already uses a last-value Map, so changing it to first-match would change established behavior .scratch/performance-overnight/baseline-app/src/components/ActivityFeed.tsx:96; src/lib/activity-timeline.ts:76 no source change +2026-10-12T03:40:03Z entry-verification Verify faster account entry and onboarding plus their loaded interactions The same guarded loader should remove the measured delay without changing account controls or plan saving docs/performance/2026-10-12/entry-performance.json; scripts/verify-entry-performance.cjs; .scratch/performance-overnight/entry-check.log; .scratch/performance-overnight/entry-build.log account379.7 to80.6ms; onboarding496.9 to197.1ms; mode switch and plan save passed;165 tests and build passed +2026-10-12T03:43:32Z ci-review Use the REST update path after the installed gh PR editor hits the classic-Projects deprecation The PR body needs the entry measurements and current preview-access limitation https://github.com/Maseeek/do-it/pull/64; .scratch/performance-overnight/pr-edit.json REST update submitted; latest implementation remains a draft awaiting final Gemini review +2026-10-12T03:53:40Z pagination-prototype Measure three bounded remaining-page reads after a known first-page count Serial authenticated history pages are a remaining loading bottleneck .scratch/performance-overnight/pagination-prototype.cjs; .scratch/performance-overnight/pagination-prototype.json stationary datasets preserve14600 ordered rows; prototype only; changing caps, cancellation, errors, and writes still require proof +2026-10-12T03:59:47Z trail-review-context Capture this T3 run with an indexed full timeline and compact conversation transcript The final cross-model audit needs actual decisions and tool receipts; recursive transcript-read events add no evidence .scratch/performance-overnight/parent-transcript-snapshot.json; .scratch/performance-overnight/parent-conversation.md; .scratch/performance-overnight/parent-timeline-index.tsv 1171 pre-collection activities captured without truncation; transcript files remain local +2026-10-12T04:07:59Z prototype-rejection Reject the simple parallel prototype when the server cap shrinks between pages A known total does not make fixed offsets safe after an unexpected short response .scratch/performance-overnight/pagination-prototype.json; .scratch/performance-overnight/pagination-prototype-r2.log serial retained1250 rows; naive parallel returned850; production code unchanged +2026-10-12T04:12:46Z prototype-fallback Test a contiguous-prefix fallback after an unexpected short speculative page The naive parallel version lost400 rows when the cap shrank; later speculative pages must not skip gaps .scratch/performance-overnight/pagination-prototype.cjs; .scratch/performance-overnight/pagination-prototype-r3.log throwaway fallback retained1250 unique rows; errors, unknown totals, cancellation, and app integration remain unproven +2026-10-12T04:15:18Z review-setup Pin the two-axis review to the recorded pre-run baseline and current400fbfe Standards and spec need independent checks against a stable diff while later work continues .scratch/performance-overnight/review-round4-plan.md; .scratch/performance-overnight/gemini-round4-brief.md; https://github.com/Maseeek/do-it/issues/63 review setup ready; waiting for Gemini quota reset04:20UTC +2026-10-12T04:18:58Z trail-audit Supersede the removed calculations-r2 test pointer in the00:24:40 row That quota-era test imported ignored baseline files; it was moved to scratch and portable expected-value tests replaced it .scratch/performance-overnight/calculations-r2-differential.test.ts; .scratch/performance-overnight/repair-differential.log; src/lib/calculation-regressions.test.ts 47-row local evidence audit found one removed historical pointer; replacement artifacts resolve; history retained +2026-10-12T04:23:40Z gemini-resume Resume two pinned reviews and delegate bounded paging implementation after quota reset Actual Gemini read/command activity confirms available service; the prototype needs cap/error/order proof before app use .scratch/performance-overnight/round4-tasks.json; .scratch/performance-overnight/pagination-r4-task.json; .scratch/performance-overnight/gemini-round4-brief.md Gemini3.8Flash standards,3.7Flash spec,3.1Pro paging running with separate ownership +2026-10-12T04:29:27Z review Read completed independent Standards and Spec reviews at 400fbfe Separate hard coding rules from optional cleanup and unverified infrastructure .scratch/performance-overnight/standards-r4.md; .scratch/performance-overnight/spec-r4.md; src/app/api/sync/apple-health/route.ts No hard standards violations; serial paging and cancellation remain open; hosted and physical mobile evidence unavailable +2026-10-12T04:32:48Z pagination Build and independently check bounded history paging before browser acceptance Three concurrent requests may reduce a measured serial bottleneck while fallback protects complete history src/lib/duel-sync.ts; src/lib/duel-sync.test.ts; .scratch/performance-overnight/pagination-parent-check.log; .scratch/performance-overnight/pagination-parent-build.log; .scratch/performance-overnight/pagination-auth-build.log 169 tests, typecheck, lint and both production builds pass; browser timing and adversarial review pending +2026-10-12T05:49:48Z paging-review Fix final-slot page growth and share the local range builder Independent Gemini review found two avoidable speculative requests after a cap grows .scratch/performance-overnight/pagination-review-r5.md; src/lib/duel-sync.ts; src/lib/duel-sync.test.ts Added ordered full-history regression; integer count validation; final checks running +2026-10-12T05:49:48Z loading Measure complete authenticated screen loading after bounded pagination Accept app improvement only from a production browser reproduction .scratch/performance-overnight/duel-loading-before-pagination.json; docs/performance/2026-10-12/duel-loading-cap400.json; scripts/verify-duel-performance.cjs Five samples: baseline2385.8ms current1034.4ms; all14600 records retained; final-source rerun pending +2026-10-12T05:49:48Z scope Leave cancellation and additional calculation proposals for later work Remaining deadline allows verification of accepted paging; cancellation needs read/write lifecycle tests .scratch/performance-overnight/cancellation-audit-r4.md; .scratch/performance-overnight/hotpaths-r5.md No cancellation or further calculator change; estimated cancellation savings are hypotheses, not measured app results +2026-10-12T05:53:00Z final-verify Repeat authenticated loading on fresh final-source production builds Verify the actual committed paging changes after review fixes .scratch/performance-overnight/final-check.log; .scratch/performance-overnight/final-build.log; .scratch/performance-overnight/final-auth-build.log; docs/performance/2026-10-12/duel-loading-cap400.json 170 tests/typecheck/lint/builds pass; five samples baseline2365.4ms current1018.5ms, all14600 records retained +2026-10-12T05:54:26Z final-behavior Repeat Duel burst, lower-cap history and optimistic Check-in race on final source Parallel loading must preserve local writes and complete records .scratch/performance-overnight/final-duel-cap1000.json; .scratch/performance-overnight/final-duel-cap400.json; .scratch/performance-overnight/final-duel-race.json All pass with no page errors; burst140to7 normal and220to11 capped;2500records retained; current Check-in never becomes unchecked +2026-10-12T05:54:26Z review Read final independent Standards and Spec reports on69d22fe Preserve review findings and record parent responses without inventing new requirements .scratch/performance-overnight/standards-final-r6.md; .scratch/performance-overnight/spec-final-r6.md; src/app/api/sync/apple-health/route.ts No hard standards or correctness failures; hosted/physical limits and cancellation deferred; hover preload authorized by speed goal; removed poll already410 atbaseline +2026-10-12T05:55:54Z trail-audit Keep final review and correct its historical sample attribution Reviewer followed the refreshed evidence but assigned newer medians to an earlier row docs/performance/2026-10-12/trail-review.md; docs/performance/2026-10-12/decisions.tsv:54; docs/performance/2026-10-12/README.md Row53 retains earlier2385.8to1034.4ms; row55 verifies final2365.4to1018.5ms; source review covers69d22fe; later changes documentation only +2026-10-12T05:59:05Z handoff Freeze verified source and make PR64 ready for human review before the fixed7am stop Final source checks and independent reviews pass; merge waits for human review commit69d22fe; commit3ec5169; https://github.com/Maseeek/do-it/pull/64; docs/performance/2026-10-12/README.md 170 tests/builds/browserchecks pass; CI/Vercel green at3ec5169; all20delegatedtasks terminal; no further app work; deadline cleanup pending diff --git a/docs/performance/2026-10-12/duel-cap400.json b/docs/performance/2026-10-12/duel-cap400.json new file mode 100644 index 0000000..1492d8d --- /dev/null +++ b/docs/performance/2026-10-12/duel-cap400.json @@ -0,0 +1,2780 @@ +{ + "measuredAt": "2026-10-12T05:52:59.158Z", + "pageCap": 400, + "historyCount": 2500, + "samples": 1, + "method": "Production copies configured only for a Playwright-routed localhost Supabase mock. 2500 CheckIns, 50ms per HTTP response, real Phoenix subscriptions/events, no live database or auth. New browser context per sample. Initial rendering measured inside the page from navigation start to Daily Ritual and a Check-in control, followed by two animation frames. Counts verify reload amplification; loading times include localhost JS and rendering, not hosted latency.", + "variants": { + "baseline": { + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + } + ], + "burst": [ + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + } + ], + "during": [ + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 100 + } + ], + "writes": [ + { + "table": "duel_check_ins", + "method": "POST" + }, + { + "table": "duel_check_ins", + "method": "DELETE" + } + ], + "errors": [], + "localHistoryUntouched": true, + "visibleHistory": 2500 + }, + "current": { + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 100 + } + ], + "burst": [ + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 100 + } + ], + "during": [ + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 100 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 100 + } + ], + "writes": [ + { + "table": "duel_check_ins", + "method": "POST" + }, + { + "table": "duel_check_ins", + "method": "DELETE" + } + ], + "errors": [], + "localHistoryUntouched": true, + "visibleHistory": 2500 + } + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/duel-loading-cap400.json b/docs/performance/2026-10-12/duel-loading-cap400.json new file mode 100644 index 0000000..1b90748 --- /dev/null +++ b/docs/performance/2026-10-12/duel-loading-cap400.json @@ -0,0 +1,3434 @@ +{ + "measuredAt": "2026-10-12T05:51:52.524Z", + "pageCap": 400, + "historyCount": 14600, + "samples": 5, + "method": "Production copies configured only for a Playwright-routed localhost Supabase mock. 14600 CheckIns, 50ms per HTTP response, real Phoenix subscriptions/events, no live database or auth. New browser context per sample. Initial rendering measured inside the page from navigation start to Daily Ritual and a Check-in control, followed by two animation frames. Counts verify reload amplification; loading times include localhost JS and rendering, not hosted latency.", + "variants": { + "baseline": [ + { + "initialRitualMs": 2492.5999999940395, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": true, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 1, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + }, + { + "initialRitualMs": 2371.3999999910593, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": true, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 1, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + }, + { + "initialRitualMs": 2365.3999999910593, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": true, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 1, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + }, + { + "initialRitualMs": 2358.4000000059605, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": true, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 1, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + }, + { + "initialRitualMs": 2363.8999999910593, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": true, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 1, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + } + ], + "current": [ + { + "initialRitualMs": 1059.699999988079, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": false, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 3, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + }, + { + "initialRitualMs": 1017.1000000089407, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": false, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 3, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + }, + { + "initialRitualMs": 1018.5, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": false, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 3, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + }, + { + "initialRitualMs": 1017.0999999940395, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": false, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 3, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + }, + { + "initialRitualMs": 1024.8999999910593, + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 3600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 4800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 5600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 6800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 7600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 8800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 9600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 10800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 11600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12400, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 12800, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13200, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 13600, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14000, + "limit": 1000, + "exact": false, + "rows": 400 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 14400, + "limit": 1000, + "exact": false, + "rows": 200 + } + ], + "peakByTable": { + "duel_habits": 1, + "duel_check_ins": 3, + "duel_stakes": 1, + "duel_reactions": 1, + "duel_rest_days": 1 + }, + "visibleHistory": 14600, + "errors": [] + } + ] + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/duel-race.json b/docs/performance/2026-10-12/duel-race.json new file mode 100644 index 0000000..b34edfd --- /dev/null +++ b/docs/performance/2026-10-12/duel-race.json @@ -0,0 +1,266 @@ +{ + "measuredAt": "2026-10-12T05:52:59.164Z", + "pageCap": 1000, + "historyCount": 2500, + "samples": 1, + "method": "Production copies configured only for a Playwright-routed localhost Supabase mock. 2500 CheckIns, 50ms per HTTP response, real Phoenix subscriptions/events, no live database or auth. New browser context per sample. Initial rendering measured inside the page from navigation start to Daily Ritual and a Check-in control, followed by two animation frames. Counts verify reload amplification; loading times include localhost JS and rendering, not hosted latency.", + "variants": { + "baseline": { + "queries": [ + { + "phase": "race", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "race", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "race", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "race", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 501 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 501 + } + ], + "trace": [ + { + "checked": true, + "ms": 641.0999999940395 + }, + { + "checked": false, + "ms": 780.7000000029802 + }, + { + "checked": true, + "ms": 845.7000000029802 + } + ], + "finalChecked": true, + "errors": [] + }, + "current": { + "queries": [ + { + "phase": "race", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "race", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": false, + "rows": 1000 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 501 + }, + { + "phase": "race", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "race", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": false, + "rows": 1000 + }, + { + "phase": "race", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 501 + } + ], + "trace": [ + { + "checked": true, + "ms": 592.4000000059605 + } + ], + "finalChecked": true, + "errors": [] + } + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/duel-reload.json b/docs/performance/2026-10-12/duel-reload.json new file mode 100644 index 0000000..e6b4933 --- /dev/null +++ b/docs/performance/2026-10-12/duel-reload.json @@ -0,0 +1,1788 @@ +{ + "measuredAt": "2026-10-12T05:52:59.159Z", + "pageCap": 1000, + "historyCount": 2500, + "samples": 1, + "method": "Production copies configured only for a Playwright-routed localhost Supabase mock. 2500 CheckIns, 50ms per HTTP response, real Phoenix subscriptions/events, no live database or auth. New browser context per sample. Initial rendering measured inside the page from navigation start to Daily Ritual and a Check-in control, followed by two animation frames. Counts verify reload amplification; loading times include localhost JS and rendering, not hosted latency.", + "variants": { + "baseline": { + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + } + ], + "burst": [ + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + } + ], + "during": [ + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": true, + "rows": 500 + } + ], + "writes": [ + { + "table": "duel_check_ins", + "method": "POST" + }, + { + "table": "duel_check_ins", + "method": "DELETE" + } + ], + "errors": [], + "localHistoryUntouched": true, + "visibleHistory": 2500 + }, + "current": { + "initialQueries": [ + { + "phase": "initial", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "initial", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": false, + "rows": 1000 + }, + { + "phase": "initial", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 500 + } + ], + "burst": [ + { + "phase": "burst", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": false, + "rows": 1000 + }, + { + "phase": "burst", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 500 + } + ], + "during": [ + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": false, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 500 + }, + { + "phase": "during-load", + "table": "duel_habits", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 20 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_stakes", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_reactions", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_rest_days", + "start": 0, + "limit": 1000, + "exact": true, + "rows": 0 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 1000, + "limit": 1000, + "exact": false, + "rows": 1000 + }, + { + "phase": "during-load", + "table": "duel_check_ins", + "start": 2000, + "limit": 1000, + "exact": false, + "rows": 500 + } + ], + "writes": [ + { + "table": "duel_check_ins", + "method": "POST" + }, + { + "table": "duel_check_ins", + "method": "DELETE" + } + ], + "errors": [], + "localHistoryUntouched": true, + "visibleHistory": 2500 + } + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/entry-performance.json b/docs/performance/2026-10-12/entry-performance.json new file mode 100644 index 0000000..2dd8b03 --- /dev/null +++ b/docs/performance/2026-10-12/entry-performance.json @@ -0,0 +1,118 @@ +{ + "measuredAt": "2026-10-12T03:35:58.495Z", + "method": "Configured production copies, isolated contexts, empty local auth for default account creation or a synthetic account with an empty Duel for onboarding. First visit warms HTTP cache; five subsequent document reloads measure navigation start through a browser-local heading readiness probe and two animation frames. Mock requests take 50ms. No accounts are created.", + "variants": { + "baseline": { + "account-entry": { + "samples": [ + { + "sample": 1, + "ms": 386.59999999403954 + }, + { + "sample": 2, + "ms": 380.09999999403954 + }, + { + "sample": 3, + "ms": 380 + }, + { + "sample": 4, + "ms": 380.20000000298023 + }, + { + "sample": 5, + "ms": 380.79999999701977 + } + ], + "errors": [] + }, + "onboarding": { + "samples": [ + { + "sample": 1, + "ms": 502.70000000298023 + }, + { + "sample": 2, + "ms": 496.29999999701977 + }, + { + "sample": 3, + "ms": 497.09999999403954 + }, + { + "sample": 4, + "ms": 497.09999999403954 + }, + { + "sample": 5, + "ms": 480.80000001192093 + } + ], + "errors": [] + } + }, + "current": { + "account-entry": { + "samples": [ + { + "sample": 1, + "ms": 77.29999999701977 + }, + { + "sample": 2, + "ms": 80.5 + }, + { + "sample": 3, + "ms": 80.79999999701977 + }, + { + "sample": 4, + "ms": 80.09999999403954 + }, + { + "sample": 5, + "ms": 80.79999999701977 + } + ], + "errors": [], + "smoke": { + "modeSwitch": true + } + }, + "onboarding": { + "samples": [ + { + "sample": 1, + "ms": 197.70000000298023 + }, + { + "sample": 2, + "ms": 196.59999999403954 + }, + { + "sample": 3, + "ms": 196.70000000298023 + }, + { + "sample": 4, + "ms": 196.79999999701977 + }, + { + "sample": 5, + "ms": 197.09999999403954 + } + ], + "errors": [], + "smoke": { + "savedPlan": true, + "ownerWrites": 1 + } + } + } + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/first-open-mobile.json b/docs/performance/2026-10-12/first-open-mobile.json new file mode 100644 index 0000000..bff384b --- /dev/null +++ b/docs/performance/2026-10-12/first-open-mobile.json @@ -0,0 +1,323 @@ +{ + "measuredAt": "2026-10-12T02:06:25.295Z", + "mode": "first-open", + "samples": 5, + "cpu": 4, + "mobile": true, + "method": "Production builds, 730 prior days across 20 seeded habits (14600 CheckIns), same Chrome process with isolated contexts. First opens follow document reload with HTTP cache retained. Interactions alternate build order, include one observed localStorage write and two animation frames, and parse its value after timing. CPU throttling simulates slower execution; it is not physical mobile hardware.", + "variants": { + "baseline": { + "samples": [ + { + "feature": "Progress", + "ms": 383.29999999701977, + "sample": 1 + }, + { + "feature": "Progress", + "ms": 359.3999999985099, + "sample": 2 + }, + { + "feature": "Progress", + "ms": 354.09999999403954, + "sample": 3 + }, + { + "feature": "Progress", + "ms": 377.29999999701977, + "sample": 4 + }, + { + "feature": "Progress", + "ms": 379.5, + "sample": 5 + }, + { + "feature": "Duel", + "ms": 377.70000000298023, + "sample": 1 + }, + { + "feature": "Duel", + "ms": 370, + "sample": 2 + }, + { + "feature": "Duel", + "ms": 360.29999999701977, + "sample": 3 + }, + { + "feature": "Duel", + "ms": 336.90000000596046, + "sample": 4 + }, + { + "feature": "Duel", + "ms": 340.8999999985099, + "sample": 5 + }, + { + "feature": "Settings", + "ms": 359.70000000298023, + "sample": 1 + }, + { + "feature": "Settings", + "ms": 354.70000000298023, + "sample": 2 + }, + { + "feature": "Settings", + "ms": 353.3999999985099, + "sample": 3 + }, + { + "feature": "Settings", + "ms": 362.5, + "sample": 4 + }, + { + "feature": "Settings", + "ms": 351.1000000014901, + "sample": 5 + }, + { + "feature": "Activity", + "ms": 422.3999999985099, + "sample": 1 + }, + { + "feature": "Activity", + "ms": 376, + "sample": 2 + }, + { + "feature": "Activity", + "ms": 363.1000000014901, + "sample": 3 + }, + { + "feature": "Activity", + "ms": 374.79999999701977, + "sample": 4 + }, + { + "feature": "Activity", + "ms": 367.3999999985099, + "sample": 5 + }, + { + "feature": "Proof", + "ms": 369.09999999403954, + "sample": 1 + }, + { + "feature": "Proof", + "ms": 379.5, + "sample": 2 + }, + { + "feature": "Proof", + "ms": 374.90000000596046, + "sample": 3 + }, + { + "feature": "Proof", + "ms": 362.19999999552965, + "sample": 4 + }, + { + "feature": "Proof", + "ms": 367.79999999701977, + "sample": 5 + }, + { + "feature": "Shortcuts", + "ms": 342.29999999701977, + "sample": 1 + }, + { + "feature": "Shortcuts", + "ms": 358.80000000447035, + "sample": 2 + }, + { + "feature": "Shortcuts", + "ms": 351.79999999701977, + "sample": 3 + }, + { + "feature": "Shortcuts", + "ms": 342.70000000298023, + "sample": 4 + }, + { + "feature": "Shortcuts", + "ms": 346.70000000298023, + "sample": 5 + } + ], + "errors": [], + "persistedAfterReload": 14600 + }, + "current": { + "samples": [ + { + "feature": "Progress", + "ms": 176, + "sample": 1 + }, + { + "feature": "Progress", + "ms": 141.10000000149012, + "sample": 2 + }, + { + "feature": "Progress", + "ms": 139.60000000149012, + "sample": 3 + }, + { + "feature": "Progress", + "ms": 130.89999999850988, + "sample": 4 + }, + { + "feature": "Progress", + "ms": 126.5, + "sample": 5 + }, + { + "feature": "Duel", + "ms": 168.19999999552965, + "sample": 1 + }, + { + "feature": "Duel", + "ms": 138, + "sample": 2 + }, + { + "feature": "Duel", + "ms": 132.60000000149012, + "sample": 3 + }, + { + "feature": "Duel", + "ms": 142.5, + "sample": 4 + }, + { + "feature": "Duel", + "ms": 142.29999999701977, + "sample": 5 + }, + { + "feature": "Settings", + "ms": 108.79999999701977, + "sample": 1 + }, + { + "feature": "Settings", + "ms": 95.80000000447035, + "sample": 2 + }, + { + "feature": "Settings", + "ms": 96.29999999701977, + "sample": 3 + }, + { + "feature": "Settings", + "ms": 97.09999999403954, + "sample": 4 + }, + { + "feature": "Settings", + "ms": 96.60000000149012, + "sample": 5 + }, + { + "feature": "Activity", + "ms": 142.19999999552965, + "sample": 1 + }, + { + "feature": "Activity", + "ms": 127.29999999701977, + "sample": 2 + }, + { + "feature": "Activity", + "ms": 135.69999999552965, + "sample": 3 + }, + { + "feature": "Activity", + "ms": 120.10000000149012, + "sample": 4 + }, + { + "feature": "Activity", + "ms": 140.69999999552965, + "sample": 5 + }, + { + "feature": "Proof", + "ms": 108.10000000149012, + "sample": 1 + }, + { + "feature": "Proof", + "ms": 88.5, + "sample": 2 + }, + { + "feature": "Proof", + "ms": 85, + "sample": 3 + }, + { + "feature": "Proof", + "ms": 85.40000000596046, + "sample": 4 + }, + { + "feature": "Proof", + "ms": 89.30000000447035, + "sample": 5 + }, + { + "feature": "Shortcuts", + "ms": 105.39999999850988, + "sample": 1 + }, + { + "feature": "Shortcuts", + "ms": 88, + "sample": 2 + }, + { + "feature": "Shortcuts", + "ms": 85.09999999403954, + "sample": 3 + }, + { + "feature": "Shortcuts", + "ms": 87.79999999701977, + "sample": 4 + }, + { + "feature": "Shortcuts", + "ms": 95.79999999701977, + "sample": 5 + } + ], + "errors": [], + "persistedAfterReload": 14600 + } + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/first-open.json b/docs/performance/2026-10-12/first-open.json new file mode 100644 index 0000000..aea5236 --- /dev/null +++ b/docs/performance/2026-10-12/first-open.json @@ -0,0 +1,323 @@ +{ + "measuredAt": "2026-10-12T01:58:44.942Z", + "mode": "first-open", + "samples": 5, + "cpu": 1, + "mobile": false, + "method": "Production builds, 730 prior days across 20 seeded habits (14600 CheckIns), same Chrome process with isolated contexts. First opens follow document reload with HTTP cache retained. Interactions alternate build order, include one observed localStorage write and two animation frames, and parse its value after timing. CPU throttling simulates slower execution; it is not physical mobile hardware.", + "variants": { + "baseline": { + "samples": [ + { + "feature": "Progress", + "ms": 334.69999999552965, + "sample": 1 + }, + { + "feature": "Progress", + "ms": 326.90000000596046, + "sample": 2 + }, + { + "feature": "Progress", + "ms": 337.19999999552965, + "sample": 3 + }, + { + "feature": "Progress", + "ms": 331.29999999701977, + "sample": 4 + }, + { + "feature": "Progress", + "ms": 326.90000000596046, + "sample": 5 + }, + { + "feature": "Duel", + "ms": 326, + "sample": 1 + }, + { + "feature": "Duel", + "ms": 331.29999999701977, + "sample": 2 + }, + { + "feature": "Duel", + "ms": 331.79999999701977, + "sample": 3 + }, + { + "feature": "Duel", + "ms": 329.80000000447035, + "sample": 4 + }, + { + "feature": "Duel", + "ms": 337.30000000447035, + "sample": 5 + }, + { + "feature": "Settings", + "ms": 335.6000000014901, + "sample": 1 + }, + { + "feature": "Settings", + "ms": 334.80000000447035, + "sample": 2 + }, + { + "feature": "Settings", + "ms": 331.30000000447035, + "sample": 3 + }, + { + "feature": "Settings", + "ms": 335.90000000596046, + "sample": 4 + }, + { + "feature": "Settings", + "ms": 334.09999999403954, + "sample": 5 + }, + { + "feature": "Activity", + "ms": 356.40000000596046, + "sample": 1 + }, + { + "feature": "Activity", + "ms": 327.8999999985099, + "sample": 2 + }, + { + "feature": "Activity", + "ms": 333.3999999985099, + "sample": 3 + }, + { + "feature": "Activity", + "ms": 334.40000000596046, + "sample": 4 + }, + { + "feature": "Activity", + "ms": 331.6000000014901, + "sample": 5 + }, + { + "feature": "Proof", + "ms": 350.29999999701977, + "sample": 1 + }, + { + "feature": "Proof", + "ms": 350.20000000298023, + "sample": 2 + }, + { + "feature": "Proof", + "ms": 348, + "sample": 3 + }, + { + "feature": "Proof", + "ms": 374.8999999985099, + "sample": 4 + }, + { + "feature": "Proof", + "ms": 346.80000000447035, + "sample": 5 + }, + { + "feature": "Shortcuts", + "ms": 347.19999999552965, + "sample": 1 + }, + { + "feature": "Shortcuts", + "ms": 352.8999999985099, + "sample": 2 + }, + { + "feature": "Shortcuts", + "ms": 369.3999999985099, + "sample": 3 + }, + { + "feature": "Shortcuts", + "ms": 354.8999999985099, + "sample": 4 + }, + { + "feature": "Shortcuts", + "ms": 351.70000000298023, + "sample": 5 + } + ], + "errors": [], + "persistedAfterReload": 14600 + }, + "current": { + "samples": [ + { + "feature": "Progress", + "ms": 82.70000000298023, + "sample": 1 + }, + { + "feature": "Progress", + "ms": 72.5, + "sample": 2 + }, + { + "feature": "Progress", + "ms": 53.600000001490116, + "sample": 3 + }, + { + "feature": "Progress", + "ms": 25.700000002980232, + "sample": 4 + }, + { + "feature": "Progress", + "ms": 43.400000005960464, + "sample": 5 + }, + { + "feature": "Duel", + "ms": 47.19999999552965, + "sample": 1 + }, + { + "feature": "Duel", + "ms": 44.80000000447035, + "sample": 2 + }, + { + "feature": "Duel", + "ms": 45.20000000298023, + "sample": 3 + }, + { + "feature": "Duel", + "ms": 36.19999999552965, + "sample": 4 + }, + { + "feature": "Duel", + "ms": 44.29999999701977, + "sample": 5 + }, + { + "feature": "Settings", + "ms": 41.29999999701977, + "sample": 1 + }, + { + "feature": "Settings", + "ms": 42.79999999701977, + "sample": 2 + }, + { + "feature": "Settings", + "ms": 38.5, + "sample": 3 + }, + { + "feature": "Settings", + "ms": 42.100000001490116, + "sample": 4 + }, + { + "feature": "Settings", + "ms": 40, + "sample": 5 + }, + { + "feature": "Activity", + "ms": 41.79999999701977, + "sample": 1 + }, + { + "feature": "Activity", + "ms": 39.70000000298023, + "sample": 2 + }, + { + "feature": "Activity", + "ms": 40.20000000298023, + "sample": 3 + }, + { + "feature": "Activity", + "ms": 43.30000000447035, + "sample": 4 + }, + { + "feature": "Activity", + "ms": 38.100000001490116, + "sample": 5 + }, + { + "feature": "Proof", + "ms": 38.900000005960464, + "sample": 1 + }, + { + "feature": "Proof", + "ms": 39.100000001490116, + "sample": 2 + }, + { + "feature": "Proof", + "ms": 44.19999999552965, + "sample": 3 + }, + { + "feature": "Proof", + "ms": 36.399999998509884, + "sample": 4 + }, + { + "feature": "Proof", + "ms": 42.399999998509884, + "sample": 5 + }, + { + "feature": "Shortcuts", + "ms": 66.29999999701977, + "sample": 1 + }, + { + "feature": "Shortcuts", + "ms": 54, + "sample": 2 + }, + { + "feature": "Shortcuts", + "ms": 31.69999999552965, + "sample": 3 + }, + { + "feature": "Shortcuts", + "ms": 48.30000000447035, + "sample": 4 + }, + { + "feature": "Shortcuts", + "ms": 25.900000005960464, + "sample": 5 + } + ], + "errors": [], + "persistedAfterReload": 14600 + } + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/interactions.json b/docs/performance/2026-10-12/interactions.json new file mode 100644 index 0000000..1b06c10 --- /dev/null +++ b/docs/performance/2026-10-12/interactions.json @@ -0,0 +1,1171 @@ +{ + "measuredAt": "2026-10-12T01:37:31.532Z", + "method": "Alternating baseline/current pairs in one Chrome process, 14600 local CheckIns, real button click through persisted mutation and two animation frames. No source profiling instrumentation. Mobile is a 390x844 viewport with 4x CDP CPU throttling, not physical hardware.", + "scenarios": { + "desktop": { + "baseline": { + "samples": [ + { + "action": "check-in", + "ms": 48.600000001490116, + "checks": 14601, + "points": 40, + "sample": 1 + }, + { + "action": "undo", + "ms": 56.100000001490116, + "checks": 14600, + "points": null, + "sample": 1 + }, + { + "action": "check-in", + "ms": 55.20000000298023, + "checks": 14601, + "points": 40, + "sample": 2 + }, + { + "action": "undo", + "ms": 53.899999998509884, + "checks": 14600, + "points": null, + "sample": 2 + }, + { + "action": "check-in", + "ms": 47.899999998509884, + "checks": 14601, + "points": 40, + "sample": 3 + }, + { + "action": "undo", + "ms": 50.80000000447035, + "checks": 14600, + "points": null, + "sample": 3 + }, + { + "action": "check-in", + "ms": 50, + "checks": 14601, + "points": 40, + "sample": 4 + }, + { + "action": "undo", + "ms": 54.5, + "checks": 14600, + "points": null, + "sample": 4 + }, + { + "action": "check-in", + "ms": 53.600000001490116, + "checks": 14601, + "points": 40, + "sample": 5 + }, + { + "action": "undo", + "ms": 54.80000000447035, + "checks": 14600, + "points": null, + "sample": 5 + }, + { + "action": "check-in", + "ms": 48.70000000298023, + "checks": 14601, + "points": 40, + "sample": 6 + }, + { + "action": "undo", + "ms": 52.600000001490116, + "checks": 14600, + "points": null, + "sample": 6 + }, + { + "action": "check-in", + "ms": 46.100000001490116, + "checks": 14601, + "points": 40, + "sample": 7 + }, + { + "action": "undo", + "ms": 50.20000000298023, + "checks": 14600, + "points": null, + "sample": 7 + }, + { + "action": "check-in", + "ms": 52.5, + "checks": 14601, + "points": 40, + "sample": 8 + }, + { + "action": "undo", + "ms": 54.5, + "checks": 14600, + "points": null, + "sample": 8 + }, + { + "action": "check-in", + "ms": 48.5, + "checks": 14601, + "points": 40, + "sample": 9 + }, + { + "action": "undo", + "ms": 48.70000000298023, + "checks": 14600, + "points": null, + "sample": 9 + }, + { + "action": "check-in", + "ms": 44.899999998509884, + "checks": 14601, + "points": 40, + "sample": 10 + }, + { + "action": "undo", + "ms": 50.20000000298023, + "checks": 14600, + "points": null, + "sample": 10 + }, + { + "action": "check-in", + "ms": 50.20000000298023, + "checks": 14601, + "points": 40, + "sample": 11 + }, + { + "action": "undo", + "ms": 52.79999999701977, + "checks": 14600, + "points": null, + "sample": 11 + }, + { + "action": "check-in", + "ms": 53.30000000447035, + "checks": 14601, + "points": 40, + "sample": 12 + }, + { + "action": "undo", + "ms": 53.70000000298023, + "checks": 14600, + "points": null, + "sample": 12 + }, + { + "action": "check-in", + "ms": 44.899999998509884, + "checks": 14601, + "points": 40, + "sample": 13 + }, + { + "action": "undo", + "ms": 50, + "checks": 14600, + "points": null, + "sample": 13 + }, + { + "action": "check-in", + "ms": 53.29999999701977, + "checks": 14601, + "points": 40, + "sample": 14 + }, + { + "action": "undo", + "ms": 54, + "checks": 14600, + "points": null, + "sample": 14 + }, + { + "action": "check-in", + "ms": 44.100000001490116, + "checks": 14601, + "points": 40, + "sample": 15 + }, + { + "action": "undo", + "ms": 48, + "checks": 14600, + "points": null, + "sample": 15 + }, + { + "action": "check-in", + "ms": 46.600000001490116, + "checks": 14601, + "points": 40, + "sample": 16 + }, + { + "action": "undo", + "ms": 53.80000000447035, + "checks": 14600, + "points": null, + "sample": 16 + }, + { + "action": "check-in", + "ms": 45.79999999701977, + "checks": 14601, + "points": 40, + "sample": 17 + }, + { + "action": "undo", + "ms": 55.100000001490116, + "checks": 14600, + "points": null, + "sample": 17 + }, + { + "action": "check-in", + "ms": 47.899999998509884, + "checks": 14601, + "points": 40, + "sample": 18 + }, + { + "action": "undo", + "ms": 49.399999998509884, + "checks": 14600, + "points": null, + "sample": 18 + }, + { + "action": "check-in", + "ms": 44.399999998509884, + "checks": 14601, + "points": 40, + "sample": 19 + }, + { + "action": "undo", + "ms": 48.600000001490116, + "checks": 14600, + "points": null, + "sample": 19 + }, + { + "action": "check-in", + "ms": 54.69999999552965, + "checks": 14601, + "points": 40, + "sample": 20 + }, + { + "action": "undo", + "ms": 55.70000000298023, + "checks": 14600, + "points": null, + "sample": 20 + } + ], + "errors": [], + "persistedAfterReload": 14600, + "overflow": { + "width": 1280, + "documentWidth": 1280 + } + }, + "current": { + "samples": [ + { + "action": "check-in", + "ms": 41.30000000447035, + "checks": 14601, + "points": 40, + "sample": 1 + }, + { + "action": "undo", + "ms": 47.79999999701977, + "checks": 14600, + "points": null, + "sample": 1 + }, + { + "action": "check-in", + "ms": 40.399999998509884, + "checks": 14601, + "points": 40, + "sample": 2 + }, + { + "action": "undo", + "ms": 45.30000000447035, + "checks": 14600, + "points": null, + "sample": 2 + }, + { + "action": "check-in", + "ms": 44, + "checks": 14601, + "points": 40, + "sample": 3 + }, + { + "action": "undo", + "ms": 48.099999994039536, + "checks": 14600, + "points": null, + "sample": 3 + }, + { + "action": "check-in", + "ms": 42.5, + "checks": 14601, + "points": 40, + "sample": 4 + }, + { + "action": "undo", + "ms": 50.29999999701977, + "checks": 14600, + "points": null, + "sample": 4 + }, + { + "action": "check-in", + "ms": 45.100000001490116, + "checks": 14601, + "points": 40, + "sample": 5 + }, + { + "action": "undo", + "ms": 44.5, + "checks": 14600, + "points": null, + "sample": 5 + }, + { + "action": "check-in", + "ms": 38.5, + "checks": 14601, + "points": 40, + "sample": 6 + }, + { + "action": "undo", + "ms": 43.399999998509884, + "checks": 14600, + "points": null, + "sample": 6 + }, + { + "action": "check-in", + "ms": 40.79999999701977, + "checks": 14601, + "points": 40, + "sample": 7 + }, + { + "action": "undo", + "ms": 43.899999998509884, + "checks": 14600, + "points": null, + "sample": 7 + }, + { + "action": "check-in", + "ms": 44.099999994039536, + "checks": 14601, + "points": 40, + "sample": 8 + }, + { + "action": "undo", + "ms": 44.100000001490116, + "checks": 14600, + "points": null, + "sample": 8 + }, + { + "action": "check-in", + "ms": 45.399999998509884, + "checks": 14601, + "points": 40, + "sample": 9 + }, + { + "action": "undo", + "ms": 48.20000000298023, + "checks": 14600, + "points": null, + "sample": 9 + }, + { + "action": "check-in", + "ms": 39, + "checks": 14601, + "points": 40, + "sample": 10 + }, + { + "action": "undo", + "ms": 45.399999998509884, + "checks": 14600, + "points": null, + "sample": 10 + }, + { + "action": "check-in", + "ms": 39.399999998509884, + "checks": 14601, + "points": 40, + "sample": 11 + }, + { + "action": "undo", + "ms": 44.20000000298023, + "checks": 14600, + "points": null, + "sample": 11 + }, + { + "action": "check-in", + "ms": 37.5, + "checks": 14601, + "points": 40, + "sample": 12 + }, + { + "action": "undo", + "ms": 45.099999994039536, + "checks": 14600, + "points": null, + "sample": 12 + }, + { + "action": "check-in", + "ms": 47.70000000298023, + "checks": 14601, + "points": 40, + "sample": 13 + }, + { + "action": "undo", + "ms": 50.399999998509884, + "checks": 14600, + "points": null, + "sample": 13 + }, + { + "action": "check-in", + "ms": 39, + "checks": 14601, + "points": 40, + "sample": 14 + }, + { + "action": "undo", + "ms": 43.600000001490116, + "checks": 14600, + "points": null, + "sample": 14 + }, + { + "action": "check-in", + "ms": 42.5, + "checks": 14601, + "points": 40, + "sample": 15 + }, + { + "action": "undo", + "ms": 42.899999998509884, + "checks": 14600, + "points": null, + "sample": 15 + }, + { + "action": "check-in", + "ms": 41.5, + "checks": 14601, + "points": 40, + "sample": 16 + }, + { + "action": "undo", + "ms": 43.20000000298023, + "checks": 14600, + "points": null, + "sample": 16 + }, + { + "action": "check-in", + "ms": 44.30000000447035, + "checks": 14601, + "points": 40, + "sample": 17 + }, + { + "action": "undo", + "ms": 51, + "checks": 14600, + "points": null, + "sample": 17 + }, + { + "action": "check-in", + "ms": 38.599999994039536, + "checks": 14601, + "points": 40, + "sample": 18 + }, + { + "action": "undo", + "ms": 46, + "checks": 14600, + "points": null, + "sample": 18 + }, + { + "action": "check-in", + "ms": 45.20000000298023, + "checks": 14601, + "points": 40, + "sample": 19 + }, + { + "action": "undo", + "ms": 46.600000001490116, + "checks": 14600, + "points": null, + "sample": 19 + }, + { + "action": "check-in", + "ms": 40.600000001490116, + "checks": 14601, + "points": 40, + "sample": 20 + }, + { + "action": "undo", + "ms": 45.79999999701977, + "checks": 14600, + "points": null, + "sample": 20 + } + ], + "errors": [], + "persistedAfterReload": 14600, + "overflow": { + "width": 1280, + "documentWidth": 1280 + } + } + }, + "mobile-4x-cpu": { + "baseline": { + "samples": [ + { + "action": "check-in", + "ms": 211.10000000149012, + "checks": 14601, + "points": 40, + "sample": 1 + }, + { + "action": "undo", + "ms": 148.10000000149012, + "checks": 14600, + "points": null, + "sample": 1 + }, + { + "action": "check-in", + "ms": 216.39999999850988, + "checks": 14601, + "points": 40, + "sample": 2 + }, + { + "action": "undo", + "ms": 171, + "checks": 14600, + "points": null, + "sample": 2 + }, + { + "action": "check-in", + "ms": 202.89999999850988, + "checks": 14601, + "points": 40, + "sample": 3 + }, + { + "action": "undo", + "ms": 146.10000000149012, + "checks": 14600, + "points": null, + "sample": 3 + }, + { + "action": "check-in", + "ms": 194.59999999403954, + "checks": 14601, + "points": 40, + "sample": 4 + }, + { + "action": "undo", + "ms": 156.20000000298023, + "checks": 14600, + "points": null, + "sample": 4 + }, + { + "action": "check-in", + "ms": 204.10000000149012, + "checks": 14601, + "points": 40, + "sample": 5 + }, + { + "action": "undo", + "ms": 225, + "checks": 14600, + "points": null, + "sample": 5 + }, + { + "action": "check-in", + "ms": 194.10000000149012, + "checks": 14601, + "points": 40, + "sample": 6 + }, + { + "action": "undo", + "ms": 154.19999999552965, + "checks": 14600, + "points": null, + "sample": 6 + }, + { + "action": "check-in", + "ms": 183.5, + "checks": 14601, + "points": 40, + "sample": 7 + }, + { + "action": "undo", + "ms": 146.69999999552965, + "checks": 14600, + "points": null, + "sample": 7 + }, + { + "action": "check-in", + "ms": 224, + "checks": 14601, + "points": 40, + "sample": 8 + }, + { + "action": "undo", + "ms": 169.29999999701977, + "checks": 14600, + "points": null, + "sample": 8 + }, + { + "action": "check-in", + "ms": 195.39999999850988, + "checks": 14601, + "points": 40, + "sample": 9 + }, + { + "action": "undo", + "ms": 149.69999999552965, + "checks": 14600, + "points": null, + "sample": 9 + }, + { + "action": "check-in", + "ms": 188.60000000149012, + "checks": 14601, + "points": 40, + "sample": 10 + }, + { + "action": "undo", + "ms": 141.20000000298023, + "checks": 14600, + "points": null, + "sample": 10 + }, + { + "action": "check-in", + "ms": 206, + "checks": 14601, + "points": 40, + "sample": 11 + }, + { + "action": "undo", + "ms": 154.70000000298023, + "checks": 14600, + "points": null, + "sample": 11 + }, + { + "action": "check-in", + "ms": 189.10000000149012, + "checks": 14601, + "points": 40, + "sample": 12 + }, + { + "action": "undo", + "ms": 146.29999999701977, + "checks": 14600, + "points": null, + "sample": 12 + }, + { + "action": "check-in", + "ms": 193.5, + "checks": 14601, + "points": 40, + "sample": 13 + }, + { + "action": "undo", + "ms": 147.30000000447035, + "checks": 14600, + "points": null, + "sample": 13 + }, + { + "action": "check-in", + "ms": 200.79999999701977, + "checks": 14601, + "points": 40, + "sample": 14 + }, + { + "action": "undo", + "ms": 156.89999999850988, + "checks": 14600, + "points": null, + "sample": 14 + }, + { + "action": "check-in", + "ms": 184, + "checks": 14601, + "points": 40, + "sample": 15 + }, + { + "action": "undo", + "ms": 149.20000000298023, + "checks": 14600, + "points": null, + "sample": 15 + }, + { + "action": "check-in", + "ms": 197.09999999403954, + "checks": 14601, + "points": 40, + "sample": 16 + }, + { + "action": "undo", + "ms": 142.29999999701977, + "checks": 14600, + "points": null, + "sample": 16 + }, + { + "action": "check-in", + "ms": 202.30000000447035, + "checks": 14601, + "points": 40, + "sample": 17 + }, + { + "action": "undo", + "ms": 153.39999999850988, + "checks": 14600, + "points": null, + "sample": 17 + }, + { + "action": "check-in", + "ms": 191, + "checks": 14601, + "points": 40, + "sample": 18 + }, + { + "action": "undo", + "ms": 150, + "checks": 14600, + "points": null, + "sample": 18 + }, + { + "action": "check-in", + "ms": 185.60000000149012, + "checks": 14601, + "points": 40, + "sample": 19 + }, + { + "action": "undo", + "ms": 148.09999999403954, + "checks": 14600, + "points": null, + "sample": 19 + }, + { + "action": "check-in", + "ms": 205.89999999850988, + "checks": 14601, + "points": 40, + "sample": 20 + }, + { + "action": "undo", + "ms": 160.39999999850988, + "checks": 14600, + "points": null, + "sample": 20 + } + ], + "errors": [], + "persistedAfterReload": 14600, + "overflow": { + "width": 390, + "documentWidth": 390 + } + }, + "current": { + "samples": [ + { + "action": "check-in", + "ms": 181.5, + "checks": 14601, + "points": 40, + "sample": 1 + }, + { + "action": "undo", + "ms": 131, + "checks": 14600, + "points": null, + "sample": 1 + }, + { + "action": "check-in", + "ms": 167.90000000596046, + "checks": 14601, + "points": 40, + "sample": 2 + }, + { + "action": "undo", + "ms": 134.69999999552965, + "checks": 14600, + "points": null, + "sample": 2 + }, + { + "action": "check-in", + "ms": 186.5, + "checks": 14601, + "points": 40, + "sample": 3 + }, + { + "action": "undo", + "ms": 128, + "checks": 14600, + "points": null, + "sample": 3 + }, + { + "action": "check-in", + "ms": 160.20000000298023, + "checks": 14601, + "points": 40, + "sample": 4 + }, + { + "action": "undo", + "ms": 121.69999999552965, + "checks": 14600, + "points": null, + "sample": 4 + }, + { + "action": "check-in", + "ms": 168.10000000149012, + "checks": 14601, + "points": 40, + "sample": 5 + }, + { + "action": "undo", + "ms": 123.80000000447035, + "checks": 14600, + "points": null, + "sample": 5 + }, + { + "action": "check-in", + "ms": 176.69999999552965, + "checks": 14601, + "points": 40, + "sample": 6 + }, + { + "action": "undo", + "ms": 131.39999999850988, + "checks": 14600, + "points": null, + "sample": 6 + }, + { + "action": "check-in", + "ms": 172.69999999552965, + "checks": 14601, + "points": 40, + "sample": 7 + }, + { + "action": "undo", + "ms": 120.90000000596046, + "checks": 14600, + "points": null, + "sample": 7 + }, + { + "action": "check-in", + "ms": 164.20000000298023, + "checks": 14601, + "points": 40, + "sample": 8 + }, + { + "action": "undo", + "ms": 119.19999999552965, + "checks": 14600, + "points": null, + "sample": 8 + }, + { + "action": "check-in", + "ms": 238.89999999850988, + "checks": 14601, + "points": 40, + "sample": 9 + }, + { + "action": "undo", + "ms": 121.19999999552965, + "checks": 14600, + "points": null, + "sample": 9 + }, + { + "action": "check-in", + "ms": 171.69999999552965, + "checks": 14601, + "points": 40, + "sample": 10 + }, + { + "action": "undo", + "ms": 125.70000000298023, + "checks": 14600, + "points": null, + "sample": 10 + }, + { + "action": "check-in", + "ms": 169.19999999552965, + "checks": 14601, + "points": 40, + "sample": 11 + }, + { + "action": "undo", + "ms": 121.79999999701977, + "checks": 14600, + "points": null, + "sample": 11 + }, + { + "action": "check-in", + "ms": 164.39999999850988, + "checks": 14601, + "points": 40, + "sample": 12 + }, + { + "action": "undo", + "ms": 119.60000000149012, + "checks": 14600, + "points": null, + "sample": 12 + }, + { + "action": "check-in", + "ms": 172.29999999701977, + "checks": 14601, + "points": 40, + "sample": 13 + }, + { + "action": "undo", + "ms": 122.40000000596046, + "checks": 14600, + "points": null, + "sample": 13 + }, + { + "action": "check-in", + "ms": 177.29999999701977, + "checks": 14601, + "points": 40, + "sample": 14 + }, + { + "action": "undo", + "ms": 198.60000000149012, + "checks": 14600, + "points": null, + "sample": 14 + }, + { + "action": "check-in", + "ms": 165.5, + "checks": 14601, + "points": 40, + "sample": 15 + }, + { + "action": "undo", + "ms": 124.10000000149012, + "checks": 14600, + "points": null, + "sample": 15 + }, + { + "action": "check-in", + "ms": 161.39999999850988, + "checks": 14601, + "points": 40, + "sample": 16 + }, + { + "action": "undo", + "ms": 121.29999999701977, + "checks": 14600, + "points": null, + "sample": 16 + }, + { + "action": "check-in", + "ms": 177.5, + "checks": 14601, + "points": 40, + "sample": 17 + }, + { + "action": "undo", + "ms": 121, + "checks": 14600, + "points": null, + "sample": 17 + }, + { + "action": "check-in", + "ms": 193, + "checks": 14601, + "points": 40, + "sample": 18 + }, + { + "action": "undo", + "ms": 138, + "checks": 14600, + "points": null, + "sample": 18 + }, + { + "action": "check-in", + "ms": 178.69999999552965, + "checks": 14601, + "points": 40, + "sample": 19 + }, + { + "action": "undo", + "ms": 126.5, + "checks": 14600, + "points": null, + "sample": 19 + }, + { + "action": "check-in", + "ms": 168.39999999850988, + "checks": 14601, + "points": 40, + "sample": 20 + }, + { + "action": "undo", + "ms": 117.19999999552965, + "checks": 14600, + "points": null, + "sample": 20 + } + ], + "errors": [], + "persistedAfterReload": 14600, + "overflow": { + "width": 390, + "documentWidth": 390 + } + } + } + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/offline.json b/docs/performance/2026-10-12/offline.json new file mode 100644 index 0000000..fce2866 --- /dev/null +++ b/docs/performance/2026-10-12/offline.json @@ -0,0 +1,37 @@ +{ + "measuredAt": "2026-10-12T01:56:12.353Z", + "mode": "offline", + "samples": 5, + "cpu": 1, + "mobile": false, + "method": "Production builds, 730 prior days across 20 seeded habits (14600 CheckIns), same Chrome process with isolated contexts. First opens follow document reload with HTTP cache retained. Interactions alternate build order, include one observed localStorage write and two animation frames, and parse its value after timing. CPU throttling simulates slower execution; it is not physical mobile hardware.", + "variants": { + "baseline": { + "samples": [], + "errors": [], + "offline": { + "localMutationAndUndo": true, + "warmedViews": [ + "Progress", + "Duel", + "Settings" + ] + }, + "persistedAfterReload": 14600 + }, + "current": { + "samples": [], + "errors": [], + "offline": { + "localMutationAndUndo": true, + "warmedViews": [ + "Progress", + "Duel", + "Settings" + ] + }, + "persistedAfterReload": 14600 + } + }, + "browser": "148.0.7778.178" +} \ No newline at end of file diff --git a/docs/performance/2026-10-12/spec-review.md b/docs/performance/2026-10-12/spec-review.md new file mode 100644 index 0000000..c58d977 --- /dev/null +++ b/docs/performance/2026-10-12/spec-review.md @@ -0,0 +1,14 @@ +# Spec Review: Issue #63 (69d22fe vs e7b284e) + +### 1. Missing or Partial Requirements +- **Physical mobile and hosted infrastructure verification**: Spec requires: *"Capture production loading and representative large-history interaction measurements before changing code"* and *"Document measurements, remaining limits, and review findings"*. Verification was conducted exclusively on localhost Chromium using synthetic 4× CPU throttling, responsive viewport emulation, and 50ms mock HTTP delays. Hosted database latency/RLS and physical mobile hardware remain unverified due to infrastructure constraints (connector 403, CLI unauthenticated). This is recorded as an acknowledged boundary in [`docs/performance/2026-10-12/README.md`](file:///home/maciek/Documents/do-it/docs/performance/2026-10-12/README.md). +- **In-flight query cancellation**: Spec requires: *"Fix measured bottlenecks with before/after evidence and preserve observable product behavior."* While bounded 3-wide parallel pagination is implemented in `69d22fe`, aborting in-flight history reads on unmount was intentionally deferred ([`decisions.tsv`](file:///home/maciek/Documents/do-it/docs/performance/2026-10-12/decisions.tsv)). Queries run to completion without `AbortSignal` propagation. + +### 2. Scope Creep +- **Apple Health polling removal**: Spec mandates *"...preserving scoring, offline persistence, Duel isolation, wearable behavior, and both appearance modes"* and brief notes: *"Existing assigned issues 32(health)... are out of scope."* Commit `fbc0c76` deleted `pullAppleHealthPending` polling in [`src/lib/store.tsx`](file:///home/maciek/Documents/do-it/src/lib/store.tsx). Although `/api/sync/apple-health` returned HTTP 410 Gone at baseline (confirming no reachable wearable regression), touching health sync touched an assigned out-of-scope domain. +- **Intent preloading**: Spec asked to: *"Make loading, navigation, and Check-ins faster"*. Pre-warming chunk imports on hover/focus (`Header`, `DesktopSidebar`, `BottomNav`) in `f8d7837` represents unrequested proactive preloading, though it preserves deployment guards. + +### 3. Implementation Correctness & Preserved Invariants +- **Pagination & history preservation**: Spec mandates: *"...preserving scoring, offline persistence, Duel isolation... and complete history."* Proven correct. `69d22fe` in [`src/lib/duel-sync.ts`](file:///home/maciek/Documents/do-it/src/lib/duel-sync.ts) implements bounded 3-wide batching with contiguous-prefix serial fallback on cap shifts, integer count validation (`Number.isInteger(count)`), and full query ranges. Verified across 14,600 rows ([`duel-loading-cap400.json`](file:///home/maciek/Documents/do-it/docs/performance/2026-10-12/duel-loading-cap400.json): 2385.8ms to 1034.4ms; 170 tests passing). +- **Duel isolation & write barriers**: Overlapping reads during local writes are rejected via `duelQueueRef` ([`src/lib/store.tsx`](file:///home/maciek/Documents/do-it/src/lib/store.tsx)), eliminating optimistic Check-in flicker ([`duel-race.json`](file:///home/maciek/Documents/do-it/docs/performance/2026-10-12/duel-race.json)). +- **Core scoring & persistence**: Duplicate-first streak precedence, rest bridging, and 365-day caps are preserved via `checkInIndex` ([`src/lib/score-calculator.ts`](file:///home/maciek/Documents/do-it/src/lib/score-calculator.ts)). LocalStorage durability remains strictly synchronous. diff --git a/docs/performance/2026-10-12/standards-review.md b/docs/performance/2026-10-12/standards-review.md new file mode 100644 index 0000000..070b47a --- /dev/null +++ b/docs/performance/2026-10-12/standards-review.md @@ -0,0 +1,41 @@ +# Standards Review (Commit 69d22fe) + +## Documented Standards +**0 hard violations.** The diff strictly follows repository guidelines: +- **`AGENTS.md` & `docs/agent-workflow.md`**: No prohibited mutations, lockfile modifications, or credential leaks. Scoped strictly to authorized performance requirements. +- **`ts-best-practices`**: Strongly typed. Count validation in `src/lib/duel-sync.ts:18` uses `Number.isInteger(count) && count >= 0` rather than loose truthiness. External Supabase responses preserve proper error handling (`if (error) throw error`). Unboxing via `row.data as T` preserves existing generic interface consistency. +- **`react-best-practices`**: Deferred views (`src/components/DeferredView.tsx`) consume stable immutable snapshots via `useSyncExternalStore` without effect synchronization anti-patterns. +- **`principle-laziness-protocol`**: The concurrency enhancement in `duel-sync.ts` implements bounded parallel requests (batch limit 3) directly without inventing an unneeded generic task scheduler. Shared `queryRange` helper deduplicates range query construction. + +--- + +## Smell Baseline (Judgement Calls) + +### 1. Duplicated Code +- **`src/lib/duel-sync.ts:59-62, 79-82`** + ```ts + rows.push(...batchData.map(row => row.data as T)); + offset += batchData.length; + if (rows.length >= totalCount) return rows; + ``` + The row unwrapping, offset advance, and completion threshold checks are duplicated verbatim across the parallel batch collector and serial fallback loop. A small local helper (e.g. `appendPage(data)`) would centralize pagination termination and buffer appending. +- **`src/components/HabitCard.tsx:28` & `src/components/TodayView.tsx:27`** + `createDeferredView` is invoked independently in both modules for `ProofGalleryModal`. Creating and exporting a shared deferred component directly in `ProofGalleryModal.tsx` would consolidate loading and cache state. + +### 2. Mysterious Name +- **`src/lib/duel-sync.ts:29`** + ```ts + const stride = data.length; + ``` + `stride` stores the discovered first-page size. `effectivePageSize` or `pageSizeStride` would more accurately describe its semantic role in speculative offset calculations. + +### 3. Speculative Generality +- **`src/lib/deferred-resource.ts:10`** + ```ts + activate: (load: () => Promise) => Promise = loadCurrentPage + ``` + The `activate` strategy parameter exists solely for test mocking and is unused in production callers. + +### 4. Data Clumps +- **`src/lib/score-calculator.ts:379-385`** + `calculateHabitStreak` accepts `(checkIns, restDays, today, indexed)` as a clump where `checkIns` is bypassed when `indexed` is present. diff --git a/docs/performance/2026-10-12/trail-review-before-paging.md b/docs/performance/2026-10-12/trail-review-before-paging.md new file mode 100644 index 0000000..4d1f1ee --- /dev/null +++ b/docs/performance/2026-10-12/trail-review-before-paging.md @@ -0,0 +1,51 @@ +# Overnight Performance Run Decision Trail Review (Round 4) + +**Reviewer Model**: `gemini-3.7-flash-high` +**Parent Agent**: `gpt-6.1-sol` (T3 Thread `f680d6b6-ccda-4e69-b783-001f03f4f08e`) +**Coverage Boundary**: Baseline `e7b284e` through commit `400fbfe` (`main`); `decisions.tsv` Rows 1–50 (`00:07:36Z`–`04:29:27Z`). +**Scope Notice**: Evaluates accepted changes only. Does not grant blanket approval for concurrent or subsequent edits (e.g., in-flight `duel-sync.ts` parallel pagination). + +--- + +### 1. Audit of Claims vs. Durable Receipts + +All 50 rows in `docs/performance/2026-10-12/decisions.tsv` were audited against repository artifacts and the parent timeline. All 118 evidence file pointers resolve. Row 48 (`04:18:58Z`) correctly superseded the removed `calculations-r2.test.ts` from Row 10 with `calculation-regressions.test.ts`. + +| Row & Timestamp | Phase | Claimed Metric / Action | Verified Receipt | Status / Severity | +|---|---|---|---|---| +| **13** `00:47:17Z` | `loading` | First open: Progress ~33ms, Duel ~40ms, Settings ~30ms | `deferred-first-navigation.json` | Verified (Low) | +| **20, 24** `01:38:59Z` | `sync` | 20-event burst coalesced to 1 reload; 5 exact counts | `authenticated-reload.json` | Verified (Low) | +| **24** `01:43:00Z` | `interact` | Check-in median 48.6→41.4ms; Undo 53.3→45.4ms | `interleaved-interactions.json` | Verified (Low) | +| **27** `01:53:18Z` | `race` | Check-in stays checked across overlapping read/write | `authenticated-race-after.json` | Verified (Low) | +| **28** `01:53:18Z` | `cap` | Retains full 2,500 records under 400-row cap | `authenticated-reload-cap400.json` | Verified (Low) | +| **37** `03:13:41Z` | `preview` | Protected Vercel preview blocked by MCP 403 / unauthenticated CLI | `vercel-whoami.json`, `whoami.log` | Access limitation (Medium) | +| **39, 41** `03:24:55Z` | `entry` | Account 380→81ms; Onboarding 497→197ms; plan saved | `entry-performance.json` | Verified (Low) | +| **45** `04:07:59Z` | `prototype` | Naive parallel dropped 400 rows (850/1250) on shrinking cap; rejected | `pagination-prototype.json` | Verified (Low) | + +--- + +### 2. Methodological Rigor & Bias Corrections + +The trail shows exceptional discipline in discarding compromised metrics: +1. **Persistence Polling Bias (Row 22, `01:39:00Z`)**: Discarded timings polluted by repeated 2.3MB JSON state parsing; replaced with one-write observation. +2. **Mock Content-Range (Row 23, `01:43:00Z`)**: Exposed missing headers to eliminate spurious unknown-count fallbacks. +3. **Pagination Quiescence (Row 25, `01:44:24Z`)**: Fixed premature completion checks across 400-row query gaps. +4. **Locator Quantization (Row 38, `03:20:56Z`)**: Replaced 864ms locator retry waits with in-browser readiness probes. +5. **Short-Page Invariant (Row 45, `04:07:59Z`)**: Promptly rejected naive parallel fetching when shrinking server caps exposed silent data loss. + +--- + +### 3. Acceptance Gaps and Residual Risks + +1. **Hosted Infrastructure Limitation vs. Failure (Row 37)**: Preview testing failed due to team SSO/403 and unauthenticated CLI credentials (`vercel-whoami.json`). This is an external access barrier, not code failure, but leaves real-world database latency and RLS unmeasured. +2. **Synthetic Hardware Constraints**: Mobile verification relied on headless Chromium with synthetic 4× CPU throttling and viewport resizing (`repro-interactions-mobile.json`); physical mobile hardware and touch events remain unverified. +3. **Apple Health Removal (Commit `fbc0c76`)**: Deleting the `pullAppleHealthPending` hook was flagged as scope creep, but baseline inspection confirms `/api/sync/apple-health` already returned HTTP 410 at `e7b284e`. Removing dead network traffic preserves wearable invariants while eliminating polling overhead. + +--- + +## Attention + +reviewed by gemini-3.7-flash-high +- Row 37 (`03:13:41Z`): Hosted preview remains unverified due to team 403 / CLI credentials; RLS and network latency depend solely on local mock validation. +- Rows 24 & 29: Mobile validation is synthetic (4× CPU throttling, viewport emulation); physical device behavior unmeasured. +- Commit `400fbfe` boundary: Accepted changes are sound, but serial history loading and in-flight cancellation remain open until the concurrent `duel-sync.ts` task finishes and passes separate review. diff --git a/docs/performance/2026-10-12/trail-review.md b/docs/performance/2026-10-12/trail-review.md new file mode 100644 index 0000000..254a3fc --- /dev/null +++ b/docs/performance/2026-10-12/trail-review.md @@ -0,0 +1,44 @@ +# Overnight Performance Run Decision Trail Review (Final Round 6) + +**Reviewer Model**: `gemini-3.7-flash-high` +**Parent Agent**: `gpt-6.1-sol` (T3 Thread `f680d6b6-ccda-4e69-b783-001f03f4f08e`) +**Coverage Boundary**: Commit `69d22fe` (`main`); `decisions.tsv` Rows 51–54 (`04:32:48Z`–`05:49:48Z`). Extends `trail-review-r4.md` (Rows 1–50, `400fbfe`). + +--- + +### 1. Audit of Rows 51–54 vs. Durable Receipts + +All evidence pointers in Rows 51–54 resolve to durable artifacts. Pinned commit `69d22fe` contains clean source and verification receipts. + +| Row & Timestamp | Phase | Claimed Metric / Action | Verified Receipt | Status / Severity | +|---|---|---|---|---| +| **51** `04:32:48Z` | `pagination` | Bounded (max 3) parallel history paging with serial fallback | `src/lib/duel-sync.ts`, `pagination-parent-check.log` | Verified (Low) | +| **52** `05:49:48Z` | `paging-review` | Fix final-slot cap growth, validate integer counts, share range builder | `pagination-review-r5.md`, `src/lib/duel-sync.test.ts`, `final-check.log` | Verified (Low) | +| **53** `05:49:48Z` | `loading` | Authenticated load 2365.4ms→1018.5ms (median 5 samples); 14,600 rows retained | `duel-loading-cap400.json`, `verify-duel-performance.cjs` | Verified (Low) | +| **54** `05:49:48Z` | `scope` | Defer in-flight cancellation and hotpath CPU micro-opts; reject 94% savings claim | `cancellation-audit-r4.md`, `hotpaths-r5.md` | Verified (Low) | + +--- + +### 2. Methodological Rigor & Verification of Receipts + +1. **Paging Correctness & Adversarial Defense (Rows 51–52)**: + - Parent incorporated adversarial SDK review (`pagination-review-r5.md`), fixing avoidable speculative queries when server caps grow (`batchData.length !== Math.min(stride, totalCount - start)`). + - Added unit regressions covering offsets `[0, 200, 400, 600, 1100, 1600]`. + - Verified in `final-check.log`: 170 tests pass (0 failures), clean typecheck, lint, and production builds (`final-build.log`, `final-auth-build.log`). +2. **Browser Loading Verification (Row 53)**: + - Evaluated 14,600 check-ins under a 400-row cap in fresh browser contexts across 5 alternating samples (`scripts/verify-duel-performance.cjs`). + - Prior to pagination, `duel-loading-before-pagination.json` recorded baseline median 2363.0ms vs current 2363.1ms. + - Post-pagination (`duel-loading-cap400.json`): baseline median 2365.4ms vs current median 1018.5ms (57% reduction). + - Trace verifies exact count requested only once (`start: 0, exact: true`), concurrency strictly bounded to 3 (`peakByTable.duel_check_ins: 3`), and Activity feed verified all 14,600 items (`visibleHistory: 14600`). +3. **Scope Discipline (Row 54)**: + - Rejected unsubstantiated claims: `cancellation-audit-r4.md` proposed aborting in-flight reads with a speculative ~94% reload bandwidth/CPU saving. Parent rightly treated this as a hypothesis and deferred implementation to avoid risking write-pipeline stability. No cancellation code or false claims entered the tree. + +--- + +## Attention + +reviewed by gemini-3.7-flash-high +- **Coverage & Baseline**: Commits `400fbfe` to `69d22fe`; `decisions.tsv` Rows 51–54. Verified against parent T3 activity (positions 1171–2043) and test/build logs. +- **Hosted Environment (Row 37)**: Hosted Vercel preview unverified due to team 403 / CLI credentials; validation relies on production builds with localhost mock. +- **Mobile Hardware (Rows 24, 29)**: Mobile figures reflect Chromium emulation with 4× CPU throttling; physical mobile devices remain unmeasured. +- **Cancellation Claims (Row 54)**: The ~94% cancellation gain from `cancellation-audit-r4.md` is an unverified hypothesis and correctly omitted from reported achievements. diff --git a/scripts/benchmark-browser.cjs b/scripts/benchmark-browser.cjs new file mode 100644 index 0000000..deacf0e --- /dev/null +++ b/scripts/benchmark-browser.cjs @@ -0,0 +1,188 @@ +const { chromium } = require('playwright'); +const { parseArgs } = require('node:util'); +const fs = require('node:fs'); +const path = require('node:path'); +const assert = require('node:assert/strict'); + +const { values } = parseArgs({ options: { + baseline: { type: 'string', default: 'http://localhost:43127/' }, + current: { type: 'string', default: 'http://localhost:43130/' }, + mode: { type: 'string', default: 'first-open' }, + samples: { type: 'string', default: '5' }, + output: { type: 'string', default: '.scratch/browser-performance.json' }, + chrome: { type: 'string' }, + mobile: { type: 'boolean', default: false }, + cpu: { type: 'string', default: '1' }, +} }); +assert.ok(['first-open', 'interactions', 'offline'].includes(values.mode)); +const samples = Number(values.samples); +const cpu = Number(values.cpu); +assert.ok(Number.isInteger(samples) && samples > 0); +assert.ok(Number.isFinite(cpu) && cpu >= 1); +const output = path.resolve(values.output); +fs.mkdirSync(path.dirname(output), { recursive: true }); +const results = { measuredAt: new Date().toISOString(), mode: values.mode, samples, cpu, mobile: values.mobile, + method: 'Production builds, 730 prior days across 20 seeded habits (14600 CheckIns), same Chrome process with isolated contexts. First opens follow document reload with HTTP cache retained. Interactions alternate build order, include one observed localStorage write and two animation frames, and parse its value after timing. CPU throttling simulates slower execution; it is not physical mobile hardware.', variants: {} }; + +async function seed(page) { + await page.getByRole('dialog', { name: 'Select Profile' }).waitFor(); + await page.evaluate(() => { + const state = JSON.parse(localStorage.getItem('do_it_app_data_v2')); + const today = new Date(); + state.checkIns = []; + for (let day = 1; day <= 730; day++) { + const d = new Date(today); d.setDate(d.getDate() - day); + const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; + for (const h of state.habits) state.checkIns.push({ id: `${h.id}-${date}`, habitId: h.id, playerId: h.playerId, date, + pointsEarned: h.points, completedAt: `${date}T12:00:00Z`, ...(h.isQuantitative ? { quantity: h.maxQuantity } : {}) }); + } + state.activePlayerId = 'maciek'; state.soundEnabled = false; + localStorage.setItem('do_it_sound_enabled', 'false'); + localStorage.setItem('do_it_app_data_v2', JSON.stringify(state)); + }); + await page.reload(); + await page.getByText('Daily Ritual', { exact: true }).waitFor(); + await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))); +} + +async function openFeature(page, feature) { + return page.evaluate(async ({ feature, mobile }) => { + const start = performance.now(); + if (feature === 'Shortcuts') window.dispatchEvent(new KeyboardEvent('keydown', { key: '?', bubbles: true })); + else { + const button = feature === 'Settings' ? document.querySelector('button[aria-label="Open profile and settings"]') + : feature === 'Proof' ? document.querySelector('button[aria-label="Mark Clean Space completed"]') + : feature === 'Activity' ? Array.from(document.querySelectorAll('button')).find(b => b.innerText === 'Activity') + : Array.from(document.querySelectorAll(mobile ? 'nav[aria-label="Main navigation"] button' : 'aside button')).find(b => b.innerText.includes(feature)); + if (!button) throw new Error('Missing trigger ' + feature); + button.click(); + } + const ready = () => feature === 'Duel' ? document.body.innerText.includes('WEEKLY WAGER') + : feature === 'Activity' ? document.body.innerText.includes('The timeline') + : feature === 'Proof' ? Boolean(document.querySelector('#proof-modal-title')) + : feature === 'Shortcuts' ? Boolean(document.querySelector('#shortcuts-modal-title')) + : document.querySelector('h1')?.innerText === feature; + await new Promise((resolve, reject) => { + const deadline = performance.now() + 10000; + function check() { + if (ready()) resolve(); + else if (performance.now() > deadline) reject(new Error('Content timeout ' + feature)); + else setTimeout(check, 5); + } + check(); + }); + await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); + return { feature, ms: performance.now() - start }; + }, { feature, mobile: values.mobile }); +} + +async function checkIn(page, undo) { + return page.evaluate(async undo => { + const label = undo ? 'Mark Gym & Strength uncompleted for today' : 'Mark Gym & Strength completed'; + const button = document.querySelector(`button[aria-label="${label}"]`); + if (!button) throw new Error('Missing action ' + label); + const original = Storage.prototype.setItem; + let timeout; + const persisted = new Promise((resolve, reject) => { + timeout = setTimeout(() => reject(new Error('Persistence timeout')), 10000); + Storage.prototype.setItem = function(key, value) { + original.call(this, key, value); + if (this === localStorage && key === 'do_it_app_data_v2') resolve(value); + }; + }); + const start = performance.now(); + let saved; + try { + button.click(); + saved = await persisted; + await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); + } finally { + Storage.prototype.setItem = original; + clearTimeout(timeout); + } + const elapsed = performance.now() - start; + const state = JSON.parse(saved); + const d = new Date(); + const today = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; + const entry = state.checkIns.find(c => c.habitId === 'maciek-gym' && c.date === today); + return { action: undo ? 'undo' : 'check-in', ms: elapsed, checks: state.checkIns.length, points: entry?.pointsEarned ?? null }; + }, undo); +} + +async function pair(page) { + const checked = await checkIn(page, false); + assert.equal(checked.checks, 14601); assert.equal(checked.points, 40); + const undone = await checkIn(page, true); + assert.equal(undone.checks, 14600); assert.equal(undone.points, null); + return [checked, undone]; +} + +async function offline(variant) { + const { context, page } = variant; + for (const feature of ['Progress', 'Duel', 'Settings']) { + await page.goto(variant.url); await page.getByText('Daily Ritual', { exact: true }).waitFor(); + await openFeature(page, feature); + } + await page.goto(variant.url); await page.getByText('Daily Ritual', { exact: true }).waitFor(); + await context.setOffline(true); + try { + assert.equal(await page.evaluate(() => navigator.onLine), false); + await pair(page); + await openFeature(page, 'Progress'); + const today = page.getByRole('button', { name: values.mobile ? 'Today' : 'Today 1', exact: true }); + await today.click(); + await page.getByText('Daily Ritual', { exact: true }).waitFor(); + await openFeature(page, 'Duel'); + await today.click(); + await page.getByText('Daily Ritual', { exact: true }).waitFor(); + await openFeature(page, 'Settings'); + assert.equal(await page.getByRole('heading', { name: 'Settings', exact: true }).isVisible(), true); + return { localMutationAndUndo: true, warmedViews: ['Progress', 'Duel', 'Settings'] }; + } finally { await context.setOffline(false); } +} + +(async () => { + const browser = await chromium.launch({ headless: true, ...(values.chrome ? { executablePath: values.chrome } : {}) }); + results.browser = browser.version(); + const live = {}; + try { + for (const [name, url] of [['baseline', values.baseline], ['current', values.current]]) { + const context = await browser.newContext({ viewport: values.mobile ? { width: 390, height: 844 } : { width: 1280, height: 800 }, timezoneId: 'Europe/London', reducedMotion: 'reduce' }); + const page = await context.newPage(); + const cdp = await context.newCDPSession(page); + await cdp.send('Emulation.setCPUThrottlingRate', { rate: cpu }); + const errors = []; page.on('pageerror', error => errors.push(error.message)); + await page.goto(url); await seed(page); + live[name] = { context, page, url }; + results.variants[name] = { samples: [], errors }; + if (values.mode === 'interactions') { await pair(page); await page.waitForTimeout(650); } + } + if (values.mode === 'interactions') { + for (let sample = 1; sample <= samples; sample++) { + for (const name of (sample % 2 ? ['baseline', 'current'] : ['current', 'baseline'])) { + results.variants[name].samples.push(...(await pair(live[name].page)).map(row => ({ ...row, sample }))); + } + await live.current.page.waitForTimeout(550); + } + } else { + for (const [name, variant] of Object.entries(live)) { + if (values.mode === 'offline') results.variants[name].offline = await offline(variant); + else for (const feature of ['Progress', 'Duel', 'Settings', 'Activity', 'Proof', 'Shortcuts']) { + for (let sample = 1; sample <= samples; sample++) { + await variant.page.goto(variant.url); await variant.page.getByText('Daily Ritual', { exact: true }).waitFor(); + results.variants[name].samples.push({ ...await openFeature(variant.page, feature), sample }); + } + } + } + } + for (const [name, variant] of Object.entries(live)) { + await variant.page.goto(variant.url); await variant.page.getByText('Daily Ritual', { exact: true }).waitFor(); + const checkCount = await variant.page.evaluate(() => JSON.parse(localStorage.getItem('do_it_app_data_v2')).checkIns.length); + assert.equal(checkCount, 14600); + assert.deepEqual(results.variants[name].errors, []); + results.variants[name].persistedAfterReload = checkCount; + } + } catch (error) { results.failure = error.message; throw error; } + finally { fs.writeFileSync(output, JSON.stringify(results, null, 2)); await browser.close(); } + console.log(JSON.stringify({ output, passed: true })); +})().catch(error => { console.error(error); process.exitCode = 1; }); diff --git a/scripts/benchmark-performance.ts b/scripts/benchmark-performance.ts index d6b0f97..a7f2811 100644 --- a/scripts/benchmark-performance.ts +++ b/scripts/benchmark-performance.ts @@ -1,7 +1,9 @@ import { performance } from 'node:perf_hooks'; import { getInitialState } from '../src/lib/seed'; import { addDays, getTodayDateString, getWeekKey } from '../src/lib/date-utils'; -import { calculatePlayerScores } from '../src/lib/score-calculator'; +import { calculateHabitStreak, calculatePlayerScores, getWeeklyDailyDuelPoints } from '../src/lib/score-calculator'; +import { calculatePlayerBadges } from '../src/lib/badge-utils'; +import { indexCheckIns } from '../src/lib/check-in-index'; import { rebalanceAllWeeklyCheckIns, rebalanceWeeklyHabitCheckIns } from '../src/lib/weekly-utils'; import type { CheckIn } from '../src/lib/types'; @@ -38,6 +40,14 @@ bench('both player summaries', () => { calculatePlayerScores('maciek', checkIns, state.habits); calculatePlayerScores('myrna', checkIns, state.habits); }); +bench('Today habit streaks with shared index', () => { + const indexed = indexCheckIns(checkIns); + for (const habit of state.habits) { + if (habit.playerId === 'maciek' && habit.isActive) calculateHabitStreak(habit, checkIns, [], today, indexed); + } +}); +bench('weekly Duel day points', () => getWeeklyDailyDuelPoints(checkIns, today)); +bench('selected Player badges', () => calculatePlayerBadges('maciek', checkIns, state.habits, state.stakes, 365)); bench('hydrate weekly rebalance', () => rebalanceAllWeeklyCheckIns(checkIns, state.habits)); const habit = state.habits.find(habit => habit.weeklyTargetDays); diff --git a/scripts/verify-duel-performance.cjs b/scripts/verify-duel-performance.cjs new file mode 100644 index 0000000..b1079a9 --- /dev/null +++ b/scripts/verify-duel-performance.cjs @@ -0,0 +1,256 @@ +const { chromium } = require('playwright'); +const fs = require('node:fs'); +const path = require('node:path'); +const assert = require('node:assert/strict'); +const { parseArgs } = require('node:util'); +const { values } = parseArgs({ options: { + baseline: { type: 'string', default: 'http://localhost:43131/' }, + current: { type: 'string', default: 'http://localhost:43132/' }, + output: { type: 'string', default: '.scratch/duel-performance.json' }, + chrome: { type: 'string' }, + 'page-cap': { type: 'string', default: '1000' }, + 'history-days': { type: 'string', default: '125' }, + 'loading-only': { type: 'boolean', default: false }, + samples: { type: 'string', default: '1' }, + race: { type: 'boolean', default: false }, +} }); +const resultPath = path.resolve(values.output); +const root = path.dirname(resultPath); +fs.mkdirSync(root, { recursive: true }); +const pageCap = Number(values['page-cap']); +assert.ok(Number.isInteger(pageCap) && pageCap > 0); +const raceOnly = values.race; +const historyDays = Number(values['history-days']); +const samples = Number(values.samples); +assert.ok(Number.isInteger(historyDays) && historyDays > 0); +assert.ok(Number.isInteger(samples) && samples > 0); +assert.ok(samples === 1 || values['loading-only'], 'Repeated samples require loading-only mode'); + +const initial = require(path.resolve('.scratch/test-build/seed.js')).getInitialState(); +const owner = '11111111-1111-4111-8111-111111111111'; +const guest = '22222222-2222-4222-8222-222222222222'; +const duelId = '33333333-3333-4333-8333-333333333333'; +const user = { id: owner, email: 'performance-owner@local.test', aud: 'authenticated', role: 'authenticated', app_metadata: {}, user_metadata: {}, created_at: '2026-01-01T00:00:00Z' }; +const duel = { id: duelId, owner_id: owner, guest_id: guest, owner_name: 'Perf Owner', guest_name: 'Perf Guest', owner_color: 'blue', guest_color: 'purple', invite_code: '44444444-4444-4444-8444-444444444444' }; +const historyCount = initial.habits.length * historyDays; +const results = { measuredAt: new Date().toISOString(), pageCap, historyCount, samples, method: `Production copies configured only for a Playwright-routed localhost Supabase mock. ${historyCount} CheckIns, 50ms per HTTP response, real Phoenix subscriptions/events, no live database or auth. New browser context per sample. Initial rendering measured inside the page from navigation start to Daily Ritual and a Check-in control, followed by two animation frames. Counts verify reload amplification; loading times include localhost JS and rendering, not hosted latency.`, variants: {} }; + +function makeRows() { + const checks = []; + const today = new Date(); + for (let day = 1; day <= historyDays; day++) { + const d = new Date(today); d.setDate(d.getDate() - day); + const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; + for (const h of initial.habits) checks.push({ id: `${h.id}-${date}`, habitId: h.id, playerId: h.playerId, date, pointsEarned: h.points, completedAt: `${date}T12:00:00Z` }); + } + return { duel_habits: initial.habits, duel_check_ins: checks, duel_stakes: [], duel_reactions: [], duel_rest_days: [] }; +} + +async function until(page, predicate, label) { + const end = Date.now() + 10000; + while (!predicate()) { + if (Date.now() > end) throw new Error('Timeout ' + label); + await page.waitForTimeout(10); + } +} + +async function verify(browser, name, url) { + const context = await browser.newContext({ timezoneId: 'Europe/London', reducedMotion: 'reduce' }); + const page = await context.newPage(); + const rows = makeRows(); + const queries = []; + const writes = []; + const subscriptions = []; + const errors = []; + let phase = 'initial'; + let active = 0; + const activeByTable = {}; + const peakByTable = {}; + let lastQueryAt = 0; + page.on('pageerror', error => errors.push(error.message)); + await page.addInitScript(({ user, token }) => { + localStorage.setItem('sb-127-auth-token', JSON.stringify({ access_token: token, refresh_token: 'local-mock-refresh', token_type: 'bearer', expires_in: 86400, expires_at: Math.floor(Date.now()/1000)+86400, user })); + localStorage.setItem('do_it_sound_enabled', 'false'); + localStorage.setItem('do_it_app_data_v2', 'local-history-sentinel'); + let scheduled = false; + const observer = new MutationObserver(() => { + if (scheduled || !document.body?.innerText.includes('Daily Ritual') || !document.querySelector('button[aria-label="Mark Gym & Strength completed"]')) return; + scheduled = true; + observer.disconnect(); + requestAnimationFrame(() => requestAnimationFrame(() => { window.initialRitualMs = performance.now(); })); + }); + observer.observe(document, { subtree: true, childList: true, attributes: true }); + }, { user, token: 'eyJhbGciOiJub25lIn0.' + Buffer.from(JSON.stringify({ sub: owner, exp: Math.floor(Date.now()/1000)+86400, role: 'authenticated' })).toString('base64url') + '.local-mock' }); + + await page.route('http://127.0.0.1:54321/**', async route => { + const request = route.request(); + const url = new URL(request.url()); + const table = url.pathname.split('/').pop(); + const headers = { 'content-type': 'application/json', 'access-control-allow-origin': '*', 'access-control-expose-headers': 'content-range' }; + if (table === 'duels') return route.fulfill({ status: 200, headers, body: JSON.stringify([duel]) }); + if (url.pathname === '/auth/v1/user') return route.fulfill({ status: 200, headers, body: JSON.stringify(user) }); + if (!(table in rows)) throw new Error('Unexpected mock request ' + url.pathname); + if (request.method() === 'POST') { + const body = request.postDataJSON(); + for (const row of Array.isArray(body) ? body : [body]) { + assert.equal(row.duel_id, duelId); + const index = rows[table].findIndex(entry => entry.id === row.id); + if (index === -1) rows[table].push(row.data); else rows[table][index] = row.data; + writes.push({ table, method: 'POST', row }); + } + return route.fulfill({ status: 201, headers, body: '' }); + } + assert.equal(url.searchParams.get('duel_id'), 'eq.' + duelId); + if (request.method() === 'DELETE') { + const id = url.searchParams.get('id').slice(3); + rows[table] = rows[table].filter(entry => entry.id !== id); + writes.push({ table, method: 'DELETE', id }); + return route.fulfill({ status: 204, headers, body: '' }); + } + assert.equal(request.method(), 'GET'); + assert.equal(url.searchParams.get('select'), 'data'); + assert.equal(url.searchParams.get('order'), 'id.asc'); + const start = Number(url.searchParams.get('offset') || 0); + const limit = Number(url.searchParams.get('limit') || 1000); + const sorted = [...rows[table]].sort((a,b) => a.id.localeCompare(b.id)); + const selected = sorted.slice(start, start + Math.min(limit, pageCap)).map(data => ({ data })); + const exact = request.headers().prefer?.includes('count=exact') ?? false; + queries.push({ phase, table, start, limit, exact, rows: selected.length }); + lastQueryAt = Date.now(); + active++; + activeByTable[table] = (activeByTable[table] ?? 0) + 1; + peakByTable[table] = Math.max(peakByTable[table] ?? 0, activeByTable[table]); + try { + await new Promise(resolve => setTimeout(resolve, 50)); + headers['content-range'] = `${start}-${Math.max(start, start + selected.length - 1)}/${exact ? sorted.length : '*'}`; + await route.fulfill({ status: 200, headers, body: JSON.stringify(selected) }); + } finally { active--; activeByTable[table]--; } + }); + + await page.routeWebSocket('ws://127.0.0.1:54321/**', socket => { + socket.onMessage(raw => { + const [joinRef, ref, topic, event, payload] = JSON.parse(String(raw)); + if (event === 'phx_join') { + const filters = (payload.config?.postgres_changes ?? []).map((filter, index) => ({ ...filter, id: index + 1 })); + subscriptions.push({ socket, topic, joinRef, filters }); + socket.send(JSON.stringify([joinRef, ref, topic, 'phx_reply', { status: 'ok', response: { postgres_changes: filters } }])); + } else if (event === 'heartbeat' || event === 'phx_leave') { + socket.send(JSON.stringify([joinRef, ref, topic, 'phx_reply', { status: 'ok', response: {} }])); + } + }); + }); + + function notify(count) { + const subscription = subscriptions.find(entry => entry.topic === 'realtime:duel-' + duelId); + assert.ok(subscription, 'Duel subscription must exist'); + const filter = subscription.filters.find(entry => entry.table === 'duel_check_ins'); + assert.ok(filter); + for (let i = 0; i < count; i++) { + subscription.socket.send(JSON.stringify([subscription.joinRef, null, subscription.topic, 'postgres_changes', { ids: [filter.id], data: { schema: 'public', table: 'duel_check_ins', commit_timestamp: new Date().toISOString(), type: 'UPDATE', errors: [], columns: [], record: {}, old_record: {} } }])); + } + } + + try { + await page.goto(url); + await page.getByText('Daily Ritual', { exact: true }).waitFor(); + await until(page, () => active === 0 && Date.now() - lastQueryAt > 250 && subscriptions.some(s => s.topic === 'realtime:duel-' + duelId), 'initial load'); + const initialQueries = queries.filter(q => q.phase === 'initial'); + const checkPages = Math.ceil(historyCount / pageCap); + assert.equal(initialQueries.filter(q => q.table === 'duel_check_ins').length, checkPages); + assert.equal(initialQueries.filter(q => q.exact).length, name === 'current' ? 5 : checkPages + 4); + assert.equal(await page.evaluate(() => localStorage.getItem('do_it_app_data_v2')), 'local-history-sentinel'); + + if (values['loading-only']) { + await page.waitForFunction(() => typeof window.initialRitualMs === 'number'); + const initialRitualMs = await page.evaluate(() => window.initialRitualMs); + await page.getByRole('tab', { name: 'Activity', exact: true }).click(); + await page.waitForFunction(() => document.body.innerText.includes('The timeline'), null, { timeout: 5000 }); + assert.equal(await page.getByText(String(historyCount), { exact: true }).count() > 0, true); + assert.deepEqual(errors, []); + return { initialRitualMs, initialQueries, peakByTable, visibleHistory: historyCount, errors }; + } + + if (raceOnly) { + phase = 'race'; + await page.evaluate(() => { + window.raceTrace = []; + let checkedBefore = false; + let last; + window.raceObserver = new MutationObserver(() => { + const checked = Boolean(document.querySelector('button[aria-label="Mark Gym & Strength uncompleted for today"]')); + if (checked) checkedBefore = true; + if (checkedBefore && checked !== last) window.raceTrace.push({ checked, ms: performance.now() }); + last = checked; + }); + window.raceObserver.observe(document.body, { subtree: true, childList: true, attributes: true }); + }); + notify(1); + await until(page, () => queries.some(q => q.phase === 'race'), 'race read starts'); + await page.getByRole('button', { name: 'Mark Gym & Strength completed', exact: true }).click(); + await until(page, () => writes.some(w => w.method === 'POST'), 'race write'); + notify(1); + await until(page, () => active === 0 && Date.now() - lastQueryAt > 250, 'race quiescence'); + const trace = await page.evaluate(() => { window.raceObserver.disconnect(); return window.raceTrace; }); + assert.equal(rows.duel_check_ins.length, historyCount + 1); + const finalChecked = await page.getByRole('button', { name: 'Mark Gym & Strength uncompleted for today', exact: true }).count() === 1; + assert.equal(finalChecked, true); + assert.equal(trace.some(entry => !entry.checked), name === 'baseline'); + assert.deepEqual(errors, []); + return { queries: queries.filter(q => q.phase === 'race'), trace, finalChecked, errors }; + } + + phase = 'burst'; + notify(20); + await until(page, () => queries.some(q => q.phase === 'burst'), 'burst first request'); + await until(page, () => active === 0 && Date.now() - lastQueryAt > 250, 'burst completion'); + const burst = queries.filter(q => q.phase === 'burst'); + assert.equal(burst.filter(q => q.table === 'duel_check_ins' && q.start === 0).length, name === 'current' ? 1 : 20); + assert.equal(burst.filter(q => q.exact).length, name === 'current' ? 5 : 20 * (checkPages + 4)); + + phase = 'during-load'; + notify(1); + await until(page, () => queries.some(q => q.phase === 'during-load'), 'follow-up first request'); + notify(5); + await until(page, () => queries.filter(q => q.phase === 'during-load' && q.table === 'duel_check_ins' && q.start === 0).length >= 2, 'follow-up request'); + await until(page, () => active === 0 && Date.now() - lastQueryAt > 250, 'follow-up completion'); + const during = queries.filter(q => q.phase === 'during-load'); + assert.equal(during.filter(q => q.table === 'duel_check_ins' && q.start === 0).length, name === 'current' ? 2 : 6); + + phase = 'mutation'; + await page.getByRole('button', { name: 'Mark Gym & Strength completed', exact: true }).click(); + await until(page, () => writes.some(w => w.method === 'POST'), 'check-in write'); + const entry = rows.duel_check_ins.find(c => c.habitId === 'maciek-gym' && c.date === new Date().toLocaleDateString('en-CA')); + assert.equal(entry?.pointsEarned, 40); + await page.getByRole('button', { name: 'Mark Gym & Strength uncompleted for today', exact: true }).click(); + await until(page, () => writes.some(w => w.method === 'DELETE'), 'undo write'); + assert.equal(rows.duel_check_ins.length, historyCount); + assert.equal(await page.evaluate(() => localStorage.getItem('do_it_app_data_v2')), 'local-history-sentinel'); + + fs.writeFileSync(path.join(root, `authenticated-${name}-before-activity.txt`), await page.locator('body').innerText()); + await page.screenshot({ path: path.join(root, `authenticated-${name}-before-activity.png`), fullPage: true }); + await page.getByRole('tab', { name: 'Activity', exact: true }).click({ timeout: 5000 }); + await page.waitForFunction(() => document.body.innerText.includes('The timeline'), null, { timeout: 5000 }); + assert.equal(await page.getByText(String(historyCount), { exact: true }).count() > 0, true); + assert.deepEqual(errors, []); + return { initialQueries, burst, during, writes: writes.map(w => ({ table: w.table, method: w.method })), errors, localHistoryUntouched: true, visibleHistory: historyCount }; + } finally { await context.close(); } +} + +(async () => { + const browser = await chromium.launch({ headless: true, ...(values.chrome ? { executablePath: values.chrome } : {}) }); + results.browser = browser.version(); + try { + for (let sample = 0; sample < samples; sample++) { + const variants = [['baseline', values.baseline], ['current', values.current]]; + if (sample % 2) variants.reverse(); + for (const [name, url] of variants) { + const result = await verify(browser, name, url); + if (samples === 1) results.variants[name] = result; + else (results.variants[name] ??= []).push(result); + console.log(JSON.stringify({ name, sample: sample + 1, passed: true, initialRitualMs: result.initialRitualMs })); + fs.writeFileSync(resultPath, JSON.stringify(results, null, 2)); + } + } + } catch (error) { results.failure = error.message; throw error; } + finally { fs.writeFileSync(resultPath, JSON.stringify(results, null, 2)); await browser.close(); } +})().catch(error => { console.error(error); process.exitCode = 1; }); diff --git a/scripts/verify-entry-performance.cjs b/scripts/verify-entry-performance.cjs new file mode 100644 index 0000000..1c6b140 --- /dev/null +++ b/scripts/verify-entry-performance.cjs @@ -0,0 +1,118 @@ +const { chromium } = require('playwright'); +const fs = require('node:fs'); +const path = require('node:path'); +const assert = require('node:assert/strict'); +const { parseArgs } = require('node:util'); +const { values } = parseArgs({ options: { + baseline: { type:'string', default:'http://localhost:43131/' }, + current: { type:'string', default:'http://localhost:43132/' }, + output: { type:'string', default:'.scratch/entry-performance.json' }, + chrome: { type:'string' }, + smoke: { type:'boolean', default:false }, +} }); +const after = values.smoke; +const resultPath = path.resolve(values.output); +fs.mkdirSync(path.dirname(resultPath),{recursive:true}); +const results = { measuredAt: new Date().toISOString(), method: 'Configured production copies, isolated contexts, empty local auth for default account creation or a synthetic account with an empty Duel for onboarding. First visit warms HTTP cache; five subsequent document reloads measure navigation start through a browser-local heading readiness probe and two animation frames. Mock requests take 50ms. No accounts are created.', variants: {} }; +const owner = '11111111-1111-4111-8111-111111111111'; +const guest = '22222222-2222-4222-8222-222222222222'; +const user = { id: owner, email: 'entry-owner@local.test', aud: 'authenticated', role: 'authenticated', app_metadata: {}, user_metadata: {}, created_at: '2026-01-01T00:00:00Z' }; +const duel = { id: '33333333-3333-4333-8333-333333333333', owner_id: owner, guest_id: guest, owner_name: 'Entry Owner', guest_name: 'Entry Guest', owner_color: 'blue', guest_color: 'purple', invite_code: '44444444-4444-4444-8444-444444444444' }; + +async function prepare(page, mode) { + const savedHabits = []; + const writes = []; + if (mode === 'onboarding') await page.addInitScript(({ user, token }) => { + localStorage.setItem('sb-127-auth-token', JSON.stringify({ access_token: token, refresh_token: 'local-mock-refresh', token_type: 'bearer', expires_in: 86400, expires_at: Math.floor(Date.now()/1000)+86400, user })); + }, { user, token: 'eyJhbGciOiJub25lIn0.' + Buffer.from(JSON.stringify({ sub: owner, exp: Math.floor(Date.now()/1000)+86400 })).toString('base64url') + '.local-mock' }); + await page.route('http://127.0.0.1:54321/**', async route => { + const url = new URL(route.request().url()); + await new Promise(resolve => setTimeout(resolve, 50)); + const table = url.pathname.split('/').pop(); + if (table === 'duels') return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify([duel]) }); + assert.ok(['duel_habits','duel_check_ins','duel_stakes','duel_reactions','duel_rest_days'].includes(table)); + if (route.request().method()==='POST') { + assert.equal(table,'duel_habits'); + const body=route.request().postDataJSON(); + for(const row of Array.isArray(body)?body:[body]) { + assert.equal(row.duel_id,duel.id); + assert.equal(row.player_slot,'maciek'); + savedHabits.push(row.data); writes.push({id:row.id,player:row.player_slot}); + } + return route.fulfill({status:201,contentType:'application/json',body:''}); + } + const data=table==='duel_habits'?savedHabits.map(data=>({data})):[]; + await route.fulfill({ status: 200, headers: { 'content-type':'application/json', 'content-range':data.length?`0-${data.length-1}/${data.length}`:'*/0', 'access-control-allow-origin':'*', 'access-control-expose-headers':'content-range' }, body: JSON.stringify(data) }); + }); + return {savedHabits,writes}; + await page.routeWebSocket('ws://127.0.0.1:54321/**', socket => { + socket.onMessage(raw => { + const [joinRef, ref, topic, event, payload] = JSON.parse(String(raw)); + if (event === 'phx_join') { + const filters = (payload.config?.postgres_changes ?? []).map((filter,index) => ({ ...filter, id:index+1 })); + socket.send(JSON.stringify([joinRef,ref,topic,'phx_reply',{status:'ok',response:{postgres_changes:filters}}])); + } else if (event === 'heartbeat' || event === 'phx_leave') socket.send(JSON.stringify([joinRef,ref,topic,'phx_reply',{status:'ok',response:{}}])); + }); + }); +} + +(async () => { + const browser = await chromium.launch({ headless:true, ...(values.chrome ? {executablePath:values.chrome} : {}) }); + results.browser = browser.version(); + try { + for (const [name,url] of [['baseline',values.baseline],['current',values.current]]) { + results.variants[name] = {}; + for (const mode of ['account-entry','onboarding']) { + const context = await browser.newContext({ viewport:{width:1280,height:800}, timezoneId:'Europe/London', reducedMotion:'reduce' }); + const page = await context.newPage(); + const errors=[]; page.on('pageerror',error => errors.push(error.message)); + const fixture=await prepare(page,mode); + const heading = mode === 'account-entry' ? 'Create your account' : 'Choose your habits.'; + await page.goto(url); await page.getByRole('heading',{name:heading,exact:true}).waitFor(); + const samples=[]; + for(let sample=1;sample<=5;sample++) { + await page.goto(url,{waitUntil:'domcontentloaded'}); + const ms=await page.evaluate(async heading => { + await new Promise((resolve,reject) => { + const deadline = performance.now()+10000; + function probe() { + const ready=Array.from(document.querySelectorAll('h1')).some(h=>h.textContent.trim()===heading && h.getClientRects().length>0); + if(ready) resolve(); + else if(performance.now()>deadline) reject(new Error('Heading timeout '+heading)); + else setTimeout(probe,5); + } + probe(); + }); + await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); + return performance.now(); + },heading); + samples.push({sample,ms}); + } + assert.deepEqual(errors,[]); + let smoke; + if(after && name==='current') { + if(mode==='account-entry') { + await page.getByRole('button',{name:'Already have an account? Sign in',exact:true}).click(); + await page.getByRole('heading',{name:'Welcome back',exact:true}).waitFor(); + await page.getByRole('button',{name:'Need an account? Sign up',exact:true}).click(); + await page.getByRole('heading',{name:'Create your account',exact:true}).waitFor(); + smoke={modeSwitch:true}; + } else { + await page.getByRole('textbox',{name:'New habit name',exact:true}).fill('Timing smoke habit'); + await page.getByRole('button',{name:'Add',exact:true}).click(); + await page.getByRole('button',{name:'Start with these habits',exact:true}).click(); + await page.getByRole('heading',{name:'Daily Ritual',exact:true}).waitFor(); + assert.ok(fixture.savedHabits.some(h=>h.title==='Timing smoke habit' && h.playerId==='maciek')); + smoke={savedPlan:true,ownerWrites:fixture.writes.length}; + } + } + assert.deepEqual(errors,[]); + results.variants[name][mode] = { samples,errors,smoke }; + await context.close(); + } + fs.writeFileSync(resultPath,JSON.stringify(results,null,2)); + console.log(JSON.stringify({name,passed:true})); + } + } catch(error) {results.failure=error.message;throw error;} + finally {fs.writeFileSync(resultPath,JSON.stringify(results,null,2));await browser.close();} +})().catch(error => {console.error(error);process.exitCode=1;}); diff --git a/src/app/page.tsx b/src/app/page.tsx index d6d828f..5a66329 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,6 +2,7 @@ import React, { Suspense, useCallback, useEffect, useRef, useState } from 'react'; import dynamic from 'next/dynamic'; +import { createDeferredView } from '@/components/DeferredView'; import { loadCurrentPage } from '@/lib/load-current-page'; import { useSearchParams } from 'next/navigation'; import { useStore } from '@/lib/store'; @@ -25,13 +26,18 @@ function ViewLoading() { return

Loading...

; } -const DuelView = dynamic(() => loadCurrentPage(() => import('@/components/DuelView')).then(module => module.DuelView), { loading: ViewLoading }); -const ProgressView = dynamic(() => loadCurrentPage(() => import('@/components/ProgressView')).then(module => module.ProgressView), { loading: ViewLoading }); -const SettingsView = dynamic(() => loadCurrentPage(() => import('@/components/SettingsView')).then(module => module.SettingsView), { loading: ViewLoading }); -const MultiplayerGate = dynamic(() => loadCurrentPage(() => import('@/components/MultiplayerGate')).then(module => module.MultiplayerGate), { loading: ViewLoading }); +const { View: DuelView, preload: preloadDuel } = createDeferredView(() => import('@/components/DuelView').then(module => module.DuelView)); +const { View: ProgressView, preload: preloadProgress } = createDeferredView(() => import('@/components/ProgressView').then(module => module.ProgressView)); +const { View: SettingsView, preload: preloadSettings } = createDeferredView(() => import('@/components/SettingsView').then(module => module.SettingsView)); + +function preloadTab(tab: TabType) { + if (tab === 'duel') preloadDuel(); + if (tab === 'progress') preloadProgress(); +} +const { View: MultiplayerGate } = createDeferredView(() => import('@/components/MultiplayerGate').then(module => module.MultiplayerGate)); const ExistingDuelInviteGate = dynamic(() => loadCurrentPage(() => import('@/components/ExistingDuelInviteGate')).then(module => module.ExistingDuelInviteGate), { loading: ViewLoading }); -const HabitOnboarding = dynamic(() => loadCurrentPage(() => import('@/components/HabitOnboarding')).then(module => module.HabitOnboarding), { loading: ViewLoading }); -const KeyboardShortcutsModal = dynamic(() => loadCurrentPage(() => import('@/components/KeyboardShortcutsModal')).then(module => module.KeyboardShortcutsModal), { loading: ViewLoading }); +const { View: HabitOnboarding } = createDeferredView(() => import('@/components/HabitOnboarding').then(module => module.HabitOnboarding)); +const { View: KeyboardShortcutsModal } = createDeferredView(() => import('@/components/KeyboardShortcutsModal').then(module => module.KeyboardShortcutsModal), null); function AppContent() { const { @@ -245,8 +251,8 @@ function AppContent() { {/* App frame */}
Skip to content - { setShowSettings(false); changeTab(tab); }} /> -
setShowSettings(true)} /> + { setShowSettings(false); changeTab(tab); }} /> +
setShowSettings(true)} onWarmSettings={preloadSettings} /> {/* Floating Quick Action Toast */} {toastMessage && (
} - { setShowSettings(false); setOpenPlanner(false); changeTab(tab); }} /> + { setShowSettings(false); setOpenPlanner(false); changeTab(tab); }} /> {isShortcutsModalOpen && { - const timelineItems: TimelineItem[] = []; - const habitById = new Map(habits.map(habit => [habit.id, habit])); - - checkIns.forEach((ci) => { - const habit = habitById.get(ci.habitId); - const player = players[ci.playerId]; - if (habit && player) { - timelineItems.push({ - type: 'check_in', - id: ci.id, - timestamp: ci.completedAt || `${ci.date}T12:00:00.000Z`, - date: ci.date, - checkIn: ci, - habit, - player, - }); - } - }); - - reactions.forEach((r) => { - const fromPlayer = players[r.fromPlayerId]; - const toPlayer = players[r.toPlayerId]; - if (fromPlayer && toPlayer) { - timelineItems.push({ - type: 'reaction', - id: r.id, - timestamp: r.timestamp, - date: formatDateString(new Date(r.timestamp)), - fromPlayer, - toPlayer, - emoji: r.emoji, - message: r.message, - }); - } - }); - - timelineItems.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); + const timelineItems = useMemo( + () => buildTimelineItems({ checkIns, habits, players, reactions }), + [checkIns, habits, players, reactions] + ); - return timelineItems; - }, [checkIns, habits, players, reactions]); + const filteredItems = useMemo( + () => + filterTimelineItems(timelineItems, { + feedFilter, + playerFilter, + activePlayerId, + partnerId, + search, + }), + [timelineItems, feedFilter, playerFilter, activePlayerId, partnerId, search] + ); - const filteredItems = useMemo(() => { - const query = search.trim().toLowerCase(); - return timelineItems.filter((item) => { - if (feedFilter !== 'all' && item.type !== feedFilter) return false; - const actorId = item.type === 'check_in' ? item.player.id : item.fromPlayer.id; - if (playerFilter === 'me' && actorId !== activePlayerId) return false; - if (playerFilter === 'partner' && actorId !== partnerId) return false; - const content = item.type === 'check_in' - ? `${item.player.name} ${item.habit.title} ${item.checkIn.note || ''}` - : `${item.fromPlayer.name} ${item.toPlayer.name} ${item.message}`; - return content.toLowerCase().includes(query); - }); - }, [timelineItems, feedFilter, playerFilter, activePlayerId, partnerId, search]); const visibleItems = filteredItems.slice(0, visibleCount); - const checkInCount = timelineItems.filter((item) => item.type === 'check_in').length; + const checkInCount = useMemo( + () => timelineItems.filter((item) => item.type === 'check_in').length, + [timelineItems] + ); const inlineReply = (itemId: string) => replyTo === itemId && partner ? (
{ diff --git a/src/components/BottomNav.tsx b/src/components/BottomNav.tsx index 43647b6..9f81910 100644 --- a/src/components/BottomNav.tsx +++ b/src/components/BottomNav.tsx @@ -11,9 +11,10 @@ export type TabType = 'today' | 'duel' | 'progress'; interface BottomNavProps { activeTab: TabType; onChangeTab: (tab: TabType) => void; + onWarmTab?: (tab: TabType) => void; } -export function BottomNav({ activeTab, onChangeTab }: BottomNavProps) { +export function BottomNav({ activeTab, onChangeTab, onWarmTab }: BottomNavProps) { const tabs = [ { id: 'today' as TabType, label: 'Today', icon: CheckCircle2 }, { id: 'duel' as TabType, label: 'Duel', icon: Flame }, @@ -42,6 +43,8 @@ export function BottomNav({ activeTab, onChangeTab }: BottomNavProps) {