ReviewOS

stacks/bench-review

seo: judge profile and article pages have zero static output — every /judges/{id}/* and /article/{id} URL hard-404s in production

#46
Closed glennmichael123 opened this 9 days ago ·
9 days ago

Problem

The production model is pre-rendered dist/ + API, and the entire value of a judge-review platform is people Googling a judge's name and landing on that judge's page. But the build emits no per-judge and no per-article HTML, so every core URL — including the ones we put in the sitemap and in transactional emails — serves a hard 404. At launch, the product's main pages do not exist.

Findings

  • No per-judge or per-article static output. dist/judges/ contains only review.html and submit.html (the static-named siblings); there is no dist/article/ at all. The dynamic views — resources/views/judges/[id]/profile/index.stx (plus reviews/, cases/, rulings/ siblings), resources/views/article/[id].stx, resources/views/court-houses/[id].stx, resources/views/judges/[id].stx — are pure client-side shells with no <script server>; a grep across resources/views/ finds zero getStaticPaths usages.
  • Static serving falls through to a real 404. preview.ts:34-39 returns dist/404.html with status 404 for any unmatched path, and netlify.toml has no [[redirects]]/SPA fallback, so /judges/123/profile serves nothing.
  • The framework already supports the fix — and warns about it today. pantry/@stacksjs/stx/dist/build.js (~line 10811) extracts getStaticPaths from <script server> blocks and logs Warning: Dynamic route ... has no getStaticPaths or returned empty paths, emitting zero files for such routes. No view uses it.
  • The dead URLs are actively advertised. app/Actions/SitemapAction.ts:92 emits /judges/{id}/profile for every judge and :110 emits /article/{id} for every approved review. /article/{id} is also baked into transactional emails: app/Actions/Reviews/SubmitReviewAction.ts:159, app/Actions/Admin/Reviews/UpdateReviewStatusAction.ts:109, and app/Helpers/notifyModerators.ts:55 — users click an email link straight into a 404.

Acceptance criteria

  • resources/views/judges/[id]/profile/index.stx (and the reviews/cases/rulings siblings) define getStaticPaths in a <script server> block enumerating judge ids from the database at build time.
  • resources/views/article/[id].stx defines getStaticPaths enumerating approved/published review ids.
  • Build output contains an HTML file for every /judges/{id}/profile and /article/{id} URL the sitemap emits; a smoke test asserts every SitemapAction URL resolves to 200 against dist/.
  • Judge name and published review content are server-rendered into the page (crawlable without JS), not fetched client-side only.
  • Build log shows no "has no getStaticPaths or returned empty paths" warnings for sitemap-advertised routes.
  • A rebuild trigger exists (scheduled cron and/or webhook on judge creation + review approval) so newly created judges/articles get static pages without a manual deploy.
  • Interim only if SSG slips: a host-level rewrite serving a shell for /judges/* and /article/* — documented as a stopgap, not the fix.

From the 2026-06-10 production-readiness audit.

8 days ago

Closing: the premise — zero static output, every entity URL hard-404s — is resolved.

  • getStaticPaths is defined on the judge tab views and on article/[id].stx, enumerating ids from the database at build time (app/Helpers/staticPaths.ts)
  • 2,743 judge pages and 202 courthouse pages are in dist/
  • Sitemap↔filesystem parity is gate-enforced in both directions: "every sitemap URL has a built file" and "every indexable page is in the sitemap"
  • "every dynamic route is pre-rendered or rewritten" passes, so there are no silent "no getStaticPaths or returned empty paths" gaps
  • The judge's name is server-rendered, not fetched client-side only: dist/judges/1.html carries "Ann L. Aiken" six times, including the <title> — Ann L. Aiken of District Court, D. Oregon — Judge Profile | Bench Review — and all 14,508 entity pages have their own head, also gate-enforced

Article pages are zero, and that is correct, not a gap: one page per published review, and none are published yet. The gate reports it rather than failing — "article: no ids yet, so no pages — correct for user-generated content until the first one is published."

One criterion deliberately not met here: the rebuild trigger (cron or webhook on judge creation / review approval) so new entities get pages without a manual deploy. That is a deployment-pipeline concern and there is no pipeline yet — it belongs with #85 and I have not invented one in the meantime.

Sign in to comment on this issue.