Skip to content

fix: close the open findings from the 2026-09-24 site audit - #115

Merged
Programcode merged 3 commits into
mainfrom
fix/audit-site-findings
Oct 7, 2026
Merged

Programcode merged 3 commits into
mainfrom
fix/audit-site-findings

Conversation

@Programcode

@Programcode Programcode commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Fixes the six findings still open from the 2026-09-24 site audit (AUD-001 to AUD-006). Each was rechecked on main at 0058704 before the fix.

Changes

  • AUD-001, no <main> on the landing page. LandingPage now wraps Hero through FinalCTA in <main>. Navbar and Footer stay outside it.
  • AUD-002, /terms and /privacy canonicalize to /. Google's JavaScript SEO guide says not to change an existing canonical from JavaScript, so this is done at build time. A small Vite plugin (selfCanonicalShells in vite.config.ts) writes dist/terms/index.html and dist/privacy/index.html. Each is the built shell with its canonical and og:url set to its own route. Vercel serves a real file before the SPA rewrite, the same way it already serves the static SEO pages in public/. The build fails if index.html stops carrying the tags the plugin rewrites. public/sitemap.xml already lists /, /terms and /privacy, so it is unchanged. /expocit/sources is not in the sitemap and keeps the shell's canonical.
  • AUD-003, no H1 on /login. The title is now an h1 with CardTitle's classes, so it looks the same. The signed-out /app redirect lands on the same page.
  • AUD-004, missing share link gives a 406 and a console error. SharedPage calls get_shared_item with .maybeSingle(). PostgREST answers 200 with no row, and the existing "no longer available" card renders.
  • AUD-005, console.error on the 404 page. Removed.
  • AUD-006, / is cacheable. vercel.json sends the same no-store header for / as for /index.html, and src/test/vercel-deployment-config.test.ts pins it. The audit pointed at nginx.conf, but the repo has no such file; Vercel is the only production host.

Not changed: AUD-007. Hidden paths like /.env still return the SPA shell with a 200. That is accepted for now.

Regression coverage

AUD-005 can come back without anyone noticing, so the existing public-page sweep now checks for it:

  • e2e/public-pages.spec.ts visits an unknown route in all three languages. Every route now waits for its h1 before the console errors are read, because the pages are lazy chunks. With the old NotFound the 404 visit fails on 404 Error: User attempted to access non-existent route.

The stack-backed coverage moved to the follow-up branch test/audit-stack-coverage, which gets its own PR once the shared local stack is free. That covers the stricter full-stack page fixture (fail on console.error and on first-party 4xx/5xx) and the agentic missing-share and 404 visits with PostgREST 406 capture. Codex round 1 found that the stricter fixture needs tolerate entries for specs that fail on purpose, such as a wrong demo code or a provider 500. Getting those right needs a real stack run, and four threads were queued on the one stack, so it doesn't block these fixes.

Verification

GitHub Actions jobs die in about 2 seconds with no logs because the account is out of Actions minutes, so the checks were run locally (see the verify comment for the result at the final head).

  • npm run lint, npm run typecheck, npm test and npm run build pass. The build writes the two shells, and they differ from index.html only in canonical and og:url.
  • e2e/public-pages.spec.ts, e2e/login.spec.ts and e2e/navigation-performance.spec.ts pass on desktop and mobile Chromium.
  • Vercel staging preview (through vercel curl): /terms and /privacy (with or without a trailing slash) serve their own canonical, and /, /login and unknown routes keep the landing canonical. / now sends no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0, the same as /index.html.
  • Real app on desktop (1280×800) and mobile (Pixel 5), using the staging-pinned npm run dev:preview and staging's real Supabase. The landing page has one main. /login and the signed-out /app redirect have one h1, "Sign in to DebateLab", with the same computed font, weight, line height and colour as the old h3. A bogus /share/<code> calls get_shared_item, gets 200, and shows "This shared content is no longer available". An unknown route shows the 404 page. None of these logged a console error or got a 4xx/5xx.
  • Before, on production today, with the same bogus share and unknown route: 406 POST /rest/v1/rpc/get_shared_item, console.error: Failed to load resource: the server responded with a status of 406, and console.error: 404 Error: User attempted to access non-existent route.

Screenshots and recordings are in the comment below.

🤖 Generated with Claude Code

