Skip to content

Add plaintiff registration to the Court of Humanity app - #320

Open
mikepsinn wants to merge 4 commits into
mainfrom
feature/court-plaintiffs
Open

Add plaintiff registration to the Court of Humanity app#320
mikepsinn wants to merge 4 commits into
mainfrom
feature/court-plaintiffs

Conversation

@mikepsinn

Copy link
Copy Markdown
Owner

Summary

Plaintiff registration was first ported into apps/warondisease. The case record belongs to the court rather than to a party in the case, so /plaintiffs and /plaintiffs/manage land in apps/courtofhumanity instead. This PR is that re-home; nothing was added to the campaign app.

What changed

New pages (apps/courtofhumanity/app/plaintiffs/)

  • page.tsx — register a plaintiff, then browse the public wall of faces with sort/cause/condition/conflict/country filters and pagination.
  • register-plaintiff-form.tsx — the name form. Anonymous visitors get their draft staged in localStorage and synced after they verify.
  • people-filter-bar.tsx, person-face-tile.tsx.
  • manage/page.tsx, manage/manage-plaintiffs-client.tsx, manage/square-photo-cropper.tsx — edit the plaintiffs you registered: photo, condition, cause of death, relationship, public note, evidence.

New API routes (apps/courtofhumanity/app/api/)

referendums/[slug]/represented-people, people/[id], conditions/search, conflicts/search, jurisdictions/search, uploads/presign, uploads/images.

Shared code into packages/site-kit

represented-people.server.ts, efficacy-lag-matcher.server.ts, represented-person-sync.ts, jurisdiction-search.server.ts, person-name.ts, person-href.ts, global-variable-lookup.server.ts, represented-person-privacy.ts, slugify.ts, plaintiffs-flow.ts, represented-life-status.ts, image-upload.{server,client}.ts, image-upload-types.ts, object-storage.server.ts, and components/shared/WelfareClaim.tsx. Existing storage.ts, constants.ts, person.server.ts, format-count.ts and routes.ts gained the additions the pages need.

Deliberately not carried over

  • site-kit/lib/subject.server.ts (a re-export of @optimitron/tracking) and the @optimitron/tracking dependency. The Court app's local lib/subject.server.ts wins the @/lib/* lookup and is the better implementation — it clears deletedAt on the upsert.
  • site-kit/lib/humanity-v-government-case.server.ts. The Court app's local file is a superset. The copied site-kit represented-people.server.ts now reads HUMANITY_V_GOVERNMENT_CASE_SLUG from @optimitron/db.
  • apps/courtofhumanity/lib/format-count.ts is deleted. It was byte-equivalent to site-kit's formatCount and shadowed it, hiding the new formatSignificantFigures helper.

Cross-domain links

  • Person tiles link to the people directory through optimitronUrl() — that directory stays on optimitron.com.
  • The 1% Treaty link is explicit https://warondisease.org/treaty; the court does not serve /treaty.
  • /humanity-v-government and the court dashboard previously deep-linked https://warondisease.org/plaintiffs. They now link to the local /plaintiffs.

Nav

courtPlaintiffs ("Register a Plaintiff") is added to nav-items.ts and to the sidebar and footer case sections of courtofhumanity.org only. No other variant's nav is touched.

Deployment note

The courtofhumanity Vercel project needs five new environment variables for plaintiff photo upload: R2_ENDPOINT, R2_BUCKET, R2_PUBLIC_URL, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY (documented in apps/courtofhumanity/.env.example). It also needs DATABASE_URL, NEXTAUTH_SECRET and NEXTAUTH_URL if they are not already set. Without the R2 variables the pages render and registration works; only photo upload fails.

Pages to review

  • /plaintiffs?logout=1
  • /plaintiffs?login=demo
  • /plaintiffs/manage?login=demo
  • /?logout=1
  • /humanity-v-government?logout=1

Verification

  • pnpm --filter @apps/courtofhumanity typecheck — pass
  • pnpm --filter @apps/warondisease typecheck — pass (shared site-kit edits)
  • pnpm typecheck:apps — pass (all seven site apps)
  • pnpm --filter @apps/courtofhumanity lint — pass
  • pnpm --filter @apps/courtofhumanity run test:unit — 5 files, 15 tests, pass
  • pnpm test:site-app-navigation — 19 tests, pass
  • pnpm --filter @apps/courtofhumanity build — pass; /plaintiffs, /plaintiffs/manage and all seven API routes compile
  • pnpm copy courtofhumanity — regenerated; /plaintiffs snapshot added, sidebar/footer entry picked up on the other court pages

