Skip to content
Cockpit0.29.0
A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live

project.the-published-ui-is-the-trunks@1

The UI the site publishes is the trunk's, and a UI that is missing, older or cut short without saying so is a failed publication

What it is

URI
majordomus://rule/project.the-published-ui-is-the-trunks@1
Kind
rule
Identity
project.the-published-ui-is-the-trunks@1
Path
.ai/repo/rules/project/the-published-ui-is-the-trunks.v1.md
Provenance
declared
Media type
text/markdown
As a capability
rule.project.the-published-ui-is-the-trunks@1

Front matter

{
  "id": "project.the-published-ui-is-the-trunks",
  "version": 1,
  "kind": "rule",
  "title": "The UI the site publishes is the trunk's, and a UI that is missing, older or cut short without saying so is a failed publication",
  "description": "The UI snapshot a publication holds at the UI's mount is read at the commit the site itself is built from, which is the trunk's head within the window the site is held to; the publication model declares that a publication holds it, the deploy refuses a site without it, and the public probe and the live gate refuse one that is absent, names another commit, serves fewer pages than its own record lists, or does not say what ended its reading.",
  "statement": "The UI published at the site's UI mount is built from the trunk's head, within the same window the site itself is held to; a published UI that names an older commit, is missing, or was cut short without saying so is a failure of `pages-live`, not a note, and no deploy path may publish the site without it.",
  "status": "active",
  "class": "blocking",
  "depends_on": [
    "project.web-surface-declared-once@1",
    "project.land-and-publish@1",
    "project.the-crate-reference-is-published@1",
    "project.ui-derived-state@1",
    "project.empty-is-not-failure@1"
  ],
  "tags": [
    "ui",
    "publication",
    "web",
    "pages"
  ],
  "x-majordomus": {
    "tests": [
      "test/cases/2983_the_published_ui_is_the_trunks.sh",
      "test/cases/1092_the_ui_snapshot_holds_every_screen.sh",
      "test/cases/1093_the_ui_snapshot_links_nothing_absent.sh",
      "test/cases/180_publication_is_observed.sh",
      "test/cases/96_site_deploy.sh",
      "scripts/pages",
      "scripts/ci/pages-check",
      "scripts/site-deploy"
    ]
  }
}

Content

---
id: project.the-published-ui-is-the-trunks
version: 1
kind: rule
title: The UI the site publishes is the trunk's, and a UI that is missing, older or cut short without saying so is a failed publication
description: The UI snapshot a publication holds at the UI's mount is read at the commit the site itself is built from, which is the trunk's head within the window the site is held to; the publication model declares that a publication holds it, the deploy refuses a site without it, and the public probe and the live gate refuse one that is absent, names another commit, serves fewer pages than its own record lists, or does not say what ended its reading.
statement: The UI published at the site's UI mount is built from the trunk's head, within the same window the site itself is held to; a published UI that names an older commit, is missing, or was cut short without saying so is a failure of `pages-live`, not a note, and no deploy path may publish the site without it.
status: active
class: blocking
depends_on: [project.web-surface-declared-once@1, project.land-and-publish@1, project.the-crate-reference-is-published@1, project.ui-derived-state@1, project.empty-is-not-failure@1]
tags: [ui, publication, web, pages]

x-majordomus:
  tests: [test/cases/2983_the_published_ui_is_the_trunks.sh, test/cases/1092_the_ui_snapshot_holds_every_screen.sh, test/cases/1093_the_ui_snapshot_links_nothing_absent.sh, test/cases/180_publication_is_observed.sh, test/cases/96_site_deploy.sh, scripts/pages, scripts/ci/pages-check, scripts/site-deploy]
---

# Rationale

ADR 0141 put the UI on the public site as a snapshot: `majordomus web report ui-snapshot`
reads the UI once at the commit being published and writes it as files, a job of the pages
workflow builds it, and the deploy job places it at the UI's mount. The ADR said what it left
out: no public probe of the mount after the deploy.

An audit on 2026-10-12 found what that omission amounted to. Every check of the publication
read something else. `scripts/pages verify` read `/build.json`, which names the site's commit
and the composed surfaces and not the snapshot. `verify-docs` and `verify-rustdoc` read their
own surfaces. `scripts/ci/pages-check`, the `pages-live` gate that runs at every `finish` and
on a thirty-minute schedule, asked five questions and none about the UI. And the fact that a
publication holds the snapshot at all was written in one place, a step of
`.github/workflows/pages.yml`, so the other deploy path did not know it: `scripts/site-deploy`
run by a person empties the published branch and copies `site/public`, which the site build
never puts the snapshot into, and so published a site whose UI mount answered 404 with every
gate green.

Three failures followed from one cause, a surface that exists only where its producer ran and
that nothing downstream was held to:

- **Missing.** A deploy that did not run the workflow's step drops the mount.
- **Older.** A snapshot left in `site/public` from an earlier build, or a CDN still answering
  the previous files, serves a UI read at another commit beside pages that name the new one.
  The UI is the product's first screen; a reader has no way to see that it is last week's.
- **Short.** The producer is bounded on purpose and records what it left out and what stopped
  it. A tree half copied, or a record that does not say what ended the reading, looks the
  same as a whole one unless the pages the record lists are asked for.

