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

114 lines
8.2 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.
# Path-to-9 execution · PHASE D — the motion system (Pillar 1) + home scroll-reveal
### Contract: docs/ux-refactor/path-to-9-2026-07-07.md v3 §Pillar 1 (5/5 endorsed, incl. the
### PATH-2 conditions: default route tier + hero morphs, focus/AT spec IN the component,
### interruptibility as acceptance criteria, commissioned waggle-settle prototype-first).
### The unanimous R19 #1 gap. Highest regression risk → scoped, feature-flagged, staged.
### All motion uses lib/motion/tokens.ts. Both themes. Reduced-motion per REDUCED map.
Recon (verified): router = react-router `<Routes>` in App.tsx; the layout route `/`
= AppShell whose single `<Outlet/>` (AppShell.tsx:449) renders the active route inside
`<main class="relative z-10 flex-1 overflow-hidden">`. `location`, `useNavigate`,
`AnimatePresence` already imported. ChatHost keep-alive portal is SEPARATE from the
Outlet (must not be wrapped — it protects in-flight SSE). Phase-0 spring family +
`--mo-*` tokens + REDUCED map exist. Hover tier = the shipped `hive-interactive`-style
classes on cards (Wave T/U/V). AmbientHiveGlow shipped Phase C (home breath).
## STAGE 1 (parallel; the two heavy motion lanes are independent)
### Lane RT — route-transition system (Pillar 1.1; PATH-2 design blocking)
Files: NEW `components/os/RouteTransition.tsx` (+ test), `components/os/AppShell.tsx`
(wrap the Outlet only), `lib/feature-flags.ts` or a local flag (verify the flag pattern),
tests.
1. **DEFAULT tier — fade-through crossfade + persistent chrome** on ALL top-level route
changes: wrap `<Outlet/>` in `<AnimatePresence mode="popLayout">` keyed by a ROUTE-GROUP
key (NOT the full pathname — a workspace tab change within /workspaces/:id must not
crossfade the whole surface; derive the key from the top segment: home/workspaces/
memory/agents/marketplace/settings/…). Enter/exit = opacity fade-through (150200ms,
`--mo-base` + `EASE_OUT`); the sidebar + StatusBar are OUTSIDE the animated subtree
(persistent chrome — they never fade). NOT a global router rewrite.
2. **Interruptibility + input-primacy** (PATH-2 design blocking — ACCEPTANCE, tested):
a route change mid-transition redirects immediately (AnimatePresence popLayout allows
the new child in without waiting for exit); navigation/input NEVER waits on an exit
animation. Add a test: fire two navigations in quick succession → the final route wins,
no lock. Keep `mode="popLayout"` (not "wait") precisely so exits don't block enters.
3. **Focus + assistive-tech spec IN the component** (PATH-2 a11y blocking): on route
commit, move focus to the destination surface's primary heading / `<main>` landmark
(focus a `tabIndex={-1}` ref); set the EXITING subtree `inert`+`aria-hidden` for its
exit duration (focus/SR cursor can never land in it); announce the route via a polite
live region (route label). Ship these INSIDE RouteTransition, not as a follow-up.
4. **Reduced-motion**: crossfade → instant swap (REDUCED.routeTransition = 'crossfade-only'
means: under reduce, no opacity animation — instant), focus + announce still fire.
5. **Feature-flag** the whole thing (default ON in dev, but a kill switch) so a regression
is one flag flip. Verify the ChatHost keep-alive still works (SSE survives a route
change — do not wrap ChatHost).
### Lane HM — hero shared-element morphs (Pillar 1.1 hero cases)
Files: `apps/AllWorkspacesApp.tsx` + the workspace route/surface (`layoutId` on the card→
surface), `apps/memory/*` (Trust↔Memories tab morph), tests. Depends on Lane RT's
RouteTransition existing (Stage 1 both start; HM reads RT's exports — if RT isn't merged
yet, HM implements the layoutId pairs and notes the integration point).
1. **card→workspace-open**: the workspace card's hex avatar + name share a `layoutId` with
the destination workspace header, so opening a card GROWS it into the surface (framer
`layoutId` + `LayoutGroup`). Scope to this ONE pair; fall back to the default crossfade
if the destination header isn't mounted.
2. **Trust↔Memories tab morph**: the shared hero/container morphs between the two memory
tabs (layout animation on the tab panel), not a hard cut.
3. Interruptibility + reduced-motion (instant) as Lane RT item 2/4. Both morphs motion-safe.
## STAGE 2 (parallel; after Stage 1)
### Lane HV — hover amplitude: "honey responds to touch" (Pillar 1.2; brand HIGH ×3 rounds)
Files: the shared hover-tier class/util (grep where cards define hover — likely inline
Tailwind on AllWorkspacesApp/SuggestedAgentCards/ExtensionCard; if there's a shared
`hive-interactive` class in index.css, edit there; else create ONE and adopt), tests.
1. Bump the hover tier amplitude to a felt "honey blooms under the cursor": on hover/
focus-visible → lift (translate-y 2→4px), shadow step, honey border-warm, AND a soft
honey glow bloom (box-shadow with `--shadow-honey`/`--honey-glow`), 120160ms
`--mo-fast` + spring feel. ONE implementation adopted by workspace cards, agent cards,
marketplace rows, provider tiles. Reduced-motion: keep color/shadow, drop translate.
2. Keep the brand judge's WIN ("respectful, not gratuitous") — bloom is subtle, not neon.
Light theme: verify the glow reads on ivory (Phase-A light --honey-glow / deeper honey).
### Lane SR — home day-story scroll-reveal (Pillar 1.1 entrance; R20 design HIGH)
Files: `apps/HomeCockpit.tsx` (+ the home section components), tests.
1. The home "day story" (I REMEMBER → START HERE → memory-review → WHILE YOU SLEPT →
pick-up cards) reveals on scroll: each section rises 8px + fades in as it enters the
viewport (IntersectionObserver + the `card-enter` / STAGGER grammar), once per section
per visit. So the scripted scroll reads as designed motion, not a static page.
Reduced-motion: all visible immediately, no rise/fade. Do not re-trigger on scroll-up.
## STAGE 3 (the signature gesture — prototype-first, PATH-2 brand blocking)
### Lane WS — the waggle-settle, commissioned + prototyped STANDALONE
Files: NEW `components/os/warm/WaggleSettle.tsx` (+ test) + a `/motion-spec` section
demonstrating it on exactly ONE moment (memory saved). Do NOT propagate to other moments
in this phase — the prototype is judged first (a mini standalone review), THEN rolled out
per the Phase-0 SIGNATURE frequency taxonomy in a later step.
1. A recognizably waggle-derived micro-choreography: a brief directional waggle-run
(the bee's figure-eight dance DNA — a small side-to-side + forward settle) → settle,
~300400ms (DUR.settle, SPRING.expressive), honey. Reduced-motion: instant state +
color pulse (no path motion). It must read as "the waggle," not a generic scale-pop.
2. Wire it to ONE moment only: the memory-saved confirmation (find the save-memory
success path — grep). Render it there behind the SIGNATURE.full taxonomy gate
(per-session cooldown). Everything else waits for the standalone judge verdict.
## VERIFY STAGE (adversarial)
- **V1-motion-system**: RouteTransition — is the crossfade interruptible (the double-nav
test), is the focus/AT spec implemented (focus moves to heading, exiting tree inert,
route announced), does reduced-motion degrade to instant, does ChatHost SSE survive a
route change? Hero morphs — do the layoutId pairs animate (code-read + the 12fps
capture if available)?
- **V2-hover-scroll**: hover amplitude felt (inspect the class), light-theme glow reads,
reduced-motion drops translate; scroll-reveal fires once per section, reduced-motion off.
- **V3-a11y-regression**: the highest risk — a keyboard-only pass through 3 route changes
(focus lands on the destination heading each time, no focus trapped in an exiting tree,
route announced); reduced-motion Playwright pass asserting no route-transition opacity
animation under reduce.
## Orchestrator after verify
tsc web + full vitest + all ux-gates (contrast/text-guard/warm/runtime) + reduced-motion
gate + browser smoke (route changes crossfade, one card→workspace morph, hover bloom) →
commit → **standalone waggle-settle mini-judge** (is it "the waggle"? kitsch check) →
if approved, roll out per taxonomy → kit-v7 capture → **R22 judge (9-ATTEMPT: min ≥ 9.0)**.
If R22 min < 9 but ≥ 8.5, one convergent-fix round; if it plateaus, stop + re-analyze.