Skip to contentWolf-Rayet

Decision records

ADR-0057 Carbon separates these on quantity, and this system has no quantity

Accepted2026-09-05Phase 4, reopened

#Context

ADR-0021's section B carries fourteen attention-carrying Arbiters. Three of them are lists — list, contained-list, structured-list — and they were entered together, from a sitemap, in one reading. They were audited together for the same reason: three components that arrive as a group are three components nobody compared.

ADR-0050 set the test and this record applies it a second time. Read what the roster row actually says; extract whatever in it is a rule; then ask whether what remains differs from something the system already has in layer, in legal levels, in interactivity, or in governed copy. Those four are every dimension the component tier has. A row that matches an existing component on all four is a name, not a component.

Section B's rows say almost nothing on their own — a slug, a layer, a ceiling — so the audit read Carbon's own usage pages for the three, from carbon-design-system/carbon-website src/pages/components/<slug>/usage.mdx, which is the source ADR-0052 used when it re-derived Carbon's roster to check ADR-0021's sitemap reading.

And the section's own argument had already declined to cover them. Section B justifies the layer in one sentence: *"Each ranks one member of a set above its siblings — a selected tab, an expanded panel, a current page, a current step, a failed upload."* Five examples for fourteen rows, and not one of the five is a list. tile gets a second sentence of its own, immediately after, because the shared one did not stretch to it. So the sentence's author had already noticed that the argument ran out — and patched it for tile and not for the three that follow.

#What Carbon says separates them, which is the finding

Each of the three pages has a *When not to use* section, and between them they form a chain that points at data-table from four directions:

PageSends the reader away whenThe axis
list"the content requires multiple columns and rows" → data tablenumber of columns
list"a basic hierarchy with tables or dividers is required" → structured list or contained listamount of structure
contained-list"nested more than one level deep and becomes overly complex and lengthy" → data tablenesting depth
contained-list"needs to contain multiple column headers" → structured listnumber of columns
structured-list"complex content ... a data table, which supports nesting items and presents a larger set of content"nesting depth, amount of content
structured-list"a small or confined space rather than on a full page" → contained listsize of the space

Four distinct separators: how many columns, how deep the nesting, how much content, how much space. Every one of them is a quantity, and this system's component tier has no quantity in it. Layer is a kind, a level is a rung on a closed ladder of four, interactivity is a boolean, and governed copy is a set of named parts. Nothing in the enforcement stack can read "how much", so two components separated only by how much would be told apart by nothing except their names — and a scene with six rows in it would satisfy both.

This is the same shape as ADR-0050's finding arriving through a different door. There, a roster row's whole content turned out to be a rule. Here, three rows' whole content turns out to be a threshold, and a threshold nobody can measure is not a distinction the system can hold.

#Decision

list, contained-list and structured-list are removed from the roster. Nothing is built and nothing is left owed. The roster moves 57 → 54, and the Carbon term of its summing sentence moves 43 → 40.

#structured-list is data-table and table-row

Carbon's structured list is column header titles, rows of cells, read-only or selectable rows, a condensed height and a hang-or-flush alignment. Every one of those is built:

data-table + table-rowa hypothetical structured-list
LayerScope hosting ArbitersScope hosting Arbiters
LevelsAmbient for the Scope, 1–3 for the rowAmbient for the Scope, 1–3 for the row
Interactivenono
Governed copycaption, labela table title, a row label

The column headers are data-table's columns. The cells and their alignment are TABLE_ROW_CHROME, which already names *cell count*, *alignment*, *gridlines*, *dividing rule* and *leading edge*. Read-only against selectable is TABLE_ROW_VARIANTS — resting and selected, drawn as fill and weight and never as hue. The condensed height is density, which ADR-0007 makes a mode and §5 makes a thing a Scope declares and a row inherits; table-row already carries an argued absence where its density prop would be, and a second table with its own height variant would put a second declaration site back exactly where that contract removed one.

What is left is Carbon's own separator: a structured list is for a *smaller* set of *simpler* data than a data table. That is the quantity.

#contained-list is data-table with one column

A list header area, a list title, list item rows, optional interactive elements in a row, and an on-page or disclosed variant. The title is a caption. The rows are rows, and an interactive element inside one is an Emitter in a cell, which is what TableRowAccepts admits and the whole reason the slot contract is written by layer rather than by component name.

The variant axis is the one part that looks like a second component and is not. On-page against disclosed is, in Carbon's words, whether the list is *"elevated by a drop shadow or border"* — and ADR-0013 replaced that vocabulary in full. Stacking here is base, raised or overlay, it is "a property a Scope declares, never a per-component effect", and data-table declares one. A component whose variant axis is another component's declaration is that component with a declaration changed.

