Skip to content

Feature/treaty dashboard message first#75

Merged
mikepsinn merged 94 commits into
mainfrom
feature/treaty-dashboard-message-first
May 12, 2026
Merged

Feature/treaty dashboard message first#75
mikepsinn merged 94 commits into
mainfrom
feature/treaty-dashboard-message-first

Conversation

@mikepsinn
Copy link
Copy Markdown
Owner

@mikepsinn mikepsinn commented May 11, 2026

Summary by CodeRabbit

  • New Features

    • Post-vote share + referral-first-conversion emails and a monthly chain-digest; email templates included in visual review screenshots.
    • Treaty page: in-page signing box with streamlined sign flow and immediate share card.
    • Visual-review: per-route toolbar, per-PR previews, and automated email-template screenshots.
  • Refactor

    • Unified canonical share message/footer; simplified treaty dashboard and task detail presentation.
  • Tests

    • New Playwright e2e (treaty, email screenshots) and unit tests for email builders/share message.
  • Documentation

    • Updated project TODOs, agent guidance, and branch/sitemap status.

Review Change Stack

mikepsinn and others added 9 commits May 10, 2026 22:47
Two new triggered emails fire after a YES vote on the 1% Treaty
referendum (`TREATY_REFERENDUM_SLUG`):

1. `post-vote-share-email` to the voter. Body is the canonical
   "I love you and don't want you to suffer and die of horrible
   diseases" share message verbatim, with the voter's referral URL
   inline + a single "End war and disease" button. Designed to be
   forwarded as-is: the user hits Forward, pastes two addresses,
   sends. The email IS the share kit. Deduped on `voteId` so
   re-votes don't double-send.

2. `referral-first-conversion-email` to the referrer (when
   `vote.referredByUserId` is set). Sent EXACTLY ONCE per referrer
   — the moment of their first confirmed conversion. Per-vote
   pings get spammy fast and don't add signal after the first
   confirmation. Subsequent conversions live on /dashboard as
   ambient stats. Deduped on `referrerUserId`.

Both emails lead with the chain math (`32 doubling rounds × 2
referrals each = 4,300,000,000 humans`) so the user internalises
the multiplier and communicates it correctly to the people they
share with. A user who understands the math is a better evangelist
than one who doesn't.

Both use `scope: "onboarding"` so users can opt out via the
existing unsubscribe rails. Failure logging is best-effort
(`log.error`) — email send failure doesn't break the vote write.

TODO.md notes the deferred work:
- Monthly chain-stats digest (cron + recursive CTE; only send
  when N > 0 to skip depressing zero-count months).
- Email-template screenshots in the visual review pipeline so
  reviewers can see email copy without setting up Resend locally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "I love you and don't want you to suffer and die of horrible
diseases..." message was duplicated across three surfaces (the
DashboardShareCard, the post-vote share email body, the referral
first-conversion email's tone). Pulled into a single helper —
`lib/share-message.ts::buildShareMessage(referralUrl)` — so the
canonical wording lives in one place. Any future edit lands once
and propagates everywhere.

Added `lib/email/share-footer.ts` rendering a compact divider +
canonical-message + chain-math block. Designed to drop in at the
bottom of any email going to an authenticated user who has a
referral URL, so the share kit is always one paste away no matter
which email they received.

Wired into:
  - DashboardShareCard: replaces the inline `buildDefaultMessage`
  - post-vote-share-email: `buildPostVoteShareMessageText` now
    delegates to the shared helper
  - referral-first-conversion-email: appends a ShareFooter to both
    HTML and plain-text bodies; new `referrerReferralUrl` input
    that the vote route now fetches alongside the referrer's email

Deferred (TODO):
  - Retrofit ShareFooter onto pre-existing engaged-user email
    templates (task-assignment-notification, task-comment-notification).
    Sweeping change across templates; better as its own focused PR.
  - The monthly chain-stats digest is still its own work: cron +
    transitive-chain recursive CTE + variant copy for the
    zero-conversion month (resend the forward kit, NOT silent).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nd kit

Earlier note said to skip zero-conversion months as "silent is better than
depressing." Reversed — silence treats zero conversions as a user-failure
signal when it's actually a we-failed-to-activate signal. The user with
zero conversions is exactly who needs the nudge; the ones already converting
are self-motivated.

