Skip to contentWolf-Rayet

Decision records

ADR-0122 The site's own harness answers for the site's surface

Accepted2026-09-21Phase 7

#Context

ADR-0119 decided that a list derives its population from the surface it reads. It was applied to tools/check-registries.js and to tools/check-roster.js. It was never applied to apps/docs/tools/check-docs.mjs, which is the largest reader of the docs surface in the repository, and every failure below is that omission.

The omission was invisible because the script died before reaching most of itself. htmlFor read a bare /components/<slug>.html with readFileSync and threw ENOENT on the first of twenty names with no such page. The docs workflow reported one stack trace. Behind it were twelve failing checks, the same shape verify-output had when it reported 8 and a trace and then 61 once it could finish.

With the crash fixed and the rest measured, on the tree this record is committed against:

  • The two vocabularies have separated. COMPONENT_LAYERS names twenty components and none of the twenty has a directory under apps/docs/app/components. They are not absent from the site: every one of them is a permanent redirect to its replacement, status-indicator to lozenge, input-field and number-input to textfield, shell-header to page-header. The site answers for all twenty by saying which component replaced each.
  • The route shape changed. No bare /components/<slug>.html is prerendered at all. next.config.ts redirects /components/:slug to /components/:slug/examples, and the specimens are on that tab: button/examples.html carries twelve wr-button, button/code.html carries none.
  • The tab names changed. checkSceneCounts looks for href="/components/<slug>/evidence". The tabs are code, usage and changelog, plus examples. There is no evidence tab.
  • The focus read asserted nothing. It required el.closest('[class*="specimenMount"]'), and no element on the built site carries that class; the only occurrence left in the HTML is inside a CSS comment. It reported 0 focus stops and ten failures. It also spelled the selector wr-<slug>, a guess, and the guess was wrong for two components: checkbox and radio both draw .wr-choice.
  • The pairing matrix has no surface. /governance/pairing-matrix is not in the build, nothing under apps/docs references it, and no redirect names it. packages/eslint-plugin/receipts/pairing-matrix.json still declares 194 cells.
  • There is no on-this-page nav. nav[aria-label="On this page"] matches nothing on any of 400 routes. Its own check passed with 0 anchors, which is the failure mode this file's header warns about, while the sibling check reported all 160 section headings as unlisted. What the site renders instead is a per-heading anchor, a.ds__anchor with href="#default", beside each section[id].
  • The analytics check asks for a third-party loader. apps/docs/app/layout.tsx carries no googletagmanager tag, and the check fails on its absence.

None of these is a site that has fallen short. Zero of twenty, zero focus stops and zero anchors are the signature of a question asked of the wrong surface, exactly as ADR-0119 described it.

#Decision

A check in the site's harness derives both its population and the route it reads from the site's own surface. Where the site has no such surface, the check retires on this record rather than failing forever.

Applied, per check:

  1. Population is the built route list, not COMPONENT_LAYERS and not the keys of packages/tokens/component.config.json. The documented components are the slugs of the /components/<slug>/examples routes the build produced, which is the surface itself rather than a list describing it. The token tier and the package are answered for by verify-output.js and check-roster.js, which read them directly. A docs check that reads them is asserting that the docs site ought to document the supervisor's console, which it was never rebuilt to do.

The component scene-count check retires. Its subject is gone twice over: there is no evidence tab, and no component page prints a scene count. The word evidence does not appear anywhere in apps/docs/app. The check was written while the count was being moved off the index, which the code's own comment records as becoming "a reader's gallery [that] carries no internals", and the move ended with the count on no page at all. It also cannot be repaired across the vocabulary split: the engine's scenes compose wr-status-indicator and the site documents lozenge, and no mapping between the two exists to count against.

  1. The route read is the tab that holds the thing being measured, which for specimens is examples. A check that reads a bare component route is reading a redirect.
  1. A selector comes off the record that declares it, never from the component's name. verify-output.js was taught this; the harness had not caught up.
  1. The on-this-page checks are replaced by one that measures the anchor the site renders. Every main section[id] must carry a link to its own id. That is the same promise to the reader, kept by the mechanism this site actually has.
  1. The pairing matrix route check retires. The receipt stays: it is the plugin's evidence and the plugin suite reads it. A check asserting that a route renders it is asserting a page that was removed.
  1. The analytics check retires. Whether the site loads a third-party tracker is a product decision, not a property of the site's correctness, and a harness that fails until one is present is a harness holding a decision hostage.
  1. A check that cannot find its subject reports and continues. No read assembled from a name may throw. This is the rule that made the six above visible and it is the one worth stating on its own.

#Consequences

check-docs.mjs loses four checks and gains one. Its remaining reads describe the site that exists, so a failure is a defect rather than a disagreement about which site is being measured.

The disagreement itself does not go away by being moved. Twenty components are exported by @wolf-rayet/react and documented only as redirects to their replacements, and three of them are interactive records in the token tier: input-field, number-input and navigation-item. That is ADR-0121's open table, not this record's, and this record deliberately leaves it open rather than letting a docs check keep reporting it as a docs failure.

One finding this record does not absorb, because it is a real defect rather than a misdirected question: the tabs record declares .wr-tabs, and Navigation.tsx renders a fragment carrying only wr-tabs__list, wr-tabs__tab and wr-tabs__panel. Nothing renders or styles .wr-tabs. It belongs to ADR-0121's row for tabs.

#Rejected

Give every retired component a docs page again. Twenty pages documenting components that the redesign replaced, so that a check can find them. The redirects already answer the reader's question, and better: they name the replacement.

Keep the on-this-page check and build the nav. A navigation component added to every component tab to satisfy a check, on a site whose headings already carry their own anchors. The reader gains a second way to reach what they can already reach.

Exempt the failing checks with a skip list. The failures would stop and the wrong question would remain asked.