- AUD-001: the landing page groups its sections in a <main> landmark.
- AUD-002: the build writes /terms and /privacy copies of the app shell
  whose canonical and og:url name their own route. Vercel serves them
  before the SPA fallback, so no canonical changes in JavaScript.
- AUD-003: the login title is an h1 styled like the CardTitle it replaces.
- AUD-004: a missing or expired share uses maybeSingle(), so PostgREST
  answers 200 with no row instead of 406.
- AUD-005: the 404 page no longer logs console.error.
- AUD-006: vercel.json sends no-store for / as it does for /index.html.

Regression coverage: the public page sweep visits an unknown route, the
agentic sweep visits a missing share and an unknown route and records
PostgREST 406s, and the full-stack page fixture fails on console.error
and failed local requests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 15:00
@vercel

vercel Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
debate-navigator Ready Ready Preview Oct 7, 2026 3:47pm UTC
debatelab-staging Ready Ready Preview Oct 7, 2026 3:47pm UTC

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

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

Next included review available in 10 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Free
  • Run ID: dacff29b-7618-4f6c-95d5-9d3f3f969378
📥 Commits

Reviewing files that changed from the base of the PR and between f9f3efc and b6d26e7.

📒 Files selected for processing (9)
  • docs/operations.md
  • e2e/public-pages.spec.ts
  • src/pages/LandingPage.tsx
  • src/pages/LoginPage.tsx
  • src/pages/NotFound.tsx
  • src/pages/SharedPage.tsx
  • src/test/vercel-deployment-config.test.ts
  • vercel.json
  • vite.config.ts
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

Comment @coderabbitai help to get the list of available commands.

Programcode and others added 2 commits October 7, 2026 15:06
The pages are lazy chunks, so DOMContentLoaded can fire before the page
mounts and logs anything.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The full-stack fixture and agentic sweep changes can only be proven on
the shared local stack. They land separately from the audit fixes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Programcode

Copy link
Copy Markdown
Owner Author

Real-app evidence at b6d26e7, staging-pinned build with staging Supabase. Desktop 1280×800 and Pixel 5: landing (has main), login h1, signed-out /app → login, missing share (RPC 200, no console error), 404 (no console error). The recordings walk the same five pages.

Desktop: landing

Desktop: login h1

Desktop: missing share

Desktop: 404

Mobile: landing

Mobile: login h1

Mobile: missing share

Mobile: 404

desktop-flow.webm
mobile-flow.webm

@Programcode

Copy link
Copy Markdown
Owner Author

Codex review (gpt-6.1-sol, high).

Round 1 on 885e6f0 had two findings:

  1. Medium: the stricter e2e/full-stack/stack.ts fixture would fail specs that cause errors on purpose (wrong demo code 404, provider 500) because none set tolerate. Not fixed here: the fixture change and the agentic coverage moved out of this PR in b6d26e7 and now live on test/audit-stack-coverage. They land in their own PR after a real stack run, which can set the exact tolerate lines.
  2. Low: the unknown-route check in e2e/public-pages.spec.ts could read the error list before the lazy NotFound mounted. Fixed in d761b3b: every route now waits for its first h1. With the old NotFound the 404 test fails, and on the fix the sweep passes 30/30 on desktop and mobile Chromium.

Round 2 on b6d26e7: no findings.

@Programcode

Copy link
Copy Markdown
Owner Author

Local stand-in for CI at b6d26e7. GitHub Actions jobs die in about 2 seconds with zero steps because the account is out of minutes.

npm run verify exited 0: docs:check, lint, typecheck, unit tests, collaboration tests, edge tests (318 passed), build, the service-worker tests, and the desktop Chromium @smoke browser set (29 passed, 1 flaky). The flaky test was e2e/admin-announcements.spec.ts ("user sees and permanently dismisses an active announcement" on /app). It failed once waiting for the announcement heading and passed on retry; this PR doesn't touch announcements or /app. This is a local pass, not a CI pass. The database gate isn't required here: the PR touches none of the Database workflow's paths.

@Programcode
Programcode merged commit 41e374a into main Oct 7, 2026
8 of 15 checks passed

This branch was successfully deployed

2 active deployments
Preview – debate-navigator — b6d26e7e Deployed Oct 7, 2026 by vercel[bot]
Preview – debatelab-staging — b6d26e7e Deployed Oct 7, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants