Skip to content

Add missions flow and safety layer#87

Open
mikepsinn wants to merge 10 commits into
mainfrom
feature/dating-safety-layer
Open

Add missions flow and safety layer#87
mikepsinn wants to merge 10 commits into
mainfrom
feature/dating-safety-layer

Conversation

@mikepsinn
Copy link
Copy Markdown
Owner

@mikepsinn mikepsinn commented May 22, 2026

Summary

  • Move the unlaunched /love/dating app to /missions while keeping the existing /api/dating/*, Dating* helpers, and Prisma model names stable.
  • Keep the dating-adjacent mechanics in the mission UI: profile, photos, likes/intros, matches, messages, mission plans, safety acknowledgement, reports, and blocks.
  • Update campaign links, route metadata, sitemap coverage, and rendered copy snapshots for the new mission route.
  • Correct War on Disease Terms/Privacy legal-entity copy to use Accelerated Medicine Foundation Inc, dba International Campaign to End War and Disease, and document the nonprofit/DBA context in AGENTS.md.
  • Includes the generated shirt/laughter parameter export that is already present in this branch history.

Validation

  • git diff --check
  • pnpm --filter @optimitron/web exec vitest run src/lib/__tests__/dating.server.test.ts
  • pnpm --filter @optimitron/web exec vitest run src/lib/__tests__/site.test.ts
  • $env:NODE_OPTIONS='--max-old-space-size=6144'; pnpm --filter @optimitron/web typecheck:fast
  • pnpm --filter @optimitron/web copy:preview -- --routes=/missions,/missions/profile,/missions/discover,/missions/matches,/missions/messages,/terms,/privacy,/shirt,/foundations,/dashboard,/tools,/signatories
  • pnpm --filter @optimitron/web copy:preview -- --routes=/terms,/privacy
  • Screenshot review captured and inspected locally for /missions, /missions/profile, /missions/discover, /missions/messages, /terms, and /privacy on desktop/mobile.

Notes

  • Full visual sweep captured the missions page, but the run failed on existing unrelated visual routes with [next-auth]: useSession must be wrapped in a <SessionProvider /> for side-menu, side-menu-auth, and mobile /tasks/1-pct-treaty.
  • /love remains as the unlinked dating-bio outreach page; /missions is the canonical app route.

Summary by CodeRabbit

  • New Features

    • Launched Earth Optimization Missions: mission profiles, mission people browsing, mission landing and discovery flows.
    • Added dedicated Messages area and mission conversation UI with mission proposal/planning and messaging composer.
    • Added blocking/reporting controls and a Mission Safety notice with required acknowledgement.
  • Content

    • Updated t‑shirt pages and mission-oriented copy across the site.
    • Added a new “Funniest Joke” page.
  • Documentation

    • Updated legal operator to Accelerated Medicine Foundation Inc and revised Privacy & Terms with mission-specific guidance and adult-only rules.

Review Change Stack

Copilot AI review requested due to automatic review settings May 22, 2026 05:18
@vercel
Copy link
Copy Markdown

vercel Bot commented May 22, 2026

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

Project Deployment Actions Updated (UTC)
optimitron-web Ready Ready Preview, Comment May 22, 2026 10:14am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

📝 Walkthrough

Walkthrough

This PR migrates the dating feature from "Earth Optimization Dates" under /love to "Earth Optimization Missions" under /missions, adds safety acknowledgement requirements and user blocking with scoped access control, updates branding to Accelerated Medicine Foundation, and introduces new shirt/laughs parameter data.

Changes

Missions Launch with Safety & Blocking

Layer / File(s) Summary
Dating safety acknowledgement library and metadata helpers
packages/web/src/lib/dating-safety.ts
New module exports acknowledgement version constant, safety copy (title, body, rules), and helpers to validate and write acknowledgement metadata timestamps.
Safety notice component
packages/web/src/components/missions/MissionSafetyNotice.tsx
New React component renders safety rules from dating-safety copy with optional compact mode, used throughout missions UI.
Block scope system and active block checking
packages/web/src/lib/dating.server.ts (lines 1-4, 14-18, 127-166)
Block scope constants for discovery and messaging, plus helper to check for active blocks between profiles across configurable scopes in both directions.
Block API and transaction-based creation
packages/web/src/app/api/dating/blocks/route.ts, packages/web/src/lib/dating.server.ts (lines 814-892)
POST endpoint validates and delegates to createDatingBlock; core logic sanitizes inputs, validates target, upserts block, marks matches as BLOCKED, and archives conversations in a transaction.
Profile safety acknowledgement in storage and forms
packages/web/src/lib/dating.server.ts, packages/web/src/app/api/dating/profile/route.ts
Profile types include metadata and safetyAcknowledged input; saveDatingProfile loads prior metadata and blocks activation without acknowledgement; API schema accepts safetyAcknowledged for PATCH requests.
Mission client components with block and report features
packages/web/src/components/missions/MissionClient.tsx
Renamed dating components add safety acknowledgement checkbox, relationship intent selection, block interaction support, new MissionBlockButton with confirmation, MissionReportButton with send state.
Candidate discovery and interaction filtering with blocks
packages/web/src/lib/dating.server.ts (lines 340-374, 404-407)
getDatingDiscoverData filters candidates by relationshipIntent and excludes blocked profiles; createDatingInteraction rejects when active block exists.
Matching and conversation access gating with blocks
packages/web/src/lib/dating.server.ts (lines 503-548, 590-614)
getDatingMatchesData filters matches with active blocks; getDatingConversationData returns null when blocked; message sending and proposals enforce match status and block checks.
Routes catalog and navigation definitions
packages/web/src/lib/routes.ts
New ROUTES.missions and ROUTES.messages entries; new missionsLink and messagesLink NavItems with metadata; wired into toolSections and routeReviewNavItems.
Site branding and organization identity
packages/web/src/lib/site.ts
Site config derives INTERNATIONAL_CAMPAIGN_LEGAL_ENTITY_NAME from nonprofit data; updates organization branding; replaces loveLink with missionsLink; adds missions to route policy allowlists.
Missions landing page with safety notice and profile status
packages/web/src/app/missions/page.tsx, packages/web/src/app/missions/page.logged-out.md
New missions page renders MISSION_ACTIONS and MISSION_EXAMPLES arrays; loads profile status; shows MissionSafetyNotice for signed-in users; includes sign-in and profile CTAs.
People directory mission-browsing mode
packages/web/src/app/people/page.tsx, packages/web/src/app/people/page.logged-out.md
People page detects mission mode via query param; renders MissionDiscoverClient with intent filtering; adds mission people link to header.
Messages page for user conversations
packages/web/src/app/messages/page.tsx
New messages page loads matches and filters to conversations; renders MissionSafetyNotice; handles errors and empty states with mission-specific guidance.
Conversation detail with mission controls
packages/web/src/app/messages/[conversationId]/page.tsx
Conversation page includes MissionBlockButton and MissionReportButton; uses MissionMessageComposer and MissionPlanForm; updates terminology; routes back to messages.
Profile page missions section
packages/web/src/app/profile/page.tsx
Profile page adds concurrent loading of mission profile and questions; renders new Missions section with MissionProfileForm and MissionQuestionsClient.
Global link rewiring across pages
packages/web/src/app/foundations/*, packages/web/src/app/shirt/*, packages/web/src/app/signatories/*, packages/web/src/app/tools/*, packages/web/src/components/dashboard/*
Foundations, shirt, signatories, tools, and dashboard pages updated to link to missions instead of dating; terminology changed from Earth Optimization Date to Earth Optimization Mission.
Privacy policy and terms of service updates
packages/web/src/app/privacy/*, packages/web/src/app/terms/*
Updated dates to May 22, 2026; changed operator to Accelerated Medicine Foundation; added sections describing missions data collection, adult-only restrictions (18+), and safety guidelines; renumbered terms sections.
Test infrastructure and seed data
packages/web/src/lib/__tests__/dating.server.test.ts, packages/db/src/managed-data/*, packages/db/src/__tests__/*, packages/web/e2e/utils/*, packages/web/scripts/*
Dating tests extended with block mocks; seed and catalog files updated from dates to missions terminology; e2e tests add messages and people-missions routes; visual review script updated with new route ordering.
AGENTS.md nonprofit attribution
AGENTS.md
Added legal nonprofit attribution specifying Accelerated Medicine Foundation and registered DBAs.

Shirt and Laughs Parameter Data

Layer / File(s) Summary
Human laughs parameters
packages/data/src/parameters/parameters-calculations-citations.ts
New HUMAN_LAUGHS_PER_HEALTHY_LIFE_YEAR (calculated) and HUMAN_LAUGHS_PER_DAY_AVERAGE (definition) parameters with metadata and citations.
Shirt program calculated parameters
packages/data/src/parameters/parameters-calculations-citations.ts
Five new calculated parameters: SHIRT_INDUCED_LAUGHS_GAINED, SHIRT_PROGRAM_EXPECTED_VALUE_PER_DOLLAR, SHIRT_PROGRAM_ROI_RATIO, SHIRT_SEED_PROGRAM_TOTAL_USD, SHIRT_VALUE_PER_WEARER_USD with formulas and citations.
Shirt program input parameters
packages/data/src/parameters/parameters-calculations-citations.ts
Four new definition parameters: SHIRT_CASCADE_PROBABILITY_GIVEN_SEED, SHIRT_SEED_COST_PER_WEARER_USD, SHIRT_SEED_WEARERS_THRESHOLD, SHIRT_WEARING_FRICTION_COST_USD with confidence intervals and citations.
Parameter index and stats updates
packages/data/src/parameters/parameters-calculations-citations.ts
Citation/index lists updated to include new parameters; PARAMETER_STATS adjusted with expanded counts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • mikepsinn/optimitron#64: Related UI/content changes in site pages; may overlap with navigation and page-copy edits.

Poem

🐰 From dates to missions, love takes flight,
Block buttons bloom and safety shines bright,
Laughs measured in data, shirts seeding the way,
The Foundation stands proud—happy Missions Day! 🎯✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Add missions flow and safety layer' clearly and accurately summarizes the main changes: migrating the dating app to a missions-focused route with additional safety features.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dating-safety-layer

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 and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR moves the previously unlaunched dating/meetup experience from /love/dating to a new /missions flow while keeping the existing /api/dating/* API surface and DB model naming stable, and adds a safety acknowledgement + blocking/reporting layer to the missions/dating mechanics.

Changes:

  • Added /missions pages (profile/questions/discover/matches/messages) and updated navigation, sitemap, and campaign links to point at missions instead of love/dating.
  • Introduced safety acknowledgement persistence (stored in profile metadata) and expanded server-side enforcement + UX notices.
  • Added blocking support (API + server logic + UI actions) and improved validations around messages/date plans/reports.

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/web/src/lib/site.ts Updates nav + allowlists and switches legal entity footer/metadata to nonprofit DBA composition.
packages/web/src/lib/site-sitemap.ts Swaps sitemap route coverage from /love to /missions.
packages/web/src/lib/routes.ts Adds ROUTES.missions and missionsLink, wires missions into nav sections/review lists.
packages/web/src/lib/dating.server.ts Adds safety-ack enforcement, block-aware filtering, conversation status checks, message validation, reports validation, and new block creation helper.
packages/web/src/lib/dating-safety.ts New shared safety copy + helpers for reading/writing acknowledgement metadata.
packages/web/src/lib/tests/dating.server.test.ts Extends mocks and adds a test ensuring blocked profiles cannot interact.
packages/web/src/components/dashboard/DashboardShareCard.tsx Updates CTA link/copy to missions.
packages/web/src/app/tools/page.logged-out.md Updates logged-out copy snapshot to reflect missions link and tool count changes.
packages/web/src/app/terms/page.tsx Updates updated-at date and adds missions/user-meeting safety terms section; legal entity copy updates flow through.
packages/web/src/app/terms/page.logged-out.md Updates rendered-copy snapshot for Terms (missions section + legal entity name).
packages/web/src/app/signatories/page.logged-out.md Updates rendered-copy snapshot link from love to missions.
packages/web/src/app/shirt/page.tsx Updates link/copy from love/date to missions.
packages/web/src/app/shirt/page.logged-out.md Updates rendered-copy snapshot (meta descriptions + missions link/copy).
packages/web/src/app/privacy/page.tsx Updates updated-at date and adds missions-specific data collection/visibility disclosures.
packages/web/src/app/privacy/page.logged-out.md Updates rendered-copy snapshot for Privacy (missions disclosures + legal entity name).
packages/web/src/app/missions/questions/page.tsx Moves questions page to /missions/questions and updates routing/copy.
packages/web/src/app/missions/profile/page.tsx Moves profile page to /missions/profile and updates routing/copy.
packages/web/src/app/missions/profile/page.logged-out.md Adds logged-out snapshot for missions profile page.
packages/web/src/app/missions/page.tsx New missions home page with safety notice and updated route metadata.
packages/web/src/app/missions/page.logged-out.md Adds logged-out snapshot for missions home page.
packages/web/src/app/missions/messages/page.tsx Adds /missions/messages inbox page built from matches/conversations.
packages/web/src/app/missions/messages/page.logged-out.md Adds logged-out snapshot for missions messages page.
packages/web/src/app/missions/messages/[conversationId]/page.tsx Adds block button + safety notice and updates routes from love to missions.
packages/web/src/app/missions/messages/[conversationId]/page.logged-out.md Adds logged-out snapshot for missions message thread page.
packages/web/src/app/missions/matches/page.tsx Updates matches page routing from love to missions (message links updated).
packages/web/src/app/missions/matches/page.logged-out.md Fixes/updates logged-out snapshot header for missions matches page.
packages/web/src/app/missions/discover/page.tsx Updates discover page routing and adds safety notice.
packages/web/src/app/missions/discover/page.logged-out.md Fixes/updates logged-out snapshot header for missions discover page.
packages/web/src/app/missions/dating-safety-notice.tsx New reusable safety notice component for missions pages.
packages/web/src/app/missions/dating-client.tsx Moves client UI to missions namespace; adds safety ack checkbox, report UX, and block actions.
packages/web/src/app/love/dating/page.logged-out.md Removes obsolete logged-out snapshot for /love/dating.
packages/web/src/app/foundations/page.tsx Updates link/copy from love/date to missions.
packages/web/src/app/foundations/page.logged-out.md Updates rendered-copy snapshot link from love/date to missions.
packages/web/src/app/api/dating/profile/route.ts Extends request schema to accept safetyAcknowledged.
packages/web/src/app/api/dating/blocks/route.ts Adds new API endpoint for creating blocks.
packages/data/src/parameters/parameters-calculations-citations.ts Adds new shirt/laughter-related parameters and updates parameter stats/exports.
AGENTS.md Documents nonprofit/DBA context for the campaign.
Comments suppressed due to low confidence (2)

packages/web/src/app/missions/dating-client.tsx:433

  • The discovery “Block” action is calling /api/dating/blocks without a scope, so it will default to DatingBlockScope.ALL. If the intent is to only hide someone from discovery (as the reason string suggests) while leaving message-level blocking separate, pass scope: DISCOVERY explicitly.
    packages/web/src/app/missions/dating-client.tsx:719
  • The conversation “Block” action calls /api/dating/blocks without a scope, so it will default to DatingBlockScope.ALL. If you want a lighter-weight message-only block (and to keep the DISCOVERY vs MESSAGES scopes meaningful), pass scope: MESSAGES explicitly here (or rename copy/reason to reflect an all-scope block).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/web/src/lib/dating.server.ts Outdated
Comment thread packages/data/src/parameters/parameters-calculations-citations.ts
Comment thread packages/data/src/parameters/parameters-calculations-citations.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: 1c5859c5e2

ℹ️ 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 packages/web/src/lib/dating.server.ts Outdated
Comment thread packages/web/src/lib/dating.server.ts Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

PR review packet

Start here

  • 🖼️ Visual review
  • 🚀 Preview deployment
  • ☝️ Cmd/Ctrl-click review links to keep this PR open.
  • 🔑 ?login=demo signs in as the demo user; ?logout=1 clears the session.
  • 💬 For a visual problem, use the comment button in latest.html or reply here with @claude and the checklist item.

Review checklist

Changed files considered
  • AGENTS.md
  • packages/data/src/parameters/parameters-calculations-citations.ts
  • packages/db/src/__tests__/seed.integration.test.ts
  • packages/db/src/managed-data/managed-dating-catalog.ts
  • packages/db/src/managed-data/managed-seed-data.ts
  • packages/web/e2e/utils/static-pages.ts
  • packages/web/e2e/utils/visual-routes.ts
  • packages/web/scripts/build-visual-review.mjs
  • packages/web/src/app/api/dating/blocks/route.ts
  • packages/web/src/app/api/dating/profile/route.ts
  • packages/web/src/app/foundations/page.logged-out.md
  • packages/web/src/app/foundations/page.tsx
  • packages/web/src/app/joke/page.logged-out.md
  • packages/web/src/app/joke/page.tsx
  • packages/web/src/app/messages/[conversationId]/page.tsx
  • packages/web/src/app/messages/page.tsx
  • packages/web/src/app/missions/page.logged-out.md
  • packages/web/src/app/missions/page.tsx
  • packages/web/src/app/people/page.logged-out.md
  • packages/web/src/app/people/page.tsx
  • packages/web/src/app/privacy/page.logged-out.md
  • packages/web/src/app/privacy/page.tsx
  • packages/web/src/app/profile/page.tsx
  • packages/web/src/app/shirt/page.logged-out.md
  • packages/web/src/app/shirt/page.tsx
  • packages/web/src/app/signatories/page.logged-out.md
  • packages/web/src/app/terms/page.logged-out.md
  • packages/web/src/app/terms/page.tsx
  • packages/web/src/app/tools/page.logged-out.md
  • packages/web/src/components/dashboard/DashboardShareCard.tsx
  • packages/web/src/components/missions/MissionClient.tsx
  • packages/web/src/components/missions/MissionSafetyNotice.tsx
  • packages/web/src/lib/__tests__/dating.server.test.ts
  • packages/web/src/lib/dating-safety.ts
  • packages/web/src/lib/dating.server.ts
  • packages/web/src/lib/routes.ts
  • packages/web/src/lib/site-sitemap.ts
  • packages/web/src/lib/site.ts

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/web/src/app/api/dating/profile/route.ts (1)

48-63: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Map expected profile-validation errors to 4xx, not 500.

saveDatingProfile can throw user-correctable business errors (e.g., safety acknowledgement required). Returning 500 here misclassifies client input failures as server faults.

✅ Suggested status mapping
   } catch (error) {
     if (error instanceof Error && error.message === "Unauthorized") {
       return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
     }
 
     if (error instanceof z.ZodError) {
       return NextResponse.json(
         { error: "Invalid dating profile." },
         { status: 400 },
       );
     }
+    if (
+      error instanceof Error &&
+      error.message === "Confirm the Earth Optimization Mission safety rules first."
+    ) {
+      return NextResponse.json({ error: error.message }, { status: 400 });
+    }
     console.error("[dating] Failed to save profile:", error);
     return NextResponse.json(
       { error: "Failed to save dating profile." },
       { status: 500 },
     );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/app/api/dating/profile/route.ts` around lines 48 - 63, The
catch block currently treats all non-Zod and non-Unauthorized errors as 500;
change it to detect and map user-correctable business errors thrown by
saveDatingProfile to 4xx responses instead of 500. Specifically, update the
catch in route.ts to check for the business-error shape thrown by
saveDatingProfile (e.g., a custom class or an object with a status/statusCode or
code/message indicating client errors) and call NextResponse.json with the
provided error message and the corresponding 4xx status; keep the existing
branches for Unauthorized and ZodError and fall back to 500 only for truly
unexpected errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/web/src/app/api/dating/blocks/route.ts`:
- Around line 31-33: The current error branch returns raw exception messages and
always uses status 400; change it to avoid leaking internal details by returning
a generic client-facing message (e.g., { error: "An unexpected error occurred"
}) and choosing status codes properly: map known client errors (e.g., a custom
ValidationError or similar) to 400, otherwise use 500 for server errors; ensure
the original Error object is logged or reported server-side via console.error or
your logger before returning the generic response. Locate the error handling in
the route (the block using "if (error instanceof Error)" and NextResponse.json)
and implement the mapping/logging and generic response there.

In `@packages/web/src/app/missions/dating-client.tsx`:
- Around line 420-449: The interact function's async flow sets busy state and
calls fetch but does not guarantee clearing that state or calling router.refresh
when fetch rejects; wrap the body of interact in a try/finally so
setBusyId(null) and router.refresh() always run in the finally block, and
optionally handle non-OK responses before leaving try; apply the same
try/finally pattern to the other interaction handlers in this file that set
busy/sending state so the UI is never left permanently disabled.

In `@packages/web/src/app/missions/matches/page.tsx`:
- Around line 24-26: The current return expression that directly returns message
(the ternary using message === "Create a mission profile first." ? ... :
message) may expose raw server text; replace it with a whitelist check: define a
set/array of allowed user-safe strings (e.g., allowedErrorMessages) and, in the
place of the current ternary, return message only if it exists in that whitelist
and otherwise return a generic fallback like "An error occurred while loading
matches."; update the logic around the message variable and the return site to
use this whitelist check so only known safe messages are shown.

In `@packages/web/src/app/missions/messages/page.logged-out.md`:
- Line 7: The page metadata for page.logged-out.md is missing a canonical URL
for the /missions/messages route; wire this route into the route metadata
pipeline by providing a canonical value from the app's route metadata source
(e.g., ensure the route id "/missions/messages" is returned by the metadata
provider and that generateMetadata or getRouteMetadata for the messages route
sets metadata.canonical or alternates.canonical). Update the route metadata
registration (the function that maps routes to metadata) so the messages route
uses the shared metadata generator and emits a populated canonical URL rather
than leaving Canonical: [missing].

In `@packages/web/src/app/missions/messages/page.tsx`:
- Around line 24-27: getDisplayError currently returns the raw backend message
for any non-matching string, which can leak internal details; change
getDisplayError to map only a whitelist of known user-safe messages (e.g.,
"Create a mission profile first." and any other expected UX strings) to their
display text and otherwise return a generic, non-sensitive fallback like "An
unexpected error occurred. Please try again or contact support." Update
references to getDisplayError in the messages UI so all error rendering goes
through this function and ensure no code directly surfaces backend error text.

In `@packages/web/src/app/missions/profile/page.logged-out.md`:
- Line 7: The logged-out snapshot file page.logged-out.md for the
/missions/profile route is missing the Canonical metadata; open that file and
add a Canonical frontmatter entry (the "Canonical" field in page.logged-out.md)
that points to the canonical URL for the route (e.g., your site base +
/missions/profile) so the logged-out snapshot and SEO metadata are consistent
with the route.

In `@packages/web/src/app/missions/questions/page.tsx`:
- Around line 25-27: The current return echoes raw backend text via the message
variable; replace that with a whitelist-based safe fallback: define an allowed
set (e.g., safeMessages = ["Create a mission profile first."]) and in the
component or helper that returns the message (where the code currently does
return message === "Create a mission profile first." ? ... : message) return the
message only if it matches an entry in safeMessages, otherwise return a generic
string like "An unexpected error occurred" (do this check where message is
evaluated so only known safe strings are exposed to the UI).

In `@packages/web/src/lib/dating-safety.ts`:
- Line 6: The user-facing string in packages/web/src/lib/dating-safety.ts
contains an uncited hardcoded numeric claim ("150 thousand people die...");
replace the literal number with a ParameterValue-driven parameter (or remove the
explicit figure) so all user-facing numbers use <ParameterValue> per the
guideline; update the string in the file to reference the parameter path (or
neutral phrasing without the number) and ensure any callers/renderers of that
string supply the citation-backed parameter.

In `@packages/web/src/lib/dating.server.ts`:
- Around line 732-755: When messageId is provided the code currently may leave
reportedProfileId as a caller-supplied value; instead always derive the report
target from the message sender to prevent misattribution. After retrieving the
message via prisma.datingMessage.findFirst (checking senderProfileId and
matching conversation by profile.id), set reportedProfileId =
message.senderProfileId (overriding any external value) and do the same enforced
assignment in the other block mentioned (the similar code around the second
occurrence) so reports always attribute to the actual sender.

---

Outside diff comments:
In `@packages/web/src/app/api/dating/profile/route.ts`:
- Around line 48-63: The catch block currently treats all non-Zod and
non-Unauthorized errors as 500; change it to detect and map user-correctable
business errors thrown by saveDatingProfile to 4xx responses instead of 500.
Specifically, update the catch in route.ts to check for the business-error shape
thrown by saveDatingProfile (e.g., a custom class or an object with a
status/statusCode or code/message indicating client errors) and call
NextResponse.json with the provided error message and the corresponding 4xx
status; keep the existing branches for Unauthorized and ZodError and fall back
to 500 only for truly unexpected errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 362bf0a8-3667-4905-96c6-a627d25b3e2b

📥 Commits

Reviewing files that changed from the base of the PR and between a12d0ed and 1c5859c.

📒 Files selected for processing (37)
  • AGENTS.md
  • packages/data/src/parameters/parameters-calculations-citations.ts
  • packages/web/src/app/api/dating/blocks/route.ts
  • packages/web/src/app/api/dating/profile/route.ts
  • packages/web/src/app/foundations/page.logged-out.md
  • packages/web/src/app/foundations/page.tsx
  • packages/web/src/app/love/dating/page.logged-out.md
  • packages/web/src/app/missions/dating-client.tsx
  • packages/web/src/app/missions/dating-safety-notice.tsx
  • packages/web/src/app/missions/discover/page.logged-out.md
  • packages/web/src/app/missions/discover/page.tsx
  • packages/web/src/app/missions/matches/page.logged-out.md
  • packages/web/src/app/missions/matches/page.tsx
  • packages/web/src/app/missions/messages/[conversationId]/page.logged-out.md
  • packages/web/src/app/missions/messages/[conversationId]/page.tsx
  • packages/web/src/app/missions/messages/page.logged-out.md
  • packages/web/src/app/missions/messages/page.tsx
  • packages/web/src/app/missions/page.logged-out.md
  • packages/web/src/app/missions/page.tsx
  • packages/web/src/app/missions/profile/page.logged-out.md
  • packages/web/src/app/missions/profile/page.tsx
  • packages/web/src/app/missions/questions/page.tsx
  • packages/web/src/app/privacy/page.logged-out.md
  • packages/web/src/app/privacy/page.tsx
  • packages/web/src/app/shirt/page.logged-out.md
  • packages/web/src/app/shirt/page.tsx
  • packages/web/src/app/signatories/page.logged-out.md
  • packages/web/src/app/terms/page.logged-out.md
  • packages/web/src/app/terms/page.tsx
  • packages/web/src/app/tools/page.logged-out.md
  • packages/web/src/components/dashboard/DashboardShareCard.tsx
  • packages/web/src/lib/__tests__/dating.server.test.ts
  • packages/web/src/lib/dating-safety.ts
  • packages/web/src/lib/dating.server.ts
  • packages/web/src/lib/routes.ts
  • packages/web/src/lib/site-sitemap.ts
  • packages/web/src/lib/site.ts
💤 Files with no reviewable changes (1)
  • packages/web/src/app/love/dating/page.logged-out.md

Comment thread packages/web/src/app/api/dating/blocks/route.ts Outdated
Comment thread packages/web/src/components/missions/MissionClient.tsx Outdated
Comment thread packages/web/src/app/missions/matches/page.tsx Outdated
Comment thread packages/web/src/app/missions/messages/page.logged-out.md Outdated
Comment thread packages/web/src/app/messages/page.tsx Outdated
Comment thread packages/web/src/app/missions/profile/page.logged-out.md Outdated
Comment thread packages/web/src/app/missions/questions/page.tsx Outdated
Comment thread packages/web/src/lib/dating-safety.ts Outdated
Comment thread packages/web/src/lib/dating.server.ts
@claude
Copy link
Copy Markdown

claude Bot commented May 22, 2026

Code review — 4 issues found (3 HIGH, 1 MEDIUM). No bugs. Checked security, logic, and CLAUDE.md compliance.


HIGH — Hardcoded user-facing number bypasses <ParameterValue>

File: packages/web/src/lib/dating-safety.ts

DATING_SAFETY_COPY.body hardcodes "150 thousand people die of disease every day" as a plain string, which DatingSafetyNotice renders directly in JSX. Per CLAUDE.md: "<ParameterValue> for every user-facing number. Grep parameters-calculations-citations.ts for a matching parameter before typing a number."

GLOBAL_DISEASE_DEATHS_DAILY (value: 150000.0) already exists at parameters-calculations-citations.ts ~L1288. Fix: move the copy into DatingSafetyNotice as JSX and embed <ParameterValue param={GLOBAL_DISEASE_DEATHS_DAILY} /> between the two text fragments.


HIGH — page.logged-out.md snapshots mis-routed and showing [missing] canonical

Five snapshot files are wrong:

  • missions/discover/page.logged-out.md — renamed from love/dating/matches/, content is for the wrong page
  • missions/matches/page.logged-out.md — renamed from love/dating/messages/[conversationId]/, content is for the wrong page
  • missions/messages/page.logged-out.mdCanonical: [missing], site-wide default title/description
  • missions/messages/[conversationId]/page.logged-out.mdCanonical: [missing], site-wide default title/description
  • missions/profile/page.logged-out.mdCanonical: [missing], site-wide default title/description

Per CLAUDE.md: "Never hand-edit page.logged-out.md snapshots. They're generated. Hand-edits are a smell that the dev server's down or the script's failing — fix that instead."

The three [missing] files were captured by running copy:preview against routes that redirect unauthenticated users, so the script captured the sign-in page instead. The two mis-routed files resulted from git mv to wrong directories. Fix: run pnpm --filter @optimitron/web copy:preview -- --routes=/missions/discover,/missions/matches,/missions/messages,/missions/profile with a valid authenticated session.


HIGH — TODO.md not updated; commit messages missing required markers

The PR implements the dating/missions MVP — work explicitly tracked in TODO.md (lines 131-150 cover the active dating foundation scope; line 91 references the parked dating registry) — but no TODO.md changes appear in the diff and none of the five commit messages contain todo-touched: or todo-skipped: markers.

Per CLAUDE.md: "Update TODO.md in the same commit as the work it covers — both the check-box and any new follow-up lines. Commit message must include todo-touched: <item> or todo-skipped: <reason>."

At minimum: check off completed dating-foundation items, add follow-up lines for deferred work (e.g., /missions/questions/page.logged-out.md is missing; old /love/dating routes have no redirect), and add todo-touched: dating-foundation-mvp to the relevant commits.


MEDIUM — New /missions/messages page missing getRouteMetadata and NavItem in routes.ts

File: packages/web/src/app/missions/messages/page.tsx

This newly created page has no export const metadata call. Per CLAUDE.md: "routes.ts is the single source of truth. Pages use getRouteMetadata(link) from @/lib/metadata.ts. Descriptions in Wishonia's voice."

The parent missions/page.tsx correctly uses getRouteMetadata(missionsLink). Two changes needed: (1) add a NavItem for /missions/messages in routes.ts; (2) add export const metadata = getRouteMetadata(messagesLink) to this file. The Canonical: [missing] in the companion snapshot is a downstream symptom.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
packages/web/src/components/missions/MissionClient.tsx (1)

464-494: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear busy/sending state in finally for async action handlers.

If fetch rejects, these handlers can leave buttons permanently disabled (busyId/isSending/isBlocking never reset). Wrap request flows in try/finally and reset state in finally.

Also applies to: 706-719, 751-769

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/components/missions/MissionClient.tsx` around lines 464 -
494, The interact function can leave busy state set if fetch throws; wrap the
request flow in a try/finally so setBusyId(null) and router.refresh() always run
(i.e., setBusyId(candidateId) before try, perform fetches in try, then in
finally call setBusyId(null) and router.refresh()). Apply the same change
pattern (use try/finally to clear isSending/isBlocking state and call
router.refresh()) to the other async handlers referenced around the other diffs
(the handlers at the ranges noted) so buttons are never left permanently
disabled.
packages/web/src/app/messages/page.tsx (1)

27-31: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not return raw backend errors to the UI.

getDisplayError still returns unrecognized backend messages directly. Keep a whitelist of user-safe messages and fall back to a generic error for everything else.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/app/messages/page.tsx` around lines 27 - 31, getDisplayError
currently returns backend messages directly; change it to map only an explicit
whitelist of safe messages (e.g., the known "Create a mission profile first."
case handled in getDisplayError) to user-friendly text and return a generic
fallback message for anything else; update getDisplayError to check against the
whitelist (or switch/lookup) and return "Turn on missions first." for the known
case and a generic user-safe string like "Something went wrong. Please try
again." for all other inputs.
packages/web/src/lib/dating.server.ts (1)

739-762: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Always derive reportedProfileId from the reported message sender.

When messageId is provided, this still allows caller-supplied reportedProfileId to persist, which can misattribute reports. Enforce sender ownership from message.senderProfileId and reject self-report if that sender is the current profile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/lib/dating.server.ts` around lines 739 - 762, When messageId
is provided, ignore any caller-supplied reportedProfileId and set
reportedProfileId = message.senderProfileId unconditionally (replace the
reportedProfileId = reportedProfileId ?? ... line); then validate that
reportedProfileId !== profile.id and throw an error (e.g., "Cannot report
yourself") if they match to reject self-reports. Locate the logic around
messageId, the local variables message, reportedProfileId and profile in
dating.server.ts and implement these two changes so the sender is always used
and self-reporting is prevented.
packages/web/src/lib/dating-safety.ts (1)

6-6: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove the hardcoded numeric claim from shared safety copy.

This string still hardcodes a user-facing statistic (150 thousand...) without citation-bound rendering. In this shared constant, either remove the explicit number or move this sentence into a UI renderer that can use a citation-backed parameter component.

As per coding guidelines packages/web/src/**/*.{ts,tsx}: “Use <ParameterValue> for every user-facing number…”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/lib/dating-safety.ts` at line 6, The shared safety copy in
packages/web/src/lib/dating-safety.ts contains a hardcoded user-facing number;
remove the explicit "150 thousand people die..." numeric claim from the constant
and either (A) delete that sentence entirely from the string in the file, or (B)
replace it with a parameter placeholder (e.g., "{diseaseDeathStat}" or similar)
in the exported constant and move the full sentence into the UI renderer where
you render this copy, inserting the actual numeric value using the
<ParameterValue> component per guidelines; update any callers of the constant to
supply the parameter if you choose option B.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/web/src/app/missions/page.tsx`:
- Around line 12-14: The code currently converts GLOBAL_DISEASE_DEATHS_DAILY to
a plain formatted string stored in DAILY_DISEASE_DEATHS; instead render the
parameter via the ParameterValue component to preserve citation/display
behavior. Replace usages of DAILY_DISEASE_DEATHS with a ParameterValue element
that takes GLOBAL_DISEASE_DEATHS_DAILY as the parameter prop (e.g.,
<ParameterValue parameter={GLOBAL_DISEASE_DEATHS_DAILY} />) and remove the
Math.round/toLocaleString formatting; apply the same replacement for the similar
occurrences around lines 123-124.

