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:
@@ -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[] = [
|
||||
|
||||
Reference in New Issue
Block a user