{ "ledgerVersion": 1, "rubricVersion": "1.2", "updated": "2026-08-10", "about": "Audit scores for Astryx components, one entry per AUDITED component. A component with no entry here has not been audited — the roster of components is read from packages/core/src and packages/lab/src, never from a list. This file is the ONLY stored form of the ledger: there is no generated table, because a second copy goes stale the moment this one changes. A rendered view in the sandbox app is planned but not built, so this file is also the only way to read it today. Hand-edit it: add or replace the component's entry, pull, commit, push. A score-ledger tool that would apply the ratchet automatically is planned but is not on main, so the ratchet is a rule the auditor applies, not a program.", "caveats": [ "All five rows come from the same calibration exercise on commit `dccdabea0b`, run by five independent graders. They are the only audits that exist so far, and they are honest about their limits rather than tidy.", "`score` is the round-2 total under rubric **v1.1** with §5 excluded and its weight redistributed — the figure the calibration record itself publishes. It was produced from **static evidence only**: no lint, test, build, axe or RTL run.", "The §5a/§5b numbers come from a later pass over 256 rendered screenshots at the same commit. That pass produced a single §5 grade (it predates the v1.2 split), so the same number is recorded on both halves. Those points are **not** folded into `score`: recomputing the total would need the per-section scores the calibration record never published.", "`blocks.count` **is** the union of both passes. Every other section reads `unpublished`: it was audited and is inside `score`, but the record published only the total. That is not `not_measured` — somebody did look — and inventing the split would be fabrication.", "`distinct_defects` is the record's own count; only Badge's (14) is a deduplicated figure, the rest are its approximations.", "Most BLOCKs here have no issue filed yet. Two link pre-existing issues. Filing the rest is the next step, not a completed one.", "Treat every row as a floor to re-audit under v1.2 with the tooling actually run, not as a settled grade." ], "components": [ { "component": "Badge", "package": "core", "status": "audited", "score": 74.2, "grade": "C", "sections": { "a11y": { "score": null, "weight": 16, "state": "unpublished" }, "theming": { "score": null, "weight": 14, "state": "unpublished" }, "api": { "score": null, "weight": 14, "state": "unpublished" }, "behavior": { "score": null, "weight": 12, "state": "unpublished" }, "design_objective": { "score": 2, "weight": 6, "state": "scored", "note": "The rendered-pixel pass produced one §5 grade; it predates the §5a/§5b split, so the same number sits on both halves." }, "design_rendered": { "score": 2, "weight": 4, "state": "scored" }, "testing": { "score": null, "weight": 8, "state": "unpublished" }, "code_health": { "score": null, "weight": 8, "state": "unpublished" }, "docs": { "score": null, "weight": 8, "state": "unpublished" }, "i18n_rtl": { "score": null, "weight": 5, "state": "unpublished" }, "responsive": { "score": null, "weight": 5, "state": "unpublished" } }, "blocks": { "count": 3, "open": [ { "id": "A18", "section": "§1 Accessibility & operable paths", "summary": "axe is green because two entries in .github/a11y-baseline.json silence it, not because the component is clean", "evidence": ".github/a11y-baseline.json", "issue": null }, { "id": "D7", "section": "§5b Design conventions — rendered", "summary": "variant=\"error\" renders #ffffff on rgb(227,63,74) = 4.14:1 at 12px/500 in light — below the 4.5:1 AA bar", "evidence": "Badge__error__rest__light.png (rendered-pixel pass)", "issue": 4414 }, { "id": "D10", "section": "§5b Design conventions — rendered", "summary": "semantic variants convey status by colour alone — `icon` is opt-in, and all 14 variants render as bare chips", "evidence": "Badge__semantic__with-and-without-icon__light.png", "issue": null } ] }, "distinct_defects": 14, "fixes": null, "nits": null, "lastAudited": "2026-08-10", "rubricVersion": "1.1", "mode": "O", "commit": "dccdabea0b", "evidence": [ { "label": "§5 rendered-pixel pass — 256 screenshots, real Chromium via Playwright, neutral light/dark + y2k/y2kdark", "note": "local capture set with a §5 report; not published to a durable location yet" } ], "notes": "Non-interactive : hover/focus/active/disabled/loading/selected are N/A with reason. Two prominence tiers inside one variant set, and four variant pairs render identical fills under a custom theme." }, { "component": "Button", "package": "core", "status": "audited", "score": 62.6, "grade": "D", "sections": { "a11y": { "score": null, "weight": 16, "state": "unpublished" }, "theming": { "score": null, "weight": 14, "state": "unpublished" }, "api": { "score": null, "weight": 14, "state": "unpublished" }, "behavior": { "score": null, "weight": 12, "state": "unpublished" }, "design_objective": { "score": 2, "weight": 6, "state": "scored", "note": "The rendered-pixel pass produced one §5 grade; it predates the §5a/§5b split, so the same number sits on both halves." }, "design_rendered": { "score": 2, "weight": 4, "state": "scored" }, "testing": { "score": null, "weight": 8, "state": "unpublished" }, "code_health": { "score": null, "weight": 8, "state": "unpublished" }, "docs": { "score": null, "weight": 8, "state": "unpublished" }, "i18n_rtl": { "score": null, "weight": 5, "state": "unpublished" }, "responsive": { "score": null, "weight": 5, "state": "unpublished" } }, "blocks": { "count": 6, "open": [ { "id": "T6", "section": "§2 Theming & token integrity", "summary": "`elevation` selects between StyleX style objects but is absent from the sibling themeProps() call — the whole axis is unthemeable", "evidence": "packages/core/src/Button/Button.tsx", "issue": null }, { "id": "A11", "section": "§1 Accessibility & operable paths", "summary": "busy sets native `disabled`, so focus is dropped mid-async (note: the wiki contradicts itself on busy-vs-disabled — rubric decision #21)", "evidence": "packages/core/src/Button/Button.tsx", "issue": null }, { "id": "D13", "section": "§5b Design conventions — rendered", "summary": "`primary` hover is a mathematical no-op — the 5% overlay composites against an inverted base, so 0.00% of pixels change in light, dark and y2k", "evidence": "Button__primary__{rest,hover}__{light,dark,y2k}.png", "issue": null }, { "id": "D7", "section": "§5b Design conventions — rendered", "summary": "`destructive` focus ring is 1.5:1 against the page in the y2k theme — WCAG 2.4.11 needs 3:1", "evidence": "Button__destructive__focusvisible__y2k.png", "issue": null } ] }, "distinct_defects": 30, "fixes": null, "nits": null, "lastAudited": "2026-08-10", "rubricVersion": "1.1", "mode": "O", "commit": "dccdabea0b", "evidence": [ { "label": "§5 rendered-pixel pass — 256 screenshots, real Chromium via Playwright, neutral light/dark + y2k/y2kdark", "note": "local capture set with a §5 report; not published to a durable location yet" } ], "notes": "Zero Effects in an 832-line component. The dark-mode primary label contrast (3.11:1) is token-inherited and deliberately not scored against the component while the system-wide fix is open." }, { "component": "SideNav", "package": "core", "status": "audited", "score": 48, "grade": "F", "sections": { "a11y": { "score": null, "weight": 16, "state": "unpublished" }, "theming": { "score": null, "weight": 14, "state": "unpublished" }, "api": { "score": null, "weight": 14, "state": "unpublished" }, "behavior": { "score": null, "weight": 12, "state": "unpublished" }, "design_objective": { "score": 2, "weight": 6, "state": "scored", "note": "The rendered-pixel pass produced one §5 grade; it predates the §5a/§5b split, so the same number sits on both halves." }, "design_rendered": { "score": 2, "weight": 4, "state": "scored" }, "testing": { "score": null, "weight": 8, "state": "unpublished" }, "code_health": { "score": null, "weight": 8, "state": "unpublished" }, "docs": { "score": null, "weight": 8, "state": "unpublished" }, "i18n_rtl": { "score": null, "weight": 5, "state": "unpublished" }, "responsive": { "score": null, "weight": 5, "state": "unpublished" } }, "blocks": { "count": 12, "open": [ { "id": "A13", "section": "§1 Accessibility & operable paths", "summary": "three transitions with no prefers-reduced-motion guard, one of them animating grid-template-rows — a layout property", "evidence": "SideNavItem.tsx:93-96,116-118; SideNavCollapseButton.tsx:43-45", "issue": null }, { "id": "I2", "section": "§9 i18n & RTL", "summary": "a concatenated English AT string (`${label} submenu`) bypasses the translator", "evidence": "packages/core/src/SideNav/SideNavItem.tsx", "issue": null }, { "id": "D13", "section": "§5b Design conventions — rendered", "summary": "selected and hover composite 1.02:1 apart in light, so hovering any row makes it look like the current page", "evidence": "SideNav__default__{rest,item-hover}__light.png", "issue": null } ] }, "distinct_defects": 35, "fixes": null, "nits": null, "lastAudited": "2026-08-10", "rubricVersion": "1.1", "mode": "O", "commit": "dccdabea0b", "evidence": [ { "label": "§5 rendered-pixel pass — 256 screenshots, real Chromium via Playwright, neutral light/dark + y2k/y2kdark", "note": "local capture set with a §5 report; not published to a durable location yet" } ], "notes": "A compound component with five sub-components: every rule gets five chances to be violated, which is why the 2.5 half-step ceiling was added in v1.2. Zero useEffect in ~4,700 lines, zero raw hex, zero physical properties, ~110 tests — and still an F, because the grade measures distance from the bar. The focus ring falls through to the browser default rather than the system's." }, { "component": "TextInput", "package": "core", "status": "audited", "score": 70.7, "grade": "C", "sections": { "a11y": { "score": null, "weight": 16, "state": "unpublished" }, "theming": { "score": null, "weight": 14, "state": "unpublished" }, "api": { "score": null, "weight": 14, "state": "unpublished" }, "behavior": { "score": null, "weight": 12, "state": "unpublished" }, "design_objective": { "score": 2, "weight": 6, "state": "scored", "note": "The rendered-pixel pass produced one §5 grade; it predates the §5a/§5b split, so the same number sits on both halves." }, "design_rendered": { "score": 2, "weight": 4, "state": "scored" }, "testing": { "score": null, "weight": 8, "state": "unpublished" }, "code_health": { "score": null, "weight": 8, "state": "unpublished" }, "docs": { "score": null, "weight": 8, "state": "unpublished" }, "i18n_rtl": { "score": null, "weight": 5, "state": "unpublished" }, "responsive": { "score": null, "weight": 5, "state": "unpublished" } }, "blocks": { "count": 4, "open": [ { "id": "A10", "section": "§1 Accessibility & operable paths", "summary": "the clear button is 16×16, under the 24×24 WCAG 2.5.8 floor, and is hand-rolled rather than composed from the shared primitive", "evidence": "packages/core/src/TextInput/TextInput.tsx", "issue": null }, { "id": "D13", "section": "§5b Design conventions — rendered", "summary": "the focus indicator is imperceptible in the y2k theme (1.06:1) and reads weaker than that theme's own hover — a keyboard user gets a weaker signal than a mouse user", "evidence": "TextInput__default__{hover,focus}__y2k.png", "issue": null } ] }, "distinct_defects": 15, "fixes": null, "nits": null, "lastAudited": "2026-08-10", "rubricVersion": "1.1", "mode": "O", "commit": "dccdabea0b", "evidence": [ { "label": "§5 rendered-pixel pass — 256 screenshots, real Chromium via Playwright, neutral light/dark + y2k/y2kdark", "note": "local capture set with a §5 report; not published to a durable location yet" } ], "notes": "Effect-free, fully ARIA-wired, i18n-clean, 79 tests, complete docs surface — and it scored F under v1.0. That result is what recalibrated the rubric. Status handling is exemplary: each status renders a distinct icon alongside the colour." }, { "component": "Tooltip", "package": "core", "status": "audited", "score": 51.3, "grade": "F", "sections": { "a11y": { "score": null, "weight": 16, "state": "unpublished" }, "theming": { "score": null, "weight": 14, "state": "unpublished" }, "api": { "score": null, "weight": 14, "state": "unpublished" }, "behavior": { "score": null, "weight": 12, "state": "unpublished" }, "design_objective": { "score": 4, "weight": 6, "state": "scored", "note": "The rendered-pixel pass produced one §5 grade; it predates the §5a/§5b split, so the same number sits on both halves." }, "design_rendered": { "score": 4, "weight": 4, "state": "scored" }, "testing": { "score": null, "weight": 8, "state": "unpublished" }, "code_health": { "score": null, "weight": 8, "state": "unpublished" }, "docs": { "score": null, "weight": 8, "state": "unpublished" }, "i18n_rtl": { "score": null, "weight": 5, "state": "unpublished" }, "responsive": { "score": null, "weight": 5, "state": "unpublished" } }, "blocks": { "count": 4, "open": [ { "id": "A8", "section": "§1 Accessibility & operable paths", "summary": "no touch path at all — the pointer-enter handler early-returns on `(hover: none)`, so the tooltip never appears on a touch device", "evidence": "useTooltip.tsx:332-342", "issue": 3885 } ] }, "distinct_defects": 22, "fixes": null, "nits": null, "lastAudited": "2026-08-10", "rubricVersion": "1.1", "mode": "O", "commit": "dccdabea0b", "evidence": [ { "label": "§5 rendered-pixel pass — 256 screenshots, real Chromium via Playwright, neutral light/dark + y2k/y2kdark", "note": "local capture set with a §5 report; not published to a durable location yet" } ], "notes": "The only one of the five with no blocking visual finding. Two of its original BLOCKs were withdrawn once the sanctioned external-system Effect exception landed: synchronizing the native Popover API from props is what an Effect is for. That exception is still pending sign-off." } ] }