Live pass against a local dev server with NEXT_PUBLIC_SITE_VARIANT=courtofhumanity.org: /plaintiffs renders logged out and signed in, /plaintiffs/manage bounces an anonymous visitor to sign-in and lists your plaintiffs when signed in, registration saves end to end and lands on the plaintiff editor, the filter query params round-trip, all three search endpoints answer 200, and POST /api/uploads/images stored a photo in R2 and returned its public URL. Screenshots captured and inspected locally at 1280 and 390 wide; no layout problems found.

🤖 Generated with Claude Code

Plaintiff registration was ported into apps/warondisease first. The case
belongs to the court, not to a party in it, so /plaintiffs and
/plaintiffs/manage land in apps/courtofhumanity instead.

New pages:
- /plaintiffs — register a plaintiff, browse the public wall of faces.
- /plaintiffs/manage — edit the plaintiffs you registered, with the
  square photo cropper.

New API routes under apps/courtofhumanity/app/api: represented-people,
people/[id], conditions/conflicts/jurisdictions search, and the two
upload routes that put plaintiff photos in Cloudflare R2. Photo upload
needs R2_ENDPOINT, R2_BUCKET, R2_PUBLIC_URL, R2_ACCESS_KEY_ID and
R2_SECRET_ACCESS_KEY on the courtofhumanity Vercel project; the block is
documented in apps/courtofhumanity/.env.example.

Shared server and client code moves into packages/site-kit/src/lib
(represented-people, efficacy-lag matcher, person name/href, image
upload, object storage, privacy and life-status helpers) plus the
WelfareClaim component. The Court app keeps its own local
humanity-v-government-case.server.ts and subject.server.ts, so the
copied site-kit code reads HUMANITY_V_GOVERNMENT_CASE_SLUG from
@optimitron/db instead. apps/courtofhumanity/lib/format-count.ts is
deleted because it shadowed the site-kit version and hid the new
formatSignificantFigures helper.

What stays on optimitron.com: the people directory. Person tiles link
there through optimitronUrl(). The 1% Treaty stays on warondisease.org.
/humanity-v-government and the court dashboard now link to the local
/plaintiffs instead of warondisease.org/plaintiffs.

Nav gains a "Register a Plaintiff" item in THE CASE, in both the
sidebar and the footer of courtofhumanity.org only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 3, 2026 19:23
@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
acceleratedmedicine Ready Ready Preview Sep 4, 2026 7:17am UTC
courtofhumanity Error Error Sep 4, 2026 7:17am UTC
optimitron-web Ready Ready Preview Sep 4, 2026 7:17am UTC
warondisease Ready Ready Preview Sep 4, 2026 7:17am UTC
4 Skipped Deployments
Project Deployment Actions Updated
curedao Skipped Skipped Sep 4, 2026 7:17am UTC
dfda Skipped Skipped Sep 4, 2026 7:17am UTC
trialabundancesurvey Skipped Skipped Sep 4, 2026 7:17am UTC
wishocracy Skipped Skipped Sep 4, 2026 7:17am UTC

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 16 minutes.

Check out review usage here.

View limit details

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

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 3c6ff967-50a9-4598-8a72-996fd61a72ac

📥 Commits

