Skip to main content

The loop and the tools

Every call returns the whole outcome, errors first. A failure arrives as data to act on rather than prose to parse. deploy is synchronous: it comes back with the result, not an acceptance. There is nothing to poll, no build log to tail and no dashboard an agent has to be told to check.

deploy → read status → crashed? read errors + log_tail → fix → deploy

A deploy that crashes is reported as a crash, never as a success, with the errors in the same response. What it does not do is leave the previous version serving: a redeploy stops the running version before it starts the new one, so a crashed redeploy takes the URL down until a deploy succeeds. That is fine while nobody depends on the app. Once somebody does, run the loop on a fork: fork, deploy to app--f1 until it is healthy and looks right, then promote. A wrong guess then costs a fork, not the URL.

Once it is up, the remaining question is whether it looks right, and screenshot is the only thing that answers it: the rendered page plus the browser's console errors and failed requests. A 200 is not a working page, and that gap is the part an agent otherwise cannot see.

  • deploy: ship a directory; get a URL, or the errors
  • logs: stdout and stderr since a timestamp, filterable to stderr alone
  • screenshot: the rendered page, console errors, failed requests
  • sql: schema, queries and migrations, against the app's SQLite or its external Postgres
  • fork · promote: clone with state, then make it the real one
  • set_secret: write-only environment secret
  • list_apps: every app with its URL, state and last deploy
  • destroy: irreversible

The console is built on exactly those endpoints. It has no private API, so there is no second contract to keep honest. Deliberately not tools: sleeping and waking happen on their own, and the durable-storage tier is a plan decision made here rather than by an agent mid-loop.