diff --git a/doc/plan/design.md b/doc/plan/design.md index 5fd5a4f..4ad658e 100644 --- a/doc/plan/design.md +++ b/doc/plan/design.md @@ -611,8 +611,34 @@ There is no invariant here that is merely written down. loads at `tireless.internal`, all three units are active, `tireless preflight` reports the expected billing mode, and the deploy workflow is green end to end. -*Not done yet:* nothing in this stage has been deployed to bob. The workflow has -never run. +**Deployed and verified, 2026-08-07.** The workflow runs green end to end; +`tireless-api` and `tireless-poller` are active on bob; `https://tireless.internal` +serves the dashboard and proxies `/v1`; the served certificate matches disk. + +*Outstanding, and deliberately so:* `tireless-runner` is in `failed`, because the +interactive agent login has not been done as the service account on bob +(`script/infra-setup.sh` step 1). That is the invariant working — a runner with +no credentials refuses to start rather than pretending — and it is the one thing +in stage 0 that cannot be automated. + +Seven faults were found getting the first deploy green, and they are worth +knowing because most were invisible rather than loud: + +| Fault | Why it did not announce itself | +| --- | --- | +| `runs-on: fedora-43-rust` — no such runner label | Job was never scheduled; two earlier runs sat queued and were reaped as "cancelled" | +| Vhost bound `:443`, which the stream SNI router owns | `nginx -t` passes; symptom is the wrong certificate on a working handshake | +| Cert path pointed at the host identity cert | Its SAN is bob's FQDN, so only a client verifying `tireless.internal` fails | +| `--rsync-path` word-split by an unquoted variable | Loud, but only on the half of the deploy that used the variable | +| `restorecon` on a directory that cannot exist yet | Ordering: the account that owns it is created later in the same deploy | +| Config shipped `0640 root:root`, unreadable by the service user | Compounded by `--chmod` being a no-op without `-p`, so the fix would have applied once and then silently stopped | +| API bound the clap default, ignoring `[api] bind` | The service looks perfectly healthy *from the host*; only the proxy that fronts it fails | +| Health probe expanded `$unit` remotely | Checked `.service`, i.e. nothing — and would have reported healthy regardless | + +The pattern is worth carrying into later stages: the expensive faults were the +ones where a check passed while measuring nothing. Where a probe exists, it +should be able to fail — which is why the health probe now runs from the proxy +rather than bob's loopback. ### Stage 1 — Forge ingestion (read-only) diff --git a/readme.md b/readme.md index b16d781..b42b2c5 100644 --- a/readme.md +++ b/readme.md @@ -28,11 +28,15 @@ Full design, constraints and the staged implementation plan: ## Status -**Stage 0 (foundations) is built and passes its gate; nothing is deployed yet.** +**Stage 0 (foundations) is built, deployed and verified.** Working: the domain model, routing, budgets, plan validation, the policy guards, configuration loading and validation, the four system prompts, and preflight. -`tireless preflight` runs today. +The API and poller run on bob, the dashboard is served at +`https://tireless.internal`, and the deploy workflow is green end to end. + +`tireless-runner` deliberately refuses to start until the interactive agent +login has been completed as the service account — see `script/infra-setup.sh`. Not built: Postgres persistence, the forge clients, the poll loop, and every agent executor. Stages 1–8 in §7 of the design document say what lands when.