Tracking issue for the work that must wait until the next app release is on the installed base — not merely published. Each item below is safe to do early only in the sense that it compiles; doing it early breaks users on the shipped build.
Filed after the #68 removal work went out (functions + rules + indexes deployed 2026-08-21). Three separate features are now queued behind the same release, and two of them are rules changes — which is the part worth reading before touching anything.
⚠️ The three rules-gated items must go out in ONE deploy
Rules deploys replace the whole ruleset. This has already bitten the project once: a --only firestore:rules from a branch without the events block silently dropped it (SPECIFICATION §13.3). So the items below must not be deployed one at a time from separate branches, or each will revert the others.
Deploy them together, from the merged branch, after cd firestore-tests && npm test, and re-read the live ruleset afterwards rather than assuming the checkout matches.
1. Narrow the three cascade delete rules (#68, step 5 of SPECIFICATION §13.3)
comments, events and takeoverRequests each grant the parent signal's reporter a delete for one reason only: the old client-side delete cascade had to empty them. That cascade is gone — signalRemoval moves the document and purgeRemoval erases descendants with an Admin SDK recursiveDelete, which bypasses rules entirely.
Why it waits: every already-released build still runs the old cascade, whose single batch covers all three subcollections. Denying it makes the batch fail atomically — so nothing is half-deleted, but Delete stops working entirely on any signal with history until the user updates. Clean failure, still a broken button.
What it closes: the documented events tamper hole, and a wider one that was never written down — there is no author-delete rule on comments at all, so today a comment's author cannot delete their own comment but the signal's reporter can delete anyone's, unaudited. That is the power master spec §18.3 deliberately routes through moderateAction.deleteComment.
2. Deploy the M-1 create rules (#67)
Blocked on the client getIdToken(true) fix (633da3b, on dev, unreleased) reaching users. See #67 for the rollback history — this was deployed once and reverted.
3. Drop the signalsPosted legacy fallback
profile_page._loadSignalsPosted falls back to a live count() over signals when publicProfiles/{uid}.signalsPosted is absent, so accounts that predate the counter do not show a proud zero.
Caveat worth recording rather than fixing: the backfill counts the live collection, so it under-reports for any account that hard-deleted signals under the old behaviour. Those documents are gone; nothing can recover them.
4. Remove the signalType compatibility shim (#70)
Already tracked; listed here so the post-release sweep is in one place.
5. Confirm #71's regression window actually closed
The case-ownership rules are already live, so released clients that are neither reporter nor holder currently get a bare "Failed to update status". Shipping the release is the fix, but it should be verified on a released build rather than assumed: a non-holder tapping the status dropdown should be offered claim-to-act, not an error.
Not blocking, but adjacent
- Moderator view of removed signals.
listQuarantined accepts source: 'removed' and QuarantinedSignal carries QuarantineSource, but no tab renders it. The abuse case it would serve is already closed at source — signalRemoval refuses while an open report names the signal — so this is a convenience, not a gap.
moderationQuarantine has no retention. Hidden signals live there forever. removedSignals now has a 30-day purge; quarantine does not, and probably should once master spec §4.10 archiving is built.
Tracking issue for the work that must wait until the next app release is on the installed base — not merely published. Each item below is safe to do early only in the sense that it compiles; doing it early breaks users on the shipped build.
Filed after the #68 removal work went out (functions + rules + indexes deployed 2026-08-21). Three separate features are now queued behind the same release, and two of them are rules changes — which is the part worth reading before touching anything.
Rules deploys replace the whole ruleset. This has already bitten the project once: a
--only firestore:rulesfrom a branch without theeventsblock silently dropped it (SPECIFICATION §13.3). So the items below must not be deployed one at a time from separate branches, or each will revert the others.Deploy them together, from the merged branch, after
cd firestore-tests && npm test, and re-read the live ruleset afterwards rather than assuming the checkout matches.1. Narrow the three cascade delete rules (#68, step 5 of SPECIFICATION §13.3)
comments,eventsandtakeoverRequestseach grant the parent signal's reporter a delete for one reason only: the old client-side delete cascade had to empty them. That cascade is gone —signalRemovalmoves the document andpurgeRemovalerases descendants with an Admin SDKrecursiveDelete, which bypasses rules entirely.firestore.rules: all three →allow delete: if false, in bothsignalsandsignals_testisParentSignalReporter()— it becomes dead code with no other consumerfirestore-tests/rules.test.jsat:338,:507,:762(the suite runs each once per collection — see the loop at the top of the file), and add denial testsWhy it waits: every already-released build still runs the old cascade, whose single batch covers all three subcollections. Denying it makes the batch fail atomically — so nothing is half-deleted, but Delete stops working entirely on any signal with history until the user updates. Clean failure, still a broken button.
What it closes: the documented
eventstamper hole, and a wider one that was never written down — there is no author-delete rule oncommentsat all, so today a comment's author cannot delete their own comment but the signal's reporter can delete anyone's, unaudited. That is the power master spec §18.3 deliberately routes throughmoderateAction.deleteComment.2. Deploy the M-1 create rules (#67)
Blocked on the client
getIdToken(true)fix (633da3b, ondev, unreleased) reaching users. See #67 for the rollback history — this was deployed once and reverted.3. Drop the
signalsPostedlegacy fallbackprofile_page._loadSignalsPostedfalls back to a livecount()oversignalswhenpublicProfiles/{uid}.signalsPostedis absent, so accounts that predate the counter do not show a proud zero.functions/scripts/backfill_signals_posted.jshas run against productionCaveat worth recording rather than fixing: the backfill counts the live collection, so it under-reports for any account that hard-deleted signals under the old behaviour. Those documents are gone; nothing can recover them.
4. Remove the
signalTypecompatibility shim (#70)Already tracked; listed here so the post-release sweep is in one place.
5. Confirm #71's regression window actually closed
The case-ownership rules are already live, so released clients that are neither reporter nor holder currently get a bare "Failed to update status". Shipping the release is the fix, but it should be verified on a released build rather than assumed: a non-holder tapping the status dropdown should be offered claim-to-act, not an error.
Not blocking, but adjacent
listQuarantinedacceptssource: 'removed'andQuarantinedSignalcarriesQuarantineSource, but no tab renders it. The abuse case it would serve is already closed at source —signalRemovalrefuses while an open report names the signal — so this is a convenience, not a gap.moderationQuarantinehas no retention. Hidden signals live there forever.removedSignalsnow has a 30-day purge; quarantine does not, and probably should once master spec §4.10 archiving is built.