Skip to content

ADR 0025 - The hub UI is the observability console; ide/ is an unshipped prototype

  • Status: Accepted
  • Date: 2026-08-20
  • Decision owner: MAC fleet owner
  • Supersedes: the "canonical browser UI" decision in ADR 0010 (Fleet IDE cut-over and parity matrix)
  • Related: ADR 0018 (the task view is a graph under progressive disclosure), ADR 0024 (the dashboard streams the bus, not just its counts)

Context

The tree carried two browser UIs and pointed operators at the wrong one.

source build output who serves it
Observability console observe/ src/mac/ui/console/ (committed) the hub, at /ui
Fleet IDE ide/ ide/dist nothing

Measured against a live hub on 2026-08-20: GET /ui returns <title>MAC - fleet observability</title> — the console. api.py mounts Path(__file__).with_name("ui") at /ui/assets and every /ui route returns ui_dir/"console"/index.html. No route, mount, deploy step, or packaging step has ever placed ide/dist on a hub.

Meanwhile the Makefile said the opposite. run-gui: ide-run ## Run the canonical Fleet IDE development server., make help advertised "Run the Fleet IDE using the active mac login profile", and install-gui / build-gui / package-gui all built ide/. So an operator who ran make run-gui to "look at the hub UI" got a different application, wired to a hub only through a local Vite proxy, and reported it as broken. That report was correct: it was not the thing the hub runs.

Two UIs is survivable. Labelling the undeployed one canonical while shipping the other is not — it makes the build system's own documentation the least reliable description of the product.

ADR 0010 declared ide/ canonical in June 2026, when the comparison was against the legacy src/mac/ui/ dashboard. That comparison is stale: the legacy dashboard was retired from /ui and the console took the front door. The parity matrix in ADR 0010 measures the IDE against a surface that no longer holds the position it was competing for.

Decision

The observability console is the hub UI. observe/ is its source, src/mac/ui/console/ is its committed bundle, the hub serves it at /ui, and every generic GUI target builds exactly that:

  • make run-gui runs the console's own dev server against a hub.
  • make install-gui / make build-gui build observe/ into src/mac/ui/console/.
  • make package-gui packages that bundle (dist/mac-hub-ui.tar.gz).

ide/ is an unshipped local prototype, and is named that everywhere. It keeps its own ide-* targets so it stays runnable and type-checked, it is no longer reachable through any target whose name implies "the GUI", and no install/build/package path produces it. ADR 0010's canonical claim is superseded; its parity matrix stays as the historical record of what the prototype set out to do.

A test holds the two halves together. tests/ui/test_hub_ui_is_one_tree.py walks the actual chain — observe/'s vite.config.ts outDir → the directory api.py serves at /ui → what make -n run-gui executes — and fails if any link stops naming the same tree, or if run-gui reaches into ide/ again.

Alternatives considered

Make the hub serve the Fleet IDE. This is the other coherent answer, and it is a much larger change: the IDE mutates (/dispatch/tick, /agents/bulk, /roles/seed, /notifier/deliver, /secrets), so serving it from /ui also re-opens the read-only guarantee that observe/tests/readonly.test.ts asserts and that the console was created to provide. It would need an auth story, a build-and-commit story for a 14-file bundle, and a decision about what /ui means. Not a rename — a project.

Delete ide/. Rejected here, deliberately. ide/ is the only consumer of src/mac/ide_launcher.py, the /dashboard/state non-ide contract, the Electron wrapper in desktop/, and tests/ui/test_fleet_ide_api_contracts.py. Deleting it deletes the last caller of several hub routes, which is an irreversible decision about the hub API and not this one. Naming it honestly removes the operator-facing harm today and leaves that call to be made on its own evidence.

Consequences

  • make run-gui shows the operator the application the hub is actually running. The bug report "the GUI is broken" stops being generated by the build system.
  • make build and make install no longer build ide/. Anyone who wants the prototype asks for it by name: make ide-install, make ide-run, make ide-build, make ide-package.
  • dist/mac-ide-web.tar.gz is now produced only by make ide-package; make package-gui writes dist/mac-hub-ui.tar.gz.
  • The deploy banner sends operators to <hub>/ui over the tunnel instead of telling them to launch the prototype.
  • If someone repoints run-gui at ide/, or moves the console's build output, tests/ui/test_hub_ui_is_one_tree.py fails. The two cannot drift apart silently again.