Files
Lumotia/docs/superpowers/audits/2026-04-25-phase10a-static-slice.md
Jake cc77befda8
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
docs(phase10a): static slice audit findings
Captures the agent-runnable portion of Phase 10a ahead of Jake's
manual walkthrough and feedback-document pass:

- a11y baseline confirmed clean (svelte-check 0/0; consistent
  aria-label + aria-hidden patterns across icon buttons; global
  :focus-visible ring set in design tokens; prefers-reduced-motion
  guards present where motion warrants them)
- WCAG 2.1 AA contrast tables for both themes computed from the
  token list at design-system/colors_and_type.css. Nine pairs miss
  AA-normal; light-theme warning misses AA-large too. Severity
  ranked, suggested token shifts noted as starting points
- CI matrix state: check.yml runs on every push, build.yml has
  never been end-to-end exercised - recommend manual workflow_
  dispatch before tagging v0.1.0
- Clean-install test plan and the Phase 9d walkthrough checklist
  consolidated for the testing session
2026-04-25 01:02:48 +01:00

262 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: Phase 10a static slice audit
description: Static a11y + contrast + CI verification produced by Wren on 2026/04/25 as the agent-runnable portion of Phase 10a, ahead of Jake's manual walkthrough and feedback-document pass.
type: audit
tags: [phase10a, audit, a11y, contrast, ci, release]
created: 2026/04/25
status: findings
author: Wren (CORBEL's resident agent) on behalf of Jake Sames
---
# Phase 10a — Static Slice Audit
> **Scope.** Everything in Phase 10a that an agent can verify without
> a running dev server. The dogfood walkthrough, RB-08 macOS power-
> assertion verification, and runtime keyboard / screen-reader
> traversal stay with Jake (or Rachmann for the Mac slot).
>
> **Baseline.** `main` at `0ca4e0e`. 277 cargo tests pass. clippy /
> fmt / svelte-check / npm build all clean.
## Summary of findings
- **A11y static rules: clean.** svelte-check reports 0/0 across 3957
files. Spot-checks confirm consistent `aria-label` on icon-only
buttons and `aria-hidden="true"` on inner SVGs. Global
`:focus-visible` ring is set in design tokens.
- **Contrast: real fails in light theme + small dim text in both
themes.** Nine token pairs miss WCAG AA-normal (4.5:1). One pair
(`warning` on `bg`, light) misses AA-large too.
- **CI: cross-platform `cargo check` matrix exists and runs on every
push to `main`.** The full Tauri installer build (`build.yml`) has
never been exercised end-to-end and should be triggered by manual
workflow_dispatch before tagging v0.1.0.
## A11y baseline
- `npm run check` (svelte-check 4): 0 errors / 0 warnings on 3957
files. Svelte's a11y ruleset enforces `<img>` alt text, form-input
labels, click-handler-on-non-interactive, autofocus restrictions,
noninteractive-tabindex, and roughly twenty other rules. All pass.
- Spot-checked icon-only buttons across `Sidebar.svelte`,
`ToastViewport.svelte`, `MicroSteps.svelte`, `HotkeyRecorder.svelte`,
`HistoryPage.svelte`, `TasksPage.svelte`. Every button carries
`aria-label`. Every inner `<svg>` carries `aria-hidden="true"`. The
pattern is consistent enough to assume it's the convention rather
than coincidence.
- Global focus ring at `src/design-system/colors_and_type.css:225`:
`:focus-visible { outline: 2px solid var(--accent); outline-offset:
3px; border-radius: var(--radius-md); }`. Covers every focusable
element by default.
- `prefers-reduced-motion` guards present in 8 files
(`ToastViewport`, `CompletionSparkline`, `SettingsGroup`,
`DictationPage`, `TasksPage`, `app.css`, `colors_and_type.css`,
plus the `preferences.svelte.ts` store that exposes the flag).
These cover the four scaled / staggered animations Phase 8 + 9
introduced. Hover-only colour transitions across the rest of the
app rely on the global `*` transition rule and don't pose a
vestibular risk; they're under the reduced-motion threshold.
## Contrast audit (WCAG 2.1 AA)
Computed via Python (sRGB → linear-RGB → relative luminance →
Web Content Accessibility Guidelines contrast ratio). Floors:
**4.5:1** for normal text, **3:1** for large text (≥18px regular
or ≥14px bold) and UI components.
### Dark theme
| Foreground | Background | Ratio | AA-normal | AA-large |
|---|---|---:|---|---|
| `text` | `bg` | 16.38 | PASS | PASS |
| `text` | `bg-elevated` | 15.35 | PASS | PASS |
| `text` | `bg-card` | 14.77 | PASS | PASS |
| `text` | `sidebar` | 15.90 | PASS | PASS |
| `text` | `nav-active` | 14.12 | PASS | PASS |
| `text` | `hover` | 14.44 | PASS | PASS |
| `text-secondary` | `bg` | 6.39 | PASS | PASS |
| `text-secondary` | `bg-elevated` | 5.99 | PASS | PASS |
| `text-secondary` | `bg-card` | 5.76 | PASS | PASS |
| `text-secondary` | `sidebar` | 6.20 | PASS | PASS |
| `text-secondary` | `hover` | 5.63 | PASS | PASS |
| `text-tertiary` | `bg` | 3.65 | **FAIL** | PASS |
| `text-tertiary` | `bg-elevated` | 3.42 | **FAIL** | PASS |
| `text-tertiary` | `bg-card` | 3.29 | **FAIL** | PASS |
| `text-tertiary` | `sidebar` | 3.54 | **FAIL** | PASS |
| `accent` | `bg` | 9.48 | PASS | PASS |
| `accent` | `bg-elevated` | 8.89 | PASS | PASS |
| `accent` | `bg-card` | 8.55 | PASS | PASS |
| `accent` | `sidebar` | 9.21 | PASS | PASS |
| `success` | `bg` | 9.75 | PASS | PASS |
| `success` | `bg-card` | 8.80 | PASS | PASS |
| `danger` | `bg` | 6.46 | PASS | PASS |
| `danger` | `bg-card` | 5.83 | PASS | PASS |
| `warning` | `bg` | 11.87 | PASS | PASS |
| `warning` | `bg-card` | 10.70 | PASS | PASS |
### Light theme
| Foreground | Background | Ratio | AA-normal | AA-large |
|---|---|---:|---|---|
| `text` | `bg` | 16.70 | PASS | PASS |
| `text` | `bg-elevated` | 15.58 | PASS | PASS |
| `text` | `bg-card` | 17.70 | PASS | PASS |
| `text` | `sidebar` | 15.85 | PASS | PASS |
| `text` | `nav-active` | 14.24 | PASS | PASS |
| `text` | `hover` | 14.64 | PASS | PASS |
| `text-secondary` | `bg` | 6.77 | PASS | PASS |
| `text-secondary` | `bg-elevated` | 6.32 | PASS | PASS |
| `text-secondary` | `bg-card` | 7.18 | PASS | PASS |
| `text-secondary` | `sidebar` | 6.43 | PASS | PASS |
| `text-secondary` | `hover` | 5.94 | PASS | PASS |
| `text-tertiary` | `bg` | 3.47 | **FAIL** | PASS |
| `text-tertiary` | `bg-elevated` | 3.24 | **FAIL** | PASS |
| `text-tertiary` | `bg-card` | 3.68 | **FAIL** | PASS |
| `text-tertiary` | `sidebar` | 3.30 | **FAIL** | PASS |
| `accent` | `bg` | 3.35 | **FAIL** | PASS |
| `accent` | `bg-elevated` | 3.12 | **FAIL** | PASS |
| `accent` | `bg-card` | 3.55 | **FAIL** | PASS |
| `accent` | `sidebar` | 3.18 | **FAIL** | PASS |
| `success` | `bg` | 3.98 | **FAIL** | PASS |
| `success` | `bg-card` | 4.22 | **FAIL** | PASS |
| `danger` | `bg` | 4.39 | **FAIL** | PASS |
| `danger` | `bg-card` | 4.65 | PASS | PASS |
| `warning` | `bg` | 2.56 | **FAIL** | **FAIL** |
| `warning` | `bg-card` | 2.72 | **FAIL** | **FAIL** |
### Severity ranking
1. **Light-theme `warning` on every surface** — fails AA-large too
(2.562.72:1). Anywhere this colour appears against `bg` or
`bg-card` is unreadable for low-vision users. Highest priority.
2. **Light-theme `accent` as link colour** — global `a { color:
var(--accent) }` rule at `colors_and_type.css:220` puts a
3.35:1 swatch on body text. Link semantics need AA-normal (4.5).
3. **`text-tertiary` in both themes** — used for `.k-caption` (12px),
`.k-eyebrow` (10px), `kbd` (11px) and many hint-text contexts
(399 class-name hits across the codebase). All small text. All
fail AA-normal in both themes.
4. **Light-theme `success` on bg / bg-card** — borderline (3.98 /
4.22). Fine if used only for icons / chips ≥18px, fails for
inline body copy.
5. **Light-theme `danger` on bg** — borderline (4.39); 4.65 on
bg-card. Fine if used for chips, marginal for inline.
### Suggested token shifts (estimates, eyeball before committing)
Numbers below are starting points, not finished colours. Re-run the
contrast script against any candidate before merging.
- `--text-tertiary` (light): `#8a8578` → roughly `#6f6a5c`. Aim
≥4.5:1 against `#faf8f5`.
- `--text-tertiary` (dark): `#716b60` → roughly `#888278`. Aim
≥4.5:1 against `#0f0e0c`.
- `--accent` (light): `#b87a4a` → roughly `#9a6535`. Aim ≥4.5:1 for
link role; UI / icon use already passes.
- `--warning` (light): `#b89a3e` → roughly `#8a7220`.
- `--success` (light) and `--danger` (light): nudge by ~510% if
you find them used as inline text during the walkthrough.
The Python contrast helper used to generate these tables is small
enough to keep inline if useful — it's at the bottom of this file.
## CI matrix state
Three workflows in `.github/workflows/`:
- `check.yml`. Runs `cargo check` on `ubuntu-22.04`,
`windows-latest`, `macos-latest` on every push to `main` and every
PR. Concurrent-cancels older runs on the same branch. **Should be
green at `0ca4e0e`** but cannot be confirmed from this machine
(`gh` CLI not installed). Worth eyeballing in the GitHub Actions
UI before declaring 10a done.
- `build.yml`. Full Tauri installer build (Linux .AppImage + .deb,
Windows .msi + .exe, macOS .dmg + .app). Triggers on tag push
`v*` or manual workflow_dispatch. **Has never been exercised
end-to-end.** First v0.1.0 tag will be its trial run. Strong
recommendation: `Run workflow` against `main` once before tagging,
to catch matrix-specific failures (Vulkan SDK on Windows,
libclang location on Linux 22.04, MoltenVK on macOS) without the
release artefact pressure.
- `audit.yml`. Weekly cargo + npm audit. Independent of v0.1.
## Clean-install test plan
Run on a spare user account or a fresh VM, with no prior Corbie /
Kon data. Three iterations: one per platform.
1. Install the artefact from the platform's `build.yml` output.
2. Launch from a clean shell (`corbie` from PATH, or the .app /
Start-menu shortcut).
3. Verify first-run setup flow renders. Walk through the Whisper /
LLM model download for the smallest tier.
4. Confirm app data lands at the expected path:
- Linux: `~/.local/share/kon/` (will become `~/.local/share/
corbie/` after Phase 10b rename).
- macOS: `~/Library/Application Support/com.corbel.kon/`.
- Windows: `%APPDATA%\com.corbel.kon\`.
5. Record a 10-second brain-dump → cleanup → task extraction.
Confirm no log leakage to stderr that references `target/` or
dev-only paths.
6. Quit the app. Open the SQLite db (`kon.db` for now) and verify
`SELECT version FROM schema_version ORDER BY version DESC LIMIT
1` returns `14`.
7. Re-launch. Confirm settings persist, history shows the test
transcript with manual + LLM tags.
8. Optional but recommended: launch with `RUST_LOG=debug` once and
archive the log. Anything referencing `/home/jake/Documents/
CORBEL-Projects/kon/target/` is a dev-leak bug.
For Phase 10c this gets re-run after the rename sweep to confirm
the migration shim correctly moves `~/.local/share/kon/`
`~/.local/share/corbie/` and renames `kon.db``corbie.db`.
## Walkthrough checklist (deferred from Phase 9d)
These need a running dev server, so they belong in Jake's testing
session:
- [ ] Keyboard-only traversal across every page. Tab order respects
visual order. No keyboard traps.
- [ ] Focus-visible ring shows on every focusable element. Zero
invisible focus states.
- [ ] WCAG AA contrast verified visually in both themes after any
token shifts from the suggestions above.
- [ ] Dark-mode parity check: every page renders correctly in
light + dark. No hard-coded greys that look fine in one theme
and broken in the other.
- [ ] Icon-only-button audit: hover reveals the title attribute or
the aria-label is announced by VoiceOver / Orca / Narrator.
- [ ] `prefers-reduced-motion: reduce` enabled in OS — sparkline
stagger, badge entrance, settings-group chevron all stop.
- [ ] Screen-reader smoke test: at least announce page titles,
primary-button labels, and the sparkline summary line on one
platform's SR.
## Appendix — contrast helper
Drop into `scripts/contrast.py` or run inline. Pass two hex strings
(with or without `#`); prints the AA verdict.
```python
def srgb_to_lin(c):
c = c / 255
return c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4
def luminance(hex_color):
h = hex_color.lstrip('#')
r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
return 0.2126 * srgb_to_lin(r) + 0.7152 * srgb_to_lin(g) + 0.0722 * srgb_to_lin(b)
def contrast(c1, c2):
l1, l2 = luminance(c1), luminance(c2)
return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05)
```
## Anchors
- Roadmap: [docs/roadmap/2026-04-23-corbie-feature-complete-roadmap.md](../../roadmap/2026-04-23-corbie-feature-complete-roadmap.md)
- Phase 9 spec: [docs/superpowers/specs/2026-04-24-phase9-polish-debt-design.md](../specs/2026-04-24-phase9-polish-debt-design.md)
- Release-blocker index: [docs/issues/README.md](../../issues/README.md)
- Latest handover: [HANDOVER.md](../../../HANDOVER.md)