# Validation Failure Triage Use this guide when the clean-checkout validation harness, CI workflow, or manual release commands fail. The goal is to fix root causes in source code, not to weaken the gates. ## General triage rules 1. Reproduce the failure from a clean working tree. 2. Keep the first failing command output; later failures may be cascading symptoms. 3. Do not remove tests, disable strict TypeScript, or loosen lint rules to make a release pass. 4. Prefer small, targeted fixes and rerun the failing gate before rerunning the full suite. 5. If a dependency API changed after version resolution, update the call site and document the confirmed version. 6. Preserve Playwright traces, screenshots, and the validation JSON report for regression analysis. ## Dependency installation failures ### Symptoms - `npm install` exits non-zero. - Peer dependency conflicts. - Native package or browser dependency install errors. - Vite, Playwright, or ESLint package resolution fails. ### Diagnosis Run: ```bash npm install --no-audit --fund=false --verbose npm ls --depth=0 ``` Check: - Node version is current enough for the resolved Vite and Playwright versions. - Only one package-manager lockfile is present. - No exact old toolchain pin is forcing an unsupported runtime. - Every imported third-party package appears in `package.json`. ### Fix strategy - Use Node 20 LTS or newer. - Keep dependency ranges flexible unless a specific upstream bug requires a temporary override. - Do not hand-edit generated lockfile internals. - If a package was used without being declared, add it to the correct dependency section. ## TypeScript failures ### Symptoms - Missing local exports. - React component props do not match. - Zustand store selectors reference removed fields. - Three.js/R3F types fail after dependency resolution. ### Diagnosis Run: ```bash npm run typecheck ``` If the script is absent: ```bash npx tsc -p tsconfig.json --noEmit ``` Check the first local source error before addressing library noise. Cross-file API drift is the most common cause: a component imports a symbol that the module no longer exports, or a store action signature changed without updating all callers. ### Fix strategy - Update the shared type/module and every caller in the same change. - Keep machine registry types aligned with viewer, catalogue, animation, and tour consumers. - Avoid `any` escape hatches; use discriminated unions or narrow helper functions where needed. - For browser globals in tests, add typed test setup rather than suppressing errors. ## ESLint and formatting failures ### Symptoms - Unused imports or variables. - Hook dependency warnings. - Accessibility lint warnings. - Formatting drift. ### Diagnosis Run: ```bash npm run lint ``` If a format-check script exists, run it as well. ### Fix strategy - Remove dead imports and unreachable code. - Memoize callbacks/selectors only where needed for correctness or performance. - Keep keyboard handlers accessible and scoped. - Let Prettier format files instead of manually fighting style rules. ## Vitest failures ### Symptoms - `window.matchMedia` missing. - Clipboard, WebGL, ResizeObserver, or IntersectionObserver unavailable. - React state updates not wrapped in `act`. - Lazy imports or Suspense tests time out. ### Diagnosis Run: ```bash npm run test:unit -- --run ``` If the configured test script already uses `vitest run`, do not add the extra `--run`. Check `src/test/setup.ts` for browser API shims. Validate that tests import components through the same public modules used by the app. ### Fix strategy - Add deterministic test shims for browser APIs that JSDOM does not implement. - Prefer user-visible assertions over implementation details. - For lazy-loaded components, await the fallback disappearing or the final text appearing. - Reset Zustand stores and localStorage between tests. ## Production build failures ### Symptoms - Vite cannot resolve an import. - Dynamic import paths fail. - Tailwind classes or CSS modules are missing. - Asset paths work in dev but not in production. - Bundle generation runs out of memory. ### Diagnosis Run: ```bash npm run build ``` Check: - Local import paths and file casing, especially on case-sensitive Linux runners. - Data-driven machine registry entries refer to existing modules/assets. - Environment variables use the `VITE_` prefix when read by client code. - No test-only modules are imported by production code. ### Fix strategy - Keep route-level and machine-level dynamic imports static enough for Vite to analyze. - Move large optional viewer code behind lazy boundaries. - Use `new URL('./asset.ext', import.meta.url).href` for bundled assets where appropriate. - Avoid importing Playwright/Vitest utilities into app source. ## Playwright failures ### Symptoms - Web server does not start. - Selectors time out. - Mobile viewport assertions fail. - Clipboard tests fail. - WebGL canvas is unavailable in headless CI. - Share-link restoration works locally but fails in CI. ### Diagnosis Run: ```bash npx playwright install --with-deps npm run test:e2e ``` For local debugging: ```bash npx playwright test --headed --debug ``` Check the Playwright HTML report and traces: ```bash npx playwright show-report ``` ### Fix strategy - Use stable accessible selectors: roles, labels, and visible names. - Ensure the app has deterministic loading states before tests interact. - Gate clipboard behavior behind user gestures and provide a fallback copy path. - Keep WebGL support fallback testable without requiring GPU acceleration. - Use URL assertions that tolerate encoded values but still verify restored state. - Confirm Playwright `webServer` points to the correct dev or preview command and port. ## URL sharing failures ### Symptoms - Copied link omits machine ID or view state. - Opening a share link resets the camera/explode/visibility state. - Unknown query values crash the viewer. - Back/forward navigation desynchronizes UI state. ### Diagnosis Check the share-state utility tests first, then reproduce in a browser: 1. Open a machine route. 2. Change camera preset, explode amount, part opacity/visibility, and display modes. 3. Copy the link. 4. Paste into a new tab. 5. Compare UI and scene state. ### Fix strategy - Keep URL encoding and decoding functions symmetrical. - Clamp numeric values during decoding. - Ignore unknown part IDs without throwing. - Use `history.replaceState` for frequent viewer-state changes and `pushState` only for navigational changes. - Keep social/meta tag updates independent from high-frequency camera updates. ## Reduced-motion failures ### Symptoms - Guided tours animate despite reduced-motion preference. - Framer Motion transitions continue after disabling motion. - Tests fail because `matchMedia('(prefers-reduced-motion: reduce)')` is not deterministic. ### Diagnosis Test both app-level preference and browser/OS media query. In automated tests, mock `matchMedia` explicitly. ### Fix strategy - Centralize motion preference reads in the motion preference utility/hook. - Use shorter or instant transitions when reduced motion is active. - Keep mechanical simulation stepping functional; reduced motion should reduce decorative motion, not break learning controls. - Avoid autoplaying tours for users with reduced motion enabled. ## Mobile and responsive failures ### Symptoms - Side panels overflow or trap focus. - Viewer controls are too small to tap. - Canvas height collapses. - Horizontal page scrolling appears. - Status bar covers primary controls. ### Diagnosis Run Playwright with mobile projects and manually inspect at widths 430, 390, and 375. Use browser devtools to identify fixed-width panels or unbounded grid columns. ### Fix strategy - Use responsive layout primitives instead of hard-coded desktop widths. - Keep controls at least 44 CSS pixels in touch layouts. - Prefer drawers/sheets for dense panels on mobile. - Ensure the canvas container has explicit min-height in every layout mode. - Test with browser UI zoom and dynamic viewport changes. ## WebGL fallback failures ### Symptoms - App crashes when WebGL is disabled. - Error boundary catches canvas errors but shows a generic message. - Fallback page is inaccessible by keyboard or screen reader. ### Diagnosis Use browser flags or test shims to force WebGL context creation to fail. Confirm the fallback renders before any R3F-specific code assumes a valid renderer. ### Fix strategy - Check WebGL support before mounting the heavy viewer when possible. - Keep the fallback independent from Three.js. - Provide clear actions: reload, return to catalogue, and open compatibility guidance. - Keep catalogue and documentation usable without WebGL. ## Social/meta failures ### Symptoms - Page title does not update when navigating machines. - Description or Open Graph image is stale. - Social crawlers see only default metadata. ### Diagnosis Inspect document head after navigation and validate deployed URLs with platform preview tools. ### Fix strategy - Update client-side metadata on route/machine changes. - Keep default meta tags in `index.html` for crawlers that do not execute JavaScript. - Use canonical absolute URLs in deployment configuration when available. - Avoid writing high-frequency viewer state into social metadata. ## Performance budget warnings ### Symptoms - The validation harness reports large JS chunks. - Mobile first interaction is slow. - Machine viewer loads catalogue code and all machine modules at once. ### Diagnosis Run a production build and inspect the dist metrics emitted by the validation harness. If needed, add a Vite bundle visualizer locally. ### Fix strategy - Preserve route-level lazy loading between catalogue and viewer. - Lazy-load machine animation/procedural modules. - Avoid importing all Three.js examples globally. - Keep GLB assets compressed and cacheable. - Use Suspense fallbacks that reserve layout space. ## When to rerun the full suite Rerun the complete validation command after any fix that touches: - Dependency manifest or Vite config. - Shared store or registry types. - Viewer controls, URL sharing, or keyboard shortcuts. - Test setup or Playwright config. - Production build, routing, or deployment config. - CSS/layout primitives that affect mobile or accessibility. Use: ```bash node scripts/validate-clean-checkout.mjs --install-playwright --json artifacts/validation-report.json ```