Skip to contentWolf-Rayet

Decision records

ADR-0030 The ceiling derives from the theme, not from the scenes it judges

Superseded by ADR-00312026-09-01Phase 1, reopened

#Context

ADR-0011 set each theme's emission ceiling from the measured loads of four fixture scenes:

ceiling = worst(01-calm) + (worst(02-one-demand) − worst(01-calm)) × 1.35

That rule made a gate a function of the population it gates. Three consequences follow from the shape alone, before any number is examined.

The calibration moves when a fixture is edited. 01-calm and 02-one-demand are ordinary files; a pixel added to either moves all four ceilings, and nothing in the repository would name that as the cause.

The ceiling cannot exist until something has been rendered. It is a property a theme is supposed to have — alongside its substrate, its headroom and its ramps — and under ADR-0011 a theme with no fixture had no budget.

And the calibration population is not the judged population. ADR-0029 widened the themed set from 21 renders to 83, and the widening immediately found two calm component scenes over field-day's ceiling — 27-checkbox-calm at 0.12331 and 14-queue-calm at 0.12181, against 0.120386. Neither had ever entered the calibration, and no component scene ever had. STATUS recorded that as open item 11 and left the question of whether the ceiling, the components or field-day itself was wrong.

The question this record answers is the first one, and it is answerable without measuring a single component: a ceiling may not be a function of the loads of the scenes it judges.

#Decision

Each theme's emission ceiling is derived from that theme's own ramps and role floors, and reads no scene.

ground   = |L(surface-raised) − L(substrate)| / headroom
step     = mean adjacent |ΔL| of the neutral ramp / headroom
ceiling  = ground + step

Read as a sentence: a Scope sits on its raised ground, which it does not choose and cannot spend, and it may average one rung of the theme's own lightness ladder above that before it is reading a whole level louder than the surface it sits on.

Lightness is OKLab L of the committed 8-bit sRGB hex, through the budget engine's own oklabL8, so the ceiling is stated in the same currency as the load it bounds. The inputs are packages/tokens/receipts/ramps-<theme>.json and the role bindings in packages/tokens/semantic.config.json. Both are committed generator output. pnpm --filter @wolf-rayet/budget ceiling now renders nothing.

Themesubstrateheadroomground (surface-raised)step (neutral rung)ceilingADR-0011
interior-light0.950410.950410.04198 (neutral/8)0.109360.1513390.108647
interior-dark0.189340.810660.03884 (neutral/0)0.114080.1529200.125469
field-day0.990590.990590.03049 (neutral/8)0.112710.1431980.120386
field-night0.067210.932800.03472 (neutral/0)0.093930.1286410.105219

One ceiling per theme is carried forward from ADR-0011 unchanged, and it is now structural rather than observed. The spread is 1.19×, as it was, but the reason has changed: field-night is lowest because its lightness range is the most compressed in the system, and the ceiling is a function of that range. generator.config.json has claimed exactly that since Phase 1 — "L_max is the lowest in the system, which is what makes this theme's emission ceiling the lowest". Under ADR-0011 that sentence was a coincidence the derivation had to be checked for. It is now true by construction, and spec §9 step 5 is a check on the config rather than on the calibration.

The role floors enter twice. Directly, as surface-raised: which ramp step the ground role binds to is a per-theme decision. Indirectly, and more importantly, through the ramp itself — the generator solves every step against the theme's apcaFloors before writing it, so a contrast floor that moves a step moves this number with it. The floor still wins every conflict; it now wins it upstream of the budget rather than against it.

The four scenes survive as a validation set, widened to every scene the themed run measures — 83 renders, 26 in interior-light, 25 in interior-dark, 25 in field-day, 7 in field-night. It reports coverage and headroom into receipts/ceiling-derivation.json and sets nothing.

#Rejected options

Raise the field-day ceiling until the two calm scenes pass. The smallest possible change, and it would have turned the suite green in one line. Rejected because it is the defect this record exists to remove, applied deliberately: a ceiling moved to admit what failed it is not a ceiling, and the number it would be moved to is a function of the two scenes it was moved for. It would also have been unfalsifiable in the direction that matters — every later component that overran would have had the same remedy available, and the budget would have ratcheted upward one component at a time with no record of when it stopped meaning anything.

Reclassify 27-checkbox-calm and 14-queue-calm as must-fail fixtures. Honest about the measurement, requires no calibration change, and the mechanism already exists — 06-overlay-violation is restricted out of the dark themes exactly this way. Rejected because those two scenes are calm by construction: a checkbox group and a work queue at rest, no level-4 element between them, built to demonstrate that the system's own components fit the system's own budget. Declaring them must-fail asserts the opposite, and it is the assertion ADR-0029 already refused when it declined to restrict them out of field-day. A must-fail fixture is a scene built to break a rule; these were built to keep one.

