Phase 13 — Bridges are agents, and the UI never joined them
31 AUG AT 11:43 AM

Phase 13 — Bridges are agents, and the UI never joined them

0 LOVES 4 VIEWS
The agent screens offered a chat relay a model provider and a task-concurrency limit. Fixing that produced four tools that generalise well beyond bridges.

Reachable by clicking

A bridge had been an Agent since the first phase — but only in the database. The agent screens still treated every row as a worker: they offered a chat relay a model provider, a task-concurrency limit, and a Skills tab, and there was no way to get from an agent row to the bridge it actually was.

The acceptance criterion was deliberately behavioural rather than structural: can you reach a bridge by clicking, starting from the dashboard? Not does a link exist in the template — the whole failure being fixed was a set of surfaces that were individually fine and collectively unnavigable. It was verified by doing exactly that.

The work split into four slices, and the last one subdivided again during the build. ⚠ That subdivision existed in chat prompts before it existed in the phase doc — which is precisely the seam this program had already been bitten by, and it is noted in the run order rather than smoothed over.

The four tools

⭐ The most reusable thing this phase produced is not code. An earlier decision drew a line between two treatments for a shared surface serving a type it was not designed for. Building the last slice needed four, and the distinction generalises well beyond bridges:

  • Blurb — the control is real and applies. “Here is what this means for you.”
  • Guard — the control is damaging. “No, and here is why”, refused in the service.
  • Hide — the control is permanently inert. “Not ever.”
  • Disable — the control is temporarily inert. “Not now” — and it comes back.

The argument separating the last two is the part worth keeping. A disabled control still occupies its slot and still shows its value, so it reads as locked — which implies the value matters and that something could unlock it. That is exactly right for a Start button while a bridge is paused: the control genuinely exists and works again the moment Resume lands. It is exactly wrong for a field that never applies to this type, because “locked” invites the question unlocked by what? — and the answer is nothing.

Hide means the concept does not apply. Disable means it applies, but not yet.

⚠ And a guard is never a disabled button. Every one of these actions has a route reachable regardless of what the page renders, so a disabled control is a suggestion. If the act can damage something, the refusal lives in the service and the button is a courtesy on top.

The founding failure, inverted

One of those four sub-pages was not merely incoherent for a bridge. It was dangerous — and the bridge detail page linked straight to it.

A bridge’s key is minted by the supervisor, not by the UI, and the key-revoke service has no idea what a bridge is. Revoke a bridge’s key and the validation starts rejecting it — but the bridge does not stop, because its heartbeat failure is only logged. So it keeps relaying chat and keeps spending, while its heartbeat lapses and the status surface — correctly, from the evidence it has — reads not responding.

⭐ That is this program’s founding failure, inverted. The original incident was a screen saying alive about a dead bridge. This is a screen saying dead about a live one. Same root — the screen and the process disagreeing — and this one is reachable in a single operator click.

A warning would not have been enough, which is why this became a guard rather than a blurb: refuse to revoke a supervisor-issued key on a bridge, and say why. No re-mint rescues it either, because key provisioning runs at spawn — so a running bridge stays broken until someone restarts it.

Pushing Tin — managing a bridge fleet from inside the product
Pushing Tin — managing a bridge fleet from inside the product
Aug 29, 2026 Pushing Tin
← Back to Pushing Tin