Add missions flow and safety layer#87
Conversation
…irt program economics
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR migrates the dating feature from "Earth Optimization Dates" under ChangesMissions Launch with Safety & Blocking
Shirt and Laughs Parameter Data
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
/missionspages (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/blockswithout ascope, so it will default toDatingBlockScope.ALL. If the intent is to only hide someone from discovery (as the reason string suggests) while leaving message-level blocking separate, passscope: DISCOVERYexplicitly.
packages/web/src/app/missions/dating-client.tsx:719 - The conversation “Block” action calls
/api/dating/blockswithout ascope, so it will default toDatingBlockScope.ALL. If you want a lighter-weight message-only block (and to keep theDISCOVERYvsMESSAGESscopes meaningful), passscope: MESSAGESexplicitly 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.
There was a problem hiding this comment.
💡 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".
PR review packetStart here
Review checklist
Changed files considered
Updated automatically when this PR's preview or visual review reruns. |
There was a problem hiding this comment.
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 winMap expected profile-validation errors to 4xx, not 500.
saveDatingProfilecan 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
📒 Files selected for processing (37)
AGENTS.mdpackages/data/src/parameters/parameters-calculations-citations.tspackages/web/src/app/api/dating/blocks/route.tspackages/web/src/app/api/dating/profile/route.tspackages/web/src/app/foundations/page.logged-out.mdpackages/web/src/app/foundations/page.tsxpackages/web/src/app/love/dating/page.logged-out.mdpackages/web/src/app/missions/dating-client.tsxpackages/web/src/app/missions/dating-safety-notice.tsxpackages/web/src/app/missions/discover/page.logged-out.mdpackages/web/src/app/missions/discover/page.tsxpackages/web/src/app/missions/matches/page.logged-out.mdpackages/web/src/app/missions/matches/page.tsxpackages/web/src/app/missions/messages/[conversationId]/page.logged-out.mdpackages/web/src/app/missions/messages/[conversationId]/page.tsxpackages/web/src/app/missions/messages/page.logged-out.mdpackages/web/src/app/missions/messages/page.tsxpackages/web/src/app/missions/page.logged-out.mdpackages/web/src/app/missions/page.tsxpackages/web/src/app/missions/profile/page.logged-out.mdpackages/web/src/app/missions/profile/page.tsxpackages/web/src/app/missions/questions/page.tsxpackages/web/src/app/privacy/page.logged-out.mdpackages/web/src/app/privacy/page.tsxpackages/web/src/app/shirt/page.logged-out.mdpackages/web/src/app/shirt/page.tsxpackages/web/src/app/signatories/page.logged-out.mdpackages/web/src/app/terms/page.logged-out.mdpackages/web/src/app/terms/page.tsxpackages/web/src/app/tools/page.logged-out.mdpackages/web/src/components/dashboard/DashboardShareCard.tsxpackages/web/src/lib/__tests__/dating.server.test.tspackages/web/src/lib/dating-safety.tspackages/web/src/lib/dating.server.tspackages/web/src/lib/routes.tspackages/web/src/lib/site-sitemap.tspackages/web/src/lib/site.ts
💤 Files with no reviewable changes (1)
- packages/web/src/app/love/dating/page.logged-out.md
|
Code review — 4 issues found (3 HIGH, 1 MEDIUM). No bugs. Checked security, logic, and CLAUDE.md compliance. HIGH — Hardcoded user-facing number bypasses File:
HIGH — Five snapshot files are wrong:
Per CLAUDE.md: "Never hand-edit The three HIGH — The PR implements the dating/missions MVP — work explicitly tracked in Per CLAUDE.md: "Update At minimum: check off completed dating-foundation items, add follow-up lines for deferred work (e.g., MEDIUM — New File: This newly created page has no The parent |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
packages/web/src/components/missions/MissionClient.tsx (1)
464-494:⚠️ Potential issue | 🟠 Major | ⚡ Quick winClear busy/sending state in
finallyfor async action handlers.If
fetchrejects, these handlers can leave buttons permanently disabled (busyId/isSending/isBlockingnever reset). Wrap request flows intry/finallyand reset state infinally.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 winDo not return raw backend errors to the UI.
getDisplayErrorstill 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 winAlways derive
reportedProfileIdfrom the reported message sender.When
messageIdis provided, this still allows caller-suppliedreportedProfileIdto persist, which can misattribute reports. Enforce sender ownership frommessage.senderProfileIdand 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 winRemove 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
📒 Files selected for processing (23)
packages/db/src/__tests__/seed.integration.test.tspackages/db/src/managed-data/managed-dating-catalog.tspackages/db/src/managed-data/managed-seed-data.tspackages/web/e2e/utils/static-pages.tspackages/web/e2e/utils/visual-routes.tspackages/web/scripts/build-visual-review.mjspackages/web/src/app/api/dating/profile/route.tspackages/web/src/app/messages/[conversationId]/page.tsxpackages/web/src/app/messages/page.tsxpackages/web/src/app/missions/page.logged-out.mdpackages/web/src/app/missions/page.tsxpackages/web/src/app/people/page.logged-out.mdpackages/web/src/app/people/page.tsxpackages/web/src/app/privacy/page.logged-out.mdpackages/web/src/app/privacy/page.tsxpackages/web/src/app/profile/page.tsxpackages/web/src/app/terms/page.logged-out.mdpackages/web/src/app/terms/page.tsxpackages/web/src/components/missions/MissionClient.tsxpackages/web/src/components/missions/MissionSafetyNotice.tsxpackages/web/src/lib/dating-safety.tspackages/web/src/lib/dating.server.tspackages/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
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>
There was a problem hiding this comment.
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
📒 Files selected for processing (13)
packages/data/src/parameters/parameters-calculations-citations.tspackages/db/src/__tests__/seed.integration.test.tspackages/web/src/app/api/dating/blocks/route.tspackages/web/src/app/api/dating/profile/route.tspackages/web/src/app/joke/page.logged-out.mdpackages/web/src/app/joke/page.tsxpackages/web/src/app/messages/page.tsxpackages/web/src/app/missions/page.tsxpackages/web/src/components/missions/MissionClient.tsxpackages/web/src/lib/dating-safety.tspackages/web/src/lib/dating.server.tspackages/web/src/lib/routes.tspackages/web/src/lib/site.ts
| <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> |
There was a problem hiding this comment.
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.
| 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 "we lost it somewhere" budget. The | ||
| missiles won'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'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'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 "voting | ||
| against a budget reallocation." 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 "no" 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'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 "I will sign the treaty." 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} />. |
There was a problem hiding this comment.
🛠️ 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.
Summary
/love/datingapp to/missionswhile keeping the existing/api/dating/*,Dating*helpers, and Prisma model names stable.Accelerated Medicine Foundation Inc, dba International Campaign to End War and Disease, and document the nonprofit/DBA context inAGENTS.md.Validation
git diff --checkpnpm --filter @optimitron/web exec vitest run src/lib/__tests__/dating.server.test.tspnpm --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:fastpnpm --filter @optimitron/web copy:preview -- --routes=/missions,/missions/profile,/missions/discover,/missions/matches,/missions/messages,/terms,/privacy,/shirt,/foundations,/dashboard,/tools,/signatoriespnpm --filter @optimitron/web copy:preview -- --routes=/terms,/privacy/missions,/missions/profile,/missions/discover,/missions/messages,/terms, and/privacyon desktop/mobile.Notes
[next-auth]: useSession must be wrapped in a <SessionProvider />forside-menu,side-menu-auth, and mobile/tasks/1-pct-treaty./loveremains as the unlinked dating-bio outreach page;/missionsis the canonical app route.Summary by CodeRabbit
New Features
Content
Documentation