Reviewing files that changed from the base of the PR and between e25d2f6 and 33335cd.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (50)
  • apps/courtofhumanity/.env.example
  • apps/courtofhumanity/app/api/conditions/search/route.ts
  • apps/courtofhumanity/app/api/conflicts/search/route.ts
  • apps/courtofhumanity/app/api/jurisdictions/search/route.ts
  • apps/courtofhumanity/app/api/people/[id]/route.ts
  • apps/courtofhumanity/app/api/referendums/[slug]/represented-people/route.ts
  • apps/courtofhumanity/app/api/uploads/images/route.ts
  • apps/courtofhumanity/app/api/uploads/presign/route.ts
  • apps/courtofhumanity/app/contact/page.logged-out.md
  • apps/courtofhumanity/app/dashboard/page.tsx
  • apps/courtofhumanity/app/humanity-v-government/page.logged-out.md
  • apps/courtofhumanity/app/humanity-v-government/page.tsx
  • apps/courtofhumanity/app/page.logged-out.md
  • apps/courtofhumanity/app/plaintiffs/manage/manage-plaintiffs-client.tsx
  • apps/courtofhumanity/app/plaintiffs/manage/page.tsx
  • apps/courtofhumanity/app/plaintiffs/manage/square-photo-cropper.tsx
  • apps/courtofhumanity/app/plaintiffs/page.logged-out.md
  • apps/courtofhumanity/app/plaintiffs/page.tsx
  • apps/courtofhumanity/app/plaintiffs/people-filter-bar.tsx
  • apps/courtofhumanity/app/plaintiffs/person-face-tile.tsx
  • apps/courtofhumanity/app/plaintiffs/register-plaintiff-form.tsx
  • apps/courtofhumanity/app/privacy/page.logged-out.md
  • apps/courtofhumanity/app/terms/page.logged-out.md
  • apps/courtofhumanity/lib/format-count.ts
  • apps/courtofhumanity/package.json
  • packages/site-kit/package.json
  • packages/site-kit/src/components/shared/WelfareClaim.tsx
  • packages/site-kit/src/lib/constants.ts
  • packages/site-kit/src/lib/efficacy-lag-matcher.server.ts
  • packages/site-kit/src/lib/format-count.ts
  • packages/site-kit/src/lib/global-variable-lookup.server.ts
  • packages/site-kit/src/lib/image-upload-types.ts
  • packages/site-kit/src/lib/image-upload.client.ts
  • packages/site-kit/src/lib/image-upload.server.ts
  • packages/site-kit/src/lib/jurisdiction-search.server.ts
  • packages/site-kit/src/lib/nav-items.ts
  • packages/site-kit/src/lib/object-storage.server.ts
  • packages/site-kit/src/lib/person-href.ts
  • packages/site-kit/src/lib/person-name.ts
  • packages/site-kit/src/lib/person.server.ts
  • packages/site-kit/src/lib/plaintiffs-flow.ts
  • packages/site-kit/src/lib/represented-life-status.ts
  • packages/site-kit/src/lib/represented-people.server.ts
  • packages/site-kit/src/lib/represented-person-privacy.ts
  • packages/site-kit/src/lib/represented-person-sync.ts
  • packages/site-kit/src/lib/routes.ts
  • packages/site-kit/src/lib/site-config.ts
  • packages/site-kit/src/lib/slugify.ts
  • packages/site-kit/src/lib/storage.ts
  • scripts/site-app-visual-routes.mjs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T19:28:44.368951Z 6198264 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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.

🔵 Needs a closer look

It introduces a large new end-to-end flow (UI + uploads + multiple APIs + shared lib extraction), and there are scalability/performance concerns that should be resolved and re-validated by a human reviewer before approval.

Pull request overview

This PR relocates plaintiff registration from apps/warondisease into apps/courtofhumanity (where the case record conceptually belongs), while extracting shared plaintiff/gallery/upload logic into packages/site-kit so campaign surfaces can reuse it consistently.

Changes:

  • Adds /plaintiffs and /plaintiffs/manage pages to the Court of Humanity app, including registration, public “wall of faces”, filtering, pagination, and plaintiff management (photo crop + evidence).
  • Introduces new Court API routes for represented-people creation/editing plus search endpoints and upload endpoints (presign + normalized image uploads).
  • Adds shared plaintiff-related utilities and components into packages/site-kit, plus navigation/copy-snapshot wiring for the court variant.
