Migrate the three FIRMS fire-tracker BROADCAST paths (wildfire_growth,
wildfire_spotting, wildfire_halted) behind the registry, NO cutover.
Attribution/pass/centroid/perimeter plumbing stays inline; legacy live
path is byte-identical.
- gating/firms.py decide() discriminates on handler-stamped _kind
(firms_growth/firms_spotting/firms_halt)
- wildfire_growth reuses formatters/fire.py (verified byte-identical:
growth SELECT uses current_* columns _render doesn't read, so the wire
is the movement+anchor line with "size/containment unknown" — a latent
legacy quirk, reproduced exactly, NOT fixed)
- formatters/firms.py renders spotting + halt wires
- tier-b (flagged): the eager latch writes (fires.last_spotting_broadcast_at,
fires.halt_broadcast_at) move into deferred commit closures — a dropped
broadcast no longer burns the latch. Validated by gate-sequence, not
golden bytes. Not-cutover live path keeps eager latches verbatim.
- FIRMS broadcasts never touch event_log (eager handled=1 at pixel
storage) → no event_log flip to wrap
- _maybe_emit_cluster stays dead (unconditional return None) + test
Deferred follow-ups (unchanged): env/firms.py native hotspot broadcast
neutralization; native canonical emission.
27 new tests; fire-tracker + firms handler suites preserved; full suite
at 34-failure baseline (1597 passed).
Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>