Skip to content

feat(performance): enforce quality and homepage budgets - #107

Merged
cameroncuster merged 1 commit into
mainfrom
cameroncuster/quality-performance-program
Jul 25, 2026
Merged

feat(performance): enforce quality and homepage budgets#107
cameroncuster merged 1 commit into
mainfrom
cameroncuster/quality-performance-program

Conversation

@cameroncuster

Copy link
Copy Markdown
Owner

Summary

Completes the quality/performance program: decomposes required CI into parallel self-contained jobs, enforces production TypeScript coverage and asset/HTML budgets, reduces initial homepage rendering to an accessible 50-row batch, narrows homepage database payloads, strengthens authorization/query/provider test seams, removes unused code/dependencies, and trims low-value comments while preserving security and accessibility rationale.

77 files changed, +6,168 / −2,737

Results

Measure Result
Unit tests 377 passed
Coverage 98.69% lines / 95.17% branches / 98.18% functions
Mocked desktop E2E 88 passed / 4 intentional skips
Mocked mobile E2E 91 passed / 1 intentional skip
Immutable JS/CSS 142,637 B gzip across 38 assets
Largest JS asset 51,950 B gzip
Largest CSS asset 8,597 B gzip
Initial homepage fixture 50 rows, 21,367 B raw / 1,610 B gzip
Security audits 0 npm findings / 0 Python findings

What Changed

CI and quality gates

  • Split Node 24 validation into parallel quality, coverage, build/performance, mocked desktop/mobile, and optional live desktop/mobile jobs.
  • Kept each Playwright process at one worker so the shared mock scenario remains deterministic.
  • Added a final required build aggregation job while retaining Node 26 as non-blocking compatibility coverage.
  • Added recursive runtime TypeScript discovery and Node loader hooks so coverage includes production src/**/*.ts modules and excludes declarations only.
  • Ratcheted thresholds to 98% lines, 95% branches, and 98% functions.
  • Added immutable JS/CSS and deterministic homepage HTML performance budgets.

Homepage performance

  • Reuses server-loaded problem data during hydration instead of issuing a duplicate client read.
  • Publicly caches only successful homepage reads; failed reads use private, no-store.
  • Narrows the problem query to fields the homepage actually renders.
  • Renders the first 50 derived rows and reveals subsequent rows in bounded client-side batches without another database request.
  • Preserves filtering/sorting over the complete collection and profile pages' existing unlimited behavior.
  • Adds accessible show-more progress, focus behavior, and aria-controls linkage.

Architecture and code quality

  • Adds dependency-injected query, authorization, submit-provider, theme, user, and runtime-composition seams with failure-path coverage.
  • Preserves generated Database typing across Supabase clients and query boundaries.
  • Removes unused src/lib/theme.js, dotenv, and glob dependencies.
  • Simplifies provider adapter composition without weakening server-side authorization or URL validation.
  • Removes localized narrational CSS/markup comments while retaining non-obvious security, SSR, race-condition, contrast, and test-isolation rationale.

New files

  • e2e/homepage-pagination.spec.ts — verifies 50-row SSR, no hydration refetch, full-collection filtering/sorting, and accessible batching.
  • scripts/coverage-hooks.mjs / scripts/coverage-scope.mjs — resolve SvelteKit virtual modules and discover runtime TypeScript modules.
  • scripts/performance-budget.mjs / scripts/check-performance-budget.mjs — measure and enforce production budgets.
  • src/lib/components/problemPagination.ts — pure bounded-batch calculations.
  • New API, authorization, coverage, homepage, performance, provider, theme, and user service regression suites under tests/.

Architecture

The server remains the owner of initial homepage data. +page.server.ts returns a typed query result and selects cache policy from explicit success/failure state. SvelteKit serializes that result into hydration; ProblemDisplay seeds its existing collection from it and limits only the rendered projection. Filters and sorting still operate on all loaded rows, and “show more” changes presentation state only.

