# GSD Report — kerf-engine — M002

## Summary

kerf-engine is 67% complete across 3 milestones. $41.85 spent. Currently executing M003/S01.

2/3Milestones
6/9Slices
planningPhase
$41.85Cost
51.67MTokens
2h 0mDuration
986Tool calls
37Units
3Remaining
17.6/hrRate
$6.98Cost/slice
52.4kTokens/tool
100.0%Cache hit
M002Scope
67%
Executing M003/S01 — Export Flow (View 3) + DXF Generation
ETA: ~10m 15s remaining (3 slices at 17.6/hr)

## Blockers

No blockers or high-risk items found.

## Progress

M001 Kerf Engine — Raster-to-Vector Pipeline & API 3/3
S01 Core Pipeline — Preprocessing + Vectorization high — dependency installation, OpenCV+Potrace+VTracer integration critical
untested
S02 Post-Processing + Output Formats (SVG, DXF, JSON) high — DXF generation quality is hard to validate programmatically S01 critical
provides: postprocess_svg() function for RDP simplification + island detection + node countingprovides: Output generators: generate_dxf(), generate_json(), generate_svg()provides: /engine/simplify endpoint for SVG-to-SVG/DXF/JSON simplificationprovides: output_format routing on /engine/trace (svg, dxf, json)provides: _format_response() pattern for consistent multi-format responsesrequires: Core pipeline: preprocessing + potrace_trace() + vtracer_trace() producing raw SVG output
passed
Decisions

- DXF output as raw bytes with application/dxf content-type and metadata in X-Kerf-Metadata header
- postprocess_svg() replaces regex metadata extraction — full XML path parsing for structured PathInfo objects
- _format_response() shared helper for consistent response shaping across endpoints
- Islands placed on separate ISLANDS layer in DXF for downstream CAM tool compatibility
- Bezier curves linearized during post-processing for RDP simplification and DXF polyline generation

Patterns

- PostProcessResult as the universal intermediate representation consumed by all output generators
- Output generators are pure functions: PostProcessResult → bytes/string, no side effects
- _format_response() pattern for consistent multi-format API responses with metadata

S03 Preset System + Engine Docker Packaging low — presets are config files; Docker packaging is well-understood S02 critical
provides: Preset system with 5 tuned configs and merge-based param resolutionprovides: GET /engine/presets endpointprovides: Docker image kerf-engine:dev with healthcheckprovides: GET /engine/health endpointrequires: Post-processing pipeline and output format generators (SVG, DXF, JSON) consumed by preset-driven trace flow
passed
Decisions

