Skip to contentWolf-Rayet

Decision records

ADR-0055 Good news may not be distinguished, and a blank is three sentences

Accepted2026-09-05Phase 4, reopened

#Context

ADR-0048 refused a screen that has replaced a lie with a blank: every reading withdraws, nothing says why, and the operator sees a calm screen either way. salience — unexplained is the check and it asks that something on the View be above the resting rung while anything is withdrawn.

The same failure exists one level down and no check reaches it. A region with nothing in it renders identically whatever the reason, and there are at least three reasons:

  1. the set is empty, and that is the healthy state;
  2. the set could not be read;
  3. the reader is looking at a slice of it that happens to be empty.

Three sentences, one blank. The third is the worst of them, because the data is there and something the operator did is hiding it — and the screen cannot say so, because a blank has nowhere to put a sentence.

#Decision

empty-state is an Emitter whose subject is an absence, and its reason is required.

Two governed parts: subject, what is not here, and reason, why it is not. The second is the component. An empty state that said only *"No alarms"* would be one of the three sentences above rendered as itself, and the props boundary refuses to let a caller ship it.

#Good news may not be distinguished: the mirror of ADR-0044

That record correlated variant and level in one direction: a variant naming a live adverse condition may not be declared Ambient, because §2 defines level 1 as resting content and a condition is not resting. Eight components take their props as a two-member union because of it, and every violation fixture in the set that mentions the rule tests that half.

This component's variants include *good news*, which nothing else's did, and the mirror comes due.

clear says the queue is empty and the shift is going well. §2's Marked is *"distinguishable, not attention-seeking"* — and a screen that distinguishes good news is manufacturing urgency about the absence of urgency, which is the failure §10 names in terms when it refuses to complain about zero demands. So clear holds the Ambient rung and no other.

filtered holds either. A filter the operator set a moment ago needs no marking; one they set an hour ago and forgot is exactly what a Marked rung is for. That is a product judgement about a particular screen, and a level is where a product judgement belongs.

The refusal is in three places and they agree by construction. The props union refuses the pair where it is written; the token generator emits no mark for a quiet variant above Ambient, so ADR-0042's argument holds on this side too — *the token for that combination does not exist either, and a missing custom property falls back silently while tsc does not*; and verify-output.js asserts the absence rather than trusting it, which is the same positive half ADR-0044's own check has.

quietVariants is declared in component.config.json beside adverseVariants, listed rather than derived for that field's reason: which variants may only rest is a judgement about what each one *means*.

#There is no unreachable variant, and that is the sharpest decision here

Reason 2 above — *the set could not be read* — is the one a reader will expect this component to carry, and it deliberately cannot.

A region that is empty because its source stopped answering is reported by staleness, which reports the source rather than the reading, may reach Demanded for it, and cannot itself go stale. A component that could say it too would let one condition be reported twice with two allocations, and the screen would then be able to disagree with itself about how loud the same fact is.

That division is what gives this component its ceiling without a second argument. An absence is never the emergency — what makes an empty region an emergency is the report about its cause — so the ladder stops at Marked, and a caller who wants to shout about an empty region finds that the component for shouting is the one that knows why.

#The mark for "nothing here" is a mark with nothing in it

clear draws a bounded outline and no fill: a perimeter around an area, and the area empty. A border rather than a polygon, so it files no shape under ADR-0028's vocabulary — the case loading's ring and radio-button's circle are already in.

filtered fills that outline with the checkbox's bar, vertex for vertex, and that is this record's second finding. The bar meant *"some of the set"* and was filed as a shape the checkbox owned alone; the discipline table's comment said so, and gave as its reason that *nothing else in the system has one*. A filtered region is a set the reader is seeing some of — the same statement about a collection with the propositions taken out. So the bar joins the vocabulary the tick joined in ADR-0051, one shape name names one geometry (ADR-0028), and react:test compares the two drawings on every run.

