Closes the last open Codex finding (#5) and removes one piece of dead
code Codex flagged in passing.
#5 — Live pool re-check before burn-in start:
Before this change, _is_unlocked compared the operator's unlock grant
against the cached drives.pool_* row. If a drive was imported into a
pool, mounted, or had ZFS labels written between the operator's
unlock click and the next ~12s poll, burn-in could still start
against the stale identity and silently destroy the new pool.
start_job now calls a fresh ssh_client.fresh_pool_check_for_drive()
immediately after the cached gate. That helper re-runs the three
detection probes (zpool list -vHP / lsblk zfs_member / findmnt) over
a fresh SSH session and returns the live answer for one devname.
If it differs from cached state we invalidate any existing unlock
grant and raise PoolMemberError with the FRESH pool name so the UI
reflects current reality. If fresh shows free but cached said locked
the drive came back to free since last poll — log it and allow.
Cost: ~200ms per burn-in start. For batch starts of 12 drives, that's
2.4s extra latency — cheap against destroying a freshly-imported pool.
Dead code removal:
ssh_client.run_badblocks() — no callers since 1.0.0-13 when the SSH
badblocks logic was inlined into burnin._stage_surface_validate_ssh
(with the asyncssh-signal-doesn't-actually-kill workaround). Removing
the dead function also lets us drop the now-unused
`from typing import Callable` import.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>