New rule: monthly email until unsubscribe, two variants picked by N:
  - N > 0: pure positive reinforcement (stats + chain math)
  - N == 0: resend the forward kit ("Still 30 seconds. Still two humans.")

Also captures the ShareFooter retrofit follow-up onto task-assignment and
task-comment-notification templates (engaged-user emails that already
ship via sendResendEmail and would benefit from the share kit at the
bottom).

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

Wires `buildShareFooterHtml`/`Text` into the two existing engaged-user
email templates. Both go through `sendResendEmail` to recipients with a
`recipientUserId`, so we can fetch their handle+referralCode and build
the personal referral URL the share footer needs.

When the recipient is external (no `userId` — a leader's office, a
press contact), `recipientReferralUrl` is null and no share footer
renders. That keeps outreach emails to non-voters off-brand-free.

Code path:
  notify*Notifications.server.ts → getRecipientReferralUrl(userId)
    → fetches User.referralCode + person.handle → buildUserReferralUrl()
    → passes to build*Email() → buildShareFooterHtml/Text() append

131 existing email tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New Playwright spec `e2e/email-screenshots.spec.ts` renders each
outbound email template with representative sample tokens, sets the
HTML as page content at a 640px-wide email-client viewport, and saves
the screenshot to `screenshots/{project}/email-{name}-{project}.png` —
the same shape route screenshots use, so `build-visual-review.mjs`
picks them up automatically alongside page screenshots.

Coverage:
- email-magic-link (War on Disease theme)
- email-post-vote-share
- email-referral-first-conversion
- email-task-assignment (with ShareFooter)
- email-task-comment-notification (with ShareFooter)