Two private shapes have now become shared in two consecutive component passes, which is worth noticing: the private table was not wrong, it was *early*. A shape is private until a second component means the same thing by it, and neither of these was going to acquire a second user until a component arrived whose subject was different enough to need one.

#The length ceiling moves, which is the mechanism working

content-lint:scan refused this component's first copy: reason at Ambient was 47 characters against a 20-character ceiling.

ADR-0018 derives each level's ceiling as *the longest real string the scan measured shipping at that level, rounded up to the next multiple of ten*, and ADR-0019 states the consequence plainly: a longer real string at any level raises that level's ceiling on the next refresh. The ceiling is descriptive of what ships, not a budget imposed on it.

The copy was tightened as far as it honestly goes — "All lines are reporting.", 24 characters — and level 1's ceiling moves 20 → 30. empty-state ships the first level-1 part whose *role* is an explanation rather than a label, and every string that set the earlier ceiling was a name for a thing rather than a sentence about one. The rule and its read-from-a-data-file mechanism are untouched; one number moved, and a real string moved it.

#Rejected options

An unreachable variant. Argued above and refused: it is staleness's sentence, and two components saying one thing with two allocations is a screen that can disagree with itself.

Make reason optional. Every design system's empty state has an optional description. It would make this component a blank with a heading, which is the second of the three sentences and not a fix for any of them. The whole argument for the component is that the reason is what tells the three apart.

Let clear reach Marked, and leave it to the product. The option that requires no new rule and no mirror. It loses because §10's refusal to complain about zero demands is not a lint preference — it is the system's central claim about manufactured urgency, and a component that could distinguish good news is that claim's exception written into the component tier.

Raise the ceiling to fit the copy that was written first. 47 characters was one clause too many — *"Every line has reported within the last minute"* says the same thing as *"All lines are reporting"* and dates it. The refresh is honest only after the copy is as short as it should be, or the mechanism becomes a ratchet the first careless sentence turns.

Draw the empty region's frame in the scene stylesheet rather than in the component. What every screen in this repository did before, implicitly, by drawing nothing. It leaves the region blank and the reason unsaid, which is where this record started.

#Consequences

Nothing in the engine changes. No new check, no new attribute, no new failure. The rules this record adds are a type, a generator refusal and a data file's number.

Scene 53 has no must-fail twin. The illegal states are compile errors — clear at Marked, a missing reason, an unreachable variant — and there is no rendered composition that breaks a rule this component owns. What the scene proves is the positive half: two blank regions that are blank for different reasons, drawn so a reader can tell which is a fault.

quietVariants is a mechanism, not a special case. It is declared, validated in both directions, and available to any later component whose variants include good news. alert and toast have none; status-indicator's ok is a nominal *reading* rather than good news about a set, and its Ambient home is ADR-0044's own evidence.

#Measured

  • The twenty-eighth component and the seventeenth Emitter. Roster 28 built of 57.
  • 16 component tier tokens, every alias resolving in 4 themes and 2 densities; 24 non-text APCA checks, all met, worst |Lc| 16.23 against an interior-light Ambient floor of 15.
  • 3 mark cells rather than 4: clear at Ambient, filtered at Ambient and Marked. The fourth is not emitted, which is what makes the type's refusal and the tier's the same refusal.
  • bar moves from private to shared: 2 components draw it, compared vertex for vertex on every run. The shared vocabulary is triangle ×5, octagon ×8, check ×4, bar ×2; two private shapes remain.
  • Level 1's length ceiling 20 → 30, driven by one real string of 24 characters.
  • Scene 53: 1 demand against a cap of 1, three Scopes, every one under the field-night ceiling of 0.128641, zero warnings. Engine roster 49 → 50 unthemed.
  • Cascade audit 2961 → 2970 scene-state pairs across 184 → 185 scenes.
  • Eight registries at 28 of 28; 162 identity positions across 28 components.