---

Duplicate comments:
In `@packages/web/src/app/messages/page.tsx`:
- Around line 27-31: getDisplayError currently returns backend messages
directly; change it to map only an explicit whitelist of safe messages (e.g.,
the known "Create a mission profile first." case handled in getDisplayError) to
user-friendly text and return a generic fallback message for anything else;
update getDisplayError to check against the whitelist (or switch/lookup) and
return "Turn on missions first." for the known case and a generic user-safe
string like "Something went wrong. Please try again." for all other inputs.

In `@packages/web/src/components/missions/MissionClient.tsx`:
- Around line 464-494: The interact function can leave busy state set if fetch
throws; wrap the request flow in a try/finally so setBusyId(null) and
router.refresh() always run (i.e., setBusyId(candidateId) before try, perform
fetches in try, then in finally call setBusyId(null) and router.refresh()).
Apply the same change pattern (use try/finally to clear isSending/isBlocking
state and call router.refresh()) to the other async handlers referenced around
the other diffs (the handlers at the ranges noted) so buttons are never left
permanently disabled.

In `@packages/web/src/lib/dating-safety.ts`:
- Line 6: The shared safety copy in packages/web/src/lib/dating-safety.ts
contains a hardcoded user-facing number; remove the explicit "150 thousand
people die..." numeric claim from the constant and either (A) delete that
sentence entirely from the string in the file, or (B) replace it with a
parameter placeholder (e.g., "{diseaseDeathStat}" or similar) in the exported
constant and move the full sentence into the UI renderer where you render this
copy, inserting the actual numeric value using the <ParameterValue> component
per guidelines; update any callers of the constant to supply the parameter if
you choose option B.

