Zoo

staging

← back to the zoo

25. Change an app by asking

A model changes your app and shows it running on a preview address, and nothing reaches staging or live until you tap ship.

The builder app: say what should change; it edits a clone, pushes preview/run-<id> (its key can push nothing else), and the creator reviews the diff and the preview, then ships through root's ship request.

This is the code serving the zoo right now: read from disk for this request, from commit 77ff74344d (staging). The zoo's own self-check fetches this page and compares it byte for byte with the file it runs.

exhibits/25-change-by-asking.php sha256 9dfa0779fa65 · raw

1<?php
2// Exhibit 25. The builder (its own app) changes an app on request: a model works in a clone, pushes
3// a preview/* branch with a key root granted for preview/* only, and nothing reaches staging or live
4// until the app's creator taps Ship. Every night the builder runs it for real on the sample app
5// `zoo-scratch` (asking /ping to answer a fresh word, charged to its own ai budget) and publishes what
6// it saw at its /proof.json; this card reads that over HTTPS and checks again, from outside, that
7// staging and live do not answer the word. (Staging's builder runs the same, 15 minutes later.)
8return [
9    "n" => 25, "wing" => "Building",
10    "title" => "Change an app by asking",
11    "promise" => "A model changes your app and shows it running on a preview address, and nothing reaches staging or live until you tap ship.",
12    "block" => "The builder app: say what should change; it edits a clone, pushes preview/run-<id> (its key can push nothing else), and the creator reviews the diff and the preview, then ships through root's ship request.",
13    "show" => function (?array $me): string {
14        $p = stored(25)["data"] ?? null;
15        $b = rc_app_url("builder");
16        if (!$p) return '<p>The <a href="' . h($b) . '/">builder</a> has not been checked yet.</p>';
17        $line = fn($k, $label) => '<li>' . $label . ': <a href="' . h($p[$k]["url"] ?? "") . '"><code>/ping</code></a> answered ' . (int)($p[$k]["status"] ?? 0)
18            . ' <code>' . h(mb_strimwidth((string)($p[$k]["body"] ?? ""), 0, 40, "…")) . '</code>' . (!empty($p[$k]["has_word"]) ? " (has the word)" : " (without it)") . "</li>";
19        return '<p>Last night the <a href="' . h($b) . '/">builder</a>\'s robot asked, on the sample app <code>zoo-scratch</code>:</p>'
20             . '<blockquote>' . h($p["request"] ?? "") . '</blockquote><ul>'
21             . $line("preview", "preview") . $line("staging", "staging") . $line("live", "live")
22             . '<li>the builder\'s key pushing <code>main</code>: ' . (!empty($p["main_push"]["refused"]) ? "refused" : "<b>accepted</b>") . "</li></ul>"
23             . '<p class="muted">' . h((string)($p["turns"] ?? "?")) . " turns, " . h((string)round(($p["tokens"] ?? 0) / 1000)) . "k tokens from the builder's own budget, " . h($p["at"] ?? "") . ".</p>";
24    },
25    "check" => function (): array {
26        $b = rc_app_url("builder");
27        [$code, , $p] = (new Browser())->get("$b/proof.json");
28        if ($code !== 200 || !is_array($p)) return [false, "the builder's /proof.json did not load ($code)"];
29        if (($p["ok"] ?? null) === null) return [false, "the builder has not run its nightly exhibit yet", $p];
30        $age = time() - (strtotime((string)($p["at"] ?? "")) ?: 0);
31        if ($age > 26 * 3600) return [false, "the builder's last nightly run is " . round($age / 3600) . " h old", $p];
32        if (!$p["ok"]) return [false, (string)($p["detail"] ?? "the nightly run failed"), $p];
33        // From outside, now: staging and live still do not answer last night's word.
34        foreach (["staging", "live"] as $e) {
35            [, , $body] = (new Browser())->get((string)($p[$e]["url"] ?? ""));
36            if (str_contains(is_string($body) ? $body : json_encode($body), (string)$p["word"])) return [false, "$e answers the preview's word: a change reached $e without a ship", $p];
37        }
38        return [true, (string)$p["detail"], $p];
39    },
40];