Reviewers no longer have to set up Resend locally and mail themselves
a test to see what outbound copy actually looks like.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #71 (feature/managed-task-tree-sync) is on main. Update TODO.md status
lines accordingly. Mark sitemap + plaintiff damages items shipped. Drop
ShareableSnippet.updatedAt — unused by consumers; cuts noise in this
generated catalog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cron at \`0 14 1 * *\` UTC (1st of every month at 14:00 UTC) calls
\`/api/cron/monthly-chain-digest\` which iterates every YES treaty
voter with an email and sends one of two variants based on the
voter's direct conversion count over the past 30 days:

  - N > 0: positive-reinforcement digest. Subject names the count +
    month. Body shows monthly + all-time totals, the doubling-rounds
    math (32 rounds × 2 each = 4.3B), a dashboard link, and the
    canonical ShareFooter so the user can paste the share message
    into any channel.

  - N == 0: re-send the forward kit. Subject pivots to "Still 30
    seconds. Still two humans you love." Body is the canonical
    share message verbatim. The zero-conversion user is exactly who
    needs the nudge; silence treats it as user-failure when it's a
    we-failed-to-activate signal.

Deduped via \`EmailLog.dedupeKey = monthly-chain-digest:{userId}:{yyyy-mm}\`
so the cron is idempotent — multiple invocations during the same
calendar month never double-send.

The current monthly count is just direct conversions
(\`referredByUserId = userId\`). A future enhancement can swap this
for a transitive recursive CTE to surface the full chain size + which
doubling round of 32 the user is actually on.

Adds the two variants to email-screenshots.spec.ts so reviewers see
both versions in the visual review.

8 vitest cases for the builders, all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Mark EmailLog FAILED when sendResendEmail returns a non-sent result
  (disabled / suppressed / etc.). Previously these left the row stuck in
  QUEUED forever; matches the pattern in task-notifications.server.ts
  (line 616).
- Fix referralUrl docstring on post-vote-share — the comment claimed
  `warondisease.org/r/ABCD or /@handle` but buildUserReferralUrl actually
  produces `${baseUrl}/vote/${handle | referralCode}`.
- "Live conversion counts live on your dashboard" -> "Live conversion
  counts are on your dashboard" (double-live). Both HTML and text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 11, 2026 05:22
@vercel
Copy link
Copy Markdown

vercel Bot commented May 11, 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 12, 2026 11:05pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removes updatedAt from shareable snippet types/exports; simplifies Treaty UI and dashboard client; adds treaty signature box; introduces canonical share message and DashboardShareCard copy changes; implements post-vote/referrer/monthly digest email templates and a central deduped sender; adds publishMonthlyChainDigest route + Vercel cron; threads per-recipient referral URLs into task notifications; and upgrades visual-review, Playwright tests, CI workflow, scripts, and docs.

Changes

Shareable snippet shape

Layer / File(s) Summary
Interface definition
packages/data/src/parameters/parameters-calculations-citations.ts
ShareableSnippet interface removes updatedAt: string; retains markdown, sourceFile, originalName.
Snippet exports
packages/data/src/parameters/parameters-calculations-citations.ts
shareableSnippets entries updated to omit updatedAt.
COURT_OF_HUMANITY_TEXT update
packages/data/src/referendums/court-of-humanity.ts
Exported snippet object drops updatedAt and migration comment updated.

Treaty UI & Dashboard

Layer / File(s) Summary
Import adjustments
packages/web/src/components/site/TreatyTaskDashboardClient.tsx
Remove session/router/state hooks and ReferralLinkBanner; keep signOut.
Component logic simplification
packages/web/src/components/site/TreatyTaskDashboardClient.tsx
Treat user as immutable from initialUser; compute referralLink and overdueSignerCount without React state/refresh.
Render path update
packages/web/src/components/site/TreatyTaskDashboardClient.tsx
No ReferralLinkBanner; show DashboardShareCard then other sections.
Treaty page
packages/web/src/app/treaty/page.tsx
Replace multi-step stepper with parameterless treaty content page; render markdown using ReactMarkdown and readerMarkdownComponents.
Signature box
packages/web/src/components/treaty/TreatyNameSignatureBox.tsx
New client component for typing name, posting YES (or saving pending vote), and showing share card or AuthForm after signing.

CI & Visual Review

Layer / File(s) Summary
Workflow permissions & steps
.github/workflows/ci.yml
Grant deployments: write, add visual-review env vars, reduce baseline lookup depth, skip visual-review env when resolving preview URL, gate per-PR gh-pages publish on visual regression success, tighten status posting, and create per-PR Visual review Deployment/Status.
Visual-review generator
packages/web/scripts/build-visual-review.mjs
Embed PR/branch/repo context, add sticky toolbar with route filter/controls, per-route "Copy context" button, client scripts, and styles.

Playwright & e2e

Layer / File(s) Summary
Playwright config
packages/web/playwright.config.ts
Set retries: isCI ? 2 : 0 to retry transient CI flakes.
Email screenshots e2e
packages/web/e2e/email-screenshots.spec.ts
Render server-side email templates, stabilize animations, screenshot variants, and attach images to test output.
Treaty page e2e
packages/web/e2e/treaty-page-structure.spec.ts
Regression test asserting treaty headline, body phrases, and signature controls render.

Share message & DashboardShareCard

Layer / File(s) Summary
Canonical share message
packages/web/src/lib/share-message.ts
Add buildShareMessage(referralUrl) helper.
Dashboard share card
packages/web/src/components/dashboard/DashboardShareCard.tsx
Use buildShareMessage, import ParameterValue & parameters, update copy to "Humanity Manager · Assignment 1".
Unit test
packages/web/src/lib/__tests__/share-message.test.ts
Test ensures buildShareMessage includes referral URL and canonical fragments.

Email system: templates, sender, publisher, cron

Layer / File(s) Summary
Post-vote & referral emails
packages/web/src/lib/email/post-vote-share-email.ts, packages/web/src/lib/email/referral-first-conversion-email.ts, packages/web/src/app/api/referendums/[slug]/vote/route.ts
Add builders and deduped senders; vote handler sends post-vote share to voter and referral-first-conversion to referrer (errors logged).
Monthly chain digest templates
packages/web/src/lib/email/monthly-chain-digest-email.ts
Add subject, HTML, and text builders with positive/resend variants and escaping/localization.
Deduped send helper
packages/web/src/lib/email/send-deduped-email.server.ts
Add sendDedupedEmail to claim EmailLog rows, call provider, and mark terminal statuses; return SendResult or duplicate marker.
Monthly digest publisher
packages/web/src/lib/email/monthly-chain-digest.server.ts
Add publishMonthlyChainDigest() to compute per-recipient counts, build inputs, send deduped emails, and aggregate results.
Cron route & Vercel cron
packages/web/src/app/api/cron/monthly-chain-digest/route.ts, packages/web/vercel.json
Add authorized Next.js GET route with force-dynamic/nodejs runtime and Vercel cron schedule 0 14 1 * *.
Share footer & referral helper
packages/web/src/lib/email/share-footer.ts, packages/web/src/lib/referral-url-helpers.server.ts
Add HTML/text share-footer builders and getRecipientReferralUrl(userId) helper.

Task notification wiring

Layer / File(s) Summary
Task assignment emails
packages/web/src/lib/tasks/task-assignment-notification-email.server.ts, packages/web/src/lib/tasks/task-assignment-notifications.server.ts
Add optional recipientReferralUrl to input; compute and include share footer when present; pass recipient referral URL when notifying assignees.
Task comment notifications
packages/web/src/lib/tasks/task-comment-notification-email.server.ts, packages/web/src/lib/tasks/task-comment-notifications.server.ts
Add optional recipientReferralUrl, thread it through the builders, conditionally include share footer in HTML/text, and compute per-recipient referral URLs during sends.

Tests & Docs

Layer / File(s) Summary
Email unit tests
packages/web/src/lib/email/__tests__/*
Add Vitest suites for monthly digest, post-vote share, and referral-first-conversion builders/variants/escaping/footer.
Reader markdown & referendum fallback
packages/web/src/components/referendum/reader-markdown-components.tsx, packages/web/src/lib/referendum-content.server.ts
New server-safe readerMarkdownComponents; add treaty fallback to use shareableSnippets.onePercentTreatyText.markdown when DB body is empty.
Donate/Signatories/Tasks UI
packages/web/src/app/donate/page.tsx, packages/web/src/app/signatories/page.tsx, packages/web/src/app/tasks/[id]/page.tsx
ParameterValue figures tweaks, remove TreatyTradeThesis intro, compact task detail sidebar into condensed delay-cost/effort displays, and update tests expecting registry labels.
Scripts & local review
packages/web/scripts/review-local.mjs, packages/web/package.json, packages/web/scripts/run-playwright.mjs
Add review-local.mjs and review:local npm script; adjust run-playwright.mjs to include treaty spec in CI smoke mode.
TODO & CLAUDE agents
TODO.md, CLAUDE.md, .claude/agents/*
Document completed session items, backlog, and agent specs for voice-critic, pr-comment-triager, and test-auditor.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

"A rabbit stitched a message bright and keen,
Footers, digests, and a treaty scene,
Seeds moved to managed flow,
Visual-review got its glow,
Deploys and todos kept the green."

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/treaty-dashboard-message-first

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 adjusts the Treaty dashboard to prioritize the share message UI, updates campaign TODO tracking, and reduces churn in the generated @optimitron/data/parameters snippet exports by removing the updatedAt field.

Changes:

  • Removes the Treaty dashboard’s ReferralLinkBanner (and the associated session refresh/state) so the share-message card is the first primary surface.
  • Updates TODO.md to reflect recent merges and to convert some funnel items into checkbox-tracked tasks.
  • Removes updatedAt from ShareableSnippet and from the generated shareableSnippets entries.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
TODO.md Updates campaign snapshot + converts select funnel tasks into checkbox format.
packages/web/src/components/site/TreatyTaskDashboardClient.tsx Removes ReferralLinkBanner and related state/hooks to keep the share-message card first.
packages/data/src/parameters/parameters-calculations-citations.ts Drops updatedAt from the exported snippet type/data.

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

Comment thread packages/data/src/parameters/parameters-calculations-citations.ts
@claude
Copy link
Copy Markdown

claude Bot commented May 11, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

mikepsinn and others added 2 commits May 11, 2026 00:39
- **Privacy bug**: voter displayName was leaked to the referrer email
  unconditionally, even when the voter's Person.isPublic is false
  (default). Referral links can be shared anywhere, so the "referrer"
  may not actually know the voter. Now gate on `voter.person.isPublic`
  — if false, the email uses "A new voter".
- **Display Identity rule**: vote/route.ts hand-rolled
  `person: { select: { displayName: true, handle: true } }` and read
  `voter.person?.displayName` directly. Now spreads `userDisplaySelect`
  and reads via `getUserDisplayName(voter)`, per CLAUDE.md.
- **DRY**: extract `sendDedupedEmail` server helper. Both senders had
  ~30 lines of identical claim/send/mark scaffolding. The helper owns
  it; each sender drops to ~10 lines (template id + dedupe key + body
  builders).
- Drop the passthrough `buildPostVoteShareMessageText` wrapper (inline
  `buildShareMessage` directly) and move its content tests to a
  share-message.test.ts where they belong.
- Drop low-signal constant-equality tests (`TEMPLATE_ID === "..."`,
  `SUBJECT === "..."`). They restate the constant declaration and only
  fail when someone intentionally renames.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mikepsinn and others added 6 commits May 11, 2026 00:51
Side-by-side screenshots in the visual review report were rendered at the
container width, which on a wide grid leaves each shot too small to read
small UI text. Click any image to open it full-viewport (max-width /
max-height: 100% with object-fit: contain). Click the image again to
toggle 1:1 native-pixel zoom (scrolls inside the backdrop). Click the
backdrop, the Close button, or press Esc to dismiss.

Pure inline CSS + vanilla JS. No new dependencies and no runtime impact
on the served pages — only the static review HTML.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- **Decouple voter share + referrer first-conversion sends**
  (vote/route.ts). Previously both lived inside a single try/catch, so a
  failure in voter fetch/send suppressed the referrer's email.
  Independent try blocks; each side logs its own error.
- **Monthly digest month-label mismatch**
  (monthly-chain-digest.server.ts). The 30-day window covers the prior
  calendar month when cron fires on day 1, but the label used
  monthLabel(now) — so an April-data email said "May 2026". Now labels
  from windowStart; dedupe bucket still keyed on now for the
  one-per-calendar-month guarantee.
- **Monthly digest EmailLog stuck in QUEUED on non-sent SendResult**.
  Switched the local send wrapper to the shared sendDedupedEmail helper,
  which marks disabled/suppressed results as FAILED with
  send_aborted:<status>. Also rolls non-sent statuses into the
  publisher's failed counter so the cron summary surfaces them.
- **Extract getRecipientReferralUrl** to
  lib/referral-url-helpers.server.ts. The function lived verbatim in
  both task-assignment-notifications and task-comment-notifications;
  CLAUDE.md "Delete on sight: copy-paste". Both callers import the
  shared helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LiveCounter rendered a real ticking value during the visual-regression
spec, so every screenshot capture saw a different millisecond of the
counter and Argos surfaced false-positive diffs on every page that
embeds it (/employees, /presidents, signer rows, dashboards).

The spec already supports `data-visual-mask="dynamic"` + the placeholder
attribute — death-counter and money-counter both use it correctly. This
component had a non-standard `data-volatile="..."` attribute that the
spec doesn't recognize, so the mask never applied.

Match the established pattern so screenshots capture a stable
placeholder (`123,456` / `$123,456,789,012`) instead of live values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous setup used `actions/upload-pages-artifact` +
`actions/deploy-pages`, which atomically replaces the entire Pages site
each deployment. Combined with the workflow's `rm -rf "$pages_root"`,
every PR's CI run wiped every other PR's visual review URL — clicking
the "Visual review" check on an older PR returned 404 the moment any
other PR's CI finished.

Switch to `peaceiris/actions-gh-pages@v4` with `keep_files: true`,
writing to a long-lived `gh-pages` branch. Each PR's content lands at
`pr-<N>/<short_sha>/` (the URL the commit status posts) AND at
`pr-<N>/latest/` (a stable per-PR link). Other PRs' directories on the
branch are preserved.

The job's `contents` permission moves from `read` to `write` so the
action can push to gh-pages. `pages: write` / `id-token: write` are
removed — they were only needed by the old deploy-pages flow.

One-time repo setup (manual): Settings → Pages → Source must be set to
"Deploy from a branch" with branch `gh-pages`. The action creates the
branch on its first successful run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LiveCounter rendered a real ticking value during the visual-regression
spec, so every screenshot capture saw a different millisecond of the
counter and Argos surfaced false-positive diffs on every page that
embeds it (/employees, /presidents, signer rows, dashboards).

The spec already supports `data-visual-mask="dynamic"` + the placeholder
attribute — death-counter and money-counter both use it correctly. This
component had a non-standard `data-volatile="..."` attribute that the
spec doesn't recognize, so the mask never applied.

Match the established pattern so screenshots capture a stable
placeholder (`123,456` / `$123,456,789,012`) instead of live values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous setup used `actions/upload-pages-artifact` +
`actions/deploy-pages`, which atomically replaces the entire Pages site
each deployment. Combined with the workflow's `rm -rf "$pages_root"`,
every PR's CI run wiped every other PR's visual review URL — clicking
the "Visual review" check on an older PR returned 404 the moment any
other PR's CI finished.

Switch to `peaceiris/actions-gh-pages@v4` with `keep_files: true`,
writing to a long-lived `gh-pages` branch. Each PR's content lands at
`pr-<N>/<short_sha>/` (the URL the commit status posts) AND at
`pr-<N>/latest/` (a stable per-PR link). Other PRs' directories on the
branch are preserved.

The job's `contents` permission moves from `read` to `write` so the
action can push to gh-pages. `pages: write` / `id-token: write` are
removed — they were only needed by the old deploy-pages flow.

One-time repo setup (manual): Settings → Pages → Source must be set to
"Deploy from a branch" with branch `gh-pages`. The action creates the
branch on its first successful run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mikepsinn and others added 3 commits May 11, 2026 01:56
The auto-generated `shareableSnippets` map in
`parameters-calculations-citations.ts` dropped `updatedAt` from
`ShareableSnippet` and its entries earlier in this PR. The hand-edited
court-of-humanity referendum body still carried `updatedAt: "2026-05-03"`
plus a module comment claiming the canonical shape included it. No code
reads `.updatedAt` from any snippet, so drop the field from this module
and update the migration note to match the new shape
(`{ markdown, sourceFile, originalName }`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three issues, all valid:

- **LiveCounter dropped `data-volatile`**, breaking
  `scripts/render-pages-to-markdown.ts` which uses that attribute to
  substitute deterministic placeholders into markdown previews
  (`pnpm copy:preview`). Restored — the component now emits BOTH
  attributes simultaneously, like death-counter and money-counter.

- **LiveCounter kept ticking during visual review**, producing layout
  jitter as the span width grew from "1" → "10" → "100" even though the
  CSS mask hid the text. Added the same `__OPTIMITRON_VISUAL_REVIEW__`
  freeze death-counter / money-counter already use: when the flag is
  set, the component renders the placeholder text directly and skips
  the setInterval entirely.

- **`pr-N/latest/` bare URL returned 404** because the directory only
  contained `latest.html`, not `index.html`. Workflow now copies
  `latest.html` to `index.html` in both the per-commit and per-PR
  latest directories so directory URLs resolve to the review page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three issues, all valid:

- **LiveCounter dropped `data-volatile`**, breaking
  `scripts/render-pages-to-markdown.ts` which uses that attribute to
  substitute deterministic placeholders into markdown previews
  (`pnpm copy:preview`). Restored — the component now emits BOTH
  attributes simultaneously, like death-counter and money-counter.

- **LiveCounter kept ticking during visual review**, producing layout
  jitter as the span width grew from "1" → "10" → "100" even though the
  CSS mask hid the text. Added the same `__OPTIMITRON_VISUAL_REVIEW__`
  freeze death-counter / money-counter already use: when the flag is
  set, the component renders the placeholder text directly and skips
  the setInterval entirely.

- **`pr-N/latest/` bare URL returned 404** because the directory only
  contained `latest.html`, not `index.html`. Workflow now copies
  `latest.html` to `index.html` in both the per-commit and per-PR
  latest directories so directory URLs resolve to the review page.

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

Addressed Copilot's updatedAt thread in commit adcf0b0.

Valid (fixed): The hand-edited packages/data/src/referendums/court-of-humanity.ts still carried updatedAt: "2026-05-03" plus a migration note claiming the canonical shape included it. No code reads .updatedAt from any snippet (grep -r 'snippet\.updatedAt' packages returns nothing), so the simpler fix was to drop the field from this module and update the migration note to match the new shape { markdown, sourceFile, originalName }. Both snippet definitions are now shape-consistent.

mikepsinn and others added 2 commits May 11, 2026 19:29
/politicians is a server-side redirect to /governments/US/politicians (same
pattern as /impact). Auto-discovery in static-pages.ts was treating it as a
public content page and asserting metadata, which a redirect never has.

Adds ROUTES.politicians to CANDIDATE_REDIRECT_ONLY_PATHS so smoke tests it
as a redirect (3xx + Location header) instead.

Same shape as the /legal fix in ff1afad — both pages exist purely to
preserve the legacy bookmark-friendly URL while the actual content lives
at the canonical path.

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

argos-ci Bot commented May 12, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
route-visuals (Inspect) 🔵 Orphan build (Review) 64 added May 12, 2026, 11:10 PM

@claude
Copy link
Copy Markdown

claude Bot commented May 12, 2026

Code review

CLAUDE.md violation: test transcribes implementation line-by-line

File: packages/db/src/managed-data/managed-humanity-v-government.test.ts (lines 11–29)

it("defines the court case verdict as a court-case referendum", () => {
expect(MANAGED_HUMANITY_V_GOVERNMENT_CASE.slug).toBe(
HUMANITY_V_GOVERNMENT_CASE_SLUG,
);
expect(MANAGED_HUMANITY_V_GOVERNMENT_CASE.juryReferendumSlug).toBe(
HUMANITY_V_GOVERNMENT_VERDICT_REFERENDUM_SLUG,
);
expect(MANAGED_HUMANITY_V_GOVERNMENT_VERDICT).toMatchObject({
slug: HUMANITY_V_GOVERNMENT_VERDICT_REFERENDUM_SLUG,
kind: ReferendumKind.COURT_CASE,
status: ReferendumStatus.ACTIVE,
});
expect(MANAGED_HUMANITY_V_GOVERNMENT_VERDICT.question).toContain(
"full damages",
);
expect(MANAGED_HUMANITY_V_GOVERNMENT_VERDICT.question).toContain(
"$2.74 million",
);
});

This test re-asserts the same named constants used to define the as const object fields. Both the test and the source import the same identifiers (HUMANITY_V_GOVERNMENT_CASE_SLUG, HUMANITY_V_GOVERNMENT_VERDICT_REFERENDUM_SLUG, ReferendumKind.COURT_CASE, ReferendumStatus.ACTIVE), then assert the literal contains those exact values. Changing a field in the source breaks the TypeScript compiler on every callsite before this test runs — zero additional signal.

Per CLAUDE.md: ❌ Tests that transcribe the implementation line-by-line

The question.toContain() assertions (lines 25–29) are the only ones exercising a cross-package resolved value. If a test is wanted here, only those two assertions belong.

voice-critic.md: rewrite goal-first instead of rule-first. The 6-section
rubric made the agent pattern-match rules to diff verbatim — flagged
<ParameterValue valueOverride="..."> as "defeats the component" when
valueOverride is the intended API for attaching citation popovers with
prose-friendly display text. New brief leads with the 5 questions the
copy should answer for a stranger, lists banned phrases as hypotheses
(verify against source before flagging), explicitly tells the agent to
read ParameterValue.tsx before judging override usage.

verify-ui-changes.mjs: scope voice gate to user-facing files only. It
was scanning .claude/agents/voice-critic.md and flagging the banned-word
list as banned-word usage. Skip .claude/, CLAUDE.md, AGENTS.md, TODO.md,
.github/, .husky/ — those legitimately list banned terms as patterns to
look for. Other gates (screenshot, copy-snapshot, test, reuse) already
filter to packages/web/src/ paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mikepsinn mikepsinn merged commit f5f002f into main May 12, 2026
9 of 10 checks passed
@mikepsinn mikepsinn deleted the feature/treaty-dashboard-message-first branch May 12, 2026 23:50
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