Skip to content

The plan's surfaces are inferred from delivery, and nothing the plan shows is typed beside it

The plan's surfaces are inferred from delivery, and nothing the plan shows is typed beside it

majordomus://adr/adr-0166proposed plansiteuiderivationdiagrams

it references

referenced by

Nothing in the layer names it. Backlinks are derived, so an empty list is an answer rather than an omission.

166. The plan's surfaces are inferred from delivery, and nothing the plan shows is typed beside it

Context

On 2026-10-16 the owner read https://majordomus.dev/roadmap/ and https://majordomus.dev/ui/roadmap/ and wrote:

  • "tohle je evidentne dost outdated, neni to derived, neni to infered, neni to generic, dynamic, integrated, tested, documented, deployed, verified, validated, eliminuj tento paralellni vesmir" (this is evidently quite outdated, it is not derived, not inferred, not generic, dynamic, integrated, tested, documented, deployed, verified, validated; eliminate this parallel universe).
  • "make main gh_page + landing page more up-to-date, generic/generated, all of above".
  • "vsechny ascii charts/graphs nahrad v souladu s ostatnimi mermaid.js ci cytoscape, interaktivni proklikatelne, enforcovane, veskery kod unifikovane highlightuj stejnym nejlepsim zpusobem" (replace every ASCII chart or graph, consistently with the rest, by mermaid.js or cytoscape, interactive and clickable, enforced; highlight all code in one unified, best way).

Measured the same morning at master d1162668036 (v0.27.0):

  1. The public roadmap lists eight of 38 milestones. site/templates/roadmap.html keeps only milestones whose hand-typed version: is set. Those are the eight written before the project had releases: 0.1 to 1.0, two of them 0.5, in rank order 0.1, 0.5, 0.6, 0.2, 0.3, 0.4, 0.5, 1.0. The product has shipped 27 minor versions. The 30 milestones where the work happens do not appear at all.
  2. An issue's status is a stamp, and nothing compares it with what happened. lib/project.awk:156-170 and plan.rs derive status from the plan start|verify|done stamps and the dependency graph only. PR #1097 merged the whole of I2653, and I2653 still reads READY. The same holds for I2417 (#930), I2652 (#1090), I2734 (#1234), I2601 (#1092) and others: a merged pull request names the issue, and the plan has not heard of it. trace.issue, delivery.report, scripts/github-sync --check and the intent drift classes each read a part of the truth. None of them sets it against the declared status.
  3. The UI roadmap's "latest release" lags the executable that drew it. The snapshot read at 07:15Z said "Latest release 0.26.0" under a 0.27.0 header, because the release record merged at 07:44Z.
  4. The landing page states the present in hand-written prose. For example, site/data/manifesto.toml says "Nothing replans" and "No command runs this today", and marketing.toml claims "straight from the repository" over figures that come from stamps.
  5. Diagrams and code are drawn three ways.
    • Box-drawing art survives in ADRs, the handbook and a UI screen, which project.a-diagram-is-drawn-not-typed does not cover.
    • Code is highlighted by Zola's syntect on the site, by highlight.js 11.9 fetched from a CDN on majordomus ui, and by the handbook's own scripts.

Every one of these is a second account of the repository, written beside it. That is the parallel universe.

