moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
# Path-to-9 execution · PHASE A — motion vocabulary (Phase-0) + AA/affordance floor (Pillar 4)
### Contract: docs/ux-refactor/path-to-9-2026-07-07.md (v3, 5/5 panel-endorsed). Same honesty
### rules as waves PX: surgical, both themes, testids/aria survive, reduced-motion gated.
Two sequential stages (stage 2 depends on stage 1's exports). Per-lane gate: related
vitest green + eslint 0 errors on touched files. NO benchmarks/**, NO packages/hive-mind-*.
## STAGE 1 (parallel lanes)
### Lane M — motion tokens + spring family + spec page (Phase-0 items 1,2,3,5,7)
NEW files only + index.css additions. Files: `apps/web/src/lib/motion/tokens.ts` (new),
`apps/web/src/lib/motion/tokens.test.ts` (new), `apps/web/src/pages/MotionSpec.tsx`
(new, DEV-only route), route wiring (find the router config; gate the route on
`import.meta.env.DEV`), `apps/web/src/index.css` (append motion custom props ONLY).
1. **The exported vocabulary** (`lib/motion/tokens.ts`) — THE single motion source:
```ts
export const SPRING = {
micro: { type: 'spring', stiffness: 550, damping: 35 }, // chips, presses, ≤150ms feel
standard: { type: 'spring', stiffness: 380, damping: 30 }, // hovers, panel/route fades
expressive: { type: 'spring', stiffness: 260, damping: 24 }, // hero morphs, settle gesture
} as const;
export const DUR = { fast: 0.15, base: 0.2, slow: 0.32, settle: 0.4 } as const; // seconds
export const EASE_OUT = [0.22, 1, 0.36, 1] as const; // cubic-bezier for non-spring CSS
export const STAGGER = { list: 0.04, brief: 0.08 } as const; // Wave W's 40ms / briefing 80ms
/** Signature-moment frequency taxonomy (Phase-0.7): FULL settle only on rare
* accrual milestones; MICRO for high-frequency actions; per-session cooldown. */
export const SIGNATURE = {
full: { moments: ['memory-saved-first-of-session', 'install-success', 'agent-spawned'], durS: DUR.settle, perSessionCooldownMs: 60_000 },
micro: { moments: ['send-arm', 'selection'], durS: DUR.fast },
} as const;
/** Reduced-motion mapping per tier (Phase-0.3) — documented AND consumed. */
export const REDUCED = {
routeTransition: 'crossfade-only', hover: 'color-shadow-only-no-transform',
settle: 'instant-state-color-pulse', streamingCaret: 'static',
countUp: 'instant-set', ambient: 'off',
} as const;
```
Tune values to FEEL right (the numbers above are starting points — validate on the
spec page); all three springs must share one physical character (same "material").
2. **CSS custom props** appended to index.css `:root` (both themes inherit — motion is
theme-invariant): `--mo-fast: 150ms; --mo-base: 200ms; --mo-slow: 320ms;
--mo-settle: 400ms; --mo-ease: cubic-bezier(0.22,1,0.36,1);` Nothing else in this file.
3. **Motion-spec page** (`/motion-spec`, DEV-only): sections demonstrating each spring
variant (click-to-replay), hover tier on a sample card, entrance stagger, exit
choreography (AnimatePresence in/out samples), the reduced-motion mapping table, and
an INVENTORY table of already-shipped motion (see Lane R's list) with its migration
status. This page is the arc's reviewable single source of motion truth.
4. Tests: token invariants (all springs share type, DUR ascending, SIGNATURE moments
disjoint, REDUCED covers every tier key).
### Lane T — text-tertiary + focus-ring token tiers + offender migration (Pillar 4 items 1 + 3)
Files: `apps/web/src/index.css` (token definitions ONLY — coordinate with Lane M: Lane M
appends motion props, Lane T appends color tokens; both are pure additions in different
blocks, no shared lines), `os/StatusBar.tsx`, `overlays/onboarding/WelcomeStep.tsx`,
`overlays/LoginBriefing.tsx` ("Don't show again"), plus the specific offender usages
listed below. Tests updated where classes are asserted.
1. **`--text-tertiary`**: a tier that is ≥4.5:1 on EVERY surface it appears over
(--bg, --bg-2, --surface, --surface-2) in BOTH themes. Compute (WCAG relative
luminance) and document the math in a comment next to the token. Dark: needs
≥ #a3987f-level; light: ≤ #6e6552-level. Verify per pair; show ratios.
2. **`--focus-ring` + `--line-affordance`** (Pillar 4.3, WCAG 1.4.11): non-text tier
≥3:1 against adjacent effective background BOTH themes — the light-theme honey ring
is the known risk (honey-on-ivory); compute and pick the light value accordingly
(e.g. a darker honey/ochre for light). Document ratios in-comment.
3. **Migrate the recurring offender families** onto --text-tertiary (verify each is
still a REAL failure first — several were fixed in Waves V/X; do not churn
compliant code): top utility bar (StatusBar plan/search/date cluster), onboarding
subtitle + privacy footnote ("Skip setup" was fixed — verify), "Don't show again",
unselected onboarding chips (raise rest contrast), card timestamps if any remain
sub-AA. Compute before/after ratios in the lane report.
### Lane G — the CI gates (Pillar 4 item 2 + verification plumbing)
NEW files: `scripts/ux-gates/contrast-tokens.mjs`, `scripts/ux-gates/text-color-guard.mjs`,
`scripts/ux-gates/contrast-runtime.mjs`, `scripts/ux-gates/README.md`; `package.json`
(root): three npm scripts (`ux:contrast`, `ux:color-guard`, `ux:contrast-runtime`).
Do NOT touch .github/workflows (wiring into CI is a follow-up once scripts are proven).
1. **contrast-tokens.mjs**: parse index.css (+ waggle-theme.css if it defines text
tokens), compute WCAG ratios for every (text-token × surface-token) pair in both
themes, assert the documented floors (--text/--text-2/--text-muted/--text-tertiary
≥4.5:1 on their allowed surfaces; --focus-ring/--line-affordance ≥3:1). Exit 1 on
failure with a table. Must run green against Lane T's final tokens.
2. **text-color-guard.mjs** (the generation-vector ban): scan apps/web/src (app
surfaces; exclude tests, the tokens file, MotionSpec) for NEW non-token text
colors: `text-[#`, raw hex in inline `style` color/background of text elements,
`text-hive-*` palette classes, and opacity-modified text tokens BELOW the safe
floor (`text-[var(--text-dim)]/40` style patterns; allow ≥/60 with a warning
list). Baseline file (`scripts/ux-gates/color-guard-baseline.json`) freezes
today's grandfathered instances; the gate fails only on NEW instances — a
ratchet, not a big-bang.
3. **contrast-runtime.mjs** (composition-aware, Playwright): against the dev server,
for each judged surface (/home, /workspaces, /memory, /agents, /marketplace,
/settings, chat), walk visible text nodes, compute EFFECTIVE fg/bg (composite
opacity up the tree; sample wallpaper/gradient via screenshot pixel at element
center when bg is an image), report all <4.5:1 (text) and <3:1 (focus indicators
— tab to 10 interactive elements per surface and measure the ring). Output a
JSON + human table; exit 1 on NEW failures vs a baseline. Seed localStorage
onboarding like the capture kit; run both themes.
## STAGE 2 (parallel lanes; imports from Lane M's tokens — stage 1 merged first)
### Lane R1 — retrofit: cards + entrances (Phase-0.6)
Files: `apps/AllWorkspacesApp.tsx`, `apps/agents/SuggestedAgentCards.tsx`,
`apps/extend/ExtensionCard.tsx`, their tests.
Migrate every duration/easing to the vocabulary: hover transitions → `--mo-fast/--mo-base`
+ `--mo-ease` (or SPRING.standard where framer-motion), entrance stagger → STAGGER.list
+ DUR.slow, lift/bloom timings → tokens. Acceptance: `grep -E "duration-(75|100|150|200|300|500)|duration: '?0\.[0-9]|ease-(linear|in-out|out)\b"` on these files returns
ONLY token-backed or justified-in-comment instances; zero raw magic numbers.
### Lane R2 — retrofit: memory + briefing + boot + theme (Phase-0.6)
Files: `apps/memory/MemoryTrustManage.tsx`, `apps/memory/MemoryCenterTab.tsx`,
`os/overlays/LoginBriefing.tsx`, `os/BootScreen.tsx`, `providers/ThemeProvider.tsx`
(+ index.css `.theme-transition` duration → var), tests.
Same acceptance as R1. The 600ms count-up, 360ms crossfade, briefing staggers, boot
phase timings all move onto DUR/STAGGER/custom props (keep the VALUES if they feel
right — tokenize, don't retune; note any value you deliberately change).
### Lane R3 — retrofit: chat + status chrome + keyframe sweep (Phase-0.6)
Files: `apps/ChatApp.tsx`, `os/StatusBar.tsx` (transition durations only — Lane T owns
its colors), `apps/WorkspaceDesktopApp.tsx`, index.css @keyframes durations where
referenced with magic numbers in components, tests.
Same acceptance. Also produce THE INVENTORY: a table (append to the motion-spec page's
inventory section — coordinate: R3 owns the final inventory content) of every motion
in the judged surfaces: name, file, tier, duration token, reduced-motion behavior.
## VERIFY STAGE (after stage 2; parallel)
- **Reviewer V1 (motion)**: adversarially verify Phase-0 acceptance — grep the judged
surfaces for non-token durations/easings (list any survivor with justification
status), confirm the spec page renders every tier, confirm reduced-motion mappings
are implemented not just documented (inspect the code paths).
- **Reviewer V2 (floor)**: run `node scripts/ux-gates/contrast-tokens.mjs` and
`node scripts/ux-gates/text-color-guard.mjs`; start dev servers if needed and run
`contrast-runtime.mjs` on at least /home + /settings both themes; report the tables.
Verify Lane T's in-comment math independently (recompute 3 spot pairs).
## Orchestrator after verify
Full tsc (web), full apps/web vitest, eslint on all touched files, browser smoke
(motion-spec page renders; one hover; theme flip), commit per stage or as one Phase-A
commit. Then Phase B (Pillar 2) spec.
## Deferred in Phase A (explicitly)
Competitive teardown side-by-sides (needs founder's Claude/ChatGPT sessions — flagged,
not skipped silently); .github/workflows wiring (after scripts prove stable); the
waggle-settle prototype (Pillar 1.3 — its own commissioned mini-arc later).