In `@packages/web/src/lib/dating.server.ts`:
- Around line 739-762: When messageId is provided, ignore any caller-supplied
reportedProfileId and set reportedProfileId = message.senderProfileId
unconditionally (replace the reportedProfileId = reportedProfileId ?? ... line);
then validate that reportedProfileId !== profile.id and throw an error (e.g.,
"Cannot report yourself") if they match to reject self-reports. Locate the logic
around messageId, the local variables message, reportedProfileId and profile in
dating.server.ts and implement these two changes so the sender is always used
and self-reporting is prevented.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5ab813ae-1c95-424f-8423-ae967ece1062

📥 Commits

Reviewing files that changed from the base of the PR and between 1c5859c and f54f4c3.

📒 Files selected for processing (23)
  • packages/db/src/__tests__/seed.integration.test.ts
  • packages/db/src/managed-data/managed-dating-catalog.ts
  • packages/db/src/managed-data/managed-seed-data.ts
  • packages/web/e2e/utils/static-pages.ts
  • packages/web/e2e/utils/visual-routes.ts
  • packages/web/scripts/build-visual-review.mjs
  • packages/web/src/app/api/dating/profile/route.ts
  • packages/web/src/app/messages/[conversationId]/page.tsx
  • packages/web/src/app/messages/page.tsx
  • packages/web/src/app/missions/page.logged-out.md
  • packages/web/src/app/missions/page.tsx
  • packages/web/src/app/people/page.logged-out.md
  • packages/web/src/app/people/page.tsx
  • packages/web/src/app/privacy/page.logged-out.md
  • packages/web/src/app/privacy/page.tsx
  • packages/web/src/app/profile/page.tsx
  • packages/web/src/app/terms/page.logged-out.md
  • packages/web/src/app/terms/page.tsx
  • packages/web/src/components/missions/MissionClient.tsx
  • packages/web/src/components/missions/MissionSafetyNotice.tsx
  • packages/web/src/lib/dating-safety.ts
  • packages/web/src/lib/dating.server.ts
  • packages/web/src/lib/routes.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/db/src/tests/seed.integration.test.ts
  • packages/web/src/app/people/page.logged-out.md
  • packages/db/src/managed-data/managed-dating-catalog.ts
  • packages/web/src/app/missions/page.logged-out.md