What is left is again the quantity: one header column rather than several, a confined space rather than a page.

#list is not an Arbiter, and the layer it belongs to is one ADR-0021 already closed

This one does not collapse into a component; it fails the layer question outright.

§3 gives the Arbiter exactly one power — *"Resolves competing requests among its children"* — and Carbon defines the unordered list as items *"of equal importance without a specific order"*. A container whose definition is that its children are equal is not resolving anything; it is declaring in advance that there is nothing to resolve. It ranks no member above its siblings, which is why section B's own sentence could not produce an example for it.

The ordered list's marker looks like the counter-example and is not. A number that ranks is an ordinal, this system has one, and table-row's contract already says what it is for: *"a queue carries an ordinal because a queue is a ranking and the number is that ranking said out loud."* An ordered list is a queue-item's encoding with the condition removed.

Strip the Arbiter claim and what remains is a marker, an indent and a run of prose the system did not author — a rule, a divider and content. §3's authority table files exactly that under Substrate: *"Cannot emit. Defines the origin others are measured from ... Surface, field, rule, divider, page background."* And ADR-0021's own Consequences already settled what Substrate gets: *"View and Substrate still acquire nothing: a View is a route and a Substrate is the ground, and neither is a component anyone imports."*

list is a row filed against the one layer the roster had already decided is not a component. It is removed on the record's own sentence rather than on a new one.

#The whole of section B was read, and the row the same test is owed on next

Auditing three rows out of fourteen would be this session's own recurring defect — a check reading a narrower set than its subject — so the question was put to every row in the section. Ten of the remaining eleven answer it in a word: a selected tab, an expanded panel, a selected option, a current step, a current page, a current crumb, a selected node, a failed upload, a ranked queue member, a selected row. Each names one member raised above its siblings, which is the Arbiter's job done out loud.

tile does not answer it, and this record does not resolve it. ADR-0021 admits that row with a single sentence — *"tile is an Arbiter for the same reason §3's own example list names a card"* — and Carbon's own tile page contradicts the premise twice on the same screen: "Carbon does not have a card pattern", and tiles *"have no pre-set styles and are purposely flexible so product teams can determine their tile content."* A container with no styles and no opinions ranks nothing either. Its four variants — base, clickable, selectable, expandable — appear to resolve to four *different* things this system already has, and that is a longer argument than the one this record makes: three rows that collapse to the same place collapse together, and one row that scatters to four places needs each of the four established. It is named here as the next application of ADR-0050's test rather than half-decided inside a record about lists.

#Rejected options

Build structured-list anyway, because Carbon publishes it. The strongest option, and ADR-0021 gave the argument for it when it added text-area after the fact: *"a roster that silently omits a member is worse than one that argues for excluding it, because the omission reads as a decision nobody made."* Rejected because this is the argued exclusion that sentence asks for, not a silent omission — the row leaves the table and enters *Not carried, with the reason*, where a reader looking for it finds the argument. Building it would add a component identical to two built ones in all four dimensions, separated from them by a threshold no check in this repository can read.

Keep the three rows and give each a distinguishing rule, as ADR-0050 did for acknowledge. Merit: it is the move the precedent actually made — extract the rule, keep the decision, drop the widget. Rejected because there is no rule to extract. acknowledge's row carried a real claim about what happens to a demand when a human presses a button, and that claim survived as ADR-0050 and then as a serialized attribute. These three carry thresholds: at how many columns does a list become a table. A record stating one would be inventing a number this system has nowhere to enforce, which is the opposite of what a record is for.

Fold the three into one list row and build that. Merit: it is the collapse the audit found, said in one component instead of none, and it would keep the section's row count honest against Carbon's page count. Rejected because the collapse does not terminate at a list — it terminates at data-table and table-row, which are built. A merged row would be a third name for them, and the merge would still have to answer the layer question list fails.

Leave the audit for the pass that builds them. Merit: the rows cost nothing while they sit there. Rejected on the arithmetic ADR-0056 put on the record: each row is the full battery, 22 checks and 6 artifacts, and three of them is three components' worth of scenes, baselines, receipts, fixtures and doc pages built to hold a distinction the system cannot express. The cheapest moment to refuse a component is before it has a stylesheet.

#The fourth counted claim, and the disagreement underneath it

ADR-0052 found three arithmetic claims in this record that disagreed with it. There is a fourth, and it survived that pass because it is two claims welded into one sentence:

