# 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
```