Comment thread packages/web/src/app/missions/page.tsx Outdated
5-step prankster-on-their-phone version of the joke essay at
manual.warondisease.org/knowledge/appendix/joke.html:

  STEP 1 — vote on this
  STEP 2 — get a permanent marker
  STEP 3 — sneak into the closets of everyone you love
  STEP 4 — write this on their shirts (auth-aware referral URL)
  STEP 5 — when they get mad at you, answer these questions

Step 5 has a collapsible accordion of the 7 verbatim objection answers
from the essay so the prankster can hold their phone while the pranked
spouse yells, find the matching objection, and read the answer.

Every English sentence on the page comes from joke.qmd modulo Quarto
template variables swapped for <ParameterValue>. Stripped two Quarto
cross-references and two Pandoc bibliography keys that would render
as broken text on a Next.js page.

Nav: jokeLink added to top-level primary nav (9th slot) and footer
"Tell Someone Else" column.

todo-touched: ship /joke as actionable prankster surface for joke.qmd
qa-passed: snapshot regenerated; auth-aware Step 4 differs correctly between logged-in (real referral URL) and logged-out (placeholder + sign-in prompt); 7 objection accordion summaries verified against joke.qmd source line-by-line

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/web/src/app/joke/page.tsx`:
- Around line 180-187: The UI shows shirtReferralUrl without a copy control; add
a compact copy affordance next to the displayed URL so users can quickly copy
it. Modify the JSX in page.tsx where shirtReferralUrl is rendered to render the
URL and a small inline copy button/icon (visible only when shirtReferralUrl
exists and personalReferralUrl is falsy), implement an onClick handler that
writes the URL to navigator.clipboard (with a fallback) and provides accessible
feedback (aria-label and a brief success toast or visually hidden status), and
reuse or create a small CopyButton component if one exists to keep
styling/behavior consistent.
- Around line 225-384: There are hardcoded user-facing numbers inside the
Objection blocks (text nodes rendered with paragraphClass in the joke/page.tsx
component) that must be replaced with ParameterValue components or rephrased if
no parameter exists; search the Objection content for literals like "847
civilizations", "88%", "30 seconds", other explicit counts/percentages/years and
swap them to <ParameterValue param={...}> using the matching keys found in
packages/data/src/parameters/parameters-calculations-citations.ts, keeping
existing components like ParameterValue, ManualLink, manualBase and Objection
intact; if no corresponding parameter exists, remove or reword the numeric claim
to avoid unsourced figures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 60fabda7-58a5-40e6-86cb-c5c89a679f77

📥 Commits

Reviewing files that changed from the base of the PR and between f54f4c3 and 14501a6.

📒 Files selected for processing (13)
  • packages/data/src/parameters/parameters-calculations-citations.ts
  • packages/db/src/__tests__/seed.integration.test.ts
  • packages/web/src/app/api/dating/blocks/route.ts
  • packages/web/src/app/api/dating/profile/route.ts
  • packages/web/src/app/joke/page.logged-out.md
  • packages/web/src/app/joke/page.tsx
  • packages/web/src/app/messages/page.tsx
  • packages/web/src/app/missions/page.tsx
  • packages/web/src/components/missions/MissionClient.tsx
  • packages/web/src/lib/dating-safety.ts
  • packages/web/src/lib/dating.server.ts
  • packages/web/src/lib/routes.ts
  • packages/web/src/lib/site.ts

Comment on lines +180 to +187
<p className="mt-4 break-words text-xl font-black leading-tight sm:text-2xl">
{shirtReferralUrl}
</p>
{!personalReferralUrl ? (
<p className="mt-4 text-base font-bold leading-7">
Sign in and vote yes to get your real referral link printed
here.
</p>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a copy affordance next to the referral value.

Line 181 shows an exact value users are expected to reuse, but there’s no nearby copy control. Add a compact copy button adjacent to the URL.

As per coding guidelines If the user is expected to copy an exact value ... provide a compact copy affordance near that value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/app/joke/page.tsx` around lines 180 - 187, The UI shows
shirtReferralUrl without a copy control; add a compact copy affordance next to
the displayed URL so users can quickly copy it. Modify the JSX in page.tsx where
shirtReferralUrl is rendered to render the URL and a small inline copy
button/icon (visible only when shirtReferralUrl exists and personalReferralUrl
is falsy), implement an onClick handler that writes the URL to
navigator.clipboard (with a fallback) and provides accessible feedback
(aria-label and a brief success toast or visually hidden status), and reuse or
create a small CopyButton component if one exists to keep styling/behavior
consistent.