File summaries
File Description
scripts/site-app-visual-routes.mjs Adds visual-route coverage for /plaintiffs and authenticated /plaintiffs/manage.
pnpm-lock.yaml Lockfile updates for new dependencies (AWS SDK, react-easy-crop, sharp) and transitive bumps.
packages/site-kit/src/lib/storage.ts Adds localStorage types + helpers for pending represented-person drafts and a sync lock.
packages/site-kit/src/lib/slugify.ts Adds slugify helper used for persisted relationship/role slugs.
packages/site-kit/src/lib/site-config.ts Adds courtPlaintiffs nav item to court variant “case” section.
packages/site-kit/src/lib/routes.ts Adds plaintiffs route constants and a getSignInPath() helper.
packages/site-kit/src/lib/represented-person-sync.ts Client-side sync to POST locally-staged drafts after verification.
packages/site-kit/src/lib/represented-person-privacy.ts Centralizes rules for publishing condition info + evidence-kind restrictions.
packages/site-kit/src/lib/represented-people.server.ts Server query for public plaintiff gallery, filters, counts, and sorting.
packages/site-kit/src/lib/represented-life-status.ts UI label helper for life-status messaging.
packages/site-kit/src/lib/plaintiffs-flow.ts Navigation helper for post-save redirect (direct edit vs list).
packages/site-kit/src/lib/person.server.ts Allows ensurePersonForUser to run on an injected transaction client.
packages/site-kit/src/lib/person-name.ts Adds displayName ⇄ name-parts conversion utilities for forms.
packages/site-kit/src/lib/person-href.ts Single source of truth for /people/{handle-or-id} links.
packages/site-kit/src/lib/object-storage.server.ts Adds R2/S3-compatible public-bucket upload + presign support.
packages/site-kit/src/lib/jurisdiction-search.server.ts Adds jurisdiction typeahead query helper.
packages/site-kit/src/lib/image-upload.server.ts Adds server-side image validation/normalization (sharp) + key generation.
packages/site-kit/src/lib/image-upload.client.ts Adds client helper for posting images to the backend upload route.
packages/site-kit/src/lib/image-upload-types.ts Defines allowed upload “kinds” for campaign/court surfaces.
packages/site-kit/src/lib/global-variable-lookup.server.ts Adds canonical condition GV lookup for condition normalization/linking.
packages/site-kit/src/lib/format-count.ts Adds formatSignificantFigures() helper alongside formatCount().
packages/site-kit/src/lib/efficacy-lag-matcher.server.ts Adds efficacy-lag matching logic + evidence upsert for memorials.
packages/site-kit/src/components/shared/WelfareClaim.tsx Extracts the campaign welfare-claim component for reuse in court pages.
packages/site-kit/package.json Adds AWS SDK + sharp dependencies needed for shared upload/storage code.
apps/courtofhumanity/package.json Adds react-easy-crop and moves sharp to dependencies for runtime usage.
apps/courtofhumanity/lib/format-count.ts Deletes local duplicate so site-kit formatting helpers are used.
apps/courtofhumanity/app/terms/page.logged-out.md Updates footer/sidebar snapshot to include “Register a Plaintiff”.
apps/courtofhumanity/app/privacy/page.logged-out.md Updates footer/sidebar snapshot to include “Register a Plaintiff”.
apps/courtofhumanity/app/plaintiffs/register-plaintiff-form.tsx Adds the client registration form with unauthenticated local draft staging + sync.
apps/courtofhumanity/app/plaintiffs/person-face-tile.tsx Adds the public gallery tile component (links to optimitron people directory).
apps/courtofhumanity/app/plaintiffs/people-filter-bar.tsx Adds client filter UI + option loading via search APIs.
apps/courtofhumanity/app/plaintiffs/page.tsx Adds /plaintiffs page: registration, copy, public wall-of-faces, filters, pagination.
apps/courtofhumanity/app/plaintiffs/page.logged-out.md Adds logged-out snapshot for /plaintiffs.
apps/courtofhumanity/app/plaintiffs/manage/square-photo-cropper.tsx Adds modal cropper UI for square photos (react-easy-crop + canvas output).
apps/courtofhumanity/app/plaintiffs/manage/page.tsx Adds /plaintiffs/manage server page: auth gate, fetch user plaintiffs, pass to client editor.
apps/courtofhumanity/app/page.logged-out.md Updates home snapshot nav to include “Register a Plaintiff”.
apps/courtofhumanity/app/humanity-v-government/page.tsx Updates CTA link to local /plaintiffs route.
apps/courtofhumanity/app/humanity-v-government/page.logged-out.md Updates CTA snapshot link to /plaintiffs.
apps/courtofhumanity/app/dashboard/page.tsx Updates dashboard CTA to local /plaintiffs route.
apps/courtofhumanity/app/contact/page.logged-out.md Updates snapshot nav to include “Register a Plaintiff”.
apps/courtofhumanity/app/api/uploads/presign/route.ts Adds authenticated presign endpoint for direct uploads to R2.
apps/courtofhumanity/app/api/uploads/images/route.ts Adds authenticated backend image-upload endpoint with normalization + R2 upload.
apps/courtofhumanity/app/api/referendums/[slug]/represented-people/route.ts Adds represented-person creation endpoint with validation, memorial/evidence handling, and case party creation.
apps/courtofhumanity/app/api/people/[id]/route.ts Adds represented-person update/delete endpoints with validation + visibility rules.
apps/courtofhumanity/app/api/jurisdictions/search/route.ts Adds jurisdictions search endpoint for typeahead/filter surfaces.
apps/courtofhumanity/app/api/conflicts/search/route.ts Adds conflicts search endpoint for filter/typeahead.
apps/courtofhumanity/app/api/conditions/search/route.ts Adds conditions search endpoint for filter/typeahead.
apps/courtofhumanity/.env.example Documents required R2 environment variables for photo uploads.
Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file
  • Files reviewed: 50/51 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/courtofhumanity/app/plaintiffs/person-face-tile.tsx
