Make fallback coverage something that can actually happen, and say so on the page

fallback.sh starts covered containers with docker start and never creates them, so a coverage list
the partner has never been sent is a promise nothing can keep — all twelve were missing. Adds the
push and remove paths, a readiness card that checks rather than infers, and the fallback state the
assistant needs to answer for it.
This commit is contained in:
Gmer4Lfe
2026-08-23 16:38:58 -04:00
parent d5cf3db2ec
commit 671e7ea5a4
7 changed files with 867 additions and 5 deletions
+26 -1
View File
@@ -73,6 +73,8 @@ function vv_fb_parse_state(string $text): array {
'tier4_started' => false,
'partnership_suspended' => false,
'partner_lost_at' => 0,
// Set only when the verdict came from the daemon rather than the file — see vv_fb_all().
'inferred' => false,
];
foreach (explode("\n", $text) as $line) {
$line = trim($line);
@@ -313,6 +315,29 @@ function vv_fb_all(): array {
: vv_fb_remote_dryrun_state($ip, $mySshKey, (int)$proc['pid']);
}
// ── A live daemon that has simply never transitioned ────────────────────────────────
// fallback.sh writes its state file ONLY on a transition; the steady NORMAL path writes
// nothing at all. So a node that has run cleanly since it was built has no file, and
// reading the file alone reports it as UNKNOWN — the same verdict given to a node whose
// daemon is dead. Those are opposite conditions and they were rendered identically.
//
// Observed on HOST2 2026-08-23: daemon live (pid 2208657, valid lock), tailscale and ssh
// both fine, no state file, card said UNKNOWN.
//
// The rule this page is built on — never claim healthy for a host you cannot verify — is
// kept: this host CAN be verified, just not from the file that was being consulted. The
// daemon holding a live lock is the evidence. reach['state_file'] still reports false,
// because there genuinely is no file; 'inferred' says where the verdict came from instead.
// Captured before the inference below rewrites it: reach[state_file] must keep answering
// "was there a file", not "do we have a verdict". Deriving it after inference made the
// card claim a state file existed on a host that has none.
$hadStateFile = ($state['state'] ?? 'UNKNOWN') !== 'UNKNOWN';
if (($state['state'] ?? 'UNKNOWN') === 'UNKNOWN' && ($proc['running'] ?? null) === true) {
$state['state'] = 'NORMAL';
$state['inferred'] = true;
}
// How fresh the state actually is. The daemon rewrites its file every check interval, so
// an age far past that interval means it is wedged even while the process still exists.
$stateAge = null;
@@ -326,7 +351,7 @@ function vv_fb_all(): array {
'tailscale' => $isMe ? true : ($ts['online'] === true),
'ip' => $isMe ? null : $ip,
'ssh' => $isMe ? true : ($running !== [] || ($proc['running'] !== null)),
'state_file' => ($state['state'] ?? 'UNKNOWN') !== 'UNKNOWN',
'state_file' => $hadStateFile,
];
$nodes[] = [