Comment on lines +225 to +384
COVID-19 killed more Americans than World War I, World War II,
Korea, Vietnam, Iraq, and Afghanistan combined. The $900 billion
murder budget watched it happen, fully armed and completely
confused. The virus did not check your passport. Disease is the
only enemy that attacks every nation simultaneously, and you are
the only species that needs to be talked into fighting it.
</p>
<p className={paragraphClass}>
Also, the Pentagon cannot account for{" "}
<ParameterValue param={PENTAGON_UNACCOUNTED_FUNDS} /> that it
has misplaced. 1% of US military spending is a fraction of the
amount that already goes into a black hole every year and no one
can locate. You are not redirecting the missile budget. You are
redirecting the &quot;we lost it somewhere&quot; budget. The
missiles won&apos;t notice.
</p>
</Objection>

<Objection title='"1% is unrealistic."'>
<p className={paragraphClass}>
Immediately before the United States won World War II, US
military spending was{" "}
<ParameterValue
param={US_1939_MILITARY_SPENDING_PCT_LOWER_THAN_CURRENT}
/>{" "}
lower than current levels. The country still built the bombs and
airplanes that won the war. After winning, the US cut military
spending <ParameterValue param={POST_WW2_MILITARY_CUT_PCT} />{" "}
over two years. GM went from B-24 bombers back to Cadillacs.
Frigidaire stopped making machine guns and went back to
refrigerators. The economy produced the greatest boom in your
history.
</p>
<p className={paragraphClass}>
Same country, in living memory, did a redirect about 87 times
the size of what this shirt asks for. Your grandparents handled
an 88% cut and built the middle class. You are being asked for
1% and acting like someone suggested disbanding the army during
an invasion.
</p>
</Objection>