Comment thread packages/site-kit/src/lib/represented-people.server.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 619826479d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/courtofhumanity/app/api/people/[id]/route.ts
Comment thread apps/courtofhumanity/app/plaintiffs/people-filter-bar.tsx
Comment thread packages/site-kit/src/lib/represented-people.server.ts
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

PR review packet

Start here

Agent preflight

The agent checks this only after rerunning the review until every diff is intentional. It resets for each commit.

  • Opened the generated visual review, inspected every changed and copy-only route, and fixed all unexplained drift.

Human review checklist

Agents leave these boxes unchecked. Mike checks them after reviewing the linked evidence.

Changed files considered
  • apps/courtofhumanity/.env.example
  • apps/courtofhumanity/app/api/conditions/search/route.ts
  • apps/courtofhumanity/app/api/conflicts/search/route.ts
  • apps/courtofhumanity/app/api/jurisdictions/search/route.ts
  • apps/courtofhumanity/app/api/people/[id]/route.ts
  • apps/courtofhumanity/app/api/referendums/[slug]/represented-people/route.ts
  • apps/courtofhumanity/app/api/uploads/images/route.ts
  • apps/courtofhumanity/app/api/uploads/presign/route.ts
  • apps/courtofhumanity/app/contact/page.logged-out.md
  • apps/courtofhumanity/app/dashboard/page.tsx
  • apps/courtofhumanity/app/humanity-v-government/page.logged-out.md
  • apps/courtofhumanity/app/humanity-v-government/page.tsx
  • apps/courtofhumanity/app/page.logged-out.md
  • apps/courtofhumanity/app/plaintiffs/manage/manage-plaintiffs-client.tsx
  • apps/courtofhumanity/app/plaintiffs/manage/page.tsx
  • apps/courtofhumanity/app/plaintiffs/manage/square-photo-cropper.tsx
  • apps/courtofhumanity/app/plaintiffs/page.logged-out.md
  • apps/courtofhumanity/app/plaintiffs/page.tsx
  • apps/courtofhumanity/app/plaintiffs/people-filter-bar.tsx
  • apps/courtofhumanity/app/plaintiffs/person-face-tile.tsx
  • apps/courtofhumanity/app/plaintiffs/register-plaintiff-form.tsx
  • apps/courtofhumanity/app/privacy/page.logged-out.md
  • apps/courtofhumanity/app/terms/page.logged-out.md
  • apps/courtofhumanity/package.json
  • packages/site-kit/package.json
  • packages/site-kit/src/components/shared/WelfareClaim.tsx
  • packages/site-kit/src/lib/constants.ts
  • packages/site-kit/src/lib/efficacy-lag-matcher.server.ts
  • packages/site-kit/src/lib/format-count.ts
  • packages/site-kit/src/lib/global-variable-lookup.server.ts
  • packages/site-kit/src/lib/image-upload-types.ts
  • packages/site-kit/src/lib/image-upload.client.ts
  • packages/site-kit/src/lib/image-upload.server.ts
  • packages/site-kit/src/lib/jurisdiction-search.server.ts
  • packages/site-kit/src/lib/nav-items.ts
  • packages/site-kit/src/lib/object-storage.server.ts
  • packages/site-kit/src/lib/person-href.ts
  • packages/site-kit/src/lib/person-name.ts
  • packages/site-kit/src/lib/person.server.ts
  • packages/site-kit/src/lib/plaintiffs-flow.ts
  • ...and 10 more

Updated automatically when this PR's preview or visual review reruns.

Fast registration posts lifeStatus UNKNOWN with no memorial, so the
efficacy-lag matcher in the creation route never runs for those
plaintiffs: the death date and condition it needs only arrive later
through the PATCH path, which never called it. Those plaintiffs got no
efficacy-lag records and so were missing from both the efficacyLag=1
filter and the "died closest to cure" ordering this PR ships. Run the
matcher after the memorial upsert, best-effort and inside the same
transaction, exactly as the creation route does.

