Files
waggle-os/docs/ux-refactor/wave-T-spec-2026-07-06.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

112 lines
7.6 KiB
Markdown
Raw Permalink 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.
# Wave T — R13-V1 convergent fixes: interaction hygiene + the missing motion layer
R13-V1 (video-regime baseline): design 7.0 · kw 7.1 · competitor 7.0 · a11y 7.0 ·
brand 7.3 (min 7.0, avg 7.08). Five systemic gaps named by 4-5 of 5 judges — this
wave ships all five. Evidence: scratchpad/video-r13/evidence (sheets + keyframes).
Same vocabulary + honesty contract as waves P-S. Surgical; both themes; testids/aria
survive. NO cross-lane files. Per-lane gate: related vitest green + eslint on touched
files. Orchestrator runs full gates + re-capture + R14-V2 after merge.
Judges' shared bar: "one perceivable hover/focus tier on every interactive element,
answered within ~100-160ms; zero wrong-state flashes; zero loading lies."
## Lane A — boot choreography + briefing bloom (all 5 judges)
Files: `os/AppShell.tsx`, `hooks/useOnboarding.ts`, `os/overlays/LoginBriefing.tsx`,
the BootScreen component (locate via AppShell render path), `index.html`, tests.
1. **Wizard flash for returning users** (s01 t≈4.0-4.5s): localStorage empty until the
async `/api/onboarding/status` P4 branch resolves → wizard mounts ~1s before home.
Hold the BootScreen until onboarding status is KNOWN (sync localStorage hit OR the
status fetch settles; cap the hold ~1.5s then fall through so a dead endpoint can't
brick boot). The wizard must never paint for a user the server knows is onboarded.
2. **Briefing opens as a ~4s bare spinner, then content pops unstaged** (design: "it
deserves a progressive reveal, not a spinner-then-dump"): prefetch the briefing
payload during BootScreen (boot already runs ~4s — fire loadBriefing's fetch at boot
start, cache at adapter/module level). Where prefetch hasn't landed, render 2 skeleton
highlight cards + 2 skeleton workspace rows (correct heights, shimmer) — never a
centered spinner in an empty box. When data arrives, stagger the recall cards in
(the existing per-card motion delays already do this — verify they fire on data
arrival, not only on mount). Keep the errored slim-row path exactly as-is.
3. **Boot pre-paint white frame**: set a dark background on `<html>` in `index.html`
(inline early style) so pre-hydration frames are hive-dark, not white.
4. **BootScreen ignores theme** (a11y/brand): when stored theme is light, boot in the
light palette — no dark→light whiplash at home mount.
5. **Light "Start Working" CTA parity** (a11y HIGH): in light theme the primary CTA
renders washed pale-honey with light text (<4.5:1). Same emphasis tier both themes:
saturated honey fill + dark text. Compute contrast in lane notes.
## Lane B — marketplace: NL promise bridge + metadata budget + row hover (4 judges)
Files: `apps/MarketplaceApp.tsx`, `apps/extend/ExtensionCard.tsx`, tests.
1. **NL dead-end** (brand HIGH: "the exact moment the brand should feel magical says
no"): when live keyword filtering finds nothing AND the query looks like natural
language (≥3 words), the empty state must bridge to the promised semantic search:
"Press Enter — Waggle matches skills to this job" + run it on Enter (the semantic
path already exists behind the Search button — wire, don't invent). Also stop the
first-keystroke full-list flash (debounce the filter ~150ms).
2. Metadata budget (R12): dedupe the repeated connector chip; cap visible tags at
3 + "+N" overflow chip (tooltip lists the rest). Two chip species only (Wave S
grammar — verify, don't rebuild).
3. Row hover tier: rest flat → hover/focus-visible = one elevation step (existing
shadow token) + primary action gains full contrast. 120-160ms ease-out.
## Lane C — workspace cards: grammar close-out (judged 8.0/7.5 — small lane)
Files: `apps/AllWorkspacesApp.tsx`, tests.
1. One-slot-order grammar audit: every card renders identical slot order/alignment
(identity → tags → preview → metrics); reserve, don't collapse, missing slots.
2. Duplicate-name chip → keep the pill but move the slug disambiguation into its
tooltip (R12: raw slug = data debris).
3. Kebab menu: drop the full-page dim/blur when the card actions menu opens —
modal-grade dimming is for modal-grade interruptions (design, low).
## Lane D — memory: loading truth + tab cache + count landing (all 5 judges)
Files: `apps/memory/*` (its surfaces/nav/hooks only), tests.
1. **Kill the false zero** (a11y: "a trust surface must not display wrong data even
for 3 seconds"): the hero stat renders skeleton/em-dash until the real count
arrives — `0` is a VALUE, never a loading state. Audit every count on the surface.
2. **Cache tab state**: Trust ↔ Memories switches must not re-fetch + re-spin; keep
fetched lists in state/cache keyed by tab for the session (staleness fine — the
surface has explicit refresh affordances elsewhere).
3. **Count-up landing** (brand): when the hero count first arrives, land it with a
~600ms count-up + settle; confidence rings draw in staggered. Honor
prefers-reduced-motion (instant set, no animation).
4. Triple-nav collapse (R12): fold the weakest of the three stacked nav tiers into a
compact control row. Do NOT remove destinations — only chrome.
## Lane E — chat: the daily surface answers the cursor (all 5 judges)
Files: `apps/ChatApp.tsx` + chat message components, tests.
1. **Message hover actions visible**: the two ~2:1 ghost icons under assistant
messages → a proper action row (copy/retry, 3:1 rest, 4.5:1 hover, slide/fade
reveal 120-160ms) that ALSO reveals on :focus-within (keyboard parity). Persistent
low-opacity hint at rest so the affordance is discoverable without hover.
2. **Composer chip hover/press states**: each chip (persona/memory/mode/model) gets a
visible hover step + pressed state; the existing send-ignition stays.
3. Entry loading: replace the bare "Loading workspace…" text with the thread skeleton
(message-shaped placeholders, correct rhythm).
## Lane F — agents surface + the signature motion (all 5 judges; brand HIGH ×2)
Files: `apps/AgentsApp.tsx`, `apps/agents/*`,
NEW shared `components/ui/BeeLoader.tsx` (+ its test) — the ONLY new file this wave.
1. **Hover tier on bee cards + "Browse all 22" strip** (design: "65 seconds of
hovering, pixel-identical frames"): workspace-card grammar (elevation step + honey
top hairline + affordance strengthen), 120-160ms, focus-visible parity.
2. **The bees respond** (brand: "the product's soul never moves"): on card hover the
bee avatar gets a ~200ms character response — a 2-3° tilt + 2px lift of the PNG
(transform only, no layout shift), settle on leave. Honor prefers-reduced-motion.
3. **BeeLoader — one signature loader**: a small waggle-dance loader (the bee mark
tracing a figure-eight waggle path, CSS/SVG only, ~1.2s loop, honey on transparent)
as a drop-in replacement for generic arc spinners. Ship the component + use it for
the agents surface's own loading states. (Other surfaces adopt it next wave — no
cross-lane files.)
4. Dark agents search-input border (R12): visible `--line` border + focus ring,
consistent with marketplace's labeled search.
## Deferred (do not touch this wave)
Settings Show-containment + light input tokens (R12; settings judged 8.5 best-in-app),
www landing diagram (not in the judged journeys), memory row-action focus parity beyond
chat's pattern (ride next wave), theme-switch "atomicity" work beyond Lane A items 3-5
(the reload/white-flash/mixed-state evidence was a capture artifact — kit v2 fixed).
## Per-lane gate
Related vitest green + eslint; NO cross-lane files; lane notes list files touched +
contrast math where required. Orchestrator after merge: web tsc, server tsc, full
vitest, browser QA pass, then R14-V2 capture (kit v2) + judge.