# Project Knowledge

Append-only register of project-specific rules, patterns, and lessons learned.
Agents read this before every unit. Add entries when you discover something worth remembering.

## Rules

| # | Scope | Rule | Why | Added |
|---|-------|------|-----|-------|

## Patterns

| # | Pattern | Where | Notes |
|---|---------|-------|-------|
| P001 | Test images generated programmatically via numpy | engine/tests/ | No fixture image files checked in. Tests create shapes (rectangles, circles) with numpy + cv2. Continue this pattern in S02/S03. |
| P002 | Tests must use .venv/bin/python -m pytest | engine/ | No system-wide `python` on PATH. Bare `python -m pytest` fails with exit 127. Always use the venv binary. |
| P003 | PostProcessResult is the universal intermediate representation | engine/pipeline/postprocess.py → engine/output/ | All output generators (DXF, JSON, SVG) consume PostProcessResult. Never generate output directly from raw SVG strings. |
| P004 | _format_response() for consistent multi-format API responses | engine/api/routes.py | DXF returns binary Response with X-Kerf-Metadata header; SVG/JSON return JSON envelopes. Use _format_response() for both /engine/trace and /engine/simplify. |
| P005 | Preset-driven pipeline: resolve_params() merges preset → user | engine/presets/loader.py, engine/api/routes.py | Presets define defaults for all pipeline stages. User params override. Mode comes from preset unless user explicitly sets it. |
| P006 | JSON.stringify for React hook dependency stabilization | app/src/hooks/useDebouncedTrace.ts | Object params in useEffect deps cause infinite loops (new ref each render). Stringify the params object and use the string in the deps array. Avoids requiring callers to useMemo. |
| P007 | Vite dev proxy for engine API calls | app/vite.config.ts | `/engine/*` requests proxy to `http://localhost:8000`. API client uses relative URLs (`/engine/presets`). No CORS issues in dev because same-origin via proxy. |
| P008 | CSS modules for views, global App.css for shared component styles | app/src/views/*.module.css, app/src/App.css | View-specific layout in CSS modules avoids conflicts. Shared styles (upload zone, preset cards, buttons) in App.css for cross-component reuse. |

## Lessons Learned

| # | What Happened | Root Cause | Fix | Scope |
|---|--------------|------------|-----|-------|
| L001 | pypotrace fails to build from pip | Requires system packages: `libpotrace-dev`, `libagg-dev`, `pkg-config` | `apt-get install -y libpotrace-dev libagg-dev pkg-config` before `pip install pypotrace` | engine build, Docker |
| L002 | VTracer Python bindings work directly — no subprocess needed | vtracer pip package exposes `convert_raw_image_to_svg()` that accepts PNG bytes | Use `vtracer.convert_raw_image_to_svg(png_bytes, img_format="png", ...)` | engine vectorize |
| L003 | pypotrace Bitmap requires uint32 data | Passing other dtypes (uint8, float) can cause segfaults | Always cast: `(img > 0).astype(np.uint32)` before `potrace.Bitmap(data)` | engine vectorize |
| L004 | ezdxf emits pyparsing deprecation warnings in tests | ezdxf uses deprecated pyparsing method names (`addParseAction`, `oneOf`, etc.) | These are harmless library warnings — don't try to fix them. Filter with `pytest -W ignore::DeprecationWarning` if noisy. | engine tests |
| L005 | DXF output format needs binary response, not JSON envelope | DXF files are binary/text data not suitable for JSON embedding | Use FastAPI `Response(content=bytes, media_type="application/dxf")` with metadata in `X-Kerf-Metadata` header | engine API |
| L006 | postprocess_svg() fully parses SVG paths into coordinates | XML parsing + path d-attribute parsing gives structured PathInfo objects | This replaces the old regex-based SVG metadata extraction (D003). All output generators (DXF, JSON, SVG) consume PostProcessResult. | engine pipeline |
| L007 | Docker build context must be project root, not engine/ | Dockerfile.engine uses `COPY engine/...` paths relative to project root | Always build with `docker build -f docker/Dockerfile.engine -t kerf-engine:dev .` from project root | Docker |
| L008 | Engine container has dual health endpoints | /health on root (main.py) and /engine/health on router (routes.py) both exist | Use /engine/health for Docker HEALTHCHECK and external probes for namespace consistency | engine API, Docker |
| L009 | useCallback-based triggerTrace caused infinite re-render loops | Object params create new references each render, retriggering useEffect even when values are unchanged | Restructure hook to use single useEffect with JSON.stringify(params) in dependency array instead of useCallback-based approach | app hooks, React |
| L010 | jest-canvas-mock crashes in Vitest with "ReferenceError: jest is not defined" | jest-canvas-mock internally calls jest.fn() in createImageBitmap.js — Jest globals don't exist in Vitest | Use vitest-canvas-mock (a Vitest-compatible fork) instead. Import it in test-setup.ts. | app tests, canvas |