The wall of faces also rendered every <img> eagerly, so a full page of
plaintiff photos was fetched up front on mobile. Added loading="lazy"
and decoding="async".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgYULvPW89bMAV3C6mXmtG
@vercel
vercel Bot temporarily deployed to Preview – wishocracy September 3, 2026 20:43 Inactive
@vercel
vercel Bot temporarily deployed to Preview – curedao September 3, 2026 20:43 Inactive
@vercel
vercel Bot temporarily deployed to Preview – trialabundancesurvey September 3, 2026 20:43 Inactive
@vercel
vercel Bot temporarily deployed to Preview – dfda September 3, 2026 20:43 Inactive
@vercel
vercel Bot temporarily deployed to Preview – warondisease September 3, 2026 20:43 Inactive
@vercel
vercel Bot temporarily deployed to Preview – optimitron-web September 3, 2026 20:43 Inactive
@vercel
vercel Bot temporarily deployed to Preview – acceleratedmedicine September 3, 2026 20:43 Inactive
The Vercel projects are configured now; this only re-runs the previews
and the checks that waited on them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@vercel
vercel Bot temporarily deployed to Preview – curedao September 3, 2026 21:14 Inactive
@vercel
vercel Bot temporarily deployed to Preview – trialabundancesurvey September 3, 2026 21:14 Inactive
@vercel
vercel Bot temporarily deployed to Preview – wishocracy September 3, 2026 21:14 Inactive
@vercel
vercel Bot temporarily deployed to Preview – dfda September 3, 2026 21:14 Inactive
@vercel
vercel Bot temporarily deployed to Preview – optimitron-web September 3, 2026 21:14 Inactive
@vercel
vercel Bot temporarily deployed to Preview – acceleratedmedicine September 3, 2026 21:14 Inactive
@vercel
vercel Bot temporarily deployed to Preview – warondisease September 3, 2026 21:14 Inactive
@claude

claude Bot commented Sep 3, 2026

Copy link
Copy Markdown

Code review

Six issues confirmed via independent verification (two bug-scan passes + a dedicated validation pass per finding). No CLAUDE.md/AGENTS.md compliance issues found — two independent audits against the root CLAUDE.md and AGENTS.md came back clean.

1. Country/condition filter dropdowns can only ever show the first 50 results — most values are unreachable