<Objection title='"The military-industrial complex will never allow it."'>
<p className={paragraphClass}>
The CEO of Lockheed Martin has two options: (a) keep apocalypse
#122, watch their family die of curable diseases, retire on the
current trajectory; or (b) give up the one apocalypse they
cannot use, watch their family live, invest in the biotech
sector that absorbs a trillion redirected dollars per year,
retire on a{" "}
<ParameterValue
param={
TREATY_TRAJECTORY_GDP_VS_CURRENT_TRAJECTORY_MULTIPLIER_YEAR_15
}
/>
-larger economy. They have not picked (b) yet because nobody has
explained it in those terms. The shirt is how that gets
explained.
</p>
<p className={paragraphClass}>
The military-industrial complex does not benefit from the
status quo. They die of the same diseases. They live in the same
economy. Their children attend the same funerals. The costs of
the current system are concentrated on everyone, including them.
There is no winner. There is no rational beneficiary of the
current arrangement. There is only a species that has not yet
done the arithmetic. This book is the arithmetic.
</p>
</Objection>

<Objection title='"What about defense industry jobs?"'>
<p className={paragraphClass}>
1% is a rounding error. The redirect funds clinical trials,
which is also a lot of jobs: trial coordinators, medical
statisticians, lab technicians, diagnostics manufacturing.
Engineers building guidance systems can build medical imaging
devices. Same differential equations, fewer funerals. Veterans
particularly need clinical trial access for the things they got
from serving (chronic pain, PTSD, traumatic brain injury); the
redirect pays for the trials they need.
</p>
</Objection>