Coverage stays dependency-free by using Node 24's loader hooks and built-in test coverage. Performance enforcement reads the existing Vercel build output, so the dedicated build job does not rebuild. CI parallelizes isolated runners, not workers inside a shared mock process.

Testing

Validated on the final rebased commit:

  • python -m pip install --require-hashes -r scripts/requirements.txt
  • PYTHONPATH=scripts python -m unittest scripts/test_classify_problems.py
  • pnpm install --frozen-lockfile
  • pnpm run lint
  • pnpm run lint:es
  • pnpm run check
  • pnpm run test:coverage — 377 passed; 98.69 / 95.17 / 98.18
  • pnpm run build
  • pnpm run check:performance
  • CI=1 pnpm exec playwright test --project=mocked-desktop --workers=1 — 88 passed, 4 skipped
  • CI=1 pnpm exec playwright test --project=mocked-mobile --workers=1 — 91 passed, 1 skipped
  • pnpm audit --audit-level low — no findings
  • pip-audit -r scripts/requirements.txt — no findings

What's NOT Changed

  • No database migration, RLS policy, production secret, or write authorization behavior changes.
  • No speculative CSS preload change: probes showed each stylesheet loads once, and removing preload behavior would add a rendering waterfall.
  • No EventEmitter/listener-limit workaround: MaxListenersExceededWarning, ObjectMultiplex, and contentscript.js messages were reproduced as browser-extension output, not application output.
  • No parallel Playwright workers inside a process; mock state remains serialized.
  • No broad visual redesign or responsive/theme behavior change.

Risk Analysis

🔴 P0 — Core authorization and writes

  • Authenticated API and submission boundaries — dependency injection and generated Supabase typing now meet at authorization/query seams.
    • API routes reject missing/invalid sessions and unsafe inputs.
    • Admin and user writes remain fail-closed across query, RPC, and thrown failures.

🟠 P1 — Homepage loading and interaction

  • SSR/cache behavior — successful reads may be edge-cached; failed reads must never be cached.
    • Successful non-empty/empty reads use public cache; failures use private, no-store.
  • Pagination/filtering/sorting — only 50 rows render initially, while all rows remain available to collection logic.
    • The 280-row E2E fixture filters and sorts beyond the first batch without a hydration refetch.
    • Desktop and mobile mocked suites pass with one worker.

🟡 P2 — CI and enforcement

  • Coverage discovery — recursive inventory imports every runtime module and enforces 98/95/98 thresholds.
  • Performance budgets — asset collection, fixture measurement, and exceeded-limit reporting are tested.
  • Parallel CI graph — required jobs aggregate while optional live smoke exits safely when unconfigured.

🟢 P3 — Cleanup

  • Dead dependency/code and comment removal — frozen install, formatting, lint, type checking, build, and runtime-composition tests pass.
Risk Level Areas Items
P0 Authorization and writes 1
P1 Homepage cache and interaction 2
P2 CI, coverage, performance 3
P3 Cleanup 1

Pull Request opened by Augment Code | View session

@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gitgud Ready Ready Preview, Comment Jul 25, 2026 4:48am

@cameroncuster
cameroncuster marked this pull request as ready for review July 25, 2026 16:19
@cameroncuster
cameroncuster merged commit bf5e855 into main Jul 25, 2026
14 checks passed
@cameroncuster
cameroncuster deleted the cameroncuster/quality-performance-program branch July 25, 2026 16:19
@cameroncuster

cameroncuster commented Jul 25, 2026

Copy link
Copy Markdown
Owner Author

PR Risk Analyzer Agent🛡️

👀 Human Input Needed
A pair-review briefing is ready for you.

→ Pair Review Briefing

@cameroncuster

Copy link
Copy Markdown
Owner Author

🛰️ Verifier Report

TL;DR: Drove the homepage's new 50-row SSR + client-side "show more" batching, narrowed problem query, and success/failure cache policy end-to-end against a production build served on a 280-row fixture, and reran the full mocked desktop + mobile suites.