A relation checked at one stage and owned by nobody at the next is this repository's most
repeated defect (`project.the-crate-reference-is-published` is the same finding about the
crate's reference). The stage after the producer is owned now.

# Required behaviour

1. **Declared once.** `.ai/repo/ci/pages.yaml` names, under `deploy.snapshots`, each surface a
   publication holds that the site build does not compose: the served surface it is a
   snapshot of, where its producer writes it, the producer, and the attribute its pages state
   their commit with. The mount is not written there; it is the topology's mount of that
   surface (`docs/generated/web.json`), because a publication holds the snapshot where a
   process serves the original. `scripts/pages snapshots` is the one reading of the entry, and
   the workflow, the deploy, the probe and the gate each go through it.
2. **Read at the commit that is published.** The snapshot a deploy publishes names, in its
   `surface.json`, the commit the site's pages name. The trunk's head, within the deploy
   window, is what the site is held to (`scripts/ci/pages-check`, sections 1 and 3); the UI is
   held to the site's commit and therefore to the same window, never to a looser one of its
   own.
3. **No deploy path without it.** `scripts/site-deploy` refuses a site whose tree lacks a
   declared snapshot at its mount, carries one read at another commit than `HEAD`, or carries
   a declaration that names another mount than the one it stands at. The refusal names the
   producer and the placement. `--release-metadata` is not a deploy of the site — it adds two
   files to what the branch already serves — and is not asked.
4. **Verified where a reader meets it.** `scripts/pages verify-ui` asks the public site: the
   snapshot's `surface.json` answers, names the commit and the mount it is served at; the
   landing page answers and its banner states the same commit; `snapshot.json` answers, names
   the same commit, and says what ended the reading and which pages were left out; and every
   page that record lists answers 200. The pages asked are bounded by `--max-pages`, spread
   evenly over the list, and the verdict states how many were asked, how many were not, how
   many the snapshot itself left out and what stopped it.
5. **Cut short is stated, never hidden.** A snapshot that stopped at its limit or its budget
   is a pass that says so. One whose record does not say what ended the reading, or does not
   list what it left out, is a failure: "every page" and "the pages there was time for" must
   be distinguishable from the public bytes alone.
6. **A gate, not a note.** The pages workflow runs the probe after a deploy, once the site
   serves the commit, and the step is not `continue-on-error`. `scripts/ci/pages-check` asks
   it of the published commit as section 2c; a UI that is absent, another commit's or not
   whole is exit `10`, and one that could not be judged is exit `12` with the half named in
   the verdict line, never a pass.
7. **The first screen is decided.** The figures the UI's first screen leads with are what a
   visitor reads the product by, and the process that reads a snapshot is no server: it holds
   no peer board and runs no mesh. So the screen leads only with what that process can
   decide. Its health figure is `health.report`'s `repository` verdict, the worst of the
   dimensions that are not the serving process's (`PROCESS_DIMENSIONS`: the server, its
   board, its mesh runtime), and where no mesh runtime is active the mesh figure says so and
   counts no machines. A first screen that shows the unknown badge or a source that did not
   answer in one of those figures is a finding of the probe, of the snapshot's own case, and
   therefore of the deployment.

# Failure behaviour

- `scripts/pages snapshots` exits `12` when a declared surface has no mount in the topology
  or an entry does not name its artifact, producer and marker.
- `scripts/site-deploy` exits `10` with `REFUSE` for each case of item 3, and for a model that
  is present and cannot be read; nothing is pushed.
- `scripts/pages verify-ui` exits `10` when the site answered and the UI is absent, another
  commit's or not whole — one `FAIL` line per finding on stderr, each ending with the command
  that shows it again — and `12` when the site never answered or no commit could be read to
  hold the UI to.
- `scripts/pages verify-ui` also exits `10` when the landing page leads with an undecided
  figure; the `FAIL` line names each figure.
- In `.github/workflows/pages.yml` it is the step `the UI it publishes`; a failure fails the
  deployment. In `scripts/ci/pages-check` it is section 2c of the `pages-live` gate, which
  `finish` asks (`at-finish`) and the scheduled pages run asks every thirty minutes, deploying
  when it fails.

# Verification

`test/cases/2983_the_published_ui_is_the_trunks.sh`, with no Rust executable and no network:
the model's entry resolves its mount from the topology and the workflow builds and uploads
what it says; `verify-ui` against a site served locally passes a snapshot read at the commit,
with and without `--commit`, and refuses by name a stale `built_from`, a missing
`surface.json`, a listed page that is absent, a record of another run, a banner of another
commit and a record that does not say what ended its reading, states what a cut-short
snapshot left out and what `--max-pages` did not ask, and returns 12 for a site that does not
answer; `pages-check` passes the UI read at the published commit and exits 10 for another
commit's and for none; `scripts/site-deploy` refuses a site without the snapshot, with
another commit's and with a misplaced one, and publishes `HEAD`'s. `test/cases/180` and
`test/cases/96` hold the gate and the deploy over a whole site that carries one;
`test/cases/1092` and `test/cases/1093` hold the producer.