functions/scripts/backfill_signals_posted.js is written and committed but has never run. It must run before the app release that ships signal removal (#68).
Why it is needed
Contribution statistics (master spec §3.5.1: "these stats remain even when old cases are deleted or archived") used to be a live count() over signals filtered by reporter — so the number really measured signals still visible. handleSignalCreated now increments publicProfiles/{uid}.signalsPosted once at report time and nothing decrements it.
profile_page._loadSignalsPosted falls back to the old live count when the field is absent, so nothing is visibly broken today. What the backfill buys is that accounts which reported before the counter existed keep their number, and that the fallback can eventually be deleted (tracked in #73).
Why the ordering is strict
The script counts the live signals collection. Once users can remove signals, a removed signal is no longer there to count — so running it after the release writes numbers lower than the ones it exists to restore, for exactly the accounts the change was meant to protect.
Run it before the release, not after.
What is blocking it
It needs Application Default Credentials, which this machine does not have:
gcloud auth application-default login
This is the same gap that leaves functions/scripts/grant_moderator.js unverified (SPECIFICATION §14) — the 2026-08-17 moderator grants were made through the Firebase MCP instead, so that script's own code path has still never executed. Setting ADC up unblocks both.
Steps
Safety notes
- Dry-run by default;
--apply is required to write.
- Writes an absolute count via
set(..., {merge: true}), not an increment, so re-running converges rather than doubling — safe to re-run after a partial failure.
- Writes only
signalsPosted. publicProfiles also holds the display name, and a full set() would erase it.
publicProfiles has no triggers, so this notifies nobody.
- Production only. Test-mode signals are excluded by
recordSignalPosted, and counting them here would write in a number the trigger would never produce.
help-a-paw-dev IS production despite the name.
Known limitation, worth recording rather than fixing
The backfill cannot recover credit for signals that were hard-deleted under the old behaviour — those documents are gone. Affected accounts will under-report permanently.
functions/scripts/backfill_signals_posted.jsis written and committed but has never run. It must run before the app release that ships signal removal (#68).Why it is needed
Contribution statistics (master spec §3.5.1: "these stats remain even when old cases are deleted or archived") used to be a live
count()oversignalsfiltered by reporter — so the number really measured signals still visible.handleSignalCreatednow incrementspublicProfiles/{uid}.signalsPostedonce at report time and nothing decrements it.profile_page._loadSignalsPostedfalls back to the old live count when the field is absent, so nothing is visibly broken today. What the backfill buys is that accounts which reported before the counter existed keep their number, and that the fallback can eventually be deleted (tracked in #73).Why the ordering is strict
The script counts the live
signalscollection. Once users can remove signals, a removed signal is no longer there to count — so running it after the release writes numbers lower than the ones it exists to restore, for exactly the accounts the change was meant to protect.Run it before the release, not after.
What is blocking it
It needs Application Default Credentials, which this machine does not have:
This is the same gap that leaves
functions/scripts/grant_moderator.jsunverified (SPECIFICATION §14) — the 2026-08-17 moderator grants were made through the Firebase MCP instead, so that script's own code path has still never executed. Setting ADC up unblocks both.Steps
gcloud auth application-default logincd functions && node scripts/backfill_signals_posted.js --project help-a-paw-devnode scripts/backfill_signals_posted.js --project help-a-paw-dev --applySafety notes
--applyis required to write.set(..., {merge: true}), not an increment, so re-running converges rather than doubling — safe to re-run after a partial failure.signalsPosted.publicProfilesalso holds the display name, and a fullset()would erase it.publicProfileshas no triggers, so this notifies nobody.recordSignalPosted, and counting them here would write in a number the trigger would never produce.help-a-paw-devIS production despite the name.Known limitation, worth recording rather than fixing
The backfill cannot recover credit for signals that were hard-deleted under the old behaviour — those documents are gone. Affected accounts will under-report permanently.