---
id: M001
title: "Kerf Engine — Raster-to-Vector Pipeline & API"
status: complete
completed_at: 2026-03-26T04:56:42.994Z
key_decisions:
  - D001: Engine is standalone module, App is consumer — zero coupling via HTTP API only
  - D003: postprocess_svg() pipeline replaces regex-based SVG metadata extraction with full path parsing into structured PathInfo objects
  - D004: Multi-stage Docker build — builder compiles pypotrace, runtime uses python:3.11-slim with only engine source
  - Bilateral filter chosen over Gaussian for edge-preserving denoise in preprocessing
  - DXF output as raw bytes with metadata in X-Kerf-Metadata header (not JSON envelope)
  - Preset default is 'sign' — covers the most common use case
  - Bezier curves linearized for RDP simplification and DXF polyline generation
key_files:
  - engine/main.py — FastAPI app entry point
  - engine/api/routes.py — All API endpoints: /engine/trace, /engine/simplify, /engine/presets, /engine/health
  - engine/pipeline/preprocessing.py — OpenCV preprocessing pipeline (grayscale, denoise, CLAHE, threshold, morphology)
  - engine/pipeline/vectorize.py — Potrace + VTracer dual-mode vectorization
  - engine/pipeline/postprocess.py — SVG path parsing, RDP simplification, island detection, open path repair (414 lines)
  - engine/output/dxf.py — AC1015 DXF generator with LWPOLYLINE entities and island layer separation
  - engine/output/json_output.py — Structured JSON output with path commands and properties
  - engine/output/svg.py — SVG output generator from PostProcessResult
  - engine/presets/loader.py — Preset loader with caching, listing, and merge-based param resolution
  - engine/presets/sign.json — Sign preset (aggressive simplification for signage)
  - engine/presets/patch.json — Patch preset (smooth curves, auto-close for embroidery)
  - engine/presets/stencil.json — Stencil preset (heavy simplification, fixed threshold)
  - engine/presets/detailed.json — Detailed preset (max fidelity for illustrations)
  - engine/presets/custom.json — Custom preset (empty defaults, user controls everything)
  - docker/Dockerfile.engine — Multi-stage Docker build with HEALTHCHECK
  - engine/tests/ — 196 tests across 6 test files
lessons_learned:
  - L001: pypotrace requires system packages (libpotrace-dev, libagg-dev, pkg-config) — must be installed before pip install
  - L002: VTracer Python bindings work directly via convert_raw_image_to_svg() — no subprocess needed
  - L003: pypotrace Bitmap requires uint32 data — cast with (img > 0).astype(np.uint32) to avoid segfaults
  - L005: DXF output needs binary Response with X-Kerf-Metadata header, not JSON envelope
  - L006: Full SVG path parsing into coordinates is necessary for DXF/JSON output — regex counting is insufficient
  - L007: Docker build context must be project root, not engine/ — Dockerfile uses COPY engine/ paths
  - Programmatic test images via numpy eliminate fixture file management and make tests self-contained
  - Tests must use .venv/bin/python -m pytest — no system python on PATH
---

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

**Shipped a stateless FastAPI raster-to-vector engine with OpenCV preprocessing, dual vectorization modes (Potrace + VTracer), RDP post-processing with island detection, three output formats (SVG, DXF, JSON), a 5-preset configuration system, and Docker packaging — 196 tests passing.**

## What Happened

M001 built the complete Kerf Engine — the proprietary raster-to-vector conversion core — across three slices.

**S01 (Core Pipeline)** stood up the FastAPI project with an OpenCV preprocessing pipeline (grayscale, bilateral filter, CLAHE, Otsu thresholding, morphological ops) and dual vectorization: Potrace for binary bitmap tracing and VTracer for color/grayscale tracing. POST /engine/trace accepts PNG uploads and returns SVG output with metadata. 76 tests established the foundation and patterns (programmatic test images via numpy, .venv pytest runner).

