Every reference to the Driver in internal/ is reworded the same way as in
library/: the reason stays, the speaker and the quote go, an open decision
reads "provisional, pending a product decision", and agent names and decision-
sheet rows are dropped. Where a gate printed the word, its message now names
the reviewer or the ruling instead.
Three spots were code, not comments, and changed with their callers:
- the element census's allowlist field `driver` is now `ruling`, in the gate
and both presets (same validation: match, ruling, date and card required);
- check:i18n-never-translated's DRIVER-GATED marker is DECISION-PENDING, in
the entries and in the regex that counts them (its output is otherwise
byte-identical);
- the package.json description of the census says "ruling".
public-hygiene-check keeps its two patterns for the word: they are what keep
it out of the served folder.
The capability gate reported five composes refs as "resolve to no known bundle/profile" —
bp-research-header, cities-internet-map, gospel-resources-map, my-upg-100-list-map,
research-dashboard-map. All five were correct data. `composes` accepts a third form besides
'<bundle>/<profile>' and a bare '<bundle>': a registered ELEMENT TAG. Nothing needed renaming and no
author's meta.json was wrong; the resolver was short. 5 unresolved -> 0.
Two things the element form forced, both measured:
· A SELF-REFERENCE is an internal child, not a nested plugin. big-picture composes
bp-research-header, its own element, which resolved back to big-picture (cap-4) and promoted the
bundle to cap-5. Excluding self keeps "nests other plugins" from meaning "has parts".
· A name covering several profiles resolves to the HIGHEST tier among them, and both non-slashed
forms now resolve the same way. A bare bundle name and that bundle's element tag both mean "this
bundle", so taking the first profile for one and the highest for the other made the answer depend
on discovery order. gospel-resources shows it: four single leaves and one cap-4 composite in one
bundle, and losing the 4 silently demotes whoever composes it.
The rule now lives in internal/_composed-tiers.mjs, imported by the generator that WRITES the tier and
the gate that CHECKS it. It was duplicated, and a rule this fiddly does not stay identical in two files;
the drift would have shown up as a gate passing a manifest the generator would refuse to write. The
data stays independent — the gate rebuilds the edges from the <profile>.meta.json files on disk and
checks the published manifest against them.
With the count at 0, an unresolved ref FAILS in both. While five were outstanding a warning was right:
they were correct data the resolver could not read, and failing would have blamed five authors for a
gap in the tooling. At zero, a typo and a genuine plain child still look identical from here, so the
one thing that must not happen is quietly reading one as the other.
Verified on the tree as it stands: 37 profiles, 25 bundles, 10 declaring composes edges — 10 agree,
0 disagree, 0 unresolved. Each fail arm negative-tested: a one-letter typo in a ref fails; a published
tier that disagrees with its own edges fails; the same composes shape pointing at another bundle's
cap-4 instead of its own element fails, which is what proves self-exclusion is load-bearing rather
than incidental.
generate-tree derives each profile's tier from its composes edges (composing
anything is capability 4; composing a capability-4 plugin is capability 5). An
explicit type is checked against the derivation and a contradiction fails the
build naming both. check:capability-derived asserts every profile is 1-5 and
agrees. documentation/PRESET-PLUGIN-STANDARD.md describes the one
*.instances.json file a preset plugin declares.