const [conditions, conflicts, jurisdictions] = await Promise.all([
fetch("/api/conditions/search?limit=200", { cache: "no-store" }),
fetch("/api/conflicts/search?limit=50", { cache: "no-store" }),
fetch("/api/jurisdictions/search?limit=200&type=COUNTRY", {
cache: "no-store",

The filter bar requests limit=200 with no q, but both search endpoints hard-clamp to 50 and sort alphabetically ascending with no way to fetch further pages:

Both <select>s render only from this truncated list, and there's no typeahead wired to q, so items past the 50th alphabetically (e.g. "United States", "Ukraine", most conditions past "C") can never be selected. Fails silently — no error, just truncated data — so it's easy to miss with small seed data but breaks at real catalog size.

2. Pixel-limit upload rejection returns 500 instead of 400

) {
throw new Error("Image has too many pixels.");
}

Throws "Image has too many pixels." when an image exceeds maxInputPixels. The route's error mapper only recognizes messages starting with "Unsupported image type", "Image must be", "Could not read", or "Could not process":

}
if (error instanceof Error) {
const expectedUploadError =
error.message.startsWith("Unsupported image type") ||
error.message.startsWith("Image must be") ||
error.message.startsWith("Could not read") ||
error.message.startsWith("Could not process");
if (expectedUploadError) {

"Image has too many pixels." matches none of them, so a user-correctable validation failure (e.g. a highly-compressible 4000×4000 PNG that passes the byte check but trips the pixel check) surfaces as a generic 500 "Failed to upload image." and gets logged as an unexpected server error instead of the actionable message with a 400.

3. "Best-effort" efficacy-lag matcher can't actually be best-effort — it runs inside the transaction it's supposed to not break

// the creation path: a matcher failure must not fail the edit.
try {
await matchEfficacyLagForMemorial(tx, memorialId);
} catch (matchError) {
console.error("Efficacy-lag matcher failed", {
memorialId,
matchError,
});
}
const evidence = await tx.personMemorialEvidence.findMany({
where: { deletedAt: null, memorialId, submittedByUserId: userId },

The comment says "a matcher failure must not fail the edit," and the call is wrapped in try/catch — but that try/catch sits inside prisma.$transaction(async (tx) => {...}) (starts at line 611), and the matcher itself does DB writes on that same tx (personEfficacyLagEvidence.upsert in efficacy-lag-matcher.server.ts). On Postgres, a failing statement aborts the whole transaction at the connection level — swallowing the JS error doesn't help, because the very next statement in the same transaction (tx.personMemorialEvidence.findMany right after) will itself throw current transaction is aborted, failing the whole PATCH with a 500. The same pattern exists in the sibling creation route added by this PR (referendums/[slug]/represented-people/route.ts), so it affects both create and edit paths. To be genuinely best-effort, the matcher needs to run after the transaction commits.

4. A failed-then-retried registration can leave a stale draft that becomes a duplicate plaintiff

return {
authorityConfirmed,
clientDraftId: createDraftId(),
displayName,

buildDraft() mints a new random clientDraftId via createDraftId() on every call — nothing memoizes it. submit() calls buildDraft() fresh on every click:

} catch (caught) {
storage.addPendingRepresentedPerson(draft)
setError(

On a failed direct POST, the draft (with its clientDraftId) is written to localStorage via storage.addPendingRepresentedPerson(draft). If the user retries and the retry succeeds, the success path never clears the earlier failed draft from storage. On a later visit, syncPendingRepresentedPeople() replays every pending draft unconditionally. Since the server dedupes on sourceRef = represented-person-draft:{userId}:{clientDraftId}, and the stale draft has a different id than the one that succeeded, this creates a genuine duplicate Person + plaintiff party record. Fix: hold one stable clientDraftId per form entry (e.g. in a ref, reset only in resetForm()) so a retry upserts onto the same sourceRef.

5. Dead branch — the "saved" confirmation screen can never render

if (result.syncedDrafts.length > 0) {
const destination = getRepresentedPersonDetailsHref(
result.syncedPeople,
)
router.push(destination)
router.refresh()
return
}
setMode(result.syncedDrafts.length > 0 ? "saved" : "idle")
})

if (result.syncedDrafts.length > 0) {
  const destination = getRepresentedPersonDetailsHref(result.syncedPeople)
  router.push(destination)
  router.refresh()
  return
}

setMode(result.syncedDrafts.length > 0 ? "saved" : "idle")

The early return above already exits whenever syncedDrafts.length > 0, so by the time the ternary runs, the length is provably 0 — it always evaluates to "idle". "saved" is otherwise unused as a setMode argument in this file, so the entire mode === "saved" confirmation UI (the "You registered {names}." screen with "Register another"/"Add details") is unreachable dead code. Low user-impact (the sync path redirects via router.push instead), but the confirmation screen and its FormMode union member are vestigial.

6. A deceased plaintiff's health condition can be published even though the UI just cleared that flag

}): boolean {
if (!input.isPublic) return false
if (input.lifeStatus === PersonLifeStatus.DECEASED) {
return input.publicDisplayAcknowledged
}
return input.showConditionPublicly && input.healthDisclosureConfirmed
}

if (!input.isPublic) return false
if (input.lifeStatus === PersonLifeStatus.DECEASED) {
  return input.publicDisplayAcknowledged
}
return input.showConditionPublicly && input.healthDisclosureConfirmed

For deceased people, publication of the health condition depends only on publicDisplayAcknowledged, completely ignoring showConditionPublicly. The manage UI, when switching a plaintiff to DECEASED, hides the "show condition publicly" toggle and forces conditionIsPublic: false client-side, sending showConditionPublicly: false on save. But publicDisplayAcknowledged is mandatorily true for any public person (the save is rejected otherwise — see the check at

const primaryCode = canonicalCondition?.externalCodes[0] ?? null;
const conditionData = {
conditionCode: primaryCode?.code ?? null,
), so PersonCondition.isPublic ends up true for every public deceased plaintiff regardless of the explicit showConditionPublicly: false the client just sent. Realistic sequence: user has a public plaintiff with a private condition, marks them deceased, saves (must tick the required public-display acknowledgment) — the cause of death becomes public even though the UI implied it would stay private, and the toggle to undo it is hidden for deceased people. This is health/death data about real people; recommend treating "show condition publicly" as required, explicit consent for deceased people too, not implied by the general display acknowledgment.

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