<Objection title='"What if countries cheat?"'>
<p className={paragraphClass}>
Of course they will. This system was designed by someone who has
watched 847 civilizations (me), and it does not use trust. It
uses math. Politicians who comply receive{" "}
<ManualLink
href={`${manualBase}/solution/incentive-alignment-bonds.html`}
>
Incentive Alignment Bond
</ManualLink>{" "}
benefits: campaign funding and cushy post-office careers.
Politicians who don&apos;t comply get nothing. Every
disbursement is published on a public ledger keyed to specific
trials with specific outcomes via the{" "}
<ManualLink
href={`${manualBase}/solution/decentralized-accountability-office.html`}
>
Decentralized Accountability Office
</ManualLink>{" "}
and{" "}
<ManualLink
href={`${manualBase}/solution/automated-revenue-service.html`}
>
Automated Revenue Service
</ManualLink>
. The 1% that gets siphoned off does not get siphoned off
invisibly. Auditors see every wire; voters see every auditor.
</p>
</Objection>

<Objection title='"What if other countries don&apos;t sign?"'>
<p className={paragraphClass}>
The first country to sign makes it easier for the second. The
treaty follows the Ottawa (landmine) and Treaty on the
Prohibition of Nuclear Weapons templates, both ratified by most
countries even with the big powers holding out. Holdouts do not
break the treaty. They make the cost of holding out visible.
Also: TikTok scaled globally and it teaches strangers
choreography. If your species can coordinate dance moves across
150 countries, it can coordinate not dying.
</p>
</Objection>