Decision

  1. An issue's delivery is inferred, once, in Rust. A capability plan.delivery answers, for every issue, the furthest of:

    • none
    • branch: a branch names it
    • pr_open: an open pull request names it
    • merged: a merge on the trunk names it, through the issue edge of I1405
    • released: the first v* tag containing that merge
    • deployed: the published site reports that release or a later one

    Each stage carries the object that proved it: the branch, the PR number, the merge commit, the tag, and the site's latest.json. It also carries the time it was observed. The forge is read through the existing served forge cache, and an unread forge is reported as unread, never as none. Nothing reimplements this in awk or shell. The site data and PLAN_STATUS.md call the executable.

  2. A contradiction between declared and inferred is a finding, and on the trunk a gate. plan.drift reports:

    • merged_not_recorded: READY, BLOCKED or ACTIVE, with merged work and no open pull request naming it.
    • active_without_work: ACTIVE, with no branch, pull request or mesh claim inside the window.
    • done_without_trace: DONE, with no merge naming it. This class is advisory, because record-only issues exist.

    merged_not_recorded older than a window declared in .ai/repo/ci/ refuses the trunk's ci, and names majordomus plan verify|done <id>. Declared status stays the record of acceptance against evidence. Inference never writes a stamp.

  3. A milestone's version is a fact about releases, not a field. version: leaves the milestone schema, and a record that carries it is refused. "Released in" is the first tag that contains the milestone's last accepted issue. An open milestone is "unreleased". The vendored roadmap-integrity rule is rewritten to match.

  4. Every plan surface draws every milestone from the one derivation. That covers /roadmap/, /plan/, the landing page's plan figures, /ui/roadmap and PLAN_STATUS.md:

    • The order is the milestone graph's.
    • Each issue shows its declared status beside its inferred stage, and links to the PR, commit and tag that prove the stage.
    • Each surface states the commit and time it was observed.
    • No template filters milestones by a hand-written field.
    • /ui/roadmap reads the release from the releases, not from a record pull request, at snapshot time.
  5. The landing page states no present tense it does not read. A sentence in site/data/*.toml that describes current state either becomes a rendered datum or goes. scripts/ci/homepage-check refuses the vocabulary of present-state claims in those files.

  6. Every relationship is drawn, and drawn interactively.

    • project.a-diagram-is-drawn-not-typed reaches version 2 and covers .ai/repo/adrs/**, .ai/repo/rules/**, handbook/** and every markdown string majordomus ui renders.
    • Diagrams are Mermaid, or Cytoscape where the graph is data.
    • Every diagram goes through the ADR 0152 viewer, with node links inferred by the build.
  7. One highlighter. Every code block, on every surface, is highlighted by syntect with one class vocabulary and one palette generated from the design tokens:

    • On the site, through Zola.
    • In majordomus ui and the reports, through the executable.
    • In the handbook, through the same generated stylesheet.

    highlight.js leaves the UI, and no surface fetches a highlighter from a CDN. A gate refuses a second highlighter.

Consequences

  • The roadmap shows 38 milestones where it showed eight, and it shows work in flight (open pull requests, mesh claims) that it never showed.
  • Releasing becomes part of the plan's own picture: an issue reads released in v0.28.0, so a merged but unreleased change is visible as such and argues for the next cut.
  • The trunk turns red when a merged issue goes unrecorded past the window. That is intended: it is the drift that produced this ADR.
  • The executable gains a syntect dependency. The palette is generated once, so the site and the UI cannot drift apart in colour.
  • The awk engine keeps deriving status from stamps. Delivery is Rust only, which case 99 (the engines agree) does not cover. This is deliberate, and recorded here.

Alternatives rejected

  • Infer status and write the stamps automatically. This rejected because a merge is not acceptance. Evidence still decides DONE, and inference only makes the gap visible and gated.
  • Keep version: and add a check that it is plausible. This was rejected because a plausible hand-written number is still hand-written. The tags already hold the answer.
  • Adopt Shiki for highlighting. This was rejected because Shiki would put Node on the path of the executable's own pages. Syntect is the engine the site already uses, available to the Rust surface without a second toolchain.

Where this comes from

canonical file
.ai/repo/adrs/0166-the-plan-surfaces-are-inferred-from-delivery.md
identity
adr-0166
also served as
MCP resource majordomus://adr/adr-0166 · majordomus entity show adr/adr-0166 · GET /api/v1/entity?uri=majordomus://adr/adr-0166
in the Cockpit
/cockpit/objects/adr/adr-0166

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.