Skip to content

In-file cleanup: host comms, admin mail, admin reports, data export (backend campaign 10) - #99

Closed
theobong wants to merge 5 commits into
chore/backend-campaign-09-inplace-b07-b09from
chore/backend-campaign-10-inplace-b10-b12
Closed

theobong wants to merge 5 commits into
chore/backend-campaign-09-inplace-b07-b09from
chore/backend-campaign-10-inplace-b10-b12

Conversation

@theobong

Copy link
Copy Markdown
Member

What changed

A behavior-neutral cleanup inside the files of three areas: host broadcasts, announcements, analytics and exports; admin mail, inbound mail, discovery and jurisdictions; admin reports, users, moderation, activity, events, system health, analytics and the personal data export. The 900-line broadcast pipeline is split into lifecycle, in-app sender, email sender and audience modules, and the 400-line data export into a table of sections. Large functions are split into named helpers, repeated code is shared, magic values are named, and dead code and needless exports are removed. Nothing a user sees should change.

Before you start

Verify

Nothing new to verify: every step below is a regression check.

Regression

Host announcements and broadcasts — [Web] [Mobile]

  1. A: "Host dashboard", then "Make announcement"; type a message and send it to everyone — Expect: "Announcement sent."
  2. B: open the event — Expect: the announcement is listed and B got a notification
  3. A (web): open the event's messages in the host area, start a new message with a subject and body, click "Send now", tick "I have read the message and want to send it now." and click "Send now" — Expect: the list shows the progress as " of sent"

Host analytics and exports — [Web] [Mobile]

  1. A (web): open the event's "Analytics" tab and switch through its tabs — Expect: the cards and charts load without errors
  2. Click "Export CSV" — Expect: a CSV downloads
  3. On the "Attendees" tab click "Export", then the attendees export — Expect: "Export requested"; reopen "Export" and click "Download" when ready — Expect: the roster CSV downloads
  4. A (mobile): "Host dashboard", then "View full analytics" — Expect: the event analytics open

Requesting your data — [Web] [Mobile]

  1. Open "Settings", then "Account", then "Request my data" (once only; it is rate limited) — Expect: "We emailed a copy of your data to ."
  2. Check the inbox — Expect: "Your civfix data export" with your data attached

Routing a report to the city — [Admin]

  1. On the Dashboard click "Open reports", search for the report and open it
  2. Click "Send to city" (or "Verify and send to city"), add a note and confirm — Expect: "Sent to "
  3. Under the city message box type a note and click "Send to city" — Expect: "Follow-up sent to city"
  4. Click "View conversation →" — Expect: Mail opens on the jurisdiction thread with the outbound message

Moderation queue — [Admin]

  1. On the Dashboard click "Open moderation"
  2. Select an item, click "Approve" (it reads "Keep" for user reports) and submit — Expect: the item leaves the queue with an approved or dismissed toast
  3. Select another, click "Remove", confirm and submit — Expect: a removed toast; the content is gone on the web app

Users, events and analytics — [Admin]

  1. Open the Dashboard — Expect: the tiles show counts, not "Could not load the dashboard"
  2. Click "Review accounts", search, open a user and switch its tabs — Expect: each tab loads
  3. Back on the Dashboard click "Open events" and open an event — Expect: the detail loads
  4. Click "See analytics" — Expect: the charts render

Findings addressed

Campaign PRs 7 to 10: the LOCAL DUP, READ and DEAD ledger findings for chunks B10 to B12, plus local magic values, large-function splits and un-exports.

  • B10: BE-DEAD-045; BE-DUP-062 (broadcast part), 065, 066, 067; BE-READ-054, 055, 056 (partly).
  • B11: BE-DEAD-048, 049, 050 (partly); BE-DUP-077 (as a derived regex); BE-READ-057, 058, 060 (partly).
  • B12: BE-DEAD-053, 056 (partly), 058, 060 (partly), 061 (partly); BE-DUP-081 (partly), 088 (partly); BE-READ-062, 063, 065, 066, 068, 069.

Largest splits: the broadcast pipeline (656-line factory) into four modules with the same repository call order, kill checks, address dedupe and retry handling; the data export (exportData, about 400 lines) into a section table with the same JSON key order, row caps, fit priority, redaction before fit and query order; outbound mail delivery; the jurisdiction directory listing; report routing and city follow-ups; the activity feed query builders.

Decisions for the reviewer

  • Behavior-neutral. An adversarial review compared every changed file before and after; the only differences it found are listed here.
  • Data export: the section queries moved into the table, so their raw whitespace differs; the text, parameters and order are the same. A local run of the export through the SQL recorder for three scenarios (small data, cap and byte-budget truncation, no email) gave identical attachment JSON, email, SQL and result hashes.
  • A percentage in the admin home figures is rounded the way the KPI cards already do (round1 of the ratio times 100). A comparison over 30 million ratios found no difference.
  • The admin report and user list responses return a fresh copy of the empty-counts object when no counts are requested, as before.
  • The bounce-sender regex is built from the daemon local-part list; it renders exactly the old pattern.
  • Kept although only tests use them or pending a decision: the consent repository file (decision D-105), the announcement count helper, the email suppression and event mute probes, the correspondent resolver, the moderation backfill, the empty comments section of the data export (a product decision) and the "Phase 3" value on the admin routing tile (tests pin it).
  • Types that appear in an exported function or type stay exported (DECISIONS-PENDING D-151).

User-visible copy changes

None.

Tests changed

None. No test file was edited.

Verification

  • Every commit typechecks on its own (api and media worker).
  • At this branch: pnpm lint, prettier check, check:sql clean; api unit suite by explicit path 428 files, 7116 passed, 3 skipped; media-worker unit 300 passed, 2 failed (the known arm64 ffprobe helper issue fixed in Backend test safety net: SQL transcripts, CSRF pairing, authz and characterization tests #92).
  • The offline SQL transcripts of all repository cases are identical to the parent branch.
  • Across PRs 7 to 10 (source metrics over the api and media worker, before -> after): functions over 100 lines 164 -> 121, over 300 lines 40 -> 31; as unknown as 36 -> 34; non-null assertions 115 -> 106; swallowed catches 148 -> 143; any unchanged at 6.
  • CI runs only on PRs based on main, so it starts here when the PRs below merge and GitHub retargets this one to main.

Staging checks

  • https://api.civfix.dev/readyz answers ready after the deploy (no migration in this PR).
  • A broadcast reaches B by email and in the app (the split pipeline).
  • The data export email arrives with the attachment.

Not covered

  • The admin activity feed and system health have no screen yet; they are covered by unit tests only.
  • Inbound city replies and bounces need a real mail round trip; the inbound processor and bounce detection are covered by unit tests.

🤖 Generated with Claude Code

@byteful
byteful added this pull request to stack #122 September 24, 2026 17:53
@byteful byteful closed this Sep 24, 2026
@byteful
byteful deleted the chore/backend-campaign-10-inplace-b10-b12 branch September 24, 2026 20:13
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