Skip to content

The UI is published as a snapshot read at one commit, and a publication never pretends to be a process

The UI is published as a snapshot read at one commit, and a publication never pretends to be a process

majordomus://adr/adr-0141proposed architectureinterfaceuipages

it references

  • put_in_force apps/majordomus-cli/src/ui/mod.rs (file)
  • put_in_force apps/majordomus-cli/src/web/report/snapshot.rs (file)
  • put_in_force adr-0022
  • put_in_force adr-0086
  • put_in_force adr-0135
  • put_in_force docs/GITHUB_PAGES_ARCHITECTURE.md (document)
  • put_in_force docs/MAJORDOMUS_UI.md (document)
  • put_in_force project.the-published-ui-is-the-trunks@1
  • put_in_force test/cases/1092_the_ui_snapshot_holds_every_screen.sh (test)
  • put_in_force test/cases/1093_the_ui_snapshot_links_nothing_absent.sh (test)
  • put_in_force test/cases/2983_the_published_ui_is_the_trunks.sh (test)

referenced by

141. The UI is published as a snapshot read at one commit, and a publication never pretends to be a process

Context

majordomus ui is the primary surface (ADR 0135), and it is a projection a running process answers: every screen composes capabilities at the moment it is asked. The public site is files on GitHub Pages. Until now the two never met. The topology declared the UI served-only, the site named the Cockpit and never the UI, and a person who had not cloned the repository could not see the product's first screen at all.

On 2026-10-12 the owner asked that the UI be on GitHub Pages too, and chose between three shapes: a static snapshot read at a pinned commit, documentation and screenshots only, or nothing published. The owner chose the snapshot.

A static copy of a live surface can lie in three ways, and each has happened to a dashboard somewhere:

  • It can look live. A page that says "read at" and refreshes itself, on a host that answers every query with the same file, shows an old reading under a new time.
  • It can lead to nothing. The UI's links are routes and queries (/ui/governance?rule=…, /cockpit/…, /api/v1/…). A file host answers a path with a file or with a 404, and ignores the query.
  • It can be a second implementation. A generator that renders "the same screens" from the same data is another UI, and the two drift.

Decision

  • The snapshot is the UI's own answer, written down. majordomus web report ui-snapshot builds the same Ui the server routes to, in-process, over one reading of the repository, asks it for every screen and panel and then for the pages those link to, and writes each answer as a file under target/web/ui-snapshot. Nothing is rendered by the exporter. There is no second template, no second route list and no second data path.
  • It is bounded, and the bound is fair. Screens and panels are always written. What they link to (an inspector per rule, per claim, per issue) is taken one page per family in turn, until --limit pages are written or --budget-seconds have passed, so a long list cannot take the whole bound and a slow source cannot hold a publication. A page past either bound, or a query of more than one pair, is not in the snapshot; the run records each one and what ended the reading (snapshot.json: left_out, stopped_by).
  • A file is addressed as a file. A link between two pages of the snapshot is relative, so the directory works at any mount and from disk. An inspector's query becomes a directory (governance/_rule/<id>/index.html).
  • A link only a process answers says so. The Cockpit, the API, a form, a page left out: each leads to the snapshot's one page about the live UI and carries the path it meant. A link to a surface a publication holds (the topology says which) keeps its path, and a link into the served documentation build is rebased onto the published site it is built from. No list of surfaces is written in the exporter.
  • A snapshot does not read again. The body carries data-snapshot; the UI's one script then fetches no panel and refreshes nothing. Every page states the commit it was read at, when, and by which executable, and links to how to see it live.
  • It is a surface of a publication and of no process. The producer declares the directory beside its output (surface.json): id ui-snapshot, mount /ui, published-only, built from this commit. A process serves the UI itself at /ui; a publication holds the snapshot there. The topology checks mounts per world (served, published), so the two do not collide, exactly as the home page and the deployed site both hold /.
  • Pages builds it from the commit it publishes. The Pages workflow gains one job that builds the executable and writes the snapshot, and the deploy job places it at /ui after the site's own checks and before the push. This is the second Rust command on the publication path, after rustdoc (ADR 0086), and for the same reason: its output is published bytes. It runs beside the rustdoc job, not after it.

Consequences

  • A visitor of the site opens /ui/ and sees the dashboard, the roadmap, the evidence and the rules of the repository as they stood at the published commit, with working navigation and inspectors, and a notice on every page that it is a snapshot.
  • What a snapshot cannot know is what the UI could not read where it was taken. A Pages runner has no mesh, no forge observation and no running executions; those panels read none, unknown or not answered, as the UI's rule requires (ui-derived-state), never zero. The snapshot is not evidence of the live state of anything.
  • An inspector costs what its sources cost, and on master today they are read again for every page: on a loaded workstation one inspector took 9 to 12 s, and a budget of 120 s wrote the 16 screens and panels and 8 inspectors. The capability cache of the UI-performance batch (perf/the-ui-answers-at-once) brings the same screens to tens of milliseconds, so this change is meant to land after it; until then the budget decides how deep a snapshot goes.
  • The publication waits for a build of the executable: about two minutes warm on a hosted runner (the mesh-lab job of validate run 38223440282 built it in 125 s), against about 70 s for rustdoc. The deploy job starts when both have finished.
  • The snapshot is a generated surface: it exists where its producer ran, so it is not in the committed topology (docs/generated/web.json), and scripts/site-build does not compose it. The deploy job's own step does, and refuses when the snapshot is absent or was built from another commit. Teaching the one composition (mj_web_composed) about producer-declared published-only surfaces would remove that step; it is left as a follow-up because lib/common.sh is held by another change today.
  • The site's own checks (scripts/site-check) do not judge the snapshot's pages, because it is placed after them. Its own proof is the exporter's tests and cases 1092 and 1093. A public probe of /ui/ after the deploy (as verify-rustdoc is for the reference) was not added with this decision and exists now: scripts/pages verify-ui, run by the deploy job's step the UI it publishes and by section 2c of scripts/ci/pages-check (the pages-live gate), with scripts/site-deploy refusing a site that lacks the snapshot the publication model declares (deploy.snapshots in .ai/repo/ci/pages.yaml). The rule is project.the-published-ui-is-the-trunks; test/cases/2983_the_published_ui_is_the_trunks.sh holds it.
  • The top bar's search form leads to the page about the live UI in a snapshot, because search asks the process.

Status

Proposed by session p12 on 2026-10-12 on the owner's choice of a static snapshot. Acceptance is the owner's act.

Where this comes from

canonical file
.ai/repo/adrs/0141-the-ui-is-published-as-a-snapshot-read-at-one-commit.md
identity
adr-0141
also served as
MCP resource majordomus://adr/adr-0141 · majordomus entity show adr/adr-0141 · GET /api/v1/entity?uri=majordomus://adr/adr-0141
in the Cockpit
/cockpit/objects/adr/adr-0141

This page is generated from the file above by scripts/generate-site-data; editing the page changes nothing, editing the file changes every surface. The relations above are edges of docs/generated/graph.json, which the executable writes from the same index.

Searches the titles and descriptions of every page this site publishes.