Vocabulary-growth governance
This document is the contract for admitting, merging, or retiring a NodeKind – the closed
discriminated-union vocabulary in src/Fuaran.UI/Types.fs that is the
Fuaran UI language's sole structural vocabulary. It exists so the curation discipline that keeps
that set small is written down as a checkable rule rather than carried as habit.
Why a closed vocabulary needs a charter. Constrained decoding makes an invalid kind structurally impossible to emit – the grammar only permits kinds that exist. The error class that remains, and the one that grows as the set approaches its natural plateau, is valid-but-wrong-kind selection:
TablevsDataGrid,CalloutvsToastvsBadge. A small, canonical, near-synonym- free vocabulary is the strongest form of the language's error-scope promise – one obvious emission per pattern. Every kind added trades a sliver of that promise for expressive reach. This charter is the rule that keeps the trade deliberate.
The static closed kind set is a feature, not a limitation: it is what makes emission statically grammar-constrainable, one-canonical-form-per-pattern, and cheap for an AI consumer to hold in working memory. Growth is expected – toward a natural plateau of roughly 60 kinds – but every step is evidenced, costed, and measured.
Scope. This charter governs the NodeKind vocabulary (the structural wire cases). It does not
govern author-surface conveniences (smart constructors, Defaults) or portability seams (runtime
interfaces) – those are additive and expected and grow freely. Application-space composition
(reusable fragments, content-packs, a consumer app's repeated patterns) is a separate growth axis with
its own home; it is never the language's kind-vocabulary axis. This charter supersedes the
fragment-first kind-routing authoring gate that an earlier plan (Phase 380) once carried – Phase 380 was
re-scoped to application-space fragments and now points here for kind governance.
1. The admission checklist
A proposal to add a NodeKind case must clear every gate below. A proposal that fails any gate is
not admitted; it is redirected (to a variant – §2; to a composition – §2; or to application-space
fragments – out of scope here).
1.1 Demand evidence (mandatory – no evidence, no admission)
The proposal cites at least one of these observed, recorded signals – not a hypothetical:
- An expression gap in a real translation. A real consumer-view translation through the typed surface reached a pattern the existing kinds could not express without an escape hatch, and the gap is recorded in the vocabulary-gap ledger (the running inventory of "patterns the vocabulary couldn't express"; ledger code FUARAN054).
Custom-node fallout. A shipped case study or demo resorted toNodeKind.Custom(the principled escape hatch) for a pattern that recurs across consumers – recurrence, not a one-off, is the signal. Since Phase 1103 the evaluation harness reports that recurrence as a standing rate over stored emissions rather than as an anecdote from whichever run somebody had open, so "recurs" is a number a proposal can cite and a reader can re-derive.- An emission-eval miss. An AI consumer emitting the canonical prompt corpus produced a "no expressible tree – missing contract case" outcome for a prompt (distinct from a low quality score, which is a legitimate eval result and is not admission evidence).
Where signal (c) comes from – the shadow evaluation leg (Phase 1103). The third bullet has a
structural problem the first two do not: an evaluation that teaches the closed kind list suppresses
the very outcome it asks for. A model handed the vocabulary does not invent a $type, and a prompt set
written against the vocabulary does not pose a task the vocabulary already cannot express – so the
channel can read clean for months while the gap it exists to detect goes unrecorded, which is how the
media vocabulary came to be admitted by other means. The evaluation harness therefore also runs a
small, periodic unconstrained shadow leg: the same prompts with the kind-list teaching removed,
solely to harvest what a model reaches for when nothing stops it – invented $type names,
discriminators the wire format does not use at all, and emissions the vocabulary has no shape to
receive.
Sightings from that leg are admissible §1.1(c) evidence, under one condition: a sighting carries its shadow provenance wherever it is cited. A shadow emission crosses no gate, scores no cell, seeds no pattern bank and enters no corpus – it is quarantined by construction, which is what makes the leg safe to run at all – so a proposal citing one as though it were a scored emission would be claiming a reliability that emission was never measured for. Cite the leg, its teaching stamp and its run. A sighting whose provenance has been dropped is not evidence, and an absence of sightings is only a result when a leg actually ran: a leg that was not executed has measured nothing, which is a different fact from a leg that found nothing. Recurrence governs here exactly as it does for signal (b) – one sighting is a sighting; a repeated or cross-model one is demand.
What the shadow leg still cannot see, and the instrument that answers it (2026-09-03). The leg measures what a model reaches for, so it can only ever report on classes somebody thought to pose a task about — which leaves the same blind spot one level up: a capability nobody has written a prompt for produces no sightings, and an absence of sightings on a class nobody probed reads exactly like an absence of demand. The harness therefore also runs a platform-baseline census: a periodic diff of the whole expressible surface against named external checklists (the HTML element inventory, WCAG 2.2, and a named list of what a page can do), which enumerates rather than measures and so finds the classes no task was ever posed about. Its first run found four capability classes carrying no Appendix A row of any kind — not admitted, not declined, not reserved — and each is now probed by a purpose-built family on the shadow leg above.
The two are complementary and neither substitutes for the other. The census does not produce §1.1 evidence — it produces candidates, and a candidate with no sighting behind it is exactly the "a real product might want it" case the paragraph below refuses. What it produces instead is a claim about this charter's own coverage, which no demand channel can make: an absent row cannot be cited, reopened or argued against, so a class with no row is not declined, it is unexamined. See the platform-baseline cluster at the end of Appendix A, whose four rows were ratified 2026-09-03.
A kind whose only justification is "a real product might want it" or "it rounds out the set" has no demand evidence and is not admitted. It may be reserved in the plateau taxonomy (Appendix A) so its name and disposition are pre-decided, but reservation is not admission – implementation stays demand-gated.
1.2 Irreducibility – why it cannot be a composition or a variant
The proposal states, explicitly, why the pattern cannot be expressed as:
- A composition of existing kinds – in particular a composition over
Switch(the state-bound conditional-child primitive; Phase 392), which absorbs conditional regions, wizard panes, empty-state alternatives, and mode toggles without new vocabulary. Many "I need a container that shows X or Y" requests areSwitchcompositions. - A spec-record variant of an existing kind – a new case on an existing spec DU
(
FormFieldKind,ChartKind, a format enum) rather than a newNodeKind. See §2. - A role on an existing kind – the
Boxcontainer carries a semantic role that reproduces a retired kind's HTML/a11y semantics (card chrome, region landmark, separator) without a distinct kind. A "new container" is almost always aBoxrole. - An application-space fragment – a repeated composite that belongs in a consumer app's fragment library, not in the language. This is the Phase 380 axis and is out of scope for kind admission.
If the pattern reduces to any of the above, it is redirected there. A NodeKind is admitted only for an
irreducible structural primitive – a shape that no combination of existing kinds, roles, variants,
or compositions can express with correct semantics.
1.3 Cost acknowledgment (the full carrying cost, named)
The proposal acknowledges, in writing, that a new kind imposes every cost below – because a kind is the most expensive thing this language can grow:
| Cost centre | What must change |
|---|---|
| Renderer × 3 host tiers | Each conformant host (F#, and the sibling reference implementations) renders the kind identically. |
| Reference CSS + class vocabulary | New fuaran-* class hooks, parity-locked across every renderer + every CSS copy. |
| Accessibility curation | The kind's semantic HTML, ARIA roles, and landmark semantics, hand-audited – not defaulted. |
| Wire corpus (§11) | Encoder + decoder + JSON Schema + wire-format-fixtures/ corpus + every sibling host, in one commit (the forward-coupling rule – WIRE_FORMAT.md §11). |
| Validator | Any kind-specific structural checks, in every host. |
| Eval + recipe coverage | Canonical-prompt coverage, a confusion-metric baseline (§3), and pattern-bank / cookbook seeds so the AI learns when to reach for it. |
| Working-memory tax | Every AI consumer, every prompt, forever, carries one more near-synonym to disambiguate against. |
The IDL codegen family (Phase 317) cheapens the mechanical legs of this table but not the conceptual cost – the a11y curation, the confusion tax, and the working-memory tax are irreducible and are the ones this charter guards.
2. Variant-vs-kind guidance (the quiet-churn rule)
Not every unit of new expressiveness is a NodeKind. Much of it lands, correctly, as a variant – a
new case on an existing spec-record DU. The distinction matters because it changes what the AI consumer
must disambiguate:
- A new kind adds a top-level choice: the consumer now picks between N+1 kinds at the point where it previously picked between N. This is the expensive axis – it widens the confusion surface.
- A new variant adds a choice within a kind the consumer has already selected: having chosen
Form→FormFieldKind, orChart→ChartKind, the consumer picks the variant locally, inside a much smaller decision. The disambiguation is scoped.
The rule: prefer the variant when the pattern is a specialisation of a kind the consumer would already have chosen. Precedents already in the tree:
FormFieldKindabsorbed date/time (Date+DateVariant), ranged number (RangedNumber), segmented choice (SegmentedChoice) – all as variants, never as new top-level kinds. A future rating, colour-picker, combobox, or autocomplete field is the same: aFormFieldKindvariant. (Comboboxis no longer future — it shipped at 0.51.0 under exactly this ruling, andRatingandColorat 0.67.0 under the same one; see their Appendix A rows. Note the case is spelledColor, notColorPicker: every case in this DU names what the reader manipulates rather than the widget that manipulates it. The multi-token input followed at 0.69.0 asTokens— spelled that way rather thanTagsbecauseTagsis a RESERVED union-case name in F#, and the reference host is what generates the corpus; see its Appendix A row.)ChartKindis the home for new chart types (gauge, funnel, heatmap, treemap, sankey) – variants, not newNodeKinds.- Format/enum cases (
Format,DateStyle) grow inside their DUs.
2.1 Below the variant line – spec-record FIELD additions
There is a third tier under the two above, and it was left implicit until the form-validation
charter (Phase 864) had to choose between it and a variant. A new field on an existing spec
record – StaticRows.sortable, GridSpec.pageStateKey, FormField.rule – adds no choice at
all: the consumer has already selected the kind and already selected the case, and the field is an
optional refinement it may ignore entirely. On the confusion axis this tier is free, which is
strictly cheaper than a variant's scoped disambiguation.
On the wire-coupling axis it is not free, and it is not cheaper than a variant either: §11's
forward-coupling rule is stated over the whole wire contract, so a field addition still costs
encoder + decoder + schema + corpus + every host in the roster, in one change-set. What it skips is
the §11.2 vocabulary attestation – manifest.formFieldKinds and manifest.kinds enumerate
cases, so a host that has never met a new field is not named by any manifest, and only the fixture
catches it. That is the tier's one genuine disadvantage and a proposal choosing it must say so.
The rule for choosing between the three:
A kind names a structural primitive. A variant names a different CONTROL or SHAPE within a kind the consumer already chose. A field names a REFINEMENT of a control that is otherwise unchanged. Where a pattern would read equally well as a variant or a field, prefer the field – it is the only one of the three that adds nothing to the working-memory tax.
The trap this rule exists to catch is a real one and it is counter-intuitive in the other direction:
widening an existing case is more expensive than adding a new one. Adding a field to a DU case
changes that case's arity, so every existing pattern match on it stops compiling; adding a new case
leaves them all alone and raises an FS0025 only where a match is exhaustive. So "just put it on
Text" is the most expensive of the three spellings, not the cheapest.
And the rule is a cost statement, not a prohibition — the one case where the expensive spelling is
the right one (2026-09-03, Phase 1126). The
cheaper alternative to widening a case is minting a SIBLING of it, and where the sibling would name
the same intent by a different route that is not a saving, it is a near-synonym pair — permanent, on
the wire, in every host's match, and in the confusion matrix this charter measures. Widening
Action.WriteToClipboard's payload from string to TextSource cost every construction site in the
estate a TextSource.Literal wrapper, which the compiler named once, in one build; the alternative
WriteToClipboardBound would have cost a reader of this vocabulary the question which of these two
do I want on every emission, forever. The discriminator to apply, so the next case is not decided
by which sentence someone quoted: a sibling is right when it names a DIFFERENT intent, and widening
is right when it names the SAME intent over a wider range of inputs. Print beside
WriteToClipboard is two intents; WriteToClipboardBound beside WriteToClipboard is one.
And note what made the cost bearable rather than what made it acceptable — the widened slot's
canonical bytes did not move (TextSource.Literal IS the bare string, WIRE_FORMAT.md §3.6), so no
shipped document broke and the §11 host cost was the bound payload alone. A widening that also moved
the wire would be a different, larger decision.
The quiet-churn caveat. A variant is not free. Per WIRE_FORMAT.md §11,
adding a case to any wire DU – NodeKind or a spec-record variant like FormFieldKind /
ChartKind – carries the identical forward-coupling cost: encoder + decoder + schema + corpus + every
host, in one commit. A variant is cheaper on the confusion axis (scoped disambiguation) but equal
on the wire-coupling axis. So variant additions are still governed: they cite demand evidence (§1.1)
and acknowledge the §11 cost, even though they skip the kind-level irreducibility test (§1.2) – a
variant is the redirect. The thing to avoid is treating variant growth as invisible: a spec DU that
accretes a case every few weeks is churning the wire contract just as surely as new kinds would, and the
confusion metric (§3) should watch intra-DU variants as well as top-level kinds.
3. Plateau budget + confusion guard
3.1 The plateau as an advisory budget
The natural plateau is roughly 60 kinds. This is an advisory budget, not a hard cap: it is the number at which a semantic UI vocabulary is expected to have covered the irreducible structural primitives a general consumer surface needs, with the long tail handled by variants, roles, and composition. Crossing it is not forbidden – but a proposal that would push the set past the budget carries a heightened burden on §1.2 (irreducibility) and should ask whether the budget is wrong or the proposal is a variant in disguise.
3.2 Every change cites a confusion delta
Every vocabulary change – an addition, a merge, or a retirement – cites a pre/post delta on the kind-confusion metric (the valid-but-wrong-kind rate captured per eval run, aggregated into per-pair confusion counts). The metric is the instrument that makes the minimalist strategy safe: it turns "the set feels confusing" into a measured number per release.
- An addition must show it does not raise the confusion rate materially – a new kind that the AI routinely confuses with an existing one is a failed admission even if it passed §1, and should be reconsidered or merged.
- A merge (near-synonym collapse) is expected to lower the rate for the merged cluster – that drop is the merge's acceptance evidence.
3.3 A sustained confusion rise triggers a merge review
If the confusion metric shows a sustained rise in a cluster's wrong-kind rate across releases, that
is the trigger for a merge review – the discipline that produced the container merge (Box
absorbing Stack / GridLayout / Dashboard / Card, Phase 390) and the tabular merge (Table folding into
DataGrid, Phase 393). Those two merges are the standing precedent: when two kinds are chronically
confused, the right move is often to collapse them into one kind with a role or mode, not to write more
documentation telling the AI how to tell them apart. The vocabulary shrinks under measured confusion
pressure exactly as it grows under measured demand pressure.
4. Post-publication versioning policy
The kind vocabulary is serialised through the canonical wire format, whose schema $id pins a major
wire-profile segment (.../wire-format/v1/schema.json). This section governs how kind changes map to
wire-profile versions after the language tier publishes 1.0.0. Until then, the pre-1.0 rules in
STABILITY.md apply: a kind addition is a minor bump (existing consumer matches
gain an FS0025 exhaustiveness warning – the correct signal, not a break), under the §11 forward-
coupling discipline.
4.1 Addition – an additive core@1.x profile minor
After 1.0.0, adding a kind is an additive minor within the v1 wire profile – call it a
core@1.(x+1) profile bump. It adds a new $type branch to the schema's top-level oneOf; every
previously-valid document stays valid; the /v1/ major segment does not move. This mirrors the
pre-1.0 additive posture – the difference post-1.0 is that the profile minor is recorded so hosts can
negotiate on it (§4.3).
4.2 Removal / rename – a v2 major (avoid; do it pre-launch)
Removing or renaming a kind – changing a $type discriminator string – is a breaking wire-format
change: a /v2/ major event, not a silent encoder bump. This is why the near-synonym merges
(Box, Table→DataGrid) are deliberately scheduled before publication: a merge retires kinds, so
doing it post-1.0 would force a major. The decode-upgrade seam (decoders accept a retired tag and upgrade
it to the survivor on read) softens replay compatibility but does not make a removal non-breaking for
emitters. Rule: retire kinds before the public launch; after it, the set grows additively or not at all.
4.3 Unknown-discriminator behaviour + the host-lag commitment
A decoder that predates a newly-added kind encounters an unknown $type and rejects it –
UNKNOWN_DU_CASE at the decoder, no matching schema branch for external validators. A new kind is
therefore forward-incompatible: a new-version emitter that emits it produces a document an
old-version host cannot render. Two commitments follow, and they are the reason the growth rate
matters:
- Emitters gate on a negotiated profile floor. An emitter must not emit a kind newer than the
core@1.xprofile the receiving host advertises support for. The host declares its supported profile; the emitter stays within it. A slowly-growing vocabulary makes this floor easy to hold – hosts rarely lag by more than one or two profile minors. - The host-lag window is bounded by the growth rate. Because kinds are admitted deliberately (a small evidenced batch pre-launch, then demand-paced – §Appendix B), the gap between "newest emitter" and "oldest conformant host" stays narrow. A vocabulary that grew a kind a week would make the host-lag commitment untenable; the deliberate rate is what makes it tractable. This is a concrete downstream reason the admission checklist's demand gate (§1.1) is strict.
5. How this charter binds phase authoring
Any development plan that changes the wire vocabulary – a kind addition, a variant addition, a
spec-record field addition (§2.1), a merge, or a retirement – cites this charter and satisfies its
gates in the plan body: the demand evidence (§1.1), the
irreducibility statement (§1.2), the cost acknowledgment (§1.3), and the pre/post confusion delta (§3.2).
A field addition's §1.2 argument is the one that most often goes unwritten, because the §2/§2.1 redirect
looks like it is the argument – it is not. The redirect says why the pattern is not a kind; §1.2 still
has to say why the pattern needs the wire at all, and a field whose only justification is that it was
cheap to add is a field with no evidence behind it.
STABILITY.md links this charter from its stability policy so the two documents stay
in agreement: STABILITY.md governs the version-bump classification of a kind change; this charter
governs whether the change is admitted at all. A plan that adds a kind without clearing this charter is
a discipline defect, the same class as a plan that introduces a breaking change without the
**Stability impact:** annotation.
5.1 Minting a FUARAN* defect code (the allocation rule)
A vocabulary change usually brings a diagnostic with it, and a defect code is allocated, never guessed:
pwsh ./scripts/fuaran-codes.ps1 -Next # the next free code
pwsh ./scripts/fuaran-codes.ps1 -Next -Count 3 # three, contiguously
pwsh ./scripts/fuaran-codes.ps1 -Check # in the gate — no code names two rules
pwsh ./scripts/fuaran-codes.ps1 -List # every code, with the registries that claim it
The rule exists because the code space is shared by THREE registries — the tree-time
validator's describe arms, the build-time source-AST walker's findings, and the Roslyn
diagnostic descriptors the C#/VB surfaces raise — while the obvious way to mint is to read the
highest number in whichever one you happen to have open. Both failure modes have happened:
two phases minted FUARAN114 on one evening (the later one renumbered its pair at fourteen sites),
and the analyzer and the walker sat on FUARAN060/FUARAN061 for different defects from Phase
315 until Phase 1646 renumbered the analyzer's pair to FUARAN150/FUARAN151.
Two properties of -Next are what make it worth running rather than eyeballing. Its floor is
strictly above the maximum over every source, the conformance corpus's published
validator/defect-vocabulary.json included — so a mint cannot hand back a number the corpus has
already put in front of other hosts. And it reads every sibling worktree of this repository,
because a concurrent session's unpushed branch is invisible to this tree and to git log, which
is precisely where the FUARAN114 collision came from.
One code may legitimately be raised by two registries when it is one rule stated at two layers
(the Tabs parity rules are, at source-AST time and at tree time). That is declared, with its
reason, in the script's own $Mirrors table; a declaration that has stopped being true fails the
check too, on the enumeration-completeness rule the authoring-surface pin uses. What the check
deliberately does not police is two cases sharing a code within one registry — several
defect-DU cases legitimately map to one code, and nothing can distinguish that from a repeat.
Appendix A – Plateau taxonomy (reserve names now, implement on evidence)
This appendix reserves names and clusters for the roughly-twenty candidates that a general UI
vocabulary tends to accrete on the way to its plateau, and pre-rules each one's disposition
(variant / composition / role / genuine kind). Deciding the naming and clustering early prevents the
near-synonym drift that the Box and Table→DataGrid merges had to clean up retroactively – the
namespace is filled now; the implementations stay demand-gated (each still needs §1 evidence before
it is built). A reservation is a pre-decision, not a commitment to ship.
Legend – disposition: Variant (a case on an existing spec DU – §2) · Composition (built from
existing kinds – §1.2) · Role (a Box/existing-kind semantic role) · Kind (a genuine
irreducible primitive, reserved pending demand) · Covered (already expressible today) ·
Host chrome (the capability belongs to the host application, and the language names it nowhere –
added 2026-08-18 by the affordance→op charter, Phase 866, which needed a way to record a decline
that is a positive design position rather than a deferral) · Field (a slot on an existing spec
RECORD – the tier §2.1 defines, below the variant line; listed here 2026-08-27 by Phase 873 because
three rows already carried the disposition while the legend did not define it, and a legend that
omits a disposition in live use is how a reader concludes the row is a typo) · Action (reserved)
(a case on the Action<'Msg> DU rather than a node — reserved pending demand on exactly Kind's
terms, since an Action case is a wire member with the full §11 cost; added 2026-09-04 because the
ScrollTo row below is the first to carry it, and for the same reason the Field entry was added).
Navigation cluster
| Reserved name | Disposition | Ruling |
|---|---|---|
NavBar / Menu | Kind (reserved) — assessed negative 2026-08-20 | Reserved as the strongest genuine-kind candidate in this cluster (distinct <nav> landmark + menu ARIA semantics). Assessed 2026-08-20 (Phase 829) and NOT ADMITTED — the reservation stands; admission does not. §1.1: no recorded emission demand at all — no sighting of an invented NavBar / Menu / NavItem / Sidebar emission is on record, where every admitted neighbour cites a count (DateRange ×9, TonedPill ×26, DragReorder ×20). §1.2: the headline irreducibility claim fails on its own terms — the landmark is already carried on the wire by AriaRole.Navigation, a first-class Accessibility.Role case and ARIA-landmark-equivalent to <nav>; an ordered item-set is a host-side projection concern; and responsive collapse is Host chrome on exactly the Virtualisation reasoning above, a tree that collapses and one that does not being identical in every respect a consumer can observe. Confirmed from the demand side rather than argued: the first production navigation composed from Group + Link (Phase 828's projection, adopted 2026-08-20) sufficed with no missing vocabulary — structure, ordering, the landmark and real crawlable anchors all fell out of the composition, and the frictions it did record were two stylesheet re-binds and one chrome-vs-navigation boundary, none of them a kind. Recorded here for the Virtualisation reason: the charter is not a surface a reader consults. Reopen condition: a §1.1 sighting count of the order the admitted rows cite, or composed nav proving semantically wrong in recorded production use. (The one genuine expression gap the assessment found — aria-current reaches the tree only through wire-omitted ExtraAttributes — is a §2 trait-field question, not this row. Disposition of that redirect, updated 2026-08-20 (Phase 951): the follow-up note reasoned a real Accessibility.Current field "would land on the anchor via the a11y projection". It would not have — the projection was emitted on the node's wrapper <div> in every renderer, the same wrong element ExtraAttributes already reached by a different route, so the §11 cost of a wire field would have bought no observable improvement. Phase 951 fixed the placement, which makes the redirect coherent rather than actionable: a Link's a11y projection now lands on the <a>, so a Current field would too — and so does the aria-current extra-attribute the nav projection already uses, which is what removed the production host's descendant CSS selector. The field itself stays gated on its own §1.1 evidence — a round-tripping consumer hitting the wire gap, which no pure-SSR adoption can supply, and which the working extra-attribute route makes less likely to arrive, not more.) |
Breadcrumb | Composition / Role | A List of Links with an ordered-trail role; reach for a kind only if the a11y trail semantics prove irreducible under demand. |
Pagination | Composition / renderer-owned affordance | Not a kind. Page state is a pageStateKey + pageSize on the grid that pages, and that grid's own renderer draws the pager. A Button + SetState composition over the same key remains legal as an additional writer, but is not the primary spelling: a free-standing pager has no structural relation to the grid it means to drive, and eleven cross-family emissions of exactly that shape wrote page state nothing read. Amended 2026-08-16 — the disposition (not a kind) is upheld; the original ruling prescribed the composition alone, which the demand evidence showed to be the fake-affordance generator. See the grid-behaviour charter (Phase 860). |
Virtualisation / VirtualList | Host chrome | Not a kind, not a field, and deliberately not the other half of Pagination. Windowing rows is a render-tier concern with nothing for the wire to say: no tree changes shape because rows are windowed, so a decoded tree that declared it and one that did not would be identical in every respect a consumer can observe. Paging is different precisely because it changes which rows exist in the projection, which is why it earned a spelling and this did not. Ruled OUT by the grid-behaviour charter (Phase 860) as a named satellite; recorded here in 2026-08-18 because the charter is not a surface a reader consults, and the ruling was re-proposed from scratch once. Reopen condition: a wire-observable consequence — a windowed grid that must tell a host its viewport, or a declared row-height contract a host cannot infer. |
CommandPalette | Composition | Modal + Filters + List – an application-space fragment, not a kind. |
TableOfContents | Composition | List + Link (+ anchor bindings). |
ScrollProgress / ScrollSpy / ReadingPosition (anything a tree would bind to WHERE THE READER'S VIEWPORT IS — a reading-progress bar, a section nav that highlights the section in view, a chart that animates on entering the viewport) | Host chrome — RULED 2026-09-04 | Not a kind, not a field, and not a Binding source — and the last of those is the decision that matters, because the other two follow from it. The direction is already chosen, in the opposite sense. Viewport geometry exists in this library today: LayoutObservation carries viewportX / viewportY per node, the browser observer derives its flags from ResizeObserver + IntersectionObserver (Phase 80), and the whole of it flows OUT of a rendering into _platform.ui.inspect_layout for a caller to reason about. Observation of a render, never input to a tree. A scroll binding would reverse a placement that was decided rather than omitted. It is the worst-fitting environment value the binding model could take. The one environment source that exists, Binding.Now, works because the host resolves it ONCE per render pass and a replayed op-stream re-supplies the recorded instant — the property the BindingSources doc names as "what keeps a tree a pure value". Scroll position is sampled in order to DRIVE a re-render on every change: journal it as Now is journaled and the op-stream fills with samples; do not, and replay diverges. The other reader-state source, Selection, is a discrete act with semantic content an author can mean something by; where the reader's eyes are is neither the author's meaning nor the host's appearance, and carries none. The Virtualisation test decides the rest: a tree that bound a progress fraction to scroll and one that did not are identical in every respect a consumer can observe — the document is the same document. "Which section is current" is already on the host-only side of the line: the NavBar assessment found a production navigation composed from Group + Link reached aria-current only through wire-omitted ExtraAttributes, and the TableOfContents row above needs nothing the wire does not carry. Phase 1077 states the posture in one sentence — a host must not infer laziness from viewport, position, or anything else the tree does not say — and the same phase declined to let the language guess where the fold is. Section-jump keys (j/k) are the KeyboardShortcut row, already ruled. §1.1 evidence is nil: no expression-gap entry, no Custom fallout, no eval miss; the sole input is a hand-authored long-form article page surveyed 2026-09-04 whose reading-position chrome is, on that page too, mounted outside the article's own markup. Two things are deliberately NOT minted, and their tier is recorded so they are costed correctly if they return: a Motion token for enter-the-viewport reveal (host-only under WIRE_FORMAT.md §9, so the 1122 tier — zero §11 cost — and consumer-authored by construction; it waits on demand, not on cost), and any read-depth or reader-behaviour telemetry (every sink today is machinery-side — ops, provider calls, cache, deny — and a "did the reader reach it" signal belongs beside ChildClippedByAncestor on the observer side, where the direction already points). Reopen condition: a wire-observable consequence, on the Virtualisation reasoning — a server-driven session that must know the reader's position to decide what to send next, which is the one consumer for whom the document would genuinely differ. Until then the host that owns the scroller owns the chrome. |
Temporal-input cluster (variant-dominated – the FormFieldKind precedent)
| Reserved name | Disposition | Ruling |
|---|---|---|
DatePicker / TimePicker / DateTime | Variant (shipped) | Already FormFieldKind.Date + DateVariant. The exemplar: temporal input is a field variant, never a kind. |
DateRange | Variant (shipped) | Shipped as FormFieldKind.DateRange at 0.7.0 (Phase 725): Range's pair mechanics with Date's ISO/variant conventions, Min/Max/Step bounding both ends. Admitted on the operator mandate plus 9 full-pack emissions of an invented $type:"DateRange"; irreducible because the two-Date-field workaround splits one semantic value across two uncoordinated bindings (one filter param, not two). |
Slider / Range | Variant (shipped surface) | FormFieldKind.RangedNumber with a slider render variant. |
Calendar (month-grid display) | Kind (reserved) or Variant | If it is display of a month grid, a possible DataGrid mode or a genuine kind; if it is input, a FormFieldKind variant. Disposition decided when demand names which. |
Media cluster
| Reserved name | Disposition | Ruling |
|---|---|---|
Media (→ Video / Audio variants) | Kind (ADMITTED 2026-08-27, Phase 1076) | <video>/<audio> carry distinct controls + captioning a11y no existing kind expresses. Admitted in exactly the shape this row pre-ruled — one Media kind with a MediaKind variant DU (Video / Audio), per-case payloads for the video-only slots — so the variant ruling stands unweakened. The §1.1 gate was overridden by an explicit operator mandate, recorded standing alone (Phase 1076's Charter admission section carries the walk and the mandate): media display is a ubiquitous platform capability whose absence the reactive demand channels structurally cannot evidence — constrained decoding suppresses the invented-$type sighting class, and the census never poses tasks the vocabulary already cannot express. The mandate is an attributable override of the evidence gate for THIS row, not a softening of it: future kinds still clear §1.1 or carry their own recorded mandate. FIELD ADDITION 2026-09-02, Phase 1110 — MediaSpec.tracks (a TrackEntry list) and MediaSpec.transcript, admitted at the §2.1 FIELD tier: no new kind, no new case, so the confusion delta is structurally zero. This is what makes the irreducibility clause above TRUE rather than merely asserted — the row was admitted citing "captioning a11y no existing kind expresses", and until this phase the shipped kind expressed none either. §1.1 is covered by the same platform-baseline operator mandate that admitted the row (no new override); §1.2 holds because a <track> is a CHILD of the media element with element-scoped semantics, so no sibling node, Binding, Accessibility slot or Custom prop can reach inside the transport to carry one. The tier's stated disadvantage applies and is acknowledged here per §2.1: a field addition skips §11.2 vocabulary attestation — manifest.kinds enumerates CASES, so a host that has never met tracks is named by no manifest and only the fixtures catch it. |
Avatar | Variant / Role | An Image variant (shape + fallback-initial role), not a kind. |
Icon | Kind (ADMITTED, Phase 821) | Pre-ruled here as "an Image variant or a Badge role; never its own kind" — and then admitted as its own NodeKind by Phase 821 on recorded §1.1 evidence (one sighting, with the governing principle written down: "the charter widens before implementation, not after"), without this row being amended. Corrected retroactively 2026-08-28: this row is the instance that taught the amend-the-row-in-the-same-change-set rule the Media admission then followed. The original reasoning is kept because its scope was right and its conclusion was not: a decorative glyph inside another kind stays a role/slot concern; what the pre-ruling missed is the standalone semantic glyph with its own a11y contract, which is what shipped. |
Carousel / Gallery | Composition | Box + Switch (Phase 392) over an index state key – the canonical "compose, don't add a kind" example the authoring guide teaches. The disposition STANDS and is now evidenced (the platform-baseline census, Appendix A's closing section: models reach for the container name and its decomposition — a stage, an arrow pair, a dot row, a position counter — from the same emissions, which is what describing a composition looks like). What the row asserted and could not deliver, corrected 2026-09-03 by Phase 1122. Until this phase the composition named above could not actually be composed: the closed Motion DU had no between-children transition, nothing advanced a state key on time, and no swipe affordance existed — so the row named a shape the vocabulary could express structurally and not kinetically, and every consumer's first question of a media vocabulary landed on the half that was missing. Recorded rather than quietly rewritten, on the Embed / Tooltip precedent: the disposition was right and the completeness was not, and the second defect is the one that leaves a capability unbuilt. The row now names its prerequisites, all shipped by Phase 1122: (1) Motion.CrossFade / Motion.SlideBetween, the first two tokens whose subject is a TRANSITION BETWEEN renderings rather than the arrival of one — admitted at the enum tier and, because Node.motion is host-only (WIRE_FORMAT.md §9), at zero §11 forward-coupling cost: no codec, no schema, no fixture, no host, which is a cheaper tier than any this appendix had previously used and is worth stating so the next transition token is not costed as a wire change; (2) SwitchSpec.autoAdvanceMs, a §2.1 FIELD — the one fact a host cannot recover from the tree, since a stage, panels, a bound index and arrow controls are all composable and none of them says a timer exists; (3) swipe and the arrow keys as renderer-owned affordances under the affordance→op charter, with no wire name for the gesture, no threshold and no event. §1.1 — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning; this phase adds no new override. §1.2 irreducibility holds for the field and only the field: a timer is a behaviour keyed by something only the document can name, the sortStateKey shape. §1.3 — paid: the IDL and generated layer, the policy decoder and its three reject vectors, the schema, the client-tier state machine, the reference stylesheet's two transition families and the extended reduce rule, one validator code (FUARAN128) with go-red twins, the C# authoring surface, and the §11 corpus. The field tier's stated disadvantage applies and is named per §2.1: a field addition skips §11.2 vocabulary attestation — manifest.kinds enumerates CASES — so a host that has never met autoAdvanceMs is named by no manifest and only the fixtures catch it. §3.2 confusion delta — structurally ZERO on both halves. The field adds no case at any level, so no pair enters the matrix. The two Motion tokens add cases to a vocabulary the metric does not measure at all: the metric counts valid-but-wrong-KIND emissions, and motion is host-only, so no model emits it and no emission can be wrong about it. What is deliberately NOT minted: a Carousel kind, a gesture name, a threshold, a pause policy, a resume rule, a direction token, or an exit transition. And one obligation is recorded normatively rather than left per-host, which is unusual enough to flag: WCAG 2.2.2's pause-on-hover / stop-permanently-on-interaction / never-start-under-reduced-motion trio is stated in WIRE_FORMAT.md beside the field, because a host that honoured the interval and not the trio would ship an accessibility defect the document has no way to ask it not to. |
Embed (iframe-like) | Kind (ADMITTED 2026-09-03, Phase 1111) | This row read Covered by Mount until this phase, and the coverage claim was FALSE — recorded here rather than quietly replaced, because the reason it was wrong is the reason the kind exists. Mount (Phase 265, §4o) carries an isolation boundary for a COOPERATING guest: a scope id, a declared message channel, a capability request list, a host-side loader that produced the guest tree. A third-party page has none of those and cannot acquire them, and widening Mount to admit an uncooperative third party would weaken every guarantee Mount currently makes — so the two contracts (bidirectional cooperation; default-deny isolation of an uncooperative party) are opposites and take separate kinds. It is equally not a Media variant: Media fetches an asset and DISPLAYS it, Embed fetches a document and lets it EXECUTE, which is a sharper class and gets its own named egress class rather than a reuse of Media’s. §1.1 was overridden by the platform-baseline operator mandate recorded at Accept (2026-08-28), on the Media row’s reasoning and covering evidence only: most internet video is a provider embed rather than a file URL, and the reactive channels structurally cannot evidence the gap — no model invents a $type that constrained decoding forbids. §1.3 is paid in full (renderers × the roster, reference CSS + class vocabulary, a11y curation, the §11 wire corpus, two validator codes, two render obligations, and the working-memory tax), and unlike a field addition this one DOES take §11.2 vocabulary attestation, because manifest.kinds enumerates cases. §3.2 confusion delta: one new top-level choice, and the triangle to watch is Embed / Mount / Media. The two boundaries an emitter must hold are stated so a rubric can measure them: a URL to a third party’s PAGE is Embed; a URL to an audio or video FILE is Media; a Fuaran tree the host itself loaded is Mount. The pairwise risk is asymmetric — Embed/Media share a src-shaped slot and are the likelier confusion, while Mount carries a scope id nothing else has and is cheap to tell apart. |
Communication / feedback cluster
| Reserved name | Disposition | Ruling |
|---|---|---|
Banner / Alert | Covered | Callout (inline) + Toast (transient) already span this. A third feedback kind here is a confusion-metric risk, not a gap – watch the Callout/Toast/Badge cluster (§3.3) rather than adding to it. |
Tooltip | Field / node-level TRAIT (ADMITTED 2026-09-03, Phase 1112) | This row read Role / Composition — "an annotation role or a Disclosure variant; a hover hint is not a structural primitive" — and the disposition was right while the redirect was false. Recorded rather than replaced, because the distinction is the whole ruling. The disposition is upheld: a tooltip is not a kind. It has no independent existence, no place in a tree of its own, and nothing to lay out; it is a property OF another node, which is the definition of the field tier. The redirect is refuted, and it was unfalsifiable in the shape it was written. Neither destination it named can carry the capability: an "annotation role" names no slot the wire has — AriaRole is a role for the node itself, not a container for supplementary text — and a Disclosure is a persistent, click-toggled, in-flow region that changes the layout, which is the opposite of a hint that appears on hover, obscures nothing permanently, and leaves the document unchanged. Following the redirect produced a shape no host could render as a tooltip; and the shipped ButtonSpec.Tooltip slot, the one existing spelling, is host-only (§10.1: never emitted, restored to None on decode), so no decoded tree on any host in any language could express a hover hint by ANY spelling. This is the TonedPill irreducibility class — not "the composition is awkward", but "no wire spelling exists at all". Tier — FIELD (§2.1), and specifically the node-level TRAIT tier, beside accessibility / state / style rather than on the 41 spec records. A hint is uniform across kinds: nothing about "a short supplementary description of this thing" varies with whether the thing is a button or a metric, so 41 per-spec fields would be 41 independently driftable decisions about one concept — and 41 chances for a host to implement 40 of them. §1.1 — covered by the platform-baseline operator mandate recorded at Accept (2026-08-28), on the Media row's reasoning; this phase adds no new override. The gap is doubly invisible to the reactive channels: constrained decoding suppresses the invented-$type sighting class, and "no tooltip" is always valid-but-less-specific rather than an error, so a census that never poses the task records a clean pass on a document missing the hint. §1.2 — irreducible, per the refutation above. §1.3 — paid, and the field tier's disadvantage is named as §2.1 requires: a field addition skips §11.2 vocabulary attestation, because manifest.kinds enumerates CASES — so a host that has never met tooltip is named by no manifest, and only the fixtures catch it. What it does cost: the IDL and the generated layer (a node-envelope field, which reaches every one of the 42 smart constructors and — the finding of this phase — the hand-written mk of every PROJECTED kind), both renderers, the reference stylesheet and the class vocabulary, the §11 corpus in five artefacts, two validator codes, and one authoring-surface decision per veneer. §3.2 confusion delta — structurally ZERO: the metric counts valid-but-wrong-KIND emissions, and no case is added at any level, so no pair enters the matrix and none changes. That is the tier's one free axis and it is why the tier was chosen. What the trait deliberately does NOT mint: a gesture. Hover, focus, long-press and touch reveal are the renderer's affordance under the affordance→op charter; no event name, placement token or delay enters the vocabulary, and a document says WHAT the hint is and never HOW it appears. And it is a DESCRIPTION, not a NAME — aria-describedby, never aria-label. The icon-only button is the headline case precisely because it needs both, saying different things: accessibility.label names the control, the tooltip explains it. The superseded ButtonSpec.Tooltip is kept compiling and documented as the non-wire legacy spelling (Types.fs): it is a shipped public field that renders correctly for the in-process authoring path it was built for, and deleting it would break that path's consumers to buy nothing the trait does not already give a new one. |
Popover | Variant (ADMITTED 2026-09-03, Phase 1119) | This row read "A Modal variant (a non-modal modality flag), not a distinct kind" — and for the second time in this wave, the ruling was simply RIGHT: the tier was right, the mechanism was right, and the ceremony had nothing to refute. What it did have to correct is the row's implied COMPLETENESS, which is a different defect and the one that left the capability unbuilt for as long as it was: "a non-modal modality flag" names one boolean and stops, and a modality flag on its own is unimplementable — a surface that does not block the page has to be positioned against SOMETHING, so the admission is a flag and an Anchor, and a row that named only the flag read as though the work were smaller than it is. Tier — VARIANT (§2), on the enum axis rather than the case axis. ModalityKind is a new two-case enum, so unlike a FormFieldKind case this adds no NodeKind and no case to any DU a $type names; the wire member modality omits at Modal, which is what makes every pre-1119 document byte-unchanged. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning; this phase adds no new override. The gap is invisible to both reactive channels in the way that mandate exists for: constrained decoding suppresses the invented-$type sighting class, and "used a Modal instead" is valid-but-less-specific rather than an error — a census records a clean pass on a document that greys out the whole page and traps focus to show a four-item menu. §1.2 irreducibility, and the accessibility contract is the reason rather than the ergonomics. Not a Modal with different CSS: aria-modal="true" is a claim that the rest of the page is INERT, and a host cannot un-make that claim from a stylesheet — the attribute is either emitted or it is not, and which one is right is a fact about the document, so it has to be on the wire. Not a Tooltip (Phase 1112): the trait is a DESCRIPTION carried by aria-describedby, non-interactive by contract, and a surface with controls inside it is a different thing. Not a Disclosure: that is an in-flow region that changes the layout, which is the opposite of a transient surface floating over it. Not a Custom node: reaching for one would mean re-implementing the open binding, the dismiss contract, the heading and the dialog role outside the vocabulary — the escape this appendix's Custom ledger exists to COUNT rather than to endorse. §1.3 cost — paid in full, and the enum tier's position is between the two named before it. Like a FormFieldKind case and unlike a field addition, a new enum with a wire spelling gets its own reject vectors and its own schema entry; unlike a case it takes no §11.2 vocabulary attestation, because manifest.kinds and manifest.formFieldKinds enumerate CASES and ModalityKind is neither — so a host that has never met modality is named by no manifest and only the fixtures catch it. What it does cost: the IDL, the generated layer, the policy decoder, the schema, four corpus artefacts, both renderers plus a new client control, the reference stylesheet and the fingerprint restamp it forces, two validator codes, one render-obligation claim, and one authoring-surface decision per veneer. §3.2 confusion delta — ONE new pair, and it is the cluster §3.3 already told this phase to watch. Modal ↔ Popover, inside the Tooltip ↔ Popover ↔ Modal triangle. The teaching line, authored here and handed to Phase 1127, written so a rubric can measure it: a hint about something already on screen is the Tooltip trait; an anchored interactive surface, opened from something the reader pointed at, is a Popover; a blocking task that must be finished or abandoned before the page continues is a Modal. The asymmetry is worth recording because it decides which direction to score harder: a Modal where a Popover was meant blocks the page and traps focus — disruptive, and the reader can at least SEE that something is wrong; a Popover where a Modal was meant lets the reader wander off mid-task with nothing to stop them, which nothing reports at all. What is deliberately NOT minted: a placement token, an offset, a delay, a flip strategy, a keystroke or an event name. Anchored placement, the viewport-edge flip and the light-dismiss gestures are renderer affordances under the affordance→op charter; a document says WHAT the surface is and WHICH node it belongs to, never how it is placed. |
NotificationCenter | Composition | List + Toast. |
SystemNotification / PushNotification / out-of-page alerting | Host chrome — RULED (operator ratification 2026-09-06) | The question is not whether the language has a notification surface — Toast (transient, in-tree, role="status" + aria-live="polite") and Callout (persistent, toned, dismissable) already span the in-page one, per the Banner/Alert row above — but whether the language may reach a user whose tab is not in front of them. Three shapes were weighed and all three are declined, for three different reasons. (1) A kind or a variant. Refused before any other gate: an out-of-page reach renders nothing, so there is no shape for a NodeKind to have — the whole content of the capability is an effect, and effects are the Action axis. (2) Extending Action.Notify's channel semantics — the shape that looks cheapest and is the most expensive. Notify(channel, payload) is deliberately an opaque host-interpreted string: the language says nothing about what a channel means, and the dispatch gate discriminates on the action's ActionDescriptor, never on the channel. Reserving a channel name meaning "escalate out of the page" would let a host that permitted in-app toasts silently permit OS-level delivery — a relaxation of an existing mediation with no gate vocabulary for the widening, which is precisely what Phase 897 fixed on the other axis by giving the destination its own typed gate rather than overloading the discriminator. Refused by name, on the grid cluster's sortable/pageable reasoning: a capability nothing can separately refuse is not gated. (3) A host-registered capability on the existing Action.Invoke route — a conventional id, default-deny, permission owned by the host (the shape Phase 1131 proposes for geolocation). Not admitted here, and it does not need to be: it requires no vocabulary at all, so blessing it is not a charter act. What the charter is asked is whether the language should teach such an id, and the recommendation is not yet — teaching a capability into the pack is itself a demand-manufacturing act, and there is no demand to justify it. §1.1, in both directions, because only one of them is evidence. One leg has run on this family: the 2026-09-02 shadow leg, run shadow-20260902-141855-anthropic, teaching removed (the leg's defining condition), on prompts posing exactly the transient-versus-persistent notice disambiguation. Every one of its eight sightings stayed inside the document (toast, ephemeral_notification, persistent_notice, notice.disclaimer, plus four ordinary structural tokens) and not one reached for a permission, a push, or any delivery outside the page — a recorded absence from a leg that ran, cited with its shadow provenance. It is not the whole answer: Phase 1118's designed notification probes are not authored, so the backgrounded-tab class has never been posed at all, and a leg that was not executed has measured nothing. §1.2 is the one gate this capability would pass — nothing composes to a reach outside the page, and unlike KeyboardShortcut there is no renderer-owed equivalent hiding behind the request. It fails the others. Trust. The browser grant is per-origin, sticky and effectively one-shot: a decoded tree from an arbitrary emitter triggering that prompt spends a permission the host can never re-ask for, on the emitter's judgement rather than the host's. That is the KeyboardShortcut row's spam-shaped reasoning with the asymmetry worse — a wrong chord is annoying; a squandered permission is unrecoverable, in the one place a mistake cannot be withdrawn cheaply (§4.2). Attention. The governing criterion is WCAG 2.2.4 Interruptions (Level AAA) — interruptions must be postponable or suppressible by the user — with 2.2.2 Pause, Stop, Hide adjacent for the auto-updating surface. The language has no vocabulary for postponement, and an OS-delivered banner leaves the accessibility tree entirely, so admitting it would have the language make an a11y claim it cannot audit. Toast's curated live-region contract is auditable by construction, which is exactly why the in-page surface is not the same question. The named first consumers are real, and they are also hosts. Long-running operator surfaces — a fleet or session board a user leaves open for hours — are the most plausible demand, and a host that owns its window owns its notifications. That is the entire content of the Host chrome disposition: the demand being genuine does not make it the language's. §1.3 cost is not paid — no renderer, class vocabulary, corpus, schema, validator or pack changes. §3 confusion delta is structurally zero: no case is added, so there is no new pair to score. Reopen condition (the falsifier): Phase 1118's notification probes authored and executed, producing cross-family, task-qualified sightings of an out-of-page reach at the counts the admitted rows cite (DateRange ×9, DragReorder ×20, TonedPill ×26) — or a recorded production consumer for which host-owned notification is demonstrably unreachable. A sighting of toast or of an in-page notice is not that falsifier; it is this row's evidence for the opposite conclusion. |
Status of the row above: RULED — the operator ratified the decline on 2026-09-06. Phase 1132's
walk recommended it (2026-09-02) and left the admission to the operator, because a capability whose
grant is per-origin and one-shot is irreversible in the sense §4.2 reserves for post-publication
vocabulary. Between the walk and the ratification the missing half of the evidence arrived: Phase
1118's designed notification probes — unauthored at walk time, the row's own recorded limit — were
authored and executed, and the platform-baseline census records the sweep's verdict as supported,
not unsettled: every capability-bearing reach was for a channel or the permission ceremony, never a
kind, which strengthens the scarce-and-unrecoverable-grant argument rather than weakening it. So the
falsifier this row pre-registered was given its chance and fired in the opposite direction — the
ratification stands on measured evidence in both directions, not on the walk's reasoning alone. The
narrow sub-question was ruled the same way: the language does not bless or teach a conventional
Invoke capability id now — teaching manufactures the demand it would then cite, and the
geolocation retirement (Phase 1131) is the standing precedent for minting such a convention when a
real consumer appears, not before. The reopen condition in the row is unchanged and stays live:
cross-family, task-qualified sightings of an out-of-page reach at the counts the admitted rows cite,
or a recorded production consumer for which host-owned notification is demonstrably unreachable — the
Binding.State seeding row is this charter's precedent that a ratified decline reopens on the
evidence it names. Recorded here rather than in a phase Outcome alone for the reason the
grid-behaviour cluster records: a ruling that lives only in a phase file is a ruling the next
proposal re-derives.
Data-display / structured cluster
| Reserved name | Disposition | Ruling |
|---|---|---|
Tree / TreeView | Kind (shipped) — ADMITTED 2026-09-03 | Shipped as NodeKind.Tree at 0.61.0 (Phase 1120). The row stood undecided for a year on the sentence — admit only if composition proves semantically wrong under demand — and that is exactly what happened, though not by the route the row expected. The depth argument is STRUCK. The obvious irreducibility claim was arbitrary depth, which a fixed composition cannot express; a probe built to force it produced NO container on either vendor — one nested four levels of domain records and stopped, which is a finite literal a static composition expresses. That claim is struck rather than left standing weakly, because an argument nobody re-examines is one the next proposal inherits. §1.1 — cross-family, on the SEMANTIC probe. Handed a task naming one focus walking the structure by keyboard, Home/End, a root-to-focus path and per-row open/closed/leaf announcement, one vendor emitted the tree roles, aria-level, aria-expanded, a roving tabindex and all six key bindings unprompted; the other abandoned recursion entirely and flattened to a row collection carrying level, state and a screen-reader block. Both reached for behaviour the vocabulary could not carry — one named it, one hand-rolled it. Provenance is shadow throughout, per §1.1. The single invented-container sighting is BELOW the cross-family bar for a container and is explicitly not the ground; the behavioural evidence is. §1.2 — irreducible, and the NavBar counter-precedent is what it had to answer. The Navigation cluster declined a nav kind BECAUSE AriaRole.Navigation already carried the landmark: the fact needing to be said was an attribute, and an attribute is expressible as data on a Box. No projection does the same here. role="tree", aria-level, aria-expanded and aria-setsize are attributes and could be carried that way — the roving focus, the six key bindings and the expand-collapse-traverse semantics are not attributes at all. A List of Disclosures is N independently focusable containers: every row its own tab stop, no focus that walks the structure, Left that does not close the row you are in and move to its parent, Home that does not reach the first row of the hierarchy, no row announcing its depth or its position among its siblings. That is the sortStateKey shape — a behaviour the host performs over rows it has not yet expanded, keyed by something only the document can name. §1.3 — the wave's most expensive admission, paid in full and priced before it was taken. A recursive codec (the wire's first self-referential record, and a FOURTH recursive entry point in every decoder, bounded on its own axis per §21.5's implementers' note); a full WAI-ARIA keyboard interaction hand-audited rather than defaulted; a normative SSR floor and a separate email projection; five new fuaran-* classes parity-locked across every renderer and every stylesheet copy; two validator codes with go-red twins; every conformant host in the §11.0 roster; §11 step 6 on all three authoring veneers; §11.2 vocabulary attestation, which a kind takes and a field does not; and the working-memory tax on every prompt forever. §3.2 confusion delta — ONE new pair, Tree ↔ List+Disclosure, and it is this row's real risk. The line an emitter must hold, recorded so a rubric can measure it and the pack teaching has one sentence: finite static nesting the reader simply reads is a List of Disclosures; a hierarchy the reader WALKS — one focus, arrow keys, open and close as they go — is a Tree. The discriminator is keyboard traversal, never nesting, which is why the scope is deliberately narrow and the composition is left standing beside the kind rather than swallowed by it. The pre baseline is this row. This is the pair to watch so it does not become the next Toast → Callout. RESERVED and explicitly out of this cut: a bound or lazily-fetched children source. It needs the shared-data-source charter's machinery — a source that yields rows on expansion is a data-fetch relationship, not a static payload — and its own walk. Nothing about the shipped kind forecloses it: items would gain a sibling source slot, not change shape. |
Timeline | Role / Variant | A List role (ordered temporal-trail rendering), not a kind. |
Rating / ColorPicker | Variant (shipped) | Shipped as FormFieldKind.Rating and FormFieldKind.Color at 0.67.0 (Phase 1130). The pre-ruling stands and the tier was never in question — this row said variant and both are variants — so, like the Combobox row, this ceremony had nothing to refute. What it has to correct is the row's own SPELLING: the case is Color, not ColorPicker, because every other case in this DU names the thing the reader manipulates rather than the widget that manipulates it, and a Picker suffix on exactly one case would be the near-synonym-by-naming the charter forbids. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning. Both were pre-ruled and both were sequenced LAST in the input cluster because both are genuinely niche next to Combobox; the mandate is what admits them for completeness ahead of promotion rather than on sighting counts neither would ever reach. §1.2 irreducibility. Nothing composes to either. A rating is not a RangedNumber with stars: the two carry different ARIA (a rating announces "3.5 out of 5", a ranged number a bare figure), different keyboard granularity, and — the discriminating fact — different SEMANTICS, which is why the confusion pair below exists. A colour is not a Text field with a rule.format: a format constrains typed text, where this case opens the operating system's colour picker, which no rule on a text box can produce. THE color RULE-FORMAT DECLINE STANDS, and the distinction is recorded here because it is the one a later reader will trip on. The EmailField row below declined color as a rule.format for want of §1.1 evidence; that decline is about a VALIDATION spelling on a text control and is untouched by this admission of a CONTROL. The two do not overlap, and admitting one is not evidence for the other. §1.3 cost — paid in full, and a variant DOES take §11.2 vocabulary attestation (manifest.formFieldKinds enumerates cases): IDL + generated layer + policy decoder + schema + corpus + spec §3.6.17 + both renderers + a new pure model shared by both + the reference stylesheet and its fingerprint + two validator codes + the server-driven submission floor + one authoring-surface decision per veneer + every host in the §11.0 roster. §3.2 confusion delta — ONE new pair, Rating ↔ RangedNumber, and it is this row's real risk. The line an emitter must hold, recorded here so a rubric can measure it and so the pack teaching (Phase 1127) has one sentence to teach: a subjective SCORE on a small ordinal scale is Rating; a numeric QUANTITY the reader types or drags is RangedNumber — the test being who the number belongs to, a judgement a person GIVES versus a measurement they REPORT. Color adds no pair at all: no other case admits a colour, and its #rrggbb value shape is refused rather than shared. The pre baseline is this row; the post measurement is 1127's. |
Tags / tag input / token field / chips | Variant (ADMITTED 2026-09-04, Phase 1121) | Shipped as FormFieldKind.Tokens at 0.69.0. This row had no pre-ruling at all — it is a genuinely new reserved name, filed WITH its admission, which is the shape §5 warns about (a ruling that lives only in a phase file is a ruling the next proposal re-derives) and the reason it is written out in full here. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning, and this row is the second-clearest instance of why that override was needed (after Combobox). The gap is not an invalid document: a multi-Select over a closed set parses, validates and renders, and simply cannot admit a value nobody listed in advance; constrained decoding suppresses the invented-$type sighting class, and "used a multi-Select instead" is valid-but-less-specific, which every census records as a clean pass. No reactive channel can report it. §1.2 irreducibility. Nothing composes to it. A multi-Select is closed by construction. A Combobox commits ONE value. A Combobox PER ITEM — the composition an emitter actually reaches for — is N fields with N ids, no gesture that removes the third entry, no way to say how many there may be, and a submission shaped tag1/tag2/tag3 rather than one list; it is not a smaller version of this control but a different one. The suggestion source needed no new vocabulary, which is the reuse half of the same test: a Query-bound suggestions IS the asynchronous feed, and the value slot is the Binding<string list> the multi-select values has carried since Phase 291. §1.3 cost — paid in full, and a variant DOES take §11.2 vocabulary attestation (manifest.formFieldKinds enumerates cases): IDL + generated layer + policy decoder + schema + corpus + spec §3.6.19 + both renderers + a new pure model shared by both + the reference stylesheet and its fingerprint + two validator codes + the server-driven submission floor + one authoring-surface decision per veneer + every host in the §11.0 roster. THE SPELLING, and it is a language constraint rather than a preference. The case is Tokens, not Tags: Tags is a RESERVED union-case name in F# — the compiler generates a nested static class Tags in every discriminated union to hold its case-tag constants, so a case spelled that way is FS1219 in ANY F# union. Keeping "Tags" on the wire with Tokens in F# was considered and DECLINED: this vocabulary's IDL has no case↔wire split for union cases at all (enumWith gives enums one; unions have none), so it would be new generator/schema/TS/sampler machinery to produce exactly one case whose wire token no host's own source can spell — and the reference host is what GENERATES the corpus. One name on both sides is worth more than the domain word, and the reserved NAME in this row stays Tags because that is what a reader searches for. The ColorPicker → Color correction one row up is the same ceremony for a different cause. THE DEFAULT POLARITY, recorded because it INVERTS Combobox's and a host will trip on it. allowFreeText omits at true here and at false there. The rule is one rule, not two habits: the default follows the REQUIRED-NESS OF THE SET. Combobox.options is required, so a combobox always has a candidate set and constrained is its resting state; Tokens.suggestions is optional, so a token box with nothing to suggest is the commonest shape rather than a degenerate one and open is its resting state. What it buys is a shortest document that WORKS on both cases — {"$type":"Tokens"} is the plain open token box — rather than one that works on one and names an unusable control on the other. §3.2 confusion delta — ONE new TRIANGLE rather than a pair, and it is this row's real risk. The line an emitter must hold, recorded here so a rubric can measure it and so the pack teaching (Phase 1127) has one sentence to teach: a CLOSED set small enough for a reader to scan is a Select with multiple; ONE value from a large, searchable or asynchronous set is a Combobox; SEVERAL values — over a set that is open, or that the document does not enumerate at all — is Tokens — two axes, how many values and whether the set is closed. The Combobox-per-item mistake is the one to watch, because it is what an emitter reaches for when it knows Combobox and has not met this case. The pre baseline is this row; the post measurement is 1127's. |
Combobox / autocomplete / typeahead | Variant (shipped) | Shipped as FormFieldKind.Combobox at 0.51.0 (Phase 1113): options as an ordinary Binding<SelectOption list>, a Choice-shaped value slot and handler, and allowFreeText omitting at false. The pre-ruling stands and the tier was never in question — this row said variant and it is a variant — so unlike the Embed and Tooltip admissions this ceremony had nothing to refute. What it had to correct is the row's own BUNDLING: Combobox sat here beside Rating and ColorPicker as though equally niche, and typeahead is the most common data-entry control on the web. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning, and this row is the clearest instance of why that override was needed. The gap is not an invalid document: a Choice over two hundred options is valid, renderable, and unusable. Constrained decoding suppresses the invented-$type sighting class, so no model reaches for a case the schema forbids; and "used a Select instead" is valid-but-less-specific, which every census records as a clean pass. That is the class the FieldRule row proved uncallable, and no reactive channel can ever report it. §1.2 irreducibility. Nothing composes to it. A Choice is a bounded menu with no search; a Text field admits anything and suggests nothing; a Text beside a List is two controls with no value relationship, no keyboard route between them, and no role="combobox" — the browser's accessibility contract for this pattern names ONE element, and no arrangement of two produces it. The option source needed no new vocabulary, which is the reuse half of the same test: a Query-bound options IS the asynchronous suggestion feed. §1.3 cost — paid in full, and unlike a field addition a variant DOES take §11.2 vocabulary attestation (manifest.formFieldKinds enumerates cases). IDL + generated layer + policy decoder + schema + corpus + spec + both renderers + the reference stylesheet and its fingerprint + one validator code + the server-driven bounds check and the FormValidation floor + one authoring-surface decision per veneer + every host in the §11.0 roster. §3.2 confusion delta — ONE new pair, Select/Choice ↔ Combobox, and it is the row's real risk. The line an emitter must hold, recorded here so a rubric can measure it and so the pack teaching (Phase 1127) has one sentence to teach: a bounded known set the reader scans is Choice; a large, searchable or asynchronous set — or one that admits a value off the list — is Combobox. The pre baseline is this row; the post measurement is 1127's. This is the pair to watch so it does not become the next Toast → Callout. |
KanbanBoard | Composition (prerequisite shipped 2026-09-03) | Box + DataGrid – application-space fragment territory (Phase 380), never a language kind. The disposition STANDS and is now honest, which it was not before Phase 1123. Until that phase the composition this row named could not be composed: Box + N DataGrids produced the arrangement of a board and had no spelling at all for its defining interaction — moving a card from one column to another — so a row that reads as a settled redirect was in fact redirecting to a target that could not carry the load. What was missing is now named: the two columns declare a shared transfer key (transferOutKey / transferInKey, the Interaction-cluster row above), and the redirect is a real one. Recorded here rather than only in that row for this appendix's own standing lesson — a ruling that lives somewhere the reader of THIS row will not look is a ruling the next session re-derives. |
New chart types (Gauge / Funnel / Heatmap / Treemap / Sankey) | Variant | ChartKind variants, one and all. |
ReferenceLine (a named value line on a chart — a zero line, a target, a threshold, a budget) | Variant (ADMITTED 2026-09-04, Phase 1489) — a case on the new ChartAnnotation DU, reached through one §2.1 Field on ChartSpec | This row and the two below are a genuinely new reserved name filed WITH its admission, which is the shape §5 warns about, so the family's walk is written out here in full and the two members below carry only what is theirs. Admitted by the charter walk BEFORE the implementation lands (Phase 1490), on the Icon row's own principle — the charter widens before implementation, not after. THE TIER IS THE FAMILY'S CENTRAL DECISION and it is why one walk covers three members. ChartSpec gains ONE optional member, annotations, a list over a closed ChartAnnotation DU; each member is a case on that DU. The rejected alternative — three separate optional fields — is the same §2.1 FIELD tier today and diverges on what the FOURTH member costs: one case on a union whose $type dispatch already exists, versus a fourth widening of ChartSpec, which breaks full-literal construction (FS0764) in every consumer that does not use the smart constructors and is a minor bump each time under the 2026-08-04 record-widening dispensation. Since more members will come one at a time by design, the list pays the record-widening cost once, for the family, and makes each subsequent member cheap and visible. It also leaves §11.2 attestation available where three fields foreclose it: a manifest enumerates CASES, so a case is the kind of thing a future chartAnnotationKinds key could name and a field is not. §1.1 demand evidence — a distinct operator mandate recorded on the requirement chart-annotations (2026-09-04), NOT the 2026-08-28 platform-baseline one, though on the Media row's reasoning about what an override is: attributable, for these rows, and not a softening of the gate. Stated plainly because it is this member's weakest point: ReferenceLine has NO sighting of its own. Its ground is the mandate plus the irreducibility below; the two sighted members are the rows underneath. The family's corroboration is the 2026-09-02 shadow leg, run shadow-20260902-141855-anthropic (provenance carried per §1.1), which left shape_marker and range_band explicitly ? under the named falsifier "decided by whether ChartSpec carries any annotation slot" — and the slot decided here is that falsifier's answer, which is what converts a logged ? into a typed disposition. Their own count is log-and-watch, so they corroborate and do not by themselves admit. §1.2 irreducibility — and the redirect that has to be refuted is Drawing, not a composition. A chart lowers to a Drawing, and a Drawing draws lines, rectangles and labels; the obvious answer is therefore "overlay one". It fails, and the reason is the whole family's justification: a Drawing is placed, and an annotation is ADDRESSED. The drawing's coordinate space is a function of the resolved data — the value mapping, the band pitch, the margins derived from the deterministic text metrics — and of the style record the HOST chose at render time, so an author cannot compute it: they may not hold the rows (a Ref, a Query, a non-empty pipeline), and they do not choose the style. An overlay at pixel coordinates is correct for exactly one dataset, one style and one canvas size and silently wrong for every other; it survives no data change, no theme flip, no restyle and no resize. It is equally not a style field — style is the host's decision at render time, and "the target is 4.5%" is a fact about the data being shown, not about how a host wants it to look; putting it there would make one document say different things on two hosts and make a fact about the world unstateable by its author. And it is not a second series: a threshold spelled as a flat series enters the legend, the palette, the accessible summary's series clause and the peak computation, and is announced as data it is not. §1.3 cost — paid in full, and stated once here for the family. The IDL and the generated layer (a new union plus one field on the Chart kind), encoder + decoder + schema, the chart-lowering/* corpus and its manifest regeneration, the lowering arm on the reference host and then on every lowering host in the roster, ChartStyle constants per member with the palette gate applied in BOTH themes where a member inks a fill, validator codes with reject vectors and go-red twins, the accessible-summary grammar gaining a clause, the three authoring veneers under §11 step 6, and the working-memory tax of one new slot with three members. The §2.1 disadvantage applies and is named as §2.1 requires: manifest.kinds and manifest.formFieldKinds enumerate cases and ChartAnnotation is neither, so a host that has never met a case is named by no manifest and only the fixtures catch it. §3.2 confusion delta — ZERO on the metric, ONE pair on the axis §2 tells us to watch. The metric counts valid-but-wrong-KIND emissions; no NodeKind, ChartKind or FormFieldKind case is added, so no pair enters the matrix. The intra-DU pair is ReferenceLine ↔ a degenerate RangeBand whose two endpoints are equal, and the line an emitter must hold, written so a rubric can measure it: a single value the reader compares against is a ReferenceLine; an INTERVAL with extent is a RangeBand; a moment in TIME the reader relates the series to is an EventMarker. The pair that would have been worse does not exist by construction: ReferenceLine carries no x address at all, so a vertical line at a date has exactly one spelling. The pre baseline is this row — the set has no other member today, so the rate is undefined rather than zero, and the first measurement is the members' own. |
EventMarker (a labelled vertical line at an x position — "GFC", "Brexit", a launch, a policy change) | Variant (ADMITTED 2026-09-04, Phase 1489) — a case on ChartAnnotation; implementation is Phase 1491 | The family's tier, §1.3 cost and §3.2 reading are on the ReferenceLine row above and are paid once for the slot; what follows is this member's own. §1.1 — the strongest sighting count of the three, and it is still only a corroboration. The 2026-09-02 shadow leg shadow-20260902-141855-anthropic emitted shape_marker ×2 on kc-009 (shape over twelve months), logged with its shadow provenance and deliberately left ? under the falsifier "decided by whether ChartSpec carries any annotation slot". Two sightings on one prompt is not the cross-family count the admitted neighbours cite (DateRange ×9, DragReorder ×20, TonedPill ×26), and the log's own disposition is log-and-watch — so the admission ground remains the 2026-09-04 operator mandate on the requirement, with this as the evidence that the gap is real rather than anticipated. §1.2 — irreducible on the ReferenceLine row's Drawing argument, and additionally on the ADDRESS. This is the member that makes the family's addressing rule concrete: an x address is a category key on a band axis or an ISO date under XScale = Temporal, which is the declared-not-inferred split the temporal-axis phase already settled, applied one level down. A mismatched address (a date under a band axis, a key under a temporal one) is REFUSED, never coerced, and a category key is grounded against the rows — present, exactly once. Nothing composes to this: a marker's x is a coordinate no author can compute, and the two forms are exactly the two the axis itself distinguishes, so no third spelling is admitted. What this member is NOT allowed to mint, and where the pressure will come from: an offset, a side, a placement token or a nudge rule. Several markers close together collide, and the resolution is the shipped label rule — fit-gated by the deterministic text metrics, SUPPRESSED on no fit, never clipped, never overlapped, never moved onto a mark or across a neighbour. A suppressed label does not suppress its marker: the line still draws and the annotation still reaches the accessible summary, which is what keeps suppression a layout decision rather than a silent loss of authored content. §3.2 — adds no pair of its own. It is the only spelling for a vertical line at an x position, and the ReferenceLine row records why: the value form addresses the value axis only. Its confusion risk is with a RangeBand on the x axis, which is the same interval-versus-instant line the row above already states. |
RangeBand (a shaded interval on either axis — a recession, a term of office, a tolerance band, a forecast window) | Variant (ADMITTED 2026-09-04, Phase 1489) — a case on ChartAnnotation; implementation is Phase 1492 | The family's tier, §1.3 cost and §3.2 reading are on the ReferenceLine row above; what follows is this member's own. §1.1 — one sighting, range_band ×1 on kc-009 from the 2026-09-02 shadow leg shadow-20260902-141855-anthropic, carried with its shadow provenance and left ? under the same falsifier this walk answers. One sighting is a sighting, never demand — the admission ground is the 2026-09-04 operator mandate on the requirement, and this row says so rather than inflating the count. §1.2 — irreducible on the ReferenceLine row's argument, and it is the member for which the placed-overlay alternative is worst. A band is a large tinted rectangle; drawn at pixels it does not merely drift on a data change, it covers marks it was never meant to touch. It is also the member that most looks like a style concern and is not: "there was a recession from 2008 to 2009" is a fact about the world the chart is showing, and the tint is the only part of it that belongs to the host. Its address is a PAIR of the same two forms the other members use, on an axis it names, because a band is the one member legible on either — which is why the case carries its axis and the other two do not. §1.3 — the one member whose ink is not free. Its fill must pass the palette gate's lightness-band checks in BOTH themes rather than being authored, so that a band reads as a tint of the surface rather than a fill competing with the marks. THIS IS THE MEMBER THAT FORCED THE Z-ORDER TO BE NORMATIVE, and the reason belongs on the public record rather than in a phase file: in inline SVG z-order IS emission order, so a host that emitted a band after its series would draw a tinted rectangle over the data and still produce a perfectly valid document — nothing fails, the chart is simply wrong, on that host only, in a way no schema and no validator can see. The draw order is therefore a lowering rule the corpus pins (Phase 1489's §4l — bands behind the series, lines and markers in front, labels last, document order within a case breaking the tie), and this member is where it is first exercised. §3.2 — the family's one real pair, stated on the ReferenceLine row: a degenerate band whose endpoints are equal is a ReferenceLine spelled the expensive way, and the discriminator is extent. |
ChartCallout / ChartNote (a pixel-placed note or leader-line callout ON a chart — not the shipped Callout NodeKind, which is an in-page inline notice and a different thing entirely) | Covered (by Drawing) — DECLINED 2026-09-04 as a ChartAnnotation case, Phase 1489 | Filed as a decline in the same walk that admitted the three rows above, so the family's boundary is written down at the moment it is drawn rather than re-argued by the first proposal that reaches for it. What is asked for: a free note, or a boxed callout with a leader line, placed there on the chart — the commonest thing a person means by "annotate this chart". Why it is not admitted into ChartAnnotation: its entire content is a POSITION and a SHAPE. Admitting it would put geometry into the one slot that exists to forbid geometry, and every subsequent member would then have a precedent for carrying a pixel — which is the attachment-scope-creep the chart plan's own risk row names and the reason each attachment kind is a charter event one at a time rather than a grammar port. The redirect is real, not a deferral, and that distinction is what the KanbanBoard row teaches: a redirect is honest only if the target can carry the load. Drawing demonstrably can — it is a closed typed shape DU with its own viewBox and coordinate space, and it is what a chart itself lowers to, so a placed annotation is expressible today by an author who states the coordinates they mean. What is NOT claimed, stated so the decline is not read as a promise: a Drawing placed over a chart does not stay aligned when the data changes, the theme flips, the style is replaced or the canvas resizes. That is the point rather than a defect — you asked for a placed picture and you get a placed picture — and it is exactly why the admitted three carry an address instead. And the half that IS admitted should not be missed by a reader of this row: every admitted member carries an optional LABEL whose position is DERIVED from its address and fit-gated by the shipped text metrics, so "annotate this threshold with a name" is expressible; only "put this text at these coordinates" is Drawing. Reopen condition (the falsifier): a recorded consumer needing a leader-line callout anchored to a mark identity — the derivation-based (series-field, category-key) id the keyed-mark-identity phase already ships — rather than to a pixel. That would be a FOURTH data-addressed member ({ at: <mark reference>; text }), a materially different proposal from the one declined here, and it clears §1 on its own evidence. A sighting of a pixel-placed note is not that falsifier; it is this row's evidence for the conclusion already reached. |
StatusPill / conditional row emphasis | Variant (shipped) | Shipped as CellKindErased.TonedPill at 0.11.0 (Phase 750): field + a value→ToneVariant map + an omit-when-default default. Admitted on 26 usage-evaluation occurrences of one intent across three prompt families and three providers, every one a partial. Irreducible in the strongest available sense — not "no composition expresses it" but "no wire spelling exists at all": the pre-existing Pill case's tone is a 'row -> ToneVariant closure, which erases to "<closure>", so the rule could not be said rather than being awkward to say. Cost: one variant + a Map field, no new kind, no new class vocabulary (it renders through the existing fuaran-grid-cell-pill / fuaran-pill-<tone> hooks). Confusion-delta: measured as the three canaries' criteria moving off PARTIAL in the next cohort — the pack change supersedes the current baseline by design. |
Grid-behaviour cluster (added 2026-08-27 by Phase 873 — the Phase 860 charter's own rows)
Filed retroactively, and the reason it was missing is worth more than the rows. Phase 860's charter
was approved and its three implementation phases shipped, but only ONE of its rulings reached this file
— Pagination, and only because a reserved NAME already sat in the Navigation cluster for it to amend.
The rest had no reserved name to attach to, so nothing pulled them here, and this charter's own §5 says
the charter is not a surface a reader consults. A ruling that lives only in a charter document and a
phase Outcome is a ruling the next session re-derives.
Its governing ruling is one sentence and it decides every row below — a grid behaviour the user drives
is declared as a named State KEY that the grid both writes and reads, carrying a descriptor whose shape
the specification fixes; the affordance belongs to the renderer. The corollary is the part that keeps
getting re-proposed: there is no grid-level sortable or pageable boolean, because the key IS the
affordance and a flag with no key behind it is a decorative control writing state nothing reads.
| Reserved name | Disposition | Ruling |
|---|---|---|
sortStateKey + bound defaultSort (Phase 861) | Field (shipped 2026-08-16, 0.26.0) | Not a kind, not a variant. sortStateKey names the key carrying {"column", "direction"}; declaring it IS the header affordance, so a "sortable grid" that names no key is prose. defaultSort reuses the record and the field NAME staticRows already carried from Phase 801 — same behaviour, same spelling, deliberately not a second vocabulary — and applies only while the key carries nothing; once the user has sorted, the state wins. A grid may declare defaultSort with no sortStateKey, which is an opening order without interactive re-sorting. §1.1: census #26 at HIGH urgency, stress-007/c2 ×cross-family. |
per-column sortable (Phase 861) | Field (shipped) | A column flag NARROWS and never widens — the charter's rule, and the reason true under a grid declaring no sortStateKey is FUARAN094 rather than a silent no-op: a column asking to turn a behaviour ON is asking for something the rule does not grant. Absent inherits; false opts out. |
pageSize + pageStateKey (Phase 862) | Field (shipped) | The Pagination row in the Navigation cluster carries the ruling; recorded here so the grid-behaviour family reads whole. The pager is renderer-owned, which is what makes a decorative pager unauthorable rather than merely discouraged. |
editStateKey (Phase 863) | Field (shipped 2026-08-16, 0.26.0) | Not a kind, not a variant, and not a convenience. Before it the only spelling for an edit destination was a closure, which erases to "<closure>" — so a DECODED editable grid could not say where its edits land, which is census #27's whole complaint. Absent keeps Phase 663's shipped behaviour exactly (write back to the grid's own source when that source is a direct State binding, display-only otherwise), so nothing already authored changes meaning. It is also the destination Phase 866's admitted row-reorder reuses rather than minting a second write path. |
per-column editable (Phase 863) | Field (shipped) | The write-side twin of sortable, and the same narrowing rule: absent inherits the grid-level editable, false makes a column read-only under a grid-level true — the declaration that read-only-by-omission could not make — and true under a non-editable grid is FUARAN095. Its own demand row is distinct from #27's: #27 asks WHERE edits go, this asks WHICH columns may be edited, and the demand log carries them separately for that reason. |
exportable (Phase 1125) | Field (shipped 2026-09-03, 0.65.0) — ADMITTED | Not a kind, not a variant, and not a second export vocabulary. One bool on DataGridSpec, omitted at false: this grid's rows are the reader's to take. The renderer draws the control and performs the act — the rows the client holds, serialised to RFC 4180 CSV and handed over as a file. It must be read beside the Interaction cluster's Export / DownloadAs and ExportRendered rows, ratified the same day, and it does not contradict either. Those rule the EFFECT axis: a document asking a HOST to produce a file, which is Covered by Action.Invoke and needs no vocabulary because the host owns the capability, the format and the permission. This is the GRID-BEHAVIOUR axis, and the fact it carries is a different one — not make me a file but these rows may be taken, which is a statement about this document's own data that no host capability can be told from outside. §1.2 irreducibility — and the composition it has to refute is Button + Action.Invoke, which is precisely the composition those two rows endorse. It fails here for the reason the Pagination row's decorative-pager argument fails: an invoked host capability receives what the TREE can name, and what an export needs is what the RENDERER resolved — the rows after the reader's sort, the columns in the order they are on screen, the declared CellFormat each was rendered through, and which page of a host-paged source the client actually holds. None of that is nameable on the wire; a binding resolves in the renderer. A free-standing export button beside the grid would therefore export something else: the source re-fetched, unsorted, unformatted, and identified only by whatever grid id the author remembered to pass. That is the affordance→op charter's governing sentence deciding the case UNAMENDED — the grid both hosts the gesture and consumes its effect, and it is the same node at both ends. Why a BOOLEAN is admissible here when a grid-level sortable / pageable is refused by name. That refusal is not a rule against flags; it is a rule against a flag with no key behind it. Sorting and paging write STATE the grid reads back, so the key IS the affordance and a boolean beside it would be a second, weaker spelling of the same thing. An export writes no state at all — it produces a file and returns nothing to the tree, the Action.Print shape of an effect that reports nothing — so there is no key it could name, no descriptor whose shape a specification would have to fix, and no reader of that key to disappoint. The boolean is the whole declaration, which is exactly why reorderable is one too. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning; this phase adds no new override. The gap is invisible to both reactive channels in the way that mandate exists for: constrained decoding suppresses the invented-member sighting class, and "emitted a grid the user cannot get data out of" is not an error a census can score, because a grid with no export is a perfectly valid tree. §1.3 cost — a field's bill, paid in full. The IDL and the generated layer, the policy decoder with its three near misses (export / exportable_csv / downloadable), the JSON Schema, the corpus, UpdateProp and availableFields, the client renderer on BOTH grid legs, one validator code with go-red twins, the reference stylesheet and its fingerprint, one authoring-surface decision per veneer, and every host in the §11.0 roster. It does NOT take §11.2 vocabulary attestation — manifest.kinds enumerates cases, and this is a member. §3.2 confusion delta — ONE new pair, and it is the pair to watch. exportable ↔ Action.Invoke-behind-a-Button, recorded here so a rubric can measure it and the pack teaching has one sentence: taking away the rows THIS GRID is showing is exportable; asking the host to produce anything else — a rendering, a report, a file the tree does not hold — is an Invoke. The discriminator is whose data it is, never who wrote the button. The Phase 900 hatch assessment: it IS a hatch, narrowly, and it is disclosed rather than waived. It puts a FILE on the reader's disk, which is the only effect in the gated set that outlives the page, and the file is NAMED by the tree, so a decoded tree from an untrusted emitter chooses what appears in a download list. What crosses: the grid's own already-rendered rows, and nothing else — no source, no unrendered column, no state. What mediates: ActionDescriptor.Export nodeId, default-deny and per-grid, so permitting one grid permits nothing else; and the reader's own activation, since the renderer never exports unbidden. What is not claimed: nothing prevents an emitter putting misleading content in a grid and inviting the reader to save it — that is the same trust a rendering already carries, and the export adds no channel a screenshot does not. It discloses nothing back: the call returns unit, so the tree cannot learn whether the reader kept the file. Reopen conditions — two, and both are about a fact the renderer does not hold. First, a recorded need for a full-dataset export over a HOST-PAGED source, which this ruling puts firmly in host chrome because the client does not hold the data and the tree cannot substantiate it. Second, recurrence-grade cross-family sightings of a DECLARED export PROJECTION — a raw-versus-formatted choice, a column excluded from the file, a delimiter — stated on the node rather than settled by the renderer; the formatted-not-raw decision this phase records is the whole of what those would reopen. Neither an inconvenience in wiring a host capability, nor a preference for a different file format, reopens it. |
a grid-level sortable / pageable boolean | Refused by name | Not reserved, not deferred — refused, and refused at DECODE by the near-miss table rather than ignored, so an emission reaching for it is told what to write. The staticRows path keeps its own sortable, which is not an exception: a static table holds its rows in the tree, so there is no state key for a reader to name. |
Interaction / affordance cluster (added 2026-08-18 — the affordance→op charter, Phase 866)
The cluster the taxonomy lacked: how a user gesture reaches an effect. Its governing ruling is one sentence and it decides every row below — a user gesture is not named on the wire; the wire names a capability on the node that both hosts the gesture and consumes its effect, and the renderer owns the affordance, dispatching through the existing dispatch gate. Four rows reduce under it; three are Host chrome, and those are the point of recording the cluster at all — a decline whose reasoning is written down is what stops the next proposal re-deriving it.
The third reduction (2026-09-03, Phase 1115) is the first the sentence decided with no amendment at all — file drop and paste ingestion arrived, the sentence answered, and the ceremony's work was spent on the accessibility consequence rather than on the tier. That is what a governing ruling is supposed to buy, and it is worth noting the first time it does.
The fourth (2026-09-03, Phase 1123) is the first that the sentence could NOT decide unamended, and
the amendment is one clause. As written the sentence names the node that both hosts the gesture and
consumes its effect — singular, and every row above it is singular: one grid reorders its own
rows, one upload consumes its own drop, one chart publishes its own selection. Moving an item from one
container to another has two nodes and neither one of them consumes the effect: the source loses an
item it no longer holds, the target gains one it did not have, and a ruling that named either alone
would be describing half a gesture. The extension, therefore, and it is deliberately the smallest one
that admits the case: where a gesture spans TWO nodes, the wire names the capability on BOTH ENDS as a
shared KEY each declares its own side of, and the effect is ONE record written to that key. Everything
else the sentence already said stands unchanged — no gesture is named, no Action case is minted, no
op-stream representation exists, and the renderer owns the drag, the drop, the drag image, the visible
drop states and the keyboard equivalent.
The third of those declines (2026-09-02) is the first in this appendix about render vocabulary rather than wire vocabulary, and it refines the governing sentence rather than sitting beside it: the renderer owning the affordance means it also owns the wiring, and a host whose renderer emits inert HTML reaches its own core for the destination rather than reading it back out of its own markup.
| Reserved name | Disposition | Ruling |
|---|---|---|
DragReorder / RowReorder | Composition / renderer-owned affordance | Not a kind and not a new Action verb. A reorder is a write of the collection the node already reads, so it is a capability flag on the grid that reorders (reorderable), whose destination is the grid's existing edit destination (editStateKey, else the source's own state key) — the Phase 863 field reused, never a second write path. The gesture itself has no wire name: the renderer draws the drag handle and its keyboard equivalent, which is part of the affordance rather than a follow-up. Admitted on ×20 cross-family task-qualified sightings (2026-08-15), ranked last of its cohort by the Phase 856 baseline read. See the affordance→op charter (Phase 866). |
CrossContainerTransfer / KanbanMove (move an item from one container to another) | Admitted 2026-09-03 (Phase 1123) — §2.1 FIELD PAIR on DataGridSpec; the governing sentence EXTENDED to two nodes | The cluster's third admission and the first that needed the sentence widened — see the amendment above, which is the row's real content and is stated there rather than here because it governs every future two-node gesture and not only this one. What is added to the wire is transferOutKey and transferInKey, two optional strings naming ONE State key: a grid declaring transferOutKey K may release rows to K, a grid declaring transferInKey K accepts them from K, and a grid that declares both with the same K does each. The drop writes {"itemId","from","to","index"} to K. The completion rule, which the anticipated shape left unstated and which is the walk's own finding. A record that nothing applies is a fake affordance in the precise sense this cluster exists to foreclose: the reader drags, an object appears in State, and no row moves — and reorderable one field over works out of the box, so an author who reached for one and then the other would find the second inert with nothing red anywhere. So the renderer OWES the application as well as the gesture: on drop it writes the record, and it commits each half to that end's own already-shipped write destination — the editStateKey precedence the reorder path already resolves through (declared key, else the Phase-663 State-source floor, else nothing), never a second write path. Why the record is not simply REPLACED by those two writes, which was the obvious smaller move and the wrong one: the canonical board's columns are FILTERED VIEWS over one collection — a Binding.Transform, which is one-directional and has no writable destination at either end — so on the case the feature exists for there is nothing to write and the record is the entire outcome; and card 7 moved to Done is a domain event that two rewritten arrays do not carry and that an application would have to recover by diffing. One mechanism whose second half degrades honestly, rather than two mechanisms. Q1 — which kinds may declare it: DataGrid, and no other, and the restriction is not arbitrary. A transfer end needs a collection whose items have identity AND that is a value the reader may change; Box.children, List.items and Tree.items are authored TREE content, so a reader moving one would be performing a tree op — the Undo row's argument exactly (the op stream's inverses invert the AI's authoring channel, and every user gesture the language admits writes STATE), and the demand log's own 866 correction says it in one line: MoveNode moves nodes in the authored tree, where the demand asks to move rows in a state-held collection. Chart and Map are the only other collection-bound kinds and are not containers a reader drops into. The rule is therefore the kinds whose contents are state rather than tree, which today is one kind and will admit a second with no new ruling. Q2 — item identity: rowKeyField, already shipped, and no second identity vocabulary is minted. The closure rowKey erases to "<closure>" on decode, so a DECODED transfer end naming only a closure cannot say what moved; FUARAN130 (Warning) reports exactly that, rather than letting a transfer write a record whose itemId names nothing. Q3 — two fields, not one. A single symmetric key would make every declaration bidirectional, and the one-way ends are real and ordinary: an archive or trash column that accepts and never releases, a Done column that releases nothing back. Two fields also make the dead-pairing check answerable in BOTH directions, which one field cannot. Neither carries the -StateKey suffix, deliberately and against sortStateKey / pageStateKey / editStateKey / expandedStateKey / selectionStateKey: that suffix marks a key a node both writes AND READS to change its own presentation, and neither end reads this one for its own presentation — borrowing it would assert a self-consumption that does not hold, which is the one thing the completion rule above had to be written down to correct. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning; this phase adds no new override. The honest read of the reactive channels is worth recording because it is sharper here than on any row that preceded it: the demand log carries drag-reorder at ×20 cross-family and not one cross-container sighting, because no probe ever posed a board task and "emitted two independent grids" is valid-but-less-specific, which every census scores as a clean pass. And the 2026-09-03 census that enumerates unexamined classes could not have found it either — its finding is about classes carrying NO row, and this class HAD one (KanbanBoard, Composition). That is the finding: a row redirecting to a composition the language cannot actually compose reads exactly like a settled ruling, and is invisible to the instrument built to find gaps in this appendix's coverage. §1.2 irreducibility. Not a kind — KanbanBoard stays a Composition and is now composable, which is the point. Not a variant. Not an Action case: the effect is a state write, and an Action.Transfer beside SetState would be a second write path, which is the DragReorder row's own sentence at a different slot. Not ExtraAttributes, which are wire-omitted, so no decoded tree on any host could carry the declaration by that route. Not covered by reorderable, which is one grid's own row order and has no second end at all. And not derivable: a host cannot infer that two grids on a page are one board rather than two unrelated tables. That pairing is the fact only the tree knows, and it is the whole of what this admission adds — the sortStateKey shape again, a behaviour the host performs keyed by something only the document can name. §1.3 cost — paid in full, and the field tier's disadvantage named as §2.1 requires: the pair SKIPS §11.2 vocabulary attestation, because manifest.kinds enumerates CASES, so a host that has never met either field is named by no manifest and only the fixtures catch it. What it does cost: the IDL and the generated layer, the policy decoder, the schema, the corpus vectors, both renderers plus a new client module, the reference stylesheet's fuaran-drag-* family and the vocabulary-fingerprint restamp it forces, two validator codes (FUARAN129/130) with go-red twins, an SSR floor stated normatively, one authoring-surface decision per veneer, and every host in the §11.0 roster. §3.2 confusion delta — structurally ZERO, the field tier's one free axis: no case is added at any level, so no pair enters the confusion matrix and none changes. The accessibility line is an obligation and not a courtesy. A drag has no keyboard equivalent and none is invented; what is provided is a SECOND ROUTE to the same effect — cut/paste, Control+X to lift and Control+V to place, Escape to cancel — advertised on the handle through aria-keyshortcuts and announced through a role="status" line, because an undiscoverable shortcut is the fake affordance the KeyboardShortcut decline names by that word. This is the FileDrop row's rule reaching the opposite outcome, and the difference is the whole of why: a file drop's alternative route already existed (the picker, emitted whatever is declared), where a transfer's did not exist at all, so here the second route had to be BUILT rather than merely preserved. Reopen conditions. A container kind whose contents are state-bound rather than tree-authored — a bound repeater over nodes, should one ever be admitted — extends the pair to that kind under this ruling with no new one. Cross-DOCUMENT transfer, between two independently-emitted trees, is a different question about identity and trust and gets none of this. |
FileDrop / PasteUpload (drag-a-file-onto-the-page; paste-an-image) | Admitted 2026-09-03 (Phase 1115) — §2.1 FIELD pair on FileUploadSpec | The cluster's second admission, and it reduces under the governing sentence without amendment: the wire names a CAPABILITY on the node that hosts the gesture and consumes its effect, so dropTarget and acceptPaste are two booleans on the control that already owns file selection, and the drag, the drop, the paste, the visible drop state and the drag image are named nowhere. Tier — FIELD, and the alternatives fail for different reasons. Not a KIND: a drop target has no independent existence, nothing to lay out, and no content of its own — it is a property OF an upload, which is the field tier's definition. Not a new Action case and not a new event name: the effect is the one onSelect already consumes, and minting Action.ReceiveDrop beside it would be a second write path for one concept — the DragReorder row's "the Phase 863 field reused, never a second write path", at a different slot. Not ExtraAttributes: those are wire-omitted, so no decoded tree on any host could ever carry the declaration. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning; this phase adds no new override. The gap is invisible to both reactive channels in the way that mandate exists for: constrained decoding suppresses the invented-$type class, and "rendered a plain picker" is valid-but-less-specific rather than an error, so a census records a clean pass on a document whose upload cannot accept the gesture every comparable product accepts. §1.2 irreducibility. No composition reaches it. A Custom node could host a drop zone, but at the cost of the whole selection contract — Accept, Multiple, onSelect, the picker, the a11y story — reimplemented outside the vocabulary, which is the escape this appendix's Custom ledger exists to count rather than to endorse. §1.3 cost — paid in full, and the field tier's disadvantage is named as §2.1 requires: a field addition SKIPS §11.2 vocabulary attestation, because manifest.kinds enumerates CASES, so a host that has never met dropTarget is named by no manifest and only the fixtures catch it. What it does cost: the IDL, the generated layer, the policy decoder, the schema, five corpus artefacts, both renderers plus a new client control, the reference stylesheet and the fingerprint restamp it forces, one validator code (FUARAN121), one render-obligation claim (picker-always-present), and one authoring-surface decision per veneer. §3.2 confusion delta — structurally ZERO, the tier's one free axis and part of why it was chosen: no case is added at any level, so no pair enters the confusion matrix and none changes. The accessibility line is the ruling's real content. A gesture that is the ONLY route to a capability is a pointer-only control, so the admission is explicitly of an ADDITIONAL route: the picker and its label are emitted whatever is declared, there is no keyboard equivalent of a drag and none is invented, and the obligation is stated in the render-fidelity manifest rather than in prose so a host that quietly replaced the picker is reported. See WIRE_FORMAT.md §3.6.10. |
MediaCapture / CameraUpload (take a photo, record a voice note) | Admitted 2026-09-04 (Phase 1116) — §2.1 FIELD on FileUploadSpec, plus one closed two-case enum | The cluster's fourth admission, and the one that has to be read beside a DECLINE rather than beside its siblings: the ScreenCapture / CameraInput row in the Interaction cluster rules display and camera capture Host chrome on trust grounds, and this row does not overturn it. The two rows partition one word. What that row governs is the GRANT — a standing stream a decoded tree could start. What this row admits is the HTML capture attribute: a request that rides the file picker the control already has, is mediated by the picker permission the reader already gives per gesture, returns one file, and grants nothing that outlives it. getUserMedia, a live preview, a recording surface and screen capture are all on the far side of that line and stay there — and the boundary is the same one that row already drew for paste, in the same terms: the listener fires only on the reader's own gesture and carries only what the reader chose. Admitting the picker-mediated half is what makes the declined half legible, because until now the word covered both. Tier — FIELD, and the alternatives fail as they did one row up. Not a KIND: a capture device has no independent existence and nothing to lay out — it is a property OF an upload. Not an Action case: the effect is the one onSelect already consumes, and a second write path for one concept is what the DragReorder row forbids by name. Not a boolean: the two devices are not one capability with a flag, and an enum makes a later source an ADDITION to a closed set rather than the replacement of a flag that could only ever have meant one of them (the TrendPolarity precedent). §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning; this phase adds no new override. The gap is invisible to both reactive channels in exactly that mandate's way: constrained decoding suppresses the invented-member class, and "rendered a file picker" is valid-but-less-specific rather than an error, so a census records a clean pass on a document that asks a reader on a handset to find a photograph in a file browser. Phase 1076 shipped the vocabulary to PLAY video and audio and nothing that could produce either; this is the other half. §1.2 irreducibility. No composition reaches it. Accept alone cannot: accept="image/*" filters what a picker will show and never opens a camera, which is precisely why the two members are one statement rather than one member doing both jobs. A Custom node could host a capture control at the cost of the whole selection contract reimplemented outside the vocabulary — the escape this appendix's Custom ledger counts rather than endorses. §1.3 cost — a field's bill plus an enum's, paid in full. The IDL and the generated layer, the policy decoder with its own reject vector, the JSON Schema, the corpus, UpdateProp, both renderers, one validator code (FUARAN134) with go-red twins, and one authoring-surface decision per veneer. What it does NOT take is §11.2 vocabulary attestation: manifest.kinds and manifest.formFieldKinds enumerate CASES, and a bare enum in a named slot is neither — so a host that has never met capture is named by no manifest and only the fixtures catch it, which is the field tier's standing disadvantage stated as §2.1 requires. It also adds NO render-fidelity obligation, on the Phase 1130 precedent: a new claim puts every roster host into "unchecked", which is a separate deliberate act. §3.2 confusion delta — structurally ZERO. No case is added at any level — a bare enum in a slot enters no $type vocabulary — so no pair enters the confusion matrix and none changes. The ruling's real content is that the projection REPORTS rather than repairs. The device is what the document knows; whether accept can select it is a fact about the two members together, and no renderer synthesises the missing half. A synthesised accept would put a filter in the document's mouth that nobody wrote and would silently fix the one case most worth reporting — an empty accept, which admits the device without choosing it. One rule, held where the pair becomes visible, a coercion nowhere: the Phase 1130 posture at a different slot. Reopen conditions — two, and neither is an inconvenience. A recorded need for a THIRD picker-mediated source the HTML attribute can actually express (there is none today; a screen is not one). And a recorded need for a declared camera FACING, which this ruling deliberately does not mint: the enum names the device, the projection picks the conforming keyword, and a facing case would be a second thing the enum names that is meaningful for only one of its cases. See WIRE_FORMAT.md §3.6.18. |
LargeBinaryUpload / StreamToStorage (upload the video, attach the archive) | Admitted 2026-09-04 (Phase 1117) — §2.1 FIELD on FileUploadSpec, plus one portability seam | The cluster's fifth admission and the first about EGRESS rather than ingress: the three before it name routes by which a file reaches the control, and this one names where the file goes afterwards. The row exists because the alternative was already in the language and was wrong. Action.ReadFileBody reads a whole body into a string and hands it to the message loop; under Base64 or DataUrl it is a third larger than the file, and on a host that persists its authoring channel it lands in a hash-chained record that replays forever. A ten-megabyte video becomes a fourteen-megabyte string in a durable log. That is not a gap the vocabulary can close by adding an encoding — the op stream must carry a REFERENCE, and a reference needs somewhere for the bytes to have gone. Tier — FIELD, and the alternatives fail in a new way here. Not a KIND: a destination has no independent existence and nothing to lay out — it is a property OF an upload, the field tier's definition, exactly as the three rows above. Not an Action case, and this is the one that had to be argued rather than cited: an Action.Upload would be a second write path for the effect onSelect already consumes, which the DragReorder row forbids by name, and it would put the transfer on the dispatch path where its progress and its refusals have nowhere to go. Not a URL in any spelling: a wire document comes from an arbitrary emitter, and an address here would let that emitter choose where a reader's file goes — which is why the member is a NAME the host registered, resolved against the host's own sink with no fallback of any kind. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning, plus one signal the other rows in this cluster did not have: the gap was created by a shipped phase. Phase 1116 gave the control a camera and a recorder, which produce exactly the files the only existing body path cannot carry; this row is the other half of that one, and the mandate's "absent with no ruling" class is met by the fact that nothing in the language said which path a large file should take. §1.2 irreducibility. No composition reaches it. Action.Call with a handler could POST a body the author read themselves — at the cost of the body having gone through ReadFileBody first, which is the thing being avoided, so the composition that looks like a workaround is a longer spelling of the defect. §1.3 cost — a field's bill plus a SEAM's, and the seam is the larger half. The IDL and the generated layer, the policy decoder with its own reject vector, the JSON Schema, the corpus, UpdateProp, one new ActionDescriptor case (so a host refuses an upload through the gate it already refuses a call with), a new portability interface with its default-deny registry and in-memory reference implementation, the client transfer with its status line, one reference-stylesheet family and the fingerprint restamp it forces, a server-driven boundary refusal, and one authoring-surface decision per veneer. What it does NOT take is §11.2 vocabulary attestation, for the third time in this cluster and the same reason: manifest.kinds enumerates CASES, and a bare string in a named slot is not one — the field tier's standing disadvantage, stated as §2.1 requires. §3.2 confusion delta — structurally ZERO. No case is added at any level, so no pair enters the confusion matrix. The ruling's real content is that a NAME is not a weaker URL, it is a different thing. A URL with a host-side allowlist over it would look equivalent and is not: the allowlist would have to parse an attacker-chosen string, and every such check is a question about what a URL means rather than about what the host offers. A name is checked by set membership against what the host registered, which is a question with an answer. That is also why there is no fallback at the resolution step — a name tried as a path or against a default destination is a URL again, wearing a shorter spelling. Reopen conditions — two. A recorded need for a DOWNLOAD counterpart (fetching a reference back), which this row deliberately does not mint: the reference carries no address, and giving it one would put the destination back on a path a decoded tree can reach. And a recorded need for resumability or chunk control ON THE WIRE, which is likewise declined here — those are transport facts the host's sink owns, and a framing declared on the wire is one every host inherits and none can change. See WIRE_FORMAT.md §3.6.20. |
ReadClipboard / PasteAction (a tree that reads the clipboard) | Declined 2026-09-03 — Host chrome, and a trust boundary | Not a kind, not a field, not an Action case, and this one is a decline on GROUNDS rather than on tier. The clipboard is a channel the reader fills for their own purposes — it routinely holds a password, a one-time code, an address copied for somewhere else entirely — so a document that could sample it at a moment of its own choosing has taken all of that without asking, which is the KeyboardShortcut row's keylogger reasoning arriving at the same answer by a shorter route. The boundary is the GESTURE, and it is already the right one: paste is user-initiated by construction — the reader chooses the instant and the target — so a paste that reaches a tree carries consent that no wire member could manufacture. Structured paste into an editable grid (Phase 1126) sits inside that boundary and reaches no vocabulary at all: a grid declaring editable plus an edit destination has already said its cells are the reader's to change, and whether they change one by typing or twenty by pasting a block is a property of the affordance under the governing sentence — the same reduction FileDrop took one row down. A pasteable flag beside exportable would read symmetric and is not: exportable had to be declared because taking a grid away as a file is a capability nothing else implies, where writing a cell is already declared, so the flag would be a second spelling of a granted permission. What the phase DID change is the write direction — Action.WriteToClipboard's payload widened string → TextSource, so a reader can copy a value the tree computed — and the asymmetry is the ruling: a tree may hand the reader something, and may not help itself to what is there. Reopen condition: none foreseeable for an unprompted read; a gesture-scoped read beyond the grid case (paste into a form, say) reduces under the governing sentence like every other affordance and needs no row here. |
ChartSelection / Crossfilter | Covered — on a renderer default | Not a kind, not a variant, no wire change at all. Chart-driven selection is the Phase 818 read rule (any read slot may take a Binding, and Binding.Selection reads what a node publishes) plus the Phase 427 write default (a node whose click handler is absent publishes under its own NodeId) extended from DataGrid to Chart. Crossfilter falls out of the same mechanism with no coordination vocabulary. The existing ChartSpec.onPointClick closure sits beside it, untouched, and continues not to survive decode. Zoom and brush are NOT covered by this ruling and are separately out: zoom is view state with no cross-node consumer, brush is a range whose value is not a row and which has no demand evidence. |
Undo / Redo | Host chrome | Not an Action case. The op-stream's inverse ops are real and certified, and they invert tree ops — the AI's authoring channel. Every user gesture the language admits writes state, and a state write has no op representation at all, so an Action.Undo would either do nothing or undo an authoring op the user never performed: a fake affordance minted at the vocabulary level, in the one place a mistake cannot be withdrawn cheaply (§4.2). A host that owns a history owns its control. Reopen condition: a durable user-action record exists (Phase 889); only then is "invert a recorded user action" a question with an answer. |
KeyboardShortcut / Hotkey | Host chrome | Not a kind, not a field, not an Action. The language's keyboard posture is already complete and deliberate: widget-local WAI-ARIA interaction, renderer-owned, named nowhere on the wire (roving tablist focus, radiogroup arrow cycling, grid key handling). A wire vocabulary would buy a per-host binding table, platform normalisation (⌘ vs Ctrl, key vs code, IME state), a conflict-resolution policy against the host's and the browser's own chords, a discoverability obligation (an undiscoverable shortcut is another fake affordance), and a trust question — a decoded tree from an untrusted emitter capturing document-level keystrokes is a keylogger-shaped capability. Against that, §1.1 evidence is nil: the demand row's own text records that its probe was never authored, so the intent has never been observed firing. Application-global chords belong to the host, which already owns the seams to dispatch into the tree. Reopen condition: a stress-authored cross-family sighting — and even then the first question is whether the demand is widget-local a11y the renderer already owes. |
a data-* write-back slot hint (a per-control attribute naming the store key a control commits to) | Host chrome — assessed 2026-09-02 (Phase 1157) and NOT ADMITTED | The proposal: a rendered editable cell carries an attribute naming the state key it commits to, so a host whose renderer emits inert HTML can wire change → store write generically. Refused, on all four gates. It is also this file's first ruling about render vocabulary rather than wire vocabulary, and that distinction is part of the ruling: §1.3's cost table already names the renderer class vocabulary as a cost centre, but the admission gates are written for things a tree can say, and three of the four therefore read differently. §1.1 — nil, and structurally so. Every admission signal is an emission signal (ledger gap, Custom fallout, an eval "no expressible tree", a shadow-leg sighting), and a render attribute is not authorable — no model can reach for it, so the channel that admits vocabulary cannot see this proposal at all. What exists is one host's recorded need (the Phase 666 abstention), which is a port demand, not an emission demand — the same distinction that refused KeyboardShortcut and NavBar. The affordance census's editable-grid row is sometimes read as the demand and is not: it asked that a decoded grid be able to declare where a commit goes, and editStateKey answered it. §1.2 — fails outright: the destination is already on the wire. A declared editStateKey, else the grid's own State source, is decoded by every conformant host, the abstaining one included — it holds that tree beside the very write path the commit would call. What it lacks is a channel from its own core to its own loader, which renders by setting innerHTML and attaches nothing. That is host-local plumbing with a shipped precedent of exactly this shape: resolved rows could not ride the tree either, and were handed over out of band through the target-neutral C-ABI rather than by growing the markup. A second accessor costs one host one function; the attribute costs every host forever. §1.3 — a permanent public markup contract across every render surface, byte-locked by the SSR parity suite and each host's emitted-vocabulary lock, and a v2 to remove (§4.2). Two costs beyond the table, both specific to publishing a store address: a bare key is not even sufficient — write-back is scope-aware, so a scoped guest's attribute would send a generic wirer to the wrong store, and carrying the scope widens the disclosure again; and a listener following the attribute writes the store directly, crossing neither the tree-state-write gate nor the host-reserved-key guard added precisely to stop a decoded binding becoming a back door around SetState. That is a hatch, and it would owe an escape-hatch entry whose "what is not claimed" paragraph would be its largest part. §3.2 — no kind-confusion delta can be cited (nothing here is authored; the metric measures wrong-kind selection), and the delta that does exist runs the wrong way: markup asserting "this cell commits to key K", emitted by hosts that wire nothing, promises an interaction most hosts do not perform — the fake-affordance class this cluster exists to foreclose. What follows. The abstaining host's recorded posture is upheld as settled, not pending: its FUARAN090 abstention stands. The reference and TypeScript hosts commit through an in-process closure and mint no slot attribute, deliberately; the ruling is recorded beside each renderer's write destination as well as here, per this appendix's own lesson that a ruling living only in a charter document is one the next session re-derives. Hosts that render no bound-grid cells at all are a different question and stay with their own phases. Reopen condition: an inert-HTML host that has exhausted the out-of-band route — shown it insufficient rather than merely unbuilt — or a second, independent host arriving with the same need, which would make this a shared render contract rather than one host's plumbing. |
ScrollTo / JumpToNode (take the reader to a node — a back-to-top control, an anchor jump, "scroll the error into view") | Action (reserved) — assessed 2026-09-04, NOT ADMITTED | The one piece of the reading-position family (Navigation cluster, ScrollProgress row) that carries a genuine AUTHOR-INTENT claim: "take the reader here" is something a document can mean, in the way Navigate is, and node-addressed intents have precedent — ModalSpec.anchor (Phase 1119) and Binding.Selection both name a node id and let the host resolve it. So this is reserved on Kind's terms rather than declined on grounds: the tier is right and the evidence is absent. §1.1 is nil — the same state the KeyboardShortcut row records, the intent never observed firing. §1.2 is only half-met. Action.Navigate already carries a route through the host's router substrate behind the egress gate, and a fragment route is the host's to interpret as an in-page jump; what it cannot say is a NODE rather than a route, which is the residue a case would buy. §1.3 is the full wire cost — Action<'Msg> is a $type-dispatched wire DU, so a case is codec + schema + corpus + every conformant host + the C# and VB authoring surfaces + the dispatch-gate descriptor, all in one change-set. And the semantics are contested in a way a wire member would have to settle. The public playground's own navigator refuses the browser primitive — "deliberately NOT scrollIntoView: that scrolls every scrollable ancestor including the viewport" — and adjusts only the nearest scrollable ancestor below the document, because the obvious semantics yanked the page. Which scroller, which alignment, whether the motion is animated, and what happens when the target sits inside a collapsed Disclosure are four decisions a case would fix for every host at once, on the strength of one host's layout. Reopen condition: a §1.1 sighting — an emission reaching for a scroll-to-node action, or a Custom renderer recurring for one — and even then the first question is whether Navigate with a fragment route already reaches it on the consumer's host. Reduced-motion applies when it lands: an animated jump is a transition, and the 1122 trio's third obligation governs it. |
Form-constraint cluster (added 2026-08-21 — the form-validation charter, Phase 864)
The cluster the taxonomy lacked on the input side: how a form says what it will ACCEPT. Its
governing ruling is one sentence — a FormFieldKind case names a CONTROL, a rule names an ACCEPTED
SET, and the renderer's choice of <input type="email"> is that set's HTML projection rather than a
second place the wire says the same thing. One row is admitted as a §2.1 field addition, three
are redirected to it, and one is out. The point of recording the redirects is that each was
independently proposed as a variant, and the reasoning that declined them is not otherwise written
down anywhere a reader would find it.
| Reserved name | Disposition | Ruling |
|---|---|---|
FieldRule / per-field constraint | Field (shipped 2026-08-21) | Admitted as FormField.rule, an optional non-discriminated record carrying format / pattern / minLength / maxLength / compare / message — a §2.1 field addition, no new case in any discriminator family. §1.1 demand evidence: one stress-authored task's constraint criteria, ×20 task-qualified sightings across two of them plus ten on a third, cross-family and cross-provider. Verbatim: "email format rule only appears in help text … no format/pattern constraint" (×10); "no cross-field constraint referencing hireStartDate is declared anywhere in the form" — ten straight NOs, the strongest single-criterion evidence in the set (×10); "email uses generic $type:Text with no email-specific kind or format attribute" (×10, on emissions that DID reach RangedNumber for a numeric bound and Date for the dates, so the residue is narrower than "no constraint vocabulary"). Every one of the twenty restated the rule as help text, which is the failure mode the admission has to beat rather than merely improve on. §1.2 irreducibility: required was the entire constraint vocabulary, so a format, a pattern, a length bound and a cross-field predicate had exactly one expressible home — prose a host cannot act on. Not a composition (Switch conditions a subtree; it does not constrain a value); not a role; not a fragment. Reuse was checked first and bounded the scope: RangedNumber already carries min/max and Date already carries min/max, so the rule slot mints no numeric or temporal bound of its own — the residue was format, pattern, length and the cross-field operand. Why a field and not a variant (the charter's headline departure): a FormattedText case beside Text manufactures one more instance of the exact error class §1's preamble names — valid-but-wrong-kind selection — and the worst-behaved instance, because Text for an email field is not wrong, only less specific, so nothing can call it. A field adds no choice to get wrong. It also matches how the demand actually arrives: the models already emit {"$type":"Text"}, so a key added beside help is the gesture they already perform, where substituting a discriminator requires knowing the alternative exists before the emission begins. §1.3 carrying cost, in full: renderer × every host tier (the constraint attributes and the submit refusal); no new fuaran-* class vocabulary (invalid-field marking rides the shipped field-error hooks); accessibility curation is required and is not defaulted (aria-invalid / aria-describedby wiring to the rule's own message); the §11 wire tax in full — IDL, generated codec, policy decoder, schema, corpus, every codec host in the roster; a three-code validator family (FUARAN099/100/101) in every host that carries one; eval + pack coverage so a model learns to reach for it; and the working-memory tax, which is the one line this row can honestly claim to have minimised rather than paid. It also gives up §11.2 vocabulary attestation — a manifest enumerates cases, not fields, so a host that never adopts rule is named by no manifest and only the fixture catches it. That loss is the strongest argument for the variant spelling and it was accepted deliberately: attestation catches a host lagging on adoption, a coordination problem with an owner, whereas the confusion tax is paid by every emission forever. §3 confusion delta: structurally zero against the existing nine FormFieldKind cases — the change adds no case, so there is no new pair for the metric to score and the pre/post baseline is unchanged by construction. That is a stronger claim than a small measured delta, and it is the reason the spelling was chosen. The residual risk is on a different axis and is measured elsewhere: whether an author reaches for rule or falls back to help prose, read as the flip measure on the three criteria above at the pack-teaching sweep. |
EmailField / UrlField / TelField | Field (redirected) | Not three cases, and not one case with a nested enum either. The Media row above already pre-ruled the three-siblings shape ("reserve one … with a variant DU … not two kinds"); the single-case spelling then failed the confusion test in the row above. Both land as rule.format, a bare-string enum of email / url / tel. password, search, number and color are HTML input types with no §1.1 evidence and are reserved, not admitted — number in particular would collide with RangedNumber and re-open the reuse rule. |
PatternField / RegexField | Field (redirected) | rule.pattern, an ECMA-262 source implicitly anchored to the whole value — the HTML pattern semantics exactly, so the browser, the static projection and every host agree without a second definition. Deliberately not carried on a format case: an email field with an additional corporate-domain pattern is real, so a pattern has to reach any control, which means the record. |
CrossFieldRule / FormPredicate | Field (redirected — and mostly a reuse) | Not a new construct. A rule's comparison operand is a read slot, and the reactive-derivation charter's one rule (any read slot may take a Binding) already says what a read slot may hold; the auto-bind rule already puts every form field's value in State under the field's own id. So a cross-field predicate is an ordinary per-field rule whose operand is {"$type":"State","key":"<sibling field id>"}, with no coordination vocabulary at all. Six operators, one operand, no boolean combinators, no arithmetic, no expression language — the standing rejection is unchanged by the slot being a predicate rather than a value. Rejected sibling spelling: letting Date.min / RangedNumber.max accept a Binding, which reaches only controls that already have bounds (so "confirm password equals password" stays inexpressible), conflates a selectable range with an accepted set, and re-types five slots where one suffices. |
FieldError / a validation-state slot on the wire | Host chrome | Not a field. An error is a state, not a declaration: an emitter would be authoring the outcome of a validation it has not performed over values it has not seen, and a decoded tree carrying an error message would replay a stale failure on every mount. Surfacing an unmet rule belongs to the host's own form feedback, which already exists on the server-driven path. Reopen condition: none foreseeable — the shape is wrong rather than unevidenced. |
Data-sharing cluster (added 2026-08-26 — the shared-data-source charter, Phase 865)
The cluster the taxonomy lacked on the provenance side: how two sibling nodes read ONE table. Its governing ruling is one sentence — the tree already has one name for host data and one name for tree-scoped data, and sharing is a question about what may DECLARE a value under the second, never about minting a third.
Nothing was admitted at first, and one thing shipped. The only shape that works is a semantics
change to an already-shipped slot rather than an additive case, and on ×2 evidence from one
criterion that was not a call to make; it was deferred, evidence-gated, pending corroboration at the
pack-teaching sweep. What shipped alongside is the defect flag that stands independent of it:
FUARAN105 (Warning), which names the silent zero the old semantics produced — a Transform over a
default-less State source resolved to the empty table and rendered a plausible wrong answer with
nothing red anywhere. A finding about shipped behaviour needs no vocabulary decision, and shipping it
did not anticipate one.
The gate then closed the other way, and that is the record worth keeping. Phase 872's sweep
(epoch b77343e) reproduced the unlinked-copies complaint at stress-001/c2 from a THIRD model
family, post-teaching, on a criterion teaching structurally cannot move — three families across two
windows. The operator ruled the charter's own pre-registered reopening condition met on 2026-08-27
and admitted the seeding rule; it shipped as Phase
1075. An evidence gate that only
ever refuses is not a gate, and this row is the first time one in this charter has been reopened by
the evidence it named in advance.
| Reserved name | Disposition | Ruling |
|---|---|---|
SharedSource / named embedded table | Semantics (ADMITTED 2026-08-27 — shipped, fuaran#1075) | Still not a kind, not a variant, not a field, and that refutation is unchanged: the two sighted nodes share no slot type but Binding, so DataSource could never have carried the answer. What was admitted is the seeding rule on Binding.State.defaultValue — a declared default fills its slot for every reader in the tree rather than falling back for its own binding alone — which is a semantics change to a shipped slot and was deferred at ×2 on one criterion. The gate reopened on its own terms: 872's sweep (epoch b77343e) reproduced stress-001/c2 from a third model family post-teaching, on a pack-independent criterion, and the operator ruled the charter's condition met. Shipped with FUARAN106 (conflicting seeds, Error) and FUARAN107 (two inline copies of one table, Warning); FUARAN105 widened to the wording the charter always gave it, since a sibling's declaration now IS a rescuer. Normative in WIRE_FORMAT.md §24.4–§24.6. See the charter §10. |
DataScope / Provide | Composition / rejected | A container that renders nothing, inventing a third tree-scoped namespace beside $state and queryResults. Scoping is not a structural primitive. |
DataSource.Named / tree-first Ref | Rejected — structurally cannot work | DataSource reaches the UI wire only inside Binding.Transform. It cannot name a grid's or a chart's source, so it could not make the sighted pair share anything. The phase file's own anticipated shape, refuted. |
declared total / "a feed larger than you inline" (stress-006/c1) | Host chrome | A number the tree cannot substantiate. A host Query knows its own count; an inline source of twenty rows captioning two hundred is a false claim, and a slot carrying it would make the language complicit rather than fix it. Routed to pack teaching (872). Reopen: a paged grid over a host feed needing a total the host cannot supply. |
Metric-semantics cluster (added 2026-08-26 — the trend-polarity charter, Phase 867)
The cluster the taxonomy lacked on the interpretation side: how a tree says what a number MEANS
as opposed to what it IS. Its governing ruling is one sentence — tone states how a reading
stands and is a fact about the value; polarity states which way the quantity improves and is a fact
about the metric; a host derives neither from the other.
| Reserved name | Disposition | Ruling |
|---|---|---|
trendPolarity / inverse / "down is good" | Field (admitted 2026-08-26) | Not a kind, not a variant. An optional bare-string enum on MetricSpec beside trend/trendFormat, defaulting to HigherIsBetter and omitted at default. tone cannot carry it: the slot already colours the tile, the two statements have different subjects, and — decisively — trend is a Binding resolved by the host, so a static enum cannot be a function of a value the emitter never sees. Admitted on ×7 cross-family SHOULD-level sightings. See the charter. |
| trend sentiment rendering | Renderer-owned — was a live defect, fixed in the same phase | Not vocabulary at all. .fuaran-metric-trend was painted --fuaran-tone-success-fg unconditionally in the reference CSS and its byte-copy, so every trend rendered as an improvement in every host, in both directions. Sign→sentiment is a renderer + CSS change needing no wire slot, and it fixed the default-polarity majority of the sighted demand on its own. A polarity slot without it would have changed nothing observable — which is why the phase shipped it first, as Part A. |
| sign inversion / "emit the trend already flipped" | Rejected | A −7.34% error rate printed as +7.34% is a false statement about the world. Polarity changes how a number reads, never what it says. |
value→tone map on a trend (the TonedPill shape) | Rejected | TonedPill maps a discrete field value through a Map; a continuous trend would need a range predicate, which is an expression language in a slot — refused here on the same grounds 864 refused boolean combinators. It also re-encodes tone where the missing fact is direction. |
Neutral polarity (a quantity with no better direction) | Reserved, not admitted | No §1.1 evidence. Reserved as a third case of the enum so a later admission is a bare-string addition rather than a boolean's replacement — which is the whole reason the slot is an enum and not inverted: bool. |
Host adoption is PARTIAL, and this is the surface the next host sweep reads. Recorded here by
Phase 873; Phase 867's Outcome undertook to leave the note and left only the CSS-defect row above,
which is the row describing a defect that was FIXED — so the standing gap was recorded nowhere. The F#,
TypeScript, Swift and Kotlin surfaces read trendPolarity and project the sentiment with a non-colour
channel. Go, Python and Rust still paint the trend unconditionally and do not read the field, which
means the constant-green defect Part A fixed in the reference tiers is still live in those three: a
falling error rate and a falling revenue both read as improvements. nodes/metric-inverted-polarity.json
exists to gate the port, so each is a mechanical follow-up against a fixture rather than a design
question. Email.fs's email-safe projection is deliberately NOT in that list — it has no class
vocabulary and tones the trend with the TILE's tone, so it never carried the defect.
Reading of the taxonomy: of ~20 reserved candidates, the overwhelming majority resolve to
variant / composition / role / covered – only NavBar/Menu, a single consolidated Media, and a
provisional Tree and Calendar are even reserved as genuine kinds, and each is admission-gated on
§1 demand evidence. That distribution is the charter's thesis made concrete: most vocabulary demand is
not a new kind. The interaction cluster added in 2026-08 sharpens it a second way: of four intents
that each arrived as a filed gap, two resolved to an existing mechanism and two resolved to nothing at
all — most vocabulary demand is not vocabulary. The form-constraint cluster added in 2026-08
sharpens it a third time, and this one cuts inside the redirect: of five intents that each arrived
proposed as a FormFieldKind variant, four resolved to a single optional field on a record and
one resolved to host chrome. Not one earned a case. So — most vocabulary demand that survives the
kind test is not a case either, which is the sentence §2.1 exists to make checkable rather than
lucky.
Platform-baseline cluster (ratified 2026-09-03 — the platform-baseline census)
Four capability classes that carried no row anywhere in Appendix A until this ratification — not admitted, not declined, not reserved. That absence was the 2026-09-03 platform-baseline census's headline finding, and it is a finding about this appendix's coverage rather than about the language: an absent row cannot be cited, reopened or argued against, so a class with no row is unexamined rather than declined, which is how one reaches a promotion-readiness wave without anyone having ruled on it.
The census proposed a disposition for each with its evidence attached; the operator ratified all four as proposed on 2026-09-03, and they are rulings from that date. Two of the four are SPLIT rulings, and in both the split is the substance: the half that belongs to the host and the half that belongs to the document are different questions with different answers, and a row answering only one of them would be read as answering both. Two are declines, which is a complete answer — the Host chrome disposition exists to record exactly that.
Evidence is cross-family throughout and comes from the census's probe sweep, harvested with the kind-list teaching removed; those sightings carry their shadow provenance per §1.1 and are evidence, never dispositions. Where a ruling admits a Field, the tier is settled here and the build is a separate phase, drafted and unnumbered at ratification — so no phase number is cited in these rows.
A fifth row was added the same day and is NOT part of that ratification. The export ruling
below defers one sub-question by name — exporting the rendered artefact rather than the rows —
and the row immediately after it is the charter walk that answers it. It sits there, beside the
half it completes, rather than in the ratification order: the deferral and its answer are one
ruling in two halves, and a reader who finds only one of them has the wrong picture. It carries
its own gates and its own reopen conditions, and it was ruled by walk rather than ratified as
proposed, because the census deliberately made no proposal for it.
| Reserved name | Disposition | Ruling |
|---|---|---|
TextDirection / BidiIsolate (right-to-left and bidirectional text) | SPLIT — Host chrome (layout mirroring) + §2.1 FIELD (per-value isolation) — RATIFIED 2026-09-03 | Layout mirroring is Host chrome, on the Virtualisation reasoning exactly: a mirrored tree and an unmirrored one are identical in every respect a consumer can observe — no tree changes shape because the reader reads right-to-left — so there is nothing for the wire to say, and the host, not the emitter, knows the reader's locale. A direction declared at the root would be a per-emitter guess at a per-reader fact. Per-value bidirectional isolation is NOT reducible to it, and is admitted as a §2.1 FIELD on the text surface — a direction slot on TextSource / SemanticStyle, below the variant line. The mirroring question is about the frame; this one is about a value inside a sentence. RR123456789IL in a right-to-left paragraph reorders under the bidirectional algorithm unless the document says that run is isolated, and the reader then reads the reference number back wrong — a correctness failure, not a styling one. It is also a statement only the document can make: a host is handed a string and cannot know which substring is an opaque identifier rather than prose. Same shape as sortStateKey — a behaviour the host performs, keyed by something only the tree can name. §1.1 — cross-family and unambiguous on the 2026-09-03 platform-baseline census's probe evidence: an isolation token reached for ×6 on one vendor, direction-carrying reaches on both, and neither vendor reached for a right-to-left container kind — both modelled direction as a property of a subtree, which is the field tier's own signature. §1.2 — irreducible: no composition reaches it, because no node's presence changes bidirectional resolution; and ExtraAttributes is wire-omitted, so no decoded tree on any host could carry the declaration by that route either. §1.3 — the field tier's costs, named as §2.1 requires: a field addition SKIPS §11.2 vocabulary attestation, because manifest.kinds enumerates CASES, so a host that has never met the slot is named by no manifest and only the fixtures catch it; what it does buy is the IDL and the generated layer, the corpus vectors, both renderers plus every other conformant host's decoder, and one authoring-surface decision per veneer. §3.2 confusion delta — structurally ZERO: no case is added at any level, so no pair enters the confusion matrix and none changes. Bears on WCAG 1.3.2 (Meaningful Sequence), which is what makes the isolation half an obligation rather than a nicety. Reopen condition (the Host-chrome half): a wire-observable consequence of mirroring — a document whose meaning, rather than its arrangement, differs between the two directions. |
PrintLayout / PageBreak (paged output) | SPLIT — Host chrome (the medium) + §2.1 FIELD (break control); medium-conditional content Covered — RATIFIED 2026-09-03 | The paged medium itself is Host chrome — page size, margins, print stylesheets, running page furniture. The host owns the medium, and a server-driven host has no printer at all: this is the Undo row's fake-affordance argument at a different slot, where vocabulary the majority of hosts cannot honour promises an outcome the document cannot deliver. Break control is admitted as a §2.1 FIELD — do not split this row, keep this block whole, repeat this header. Argued irreducible on its own terms: each of those is a statement about which subtree must stay together, and only the tree knows its own subtrees. A host laying out pages sees boxes; it cannot infer that the totals block is one thing, and no rendering carries that fact back to it. The sortStateKey shape again — a behaviour the host performs, keyed by something only the document can name. Medium-conditional content is Covered — screen-only and print-only branches — and it is recorded rather than left silent because both vendors invented a chrome-grouping vocabulary for it: Switch already selects between branches on a binding, and which medium is in force is a host-supplied binding, not a vocabulary question. §1.1 — cross-family on the census's probe evidence: both vendors independently modelled two renderings of one document, and both invented a rule vocabulary for the boundary behaviour rather than a paged container kind, which separates the two halves of this ruling from the demand side rather than by argument. §1.2 — irreducible for the admitted half only: the medium reduces to host configuration and the conditional content reduces to Switch, and it is precisely the residue — subtree cohesion — that neither of them reaches. §1.3 — the field tier's costs, and its §2.1 disadvantage named: the §11.2 attestation skip above applies unchanged, against the IDL and the generated layer, the corpus vectors, a print stylesheet floor on the renderers, and one authoring-surface decision per veneer. §3.2 confusion delta — structurally ZERO, for the same reason: no case at any level. The Host-chrome half is DISCHARGED, not merely ruled (Phase 1124, 2026-09-03): this row's §1.3 costs named "a print stylesheet floor on the renderers" as an obligation the ruling created, and the reference sheet now carries one — a @media print block of DEFAULTS ordered BEFORE the authored break-control block so a declared member wins at equal specificity, with the ordering, the absence of any break-before default, and the absence of any blanket row-cohesion default pinned by test rather than asserted. That last is this row's own rule protecting itself: a tr { break-inside: avoid } default would make keepRowsTogether change no rendering on any grid, and a shipped member whose declaration does nothing is the fake affordance the ruling's other half exists to foreclose. The floor deliberately declares no @page geometry, which is the Host-chrome ruling applied to the reference host itself rather than only to the wire. RAISING a print — as opposed to the paged rendering — is a separate question and now a separate row: Print / "print this invoice", admitted as a payload-free Action case. Reopen condition (the Host-chrome half): a paged obligation a document can state and a host cannot infer, beyond subtree cohesion — the admitted half is the whole of what has been shown to be one. |
Print / "print this invoice" (RAISING a print, as distinct from the paged rendering) | Admitted 2026-09-03 (Phase 1124) — an Action CASE, payload-free | The effect-axis companion to the row above, and it is a separate row for the reason the Export / ExportRendered pair is two rows: the medium and the act of reaching it are different questions with different answers, and the row above answers only the first. Nothing in this appendix ruled on the act until now, which is exactly the shape the 2026-09-03 census names — a class whose neighbours all carry rows reads as settled. Tier — an Action case, and the alternatives were live rather than nominal. Not a KIND or a FIELD: printing is an effect a control raises, not a thing on the page nor a property of one. Not Covered by Action.Invoke, which is where the two Export rows landed and is the alternative that had to be refuted rather than dismissed: Invoke names a capability the HOST registers and owns, and it is right for an export because the host owns the rasteriser, the format and the file. Printing owns nothing — window.print() takes no arguments, exists on every browser host, and needs no registration — so routing it through Invoke would make every host register an identical capability under a name they would each choose differently, which is a vocabulary question answered by convention instead of by the wire. The near precedent is Action.WriteToClipboard, admitted as a case on the same reasoning: a universal, argument-free browser capability, gated, with a matching ClientEffect. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning; this phase adds no new override. The gap is invisible to both reactive channels in the way that mandate exists for: constrained decoding suppresses the invented-$type sighting class, and "emitted a Button that does nothing" is not an error a census can score, because a Button with an empty Chain is a perfectly valid tree. §1.2 irreducibility. Nothing composes to it. A Link to a print stylesheet is not printing; a Custom node reaching window.print() is the escape this appendix's ledger exists to COUNT rather than endorse, and it would put a browser API behind an opaque body the dispatch gate cannot see — the opposite of what admitting it buys. And the fact it carries is one no host can infer: a host can offer print in its own chrome, but only the document knows that THIS control means print — the sortStateKey shape again. §1.3 cost — paid in full, and the case tier's full bill. The IDL and the generated layer, the policy decoder (with the strict-member refusal below), the JSON Schema, two corpus vectors, the canonical + lite encoders, both resume paths, the server-driven lowering and its ClientEffect, the dispatch gate's descriptor, the structural-query surface, the wire-survivability table, the C# veneer, the specification's §3.6.14, and every host in the §11.0 roster. Unlike a field it DOES cost §11.2 attestation — Action is a $type family the vocabulary enumerations cover. §3.2 confusion delta — ONE new pair, and it is confusion-NEGATIVE on the axis that matters. Print ↔ Invoke, which is the pair every effect-axis row has to answer; the discriminator is sharp and stateable in one line, recorded here so a rubric can measure it and Phase 1127's pack teaching has a sentence: an act every browser can perform with no arguments is a case; an act the host must supply, name and parameterise is an Invoke. It does NOT enter the WriteToClipboard ↔ ReadFileBody cluster the ExportRendered row prices, because that cluster is about content crossing the page boundary — text out, bytes in, pixels out — and Print moves no content anywhere the tree can name or read back. The PAYLOAD-FREE shape is the ruling, not an implementation detail. No page size, margin, orientation, sheet range, copy count or target subtree, and each is declined for the row above's reason rather than for brevity: the medium is host chrome and the parameters are the reader's, set in the dialogue they are looking at. A member beside $type is therefore REFUSED rather than dropped — the only Action arm that is strict about it — because a dropped member leaves an emitter believing it constrained a printing it did not. The Phase 900 hatch assessment: NOT A HATCH, and the checklist line is answered rather than waived. A hatch is a slot through which a decoded tree reaches capability the vocabulary does not describe. This describes its capability exactly and completely. It transfers nothing (no payload in either direction), discloses nothing (the call returns unit; the tree cannot learn whether the reader printed, cancelled, or what they chose — so it is the only member of the gated set that cannot be used to observe the reader at all), is user-visible and user-cancellable by contract (the obligation is to raise the platform's dialogue, never to print silently), and is default-deny gated (ActionDescriptor.Print) so a host rendering untrusted trees refuses an unbidden dialogue through the seam it already has. It is gated DESPITE disclosing nothing, because a modal that steals focus and on some platforms begins a physical act is host-observable however little it says. Reopen conditions. A parameter that is genuinely the DOCUMENT's rather than the host's or the reader's — and the row above already sets that bar: subtree cohesion cleared it, page geometry did not. And a target subtree, should a host ever be shown unable to identify from its own rendering what to print, which is the ExportRendered row's first reopen condition at this slot and fails today for the same reason it fails there: the host drew the page and holds every node of it. |
Confirm / "delete - are you sure?" (asking the reader before acting) | Admitted 2026-09-06 (Phase 1537) — an Action CASE, recursive | The effect-axis sibling of the Print row above, and it is admitted on that row's tier for that row's reason: an act every browser can perform with no host registration is a case, not an Invoke. What distinguishes it from every other case on this union is that it CARRIES TWO ACTIONS, which is the whole of what had to be justified. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning; this phase adds no new override. The gap is invisible to both reactive channels in the way that mandate exists for: an emitter asked for a destructive control emits a valid Button whose onClick deletes, and "emitted no confirmation" is not an error a census can score. §1.2 irreducibility — and the composition that has to be refuted is a real one the language already supports. Delete — are you sure? composes today as Modal.Open + a state key + SetState + a second Button inside the modal carrying the real action. That composition WORKS, which is why the argument has to be made rather than asserted. It fails on three counts. It requires the emitter to INVENT a state key and keep it unique, which is the one thing a constrained emitter is worst at and the class the sortStateKey rows keep naming. It puts the destructive action in a DIFFERENT NODE from the control that means to raise it, so a structural query for "what does this button do" answers opens a modal — where the answer a reviewer, a validator and a rubric all want is deletes the order, after asking. And it makes the QUESTION a rendering rather than a declaration: a host with no modal surface, a static document and a server-driven shim each get a different thing, where every one of them can ask a yes/no. The irreducible fact is the BINDING of a question to the act it guards, and nothing in the composition carries it. §1.3 cost — the case tier's full bill, paid. The IDL and the generated layer, the policy decoder with its depth-one refusal, the JSON Schema (which cannot state that bound — recorded rather than glossed), three corpus vectors plus two reject vectors, the canonical and lite encoders, both resume paths, the server-driven lowering with its new ClientEffect and the answer round trip, the dispatch gate's descriptor, the structural-query surface, the wire-survivability table, the three action-log census fixtures, the C# veneer, the specification’s §3.6.22, and every host in the §11.0 roster. Like Print it DOES cost §11.2 attestation. §3.2 confusion delta — ONE new pair, and it is the pair to watch. Confirm ↔ Modal.Open-plus-a-second-button, which is precisely the composition refuted above; the discriminator is stateable in one line for a pack: a yes/no about an act the tree already names is a Confirm; a surface with content of its own — a form, a preview, more than two answers — is a Modal. It does NOT enter the Print ↔ Invoke cluster: nothing is registered, named or parameterised by a host here. The RECURSION is the ruling, and it is bounded at depth one. A confirm inside a confirm's continuation is refused at decode, because a dialogue that answers a dialogue is a modal stack the reader cannot escape and expresses no intent one question does not. The Phase 900 hatch assessment: it IS a hatch, narrowly, and it is disclosed rather than waived. What crosses: ONE BIT — the reader's answer — from the reader back into the tree's own dispatch, which is one bit more than Print transfers and is why this row answers the checklist where that one could refuse it. What mediates: the dialogue is gated by ActionDescriptor.Confirm (default-deny, per-prompt), and the continuation re-enters the ORDINARY dispatch entry so it meets its OWN gate and its own egress check — a confirm cannot reach an effect a host refuses. What is not claimed: a confirmation is not an authorisation. The answer comes from the client, a hostile client answers yes without asking anyone, and nothing about this case makes an act safe that was not safe before. Reopen conditions — two. A recorded need for a question with more than two answers, which is a Modal and not this; and a recorded need for the PROMPT'S PRESENTATION to be declarable — a title, a button label, a destructive styling — which is declined today for the Print row’s reason exactly: the dialogue belongs to the host and the reader. Neither an inconvenience in wiring a modal, nor a preference for a styled dialogue, reopens it. |
Focus / "put the caret here" (moving keyboard focus) | Admitted 2026-09-06 (Phase 1537) — an Action CASE, one string | Admitted on the Print row's tier and, unusually for this appendix, closing an ASYMMETRY the estate already carried rather than opening a capability: the server-driven tier has shipped ClientEffect.Focus of nodeId since Phase 152 with no Action able to reach it, which Phase 1124's outcome recorded in passing. A capability reachable from the driver and not from a tree is a gap in the language, not a feature of the driver. §1.1 demand evidence — the platform-baseline operator mandate (2026-08-28), on the Media row's reasoning. The gap is invisible to both reactive channels for the usual reason: a form that never moves the caret is a valid tree, and no census scores an absent affordance. The estate's own shipped effect is the stronger evidence here, and it is evidence of a kind this appendix rarely has — a host tier that needed the act badly enough to build it. §1.2 irreducibility. Nothing composes to it. Focus is not state the tree can write: there is no key a SetState could set that any renderer reads as the caret is here, and a Custom node calling .focus() is the escape this appendix's ledger COUNTS rather than endorses. The fact it carries is one no host can infer — a host knows how to move focus and cannot know that THIS control means now put the caret in the search box, the sortStateKey shape again. §1.3 cost — the case tier's bill, at its cheapest. One required string, no optional members, no new enum: the IDL and generated layer, the policy decoder, the schema, one corpus vector, both encoders, both resume paths, the server-driven lowering (which reuses the shipped effect and adds none), the gate descriptor, the structural-query surface, the survivability table, the C# veneer, §3.6.22, and every host in the roster. §11.2 attestation applies as it does to every case. §3.2 confusion delta — ZERO new pairs of consequence. The nearest neighbour is CommitLocal, which also carries a bare node id, and the two are not confusable in either direction: one flushes a buffered value and the other moves a caret, and no emission of one reads as the other. What is deliberately NOT minted, and this is half the ruling. No scroll behaviour, no selection or caret position, no "focus the first invalid field". That last is the one to state plainly because it is the intent people bring: it is this case naming a node the AUTHOR chose, and choosing the first INVALID one is a renderer-owned affordance under the form-validation charter, not an argument here. A tree that could ask the renderer to compute which node to focus would be asking for a selector language. The Phase 900 hatch assessment: NOT A HATCH, and answered rather than waived. It transfers nothing and discloses nothing — .focus() returns unit and reports neither where focus was nor whether it moved, so a tree learns nothing about the reader. It is gated all the same (ActionDescriptor.Focus nodeId, per-node), because moving the reader's caret and scrolling their viewport is host-observable, which is CommitLocal's position exactly. Reopen condition — one. A recorded need to address focus by anything other than a node id the document already contains; a selector, a role or an ordinal would each be a different question with a different answer, and none has been shown. |
Export / DownloadAs (take this away as a file) | Covered — Action.Invoke, no vocabulary needed — RATIFIED 2026-09-03 | Recorded because an absent row is not a decline. Every reach in the census's probe evidence was on the effect axis — an export action, export options, an export button — and neither vendor reached for a kind, so the answer arrives from the demand side rather than being argued to. The position: a host-registered capability id on Action.Invoke is default-deny, its permission is the host's, and its result and its refusal are both typed — so the class needs no vocabulary at all. That is the same reduction that already answers geolocation, wake lock, badging, contact picking and payment, none of which needed a row and none of which gets one; and Action.WriteToClipboard is the shipped proof that handing data out of the page is an effect this language expresses today. Nothing is added and nothing is reserved by this ruling — what changes is that the position is citable, which is the whole reason it is written down. Deferred, and deliberately undecided: exporting the rendered artefact — a chart as a picture — rather than the rows. The data is the tree's; the rendering is the host's. The probe authored to separate the two did separate them, and the residue needs its own charter walk, which it does not get here. Reopen condition: a recorded need Action.Invoke demonstrably cannot carry — a result or refusal shape the typed envelope cannot express — rather than an inconvenience in wiring one. AMENDED 2026-09-03 (Phase 1125), and the amendment is a boundary rather than a retraction: the ruling above holds for every export whose content the HOST produces, and it is not reached by DataGridSpec.exportable, admitted the same day in the Grid-behaviour cluster. That member is not a second spelling of this class. It carries no format, no capability id and no host permission, and what it hands over is not data the host produces but the rows THIS GRID already resolved, sorted and formatted on the reader's own screen — which is the one thing an invoked host capability structurally cannot be handed, because a binding resolves in the renderer and nothing on the wire can name its result. Recorded here rather than only in that row for this appendix's standing lesson: a ruling that lives somewhere the reader of THIS row will not look is a ruling the next session re-derives. |
ExportRendered / SaveAsImage (export the RENDERED artefact — a chart as a picture) | Covered — Action.Invoke with a host-owned capability id; no vocabulary — RULED 2026-09-03 | The answer to the deferral the Export / DownloadAs row above records by name, and it is meant to be read with it: that row rules the data — the rows, the values, the tree's own content — and this one rules the rendering, which is the host's. Same disposition, reached by a different route, and the route is why this is a row rather than a sentence in that one. §1.1 — cross-family on the effect axis, and the size arrives as a CONTROL. The 2026-09-03 platform-baseline census's probe evidence includes one probe authored specifically to force the data/rendering split, which named the size question outright — a picture at a size worth putting on a slide rather than the size it happens to be drawn at on screen — because the size is the only parameter a Field for this class would have to carry. Both vendors reached on the effect axis for the act (a named export tool on one; an export button and export options on the other), and neither reached for a rasterise kind, nor declared an export capability on the chart node. Both then answered the size with a reader-operated control — selects, a radio and a paired-dimension control on one vendor, export options on the other. Nobody declared a size on the chart: a probe built to elicit a Field returned a form. Provenance is shadow throughout, and recurrence is NOT met — one trial per cell. That is stated rather than glossed, because it is the honest shape of the evidence: it would not be admission-grade for anything, and it does not need to be, since a decline carries no demand threshold. Evidence too thin to admit on can be entirely sufficient to decline on. §1.2 — fails, and it fails for the OPPOSITE reason to the row two above. The candidate irreducible fact is the subtree boundary — rasterise THIS chart — which reads like the PrintLayout row's admitted break control: only the tree knows its own subtrees. It is not the same fact. A page break is a statement about a subtree the host cannot observe — a host laying out pages sees boxes and cannot infer that the totals block is one thing. A rasterise target is a subtree the host already holds: the host drew it, has the node, emits that node's identity into its own output, and computed its bounds. There is nothing here the tree knows and the host does not. The second candidate fact, the geometry, is not the tree's either — a slide-sized picture is a fact about where the reader is pasting it, which is the per-reader-fact argument that declined the mirroring half of the TextDirection row, and the demand evidence reaches it independently. No residue. The composition runs the other way: a Button whose action is an Action.Invoke, with the reader-chosen size in an ordinary Form / Select that the invoke arguments read through binding machinery already shipped. §1.3 — the ruling costs nothing, and the Field it declines is priced rather than dismissed. A Field would buy §2.1's full bill — the IDL and the generated layer, corpus vectors, every conformant host's decoder plus the native surfaces, both renderers, one authoring-surface decision per veneer — while SKIPPING §11.2 vocabulary attestation, since manifest.kinds enumerates cases, so a host that never met the slot is named by no manifest and only a fixture catches it. And it would promise what most hosts cannot keep: a server-driven host has no canvas, exactly as it has no printer and no undo history. That is the Undo row's fake-affordance argument and the paged-medium half's, at a third slot. §3.2 confusion delta — structurally ZERO, and the interesting number is the one AVOIDED. No case is added at any level, so no pair enters the confusion matrix. What the ruling prevents is the near-synonym pressure a sibling Action case would create: WriteToClipboard sends out of the page text the tree already holds, ReadFileBody brings in bytes the reader selected — both symmetric about the tree's own content — where a rasterise case would send out pixels the tree never held. A model choosing among three would face put this out of the page spelled two ways with a non-obvious discriminator: the content, or the picture of the content. That is the pressure §3.3's merge reviews exist to clean up retroactively. As an opaque host-owned id behind Invoke it is not a sibling, and the choice stays a clean one — the decline is confusion-negative, not merely neutral. Both routes need nothing, and recording both is the point. A tree MAY ask, through Action.Invoke with a host-owned id — default-deny, the token taken per name so permitting a chart rasterise permits nothing else, and the refusal rendered through the value route's existing loading/error surface rather than swallowed. A host MAY equally offer save this as a picture in its own chrome, saying nothing on the wire. Neither is preferred here and neither costs vocabulary; stating both is what stops a reader concluding the tree must ask. Reopen condition — two, and both are about the tree holding a fact the host cannot recover. First: a host demonstrably unable to identify the subtree to rasterise from its own rendering, which would mean node identity had stopped reaching that output — the §1.2 argument is load-bearing on it and fails without it. Second: recurrence-grade, cross-family sightings of a DECLARED export geometry — a size or format stated on the node rather than picked by the reader — since the whole §1.1 read here rests on the geometry arriving as a control. Neither an inconvenience in wiring the capability, nor a host's preference for a declarative spelling, reopens it. |
ScreenCapture / CameraInput (screen capture and camera input) | Host chrome — RATIFIED 2026-09-03 | Not a kind, not a field, not an Action case, and not a reserved channel string. The KeyboardShortcut trust reasoning, and stronger: a decoded tree from an untrusted emitter that can start a screen capture is a screen-recorder-shaped capability — where a keystroke capture is a keylogger over one document, a display capture reaches everything the reader has open. The camera grant is per-origin and sticky in the way the out-of-page alerting row records: the grant, not the delivery, is the scarce and unrecoverable thing, and the origin is the host's while the emitter is arbitrary. §1.1 evidence exists and is cross-family — capture modes, a region selector, a camera step and a photo-capture field type across both vendors, one of them reaching for the consent step unprompted — and that is demand for a capability, never demand for vocabulary; the ruling is about who may grant it, which no sighting count settles. If it is ever wanted it is Action.Invoke with a host-owned capability id — never a kind, and never a channel string, for the reason the out-of-page alerting row gives by name: a reserved channel string would let a host that permitted one delivery silently permit another, with no gate vocabulary for the difference. One boundary worth keeping visible: paste-an-image is the admitted FileDrop / PasteUpload pair, where the listener fires only on the reader's own gesture and carries only what the reader pasted; take-a-picture-of-the-screen is this row, where one gesture grants a standing stream. A reader who conflates them will conclude this class is covered when it is not. AMENDED 2026-09-04 (Phase 1116), and the amendment is a boundary rather than a retraction: the MediaCapture / CameraUpload row in the Affordance cluster admits FileUploadSpec.capture, the HTML capture attribute, and it is not a partial grant of what this row declines. The line is the one this row already drew for paste, restated for a device: what is declined here is the standing grant — a decoded tree that can start a stream, which for a display reaches everything the reader has open and for a camera is sticky per origin. What is admitted there is a picker-mediated request: it rides the file picker the control already has, the reader performs one gesture, the platform returns one file, and nothing outlives it. getUserMedia, a live preview, a recording surface and screen capture are all on this side of the line and stay here. Recorded on BOTH rows because one word covers the two, and a ruling that lives only where the reader is not looking is one the next session re-derives. Reopen condition: a host that has already registered such a capability and needs a vocabulary affordance the Invoke envelope cannot carry — never a request to make the grant easier to obtain. |
Rows the same sweep bears on — not ruled here (platform-baseline census, 2026-09-03)
Nothing in this subsection is a ruling. These are existing rows the census's evidence bears on without proposing a change, plus the three classes it re-probed as post-ship adoption. They sat outside the 2026-09-03 ratification and stand exactly as they stood before it.
Three existing rows the same sweep bears on, none of which it proposes to change:
SystemNotification/ out-of-page alerting — the 2026-09-02 Host-chrome recommendation is supported, not unsettled. Every capability-bearing reach was for a channel or a permission, never a kind; and the channel shape is the one that ruling refused by name, because a reserved channel string would make a host permitting in-app toasts silently permit OS-level delivery with no gate vocabulary for the difference. What the sweep adds is that the reaches are for the permission ceremony as much as the delivery, which strengthens the scarce-and-unrecoverable-grant argument.Tree/TreeView— the follow-on probe was run, and the row is now RULED; see the Data-display cluster, where it reads ADMITTED as of 2026-09-03. What the census recorded here was that both vendors expressed the hierarchy as recursive domain nodes rather than as a tree widget, which pointed to Composition, and that the unsettled counter-argument was arbitrary depth — with a depth-forcing follow-on named as the cheapest next measurement. That follow-on settled it in two halves and neither half is the one this bullet anticipated. Depth alone produced no container on either vendor, so the depth argument is struck rather than upheld. The SEMANTICS broke the composition instead: a probe naming one keyboard focus,Home/Endand per-row state announcement had both vendors reaching for behaviour the vocabulary cannot carry. The census's own reading was therefore right about the structure and incomplete about the widget, which is worth leaving visible: an emission that models a hierarchy as data says nothing about whether a reader can WALK it.Carousel/Gallery— the standing Composition ruling holds, and now has evidence. The container name does appear, but so does its decomposition from the same emissions: a stage, an arrow pair, a dot row, a position counter. A model reaching for the parts as well as the whole is describing a composition.
And three rows admitted between the census being commissioned and being run — Embed, Tooltip and
the Combobox variant — were re-probed as post-ship adoption evidence rather than as gaps. All
three are reached for under their shipped names by unconstrained models that were never given them,
which is the compounding signal §1.1's recurrence rule predicts and the estate has rarely been able
to measure.
Appendix B – Demand-evidence sweep (recorded 2026-07-07)
This is the recorded output of the demand-evidence sweep the charter mandates before publication: a mine
of the instruments the language already has, to determine whether a deliberate pre-publication
kind-admission batch exists. The verdict feeds two decisions – the batch list below (a follow-up
feature-proposal input) and, because a kind-forcing gap would mean the contract is not settled, the
language-settled gate (LANGUAGE-SETTLED-CHECKLIST.md item (b)/(c)).
Sources swept + verdicts
| Source | What it records | Sweep verdict |
|---|---|---|
Custom-node usage in shipped case studies / demos (the FUARAN054 "couldn't express" ledger) | Patterns a real translation had to drop to NodeKind.Custom to render | No kind-forcing entry. The consolidated multi-app harvest concludes every surfaced gap closed additively – an author-surface helper or a portability seam – and explicitly records no NodeKind.Custom escape was needed across the three structurally-distinct views exercised. |
| Real-app translation exercises (three structurally-distinct consumer views: form-heavy, data-grid/upload/drill-down, selection-driven analysis) | Whether translating a real view forced a new core NodeKind / Spec / Binding / Action case | No kind-forcing gap. All harvested gaps were additive author-surface / portability-seam items (each shipped or recorded); none forced a new core wire case. This is precisely the settle-checklist item (b) signal. |
| Emission-eval failures (canonical-prompt runs classified as "no expressible tree – missing contract" vs low quality) | Prompts the vocabulary cannot satisfy at all | No recorded miss. The kind-confusion metric and the release-gate emission micro-eval are the producing instruments. The confusion metric's first baseline landed 2026-08-16 (below); the release-gate eval's first canonical-set pass has not. No "missing contract case" outcome is on record from either. |
The evidenced pre-launch batch
Result: the evidenced pre-publication kind-admission batch is currently EMPTY. No candidate presently clears the admission checklist's §1.1 demand gate on recorded evidence. Every gap the language's own instruments have surfaced to date closed as a variant, an author-surface convenience, or a portability seam – not as a new kind.
This is a settle-positive result, not a gap: an empty batch means the contract did not have to grow a structural case to express the real views exercised (settle-checklist items (b)/(c) point the same way).
Instrument status (updated 2026-08-16 – the batch verdict is unchanged). One of the two
still-landing instruments has now landed: the kind-confusion metric captured its first live baseline
on 2026-08-16 – 12.5% valid-but-wrong-kind (1 substitution over 8 scored prompts against
Fuaran.UI.Ops 0.26.0, one cohort, one model), with a single substitution pair, Toast → Callout. The
baseline carries a decoder + prompt-set + cohort provenance stamp, so a pre/post delta against it is
attributable rather than merely comparable.
That result is not §1.1 demand evidence, and the distinction is the point. A confusion is a selection error among kinds that all exist – the emission was valid, renderable, and reached for the wrong one of two kinds the language already has. It is evidence about learnability, which is what §3's confusion guard weighs, and it says nothing about expressibility. Only a "no expressible tree – missing contract" outcome feeds the §1.1 gate, and the first baseline produced none. So the batch stays EMPTY, and stays open on the remaining instrument – the release-gate emission eval's first canonical-set pass. If either surfaces a valid-but-unexpressible pattern that clears §1, that candidate feeds a follow-up feature-proposal pass, designed against the named real consumer, shipped with recipe + eval seeds, confusion-checked, and landed before the OSS flip (so it needs no post-1.0 profile bump) and before the IDL codegen's full-breadth migration (so the IDL carries it). The unevidenced tail – the Appendix A reservations without demand – stays post-launch and demand-paced.
Re-run trigger
Re-run this sweep (and update this appendix) when: the confusion metric's first baseline run lands
(fired 2026-08-16 – swept, verdict unchanged, see the instrument-status note above); the
release-gate emission eval completes its first canonical-set pass; or a fourth real-app translation
exercise surfaces a gap. A non-empty batch result promotes to a feature-proposal pass under this
charter's gates.
This appendix's successor is a standing instrument (2026-09-03). The sweep recorded here was a one-off: it ran once, against the sources listed above, and its re-run trigger is a list of events somebody has to notice. The platform-baseline census described in §1.1 generalises it — the same supply-side question (what do external checklists contain that the surface does not name?), versioned, re-runnable, offline, free, owned by the evaluation harness's census stage, and carrying a surface fingerprint so that "is this stale?" is a comparison rather than a judgement. Its own re-run trigger fires on any movement in the expressible surface, a new element family, a WCAG dot release, a new named web capability, or twelve months. Its first run's rows — ratified 2026-09-03 — are at the end of Appendix A. This appendix is not superseded — it records a verdict at a date, and the census diffs against it rather than replacing it.
A confusion-metric re-run is not a sweep trigger on its own. The trigger above is the metric's first baseline – the moment it stopped being an absent instrument. Subsequent runs measure a rate, and a rate cannot promote a candidate into the batch, because §1.1 asks whether a pattern is expressible and the metric only ever reports on patterns that were. A rising rate fires §3.3's merge review instead, which is a different gate with a different remedy (merge two near-synonyms; do not admit a third).
See also
STABILITY.md– version-bump classification of a kind change (this charter governs whether the change is admitted; STABILITY.md governs what version bump it is).WIRE_FORMAT.md§11 – the forward-coupling rule every kind/variant addition obeys.LANGUAGE-SETTLED-CHECKLIST.md– the settle gate whose items (b)/(c) consume the demand-evidence sweep's "no new core case" result.