FIX5 04 Fingerprint v5 NULL-Strict
04 — Fingerprint v5 (NULL-strict; includes signoff + capability state)
fn_qt001_plan_fingerprint_v5() composite over: scope, watermark, tier rules, v5 engine bodies, governance, SIGNOFF STATE (binding rows + per-eligible-collection signoff_satisfies_v5), CAPABILITY behavioral STATE, SCALE behavioral STATE, PUBLIC/DIRECTUS authority risk, KEYSET/RESUME/PERF state, identity/evidence registries, COMPLETE trigger fingerprint (gateway fn + all birth_registry triggers + tga), gateway release, dot-freeze, blockers, count.
NULL-STRICT: an ELIGIBLE row with a NULL control fact, or any undocumented NULL row, sets invalid=true → composite = literal INVALID_NULL_COMPONENT (never coalesced to 0). The 6 ineligible NULL rows (BLOCKED_UNCLASSIFIED / no_pg_table) are legitimately NULL and carry a no_go_reason; counted as documented_ineligible_null_rows=6, NOT as failures.
v_qt001_plan_fingerprint_v5_null_guard: CONSISTENT with the fingerprint — pass iff eligible_rows_with_null=0 AND undocumented_null_rows=0 AND fingerprint_invalid=false. Resolves the FIX4 contradiction (6 NULL rows vs pass=true). LIVE: eligible_null=0, documented_ineligible_null=6, invalid=false, pass=true.
v_qt001_plan_fingerprint_v5_sensitivity_tests: proves composite is a function of every component — composite = md5(component_vector) and each of signoff/capability/scale/authority/keyset/trigger hashes is a substring of the vector; plus the NULL-strict invalid branch exists. All pass. (Note: callgraph_hash was intentionally excluded from the fingerprint for performance — engine_hash already captures v5 body changes, and the structural callgraph is a dedicated readiness gate.)