sheleg-design-skill 1.45.0 → 1.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1170 -4
- package/README.md +6 -4
- package/bin/cli.js +6 -2
- package/cursor/rules/sheleg-design.mdc +5 -1
- package/kits/atrium/src/styles.css +23 -5
- package/kits/babylove/.design-sync/config.json +14 -0
- package/kits/babylove/.design-sync/conventions.md +29 -0
- package/kits/babylove/README.md +16 -0
- package/kits/babylove/package.json +14 -0
- package/kits/babylove/src/Button.md +15 -0
- package/kits/babylove/src/Button.tsx +19 -0
- package/kits/babylove/src/Card.md +17 -0
- package/kits/babylove/src/Card.tsx +23 -0
- package/kits/babylove/src/Chip.md +13 -0
- package/kits/babylove/src/Chip.tsx +16 -0
- package/kits/babylove/src/DataTable.md +10 -0
- package/kits/babylove/src/DataTable.tsx +29 -0
- package/kits/babylove/src/EmptyState.md +16 -0
- package/kits/babylove/src/EmptyState.tsx +18 -0
- package/kits/babylove/src/Heading.md +15 -0
- package/kits/babylove/src/Heading.tsx +13 -0
- package/kits/babylove/src/Rule.md +11 -0
- package/kits/babylove/src/Rule.tsx +10 -0
- package/kits/babylove/src/Stat.md +14 -0
- package/kits/babylove/src/Stat.tsx +19 -0
- package/kits/babylove/src/index.ts +8 -0
- package/kits/babylove/src/styles.css +200 -0
- package/kits/babylove/tsconfig.json +15 -0
- package/kits/blueprint/src/styles.css +17 -8
- package/kits/bulletin/src/styles.css +17 -6
- package/kits/cyclorama/src/styles.css +1 -1
- package/kits/datasheet/src/styles.css +9 -9
- package/kits/field-notes/src/styles.css +16 -9
- package/kits/instrument-console/src/styles.css +18 -1
- package/kits/ledger/src/styles.css +56 -40
- package/kits/manpage/src/styles.css +1 -1
- package/kits/maquette/src/styles.css +9 -2
- package/kits/ora/src/styles.css +22 -7
- package/kits/orchard/src/styles.css +3 -3
- package/kits/outrank/.design-sync/config.json +14 -0
- package/kits/outrank/.design-sync/conventions.md +33 -0
- package/kits/outrank/README.md +16 -0
- package/kits/outrank/package.json +14 -0
- package/kits/outrank/src/Button.md +18 -0
- package/kits/outrank/src/Button.tsx +24 -0
- package/kits/outrank/src/Card.md +16 -0
- package/kits/outrank/src/Card.tsx +23 -0
- package/kits/outrank/src/Chip.md +17 -0
- package/kits/outrank/src/Chip.tsx +19 -0
- package/kits/outrank/src/DataTable.md +15 -0
- package/kits/outrank/src/DataTable.tsx +40 -0
- package/kits/outrank/src/EmptyState.md +15 -0
- package/kits/outrank/src/EmptyState.tsx +18 -0
- package/kits/outrank/src/Heading.md +15 -0
- package/kits/outrank/src/Heading.tsx +13 -0
- package/kits/outrank/src/Rule.md +11 -0
- package/kits/outrank/src/Rule.tsx +10 -0
- package/kits/outrank/src/Stat.md +16 -0
- package/kits/outrank/src/Stat.tsx +19 -0
- package/kits/outrank/src/index.ts +8 -0
- package/kits/outrank/src/styles.css +328 -0
- package/kits/outrank/tsconfig.json +15 -0
- package/kits/paperclip/src/styles.css +17 -17
- package/kits/prism/src/styles.css +4 -3
- package/kits/roster/src/styles.css +1 -1
- package/kits/scoreboard/src/styles.css +24 -11
- package/kits/showroom/src/styles.css +24 -9
- package/package.json +3 -2
- package/plugins/sheleg-design/.claude-plugin/plugin.json +2 -2
- package/plugins/sheleg-design/commands/sheleg-design.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +2 -2
- package/plugins/sheleg-design/skills/sheleg-design/FIGMA_BRIDGE.md +4 -1
- package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +19 -7
- package/plugins/sheleg-design/skills/sheleg-design/SURFACE_COMPOSITION.md +4 -2
- package/plugins/sheleg-design/skills/sheleg-design/styles/STYLE_PACK_TEMPLATE.md +109 -9
- package/plugins/sheleg-design/skills/sheleg-design/styles/almanac.md +5 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/atrium.md +186 -12
- package/plugins/sheleg-design/skills/sheleg-design/styles/awning.md +10 -2
- package/plugins/sheleg-design/skills/sheleg-design/styles/babylove.md +195 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/blueprint.md +4 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/briefing-room.md +6 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/bulletin.md +7 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/cyclorama.md +9 -6
- package/plugins/sheleg-design/skills/sheleg-design/styles/datasheet.md +4 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/daylight.md +5 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/editorial-luxury.md +3 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/field-notes.md +67 -12
- package/plugins/sheleg-design/skills/sheleg-design/styles/instrument-console.md +23 -4
- package/plugins/sheleg-design/skills/sheleg-design/styles/ledger.md +3 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/manpage.md +7 -4
- package/plugins/sheleg-design/skills/sheleg-design/styles/maquette.md +5 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/notation.md +5 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/ora.md +3 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/orchard.md +7 -4
- package/plugins/sheleg-design/skills/sheleg-design/styles/outrank.md +218 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/paperclip.md +4 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/pigeonhole.md +24 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/prism.md +7 -4
- package/plugins/sheleg-design/skills/sheleg-design/styles/proscenium.md +8 -3
- package/plugins/sheleg-design/skills/sheleg-design/styles/roster.md +10 -5
- package/plugins/sheleg-design/skills/sheleg-design/styles/router.md +8 -3
- package/plugins/sheleg-design/skills/sheleg-design/styles/scoreboard.md +29 -11
- package/plugins/sheleg-design/skills/sheleg-design/styles/showroom.md +13 -3
- package/plugins/sheleg-design/skills/sheleg-design/styles/tenor.md +7 -4
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/atrium.css +23 -5
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/babylove.css +114 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/blueprint.css +3 -3
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/bulletin.css +17 -6
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/cyclorama.css +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/datasheet.css +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/field-notes.css +16 -9
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/instrument-console.css +18 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/ledger.css +57 -40
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/manpage.css +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/maquette.css +9 -2
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/ora.css +22 -7
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/orchard.css +3 -3
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/outrank.css +158 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/paperclip.css +17 -17
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/prism.css +4 -3
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/roster.css +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/scoreboard.css +21 -8
- package/plugins/sheleg-design/skills/sheleg-design/styles/tokens/showroom.css +24 -9
- package/plugins/sheleg-design/skills/sheleg-design/styles/vitrine.md +5 -0
- package/plugins/sheleg-design/skills/sheleg-design/styles/workbench.md +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,1172 @@ All notable changes to this project are documented in this file. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
|
|
5
5
|
follow [SemVer](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [1.46.0] - 2026-08-22
|
|
10
|
+
|
|
11
|
+
### `babylove` — the thirty-first pack, and the other end of the same question
|
|
12
|
+
|
|
13
|
+
Measured 2026-08-21 off <https://www.babylovegrowth.ai/en> and its
|
|
14
|
+
`/dashboard/overview` in a logged-in session.
|
|
15
|
+
|
|
16
|
+
**The pair is the argument.** `outrank` and `babylove` are the same product category in the
|
|
17
|
+
same year — SEO and AI-visibility SaaS with a landing and a dashboard. `outrank` answers *how
|
|
18
|
+
much system do you need* with **536 custom properties across two borrowed design systems**.
|
|
19
|
+
This one answers it with **seven**: `--brand` and six steps of it, every grey taken from
|
|
20
|
+
Tailwind v4's defaults, and nothing else declared at all. Neither is wrong, and the library now
|
|
21
|
+
carries both ends priced.
|
|
22
|
+
|
|
23
|
+
Three findings the arithmetic produced:
|
|
24
|
+
|
|
25
|
+
* **The brand orange cannot carry a word in either direction** — `#FA5C12` is 3.18:1 on the
|
|
26
|
+
page and white on it is 3.18:1. The reference knows: its hero CTA is **near-black at
|
|
27
|
+
19.66:1**. The one place it breaks its own rule is the nav button, white on `#F25533` at
|
|
28
|
+
**3.43:1**, in the same viewport as the one it got right.
|
|
29
|
+
* **The rail's section labels are 2.60:1** — Tailwind's gray-400 at 11px, and 2.49:1 on the
|
|
30
|
+
inner card. Corrected to gray-500 `#6A7282`, which is the framework's own next step rather
|
|
31
|
+
than a value from me.
|
|
32
|
+
* **`--brand-dark` is 4.49:1** — one hundredth under AA. Kept because the reference declares
|
|
33
|
+
it, and named as not-the-token-for-text.
|
|
34
|
+
|
|
35
|
+
Its signature is the **disconnected state as a first-class surface**: a card whose source is
|
|
36
|
+
not connected renders at full size with an illustration, a sentence naming what connecting
|
|
37
|
+
buys, and a button — so the dashboard's shape does not change when a source is added, and a new
|
|
38
|
+
account reads as unfinished rather than as broken.
|
|
39
|
+
|
|
40
|
+
### Two gate defects the thirty-first pack exposed
|
|
41
|
+
|
|
42
|
+
**`NUMBER_WORDS` stopped at thirty, and a second copy of it stopped there too.**
|
|
43
|
+
`COUNT_WITHOUT_ITS_NOUN` spelled the same vocabulary out a second time. At thirty-one the first
|
|
44
|
+
turned every count check red — loudly, correctly — and the second **went quiet on exactly the
|
|
45
|
+
sentence it exists for**. The silent one is the dangerous half, and it surfaced only because
|
|
46
|
+
its own plant then reported the validator going red for an unrelated reason. The pattern is
|
|
47
|
+
built from the dictionary now, longest-alternative-first so `twenty` cannot shadow
|
|
48
|
+
`twenty-one`.
|
|
49
|
+
|
|
50
|
+
**Four plants are anchored on counts that every new pack moves.** Adding `outrank` broke two;
|
|
51
|
+
adding `babylove` broke all four. Each pinned the literal a count edit rewrites. All four
|
|
52
|
+
derive their anchor now. The residue — 57 plants not read for the same shape — is B-131.
|
|
53
|
+
|
|
54
|
+
### `outrank` — the thirtieth pack, and the first that is two surfaces
|
|
55
|
+
|
|
56
|
+
Measured 2026-08-21 off <https://www.outrank.so> and its `/dashboard` in a logged-in
|
|
57
|
+
session, with `getComputedStyle` in both themes.
|
|
58
|
+
|
|
59
|
+
**Said before anything else: none of this is Outrank's invention.** The marketing site runs
|
|
60
|
+
**Tailwind + daisyUI** — `--rounded-box`, `--btn-text-case` and the `--p`/`--pc` HSL triplets
|
|
61
|
+
are daisyUI's own names — and the product runs **Semrush's Intergalactic**, 384
|
|
62
|
+
`--intergalactic-*` properties on the dashboard. A pack presenting this as a bespoke language
|
|
63
|
+
would be claiming authorship of two published systems.
|
|
64
|
+
|
|
65
|
+
That is the reason to have it. **Every other pack in this library is one surface.** This one is
|
|
66
|
+
the seam between a landing that must convert and a tool that must be worked in for hours, and
|
|
67
|
+
the two halves *disagree* — on the ink, the radius and the type family — while agreeing
|
|
68
|
+
completely on the brand. A team shipping both needs that agreement written down.
|
|
69
|
+
|
|
70
|
+
What the measurement produced, and it is not decoration: **the pack corrects the reference
|
|
71
|
+
three times, and each correction is the smallest the rule permits.**
|
|
72
|
+
|
|
73
|
+
* The eyebrow `#64748B` is **4.37:1 on `--surface`** at the 13px it is set at — below AA on
|
|
74
|
+
the field it appears on most. `#5B6779` is the nearest value clearing 4.5 on both fields.
|
|
75
|
+
* The warning `#FF642D` is **2.95:1**, under even the 3:1 non-text floor. Darkening alone was
|
|
76
|
+
not enough: at `#E5511B` it sits **7.2 OKLab from `--danger`** against a hard floor of 10.0,
|
|
77
|
+
so red and orange are one state to a reader who cannot separate them. `#C2740A` is an amber
|
|
78
|
+
15.2 away — and amber is the conventional warning encoding, so separation and convention
|
|
79
|
+
agree.
|
|
80
|
+
* The brand violet **does not survive the dark theme** at 2.88:1 on the dark card. The twin
|
|
81
|
+
uses the reference's own `--violet-400` `#AB6CFE` at 4.76:1 — taken from its scale, not
|
|
82
|
+
invented.
|
|
83
|
+
|
|
84
|
+
And one the reference gets right that most packs get wrong: `--accent` `#882AFF` carries text
|
|
85
|
+
**in both directions** — 5.49:1 as a word on the page and 5.49:1 under white on the filled
|
|
86
|
+
button. Most brand violets at that saturation clear neither, which is why the value is kept.
|
|
87
|
+
|
|
88
|
+
The kit ships eight components on the `or-` prefix, including a `DataTable` whose header row is
|
|
89
|
+
the eyebrow carrying its own count, and an `EmptyState` that keeps its border — on the
|
|
90
|
+
reference's content calendar an empty day still renders its cell, because the month's shape is
|
|
91
|
+
the information.
|
|
92
|
+
|
|
93
|
+
**Disabled is a token pair here, not an opacity.** WCAG 1.4.3 exempts an inactive control, which
|
|
94
|
+
is why most packs reach for `opacity: 0.38` — the palette gate computes that composite and
|
|
95
|
+
refuses it. `--ink-eyebrow` on `--surface-2` is 4.40:1, so a disabled control still reads as the
|
|
96
|
+
control it is.
|
|
97
|
+
|
|
98
|
+
Registering the thirtieth pack moved 23 files' counted claims from twenty-nine to thirty and
|
|
99
|
+
broke two plants whose anchors were those very numbers — caught by the self-test and
|
|
100
|
+
re-anchored, which is the failure mode this repository already had on record.
|
|
101
|
+
|
|
102
|
+
### Five defects the gate could not see, found by three blind readings (B-125…B-130)
|
|
103
|
+
|
|
104
|
+
`task-pipeline` v1.74.0 closes a node with three independent readings at escalating visibility
|
|
105
|
+
instead of one verdict. Its **first live dispatch** was aimed at this repository's own
|
|
106
|
+
`f88c14b` — the strict reduced-motion gate — and it returned two `fail` verdicts and five
|
|
107
|
+
`breaks` findings against work that had shipped green. What each tier caught is the argument for
|
|
108
|
+
the mechanism, so it is recorded here rather than summarised:
|
|
109
|
+
|
|
110
|
+
* **The gate matched durations by NAME, and the product tier proved it with a plant.** It removed
|
|
111
|
+
`--t-hero-art: 0s;` from `paperclip`'s reduced-motion branch in all three copies, left
|
|
112
|
+
`--t-hero-art: 1.1s` in `:root`, and watched the gate print `0 silent duration(s)` and exit 0.
|
|
113
|
+
Thirteen time-valued tokens across ten layers were outside the walk entirely — `paperclip`'s six
|
|
114
|
+
`--t-*`, seven `--stagger*`, `--marquee-cycle`, `--scan-period` — all answered today by the
|
|
115
|
+
authors' habit rather than by the check. **It matches by value now: 127 durations examined, was
|
|
116
|
+
112**, and that plant is kept so the name-keyed form cannot return.
|
|
117
|
+
* **The ratchet priced the exception rather than the rule.** The seam tier instrumented `check()`
|
|
118
|
+
and collapsed `atrium`'s four kept durations — the remediation the requirement names *first* —
|
|
119
|
+
and measured the count **drop by 4 against a floor of 4026**, turning `npm test` red on the
|
|
120
|
+
stricter answer, where the only way through was lowering a ratchet whose own `_why` says a
|
|
121
|
+
falling count is how a deleted requirement hides. The walk now contributes **exactly one check
|
|
122
|
+
per duration** whatever its answer, so the floor is a property of the corpus.
|
|
123
|
+
* **`docs/DOCMAP.md`'s propagation row still stated the obligation the commit replaced** — one
|
|
124
|
+
collapsed duration, and the superseded check as its proof. **Both blind tiers found this
|
|
125
|
+
independently**, which is the clearest signal the two levels are reading different things. An
|
|
126
|
+
author obeying the row failed the gate; the commit had edited the CHANGELOG, the board, the
|
|
127
|
+
floors, the gate and the CSS, and not the table that tells a contributor what to do.
|
|
128
|
+
* **A published pair was restated and wrong.** *8 of the library's 11 re-declarations* resolved
|
|
129
|
+
against no reading of *re-declaration* the document defined. Measured: **112 at `0s`, 9 at the
|
|
130
|
+
0.01ms idiom, 6 above an instant.** Both figures are computed here now.
|
|
131
|
+
* **B-120's residual scope did not resolve in the repository holding it** — it named
|
|
132
|
+
`test/*_test.js` and *the other three ratchet files*, and there are exactly three ratchet gates,
|
|
133
|
+
all covered, and no `*_test.js` in this tree.
|
|
134
|
+
|
|
135
|
+
Four more findings are filed rather than fixed, because each is a decision rather than a repair:
|
|
136
|
+
the re-declare-with-reason form is enforced by a gate and sanctioned by no document (**B-126**);
|
|
137
|
+
`atrium`'s canvas-removal promise is unverifiable here and reads as covered (**B-127**); `prism`'s
|
|
138
|
+
ambient point cloud has no stated still (**B-128**); the reason window crosses reduce-block
|
|
139
|
+
boundaries (**B-129**); and four competing definitions of *a duration token* now live in one file
|
|
140
|
+
(**B-130**). **B-125** is the one about me: every floor set on 2026-08-20 sits below its true
|
|
141
|
+
count, because the sequence was measure → keep editing → restate.
|
|
142
|
+
|
|
143
|
+
### The pairing check the prose ratios needed had been unreachable since it was written (B-053)
|
|
144
|
+
|
|
145
|
+
Forty-six stated ratios sat in prose that named no partner, and the board carried them as a
|
|
146
|
+
residue to be closed one sentence at a time. **The check they needed already existed.** Twenty-nine
|
|
147
|
+
lines inside `validate_stated_ratios` — a full pairing pass that narrows to the token named on the
|
|
148
|
+
line and refuses a number no pair produces — sat behind a `continue`, unreachable from the day it
|
|
149
|
+
was written, reading to any reader as a live check.
|
|
150
|
+
|
|
151
|
+
The comment above it was honest about the bypass: nine lines fail the pass and eight of those are
|
|
152
|
+
correct writing. So the nine were adjudicated one at a time.
|
|
153
|
+
|
|
154
|
+
* **Three are out of scope by construction** — a comparison against a colour the pack does not
|
|
155
|
+
ship (*"7.53:1 on pure white"*), a bound over a set of surfaces (*"11.8:1 on every tint"*,
|
|
156
|
+
*"1.97:1 on any of the six field stops"*), and a candidate measured in order to be rejected.
|
|
157
|
+
Named in `ARGUED` now, each with its line.
|
|
158
|
+
* **Two were an indented table row misread as prose.** `line.startswith("|")` does not see a
|
|
159
|
+
leading space, so the closable-by-one-header-edit class under-reported and the prose residue
|
|
160
|
+
over-reported by exactly those two.
|
|
161
|
+
* **Four were wrong writing.** The figure and its subject had drifted onto different lines, or the
|
|
162
|
+
token on the line was the *remedy* rather than the subject — and a reader cannot tell either from
|
|
163
|
+
a claim. All four were right about the arithmetic (recomputed) and are reflowed in `manpage` and
|
|
164
|
+
`roster` so the subject travels with the figure.
|
|
165
|
+
|
|
166
|
+
**457 computed + 31 verified by the broad pass, 23 unguarded (4%), 6 prose the pass cannot pair.**
|
|
167
|
+
The printed line reports the broad pass as its own class: counting a claim the run just verified as
|
|
168
|
+
*unguarded* is the same untruth as the number it replaced, in the other direction. Two further
|
|
169
|
+
corrections fell out of that — the total is a sum of buckets, so adding one without adding it to the
|
|
170
|
+
total printed **495 claims where 526 exist**, a report getting quieter as coverage improved. And a
|
|
171
|
+
palette plant had been asserting only that a partnerless prose claim *landed in a bucket*; it now
|
|
172
|
+
asserts that a wrong number is **refused**, which is what the revived pass makes possible.
|
|
173
|
+
|
|
174
|
+
### A carrier phrase was dropped twice, and the second time shipped in seven releases (B-052)
|
|
175
|
+
|
|
176
|
+
`T1` gives an agent five skill descriptions and fourteen tasks and passes only at 0 misses,
|
|
177
|
+
0 false loads. Its own record already carried the lesson: the 1.12.0 run scored 13/14 because
|
|
178
|
+
*"scrubbed sections"* had been dropped to make room for a mobile trigger, and that phrase was
|
|
179
|
+
the only thing in the description carrying **section by section**. It was restored, the set
|
|
180
|
+
re-ran green, and the harness gained the rule that a description edit obliges the full trigger
|
|
181
|
+
set.
|
|
182
|
+
|
|
183
|
+
**The rule did not hold.** `v1.37.5` (`de09f9e`, *"an unqualified landing page reaches both
|
|
184
|
+
crafts"*) removed `scrubbed sections` while the description **grew** from 1009 to 1021 chars —
|
|
185
|
+
additive by every appearance. T1 was not re-run, so the regression shipped in **v1.37.5 through
|
|
186
|
+
v1.45.0**, seven releases. It was found by walking the phrase across every tag, not by reading
|
|
187
|
+
a diff, which is the only reason it was found at all.
|
|
188
|
+
|
|
189
|
+
Restored (1007/1024), and the class is mechanical now: `validate_t1_carriers_survive` pins the
|
|
190
|
+
twelve phrases T1's tasks depend on to the description and fails when one goes, watched failing
|
|
191
|
+
against the v1.37.5 edit itself.
|
|
192
|
+
|
|
193
|
+
**What this does not do is stated where it matters.** A string test cannot prove a phrasing
|
|
194
|
+
routes here rather than to `super-ux` or `copywriting`. The last fresh-context T1 result is
|
|
195
|
+
2026-08-11 and the description has changed six times since, so T1's record now says *do not read
|
|
196
|
+
the green below as current* directly above it. That measurement is what B-052 stays open for.
|
|
197
|
+
|
|
198
|
+
### Every plant names the check it exercises, and one of them never had (B-051)
|
|
199
|
+
|
|
200
|
+
The self-test's fourth tuple element is the string the output must contain — the only thing
|
|
201
|
+
separating *the gate went red* from *the check I wrote went red*. **13 of 60 plants carried
|
|
202
|
+
none.** Running those thirteen one at a time and reading what each actually made fail turned up
|
|
203
|
+
the defect the mechanism exists for: the container plant replaced a bullet's **label**
|
|
204
|
+
(*"Container queries"* → *"Breakpoints"*) while `scoreboard` answers the container question
|
|
205
|
+
twice in the same section, so `CONTAINER_ANSWER` still matched, the check stayed correctly
|
|
206
|
+
green, and the `.cursor` mirror-drift check supplied the red. **It had reported `caught` on
|
|
207
|
+
every run since it was written.** Four more of the thirteen produced a mirror failure ahead of
|
|
208
|
+
their own, which is the same defect one reordering away.
|
|
209
|
+
|
|
210
|
+
All thirteen carry their own check's string now, the container plant removes *every* answer in
|
|
211
|
+
the section rather than one, and `validate_every_plant_names_its_check` makes `expect`
|
|
212
|
+
mandatory — with a plant that strips one, planted into the plant table itself.
|
|
213
|
+
|
|
214
|
+
### The run-stamp count is recomputed, and the retirement window is readable (B-051)
|
|
215
|
+
|
|
216
|
+
The Run stamps preamble said **twenty-six** rows carry no `Diverged?` answer; the table held
|
|
217
|
+
**thirty-four**. In the one document whose subject is what a run may conclude, that is the same
|
|
218
|
+
restated-number defect this gate refuses everywhere else — and it matters because the third
|
|
219
|
+
retirement trigger reads a stamp *count*, so the number decides what may be retired on absences
|
|
220
|
+
of evidence. `validate_reconstructed_stamp_count` recomputes it and fails when the paragraph
|
|
221
|
+
disagrees.
|
|
222
|
+
|
|
223
|
+
A derivation was tried before any of this and failed honestly: `Diverged?` cannot be recovered
|
|
224
|
+
from the Log, because **0 of the 34** rows have their commit named there. So the row's own
|
|
225
|
+
prescription was executed instead — this session's five runs wrote their own stamps. The newest
|
|
226
|
+
five now answer, which **opens the third trigger for the first time in eighteen releases**.
|
|
227
|
+
`validate_retirement_window` prints OPEN or CLOSED, and the trigger's wording now states that an
|
|
228
|
+
instruction may only be retired when all five newest stamps answer.
|
|
229
|
+
|
|
230
|
+
Two bugs in the new checks, both caught by their own output. `\s*$` under `re.M` eats the
|
|
231
|
+
newline, so `finditer` returned **every other row** — 29 of 57 — while the check printed a
|
|
232
|
+
confident number over half a table. And `"five rows"` matched the preamble's own aside (*"a list
|
|
233
|
+
that is short by five rows"*), so the count check read 5 against 34 and failed for the wrong
|
|
234
|
+
reason.
|
|
235
|
+
|
|
236
|
+
### The doctrine's duration bands reach the token layer (B-049)
|
|
237
|
+
|
|
238
|
+
`validate_motion_bands` reads a pack's `## Components`, `## Micro-interactions` and
|
|
239
|
+
`## Motion flavor`, because prose is where a pack prescribes an interaction. It never read
|
|
240
|
+
the token layer, and the board named the hole with its instance: `bulletin`'s
|
|
241
|
+
`--dur-panel: 0.4s; /* the nav dropdown */`, 150 ms past §3's dropdown band and invisible to
|
|
242
|
+
every check here.
|
|
243
|
+
|
|
244
|
+
`validate_token_comments_respect_the_bands` reads a **declaration** whose token name or
|
|
245
|
+
trailing comment names a banded element. **9 subjects across 8 packs, all inside their band.**
|
|
246
|
+
|
|
247
|
+
The 30 durations mentioned inside comment *prose* were extracted and deliberately left out.
|
|
248
|
+
Every one of them describes the reference (*"the reference declares `--t-normal: 250ms`"*),
|
|
249
|
+
quotes the doctrine's own band, or explains a reduced-motion collapse. A check reading those
|
|
250
|
+
flags a pack for correctly reporting what it measured, which is how a gate teaches an agent to
|
|
251
|
+
route around it.
|
|
252
|
+
|
|
253
|
+
**Adjudicating the nine found what the row had not.** The same token was called *the nav
|
|
254
|
+
dropdown* in the CSS and *the nav sheet* in the pack's own Motion prose, and §3 bands dropdowns
|
|
255
|
+
at 150–250 ms and sheets at 200–500 — one word made 0.4s a violation and the other made it
|
|
256
|
+
correct. It is a sheet: full-width, `--dropdown-min-h` 420, a `--surface` plane with a bottom
|
|
257
|
+
`--line`. **The naming moved and the value did not.** 0.4s is a pack choice rather than a
|
|
258
|
+
measurement — the reference's whole budget is an entrance fade, a 0.12s press and a 0.3s hover
|
|
259
|
+
— and changing shipped motion on the strength of a word in a comment is the wrong repair. What
|
|
260
|
+
a pack's two files disagreeing costs any band gate is filed as B-122.
|
|
261
|
+
|
|
262
|
+
The first version of the band table silently judged **eight** subjects where nine exist:
|
|
263
|
+
`\bdropdown\b` does not match *"dropdowns"*, so `awning`'s `--dur-control: 200ms /* DERIVED —
|
|
264
|
+
dropdowns and selects */` escaped it. That value is legal, so nothing was hidden — the ninth
|
|
265
|
+
subject would have been the one to hide an illegal value written in the plural.
|
|
266
|
+
|
|
267
|
+
### Two scoreboard ratios described pairings that never render (B-047)
|
|
268
|
+
|
|
269
|
+
All 30 ratio claims in `scoreboard` were recomputed. **28 agree** to within the repository's
|
|
270
|
+
0.1 tolerance — including the composited focus halo, which measures 1.28 against a stated 1.29.
|
|
271
|
+
The row predicted five disagreements; there were two.
|
|
272
|
+
|
|
273
|
+
A Gotcha warned that the reference's positive delta is unreadable and put the number on the
|
|
274
|
+
wrong field: `#00D492` **on white — 1.84:1**. 1.84 is its ratio against this pack's paper
|
|
275
|
+
`--bg` `#FAF9F5`; on true white it is **1.94**. A reader who checked the figure against white
|
|
276
|
+
got a different number and a document that looked wrong about the very thing it was warning
|
|
277
|
+
them about.
|
|
278
|
+
|
|
279
|
+
The ring table's header measured every row against `--bg`, and the `--ring-focus-sand` row
|
|
280
|
+
confines its token to `--surface-sand` — the second ring exists *because* the first misses the
|
|
281
|
+
floor there, so `--bg` is the one surface it cannot be about. Its stated **15.88** was correct
|
|
282
|
+
arithmetic about a pairing that never renders; on sand the token measures **14.60**. The table
|
|
283
|
+
has a `Measured on` column now.
|
|
284
|
+
|
|
285
|
+
Both were fixed the way the row asked for rather than by editing a digit: each figure names a
|
|
286
|
+
partner the token layer can pair, so the palette gate **computes** them on every run.
|
|
287
|
+
`computed_at_least` moves 454 → 456. An unguarded correct number is one edit from an unguarded
|
|
288
|
+
wrong one.
|
|
289
|
+
|
|
290
|
+
**The broad check was tried and rejected.** 58 rows across 24 packs name a surface other than
|
|
291
|
+
their header's base, and in nearly all of them the row's *subject* is a surface token, measured
|
|
292
|
+
correctly on the declared base. A gate that flags those is a gate an agent learns to route
|
|
293
|
+
around. Only a row that **confines** its token elsewhere is making the mistake, and after the
|
|
294
|
+
fix there are zero of those — so `validate_confined_tokens_measured_where_used` ships as a
|
|
295
|
+
regression guard with a plant, and says `0` out loud rather than implying a measurement. The
|
|
296
|
+
undecidable half is filed as B-121.
|
|
297
|
+
|
|
298
|
+
Two footnotes on the row itself. Its own coverage figure — *150 unguarded at 22 packs* — had
|
|
299
|
+
gone stale during the same day and reads **57 unguarded (11%) at 17 packs** now. And the plant
|
|
300
|
+
for the new check needed correcting: changing only the table row left a 4-cell row under a
|
|
301
|
+
5-column header, which the table-shape guard catches first, so the plant was proving that guard
|
|
302
|
+
worked. The defect as it shipped was internally consistent, and the plant is now too.
|
|
303
|
+
|
|
304
|
+
### Every duration answers the reduced-motion query, and a promise is checked in the kit (B-045)
|
|
305
|
+
|
|
306
|
+
`validate_reduced_motion` was a floor: a token layer had to *have* a branch and the branch
|
|
307
|
+
had to collapse *something*. A layer collapsing one duration of nine passed it.
|
|
308
|
+
`validate_every_duration_answers_reduce` asks the whole question — every duration declared
|
|
309
|
+
outside the branch appears inside it, collapsed to an instant or re-declared at its own value
|
|
310
|
+
**with a reason** — and it verifies the part the board row asked for by name: where the reason
|
|
311
|
+
promises a stop in the component layer, the kit is checked for that stop.
|
|
312
|
+
|
|
313
|
+
Now **29 layers, 0 silent durations, 6 kept with a reason, 2 promising a component-layer stop
|
|
314
|
+
and both keeping it.** `0.01ms` counts as a collapse rather than an exception: it is the
|
|
315
|
+
standard idiom for *instant but still fires `animationend`*, and of the 15 in-branch declarations that are not plain `0s`, **9 are that idiom and 6 sit above an instant**. Both figures are computed from the tree, not restated: the pair this paragraph carried until 2026-08-21 was *8 of 11*, and neither number resolved against any reading of *re-declaration* the document defined — the product tier of the certification measured it
|
|
316
|
+
re-declarations are exactly that.
|
|
317
|
+
|
|
318
|
+
**Two layers were fixed.** `prism` had added `--dur-press: 0.14s` and never updated its branch.
|
|
319
|
+
`atrium`'s four `--flute-dur-*` are kept on purpose — under reduce the refraction canvas is
|
|
320
|
+
*removed* and the still underneath becomes the hero, so a collapsed duration would state the
|
|
321
|
+
opposite of what happens — and that reason now lives in the CSS beside the values instead of
|
|
322
|
+
only in prose a gate cannot read. The row's own measurement was half stale: the uncollapsed
|
|
323
|
+
pair was `atrium` and `prism`, not `roster`, which already used the re-declare-with-reason form
|
|
324
|
+
this check now requires.
|
|
325
|
+
|
|
326
|
+
**The first version of the check reproduced the defect the row exists for.** It returned early
|
|
327
|
+
on any duration that collapsed, so `pigeonhole`'s `--dur-marquee` — which goes to `0.01ms`
|
|
328
|
+
precisely *because* the kit pauses it, since 0.01ms strobes an infinite animation at the reader
|
|
329
|
+
the query protects — never reached the kit verification. Its plant stayed green. The promise is
|
|
330
|
+
a property of the layer, not of one token's verdict, and is checked once per layer now.
|
|
331
|
+
|
|
332
|
+
### A gate constant was shadowed, and Python said nothing (B-120)
|
|
333
|
+
|
|
334
|
+
The check above shipped broken and passing. A new `DUR_DECL` near line 3120 of
|
|
335
|
+
`test/validate.py` was overwritten by an existing module-level constant of the same name at
|
|
336
|
+
3716, whose capture groups mean something else — so the new check parsed every duration with
|
|
337
|
+
the wrong regex and printed **`0 kept`** against a tree where six were measured by hand. It
|
|
338
|
+
reported a number instead of failing, which is the exact shape this repository keeps finding.
|
|
339
|
+
|
|
340
|
+
`validate_gate_has_no_shadowed_names` walks the three gate modules with `ast` and refuses any
|
|
341
|
+
top-level name bound twice. It found one pre-existing duplicate — `CSS_COMMENT`, defined twice
|
|
342
|
+
identically, harmless and now deduped. Its own plant needed two fixes to prove anything: the
|
|
343
|
+
check first read `Path(__file__).parent`, which is the one directory a plant can never reach
|
|
344
|
+
because the self-test runs the real module against a copied tree.
|
|
345
|
+
|
|
346
|
+
### A pack cannot prescribe a token its own layer never defines (B-043)
|
|
347
|
+
|
|
348
|
+
`instrument-console`'s prose named `--dur-press` and its token layer never declared it.
|
|
349
|
+
`var(--dur-press)` is then an undefined custom property — invalid at computed-value time,
|
|
350
|
+
falling back with no error anywhere — which is board B-038's class, described in a comment
|
|
351
|
+
inside the very file that had just acquired a fresh one. The commit that introduced it was
|
|
352
|
+
the one closing B-042, hours earlier.
|
|
353
|
+
|
|
354
|
+
`validate_prescribed_tokens_resolve` refuses it, and the naive form of that check was
|
|
355
|
+
measured before it was written rather than after: **19 of 29 packs quote a token their layer
|
|
356
|
+
does not define**, and nearly all of it is a pack quoting its *reference's* vocabulary —
|
|
357
|
+
`datasheet`'s `--gray-6`, `ora`'s `--font-lora`. What separates a promise from a quotation is
|
|
358
|
+
the prefix family: a pack naming `--dur-press` while defining four other `--dur-*` is speaking
|
|
359
|
+
its own vocabulary. That cut 60 candidates to 11; excluding the `--font-` family on principle
|
|
360
|
+
(a pack layer deliberately does not own font loading) left 3; one is a declared exemption with
|
|
361
|
+
its reason and a line reference. Two were real — the second, `roster`'s `--pattern-grid`, is
|
|
362
|
+
fixed in the prose rather than by inventing a token, because the pattern is composed from the
|
|
363
|
+
five `--pattern-*` values the layer does define.
|
|
364
|
+
|
|
365
|
+
**The plant found a hole in the check itself.** Dropping `--dur-press` from `:root` left the
|
|
366
|
+
reduced-motion block's copy standing, so the token still counted as defined. A declaration
|
|
367
|
+
inside `@media` is not a definition for anyone not asking for reduced motion; `_outside_media`
|
|
368
|
+
now strips those blocks. Zero tokens sit in that position today across 29 layers, which is why
|
|
369
|
+
it was worth closing before the first one does.
|
|
370
|
+
|
|
371
|
+
**`--section` was the library's only raw `vh`.** `clamp(9rem, 24vh, 20rem)` drove section
|
|
372
|
+
padding off the viewport, re-laying out every section when a mobile address bar shows or hides.
|
|
373
|
+
It is `24svh` now, with the reasoning written where the five packs carrying a `--hero-min-h`
|
|
374
|
+
wrote theirs: a hero wants the viewport visible right now and takes `dvh`; padding wants a
|
|
375
|
+
height that does not move.
|
|
376
|
+
|
|
377
|
+
Two of B-043's five claims were false and the row says so. The layer is **4869 bytes, 7th
|
|
378
|
+
smallest of 29** — not 1.4 KB and not the smallest; `briefing-room.css` is smaller. And the
|
|
379
|
+
focus-ring claim misread template rule 5, which governs fallback order for derived colour
|
|
380
|
+
rather than requiring a ring token: **20 of 29 packs ship none.** The type-scale half is real,
|
|
381
|
+
library-wide and refiled as B-119 with its measurement: 6 packs token it, 11 state the sizes in
|
|
382
|
+
prose, 12 do neither.
|
|
383
|
+
|
|
384
|
+
### One site-wide curve and a mandated scrub cannot both be total (B-042)
|
|
385
|
+
|
|
386
|
+
`instrument-console` declared *"the one site-wide curve"* and then mandated scrubbed SVG
|
|
387
|
+
instruments. `MOTION_DOCTRINE.md` §6 settles it — *under `scrub`, easing must be `none`;
|
|
388
|
+
the scrollbar is already the clock* — and easing a scrubbed timeline eases against the
|
|
389
|
+
scroll position twice, which the doctrine itself calls the family's most common motion
|
|
390
|
+
bug. The pack never carved the exception, so an implementer reading it alone landed on
|
|
391
|
+
exactly that bug. **The carve is now written into its Motion tokens**, and
|
|
392
|
+
`validate_scrub_carves_its_exception` refuses a pack that declares one curve, mandates a
|
|
393
|
+
scrub and stays silent. Measuring the shape across the library found **five** such packs;
|
|
394
|
+
the other four are pinned at `scrub_uncarved_at_most: 4` and filed as B-118, because each
|
|
395
|
+
carve has to be written against that pack's own motion section rather than swept.
|
|
396
|
+
|
|
397
|
+
Two more things the row was right about. The pack's `Contract: core` line said the hero,
|
|
398
|
+
its line ceiling and per-component states were *"yours to decide"* while the pack states
|
|
399
|
+
all three — **the declination is narrowed to what it actually leaves open**, because a
|
|
400
|
+
reader who believed the old wording would have decided them twice. And its fastest step,
|
|
401
|
+
`0.18s`, sits outside the doctrine's 100–160 ms button-press band with no deviation clause
|
|
402
|
+
to appeal to; `--dur-press: 0.16s` is the band's own ceiling, derived from the doctrine
|
|
403
|
+
rather than picked, since ADR 0003 forbids widening this pack by invention.
|
|
404
|
+
|
|
405
|
+
The first plant for the new check was reported *caught* with the ratchet lifted past the
|
|
406
|
+
defect — an edit to a pack trips the `.cursor` mirror check too, so the plant had been
|
|
407
|
+
proving the mirror check worked and saying nothing about the check it was written for. It
|
|
408
|
+
carries the required `expect` string now: with the pin at 5 it reports `MISSED`.
|
|
409
|
+
|
|
410
|
+
### The library's contrast coverage was 15 claims smaller than it said (B-013)
|
|
411
|
+
|
|
412
|
+
`stated ratios: … 336 guarded` counted a claim as covered before computing a single
|
|
413
|
+
pair. Fifteen of those 336 had never been through the arithmetic — a claim whose only
|
|
414
|
+
named partner is itself (`--ink` in a table declaring `--ink` as its base), or whose
|
|
415
|
+
subject is a composite the gate cannot resolve to a solid (`ledger.css:33`'s `--muted`).
|
|
416
|
+
**The word `guarded` is gone from the line.** What prints now is what happened:
|
|
417
|
+
`513 claims — 348 computed, 15 named a partner this pack cannot pair, 150 unguarded`,
|
|
418
|
+
and each of the 15 is listed by `file:line` on every run, because a number nobody can
|
|
419
|
+
act on is a number nobody acts on.
|
|
420
|
+
|
|
421
|
+
**The unguarded set is classified instead of lumped.** 177 said nothing about which of
|
|
422
|
+
them a person could close. The split says it: `0 in a table declaring no base, 10
|
|
423
|
+
placing the ratio at a gradient stop, 140 prose`. The 27 that sat in five tables closed
|
|
424
|
+
by declaring the base each table's own numbers already resolve against — `atrium`,
|
|
425
|
+
`router` and `orchard` on `--bg`, `cyclorama` on `--ink` with four cells naming
|
|
426
|
+
`--field-2`, `prism`'s components row on `--accent`. No ratio was changed and no base
|
|
427
|
+
was chosen: each was found by computing which token produces the numbers already
|
|
428
|
+
written. `atrium`'s row also had to spell `--ink-2` and `--ink-3` instead of `-2` / `-3`,
|
|
429
|
+
because a shorthand is not a token reference.
|
|
430
|
+
|
|
431
|
+
**A third guard was written and thrown away, and that is the useful half.** A wrapped
|
|
432
|
+
comment leaves the partner alone with the number (`4.40:1 on --bg-deep.`), so reading
|
|
433
|
+
the subject off the line above looked like free coverage — a probe said it resolved 7 of
|
|
434
|
+
the 15. It did not: the probe accepted a match under *either* comparison mode, so any
|
|
435
|
+
ratio above the claim counted as agreement. Inside the gate it produced two false
|
|
436
|
+
failures at `ledger.css:33`, where the real subject is a composite and the line above
|
|
437
|
+
offers a different token. Substituting a subject is the same act as guessing a partner,
|
|
438
|
+
which is what killed attempts one and two. The reason is recorded where the code would
|
|
439
|
+
have gone.
|
|
440
|
+
|
|
441
|
+
### A pack's own semantic tokens were compared against nothing (B-017)
|
|
442
|
+
|
|
443
|
+
`validate_theme`'s peer set is `STATUS_TOKENS` plus `--accent`/`--primary`/`--cta`, so a
|
|
444
|
+
pack carrying meaning in tokens of its own naming was checked by nothing. `field-notes`
|
|
445
|
+
exists to render provenance, and all four of its provenance tokens were excluded **by
|
|
446
|
+
name**: `--brand-ink` (`[INFERRED]`) and `--witness-ink` (`[AMBIGUOUS]`) are **3.2 apart**
|
|
447
|
+
at full colour and **0.8 under deuteranopia** — the two labels a reader most needs to tell
|
|
448
|
+
apart, on the pack whose reason to exist is provenance. The dark theme is worse than the
|
|
449
|
+
board recorded: `--witness` and `--danger` are **1.2 apart** there, 0.4 under deuteranopia.
|
|
450
|
+
|
|
451
|
+
**The peer group is now whatever the pack declares**, and none of the three forms is new
|
|
452
|
+
syntax — all three already ship:
|
|
453
|
+
|
|
454
|
+
- the state map in a token layer, `[EXTRACTED] → --verify-ink · [INFERRED] → --brand-ink`;
|
|
455
|
+
- a distinctness claim, *"destructive — kept distinct from `--witness`"*;
|
|
456
|
+
- the pack's own disclosure that two tokens share a hue family.
|
|
457
|
+
|
|
458
|
+
What it deliberately does **not** do is infer a set from token names. `--brand` and
|
|
459
|
+
`--brand-ink` are one colour's two jobs; `--witness` and `--danger` are two meanings that
|
|
460
|
+
share a hue. Nothing in the naming separates those cases, so the pack has to say which it
|
|
461
|
+
means.
|
|
462
|
+
|
|
463
|
+
**A disclosure carries its own arithmetic.** *"One hue family, 2.8 apart"* is an honest
|
|
464
|
+
answer only while 2.8 is what the values produce, and `validate_stated_ratios` cannot see
|
|
465
|
+
it — that check reads contrast ratios and this is an OKLab distance. Any distance a
|
|
466
|
+
disclosure states is now computed, bound to the quantity it names: `N apart`, `N at full
|
|
467
|
+
colour`, `N under deuteranopia`.
|
|
468
|
+
|
|
469
|
+
**No hex moved.** `Origin: graphify.com (2026)` makes these values measurements, and a hue
|
|
470
|
+
read off a reference may not be re-stepped. So `field-notes` answers in writing: rust and
|
|
471
|
+
red-orange are one hue family by measurement, the provenance state is carried by the
|
|
472
|
+
bracketed word and its border, and `--danger` is separated from `--witness` by form — a
|
|
473
|
+
filled control, never an inline tag. The sentence *"kept distinct from `--witness`"* was
|
|
474
|
+
false at 2.8 apart and is gone; the Bans now say the conflation they forbid is **in role**,
|
|
475
|
+
and a new ban forbids a provenance state carried by colour alone. `Three semantic hues and
|
|
476
|
+
no fourth` became `three semantic ROLES, and two hue families rather than three`.
|
|
477
|
+
|
|
478
|
+
**Four false-positive shapes were found by measurement and closed, and each is recorded
|
|
479
|
+
where the code is.** A one-tier floor called a pair 19.7 apart at full colour a hard
|
|
480
|
+
failure. A substring test let a line about `--witness-ink` excuse `--witness`. A
|
|
481
|
+
paragraph-wide bind gave every figure in `cyclorama`'s status bullet to every pair — six
|
|
482
|
+
failures against six correct sentences. And two separate regexes cut *"6.4 apart under
|
|
483
|
+
deuteranopia"* in half, comparing a deuteranopia figure with a full-colour distance.
|
|
484
|
+
|
|
485
|
+
### A row that states an instruction instead of a ratio was checked by nothing (B-048)
|
|
486
|
+
|
|
487
|
+
`validate_stated_ratios` reads a number the document states. A Components row often states
|
|
488
|
+
none — it states an **instruction**: *"`--surface-2` fill at 4% ink, `--ink` at 50%"* — and
|
|
489
|
+
nothing composited it. `scoreboard`'s secondary button shipped a **10–12px/600 label at
|
|
490
|
+
3.16:1** that way, below AA, with every gate green over it because there was no number to
|
|
491
|
+
check.
|
|
492
|
+
|
|
493
|
+
**One sub-claim of the board row was itself wrong**, and the measurement says so: the row
|
|
494
|
+
held that 4% ink over the paper *"resolves lighter than the declared `--surface-2`"*. It
|
|
495
|
+
resolves **darker** — luminance 0.873 against 0.941 — which is precisely why it pushes the
|
|
496
|
+
label below AA rather than above it.
|
|
497
|
+
|
|
498
|
+
The fix replaces two ad-hoc composites with tokens the pack already ships (`--surface-2`
|
|
499
|
+
fill, `--ink-soft` label) **and states the ratio it renders**, 4.55:1. The first version of
|
|
500
|
+
the fix did not: correcting the spec moved the pair out of every check, which is the same
|
|
501
|
+
trap as B-017, where a false distinctness claim was the only thing making a pair
|
|
502
|
+
observable. A correction that removes the claim removes the coverage with it.
|
|
503
|
+
|
|
504
|
+
**Three behaviours with no token now have one.** `--t-display-sm` 42px and `--t-display-md`
|
|
505
|
+
44px carry the headline's two unnamed breakpoint steps; `--label-col` 95px,
|
|
506
|
+
`--label-col-narrow` 75px and `--numeral-col-narrow` 70px carry the ledger's geometry, which
|
|
507
|
+
the kit had as three `width:` literals and the pack as prose numbers. Every value is the
|
|
508
|
+
measured one that was already written down — a token names it, it does not choose it.
|
|
509
|
+
|
|
510
|
+
### A pair a Components row prescribes was computed by nobody (B-050)
|
|
511
|
+
|
|
512
|
+
A ratio is checked when the document states one. A Components row usually states none: it
|
|
513
|
+
**prescribes a pair** — *"`--accent` fill, `--on-accent` label"* — and its hover and active
|
|
514
|
+
cells replace one half of it. `contrast(label, fill)` is derivable at every state of every
|
|
515
|
+
component, and nothing derived it. That is how `instrument-console` shipped a pressed label
|
|
516
|
+
at 3.06:1 (B-039): the press dimmed the fill and the label stayed.
|
|
517
|
+
|
|
518
|
+
`validate_prescribed_pairs()` reads the resting cell for a fill and a label, applies each
|
|
519
|
+
state cell's `fill →` / `label →` swaps, and holds the result to the AA floor for the row's
|
|
520
|
+
type size. 13 rows across 7 packs name both halves and carry 8 state swaps between them.
|
|
521
|
+
|
|
522
|
+
**Both defects it found sit in the two rows the widened phrasings added:**
|
|
523
|
+
|
|
524
|
+
- **`maquette`'s leader label is invisible.** The row prescribes `--ink` on a `--model-top`
|
|
525
|
+
fill, and those are the same hex — `#FFF9F4`, **1.00:1** — in the pack whose central rule
|
|
526
|
+
is that type and object are built from one cream. The pack now ships `--on-model`
|
|
527
|
+
(`#151515`, the field's own colour) at 17.49:1, and the token layer says why in the place
|
|
528
|
+
a reader hits it.
|
|
529
|
+
- **`prism` sets a `$` prompt in `--ink-faint`** at **2.31:1**, a token its own layer calls
|
|
530
|
+
*"furniture only, never text"*. It is `--ink-soft` at 4.70:1 now — the token that layer
|
|
531
|
+
says *"MAY carry body copy"*.
|
|
532
|
+
|
|
533
|
+
As the board row predicted, this check could **not** have caught B-039: `instrument-console`
|
|
534
|
+
is a core-contract pack with no Components table, which is also why the defect survived
|
|
535
|
+
there. 16 rows state half a pair and the count prints on every run (B-059).
|
|
536
|
+
|
|
537
|
+
### 88 prose ratios joined the arithmetic, and four of them were wrong to (B-003, B-053)
|
|
538
|
+
|
|
539
|
+
140 stated ratios sat in prose that named no partner, so nothing computed them. They were
|
|
540
|
+
not closed by widening the check — a heuristic that guesses the other side is what this
|
|
541
|
+
file has thrown away three guards for. They were closed by testing **one hypothesis**
|
|
542
|
+
against all 140: *this ratio is against the pack's own field.* **88 agreed with it
|
|
543
|
+
arithmetically**, claim by claim, on 77 lines.
|
|
544
|
+
|
|
545
|
+
Those lines now name `--bg`. Many already said *"on the field"* or *"on the paper"* — the
|
|
546
|
+
same fact, written so a machine cannot read it — and the rest said nothing. Coverage went
|
|
547
|
+
from `353 computed / 150 unguarded` to **`448 / 57`**, 29% unguarded to 11%.
|
|
548
|
+
|
|
549
|
+
**Four of the 77 went red on the first run and every one was a real find**, because giving
|
|
550
|
+
a line a partner puts its *other* claims under the arithmetic too:
|
|
551
|
+
|
|
552
|
+
- `blueprint`'s `3:1` is the non-text **floor**, and the word *floor* had wrapped to the
|
|
553
|
+
next line, so `RATIO_SKIP` could not see it. Reflowed.
|
|
554
|
+
- `proscenium`'s subject was the words *"Body ink"* — no token — and its *"cool act"* is
|
|
555
|
+
`--panel-2`, which the token layer confirms at **16.49:1**. Both named now.
|
|
556
|
+
- `tenor`'s subject sat on the line above its claim, the same shape as the fifteen the
|
|
557
|
+
palette run prints as `unpairable`.
|
|
558
|
+
|
|
559
|
+
The 52 that refused the hypothesis were left alone and stay on the board, because a claim
|
|
560
|
+
whose partner is a panel, a literal the pack does not ship, or a token on the previous line
|
|
561
|
+
needs its own answer, not a rule.
|
|
562
|
+
|
|
563
|
+
### The board's own table hid two `high` rows for ten iterations
|
|
564
|
+
|
|
565
|
+
A row's priority and status are read from the column its header names. An unescaped `|`
|
|
566
|
+
inside a row shifts every cell after it, and three rows here carried a shell pipe inside a
|
|
567
|
+
code span — `sort -u`, `cut -d/ -f2`, `grep … | wc -l`. Their status cells came back holding
|
|
568
|
+
`sort -u`, `` `datasheet` run, stage 0 `` and `medium`, so the queue that orders this
|
|
569
|
+
repository's work never saw **B-004** or **B-014**, the only two `high` rows in the file.
|
|
570
|
+
|
|
571
|
+
The rows were never wrong. The table was, and nothing looked at the table.
|
|
572
|
+
|
|
573
|
+
`validate_board_columns()` now takes each table's width from its own `| id | … |` header and
|
|
574
|
+
refuses any row that does not match, across `backlog.md` and `verification.md`. Planted with
|
|
575
|
+
an unescaped pipe and watched refusing. Floor 3560 → 3619, one check per row.
|
|
576
|
+
|
|
577
|
+
Measured across the whole family in the same pass: **13 such rows in five repositories**,
|
|
578
|
+
including rows whose columns were simply missing or doubled. All are fixed and the count is
|
|
579
|
+
zero.
|
|
580
|
+
|
|
581
|
+
### A tag can no longer publish a release no clone can reach (B-014)
|
|
582
|
+
|
|
583
|
+
A tag pushed without its branch is a release that exists on npm and on GitHub and nowhere a
|
|
584
|
+
`git clone` can find it. Nothing fails loudly, because the release workflow fires on the
|
|
585
|
+
**tag**: the release is created, the package publishes, and `origin/main` still reads the
|
|
586
|
+
previous era. This repository hit it twice — the `v1.4.0` ghost on 2026-08-04, and `v1.18.0`
|
|
587
|
+
on 2026-08-12 with `main` two commits ahead of `origin/main` while the tag was public.
|
|
588
|
+
|
|
589
|
+
The release job now refuses it: `git merge-base --is-ancestor HEAD "origin/$DEFAULT"`,
|
|
590
|
+
against the repository's **actual** default branch read from the API rather than a hardcoded
|
|
591
|
+
`main`, so a fork that renamed its branch is not told its correct tag is unreachable. The tag
|
|
592
|
+
checkout gains `fetch-depth: 0`, because ancestry on a shallow clone is not an answer — it is
|
|
593
|
+
whatever the depth happened to give.
|
|
594
|
+
|
|
595
|
+
Watched refusing against a reproduction of the defect: a commit that was never pushed,
|
|
596
|
+
tagged, and the ancestry test says so. Watched passing on this repository's real HEAD. **All
|
|
597
|
+
eight members carry it**, and the umbrella requires both halves in each, watched failing when
|
|
598
|
+
either was removed.
|
|
599
|
+
|
|
600
|
+
### `atrium` is widened, from the reference rather than from its own tokens (B-004)
|
|
601
|
+
|
|
602
|
+
Three packs were held at `Contract: core` because widening them from the token layer would
|
|
603
|
+
have been *inventing values with a citation attached* — the one thing the pack layer exists
|
|
604
|
+
to prevent. Their references are addressable, so the honest route was always a measurement
|
|
605
|
+
pass, and this is the first of three: `atrium`, read on 2026-08-20 from
|
|
606
|
+
`functionhealth.com`'s own `/_astro/utilities.*.css` and `/_astro/PageSections.*.css`, mapped
|
|
607
|
+
through the reference's **own** token names — `--fill-04` is `--accent`, `--fill-03` is
|
|
608
|
+
`--ink`, `--fill-01` is `--bg`, `--stroke-02` is `--line`.
|
|
609
|
+
|
|
610
|
+
- **`## Components`** is the reference's single `.fh-button` class and its four skins, with
|
|
611
|
+
every state: the pill radius (`999px`, not `--radius-lg`), 48px and 40px heights, the exact
|
|
612
|
+
paddings, and the behaviour that matters — **the press does not dim the accent, it replaces
|
|
613
|
+
it with the ink**, so the label goes from 4.59:1 to 13.47:1 rather than losing contrast.
|
|
614
|
+
Hover and focus-visible share a fill, so the 2px accent-at-35% ring is the only thing that
|
|
615
|
+
separates keyboard from pointer.
|
|
616
|
+
- **`## Hero`** is the numbers of the full-viewport media hero: a 540px text column at the
|
|
617
|
+
bottom of the stage, `--h1` fluid from **57px to 80px** at weight 300 and `line-height: .9`,
|
|
618
|
+
the `blur(15px)` eyebrow pill over 7% white, a stat rail with 58px hairline dividers, and
|
|
619
|
+
what changes below 768px — the stage becomes 666px and the rail leaves the media for a
|
|
620
|
+
`--bg` bar beneath it.
|
|
621
|
+
- **`## Responsive`** is its **184 media queries, counted**: 768px and 1100px carry 140 of
|
|
622
|
+
them. It also records the thing a reader would otherwise get wrong — **type does not step at
|
|
623
|
+
those boundaries, it slides**, `clamp()`-ed between 376px and 1440px, so mixing a fluid
|
|
624
|
+
heading with a font-size media query breaks the rhythm. And it answers the container
|
|
625
|
+
question with a technique rather than a shrug: the reference declares `container-type` in
|
|
626
|
+
exactly one place, a stage whose every dimension is in `em` and whose `font-size` is driven
|
|
627
|
+
by the container width, so one number scales the whole composition. 152 `vw` expressions
|
|
628
|
+
against 2 container-relative ones.
|
|
629
|
+
- **`## Signature element`** names the fluted-glass hero that Signature motifs already
|
|
630
|
+
measured, and says why it is the signature rather than one motif of several.
|
|
631
|
+
|
|
632
|
+
**The pass found a value the reference does not ship.** `--accent-tint` was `#F7EEEB` —
|
|
633
|
+
which appears **nowhere** in the reference's CSS or its HTML — against its
|
|
634
|
+
`--orange-10p: #F6E9DC`. The mapping is not a guess: its neighbour `--green-10p` is `#f1f3e5`,
|
|
635
|
+
this pack's `--good-tint` to the character. Corrected, with the search that found it recorded
|
|
636
|
+
in the token layer.
|
|
637
|
+
|
|
638
|
+
It also found what no section had ever said: **`--accent` is 4.17:1 on `--surface`**, below
|
|
639
|
+
AA. The accent is safe as a label on `--bg` and on `--ink`, and not on the cream card. The
|
|
640
|
+
reference never puts one there; now the pack says so.
|
|
641
|
+
|
|
642
|
+
Contract split **7 core / 22 widened → 6 / 23**. `orchard` (gutgutgoose.com) and
|
|
643
|
+
`editorial-luxury` (prowl.chat) remain, one measurement pass each.
|
|
644
|
+
|
|
645
|
+
### `orchard` was measured and deliberately not widened (B-004, B-110)
|
|
646
|
+
|
|
647
|
+
The second of the three packs held at `Contract: core`. Its reference is addressable, so the
|
|
648
|
+
same measurement pass was run against `gutgutgoose.com` — and it stopped, on purpose, at
|
|
649
|
+
`## Components`.
|
|
650
|
+
|
|
651
|
+
**What the pass established.** All **13** of `orchard`'s token values are present in the
|
|
652
|
+
served document — 10 as hex and 3 as `rgb()` triples, which a hex-only search had first
|
|
653
|
+
reported as missing, so the first answer was wrong and looking harder was the fix rather than
|
|
654
|
+
concluding. Its responsive model is the **opposite of `atrium`'s**: three fixed frames
|
|
655
|
+
(desktop ≥1200, tablet 810–1199.98, phone ≤809.98) with a **390px phone frame** carrying a
|
|
656
|
+
342px column, and the tablet/phone tier changing 61 rules dominated by `width` (32), `order`
|
|
657
|
+
(22), `height` (16) and `padding` (14). **No `clamp()`, no container query, no `vw` at all** —
|
|
658
|
+
variants are swapped, not scaled. The slab radii are 12px (23 uses), 20px (10), 24px (9) and
|
|
659
|
+
40px (4).
|
|
660
|
+
|
|
661
|
+
**Why it was not widened.** `## Components` is per-component states, and a Framer document
|
|
662
|
+
serves none. The contract is all-or-nothing by design — *touch one of the four and you owe
|
|
663
|
+
all four* — so writing three from measurement and the fourth from the token layer would be
|
|
664
|
+
precisely the failure `core` exists to prevent.
|
|
665
|
+
|
|
666
|
+
**And the pass found a contradiction worth more than the widening.** The pack calls its candy
|
|
667
|
+
pill *"the only orange object on the page"* and `--cta` *"the ONE action colour, a fill"*.
|
|
668
|
+
On the served page `#FA7241` occurs **only inside SVG `fill=` attributes** and **never as a
|
|
669
|
+
`background-color`**; what paints controls there is `--surface`, `--bg`, **`--primary`
|
|
670
|
+
`#78934a` (14 declarations)**, its 10% wash, and `--surface-ink`. Three readings fit the
|
|
671
|
+
evidence and it does not choose between them, so the pack was **not** rewritten — filed as
|
|
672
|
+
B-110, to be settled in the same rendered pass that `## Components` needs.
|
|
673
|
+
|
|
674
|
+
### `## Hero` had two readings, one worked example, and no check (B-007)
|
|
675
|
+
|
|
676
|
+
The board said the standalone reading was missing from the library. **It was not** —
|
|
677
|
+
`ledger` had already written it: *"A console's hero is its first viewport after sign-in"*, with
|
|
678
|
+
a kicker, a title at a console size rather than a display size, the answer before the evidence
|
|
679
|
+
that explains it, and an explicit list of what the screen must not carry. What was missing is
|
|
680
|
+
that **the skeleton did not carry it**, so every console-pack author re-derived the heading
|
|
681
|
+
alone, which is exactly what the T7 author did and said.
|
|
682
|
+
|
|
683
|
+
The skeleton now names both readings with a worked example each — `datasheet` for the
|
|
684
|
+
marketing page, `ledger` for the console — and states that both owe the same two things.
|
|
685
|
+
|
|
686
|
+
**And nothing had ever read the answer.** The skeleton has demanded a line ceiling *and the
|
|
687
|
+
container width that keeps it there* since 1.5.0, and no check looked. Across the 23 packs
|
|
688
|
+
that carry a `## Hero` **heading** — never the string, which a `core` pack names in its own
|
|
689
|
+
contract line to decline it — there was exactly one live defect of each kind:
|
|
690
|
+
|
|
691
|
+
- **`atrium` stated no ceiling.** Widened by this same loop one iteration earlier, with every
|
|
692
|
+
measurement in place and the one obligation the skeleton spells out left out. Fixed from the
|
|
693
|
+
reference: **two lines in the 540px column**, and the ceiling is a word budget rather than a
|
|
694
|
+
CSS rule — the reference's own headline is *"Check your health."*, three words.
|
|
695
|
+
- **`showroom` stated a ceiling with nothing holding it.** Fixed from its reference, and the
|
|
696
|
+
answer is the interesting one: **no `max-width` holds it at all**. The headline sits in a
|
|
697
|
+
centred column with side padding only — 24px, 56px, **58px from 1024px** — and
|
|
698
|
+
`text-wrap: balance` at `line-height .95` does the rest, so the ceiling is a **four-word
|
|
699
|
+
budget**. The reference's headline is *"Welcome to agentic revenue."*
|
|
700
|
+
|
|
701
|
+
Two plants, both against `maquette`, chosen by measurement rather than by eye: its hero carries
|
|
702
|
+
exactly one phrase of each kind, so removing one leaves the other standing and each plant
|
|
703
|
+
proves its own half. Floor 3619 → 3667.
|
|
704
|
+
|
|
705
|
+
### Every pack now declares its two real silences, and the gate derives them (B-008)
|
|
706
|
+
|
|
707
|
+
`Contract: core` named four sections and nothing else, so a pack's **other** silences stayed
|
|
708
|
+
undeclared. The case on the board: `workbench` ships four status roles and no severity ramp,
|
|
709
|
+
and a fresh-context agent building an incident list inferred a rank scale from role
|
|
710
|
+
descriptions and discovered the gap by hitting it.
|
|
711
|
+
|
|
712
|
+
The row offered two answers and **both were refused, on evidence**. A free *"also not
|
|
713
|
+
specified here"* list is unbounded and uncheckable — every pack would write a different one
|
|
714
|
+
and nothing could compare them. And four is not the boundary, because two silences are real
|
|
715
|
+
and were counted before anything was chosen:
|
|
716
|
+
|
|
717
|
+
- **Themes: 10 ship a dark twin, 13 ship one block, 6 ship a second block that is a
|
|
718
|
+
*surface* rather than a theme** — `[data-surface="dark"]`, `[data-surface="panel"]`,
|
|
719
|
+
`[data-state="alarm"]`. So *"does this pack have a dark mode?"* had three different answers
|
|
720
|
+
depending on how a reader looked, and `tenor` in particular ships something dark that is
|
|
721
|
+
not a dark theme.
|
|
722
|
+
- **Rank: exactly one pack of twenty-nine ships an ordered ramp** — `tenor`, with
|
|
723
|
+
`--sev-ask` → `--sev-limit` → `--sev-never`. The other twenty-eight were silent.
|
|
724
|
+
|
|
725
|
+
So each pack carries a `Themes:` line and a `Rank:` line, **derived from its own token
|
|
726
|
+
layer**, and `validate_pack_declares_its_silences()` re-derives both on every run and refuses
|
|
727
|
+
a declaration that has drifted from the thing it describes. That is the property a prose list
|
|
728
|
+
could never have: the answer is checkable against the tokens, not merely present.
|
|
729
|
+
|
|
730
|
+
Four plants — each line removed, and each claiming what its own tokens contradict. The
|
|
731
|
+
skeleton carries both questions with the counts and the reason, and says plainly that a third
|
|
732
|
+
question is only added when it can be answered the same way. Floor 3667 → 3783.
|
|
733
|
+
|
|
734
|
+
The library's own counted-claims guard refused the first draft of that skeleton text, which
|
|
735
|
+
said *"11 packs"* where the checkable phrase means the pack total. Reworded, not exempted.
|
|
736
|
+
|
|
737
|
+
### The code graph is current again, and the refresh needed no key (B-009)
|
|
738
|
+
|
|
739
|
+
The row had been re-filed as *staleness alone — a re-run, not a credential question*, at 13
|
|
740
|
+
commits behind. By the time this loop reached it, the same loop had pushed it to **31**.
|
|
741
|
+
|
|
742
|
+
`graphify update .` — the path whose own help says *no LLM needed* — moved `built_at_commit`
|
|
743
|
+
from `de09f9e` to **`856f12c`, which is HEAD**. And it did not do the thing the row's history
|
|
744
|
+
feared, where a code-only pass had once written fewer nodes than the curated graph held:
|
|
745
|
+
**every class grew.** Documents **1797 → 2173**, code **1646 → 2105**, concept 243 → 340,
|
|
746
|
+
rationale 126 → 155. In total **3812 → 4773 nodes** and 4185 → 4473 edges across 920
|
|
747
|
+
communities. The shrink guard never fired because there was nothing to guard against — and
|
|
748
|
+
the curated graph was copied to `graphify-out/2026-08-20/` before the run regardless, because
|
|
749
|
+
that is the order the row itself insists on.
|
|
750
|
+
|
|
751
|
+
**No placeholder labels survived.** 116 communities changed membership and were renamed by
|
|
752
|
+
their hub; a scan of all 4773 nodes finds **zero** carrying a bare `Community N`.
|
|
753
|
+
|
|
754
|
+
**Stage 9's own check passes.** All ten god-nodes resolve to documented seams — the two gates
|
|
755
|
+
and their `check`/`read`/`main`, the pack skeleton, two style packs, the CHANGELOG, and the
|
|
756
|
+
third-largest, `Component Spine`, which is a rationale node from `kits/atrium/src/Button.md`
|
|
757
|
+
named in the CHANGELOG and in two specs.
|
|
758
|
+
|
|
759
|
+
**Scope, stated rather than implied:** `graphify-out/` is gitignored — *machine state, not
|
|
760
|
+
source* — so *current* means current on this machine. A clone still builds its own, and this
|
|
761
|
+
close does not change that.
|
|
762
|
+
|
|
763
|
+
### A count whose noun is only implied, and the one form that can be caught (B-016)
|
|
764
|
+
|
|
765
|
+
`validate_counted_claims` reads *"N packs"*. It cannot read *"the accent role resolves to
|
|
766
|
+
`--accent` in twenty-seven,"* where the noun is implied by context — which is how that one
|
|
767
|
+
sentence sat wrong at ten, then thirteen, then fourteen, corrected by hand each time and
|
|
768
|
+
unguarded again the moment it was.
|
|
769
|
+
|
|
770
|
+
**Three patterns were measured and two thrown away**, which is the useful half:
|
|
771
|
+
|
|
772
|
+
| Pattern | Spans | Verdict |
|
|
773
|
+
|---|---|---|
|
|
774
|
+
| every numeral with no counted noun after it | **2553** | ordinary prose — *three cards*, *one motion methodology* |
|
|
775
|
+
| a numeral **closing its clause** | 62 | nearly all correct ellipsis — *one gate of four*, *wrong two times out of three* |
|
|
776
|
+
| the same, in sentences naming a library noun | 69 | worse: mostly the word *one* |
|
|
777
|
+
| **`the <numeral>` closing a clause** | **7** | precise — a definite article asserts a known set |
|
|
778
|
+
|
|
779
|
+
Of those seven, three are library counts, and **one of the three was wrong**: `tenor.md:328`
|
|
780
|
+
said token names are *"not uniform across the twenty"* while thirty-one packs ship. A live
|
|
781
|
+
wrong number, invisible to every gate, found by narrowing rather than by widening.
|
|
782
|
+
|
|
783
|
+
All seven now name their noun — which hands them to the existing check forever — and
|
|
784
|
+
`validate_a_count_names_its_noun()` refuses the bare form. Both count checks read one shared
|
|
785
|
+
corpus now; it was inline in one of them, and two copies of a source list are two lists that
|
|
786
|
+
drift.
|
|
787
|
+
|
|
788
|
+
**Naming the noun immediately exposed two more defects, which is the point of the exercise.**
|
|
789
|
+
`the nine` became `the nine headings` and the gate refused it at once: the contract is
|
|
790
|
+
thirteen. It reads `nine of the thirteen headings` now. And a *true subset* — the accent role
|
|
791
|
+
resolving in 27 of 29 — is refused when written `twenty-seven packs`, because the check reads
|
|
792
|
+
any `N packs` as a claim about the total. A subset is written **`N of the M packs`**, and that
|
|
793
|
+
rule is now in the pack skeleton with the rest.
|
|
794
|
+
|
|
795
|
+
The skeleton carries this as its **sixth** rule — and writing it corrected the section's own
|
|
796
|
+
heading, which promised four rules over five. Floor 3783 → 3789.
|
|
797
|
+
|
|
798
|
+
### `field-notes` ships thirty dark overrides that no prose carried (B-018)
|
|
799
|
+
|
|
800
|
+
The pack declares `Themes: light + dark — a full theme twin` and its Palette documented only
|
|
801
|
+
the light values. `tokens/field-notes.css:251-291` is now written out as a table, and writing
|
|
802
|
+
it confirmed all three consequences the T24b run reported — plus one it missed:
|
|
803
|
+
|
|
804
|
+
- **`--surface-2` and `--surface-3` are the same value** in dark, `#262019`, so the deepest
|
|
805
|
+
step the light table names does not exist there. **`--ink` and `--ink-2` collapse the same
|
|
806
|
+
way**, which the row did not catch.
|
|
807
|
+
- **No `--dawn-*` stop is re-declared**, so `--hero-dawn` still ends `--dawn-7` `#E9ECDF` at
|
|
808
|
+
98% into `--bg` at 100%. In dark that is near-white meeting `#14110E` across two percent of
|
|
809
|
+
the gradient — the hard edge this pack's own Bans forbid. The Palette now says plainly:
|
|
810
|
+
**do not ship the hero in dark** until that decision is made.
|
|
811
|
+
- **The pack's stated decision and its shipped theme disagree.** Gotchas says the forest
|
|
812
|
+
family was kept over the reference's warm-brown `.dark`; the layer ships `--bg: #14110e`,
|
|
813
|
+
which is that warm-brown. **The reference cannot settle it**: `graphify.com` serves no dark
|
|
814
|
+
theme at all today — no `[data-theme="dark"]`, no `.dark`, no `prefers-color-scheme`, and
|
|
815
|
+
none of the thirty dark values anywhere in the 488 KB it returns. Recorded where a reader
|
|
816
|
+
hits it and filed as B-112, not guessed at.
|
|
817
|
+
|
|
818
|
+
The row's fourth claim was imprecise and is corrected rather than repeated: `SKILL.md` carries
|
|
819
|
+
no list of packs with a mode collection. `FIGMA_BRIDGE.md` uses `workbench` as an *example*,
|
|
820
|
+
and now says so, pointing at the `Themes:` line each pack carries.
|
|
821
|
+
|
|
822
|
+
### A number this run published four hours ago was wrong
|
|
823
|
+
|
|
824
|
+
The theme split shipped into the pack skeleton as **11 / 13 / 5**. The tree says **10 / 13 /
|
|
825
|
+
6**. Two derivations of one number existed in the run that wrote it — a loose regex over the
|
|
826
|
+
raw CSS, which counts any pack merely mentioning `dark`, and the strict one that generated the
|
|
827
|
+
`Themes:` declarations — and the prose quoted the loose one.
|
|
828
|
+
|
|
829
|
+
Corrected in all six places it had reached, and `validate_theme_split_is_derived()` now
|
|
830
|
+
computes the split from the declarations and refuses a document that states another. Planted
|
|
831
|
+
with the exact wrong number that shipped.
|
|
832
|
+
|
|
833
|
+
### Routing: four of five findings held, and correcting one number found two more (B-019)
|
|
834
|
+
|
|
835
|
+
- **The dial precedence rule had no third case.** *"The row that names the surface wins over
|
|
836
|
+
the row that names a mood"* does not arbitrate a row naming **neither** — *"trust-first:
|
|
837
|
+
regulated, public-sector, clinical"* names who reads it, not what they read it on. Both T24
|
|
838
|
+
branches hit exactly that and both split two-to-one on MOTION, which is the tell. The rule
|
|
839
|
+
now says an audience posture does not arbitrate DENSITY and **wins on MOTION and ornament**,
|
|
840
|
+
because that is the whole of what it constrains.
|
|
841
|
+
- **`widened` appeared zero times in the selection text**, although every non-core pack
|
|
842
|
+
declares it. It appears now, beside the core marks.
|
|
843
|
+
- **"Fetch the kit first" was unconditional inside a core-scoped paragraph.** A kit exists for
|
|
844
|
+
every pack; for a widened pack the kit and `## Components` must agree, and for a core pack
|
|
845
|
+
the kit is the only answer either of them gives.
|
|
846
|
+
- **The greenfield contradiction does not hold against the current text.** Lines 219–226 give
|
|
847
|
+
the path explicitly — *build the page in the pack the brief's register points at*, with
|
|
848
|
+
`workbench` and `showroom` as the no-signal defaults. Recorded as not-reproduced rather than
|
|
849
|
+
repeated.
|
|
850
|
+
|
|
851
|
+
**Correcting one count exposed two more in the same file.** `SKILL.md` said **seven** packs are
|
|
852
|
+
on the core contract where six are; it still marked `atrium` `**core contract**` **eight
|
|
853
|
+
iterations after this loop widened it**; and it said **twenty-two** widened where twenty-three
|
|
854
|
+
are — a remainder the gate's own message records as stale across two consecutive releases.
|
|
855
|
+
|
|
856
|
+
The check that existed compared the prose *count* with the table, so it could only fire when
|
|
857
|
+
somebody edited the prose. `validate_table_marks_match_the_contract()` now reads the table
|
|
858
|
+
against each pack's own `Contract:` line, **in both directions**: a mark left behind after a
|
|
859
|
+
widening, and a core pack the table forgets to mark. Planted both ways.
|
|
860
|
+
|
|
861
|
+
Finding (1) is real and larger than the row said — filed as B-114 with the spread measured:
|
|
862
|
+
the motion ceiling has **two homes**, the doctrine names ten packs, four state one in their own
|
|
863
|
+
Register, the overlap is two, and six packs marked `(standalone)` have a ceiling in neither
|
|
864
|
+
place.
|
|
865
|
+
|
|
866
|
+
### The motion ceiling had two homes; now it has one (B-114)
|
|
867
|
+
|
|
868
|
+
Measured before anything moved: `MOTION_DOCTRINE.md`'s standalone passage named ceilings for
|
|
869
|
+
**ten** packs, **four** packs stated one in their own `## Register`, the overlap was **two**,
|
|
870
|
+
`awning` stated a 4 the doctrine never mentioned, and **six** packs marked `(standalone)` in
|
|
871
|
+
`SKILL.md` had a ceiling in neither place. A reader who chose off the table and stopped took a
|
|
872
|
+
ceiling that might not exist.
|
|
873
|
+
|
|
874
|
+
**The Register is the home**, because it is what a reader of one pack actually has. Eleven
|
|
875
|
+
Registers gained a statement — each relocated from the doctrine with the doctrine's own reason
|
|
876
|
+
(`workbench` and `briefing-room` at 3 as not cinematic; `ledger` at 3 with its three state
|
|
877
|
+
loops stopped under reduced motion; `pigeonhole` at 4 at the family's floor; `roster` at 4,
|
|
878
|
+
keeping only its sticky nav; `field-notes` with **no hard ceiling**, the one standalone
|
|
879
|
+
exception) — or, for the six the doctrine never named, from the default it does state: the dial
|
|
880
|
+
is cut by §1's frequency table alone. All **fifteen** standalone packs now state a ceiling or
|
|
881
|
+
state they have none.
|
|
882
|
+
|
|
883
|
+
`validate_motion_ceiling_has_one_home()` asks three things: every `(standalone)` pack states
|
|
884
|
+
one, no Register states two, and where the doctrine names a number the Register agrees.
|
|
885
|
+
|
|
886
|
+
**Two defects in the check were found by its own plants**, which is why they exist. Reading the
|
|
887
|
+
number *before* the "no ceiling is pinned" phrase made a Register that says both report the
|
|
888
|
+
number and pass — hiding the exact case the next block was written to catch. And the doctrine
|
|
889
|
+
writes a ceiling two ways, *"`pigeonhole` … above **4**"* and *"`ora` at **4**, `tenor` at
|
|
890
|
+
**4**"*, so reading only the first left four of the packs it names uncompared.
|
|
891
|
+
|
|
892
|
+
Corrected, it failed immediately on `proscenium` — where **this run's own default sentence sat
|
|
893
|
+
on top of a Register that already named 4**. Removed. Floor 3795 → 3830.
|
|
894
|
+
|
|
895
|
+
### The two radius rules were never competing (B-020)
|
|
896
|
+
|
|
897
|
+
The board said the skeleton teaches a rule a shipped pack records as a defect, and asked which
|
|
898
|
+
one is right. **Both are** — they govern different layers, and the row's framing was the thing
|
|
899
|
+
to fix. Measured across the library: **sixteen packs state radius arithmetic.**
|
|
900
|
+
|
|
901
|
+
- **The proportional ramp defines the token set.** `--radius-sm: calc(var(--radius) * 0.6)`.
|
|
902
|
+
That is where a value comes from.
|
|
903
|
+
- **Subtraction adjusts a nested instance** where the padding is known:
|
|
904
|
+
`calc(var(--radius-control) - 4px)`, and `datasheet`'s 16 − 8 = 8 is subtraction done right.
|
|
905
|
+
|
|
906
|
+
`ora` uses both and says which is which. `field-notes`' recorded error was **conflating them**:
|
|
907
|
+
its rule said subtraction, its worked example subtracted 12 from 12 and announced 7.2, and its
|
|
908
|
+
token layer used neither — three systems, and an agent applies the one it can read.
|
|
909
|
+
|
|
910
|
+
The skeleton teaches both layers now, what each governs, and that failure by name. And
|
|
911
|
+
`field-notes`' correction no longer over-reaches: it said *"not by subtraction"*, which reads as
|
|
912
|
+
*subtraction is wrong* rather than *subtraction is the other layer*.
|
|
913
|
+
|
|
914
|
+
`validate_worked_radius_sums_compute()` refuses a written subtraction that does not compute,
|
|
915
|
+
excluding a `*(Corrected …)*` note because that is a record of an error rather than a claim. It
|
|
916
|
+
ships with **no live subject** — every sum in the sixteen packs computes — so it ships with a
|
|
917
|
+
plant instead. It then caught **this run's own cautionary quote in the skeleton**, where the
|
|
918
|
+
broken sum had been reproduced verbatim: a reader copies what they see, so the warning is now
|
|
919
|
+
written as prose rather than as a copyable equation. Floor 3830 → 3833.
|
|
920
|
+
|
|
921
|
+
### The Components list is a contract, and the library had already decided (B-025)
|
|
922
|
+
|
|
923
|
+
The board asked whether the skeleton's six component classes are a contract that ten widened
|
|
924
|
+
packs owe entries for, or a menu an author picks from. **The library's own behaviour settles
|
|
925
|
+
it.** Counted over the twenty-three packs carrying the heading: **buttons in all 23, cards in
|
|
926
|
+
22, inputs and navigation in 21, loaders and empty states in 19.** Calling the list a menu
|
|
927
|
+
would license the gaps rather than close them.
|
|
928
|
+
|
|
929
|
+
So the skeleton says **contract** — and says the thing that makes it liveable: **"none, and
|
|
930
|
+
here is why" is a full answer.** What is not an answer is silence, because a reader cannot tell
|
|
931
|
+
a pack that has no loader from a pack whose author ran out of afternoon.
|
|
932
|
+
|
|
933
|
+
**`atrium`'s four gaps are closed by measurement**, read again off `functionhealth.com`:
|
|
934
|
+
|
|
935
|
+
- **Inputs** at the button's own 48px control height, a pill border in `--line-strong`, a white
|
|
936
|
+
fill rather than `--bg`, an `--ink-3` placeholder — and the honest note that label position
|
|
937
|
+
and error placement are **not on the reference's page**, so they are the implementer's.
|
|
938
|
+
- **Navigation**'s mobile shape in full: the panel's fill goes `--accent` closed → `--ink`
|
|
939
|
+
open, with an `::after` strip carrying the same fill so the two cannot disagree mid-transition.
|
|
940
|
+
- **Empty states** as `--ink-2` at 16px/300, centred, no illustration and no button.
|
|
941
|
+
- **Loaders: none** — and that is measured, not omitted. Zero skeleton, shimmer or spinner
|
|
942
|
+
rules across both stylesheets.
|
|
943
|
+
|
|
944
|
+
`validate_component_classes_are_answered()` pins the remaining **nine** (class, pack) pairs as
|
|
945
|
+
a ratchet that may only fall. It is a ratchet rather than a sweep because each fall needs its
|
|
946
|
+
own reference read, and inventing an answer is precisely what the pack layer exists to prevent
|
|
947
|
+
(B-115).
|
|
948
|
+
|
|
949
|
+
**Its plant needed two rewrites, and both failures are instructive.** Removing the Loaders
|
|
950
|
+
bullet's *label* left the words *skeleton*, *shimmer* and *spinner* standing in its body, so the
|
|
951
|
+
class still read as answered and the plant proved nothing. And the regex that then removed the
|
|
952
|
+
whole bullet was anchored on *"have had"* where the pack says *"never had"* — a fixture that
|
|
953
|
+
changed nothing, caught by the runner's own "the fixture changed nothing" branch.
|
|
954
|
+
|
|
955
|
+
### An exclusion that held by accident now holds by declaration (B-026)
|
|
956
|
+
|
|
957
|
+
`pigeonhole`'s nine `--cat-*-ink` tokens sit outside the palette gate's semantic peer set, and
|
|
958
|
+
**the exclusion is correct**. Re-measured with the gate's own OKLab metric: the closest pair is
|
|
959
|
+
**4.92 ΔE** at full colour (`--cat-notification-ink` / `--cat-team-ink`) and **1.24 ΔE** under
|
|
960
|
+
deuteranopia (`--cat-marketing-ink` / `--cat-notification-ink`), against a hard floor of 10 and
|
|
961
|
+
a dichromacy floor of 8. No arrangement of nine hues clears those. The board's 4.42 was slightly
|
|
962
|
+
off; its 1.24 was exact.
|
|
963
|
+
|
|
964
|
+
What was wrong is *how* the exclusion held: `STATUS_TOKENS` matches by **name**, so an author
|
|
965
|
+
renaming a category token `--danger-ink` got a red gate for the right reason **by luck**, and one
|
|
966
|
+
adding a tenth hue got nothing at all.
|
|
967
|
+
|
|
968
|
+
The pack now declares it where the categories are enumerated — with those numbers, and with the
|
|
969
|
+
carrier named: the chip's **label word**, already required in Components, plus every ink clearing
|
|
970
|
+
AA against its own tint. The hue is recognition, never identification, and a chip rendered as a
|
|
971
|
+
bare swatch is a defect in this pack.
|
|
972
|
+
|
|
973
|
+
`validate_excluded_sets_are_declared()` holds the enumeration equal to the shipped tokens **in
|
|
974
|
+
both directions** — a tenth hue with no row fails, a row with no token fails — and requires the
|
|
975
|
+
carrier to be stated **in the section that enumerates the set**.
|
|
976
|
+
|
|
977
|
+
**That last scoping is the fix its own plant forced.** Searching the whole file let a chip rule
|
|
978
|
+
three sections away satisfy a declaration about the hues: the plant broke two of the phrase's
|
|
979
|
+
three occurrences and the third kept the check quiet. A declaration belongs where the reader of
|
|
980
|
+
that set is standing.
|
|
981
|
+
|
|
982
|
+
B-017's widening, which this row asked to wait for, shipped earlier the same day. Floor 3835 →
|
|
983
|
+
3839.
|
|
984
|
+
|
|
985
|
+
### A focus ring was guarded by a mechanism that cannot guard (B-027)
|
|
986
|
+
|
|
987
|
+
The board asked for 42 hand-restated channel literals to be migrated to relative colour, with
|
|
988
|
+
the rule *"the literal must ship first as a fallback"*. Re-measured, the count is **71 across
|
|
989
|
+
14 packs**, not 42 — it grew with the library and nothing was watching it. And **56 of the 71
|
|
990
|
+
are custom property definitions**, which is what settles the decision:
|
|
991
|
+
|
|
992
|
+
**For a custom property, a literal above a relative declaration is not a fallback.**
|
|
993
|
+
`--x: rgb(from …)` **parses everywhere** — a custom property accepts almost any token sequence
|
|
994
|
+
— so it wins the cascade unconditionally, and the invalidity surfaces only where the property
|
|
995
|
+
is *substituted*, as invalid-at-computed-value-time. By then the literal declaration is gone
|
|
996
|
+
and every `var(--x)` resolves to `unset`, not to the line above it.
|
|
997
|
+
|
|
998
|
+
**Twenty-three properties across `ledger` and `showroom` shipped exactly that pattern**, and
|
|
999
|
+
`showroom`'s was **`--ring-focus`**. Its own comment named the stake — *"a dropped declaration
|
|
1000
|
+
is an invisible focus indicator, which is the one degradation this library may not ship"* — and
|
|
1001
|
+
then relied on the mechanism that cannot deliver it. A twenty-fourth, `ora`'s `--accent-hover`,
|
|
1002
|
+
had **no fallback at all**.
|
|
1003
|
+
|
|
1004
|
+
All twenty-four are inside `@supports (color: rgb(from red r g b))` now, which is where a
|
|
1005
|
+
literal fallback actually survives. `validate_relative_colour_is_guarded()` refuses any
|
|
1006
|
+
relative-colour custom property outside such a block, planted with `showroom`'s exact shipped
|
|
1007
|
+
pattern. `ora`'s literal is per-theme on purpose: `:root[data-theme="light"]` sets its own
|
|
1008
|
+
value, so a single literal carries one theme and needs to.
|
|
1009
|
+
|
|
1010
|
+
**The remaining 47 are not migrated, and that is the answer rather than a deferral.** Relative
|
|
1011
|
+
colour in a definition needs an `@supports` block per pack, which is machinery in the file a
|
|
1012
|
+
reader is told to copy verbatim. The cheaper fix is for each literal to state its source —
|
|
1013
|
+
`/* = --on-ink at 72% */`, which `ledger`'s eighteen now do — and for a check to recompute the
|
|
1014
|
+
declared relationship, so a token that moves fails in CI instead of drifting silently (B-116).
|
|
1015
|
+
|
|
1016
|
+
Floors 3839 → 3863 and 2220 → 2346; the palette count moved because the `@supports` blocks add
|
|
1017
|
+
a theme map the palette gate now reads.
|
|
1018
|
+
|
|
1019
|
+
### The kit migration was finished, and a guard was complete by accident (B-029)
|
|
1020
|
+
|
|
1021
|
+
The board said *15 remain*. Fifteen kits carry no container query — and **eleven of those carry
|
|
1022
|
+
no width query at all**, so there is nothing in them to migrate. The actionable set was four
|
|
1023
|
+
kits, six blocks, and every one of them is already resolved:
|
|
1024
|
+
|
|
1025
|
+
- **`datasheet`** — two blocks, marked *"SELF for the guides, PAGE for the heading"* with the
|
|
1026
|
+
reason for each half.
|
|
1027
|
+
- **`field-notes`** — two blocks, marked PAGE and SELF.
|
|
1028
|
+
- **`maquette`** — one, SELF, with the reason that wrapping every model block to let it query
|
|
1029
|
+
itself is worse than the query.
|
|
1030
|
+
- **`pigeonhole`** — one, and it is a `:root` token switch, which the guard exempts **by
|
|
1031
|
+
design**: `:root` is inside nobody's container, so it cannot be replaced by one.
|
|
1032
|
+
|
|
1033
|
+
**Zero unmarked, unexempt viewport queries remain across 29 kits.**
|
|
1034
|
+
|
|
1035
|
+
What *was* worth fixing is the guard's reach. It read `*/src/styles.css` and nothing else.
|
|
1036
|
+
Measured today: zero width queries live outside `styles.css`, and zero CSS files fall outside
|
|
1037
|
+
that glob — so its coverage was complete **by accident**. A `@media (max-width: …)` inside a
|
|
1038
|
+
`.tsx` template literal, or a second stylesheet beside the first, would have been invisible.
|
|
1039
|
+
|
|
1040
|
+
It now reads every `.css`, `.tsx`, `.ts`, `.jsx` and `.js` a kit ships, refuses a corpus of
|
|
1041
|
+
fewer than two files rather than passing on an empty one, and is planted with a width query in
|
|
1042
|
+
a `.tsx` template literal. Floor 3863 → 3865.
|
|
1043
|
+
|
|
1044
|
+
### Three of five typographic findings did not hold, and one counted comments (B-030)
|
|
1045
|
+
|
|
1046
|
+
The row listed five primitives the library specifies by hand. Re-measured today, **three of
|
|
1047
|
+
the five are wrong or have closed themselves**:
|
|
1048
|
+
|
|
1049
|
+
| The row said | Measured 2026-08-20 |
|
|
1050
|
+
|---|---|
|
|
1051
|
+
| 0 declare a measure in `ch` | **11** files use `ch`; 12 declare a px measure |
|
|
1052
|
+
| fluid spacing 0 of 16, type fluid in 5 | **6** and **15** |
|
|
1053
|
+
| `font-display` in 21 files, `size-adjust` in none — *"a guaranteed reflow"* | `font-display` in **three** places, all the **same comment line**; **zero** `@font-face` blocks |
|
|
1054
|
+
|
|
1055
|
+
The third is the one worth dwelling on. That count matched **string occurrences** — a token
|
|
1056
|
+
named `--font-display: aeonik`, and a comment in `manpage`'s token layer describing its
|
|
1057
|
+
reference. There is no `@font-face` anywhere in this library and no `font-display` declaration,
|
|
1058
|
+
so `size-adjust` and `ascent-override` have nowhere to attach: **font loading is the consumer's
|
|
1059
|
+
layer, and that is a decision rather than a gap.**
|
|
1060
|
+
|
|
1061
|
+
`text-box-trim` / `text-box-edge` and `@property` do stand, both at zero.
|
|
1062
|
+
|
|
1063
|
+
The skeleton's `## Type` section now carries all three answers with today's numbers and a
|
|
1064
|
+
support line each: `text-box-trim` as a **recommendation** rather than a rule because Firefox
|
|
1065
|
+
lacks it — and with the reason it matters, that a 60px display at `line-height: 1` is 60px of
|
|
1066
|
+
line box and not of cap height; `ch` as the rule a px measure stops obeying the moment the face
|
|
1067
|
+
changes; and font loading as explicitly not this layer, so a pack that wants metric-matched
|
|
1068
|
+
fallbacks says so in Gotchas instead of shipping an inert descriptor.
|
|
1069
|
+
|
|
1070
|
+
`validate_font_loading_stays_out()` holds that last decision — and **strips comments first**,
|
|
1071
|
+
because a comment is exactly what made the original count wrong. Planted both ways.
|
|
1072
|
+
|
|
1073
|
+
`@property` is filed rather than done (B-117): the token layers are files a reader is told to
|
|
1074
|
+
copy verbatim, and a block per token would roughly double them. The narrow version worth pricing
|
|
1075
|
+
is the tokens that are actually animated, and that list has to be measured from the kits'
|
|
1076
|
+
transitions rather than guessed.
|
|
1077
|
+
|
|
1078
|
+
Floor 3865 → 3981.
|
|
1079
|
+
|
|
1080
|
+
### Two deferred breakpoints, and neither had the container answer the board assumed (B-032)
|
|
1081
|
+
|
|
1082
|
+
Both blocks carried `TODO-CONTAINER B-032` and both were held out of 1.23.0 on the right
|
|
1083
|
+
principle: **the breakpoint has to come from the component's own geometry**, and carrying 768
|
|
1084
|
+
or 640 into `@container` would be the same mistake in newer syntax. Doing that arithmetic is
|
|
1085
|
+
what closed the row — and it did not produce container queries.
|
|
1086
|
+
|
|
1087
|
+
**`datasheet`'s instrument grid needed no query at all.** It is already
|
|
1088
|
+
`repeat(auto-fit, minmax(180px, 1fr))`: two columns need 180 + 180 + 1px of border = **361px**
|
|
1089
|
+
of grid width, and below that `auto-fit` drops to one on its own. The
|
|
1090
|
+
`@media (max-width: 640px)` was not helping it — it was **overriding** it, forcing a single
|
|
1091
|
+
column on a 640px phone whose instrument may hold two comfortably. Resolved by deleting the
|
|
1092
|
+
block.
|
|
1093
|
+
|
|
1094
|
+
**`blueprint`'s two derive to numbers that give the game away.** The registration tick is
|
|
1095
|
+
`--tick-len: 8px` sitting `--tick-gap: 6px` off each corner, so its two top arms consume 16px
|
|
1096
|
+
of the element's width, and at the field's own `--grid-step` of 32px the marks stop reading as
|
|
1097
|
+
corners only below **~48px**. The column rule is `--rule-w: 1px` at each edge of a column whose
|
|
1098
|
+
inner padding is `--space-8` = 32px — and **this pack states no measure anywhere**, so the only
|
|
1099
|
+
derivable floor is the **64px** of padding itself.
|
|
1100
|
+
|
|
1101
|
+
48px and 64px are far below anything a container reaches. That is the tell: the 768px
|
|
1102
|
+
breakpoint was never about fitting. A registration mark and an edge rule are **ornament**, and
|
|
1103
|
+
how much ornament a page carries is a page decision — so both are marked PAGE, with the
|
|
1104
|
+
arithmetic written at the block so the next reader does not redo it.
|
|
1105
|
+
|
|
1106
|
+
**TODO-CONTAINER is at zero and pinned there.** An escape with no ceiling becomes the habit it
|
|
1107
|
+
was meant to interrupt; the ratchet is planted with a marker added back. Floor 3981 → 3983.
|
|
1108
|
+
|
|
1109
|
+
### Gate
|
|
1110
|
+
|
|
1111
|
+
- **Coverage is pinned, in both directions.** `check_ratio_coverage()` reads
|
|
1112
|
+
`validate_palette.py:ratios` from `test/floors.json`: `computed` may only rise,
|
|
1113
|
+
`unresolved` and `unguarded` may only fall. A check-count floor cannot see this —
|
|
1114
|
+
deleting one `on \u0060--bg\u0060` from a table header moved 27 claims out of the
|
|
1115
|
+
arithmetic while every other check stayed put. Watched failing at 348 → 342.
|
|
1116
|
+
- **Five plants, one per bucket**, so the *classification* is watched discriminating and
|
|
1117
|
+
not only the arithmetic: a table declaring a base its numbers contradict, the same
|
|
1118
|
+
claim with the base removed, prose naming no partner, a ratio placed at a gradient
|
|
1119
|
+
stop, and a named partner whose subject cannot be paired.
|
|
1120
|
+
- `PARTNER_PHRASE` now reads past a determiner — *"text on the `--accent` fill"* named a
|
|
1121
|
+
partner the gate could not see. The regex moved rather than the sentence.
|
|
1122
|
+
- `ARGUED` lists only terms that can reach it. `RATIO_SKIP` already drops a floor, a
|
|
1123
|
+
bound and a rejected candidate one step earlier, so repeating those would have been
|
|
1124
|
+
alternatives that never match — which reads as coverage and is not.
|
|
1125
|
+
- Floor `validate_palette.py` 1924 → 1951, all 27 from the newly-checked table claims,
|
|
1126
|
+
then 1951 → 2087 for the declared-set sweep.
|
|
1127
|
+
- **Four plants for the declared sets, and the third is the point**: a file-wide
|
|
1128
|
+
*"never by colour alone"* must NOT excuse a pair below the hard floor. Without it the
|
|
1129
|
+
check is a phrase-detector and one sentence silences it.
|
|
1130
|
+
- A figure with no qualifier is skipped rather than guessed, and the count of
|
|
1131
|
+
disclosure figures no check could bind prints on every run (7 today, B-055/B-056).
|
|
1132
|
+
- The `field-notes` kit's token block was re-copied from the pack's layer, which the
|
|
1133
|
+
copy-never-transcribe check caught on the first run after the comment changed.
|
|
1134
|
+
- **`validate_composited_fill_contrast` composites in sRGB gamma space**, because that is
|
|
1135
|
+
where the browser does it. A first draft composited this module's linear values and
|
|
1136
|
+
reported 1.88:1 where the page renders 3.16 — wrong in the direction that fails a
|
|
1137
|
+
correct pack.
|
|
1138
|
+
- **Composites are read per CELL.** A Components row is resting | hover | active |
|
|
1139
|
+
disabled, and reading the whole row made `cyclorama`'s hover fill the resting chip's
|
|
1140
|
+
text colour: 1.26:1 reported against a chip that renders at 12.38:1. There is a plant
|
|
1141
|
+
for it.
|
|
1142
|
+
- Two live subjects, one of each verdict — which is what makes it a check. Measured
|
|
1143
|
+
first: 44 lines in the library pair a token with a percentage and only two state a
|
|
1144
|
+
composite fill AND a type size. The other 42 are filed (B-058), not guessed at.
|
|
1145
|
+
- Floors `validate_palette.py` 2087 → 2092, `computed_at_least` 348 → 349.
|
|
1146
|
+
- **Five plants for the prescribed pairs**, including the hover-swap path B-039 is made of
|
|
1147
|
+
and the `as above` inheritance. The fixture had to be corrected before they meant
|
|
1148
|
+
anything: it used B-039's own values, where the resting pair already fails, so three of
|
|
1149
|
+
the five were passing for the wrong reason.
|
|
1150
|
+
- A claim added in this change had to be rewritten because `on` and its token landed on
|
|
1151
|
+
either side of a line break — the same refusal B-039's own fix met, and correct both
|
|
1152
|
+
times.
|
|
1153
|
+
- Floors 2092 → 2119 and `computed_at_least` 349 → 353.
|
|
1154
|
+
- **`scripts/sync-kit-tokens.py`, and `npm run sync-kits`.** A kit's `styles.css` is its
|
|
1155
|
+
pack's token layer verbatim plus its own component half, so every edit to a token
|
|
1156
|
+
layer — down to a comment — makes the kit red until the block is copied again. That
|
|
1157
|
+
copy was done by hand three times today and the third touched **15 kits at once**. The
|
|
1158
|
+
script takes the block boundary from the file's *committed* version instead of guessing
|
|
1159
|
+
it, and a kit that was hand-edited inside its token block reports `STUCK` and is left
|
|
1160
|
+
byte-identical. Watched refusing in a worktree, with the component half intact.
|
|
1161
|
+
- Floors 2119 → 2214, `computed_at_least` 353 → 448, `unguarded_at_most` 150 → 57.
|
|
1162
|
+
|
|
1163
|
+
### Fixed
|
|
1164
|
+
|
|
1165
|
+
- **1.45.0's release notes were stranded under `[Unreleased]`.** The release commit wrote
|
|
1166
|
+
a summary above the accumulated section and left it in place, so the shipped version's
|
|
1167
|
+
detail was labelled unreleased and the next entry would have landed on top of it. The
|
|
1168
|
+
detail is folded into 1.45.0 below, where it belongs. `validate_release_register()`
|
|
1169
|
+
now refuses an `[Unreleased]` heading that is not the first section, and refuses a
|
|
1170
|
+
second one — the same class as the duplicate `## [1.35.0]` found on 2026-08-20, one
|
|
1171
|
+
heading over, and nothing had been looking at it.
|
|
1172
|
+
|
|
7
1173
|
## [1.45.0] - 2026-08-20
|
|
8
1174
|
|
|
9
1175
|
**Four packs contradicted their own token layers, and the layer is what an implementer
|
|
@@ -36,7 +1202,7 @@ Registers that had stopped:
|
|
|
36
1202
|
at sixteen packs and asserted all 71 had been hand-verified. Measured today: **513 claims,
|
|
37
1203
|
336 guarded, 177 unguarded at 22 packs** — and the run prints that pair every time, with
|
|
38
1204
|
the hand-verification dated for what it was. Two more restated counts went the same way.
|
|
39
|
-
- **`package.json` said
|
|
1205
|
+
- **`package.json` said thirty-one packs and named 27** — `awning` and `bulletin` missing
|
|
40
1206
|
from the surface an npm reader chooses from. It is an enumeration site now.
|
|
41
1207
|
- **Two `## [1.35.0]` sections** under one version, and four releases in the CHANGELOG with
|
|
42
1208
|
no tag. The duplicate is resolved; the untagged four are **reported, not created** — a tag
|
|
@@ -63,7 +1229,7 @@ that failed.
|
|
|
63
1229
|
Checks: `validate.py` 3398 to **3543**, `validate_palette.py` 1912 to **1924**, `sloplint.py`
|
|
64
1230
|
635. Ten new plants, all caught; 49 planted defects across the three self-tests, 0 missed.
|
|
65
1231
|
|
|
66
|
-
|
|
1232
|
+
|
|
67
1233
|
|
|
68
1234
|
### Four packs contradicted themselves, and six sweeps now say so
|
|
69
1235
|
|
|
@@ -164,7 +1330,7 @@ measured, and never gates content.
|
|
|
164
1330
|
the comment says plainly which of them was ever verified.
|
|
165
1331
|
- `test/validate.py` said "the six packs shipped before the widening" (seven) and
|
|
166
1332
|
"seven of the ten widened packs" (twenty-two widened). The split is printed now.
|
|
167
|
-
- `package.json` said "
|
|
1333
|
+
- `package.json` said "thirty-one locked style packs" over a list of **27** —
|
|
168
1334
|
`awning` and `bulletin` were missing. `ENUMERATION_SITES` gained `package.json`,
|
|
169
1335
|
which is the description npmjs.com renders: its count was already policed and
|
|
170
1336
|
the names beside it were not.
|
|
@@ -288,7 +1454,7 @@ with their numbers.
|
|
|
288
1454
|
Rendered and confirmed at 1440×1000, including the press: the primary's offset goes
|
|
289
1455
|
3px → 1px as the control travels 2px, while the secondary beside it keeps its 3px.
|
|
290
1456
|
- **Three stale counts in `SURFACE_COMPOSITION.md` that no gate could see.** It said the
|
|
291
|
-
token names were not uniform "across the twenty-one" when
|
|
1457
|
+
token names were not uniform "across the twenty-one" when thirty-one packs ship, and
|
|
292
1458
|
that the accent is `--accent` "in eighteen" when it is twenty-seven — `--brand` in
|
|
293
1459
|
`field-notes` and `--cta` in `orchard` are still the only two exceptions, counted. The
|
|
294
1460
|
third was a count of packs carrying an `@role non-text:` colour, which is now stated as
|