ADR-0116 The intensity ladder's band boundaries come from the theme, and the dark theme had been scored against the light one's
#Context
ADR-0115 removed the last rasterisation constant from the ration engine and named what was left: SUBSTRATE_MAX = 0.02 and AMBIENT_MAX = 0.12 in apps/docs/tools/score-ladder.mjs, one pair of numbers, applied to both themes, picked. The tool said so itself:
Levels 0 to 2 are absolute and measurable. The boundaries are the ones the names already imply.
That is taste, stated plainly and in good faith, and this system's standing refusal is that no threshold it carries may be picked by taste. ADR-0005 refused a fixed percentage share and a per-theme tunable token on those grounds. ADR-0036 applied the refusal to ADR-0005's own answer. These two numbers decide the level of 109 grounds per theme, the whole of /foundations/intensity, and the intensity table on all 65 component Usage tabs. They were the largest unprincipled constant left in the system.
ADR-0115 also supplied the evidence that they were wrong rather than merely underivable. The two themes' ladders differ: a rung is 0.119068 in light and 0.161732 in dark, a third wider. One pair of boundaries applied to both is one theme's ladder presented as the system's, and the scores said which theme it belonged to. Light scored 12 substrate, 61 ambient, 36 marked. Dark scored 9, 31, 69 on the same vocabulary: nearly twice as many grounds called *plainly a different surface*, from a theme with no more grounds in it.
#Decision
Both boundaries are derived, per theme, from quantities the system already derives.
substrateMax(theme) = max deviation over --wr-surface, -raised, -sunken, -overlay ambientMax(theme) = substrateMax + LADDER_STEP[theme]
Read as a sentence, and it is ADR-0030's sentence with a band in place of a ceiling: a Scope sits on a ground it did not choose and cannot spend, and an appearance may stand one rung of the theme's own lightness ladder above that before it is reading a level louder than the surface it sits on. That record derived an emission ceiling as ground + step. This is the same two terms answering the same question one layer down, about a single ground rather than a whole Scope's load.
The surfaces are taken as a family and not as one role. ADR-0030 names surface-raised specifically, because a Scope sits on its raised ground. The question here is different: it is where level 0 ends, and level 0 is named Substrate, and all four surface roles are it. Taking surface-raised alone would be actively wrong in light, where it and surface are both #ffffff, so the theme would get a substrate band of zero width and the page's own sunken surface would score as an expenditure.
Two roles resolving to one lightness are one rung, which derive-rung.mjs already enforces on the ladder this borrows.
The rung is imported from @wolf-rayet/react/scope, not recomputed. It is the same number the dominance margin is denominated in and it is defended by derive-rung.mjs against the stylesheet. A second derivation of it here would be a second chance to disagree, which is the defect packages/core/src/attributes.ts opens by naming.
The derived values: light 0.020871 and 0.139939; dark 0.069979 and 0.231711.
#What moved
109 grounds per theme, rescored with nothing else touched.
Light moves two, and one of them is the argument in miniature. wr-code-block on --wr-surface-sunken at 0.0209 was Ambient and is Substrate. A code block sitting on the page's own sunken surface was being counted as spending attention, against a boundary of 0.02 that fell 0.000871 short of the surface it was measuring. wr-progress-bar--inverse on --wr-track-inverse at 0.1239 moves from Marked to Ambient. Light finishes at 13, 61, 35 against 12, 61, 36.
Dark moves 51 of 109, and every move is downward. Thirty from Marked to Ambient, twenty-one from Ambient to Substrate. Dark finishes at 30, 40, 39 against 9, 31, 69 — a distribution that now resembles light's instead of contradicting it. Nothing in the dark theme changed. What changed is that it stopped being measured against another theme's ladder.
The rescoring poses a question about the dark theme rather than settling one. The loudest grounds now scoring Substrate in dark are --wr-accent-purple-subtlest at 0.0699 and --wr-neutral at 0.066, the latter being the ground under every default button and every default lozenge. They score Substrate because they sit closer to the page than --wr-surface-overlay does, which is a true statement about the dark theme's surfaces and an uncomfortable one: a default button's fill is less distinct from the page than one of the page's own surfaces is. Whether that is a defect in the theme is a question for a theme record. The measurement's job here was to make it askable, and it was not askable while the boundary came from light.
#Rejected options
Keep one pair of boundaries and derive it from both themes, by taking the mean or the minimum. Merit: the levels stay comparable across themes by construction, which is what a shared vocabulary wants, and a reader comparing the two columns of the scored table reads one ladder rather than two. It lost because the deviation is already normalised by headroom, which is the term that makes the *measurement* comparable between themes, and it does nothing about how a theme spaces its neutrals. Dark's rung is a third wider because its ramp is spaced a third wider. Averaging the two boundaries would give a number that is one theme's ladder wrong in one direction and the other's wrong in the other, and it would be a derived constant that is nobody's, which is worse than a picked one that is at least honest about being picked.
Derive substrateMax from surface-raised alone, as ADR-0030 does. Merit: it is literally the precedent, uses the role that record names, and needs no argument about what a family is. It lost on light, where surface and surface-raised are the same white and the band would collapse to zero width. Then the only grounds scoring Substrate would be those identical to the page, surface-sunken would be an expenditure, and level 0 would stop meaning *indistinguishable from the ground it sits on* and start meaning *the same hex*.
Leave the boundaries picked and record that they are. Merit: they were nearly right for light — AMBIENT_MAX at 0.12 against a derived 0.139939 — and rescoring 51 grounds in dark moves what every component's Usage tab says about itself, which is a large blast radius for a tool nothing gates on. It lost because the blast radius *is* the finding. Fifty-one grounds were being reported to readers at a level the theme does not support, on 65 component pages, and a system whose published argument is that attention can be measured cannot carry a measurement it knows to be one theme's opinion.
Derive the boundaries and keep the ladder table showing one column. Merit: the table is about the ladder, not about the themes, and two columns of numbers in a five-row table is a lot of number for a page whose job is the argument. It lost for the reason the scoring lost: one column would have to pick a theme, and picking light again is the defect with a narrower surface.
#Consequences
The system carries no picked threshold in the intensity path. The rung comes from the neutral ladder, the bands come from the rung and the surfaces, the dominance margin comes from the rung, and the emission formula comes from ADR-0004. derive-rung.mjs defends the one literal underneath all of it.
LADDER_BOUNDS changes shape, from a pair to a record keyed by theme, and the ladder table on /foundations/intensity grows a Light and a Dark column. Every consumer is in Ladder.tsx.
The intensity page states the derivation, because a site whose thesis is that attention is measurable should say where its boundaries came from, and it previously said nothing at all about them.
And it stops publishing a retired sentence. That page explained the Directed margin as *"more than the measurement's own noise floor"*, which is ADR-0005's clause, which ADR-0036 replaced and ADR-0115 removed from the engine. It now says a level, and says that a level is one rung across the leader's pixels. The engine, the records and the site agree.
Nothing is gated on this. The scoring is reporting, not enforcement: no check fails on a level. The 51 moved grounds change what 65 component pages say about themselves and change no verdict anywhere. That is worth stating plainly rather than leaving for a reader to infer from the absence of a failing suite.
No token, component or stylesheet moved. One tool, one generated file, one component, one page.
#Measured
- Derived boundaries: light substrateMax 0.020871, ambientMax 0.139939; dark 0.069979 and 0.231711. Rungs 0.119068 and 0.161732, imported from
@wolf-rayet/react/scope*(this pass)*. - Surface deviations, light:
surface0,surface-raised0,surface-overlay0,surface-sunken0.020871. Dark:surface0,surface-raised0.032293,surface-sunken0.036015,surface-overlay0.069979 *(this pass)*. - 109 grounds, 96 classes, 43 exported components carrying at least one ground, unchanged.
- Light: 12/61/36 → 13/61/35, 2 moved, both down — one
2→1, one1→0. - Dark: 9/31/69 → 30/40/39, 51 moved, all down — 30
2→1, 211→0. - Loudest dark grounds now scoring Substrate:
--wr-accent-purple-subtlestat 0.0699,--wr-neutralat 0.066 underwr-button,wr-button--default,wr-lozengeandwr-lozenge--default. tsc -p apps/docs/tsconfig.json --noEmitclean;/foundations/intensityreturns 200 and renders both bands *(this pass)*.