$c["at"], "lines" => array_values(array_filter($c["checks"], fn($l) => !str_starts_with($l["line"], "(exhibit")))];
}
return [
"n" => 22, "wing" => "Operations",
"title" => "The zoo checks itself",
"promise" => "Every self-running exhibit is rechecked on a schedule, and the front page says when.",
"block" => "The zoo is an ordinary app: a five-minute job re-runs its exhibits and stores the results, and root's hourly check of the platform arrives in RC_FACTS/platform.json.",
"files" => ["bin/selfcheck.php"],
"show" => function (?array $me): string {
$t = meta("checked_at");
$out = "
Checked " . ago($t ? (int)$t : null) . "" . (($d = meta("checked_ms")) ? " (took " . round($d / 1000, 1) . " s)" : "") . ".
";
$b = back_of_house();
if (!$b) return $out . 'Root\'s check of the back of house is not published to apps yet (RC_FACTS/platform.json is missing).
';
$out .= "The back of house, checked by root " . ago(strtotime($b["at"])) . ":
";
foreach ($b["lines"] as $l) $out .= '- ' . h($l["status"]) . '' . h($l["line"]) . ' ' . h($l["detail"]) . "
";
return $out . "
";
},
// Runs last in bin/selfcheck.php, after every other exhibit.
"check" => function (): array {
$red = [];
foreach (exhibits() as $n => $e) if ($n !== 22 && $e["kind"] === "self" && light($e)["color"] === "red") $red[] = $n;
$b = back_of_house();
if (!$b) return [false, "root's back-of-house check is not readable: RC_FACTS/platform.json is missing"];
if (time() - strtotime($b["at"]) > 3 * 3600) return [false, "root's back-of-house check is " . intdiv(time() - strtotime($b["at"]), 3600) . " hours old"];
$fail = array_filter($b["lines"], fn($l) => $l["status"] !== "pass");
if ($fail) return [false, "back of house: " . implode("; ", array_map(fn($l) => $l["line"], $fail))];
if ($red) return [false, "red: exhibit" . (count($red) > 1 ? "s " : " ") . implode(", ", $red)];
return [true, "every self-running exhibit is green, and root's " . count($b["lines"]) . " back-of-house lines pass"];
},
];