**S02 (Post-Processing + Output Formats)** added the post-processing layer that parses raw SVG paths into structured PathInfo objects, implements Ramer-Douglas-Peucker simplification, detects islands via signed-area calculation, and identifies open paths. Three output generators consume PostProcessResult: DXF (AC1015 with LWPOLYLINE entities, islands on separate layer), JSON (structured path commands with properties), and SVG (simplified output). A /engine/simplify endpoint was added for SVG-to-SVG/DXF/JSON conversion. The old regex-based metadata extraction was replaced entirely by full path parsing — a necessary evolution to support DXF coordinate output and island detection.

**S03 (Presets + Docker)** wrapped the milestone with a preset system (5 tuned configs: sign, patch, stencil, detailed, custom) that drives the entire pipeline via merge-based param resolution (preset defaults + user overrides). GET /engine/presets exposes all configs. Docker packaging uses a multi-stage build — builder stage compiles pypotrace with C dependencies, runtime stage uses python:3.11-slim with only engine source (no App code), enforcing the Engine/App separation mandated by D001. Container includes HEALTHCHECK on /engine/health.

The slices integrated cleanly: S01's vectorizer output feeds S02's postprocess_svg(), which produces PostProcessResult consumed by output generators; S03's preset system configures all three pipeline stages. routes.py evolved across all slices with no regressions — the final 196-test suite validates every endpoint and format combination.

## Success Criteria Results

### S01: "POST /engine/trace with a PNG logo returns valid SVG using both Potrace and VTracer modes"
- ✅ **MET** — POST /engine/trace endpoint exists and works. Integration tests confirm both Potrace and VTracer modes produce valid SVG with metadata (path_count, node_count_total, open_paths, processing_ms). 6 mode-specific tests pass.

### S02: "/engine/trace returns valid DXF and JSON output; /engine/simplify reduces node count on complex SVG"
- ✅ **MET** — output_format parameter routes to SVG/DXF/JSON. DXF returns binary with X-Kerf-Metadata header. JSON returns structured path arrays. /engine/simplify accepts SVG + epsilon and returns simplified output in all formats. 22 format/simplify tests pass including test_simplify_reduces_nodes.

### S03: "GET /engine/presets returns all presets; each preset produces distinct output from same input; engine runs in Docker"
- ✅ **MET** — GET /engine/presets returns all 5 presets. test_presets_produce_different_node_counts confirms cross-preset differentiation. Docker image builds from multi-stage Dockerfile, starts healthy, serves /engine/health. 28 preset tests pass.

## Definition of Done Results

- ✅ All 3 slices marked complete (S01 ✅, S02 ✅, S03 ✅)
- ✅ All slice summaries exist on disk (S01-SUMMARY.md, S02-SUMMARY.md, S03-SUMMARY.md)
- ✅ 196 tests pass in 1.24s with exit 0
- ✅ 31 non-GSD source files created (3,619 lines of code)
- ✅ Cross-slice integration verified: S01→S02 pipeline path works, S02→S03 preset-driven flow works, all endpoints coexist in routes.py
- ✅ Validation passed with verdict: pass (M001-VALIDATION.md)

## Requirement Outcomes

No formal requirements (REQUIREMENTS.md) were defined for this milestone. Governance was via architectural decisions (D001-D004) and per-slice success criteria.

**Decision compliance:**
- D001 (Engine/App separation): Validated — Docker image contains only engine source, zero App coupling.
- D002 (Build order): Validated — Engine built first as mandated. M002/M003 deferred.
- D003 (Post-processing replaces regex): Validated — postprocess_svg() fully supersedes regex extraction.
- D004 (Multi-stage Docker build): Validated — builder compiles pypotrace, runtime uses slim image.

## Deviations

D003 (regex metadata extraction) was superseded mid-milestone — postprocess_svg() replaced it entirely with full XML path parsing, which was necessary for DXF/JSON output generation. Docker build context correction: plan referenced /vmPool/r/repos/xpltdco/kerf which doesn't exist — actual context is project root. Added /engine/health alongside /health for namespace consistency (not originally planned). Postprocessing params (auto_close, close_tolerance) exposed through preset system beyond original plan scope.

## Follow-ups

Consider adding preset validation on load (schema check). Consider documenting pipeline defaults in the custom preset for discoverability. Future M002 will consume the engine container as a service for the App canvas UI. Future M003 handles export, deployment, and embed functionality.