Leave the calibration reading the Phase 0 scenes and treat the two failures as scene defects, with no calibration change. Defensible on the evidence available: the two scenes are within 2.4% of the ceiling, field-day is the theme with the least headroom by design, and a component that overruns its budget is exactly what the budget is for. Rejected because it cannot be tested. With the calibration reading four scenes and the judgement covering 83, "the components are too loud" and "the ceiling is calibrated on the wrong population" predict the same observation, and no measurement of the components can separate them while the ceiling still depends on four of them. Fixing the derivation first is what makes the component question answerable — and, as it happens, answers it: under a ceiling derived from the theme, both scenes pass with 13.9% and 14.9% of their budget unspent.

The smallest rung rather than the mean. More conservative, and it reads directly onto field-day's deltaLMin, the one place the config already names a minimum step. Rejected on the numbers: the interior themes' eased curves put their smallest rungs at the extremes, where steps are near-invisible, so the smallest rung is 0.0094 in interior-light against 0.1066 in linear field-day. The resulting spread is 3.06× and is measuring curve shape rather than what a theme can afford. Load is a mean over pixels; the rung it is compared against should be a mean too.

One rung with no ground term. Simpler, one input instead of two, and it gives field-night 0.09393. Rejected because a Scope does not choose its own ground: under ADR-0016 emission is measured from the theme substrate, so a Scope that renders its declared raised surface has already spent ground before it draws anything. A ceiling that excluded it would charge every Scope for existing. Measured, it leaves 02-one-demand--field-night — one legitimate demand, the thing the ceiling is sized to admit — at 99.1% of its budget.

#Consequences

All four ceilings rise, between 18.9% and 39.3%, and every verdict change follows from that. Two, and only two: 27-checkbox-calm--field-day and 14-queue-calm--field-day go from fail to pass. No measured load moved by any amount — the pixels are identical, all 83 scenes, and only the number they are judged against changed. STATUS open item 11 closes.

The must-fail scenes still fail, on emission, in every theme: 06-overlay-violation--interior-light by 106.9%, 03-violations by 21.3% in field-day, 20.1% in interior-dark, 17.0% in field-night — and by 2.4% in interior-light, which is the thinnest margin in this record. It was 42.6% under ADR-0011. That scene fails on scope salience as well, so its must-fail status does not rest on the emission margin; but a scene whose second failure is 2.4% away from disappearing is a scene worth watching, and it is named here rather than discovered later.

field-day moved, and the mechanism has no dependency on either scene that failed under it. Its ceiling is ground + step: ground is neutral/8's distance from the substrate, and step is the mean rung of the neutral ramp. Both are read out of packages/tokens/receipts/ramps-field-day.json. Neither expression contains a load, a fixture path, a render, or anything a scene can influence. Deleting 27-checkbox-calm and 14-queue-calm from the repository would leave all four ceilings bit-identical.

derive-ceiling.js loses its browser dependency. It was 16 renders; it is now arithmetic over committed JSON, so it runs in milliseconds and two machines agree on it for the same reason they agree on the token files. The comparison against config.js stays bounded at PLATFORM_TOLERANCE rather than becoming exact, because the bound costs nothing and the failure it catches — a value hand-edited away from its derivation — is orders of magnitude larger than the cube-root disagreement that is all the drift left.

The SAFETY factor is gone. ADR-0011 named it as "the one judgement in the derivation that is not measured"; there is now no such judgement, only the choice of which two quantities the theme's ceiling is built from, which is this record.

Spec §9 steps 1–3 are superseded and marked so in docs/spec-ramp-generator.md. Step 4 is unchanged and re-answered the same way. Step 5 survives as a check, and is now a check on the ramp configuration rather than on the calibration.

What this makes harder. The ceiling no longer reads the fixtures, so a fixture population that drifts loud will not move it — which is the point, and also means the drift has to be caught by something else. The validation set is that something else: it reports every scene's headroom against its theme in the same receipt, and the themed run is what gates. A theme whose scenes all crowd their ceiling is now visible in one table rather than absorbed into the next calibration.

What it does not settle. The attribution recorded this pass says the largest consumer in both field-day scenes is the Scope's own undeclared chrome — 64.9% of the load in 27-checkbox-calm, 57.1% in 14-queue-calm, in both cases the governed pixels no declared element covers. That is a composition question about how much a Scope's own frame should cost, and it belongs to the Phase 3 work on Scope-declared budgets that ADR-0011 already deferred. It is not a defect in either component, and no component is edited here.