Problem
A judge-review platform lives or dies on its judge directory. Today the entire inventory is 8 hand-seeded judges across 6 courthouses, with no way to grow it (no importer, no admin create path, broken public submit form) and no way to keep it accurate (no disambiguation fields, no unique identity, no lifecycle, no correction channel). Misattributed reviews on a same-name judge are first-party platform data — CDA 230 does not shield profile records we author — so identity correctness is a legal exposure, not just a product gap.
Findings
- Supply is 8 judges / 6 courthouses with no bulk-import pipeline.
database/seeders/JudgeSeeder.ts:42-135(8 judges) anddatabase/seeders/CourtHouseSeeder.ts:29-79(6 courts) are the only working create paths. Noscripts/dir; CourtListener appears only in opinions-related comments (app/Actions/Admin/Opinions/CreateJudgeOpinionAction.ts:11,routes/api.ts:143). - The two nominal create paths are broken — one dangerously.
app/Models/Judge.ts:38-41declaresuseApi.routes: ['store'], so the framework auto-CRUD generator registers POST /api/judges with no auth (storage/framework/orm/routes.ts:708-709, mounted viastorage/framework/core/actions/src/dev/api.ts:142) — contradictingroutes/api.ts:109-111's "keep those guarded by auth" note. It's also broken: fillable keys are camelCase (imageUrl,practiceArea) with no snake_case mapping, andcourt_house_idisn't fillable. A framework bug is filed upstream; bench should not rely on it. Meanwhile the public "Submit a Judge" form (resources/views/judges/submit.stx:37) POSTs to/api/judges/submit, which has no route or action — every submission 404s. - Judges schema is too thin for identity or roster data.
database/migrations/0000000007-create-judges-table.sqlholds onlyname,image_url,practice_area,court_house_id— no jurisdiction, status (active/retired/deceased), external_id, bio, appointed_year, or slug, even thoughsubmit.stxalready collects those fields. - No unique identity or merge tooling. The name index is non-UNIQUE (
0000000082-create-judges_name_index-index-in-judges.sql);app/Actions/Admin/has no Judges directory. Same-name judges at the same (or null) courthouse are indistinguishable. - Correction channel is advertised but dead.
resources/views/faq.stx:35promises a "Suggest an edit" link that exists on no profile component;resources/views/contact.stx:111offers a "Judge data correction" topic but the form posts to/api/contact, which has no route. - Opinion provenance is optional, not enforced.
judge_opinionsdoes havecitation/source_url/source_provider(0000000032-create-judge_opinions-table.sql:4-10), butsource_urlis nullable, so admin-entered "Recent Rulings" can publish with zero source.
Acceptance criteria
- One-shot importer (seeder or CLI) against the CourtListener judges API populating
court_houses+judges; at minimum one full jurisdiction imported beyond the 8 seeds judgesschema widened: jurisdiction/state,status(active|retired|deceased),external_id,bio,appointed_year,slug- Auto-CRUD
POST /api/judgesremoved (dropstorefromuseApi) or replaced by an auth-gated admin action; verified no unauthenticated judge write path remains - Admin judge create/edit (and merge-duplicates) actions under
app/Actions/Admin/Judges/with an admin view /api/judges/submitbackend implemented (submissions land in a reviewable queue) or the form and FAQ references removed- Courthouse + jurisdiction rendered next to the judge name everywhere reviews appear, for same-name disambiguation
- Working "Report incorrect information" / "Suggest an edit" link on judge profiles, routed to a live intake with
judge_idattached source_url(or citation) required before ajudge_opinionsrow is published
From the 2026-06-10 production-readiness audit.
Upstream (framework root causes):
- https://github.com/stacksjs/stacks/issues/1949 — auto-CRUD useApi writes unauthenticated + camelCase-broken