<Objection title='"Politicians will never agree to this."'>
<p className={paragraphClass}>
Politicians are also humans. They also get diseases. A
politician who votes against the treaty is not &quot;voting
against a budget reallocation.&quot; They are voting to keep
apocalypse #122 (which is functionally identical to apocalypse
#121, since you only have one civilization) in exchange for
ensuring that their constituents, their donors, their families,
and they themselves continue to suffer and die of preventable
diseases while being{" "}
<ParameterValue
param={
TREATY_TRAJECTORY_GDP_VS_CURRENT_TRAJECTORY_MULTIPLIER_YEAR_15
}
/>{" "}
poorer. That is what a &quot;no&quot; vote means. The shirt
makes sure voters understand what their representative is
actually voting for.
</p>
<p className={paragraphClass}>
Once the cascade triggers and a majority of any
politician&apos;s constituents show up on the public treaty
scoreboard, signing is 30 seconds of work on a budget
spreadsheet. Refusing gets them replaced by the next candidate
who runs on &quot;I will sign the treaty.&quot; The campaign
also outbids the defense lobby directly: defense contractors
spend <ParameterValue param={DEFENSE_LOBBYING_ANNUAL} /> per
year buying politicians; the treaty{" "}
<ManualLink
href={`${manualBase}/economics/campaign-budget.html`}
>
lobbying budget
</ManualLink>{" "}
is <ParameterValue param={TREATY_CAMPAIGN_BUDGET_LOBBYING} />.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Replace hardcoded public numeric claims with parameter-backed values where available.

This section still hardcodes several user-facing figures (for example, Line 226, Line 311, Line 347). Use parameter/citation-backed rendering for sourceable claims, and remove/rephrase claims that do not have a sourceable parameter.

As per coding guidelines Use <ParameterValue> for every user-facing number; grep packages/data/src/parameters/parameters-calculations-citations.ts for a matching parameter before typing a number.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/app/joke/page.tsx` around lines 225 - 384, There are
hardcoded user-facing numbers inside the Objection blocks (text nodes rendered
with paragraphClass in the joke/page.tsx component) that must be replaced with
ParameterValue components or rephrased if no parameter exists; search the
Objection content for literals like "847 civilizations", "88%", "30 seconds",
other explicit counts/percentages/years and swap them to <ParameterValue
param={...}> using the matching keys found in
packages/data/src/parameters/parameters-calculations-citations.ts, keeping
existing components like ParameterValue, ManualLink, manualBase and Objection
intact; if no corresponding parameter exists, remove or reword the numeric claim
to avoid unsourced figures.

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