- Preset default is 'sign' — covers the most common use case
- Presets use flat JSON with three sections (preprocessing, vectorization, postprocessing)
- resolve_params merges preset → user_params with user taking precedence
- Custom preset has empty param sections so pipeline defaults apply unless user provides overrides
- Multi-stage Docker build separates build deps from runtime (smaller image, no compiler tools)
- Engine image contains only engine source — enforces Engine/App separation (D001)
- Health endpoint at /engine/health for namespace consistency with other /engine/* routes

Patterns

- Preset-driven pipeline configuration: presets define defaults, user params override
- Multi-stage Docker build pattern for Python+C extension packages (pypotrace)
- Dual health endpoints: /health (root) for simple checks, /engine/health (namespaced) for Docker/orchestration

M002 M002: React Frontend — Import & Convert UI + Design Canvas 3/3
S01 Import & Convert UI (View 1) medium — debounced preview updates, Engine API integration from browser critical
untested
S02 Design Canvas Core (View 2) medium — Konva.js setup, selection handles, undo/redo state management S01 critical
provides: postprocess_svg() function for RDP simplification + island detection + node countingprovides: Output generators: generate_dxf(), generate_json(), generate_svg()provides: /engine/simplify endpoint for SVG-to-SVG/DXF/JSON simplificationprovides: output_format routing on /engine/trace (svg, dxf, json)provides: _format_response() pattern for consistent multi-format responsesrequires: Core pipeline: preprocessing + potrace_trace() + vtracer_trace() producing raw SVG output
passed
Decisions

- DXF output as raw bytes with application/dxf content-type and metadata in X-Kerf-Metadata header
- postprocess_svg() replaces regex metadata extraction — full XML path parsing for structured PathInfo objects
- _format_response() shared helper for consistent response shaping across endpoints
- Islands placed on separate ISLANDS layer in DXF for downstream CAM tool compatibility
- Bezier curves linearized during post-processing for RDP simplification and DXF polyline generation

Patterns

- PostProcessResult as the universal intermediate representation consumed by all output generators
- Output generators are pure functions: PostProcessResult → bytes/string, no side effects
- _format_response() pattern for consistent multi-format API responses with metadata

S03 Text System + Font Loading medium — opentype.js integration, font loading from volume, path extraction accuracy S02 critical
provides: Preset system with 5 tuned configs and merge-based param resolutionprovides: GET /engine/presets endpointprovides: Docker image kerf-engine:dev with healthcheckprovides: GET /engine/health endpointrequires: Post-processing pipeline and output format generators (SVG, DXF, JSON) consumed by preset-driven trace flow
passed
Decisions

- Preset default is 'sign' — covers the most common use case
- Presets use flat JSON with three sections (preprocessing, vectorization, postprocessing)
- resolve_params merges preset → user_params with user taking precedence
- Custom preset has empty param sections so pipeline defaults apply unless user provides overrides
- Multi-stage Docker build separates build deps from runtime (smaller image, no compiler tools)
- Engine image contains only engine source — enforces Engine/App separation (D001)
- Health endpoint at /engine/health for namespace consistency with other /engine/* routes

Patterns

- Preset-driven pipeline configuration: presets define defaults, user params override
- Multi-stage Docker build pattern for Python+C extension packages (pypotrace)
- Dual health endpoints: /health (root) for simple checks, /engine/health (namespaced) for Docker/orchestration

M003 M003 0/3 critical path
S01 Export Flow (View 3) + DXF Generation high — DXF scale accuracy and geometry quality critical
untested
S02 Docker Packaging + README low — Docker packaging is well-understood pattern S01 critical
provides: postprocess_svg() function for RDP simplification + island detection + node countingprovides: Output generators: generate_dxf(), generate_json(), generate_svg()provides: /engine/simplify endpoint for SVG-to-SVG/DXF/JSON simplificationprovides: output_format routing on /engine/trace (svg, dxf, json)provides: _format_response() pattern for consistent multi-format responsesrequires: Core pipeline: preprocessing + potrace_trace() + vtracer_trace() producing raw SVG output
passed
Decisions

- DXF output as raw bytes with application/dxf content-type and metadata in X-Kerf-Metadata header
- postprocess_svg() replaces regex metadata extraction — full XML path parsing for structured PathInfo objects
- _format_response() shared helper for consistent response shaping across endpoints
- Islands placed on separate ISLANDS layer in DXF for downstream CAM tool compatibility
- Bezier curves linearized during post-processing for RDP simplification and DXF polyline generation

Patterns

- PostProcessResult as the universal intermediate representation consumed by all output generators
- Output generators are pure functions: PostProcessResult → bytes/string, no side effects
- _format_response() pattern for consistent multi-format API responses with metadata

S03 Embed Mode medium — Shadow DOM + Konva.js + font loading interactions S02 critical
provides: Preset system with 5 tuned configs and merge-based param resolutionprovides: GET /engine/presets endpointprovides: Docker image kerf-engine:dev with healthcheckprovides: GET /engine/health endpointrequires: Post-processing pipeline and output format generators (SVG, DXF, JSON) consumed by preset-driven trace flow
passed
Decisions

- Preset default is 'sign' — covers the most common use case
- Presets use flat JSON with three sections (preprocessing, vectorization, postprocessing)
- resolve_params merges preset → user_params with user taking precedence
- Custom preset has empty param sections so pipeline defaults apply unless user provides overrides
- Multi-stage Docker build separates build deps from runtime (smaller image, no compiler tools)
- Engine image contains only engine source — enforces Engine/App separation (D001)
- Health endpoint at /engine/health for namespace consistency with other /engine/* routes

Patterns

- Preset-driven pipeline configuration: presets define defaults, user params override
- Multi-stage Docker build pattern for Python+C extension packages (pypotrace)
- Dual health endpoints: /health (root) for simple checks, /engine/health (namespaced) for Docker/orchestration

## Timeline

# | Type | ID | Model | Started | Duration | Cost | Tokens | Tools | Tier | Routed | Trunc | CHF
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
1 | execute-task | M001/S01/T01 | opus-4-6 | Mar 26, 2026, 04:00 AM | 6m 19s | $1.14 | 1.31M | 39 |  |  |  |
2 | execute-task | M001/S01/T02 | opus-4-6 | Mar 26, 2026, 04:07 AM | 3m 41s | $0.976 | 1.03M | 30 |  |  |  |
3 | execute-task | M001/S01/T03 | opus-4-6 | Mar 26, 2026, 04:11 AM | 3m 59s | $1.12 | 1.29M | 34 |  |  |  |
4 | execute-task | M001/S01/T04 | opus-4-6 | Mar 26, 2026, 04:15 AM | 3m 29s | $0.0000 | 0 | 0 |  |  |  |
5 | execute-task | M001/S01/T05 | opus-4-6 | Mar 26, 2026, 04:18 AM | 4m 8s | $1.37 | 1.35M | 40 |  |  |  |
6 | complete-slice | M001/S01 | opus-4-6 | Mar 26, 2026, 04:22 AM | 3m 19s | $0.734 | 655.6k | 21 |  |  |  |
7 | complete-slice | M001/S01 | opus-4-6 | Mar 26, 2026, 04:25 AM | 2m 4s | $0.796 | 881.5k | 30 |  |  |  |
8 | execute-task | M001/S02/T01 | opus-4-6 | Mar 26, 2026, 04:28 AM | 4m 27s | $1.42 | 1.43M | 29 |  |  |  |
9 | execute-task | M001/S02/T03 | opus-4-6 | Mar 26, 2026, 04:37 AM | 2m 33s | $1.12 | 833.4k | 20 |  |  |  |
10 | complete-slice | M001/S02 | opus-4-6 | Mar 26, 2026, 04:39 AM | 2m 8s | $0.572 | 626.3k | 17 |  |  |  |
11 | execute-task | M001/S03/T01 | opus-4-6 | Mar 26, 2026, 04:42 AM | 3m 51s | $1.44 | 1.64M | 31 |  |  |  |
12 | execute-task | M001/S03/T02 | opus-4-6 | Mar 26, 2026, 04:45 AM | 3m 45s | $1.52 | 2.14M | 35 |  |  |  |
13 | complete-slice | M001/S03 | opus-4-6 | Mar 26, 2026, 04:49 AM | 2m 25s | $0.682 | 870.9k | 20 |  |  |  |
14 | validate-milestone | M001 | opus-4-6 | Mar 26, 2026, 04:52 AM | 2m 30s | $1.29 | 1.96M | 16 |  |  |  |
15 | complete-milestone | M001 | opus-4-6 | Mar 26, 2026, 04:54 AM | 2m 33s | $0.946 | 1.33M | 22 |  |  |  |
16 | research-slice | M002/S01 | opus-4-6 | Mar 26, 2026, 04:57 AM | 2m 26s | $0.812 | 1.02M | 28 |  |  |  |
17 | plan-slice | M002/S01 | opus-4-6 | Mar 26, 2026, 04:59 AM | 2m 35s | $0.760 | 842.6k | 17 |  |  |  |
18 | execute-task | M002/S01/T01 | opus-4-6 | Mar 26, 2026, 05:02 AM | 3m 12s | $1.34 | 1.98M | 37 |  |  |  |
19 | execute-task | M002/S01/T02 | opus-4-6 | Mar 26, 2026, 05:05 AM | 2m 5s | $0.787 | 931.1k | 23 |  |  |  |
20 | execute-task | M002/S01/T03 | opus-4-6 | Mar 26, 2026, 05:07 AM | 8m 6s | $2.90 | 3.97M | 56 |  |  |  |
21 | execute-task | M002/S01/T04 | opus-4-6 | Mar 26, 2026, 05:15 AM | 2m 4s | $0.973 | 1.33M | 25 |  |  |  |
22 | complete-slice | M002/S01 | opus-4-6 | Mar 26, 2026, 05:17 AM | 2m 32s | $0.850 | 1.12M | 15 |  |  |  |
23 | research-slice | M002/S02 | opus-4-6 | Mar 26, 2026, 05:20 AM | 3m 31s | $1.29 | 1.71M | 38 |  |  |  |
24 | plan-slice | M002/S02 | opus-4-6 | Mar 26, 2026, 05:23 AM | 2m 52s | $0.861 | 959.7k | 20 |  |  |  |
25 | execute-task | M002/S02/T01 | opus-4-6 | Mar 26, 2026, 05:26 AM | 5m 17s | $2.03 | 2.61M | 40 |  |  |  |
26 | execute-task | M002/S02/T02 | opus-4-6 | Mar 26, 2026, 05:32 AM | 4m 9s | $1.76 | 2.24M | 38 |  |  |  |
27 | execute-task | M002/S02/T03 | opus-4-6 | Mar 26, 2026, 05:36 AM | 3m 51s | $1.49 | 1.61M | 25 |  |  |  |
28 | execute-task | M002/S02/T04 | opus-4-6 | Mar 26, 2026, 05:40 AM | 1m 25s | $0.819 | 1.11M | 17 |  |  |  |
29 | complete-slice | M002/S02 | opus-4-6 | Mar 26, 2026, 05:41 AM | 2m 19s | $0.535 | 483.4k | 11 |  |  |  |
30 | research-slice | M002/S03 | opus-4-6 | Mar 26, 2026, 05:44 AM | 2m 44s | $1.16 | 1.46M | 37 |  |  |  |
31 | plan-slice | M002/S03 | opus-4-6 | Mar 26, 2026, 05:46 AM | 2m 10s | $0.714 | 677.4k | 18 |  |  |  |
32 | execute-task | M002/S03/T01 | opus-4-6 | Mar 26, 2026, 05:48 AM | 4m 6s | $1.50 | 2.05M | 29 |  |  |  |
33 | execute-task | M002/S03/T02 | opus-4-6 | Mar 26, 2026, 05:53 AM | 2m 40s | $1.36 | 1.86M | 30 |  |  |  |
34 | execute-task | M002/S03/T03 | opus-4-6 | Mar 26, 2026, 05:55 AM | 2m 20s | $1.28 | 1.67M | 23 |  |  |  |
35 | complete-slice | M002/S03 | opus-4-6 | Mar 26, 2026, 05:58 AM | 2m 39s | $0.994 | 1.28M | 16 |  |  |  |
36 | validate-milestone | M002 | opus-4-6 | Mar 26, 2026, 06:00 AM | 2m 34s | $0.913 | 1.08M | 28 |  |  |  |
37 | complete-milestone | M002 | opus-4-6 | Mar 26, 2026, 06:03 AM | 3m 54s | $1.57 | 2.34M | 31 |  |  |  |

## Dependencies

### M001: Kerf Engine — Raster-to-Vector Pipeline & API

done active pending parked

### M002: M002: React Frontend — Import & Convert UI + Design Canvas

done active pending parked

### M003: M003

done active pending parked

## Metrics

$41.85Total cost
51.67MTotal tokens
1.7kInput
321.5kOutput
49.94MCache read
1.41MCache write
2h 0mDuration
37Units
986Tool calls
0Truncations

### Token breakdown

Input: 1.7k (0.0%)Output: 321.5k (0.6%)Cache read: 49.94M (96.6%)Cache write: 1.41M (2.7%)

### Cost over time

### Cost by phase

research
$3.26
3 units
planning
$2.33
3 units
execution
$31.09
24 units
completion
$5.16
7 units

### Tokens by phase

research
4.19M
$3.26
planning
2.48M
$2.33
execution
39.09M
$31.09
completion
5.91M
$5.16

### Cost by slice

M001
$2.23
2 units
M001/S01
$6.14
7 units
M001/S02
$3.11
3 units
M001/S03
$3.65
3 units
M002
$2.48
2 units
M002/S01
$8.43
7 units
M002/S02
$8.79
7 units
M002/S03
$7.02
6 units

### Cost by model

opus-4-6
$41.85
37 units

### Duration by slice

M001
5m 3s
$2.23
M001/S01
27m 2s
$6.14
M001/S02
9m 9s
$3.11
M001/S03
10m 2s
$3.65
M002
6m 28s
$2.48
M002/S01
23m 3s
$8.43
M002/S02
23m 27s
$8.79
M002/S03
16m 40s
$7.02

### Slice timeline

## Health

Token profile | standard
Truncation rate | 0.0% per unit (0 total)
Continue-here rate | 0.0% per unit (0 total)
Tool calls | 986
Messages | 608 assistant / 3 user

### Tier breakdown

Tier | Units | Cost | Tokens
--- | --- | --- | ---
unknown | 37 | $41.85 | 51.67M

## Changelog 6

M002/S03 Text System + Font Loading Mar 26, 2026, 06:00 AM

Added text objects to the design canvas with fontService (opentype.js-powered font loading, caching, text-to-path conversion), text tool in toolbar, text-specific property controls, and Convert to Paths action — 24 fontService tests + all 95 app tests pass

Decisions

- Preset default is 'sign' — covers the most common use case
- Presets use flat JSON with three sections (preprocessing, vectorization, postprocessing)
- resolve_params merges preset → user_params with user taking precedence
- Custom preset has empty param sections so pipeline defaults apply unless user provides overrides
- Multi-stage Docker build separates build deps from runtime (smaller image, no compiler tools)
- Engine image contains only engine source — enforces Engine/App separation (D001)
- Health endpoint at /engine/health for namespace consistency with other /engine/* routes

14 files modified

- `app/src/utils/fontService.ts` — New — font loading, caching, text-to-path conversion service using opentype.js
- `app/src/utils/__tests__/fontService.test.ts` — New — 24 unit tests for fontService (registry, loading, caching, path conversion)
- `app/public/fonts/Roboto-Regular.ttf` — New — bundled OFL-licensed Roboto variable font
- `app/public/fonts/OpenSans-Regular.ttf` — New — bundled OFL-licensed Open Sans variable font
- `app/public/fonts/Lato-Regular.ttf` — New — bundled OFL-licensed Lato font
- `app/src/App.css` — Modified — added @font-face declarations for Roboto, Open Sans, Lato
- `app/package.json` — Modified — added opentype.js v1.3.4 dependency
- `app/src/types/canvas.ts` — Modified — added TextObject interface and extended CanvasObject union with 'text' type
- `app/src/components/canvas/KonvaStage.tsx` — Modified — text rendering, text tool creation, text sizing/transform, 'text' in CanvasTool union
- `app/src/components/canvas/CanvasToolbar.tsx` — Modified — added text tool to TOOLS array
- `app/src/components/canvas/ObjectPanel.tsx` — Modified — added 'T' icon for text objects in TYPE_ICONS
- `app/src/components/canvas/ShapeProperties.tsx` — Modified — text property controls (content, font family, size, letter spacing, line height), Convert to Paths button, getWidth/getHeight cases
- `app/src/components/canvas/AlignmentBar.tsx` — Modified — added text case to toBoundingRect for exhaustive switch
- `app/src/views/DesignCanvas.tsx` — Modified — added onConvertToPath handler wiring ShapeProperties to canvas state

M002/S02 Design Canvas Core (View 2) Mar 26, 2026, 05:44 AM

Built the Konva.js-powered 2D design canvas with artboard setup, shape tools, selection/transform, layer management, alignment, properties editing, keyboard shortcuts, and undo/redo — 48 new tests (71 total), zero TypeScript errors.

Decisions

- DXF output as raw bytes with application/dxf content-type and metadata in X-Kerf-Metadata header
- postprocess_svg() replaces regex metadata extraction — full XML path parsing for structured PathInfo objects
- _format_response() shared helper for consistent response shaping across endpoints
- Islands placed on separate ISLANDS layer in DXF for downstream CAM tool compatibility
- Bezier curves linearized during post-processing for RDP simplification and DXF polyline generation

19 files modified

- `app/src/types/canvas.ts` — New — discriminated union type system for canvas objects (rect, circle, ellipse, line, image), ArtboardConfig, CanvasState, and CanvasAction types
- `app/src/hooks/useCanvasState.ts` — New — central state management hook with useReducer+useRef for CRUD, selection, undo/redo, reorder, visibility/lock
- `app/src/hooks/__tests__/useCanvasState.test.ts` — New — 20 tests covering all useCanvasState operations including undo/redo edge cases
- `app/src/utils/artboardShapes.ts` — New — artboard shape presets, shield/pennant SVG path generators, toPx/fromPx unit conversion, artboardClipPath
- `app/src/utils/__tests__/artboardShapes.test.ts` — New — 15 tests for artboard presets, path generation, unit conversion, clip paths
- `app/src/utils/alignment.ts` — New — 9 pure alignment/distribute/center functions for canvas objects
- `app/src/utils/__tests__/alignment.test.ts` — New — 13 tests for alignment, distribution, and center-on-artboard
- `app/src/components/canvas/ArtboardSetup.tsx` — New — modal for artboard shape/size/unit selection on entering canvas view
- `app/src/components/canvas/KonvaStage.tsx` — New — Konva Stage+Layer rendering artboard, all object types, Transformer, rubber-band selection
- `app/src/components/canvas/ObjectPanel.tsx` — New — layer management panel with z-order list, reorder, visibility/lock toggles, rename
- `app/src/components/canvas/AlignmentBar.tsx` — New — alignment and distribution toolbar consuming alignment utils
- `app/src/components/canvas/CanvasToolbar.tsx` — New — tool switcher, undo/redo, grid toggle, zoom controls
- `app/src/components/canvas/ShapeProperties.tsx` — New — property editor for selected shape (stroke, fill, dimensions, line style, opacity)
- `app/src/views/DesignCanvas.tsx` — New — View 2 container wiring KonvaStage + all panels to useCanvasState, SVG import, keyboard shortcuts
- `app/src/views/DesignCanvas.module.css` — New — CSS module for DesignCanvas layout
- `app/src/App.tsx` — Modified — wired DesignCanvas with svgData/traceMetadata props, removed underscore prefixes
- `app/src/App.css` — Modified — added comprehensive canvas UI styles (toolbar, panels, artboard setup, shape properties, alignment bar)
- `app/src/test-setup.ts` — Modified — added vitest-canvas-mock import for Konva testing
- `app/package.json` — Modified — added konva, react-konva, vitest-canvas-mock dependencies

M002/S01 Import & Convert UI (View 1) Mar 26, 2026, 05:20 AM

Built the complete Import & Convert view (View 1) — file upload, preset selection, debounced live vectorization preview with parameter sliders, output stats bar, and Use This button to advance to canvas.

18 files modified

- `engine/main.py` — Added CORSMiddleware allowing all origins for dev
- `app/vite.config.ts` — Vite config with React plugin, dev proxy to engine, Vitest config
- `app/src/types/engine.ts` — TypeScript interfaces for PresetConfig, TraceResponse, TraceMetadata, PresetsResponse
- `app/src/api/engine.ts` — Typed API client: getPresets(), traceImage(), simplifyVector() with AbortSignal
- `app/src/api/__tests__/engine.test.ts` — 9 unit tests for API client covering URL/method/FormData/AbortSignal/errors
- `app/src/hooks/useDebouncedTrace.ts` — Custom hook: debounced trace with AbortController, SVG mode detection, params stabilization
- `app/src/hooks/__tests__/useDebouncedTrace.test.ts` — 7 tests for debounce, abort, SVG routing, error handling, cleanup
- `app/src/views/ImportConvert.tsx` — View 1 container: two-column layout wiring FileUpload, PresetSelector, ParameterSliders, SvgPreview, OutputInfoBar, Use This button
- `app/src/views/ImportConvert.module.css` — CSS module for ImportConvert layout
- `app/src/components/FileUpload.tsx` — Drag-and-drop file upload with thumbnail preview and SVG detection
- `app/src/components/PresetSelector.tsx` — Fetches presets from engine, renders cards with selection state, auto-selects sign
- `app/src/components/ParameterSliders.tsx` — Mode-aware range sliders for vectorization parameters
- `app/src/components/SvgPreview.tsx` — Responsive SVG rendering with loading/error states
- `app/src/components/OutputInfoBar.tsx` — Color-coded stats bar with warnings display
- `app/src/components/__tests__/OutputInfoBar.test.tsx` — 7 tests for color coding logic and edge cases
- `app/src/App.tsx` — ViewState routing, SVG/metadata state for View 2 handoff
- `app/src/App.css` — Global styles for shared components
- `app/src/test-setup.ts` — Vitest setup with jest-dom matchers

M001/S03 Preset System + Engine Docker Packaging Mar 26, 2026, 04:52 AM

Shipped 5 pipeline presets (sign, patch, stencil, detailed, custom) with merge-based param resolution, GET /engine/presets endpoint, and a multi-stage Docker image with healthcheck that runs the engine standalone.

Decisions

- Preset default is 'sign' — covers the most common use case
- Presets use flat JSON with three sections (preprocessing, vectorization, postprocessing)
- resolve_params merges preset → user_params with user taking precedence
- Custom preset has empty param sections so pipeline defaults apply unless user provides overrides
- Multi-stage Docker build separates build deps from runtime (smaller image, no compiler tools)
- Engine image contains only engine source — enforces Engine/App separation (D001)
- Health endpoint at /engine/health for namespace consistency with other /engine/* routes

10 files modified

- `engine/presets/sign.json` — Sign preset — aggressive simplification for signage vectorization
- `engine/presets/patch.json` — Patch preset — smooth curves with auto-close for embroidery
- `engine/presets/stencil.json` — Stencil preset — heavy simplification with fixed threshold
- `engine/presets/detailed.json` — Detailed preset — max fidelity for illustrations
- `engine/presets/custom.json` — Custom preset — empty defaults, user controls everything
- `engine/presets/loader.py` — Preset loader with caching, listing, and param merge resolution
- `engine/api/routes.py` — Added GET /engine/presets, GET /engine/health, wired preset into /engine/trace
- `engine/tests/test_presets.py` — 28 tests covering loader, resolution, endpoint, integration, cross-preset differentiation
- `docker/Dockerfile.engine` — Multi-stage Dockerfile: builder compiles pypotrace, runtime uses slim image with engine source only
- `.dockerignore` — Excludes .git, .venv, __pycache__, .gsd, node_modules from Docker build context

M001/S02 Post-Processing + Output Formats (SVG, DXF, JSON) Mar 26, 2026, 04:41 AM

Full post-processing pipeline (RDP simplification, island detection, open path repair) with three output format generators (SVG, DXF, JSON) and /engine/simplify endpoint — 169 tests passing.

Decisions

- DXF output as raw bytes with application/dxf content-type and metadata in X-Kerf-Metadata header
- postprocess_svg() replaces regex metadata extraction — full XML path parsing for structured PathInfo objects
- _format_response() shared helper for consistent response shaping across endpoints
- Islands placed on separate ISLANDS layer in DXF for downstream CAM tool compatibility
- Bezier curves linearized during post-processing for RDP simplification and DXF polyline generation

9 files modified

- `engine/pipeline/postprocess.py` — New: Post-processing module with RDP simplification, island detection, open path repair, SVG path parsing (414 lines)
- `engine/output/__init__.py` — New: Package init exporting generate_dxf, generate_json, generate_svg
- `engine/output/dxf.py` — New: AC1015 DXF generator using ezdxf — LWPOLYLINE entities with island layer separation (66 lines)
- `engine/output/json_output.py` — New: JSON output generator with path commands and properties (76 lines)
- `engine/output/svg.py` — New: SVG output generator returning simplified SVG from PostProcessResult (22 lines)
- `engine/api/routes.py` — Rewritten: integrated postprocess_svg(), output_format routing, /engine/simplify endpoint, _format_response() helper (175 lines)
- `engine/tests/test_postprocess.py` — New: Tests for RDP, island detection, open paths, SVG parsing, node counting (375 lines)
- `engine/tests/test_output.py` — New: Tests for DXF structure, JSON structure, SVG output, round-trip consistency (274 lines)
- `engine/tests/test_api.py` — Rewritten: 35 integration tests for /engine/trace and /engine/simplify across all format combinations (515 lines)

M001/S01 Core Pipeline — Preprocessing + Vectorization Mar 26, 2026, 04:30 AM

## Knowledge 25

### Rules 1

ID | Scope | Rule
--- | --- | ---
# | Scope | Rule

### Patterns 13

ID | Pattern
--- | ---
# | Pattern
P001 | Test images generated programmatically via numpy
P002 | Tests must use .venv/bin/python -m pytest
P003 | PostProcessResult is the universal intermediate representation
P004 | _format_response() for consistent multi-format API responses
P005 | Preset-driven pipeline: resolve_params() merges preset → user
P006 | JSON.stringify for React hook dependency stabilization
P007 | Vite dev proxy for engine API calls
P008 | CSS modules for views, global App.css for shared component styles
P009 | opentype.js needs dynamic import() and local type declarations
P010 | Font Y-axis flip: canvas_y = ascender - font_y * scale
P011 | Letter spacing requires manual per-character glyph positioning
P012 | Adding new CanvasObject types requires exhaustive switch updates in 6 files

### Lessons 11

ID | Lesson
--- | ---
# | What Happened
L001 | pypotrace fails to build from pip
L002 | VTracer Python bindings work directly — no subprocess needed
L003 | pypotrace Bitmap requires uint32 data
L004 | ezdxf emits pyparsing deprecation warnings in tests
L005 | DXF output format needs binary response, not JSON envelope
L006 | postprocess_svg() fully parses SVG paths into coordinates
L007 | Docker build context must be project root, not engine/
L008 | Engine container has dual health endpoints
L009 | useCallback-based triggerTrace caused infinite re-render loops
L010 | jest-canvas-mock crashes in Vitest with "ReferenceError: jest is not defined"

## Captures

No captures recorded.

## Artifacts

### Missing changelogs 3

Milestone | Slice | Title
--- | --- | ---
M003 | S01 | Export Flow (View 3) + DXF Generation
M003 | S02 | Docker Packaging + README
M003 | S03 | Embed Mode

### Recently completed 6

Milestone | Slice | Title | Completed
--- | --- | --- | ---
M002 | S03 | Text System + Font Loading | Mar 26, 2026, 06:00 AM
M002 | S02 | Design Canvas Core (View 2) | Mar 26, 2026, 05:44 AM
M002 | S01 | Import & Convert UI (View 1) | Mar 26, 2026, 05:20 AM
M001 | S03 | Preset System + Engine Docker Packaging | Mar 26, 2026, 04:52 AM
M001 | S02 | Post-Processing + Output Formats (SVG, DXF, JSON) | Mar 26, 2026, 04:41 AM

## Planning

ID | Milestone | State | Context | Draft | Updated
--- | --- | --- | --- | --- | ---
M001 | Kerf Engine — Raster-to-Vector Pipeline & API | undiscussed |  |  |
M002 | M002: React Frontend — Import & Convert UI + Design Canvas | undiscussed |  |  |
M003 | M003 | undiscussed |  |  |