43 published components, 2 dropped, 41 carried. Plus 4 this system ships that Carbon does not publish: 45 on the roster, 7 built, 38 to build.

The first half is a claim about the table directly above it and is derivable on every run. The second half is a dated account of a moment — 7 built was true when the record was written and is meant to stay written. tools/check-roster.js could read neither, because a pattern matching the sentence would have had to match both. This is ADR-0049's rule arriving one level down: there, the line between a narrative and a snapshot ran between two headings of docs/STATUS.md; here it runs through the middle of a sentence.

Splitting it exposed a disagreement the record had carried since ADR-0042. The sentence says 41 carried. The summing sentence four hundred lines below says *43 rows derived from Carbon's published set*. Both were written deliberately, both were checked by someone, and they cannot both be counting the same thing — but nothing ever joined them, so nothing ever asked.

The reason is that two Carbon pages are carried as two rows each, and each split has its own record. Carbon publishes one notification page; ADR-0042 split it into alert and toast, because a condition and an event are not the same claim on attention. Carbon publishes one text-input page; ADR-0052 found that text-area belonged to it, and left both rows standing because the components are real and only the provenance had been wrong. So *38 Carbon components carried* and *40 roster rows derived from Carbon* are both true, they differ by exactly those two splits, and neither number is the other.

Two roster rows in this system are one component in Carbon's, twice. That is a fact about the roster that no sentence in it stated until now, and it is the kind of fact that makes two honest counts look like one wrong one.

The check that replaces the reconciliation is an identity rather than three figures. check-roster.js gains a fourth counted() call for the split sentence, and beside it:

5 dropped + 38 distinct Carbon slugs carried = 43, Carbon's published count

Dropped is read from the *Not carried, with the reason* table — the section, not the file, because the first attempt at this check read every two-column row in the record and reported twenty-five drops. Carried is the distinct slugs among the roster's Carbon rows, which is what makes the two splits visible instead of double-counted. The identity is the join between the two tables, and it is the only thing standing between this record and a row that leaves the roster without a reason entering the exclusion table. Observed red on exactly that breakage — one row deleted, no reason added — before it was trusted green (ADR-0041).

The run now prints the two splits by name on every pass, in ADR-0051's shape: an exception that cannot be derived is printed rather than assumed.

#Consequences

The roster is 54 and the built count does not move. 28 built of 54, three rows fewer to build, and the two counting sentences roster:check reads are updated with the table rather than after it.

Section B drops from fourteen rows to eleven, and its justifying sentence now covers every row that is under it except tile, which the section already knew and said in a second sentence.

No component, no token, no scene, no baseline and no fixture moves. This pass is a record and three table rows, exactly as ADR-0050 was. pnpm battery is run because the manifest says it is what a commit is measured by, not because this pass moved anything it measures.

One check gains two assertions and no check kind is added. Playbook §10 stands at 26. roster:check goes from three counted claims to four — which is the whole of ADR-0021's arithmetic, there being no fifth counting sentence in the record — and gains the identity that joins the roster table to the exclusion table. It had never read the exclusion table at all: the check written to stop this record disagreeing with itself was reading one of its two tables.

What this makes harder, stated plainly. A future reader who wants Carbon parity by row count will find three rows missing and a table telling them why, which is the trade ADR-0021 named when it took Carbon as an adversarial sample rather than as a specification. The sample was taken to test whether the layer model, the ladder and the ration hold across a large roster. A row that the model refuses is the sample doing its job, not the roster failing to keep up.

#Measured

  • Carbon's usage pages for the three rows, read from carbon-design-system/carbon-website src/pages/components/<slug>/usage.mdx on 2026-09-05 — the source ADR-0052 used.
  • Four separating axes across the three pages' *When not to use* sections, all four quantities.
  • Section B's justifying sentence: five examples for fourteen rows, none of them a list, plus one extra sentence for tile.
  • The four dimensions of ADR-0050's test, applied to all fourteen rows: ten answer it, three collapse, one (tile) is named and deferred.
  • Roster 57 → 54; the roster's Carbon rows 43 → 40; built 28, unchanged.
  • Carbon's published set re-derived to check the drops: carbon-design-system/carbon-website src/pages/components/, 44 directories, 43 components, matching ADR-0052.
  • The identity now checked: 5 dropped + 38 distinct slugs carried = 43, and 2 Carbon pages carried as two rows each — notification (ADR-0042) and text-input (ADR-0052) — which is the whole of the difference between 38 and 40.
  • roster:check — 9 assertions, up from 7; the identity observed red on a deleted row and green again.
  • pnpm battery — the verdict this pass is measured by.