🔍 Walkthrough

  • Homepage ships 50 rows server-side and reveals the rest in client batches → live browser: initial 50 of 280, clicked through to all 280 with the final control disabled and focused; the 280-row E2E fixture passes.

    SSR row count, accessible control, and end-state read live from the running page
    • SSR HTML <tbody id="problem-table-body"> contains exactly 50 <tr>; button aria-label="Show 50 more problems", type="button", aria-controls="problem-table-body"; a role="status" region reads 50 of 280 problems shown.
    • After revealing every batch: 280 rows, status 280 of 280 problems shown, final button aria-label="All problems shown" with aria-disabled="true" and focus moved to it.
    { rows: 280, status: "280 of 280 problems shown",
      finalLabel: "All problems shown", finalDisabled: "true", focused: true }
    
    table ends at row 50 with 'Show 50 more' and '50 of 280 problems shown' all 280 rows revealed, deep past row 1096
  • Revealing all rows issues zero extra database reads → after a clean reset, one navigation, and clicking through every batch, the mock recorded problemsReadCount === 1.

    no hydration refetch — read count stays at 1 across full batching

    Reset mock → navigate / → click all show-more batches to 280 rows → GET /__control/scenario reports problemsReadCount: 1. On mainline the client re-issued a /rest/v1/problems read after hydration; on the head it reuses the SSR payload.

  • The problem query is narrowed to only the columns the homepage renders → the mock's recorded select dropped tags, solved, and date_added, and date_added is absent from the SSR HTML.

    recorded select — 9 columns on head vs 12 on mainline
    lastProblemsSelect = id,name,difficulty,url,added_by,added_by_url,likes,dislikes,type
    

    On mainline (04e82c0) PROBLEM_COLUMNS also included tags, solved, date_added; the head removes them.

  • Cache policy is chosen from success vs failure → a successful read served cache-control: public, max-age=0, s-maxage=60, stale-while-revalidate=300; the error scenario served cache-control: private, no-store.

    both cache headers observed live against the served build
    success:  cache-control: public, max-age=0, s-maxage=60, stale-while-revalidate=300
    failure:  cache-control: private, no-store
    

    Mainline set no cache-control on this route at all.

  • The whole app still works and the new performance budget enforces → full mocked desktop 88 passed / 4 skipped and mobile 91 passed / 1 skipped; check:performance passes (142,654 B gzip across 38 assets, largest JS 51,962 B, 50-row homepage fixture 21,367 B raw / 1,610 B gzip).

    profile page stays unlimited; suites + budget output

    Profile page (/user/…) renders all 280 rows with no show-more control (unlimited behavior preserved). pnpm exec playwright test --project=mocked-desktop --workers=1 → 88/4; --project=mocked-mobile → 91/1; pnpm run check:performance → "Performance budgets passed."

📋 Scope

Under test: PR #107 makes the homepage render an initial 50 rows via SSR and reveal the rest in bounded client-side batches (no post-hydration refetch), narrows the problems query to rendered columns, and sets public cache on successful reads / private, no-store on failures — plus CI/coverage restructuring and dead-code/comment cleanup.

Not tested:

  • Lint / format / type-check / plain build — CI's job, not re-run here.
  • Coverage-threshold enforcement (98/95/98) and the CI job-graph parallelization — GitHub-Actions-only surface, not runtime-observable from this runner; the coverage/perf helper scripts are exercised indirectly via the passing suites + check:performance.
  • Live Supabase behavior — opt-in read-only smoke (SUPABASE_SMOKE=1) is off by default; all reads/writes here went to the local mock.
  • Comment/dead-code removal and DI seam refactors — pure-logic, covered by the diff's own unit suites (377 passed per CI), not driven at runtime.

📦 Artifacts · initial 50 rows · show-more control · all 280 shown


Don't like what you see? Add a verification skill so the next run tests this kind of change.

Want another run? Comment cosmos verify on this pull request.

👍 / 👎 · View session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant