init: GSD project scaffolding for Kerf — 3 milestones, requirements, decisions
M001: Kerf Engine (pipeline, API, presets, Docker) — 3 slices, 10 tasks M002: Kerf App (Import UI, Canvas, Text system) — 3 slices (planned, not detailed) M003: Export, Docker Compose, Embed Mode — 3 slices (planned, not detailed) 25 requirements extracted from GSD-INITIATE.md 7 architectural decisions recorded Human checkpoints gate M001→M002→M003 transitions
This commit is contained in:
commit
3cca4a2350
32 changed files with 1875 additions and 0 deletions
1
.bg-shell/manifest.json
Normal file
1
.bg-shell/manifest.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
10
.gsd/DECISIONS.md
Normal file
10
.gsd/DECISIONS.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Decisions Register
|
||||
|
||||
<!-- Append-only. Never edit or remove existing rows.
|
||||
To reverse a decision, add a new row that supersedes it.
|
||||
Read this file at the start of any planning or research phase. -->
|
||||
|
||||
| # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |
|
||||
|---|------|-------|----------|--------|-----------|------------|---------|
|
||||
| D001 | | architecture | Engine/App architectural relationship | Engine is standalone module, App is a consumer. Zero coupling — App calls Engine only via HTTP API. | Engine is proprietary IP that must be embeddable into future applications independently. Clean input/output contracts enable this. | No | human |
|
||||
| D002 | | architecture | Build order and gating strategy | Build Engine first (M001), then App canvas (M002), then Export+Deploy+Embed (M003). Human checkpoints gate each transition. | Brief explicitly mandates: validate engine output quality before building canvas UI, validate canvas before export/deploy. Engine is the hardest and most valuable piece. | No | human |
|
||||
20
.gsd/STATE.md
Normal file
20
.gsd/STATE.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# GSD State
|
||||
|
||||
**Active Milestone:** M001: Kerf Engine — Raster-to-Vector Pipeline & API
|
||||
**Active Slice:** S01: Core Pipeline — Preprocessing + Vectorization
|
||||
**Phase:** executing
|
||||
**Requirements Status:** 0 active · 0 validated · 0 deferred · 0 out of scope
|
||||
|
||||
## Milestone Registry
|
||||
- 🔄 **M001:** Kerf Engine — Raster-to-Vector Pipeline & API
|
||||
- ⬜ **M002:** M002
|
||||
- ⬜ **M003:** M003
|
||||
|
||||
## Recent Decisions
|
||||
- None recorded
|
||||
|
||||
## Blockers
|
||||
- None
|
||||
|
||||
## Next Action
|
||||
Execute T01: Repository scaffolding + Engine project setup in slice S01.
|
||||
6
.gsd/event-log.jsonl
Normal file
6
.gsd/event-log.jsonl
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{"cmd":"plan-milestone","params":{"milestoneId":"M001"},"ts":"2026-03-26T03:52:29.296Z","actor":"agent","hash":"e7646f64e62daa33","session_id":"5b5a1848-fcbc-4200-aa49-5260215f4e78"}
|
||||
{"cmd":"plan-milestone","params":{"milestoneId":"M002"},"ts":"2026-03-26T03:53:44.554Z","actor":"agent","hash":"e0e3f50cb5178909","session_id":"5b5a1848-fcbc-4200-aa49-5260215f4e78"}
|
||||
{"cmd":"plan-milestone","params":{"milestoneId":"M003"},"ts":"2026-03-26T03:54:24.043Z","actor":"agent","hash":"9287091a0c2eb492","session_id":"5b5a1848-fcbc-4200-aa49-5260215f4e78"}
|
||||
{"cmd":"plan-slice","params":{"milestoneId":"M001","sliceId":"S01"},"ts":"2026-03-26T03:55:00.035Z","actor":"agent","hash":"b4e65a48e7b42f57","session_id":"5b5a1848-fcbc-4200-aa49-5260215f4e78"}
|
||||
{"cmd":"plan-slice","params":{"milestoneId":"M001","sliceId":"S02"},"ts":"2026-03-26T03:55:23.088Z","actor":"agent","hash":"7990d7932192bfde","session_id":"5b5a1848-fcbc-4200-aa49-5260215f4e78"}
|
||||
{"cmd":"plan-slice","params":{"milestoneId":"M001","sliceId":"S03"},"ts":"2026-03-26T03:55:42.360Z","actor":"agent","hash":"2d5b99521edd6ccd","session_id":"5b5a1848-fcbc-4200-aa49-5260215f4e78"}
|
||||
BIN
.gsd/gsd.db
Normal file
BIN
.gsd/gsd.db
Normal file
Binary file not shown.
BIN
.gsd/gsd.db-shm
Normal file
BIN
.gsd/gsd.db-shm
Normal file
Binary file not shown.
BIN
.gsd/gsd.db-wal
Normal file
BIN
.gsd/gsd.db-wal
Normal file
Binary file not shown.
11
.gsd/milestones/M001/M001-ROADMAP.md
Normal file
11
.gsd/milestones/M001/M001-ROADMAP.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# M001: Kerf Engine — Raster-to-Vector Pipeline & API
|
||||
|
||||
## Vision
|
||||
Build and validate the standalone Kerf Engine: a stateless HTTP API that accepts raster images (or existing SVGs) and returns clean, production-quality vector output (SVG, DXF, JSON) through a multi-stage pipeline of preprocessing, vectorization, post-processing, and format conversion. This is the proprietary IP core — it must work flawlessly before anything else is built.
|
||||
|
||||
## Slice Overview
|
||||
| ID | Slice | Risk | Depends | Done | After this |
|
||||
|----|-------|------|---------|------|------------|
|
||||
| S01 | Core Pipeline — Preprocessing + Vectorization | high — dependency installation, opencv+potrace+vtracer integration | — | ⬜ | POST /engine/trace with a PNG logo returns valid SVG using both Potrace and VTracer modes |
|
||||
| S02 | Post-Processing + Output Formats (SVG, DXF, JSON) | high — dxf generation quality is hard to validate programmatically | S01 | ⬜ | /engine/trace returns valid DXF and JSON output; /engine/simplify reduces node count on complex SVG |
|
||||
| S03 | Preset System + Engine Docker Packaging | low — presets are config files; docker packaging is well-understood | S02 | ⬜ | GET /engine/presets returns all presets; each preset produces distinct output from same input; engine runs in Docker |
|
||||
62
.gsd/milestones/M001/slices/S01/S01-PLAN.md
Normal file
62
.gsd/milestones/M001/slices/S01/S01-PLAN.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# S01: Core Pipeline — Preprocessing + Vectorization
|
||||
|
||||
**Goal:** Stand up FastAPI server with OpenCV preprocessing pipeline and dual-mode vectorization (Potrace + VTracer). Establish the repo structure, dependencies, and core /engine/trace endpoint.
|
||||
**Demo:** After this: POST /engine/trace with a PNG logo returns valid SVG using both Potrace and VTracer modes
|
||||
|
||||
## Tasks
|
||||
- [ ] **T01: Repository scaffolding + Engine project setup** — 1. Create the repo directory structure at /vmPool/r/repos/xpltdco/kerf/
|
||||
2. Set up engine/ as a Python project with pyproject.toml
|
||||
3. Pin dependencies: fastapi, uvicorn, opencv-python-headless, pypotrace, vtracer, python-multipart, Pillow
|
||||
4. Create engine/main.py with FastAPI app skeleton
|
||||
5. Create engine/requirements.txt or use pyproject.toml deps
|
||||
6. Add .gitignore, initial README.md
|
||||
7. Verify: pip install works, uvicorn starts the empty app
|
||||
- Estimate: 30min
|
||||
- Files: engine/pyproject.toml, engine/main.py, engine/requirements.txt, .gitignore, README.md
|
||||
- Verify: cd engine && pip install -e . && uvicorn main:app --host 0.0.0.0 --port 8000 & sleep 3 && curl -s http://localhost:8000/docs | head -20
|
||||
- [ ] **T02: OpenCV preprocessing pipeline** — 1. Create engine/pipeline/preprocessing.py
|
||||
2. Implement preprocessing stages in order:
|
||||
- Grayscale conversion
|
||||
- Denoise (bilateral filter, tunable d/sigmaColor/sigmaSpace)
|
||||
- Contrast enhancement (CLAHE, tunable clipLimit/tileGridSize)
|
||||
- Threshold (Otsu auto-threshold default, manual override)
|
||||
- Edge detection (optional Canny, tunable thresholds)
|
||||
- Morphological ops (dilation/erosion, tunable kernel size/iterations)
|
||||
3. Each stage tunable via parameters dict
|
||||
4. Pipeline accepts raw image bytes, returns processed numpy array
|
||||
5. Add unit tests for each stage
|
||||
- Estimate: 45min
|
||||
- Files: engine/pipeline/__init__.py, engine/pipeline/preprocessing.py, engine/tests/test_preprocessing.py
|
||||
- Verify: cd engine && python -m pytest tests/test_preprocessing.py -v
|
||||
- [ ] **T03: Potrace vectorization (Mode A)** — 1. Create engine/pipeline/vectorize.py
|
||||
2. Implement potrace_trace() function:
|
||||
- Accepts preprocessed numpy array (binary image)
|
||||
- Calls pypotrace with tunable params: turdsize, alphamax, opticurve, opttolerance
|
||||
- Returns SVG string
|
||||
3. Handle potrace bitmap creation from numpy array
|
||||
4. Test with a clean high-contrast test image
|
||||
5. Verify SVG output is well-formed
|
||||
- Estimate: 45min
|
||||
- Files: engine/pipeline/vectorize.py, engine/tests/test_vectorize.py, engine/tests/fixtures/
|
||||
- Verify: cd engine && python -m pytest tests/test_vectorize.py -v -k potrace
|
||||
- [ ] **T04: VTracer vectorization (Mode B)** — 1. Add VTracer tracing to engine/pipeline/vectorize.py
|
||||
2. Implement vtracer_trace() function:
|
||||
- Accepts preprocessed image (can be color for VTracer)
|
||||
- Calls vtracer with tunable params: colormode, hierarchical, filter_speckle, color_precision, layer_difference, corner_threshold, length_threshold, splice_threshold
|
||||
- Returns SVG string
|
||||
3. Research vtracer Python bindings — may need vtracer pip package or subprocess
|
||||
4. Test with photographic/noisy input
|
||||
5. Compare Mode A vs Mode B output on same input
|
||||
- Estimate: 45min
|
||||
- Files: engine/pipeline/vectorize.py, engine/tests/test_vectorize.py
|
||||
- Verify: cd engine && python -m pytest tests/test_vectorize.py -v -k vtracer
|
||||
- [ ] **T05: Wire up /engine/trace endpoint** — 1. Create engine/api/routes.py with trace endpoint
|
||||
2. POST /engine/trace accepts multipart/form-data:
|
||||
- file (image), mode (potrace|vtracer), output_format (svg only for now), preset (ignored for now), params (JSON override)
|
||||
3. Wire pipeline: receive file → preprocess → vectorize (mode-dependent) → return SVG + metadata
|
||||
4. Response shape: {output: '<svg>...', format: 'svg', metadata: {path_count, node_count_total, open_paths, warnings, processing_ms}}
|
||||
5. Add basic metadata extraction from SVG output
|
||||
6. Integration test: upload PNG → get SVG back
|
||||
- Estimate: 45min
|
||||
- Files: engine/api/__init__.py, engine/api/routes.py, engine/main.py, engine/tests/test_api.py
|
||||
- Verify: cd engine && python -m pytest tests/test_api.py -v
|
||||
30
.gsd/milestones/M001/slices/S01/tasks/T01-PLAN.md
Normal file
30
.gsd/milestones/M001/slices/S01/tasks/T01-PLAN.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
estimated_steps: 7
|
||||
estimated_files: 5
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T01: Repository scaffolding + Engine project setup
|
||||
|
||||
1. Create the repo directory structure at /vmPool/r/repos/xpltdco/kerf/
|
||||
2. Set up engine/ as a Python project with pyproject.toml
|
||||
3. Pin dependencies: fastapi, uvicorn, opencv-python-headless, pypotrace, vtracer, python-multipart, Pillow
|
||||
4. Create engine/main.py with FastAPI app skeleton
|
||||
5. Create engine/requirements.txt or use pyproject.toml deps
|
||||
6. Add .gitignore, initial README.md
|
||||
7. Verify: pip install works, uvicorn starts the empty app
|
||||
|
||||
## Inputs
|
||||
|
||||
- `GSD-INITIATE.md`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/pyproject.toml`
|
||||
- `engine/main.py`
|
||||
- `engine/.gitignore`
|
||||
- `README.md`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && pip install -e . && uvicorn main:app --host 0.0.0.0 --port 8000 & sleep 3 && curl -s http://localhost:8000/docs | head -20
|
||||
32
.gsd/milestones/M001/slices/S01/tasks/T02-PLAN.md
Normal file
32
.gsd/milestones/M001/slices/S01/tasks/T02-PLAN.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
estimated_steps: 11
|
||||
estimated_files: 3
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T02: OpenCV preprocessing pipeline
|
||||
|
||||
1. Create engine/pipeline/preprocessing.py
|
||||
2. Implement preprocessing stages in order:
|
||||
- Grayscale conversion
|
||||
- Denoise (bilateral filter, tunable d/sigmaColor/sigmaSpace)
|
||||
- Contrast enhancement (CLAHE, tunable clipLimit/tileGridSize)
|
||||
- Threshold (Otsu auto-threshold default, manual override)
|
||||
- Edge detection (optional Canny, tunable thresholds)
|
||||
- Morphological ops (dilation/erosion, tunable kernel size/iterations)
|
||||
3. Each stage tunable via parameters dict
|
||||
4. Pipeline accepts raw image bytes, returns processed numpy array
|
||||
5. Add unit tests for each stage
|
||||
|
||||
## Inputs
|
||||
|
||||
- `GSD-INITIATE.md`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/pipeline/preprocessing.py`
|
||||
- `engine/tests/test_preprocessing.py`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && python -m pytest tests/test_preprocessing.py -v
|
||||
29
.gsd/milestones/M001/slices/S01/tasks/T03-PLAN.md
Normal file
29
.gsd/milestones/M001/slices/S01/tasks/T03-PLAN.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
estimated_steps: 8
|
||||
estimated_files: 3
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T03: Potrace vectorization (Mode A)
|
||||
|
||||
1. Create engine/pipeline/vectorize.py
|
||||
2. Implement potrace_trace() function:
|
||||
- Accepts preprocessed numpy array (binary image)
|
||||
- Calls pypotrace with tunable params: turdsize, alphamax, opticurve, opttolerance
|
||||
- Returns SVG string
|
||||
3. Handle potrace bitmap creation from numpy array
|
||||
4. Test with a clean high-contrast test image
|
||||
5. Verify SVG output is well-formed
|
||||
|
||||
## Inputs
|
||||
|
||||
- `engine/pipeline/preprocessing.py`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/pipeline/vectorize.py`
|
||||
- `engine/tests/test_vectorize.py`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && python -m pytest tests/test_vectorize.py -v -k potrace
|
||||
28
.gsd/milestones/M001/slices/S01/tasks/T04-PLAN.md
Normal file
28
.gsd/milestones/M001/slices/S01/tasks/T04-PLAN.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
estimated_steps: 8
|
||||
estimated_files: 2
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T04: VTracer vectorization (Mode B)
|
||||
|
||||
1. Add VTracer tracing to engine/pipeline/vectorize.py
|
||||
2. Implement vtracer_trace() function:
|
||||
- Accepts preprocessed image (can be color for VTracer)
|
||||
- Calls vtracer with tunable params: colormode, hierarchical, filter_speckle, color_precision, layer_difference, corner_threshold, length_threshold, splice_threshold
|
||||
- Returns SVG string
|
||||
3. Research vtracer Python bindings — may need vtracer pip package or subprocess
|
||||
4. Test with photographic/noisy input
|
||||
5. Compare Mode A vs Mode B output on same input
|
||||
|
||||
## Inputs
|
||||
|
||||
- `engine/pipeline/preprocessing.py`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/pipeline/vectorize.py`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && python -m pytest tests/test_vectorize.py -v -k vtracer
|
||||
29
.gsd/milestones/M001/slices/S01/tasks/T05-PLAN.md
Normal file
29
.gsd/milestones/M001/slices/S01/tasks/T05-PLAN.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
estimated_steps: 7
|
||||
estimated_files: 4
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T05: Wire up /engine/trace endpoint
|
||||
|
||||
1. Create engine/api/routes.py with trace endpoint
|
||||
2. POST /engine/trace accepts multipart/form-data:
|
||||
- file (image), mode (potrace|vtracer), output_format (svg only for now), preset (ignored for now), params (JSON override)
|
||||
3. Wire pipeline: receive file → preprocess → vectorize (mode-dependent) → return SVG + metadata
|
||||
4. Response shape: {output: '<svg>...', format: 'svg', metadata: {path_count, node_count_total, open_paths, warnings, processing_ms}}
|
||||
5. Add basic metadata extraction from SVG output
|
||||
6. Integration test: upload PNG → get SVG back
|
||||
|
||||
## Inputs
|
||||
|
||||
- `engine/pipeline/preprocessing.py`
|
||||
- `engine/pipeline/vectorize.py`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/api/routes.py`
|
||||
- `engine/tests/test_api.py`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && python -m pytest tests/test_api.py -v
|
||||
47
.gsd/milestones/M001/slices/S02/S02-PLAN.md
Normal file
47
.gsd/milestones/M001/slices/S02/S02-PLAN.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# S02: Post-Processing + Output Formats (SVG, DXF, JSON)
|
||||
|
||||
**Goal:** Add post-processing (RDP simplification, island detection, open path repair, node counting) and all three output formats (SVG, DXF, JSON) plus the /engine/simplify endpoint.
|
||||
**Demo:** After this: /engine/trace returns valid DXF and JSON output; /engine/simplify reduces node count on complex SVG
|
||||
|
||||
## Tasks
|
||||
- [ ] **T01: Post-processing: RDP simplification, island detection, open path repair** — 1. Create engine/pipeline/postprocess.py
|
||||
2. Implement RDP path simplification:
|
||||
- Parse SVG path data into coordinate arrays
|
||||
- Apply Ramer-Douglas-Peucker algorithm with tunable epsilon
|
||||
- Return simplified path data
|
||||
3. Implement island detection:
|
||||
- Identify interior counter paths (holes in letters, enclosed shapes)
|
||||
- Flag with metadata for proper DXF nesting
|
||||
4. Implement open path detection:
|
||||
- Check if path start/end points match
|
||||
- Flag open paths in metadata
|
||||
- Optional auto-close with straight line segment
|
||||
5. Implement node count reporting per path
|
||||
6. Unit tests for each function
|
||||
- Estimate: 60min
|
||||
- Files: engine/pipeline/postprocess.py, engine/tests/test_postprocess.py
|
||||
- Verify: cd engine && python -m pytest tests/test_postprocess.py -v
|
||||
- [ ] **T02: Output format generators: DXF (AC1015+), JSON, SVG** — 1. Create engine/output/dxf.py
|
||||
2. Generate AC1015+ DXF format:
|
||||
- Use ezdxf library (Python-native, well-maintained)
|
||||
- Convert SVG path data to DXF LWPOLYLINE/SPLINE entities
|
||||
- Handle bezier curves → polyline approximation
|
||||
- Proper island/counter-path nesting
|
||||
3. Create engine/output/json_output.py
|
||||
4. JSON format: array of path objects with bezier/polyline commands
|
||||
5. Update engine/output/svg.py for clean SVG serialization
|
||||
6. Test DXF output opens in Inkscape (if available) or validate structure
|
||||
- Estimate: 90min
|
||||
- Files: engine/output/__init__.py, engine/output/dxf.py, engine/output/json_output.py, engine/output/svg.py, engine/tests/test_output.py
|
||||
- Verify: cd engine && python -m pytest tests/test_output.py -v
|
||||
- [ ] **T03: Wire post-processing + /engine/simplify endpoint** — 1. Integrate post-processing into /engine/trace pipeline
|
||||
2. Add output_format parameter routing (svg/dxf/json)
|
||||
3. Create /engine/simplify endpoint:
|
||||
- Accepts SVG file upload + epsilon + output_format
|
||||
- Parses SVG, applies RDP simplification, outputs result
|
||||
- Same response shape as /engine/trace
|
||||
4. Update metadata to include all fields: format, path_count, node_count_total, open_paths, warnings, processing_ms
|
||||
5. Integration tests for all format combinations
|
||||
- Estimate: 45min
|
||||
- Files: engine/api/routes.py, engine/tests/test_api.py
|
||||
- Verify: cd engine && python -m pytest tests/test_api.py -v
|
||||
35
.gsd/milestones/M001/slices/S02/tasks/T01-PLAN.md
Normal file
35
.gsd/milestones/M001/slices/S02/tasks/T01-PLAN.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
estimated_steps: 14
|
||||
estimated_files: 2
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T01: Post-processing: RDP simplification, island detection, open path repair
|
||||
|
||||
1. Create engine/pipeline/postprocess.py
|
||||
2. Implement RDP path simplification:
|
||||
- Parse SVG path data into coordinate arrays
|
||||
- Apply Ramer-Douglas-Peucker algorithm with tunable epsilon
|
||||
- Return simplified path data
|
||||
3. Implement island detection:
|
||||
- Identify interior counter paths (holes in letters, enclosed shapes)
|
||||
- Flag with metadata for proper DXF nesting
|
||||
4. Implement open path detection:
|
||||
- Check if path start/end points match
|
||||
- Flag open paths in metadata
|
||||
- Optional auto-close with straight line segment
|
||||
5. Implement node count reporting per path
|
||||
6. Unit tests for each function
|
||||
|
||||
## Inputs
|
||||
|
||||
- `engine/pipeline/vectorize.py`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/pipeline/postprocess.py`
|
||||
- `engine/tests/test_postprocess.py`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && python -m pytest tests/test_postprocess.py -v
|
||||
32
.gsd/milestones/M001/slices/S02/tasks/T02-PLAN.md
Normal file
32
.gsd/milestones/M001/slices/S02/tasks/T02-PLAN.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
estimated_steps: 10
|
||||
estimated_files: 5
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T02: Output format generators: DXF (AC1015+), JSON, SVG
|
||||
|
||||
1. Create engine/output/dxf.py
|
||||
2. Generate AC1015+ DXF format:
|
||||
- Use ezdxf library (Python-native, well-maintained)
|
||||
- Convert SVG path data to DXF LWPOLYLINE/SPLINE entities
|
||||
- Handle bezier curves → polyline approximation
|
||||
- Proper island/counter-path nesting
|
||||
3. Create engine/output/json_output.py
|
||||
4. JSON format: array of path objects with bezier/polyline commands
|
||||
5. Update engine/output/svg.py for clean SVG serialization
|
||||
6. Test DXF output opens in Inkscape (if available) or validate structure
|
||||
|
||||
## Inputs
|
||||
|
||||
- `engine/pipeline/postprocess.py`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/output/dxf.py`
|
||||
- `engine/output/json_output.py`
|
||||
- `engine/output/svg.py`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && python -m pytest tests/test_output.py -v
|
||||
30
.gsd/milestones/M001/slices/S02/tasks/T03-PLAN.md
Normal file
30
.gsd/milestones/M001/slices/S02/tasks/T03-PLAN.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
estimated_steps: 8
|
||||
estimated_files: 2
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T03: Wire post-processing + /engine/simplify endpoint
|
||||
|
||||
1. Integrate post-processing into /engine/trace pipeline
|
||||
2. Add output_format parameter routing (svg/dxf/json)
|
||||
3. Create /engine/simplify endpoint:
|
||||
- Accepts SVG file upload + epsilon + output_format
|
||||
- Parses SVG, applies RDP simplification, outputs result
|
||||
- Same response shape as /engine/trace
|
||||
4. Update metadata to include all fields: format, path_count, node_count_total, open_paths, warnings, processing_ms
|
||||
5. Integration tests for all format combinations
|
||||
|
||||
## Inputs
|
||||
|
||||
- `engine/pipeline/postprocess.py`
|
||||
- `engine/output/dxf.py`
|
||||
- `engine/output/json_output.py`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/api/routes.py (updated)`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && python -m pytest tests/test_api.py -v
|
||||
27
.gsd/milestones/M001/slices/S03/S03-PLAN.md
Normal file
27
.gsd/milestones/M001/slices/S03/S03-PLAN.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# S03: Preset System + Engine Docker Packaging
|
||||
|
||||
**Goal:** Implement preset system (sign, patch, stencil, detailed, custom) as JSON config files and package engine as standalone Docker container with healthcheck.
|
||||
**Demo:** After this: GET /engine/presets returns all presets; each preset produces distinct output from same input; engine runs in Docker
|
||||
|
||||
## Tasks
|
||||
- [ ] **T01: Preset JSON definitions + GET /engine/presets + preset wiring** — 1. Create engine/presets/ directory
|
||||
2. Define 5 preset JSON files: sign.json, patch.json, stencil.json, detailed.json, custom.json
|
||||
3. Each preset specifies: preprocessing params, vectorization mode + params, postprocessing params
|
||||
4. Create engine/presets/loader.py to load presets from directory
|
||||
5. Implement GET /engine/presets endpoint returning all presets
|
||||
6. Wire preset selection into /engine/trace — preset params as defaults, params field overrides
|
||||
7. Test: same image with different presets produces different output
|
||||
- Estimate: 45min
|
||||
- Files: engine/presets/sign.json, engine/presets/patch.json, engine/presets/stencil.json, engine/presets/detailed.json, engine/presets/custom.json, engine/presets/loader.py, engine/api/routes.py
|
||||
- Verify: cd engine && python -m pytest tests/ -v -k preset
|
||||
- [ ] **T02: Engine Dockerfile + healthcheck** — 1. Create docker/Dockerfile.engine (multi-stage build)
|
||||
2. Install system deps: libopencv, potrace libs
|
||||
3. Install Python deps from pyproject.toml
|
||||
4. Add healthcheck endpoint: GET /engine/health → {status: 'ok'}
|
||||
5. Configure Dockerfile HEALTHCHECK instruction
|
||||
6. Build and test container starts
|
||||
7. Verify API responds from inside container
|
||||
8. Ensure no App dependencies in engine image
|
||||
- Estimate: 30min
|
||||
- Files: docker/Dockerfile.engine, engine/api/routes.py
|
||||
- Verify: cd /vmPool/r/repos/xpltdco/kerf && docker build -f docker/Dockerfile.engine -t kerf-engine:dev . && docker run --rm -d --name kerf-engine-test -p 8100:8000 kerf-engine:dev && sleep 5 && curl -s http://localhost:8100/engine/health && docker stop kerf-engine-test
|
||||
33
.gsd/milestones/M001/slices/S03/tasks/T01-PLAN.md
Normal file
33
.gsd/milestones/M001/slices/S03/tasks/T01-PLAN.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
estimated_steps: 7
|
||||
estimated_files: 7
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T01: Preset JSON definitions + GET /engine/presets + preset wiring
|
||||
|
||||
1. Create engine/presets/ directory
|
||||
2. Define 5 preset JSON files: sign.json, patch.json, stencil.json, detailed.json, custom.json
|
||||
3. Each preset specifies: preprocessing params, vectorization mode + params, postprocessing params
|
||||
4. Create engine/presets/loader.py to load presets from directory
|
||||
5. Implement GET /engine/presets endpoint returning all presets
|
||||
6. Wire preset selection into /engine/trace — preset params as defaults, params field overrides
|
||||
7. Test: same image with different presets produces different output
|
||||
|
||||
## Inputs
|
||||
|
||||
- `GSD-INITIATE.md`
|
||||
- `engine/api/routes.py`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `engine/presets/sign.json`
|
||||
- `engine/presets/patch.json`
|
||||
- `engine/presets/stencil.json`
|
||||
- `engine/presets/detailed.json`
|
||||
- `engine/presets/custom.json`
|
||||
- `engine/presets/loader.py`
|
||||
|
||||
## Verification
|
||||
|
||||
cd engine && python -m pytest tests/ -v -k preset
|
||||
28
.gsd/milestones/M001/slices/S03/tasks/T02-PLAN.md
Normal file
28
.gsd/milestones/M001/slices/S03/tasks/T02-PLAN.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
estimated_steps: 8
|
||||
estimated_files: 2
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T02: Engine Dockerfile + healthcheck
|
||||
|
||||
1. Create docker/Dockerfile.engine (multi-stage build)
|
||||
2. Install system deps: libopencv, potrace libs
|
||||
3. Install Python deps from pyproject.toml
|
||||
4. Add healthcheck endpoint: GET /engine/health → {status: 'ok'}
|
||||
5. Configure Dockerfile HEALTHCHECK instruction
|
||||
6. Build and test container starts
|
||||
7. Verify API responds from inside container
|
||||
8. Ensure no App dependencies in engine image
|
||||
|
||||
## Inputs
|
||||
|
||||
- `engine/pyproject.toml`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- `docker/Dockerfile.engine`
|
||||
|
||||
## Verification
|
||||
|
||||
cd /vmPool/r/repos/xpltdco/kerf && docker build -f docker/Dockerfile.engine -t kerf-engine:dev . && docker run --rm -d --name kerf-engine-test -p 8100:8000 kerf-engine:dev && sleep 5 && curl -s http://localhost:8100/engine/health && docker stop kerf-engine-test
|
||||
11
.gsd/milestones/M002/M002-ROADMAP.md
Normal file
11
.gsd/milestones/M002/M002-ROADMAP.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# M002:
|
||||
|
||||
## Vision
|
||||
Build the React frontend with the Import & Convert view (View 1) and the Design Canvas (View 2). View 1 integrates with the Engine API for vectorization with live preview. View 2 is a Konva.js-powered 2D design environment with artboard shapes, text objects, basic shapes, layers, alignment tools, and undo/redo.
|
||||
|
||||
## Slice Overview
|
||||
| ID | Slice | Risk | Depends | Done | After this |
|
||||
|----|-------|------|---------|------|------------|
|
||||
| S01 | Import & Convert UI (View 1) | medium — debounced preview updates, engine api integration from browser | — | ⬜ | Upload a PNG, see live preview with preset selection and slider controls, click 'Use This' to advance to canvas |
|
||||
| S02 | Design Canvas Core (View 2) | medium — konva.js setup, selection handles, undo/redo state management | S01 | ⬜ | Create artboards in each shape, add/move/resize shapes, multi-select with alignment, undo/redo through history |
|
||||
| S03 | Text System + Font Loading | medium — opentype.js integration, font loading from volume, path extraction accuracy | S02 | ⬜ | Add text objects, select fonts from picker, change size/spacing, convert text to paths — paths match original glyphs |
|
||||
6
.gsd/milestones/M002/slices/S01/S01-PLAN.md
Normal file
6
.gsd/milestones/M002/slices/S01/S01-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# S01: Import & Convert UI (View 1)
|
||||
|
||||
**Goal:** Build View 1 (Import & Convert) with Engine API integration, preset selector, tuning sliders with live preview, and output info bar
|
||||
**Demo:** After this: Upload a PNG, see live preview with preset selection and slider controls, click 'Use This' to advance to canvas
|
||||
|
||||
## Tasks
|
||||
6
.gsd/milestones/M002/slices/S02/S02-PLAN.md
Normal file
6
.gsd/milestones/M002/slices/S02/S02-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# S02: Design Canvas Core (View 2)
|
||||
|
||||
**Goal:** Build View 2 Design Canvas with Konva.js — artboard setup, basic shapes, object panel, toolbar, alignment tools, undo/redo
|
||||
**Demo:** After this: Create artboards in each shape, add/move/resize shapes, multi-select with alignment, undo/redo through history
|
||||
|
||||
## Tasks
|
||||
6
.gsd/milestones/M002/slices/S03/S03-PLAN.md
Normal file
6
.gsd/milestones/M002/slices/S03/S03-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# S03: Text System + Font Loading
|
||||
|
||||
**Goal:** Add text objects with opentype.js font rendering, font picker from fonts.json manifest, text-to-paths conversion
|
||||
**Demo:** After this: Add text objects, select fonts from picker, change size/spacing, convert text to paths — paths match original glyphs
|
||||
|
||||
## Tasks
|
||||
11
.gsd/milestones/M003/M003-ROADMAP.md
Normal file
11
.gsd/milestones/M003/M003-ROADMAP.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# M003:
|
||||
|
||||
## Vision
|
||||
Complete the Kerf App with the Export view (View 3), Docker Compose packaging for the full stack, and embed mode for third-party site integration. This milestone delivers the final end-to-end experience: upload → design → export → download.
|
||||
|
||||
## Slice Overview
|
||||
| ID | Slice | Risk | Depends | Done | After this |
|
||||
|----|-------|------|---------|------|------------|
|
||||
| S01 | Export Flow (View 3) + DXF Generation | high — dxf scale accuracy and geometry quality | — | ⬜ | Design a sign with text + imported vector, export as DXF, open in Inkscape/LightBurn with correct geometry and scale |
|
||||
| S02 | Docker Packaging + README | low — docker packaging is well-understood pattern | S01 | ⬜ | docker-compose up starts all services; Engine container runs independently; healthchecks pass |
|
||||
| S03 | Embed Mode | medium — shadow dom + konva.js + font loading interactions | S02 | ⬜ | Embed snippet in plain HTML page; component renders; styles don't bleed; download works from embedded context |
|
||||
6
.gsd/milestones/M003/slices/S01/S01-PLAN.md
Normal file
6
.gsd/milestones/M003/slices/S01/S01-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# S01: Export Flow (View 3) + DXF Generation
|
||||
|
||||
**Goal:** Build View 3 Export UI with DXF/SVG/PNG generation, layer assignment, text-to-paths enforcement, pre-export validation, and download
|
||||
**Demo:** After this: Design a sign with text + imported vector, export as DXF, open in Inkscape/LightBurn with correct geometry and scale
|
||||
|
||||
## Tasks
|
||||
6
.gsd/milestones/M003/slices/S02/S02-PLAN.md
Normal file
6
.gsd/milestones/M003/slices/S02/S02-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# S02: Docker Packaging + README
|
||||
|
||||
**Goal:** Create Docker Compose configuration with kerf-engine, kerf-app, kerf-server services, environment config, volumes, and healthchecks
|
||||
**Demo:** After this: docker-compose up starts all services; Engine container runs independently; healthchecks pass
|
||||
|
||||
## Tasks
|
||||
6
.gsd/milestones/M003/slices/S03/S03-PLAN.md
Normal file
6
.gsd/milestones/M003/slices/S03/S03-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# S03: Embed Mode
|
||||
|
||||
**Goal:** Build embed mode: bundled JS + Shadow DOM scoped styles + postMessage event communication with host page
|
||||
**Demo:** After this: Embed snippet in plain HTML page; component renders; styles don't bleed; download works from embedded context
|
||||
|
||||
## Tasks
|
||||
802
.gsd/state-manifest.json
Normal file
802
.gsd/state-manifest.json
Normal file
|
|
@ -0,0 +1,802 @@
|
|||
{
|
||||
"version": 1,
|
||||
"exported_at": "2026-03-26T03:55:42.360Z",
|
||||
"milestones": [
|
||||
{
|
||||
"id": "M001",
|
||||
"title": "Kerf Engine — Raster-to-Vector Pipeline & API",
|
||||
"status": "active",
|
||||
"depends_on": [],
|
||||
"created_at": "2026-03-26T03:52:29.269Z",
|
||||
"completed_at": null,
|
||||
"vision": "Build and validate the standalone Kerf Engine: a stateless HTTP API that accepts raster images (or existing SVGs) and returns clean, production-quality vector output (SVG, DXF, JSON) through a multi-stage pipeline of preprocessing, vectorization, post-processing, and format conversion. This is the proprietary IP core — it must work flawlessly before anything else is built.",
|
||||
"success_criteria": [
|
||||
"Clean high-contrast PNG logo → SVG renders correctly in browser",
|
||||
"Same logo → DXF opens in Inkscape/LightBurn with closed paths and clean geometry",
|
||||
"Noisy/photographic input → Mode A (Potrace) vs Mode B (VTracer) produce meaningfully different outputs",
|
||||
"Complex SVG → /engine/simplify reduces node count while preserving shape",
|
||||
"Each preset (sign, patch, stencil, detailed) produces distinct, appropriate output",
|
||||
"Engine API handles concurrent requests with no state leakage",
|
||||
"All output includes accurate metadata (path_count, node_count, open_paths, warnings, processing_ms)",
|
||||
"Human checkpoint sign-off received before proceeding to M002"
|
||||
],
|
||||
"key_risks": [
|
||||
{
|
||||
"risk": "Potrace/VTracer Python bindings may have installation complexity or version conflicts",
|
||||
"whyItMatters": "These are the core vectorization engines — if they don't install cleanly in Docker, the entire pipeline is blocked"
|
||||
},
|
||||
{
|
||||
"risk": "DXF output quality — AC1015 compatibility and closed-path geometry are hard to validate programmatically",
|
||||
"whyItMatters": "DXF files must open cleanly in LightBurn/Inkscape — the human checkpoint is the real validation"
|
||||
},
|
||||
{
|
||||
"risk": "OpenCV preprocessing parameter tuning may require iteration",
|
||||
"whyItMatters": "Bad preprocessing produces bad vectors regardless of tracing quality"
|
||||
},
|
||||
{
|
||||
"risk": "VTracer may not have stable Python bindings",
|
||||
"whyItMatters": "Mode B is required for photographic/noisy inputs — may need Rust FFI wrapper or subprocess call"
|
||||
}
|
||||
],
|
||||
"proof_strategy": [
|
||||
{
|
||||
"riskOrUnknown": "Potrace/VTracer installation in Docker",
|
||||
"retireIn": "S01",
|
||||
"whatWillBeProven": "Both engines install and run inside Docker container with Python FastAPI"
|
||||
},
|
||||
{
|
||||
"riskOrUnknown": "DXF output quality",
|
||||
"retireIn": "S02",
|
||||
"whatWillBeProven": "Generated DXF files open in Inkscape with closed paths — verified by human"
|
||||
},
|
||||
{
|
||||
"riskOrUnknown": "Pipeline produces meaningful output variation across presets",
|
||||
"retireIn": "S03",
|
||||
"whatWillBeProven": "Same input with different presets produces measurably different SVG output"
|
||||
}
|
||||
],
|
||||
"verification_contract": "All API endpoints return correct HTTP status codes, valid JSON responses, and accurate metadata. Unit tests cover preprocessing, vectorization, post-processing, and output formatting stages independently.",
|
||||
"verification_integration": "End-to-end tests: raster image in → SVG/DXF/JSON out for both Potrace and VTracer modes. /engine/simplify reduces node count on complex SVG input.",
|
||||
"verification_operational": "Engine starts in Docker, healthcheck passes, API responds within 5s for typical inputs. Concurrent requests handled without crash or state leakage.",
|
||||
"verification_uat": "Human Checkpoint 1: Submit test images, review SVG in browser, open DXF in Inkscape/LightBurn, compare presets, confirm output quality before App work begins.",
|
||||
"definition_of_done": [
|
||||
"Engine API serves /engine/trace, /engine/simplify, /engine/presets",
|
||||
"Both Potrace and VTracer modes produce valid output",
|
||||
"All 5 presets defined and produce distinct output",
|
||||
"DXF output is AC1015+ compatible with closed paths",
|
||||
"Output metadata is accurate and complete",
|
||||
"Engine runs in Docker container with healthcheck",
|
||||
"Human Checkpoint 1 signed off"
|
||||
],
|
||||
"requirement_coverage": "R001–R010 (all Engine requirements), R025 (decoupling — engine has zero App imports)",
|
||||
"boundary_map_markdown": "```\\n[Raster Input] → [OpenCV Preprocessing] → [Potrace/VTracer] → [Post-Processing] → [SVG/DXF/JSON Output]\\n ↑\\n[SVG Input] ────────────────────────────────────────────────────────────┘ (simplify-only path)\\n\\nHTTP API boundary: POST /engine/trace, POST /engine/simplify, GET /engine/presets\\nNo state stored between requests. No dependency on App layer.\\n```"
|
||||
},
|
||||
{
|
||||
"id": "M002",
|
||||
"title": "",
|
||||
"status": "queued",
|
||||
"depends_on": [],
|
||||
"created_at": "2026-03-26T03:52:32.553Z",
|
||||
"completed_at": null,
|
||||
"vision": "Build the React frontend with the Import & Convert view (View 1) and the Design Canvas (View 2). View 1 integrates with the Engine API for vectorization with live preview. View 2 is a Konva.js-powered 2D design environment with artboard shapes, text objects, basic shapes, layers, alignment tools, and undo/redo.",
|
||||
"success_criteria": [
|
||||
"Upload flow works with all supported image formats",
|
||||
"Live preview updates within 300ms debounce",
|
||||
"All artboard shapes render at correct proportions",
|
||||
"Canvas objects (text, shapes, imported vectors) are fully interactive",
|
||||
"Font picker loads fonts from manifest; text renders accurately",
|
||||
"Text-to-paths conversion preserves glyph fidelity",
|
||||
"Undo/redo works across all operations",
|
||||
"Alignment tools work against artboard and selection",
|
||||
"Human checkpoint sign-off received before proceeding to M003"
|
||||
],
|
||||
"key_risks": [
|
||||
{
|
||||
"risk": "Konva.js performance with complex vector paths from engine output",
|
||||
"whyItMatters": "Imported vector objects may have hundreds of paths/nodes — canvas must remain interactive"
|
||||
},
|
||||
{
|
||||
"risk": "opentype.js font rendering fidelity and path extraction accuracy",
|
||||
"whyItMatters": "Text must look identical as vector preview and as converted paths for DXF export"
|
||||
},
|
||||
{
|
||||
"risk": "State management complexity across three views",
|
||||
"whyItMatters": "Data must flow cleanly from Import → Canvas → Export without loss"
|
||||
}
|
||||
],
|
||||
"proof_strategy": [
|
||||
{
|
||||
"riskOrUnknown": "Engine integration from React frontend",
|
||||
"retireIn": "S01",
|
||||
"whatWillBeProven": "View 1 calls Engine API, displays live preview, carries vector data to canvas"
|
||||
},
|
||||
{
|
||||
"riskOrUnknown": "Konva.js performance with complex vectors",
|
||||
"retireIn": "S02",
|
||||
"whatWillBeProven": "Canvas remains interactive with imported vector objects containing 500+ nodes"
|
||||
},
|
||||
{
|
||||
"riskOrUnknown": "opentype.js rendering fidelity",
|
||||
"retireIn": "S03",
|
||||
"whatWillBeProven": "Text renders accurately and path conversion preserves glyph shapes"
|
||||
}
|
||||
],
|
||||
"verification_contract": "React components render correctly with expected props. Canvas operations (add, move, resize, delete, undo) produce correct state changes.",
|
||||
"verification_integration": "End-to-end: upload image → adjust sliders → accept → lands on canvas → add text + shapes → alignment works → undo/redo works.",
|
||||
"verification_operational": "kerf-app and kerf-server start in Docker, serve frontend, proxy Engine API calls.",
|
||||
"verification_uat": "Human Checkpoint 2: Create artboards, add text with different fonts, add shapes with all line styles, test undo/redo, test alignment tools.",
|
||||
"definition_of_done": [
|
||||
"View 1: Import & Convert UI works with live preview and preset selection",
|
||||
"View 2: Canvas with all artboard shapes, text, shape objects, layer panel",
|
||||
"Text objects render with loaded fonts via opentype.js",
|
||||
"Undo/redo and alignment tools work correctly",
|
||||
"Navigation between views preserves state",
|
||||
"Human Checkpoint 2 signed off"
|
||||
],
|
||||
"requirement_coverage": "R011–R018, R024 (font system), R025 (decoupling via HTTP)",
|
||||
"boundary_map_markdown": "```\\n[View 1: Import & Convert] → [View 2: Design Canvas] → [View 3: Export]\\n ↓ ↓ ↓\\n Calls Engine API Konva.js canvas (M003 scope)\\n via kerf-server react-konva\\n opentype.js fonts\\n\\nkerf-server (FastAPI) proxies Engine calls and serves fonts/assets.\\nkerf-app (React) is the SPA frontend.\\n```"
|
||||
},
|
||||
{
|
||||
"id": "M003",
|
||||
"title": "",
|
||||
"status": "queued",
|
||||
"depends_on": [],
|
||||
"created_at": "2026-03-26T03:53:46.549Z",
|
||||
"completed_at": null,
|
||||
"vision": "Complete the Kerf App with the Export view (View 3), Docker Compose packaging for the full stack, and embed mode for third-party site integration. This milestone delivers the final end-to-end experience: upload → design → export → download.",
|
||||
"success_criteria": [
|
||||
"DXF export produces files with correct real-world scale and clean geometry",
|
||||
"Pre-export validation catches all issues (open paths, unconverted text, high complexity)",
|
||||
"Docker Compose deploys full stack with single command",
|
||||
"Engine container runs independently",
|
||||
"Embedded component renders with no style bleed",
|
||||
"postMessage events fire correctly",
|
||||
"README is complete per spec",
|
||||
"Human Checkpoints 3 and 4 signed off"
|
||||
],
|
||||
"key_risks": [
|
||||
{
|
||||
"risk": "DXF geometry quality at real-world scale — text paths + imported vectors must produce correct dimensions",
|
||||
"whyItMatters": "This is the final output users cut on machines. Scale errors mean wasted material."
|
||||
},
|
||||
{
|
||||
"risk": "Shadow DOM compatibility with Konva.js and font loading",
|
||||
"whyItMatters": "Shadow DOM may interfere with canvas rendering or font CSS injection"
|
||||
},
|
||||
{
|
||||
"risk": "Embed mode postMessage security and origin validation",
|
||||
"whyItMatters": "Embedded components must not leak data to unauthorized host pages"
|
||||
}
|
||||
],
|
||||
"proof_strategy": [
|
||||
{
|
||||
"riskOrUnknown": "DXF geometry quality at real-world scale",
|
||||
"retireIn": "S01",
|
||||
"whatWillBeProven": "Exported DXF opens in LightBurn with correct dimensions and clean geometry — human verified"
|
||||
},
|
||||
{
|
||||
"riskOrUnknown": "Multi-service Docker deployment",
|
||||
"retireIn": "S02",
|
||||
"whatWillBeProven": "docker-compose up starts all services; healthchecks pass; Engine works independently"
|
||||
},
|
||||
{
|
||||
"riskOrUnknown": "Shadow DOM + Konva.js compatibility",
|
||||
"retireIn": "S03",
|
||||
"whatWillBeProven": "Embedded component renders canvas, loads fonts, downloads files from host page context"
|
||||
}
|
||||
],
|
||||
"verification_contract": "Export generates valid DXF/SVG/PNG. Pre-export validation catches all specified error conditions.",
|
||||
"verification_integration": "Full workflow: import → design → export. DXF opens cleanly in external tools. Embed mode renders in host page.",
|
||||
"verification_operational": "docker-compose up starts all services. Healthchecks pass. Engine works standalone.",
|
||||
"verification_uat": "Human Checkpoint 3: DXF opened in LightBurn/Inkscape. Human Checkpoint 4: Embedded in test page, no style bleed, download works.",
|
||||
"definition_of_done": [
|
||||
"Export produces valid DXF/SVG/PNG files at correct real-world scale",
|
||||
"Pre-export validation catches open paths and unconverted text",
|
||||
"Docker Compose starts all services with healthchecks",
|
||||
"Engine independently deployable",
|
||||
"Embed mode renders in host page with no style bleed",
|
||||
"postMessage events fire on download/export",
|
||||
"README covers all documented topics",
|
||||
"Human Checkpoints 3 and 4 signed off"
|
||||
],
|
||||
"requirement_coverage": "R019–R023 (export, deployment, embed)",
|
||||
"boundary_map_markdown": "```\\n[View 3: Export] → [DXF/SVG/PNG generation] → [Browser download]\\n ↓\\n[Docker Compose] → kerf-engine + kerf-app + kerf-server\\n ↓\\n[Embed Mode] → <script> + <div> → Shadow DOM → postMessage\\n```"
|
||||
}
|
||||
],
|
||||
"slices": [
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"id": "S01",
|
||||
"title": "Core Pipeline — Preprocessing + Vectorization",
|
||||
"status": "pending",
|
||||
"risk": "high — dependency installation, OpenCV+Potrace+VTracer integration",
|
||||
"depends": [],
|
||||
"demo": "POST /engine/trace with a PNG logo returns valid SVG using both Potrace and VTracer modes",
|
||||
"created_at": "2026-03-26T03:52:29.269Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Stand up FastAPI server with OpenCV preprocessing pipeline and dual-mode vectorization (Potrace + VTracer). Establish the repo structure, dependencies, and core /engine/trace endpoint.",
|
||||
"success_criteria": "- FastAPI server starts and serves /engine/trace\\n- Potrace mode produces SVG from clean PNG input\\n- VTracer mode produces SVG from same input\\n- OpenCV preprocessing stages applied and tunable\\n- Concurrent requests handled without state issues",
|
||||
"proof_level": "integration — end-to-end raster→SVG via HTTP",
|
||||
"integration_closure": "HTTP API contract established; response shape locked for all downstream consumers",
|
||||
"observability_impact": "processing_ms in response metadata; structured logging for each pipeline stage",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"id": "S02",
|
||||
"title": "Post-Processing + Output Formats (SVG, DXF, JSON)",
|
||||
"status": "pending",
|
||||
"risk": "high — DXF generation quality is hard to validate programmatically",
|
||||
"depends": [
|
||||
"S01"
|
||||
],
|
||||
"demo": "/engine/trace returns valid DXF and JSON output; /engine/simplify reduces node count on complex SVG",
|
||||
"created_at": "2026-03-26T03:52:29.269Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Add post-processing (RDP simplification, island detection, open path repair, node counting) and all three output formats (SVG, DXF, JSON) plus the /engine/simplify endpoint.",
|
||||
"success_criteria": "- RDP simplification reduces node count with tunable epsilon\\n- Island detection flags interior counter paths\\n- Open path detection identifies and optionally closes endpoints\\n- DXF output is AC1015+ compatible\\n- JSON output contains raw path data\\n- /engine/simplify accepts SVG and returns simplified output\\n- All outputs include complete metadata",
|
||||
"proof_level": "integration + manual — DXF validated in Inkscape by human",
|
||||
"integration_closure": "All output formats complete; metadata contract locked; simplify endpoint live",
|
||||
"observability_impact": "Node count and open path warnings in metadata; per-stage timing in logs",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"id": "S03",
|
||||
"title": "Preset System + Engine Docker Packaging",
|
||||
"status": "pending",
|
||||
"risk": "low — presets are config files; Docker packaging is well-understood",
|
||||
"depends": [
|
||||
"S02"
|
||||
],
|
||||
"demo": "GET /engine/presets returns all presets; each preset produces distinct output from same input; engine runs in Docker",
|
||||
"created_at": "2026-03-26T03:52:29.269Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Implement preset system (sign, patch, stencil, detailed, custom) as JSON config files and package engine as standalone Docker container with healthcheck.",
|
||||
"success_criteria": "- All 5 presets defined as JSON files in /engine/presets/\\n- GET /engine/presets returns complete preset list with parameter values\\n- Preset selection in /engine/trace produces meaningfully different output\\n- Engine Dockerfile builds and runs\\n- Healthcheck endpoint passes\\n- Engine deployable independently (no App dependency)",
|
||||
"proof_level": "integration + operational — Docker container starts, healthcheck passes, presets work",
|
||||
"integration_closure": "Engine is complete and independently deployable; ready for human Checkpoint 1",
|
||||
"observability_impact": "Healthcheck endpoint; preset name in response metadata; Docker container logs",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
},
|
||||
{
|
||||
"milestone_id": "M002",
|
||||
"id": "S01",
|
||||
"title": "Import & Convert UI (View 1)",
|
||||
"status": "pending",
|
||||
"risk": "medium — debounced preview updates, Engine API integration from browser",
|
||||
"depends": [],
|
||||
"demo": "Upload a PNG, see live preview with preset selection and slider controls, click 'Use This' to advance to canvas",
|
||||
"created_at": "2026-03-26T03:53:44.532Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Build View 1 (Import & Convert) with Engine API integration, preset selector, tuning sliders with live preview, and output info bar",
|
||||
"success_criteria": "- File upload accepts all supported formats\n- Preset cards populate sliders with defaults\n- Sliders update preview within 300ms debounce\n- Output info bar shows path count, node count, warnings\n- 'Use This' carries vector data to View 2\n- 'Re-import' resets the flow",
|
||||
"proof_level": "integration — React frontend calling live Engine API",
|
||||
"integration_closure": "View 1 complete; vector data carried to canvas on 'Use This' action",
|
||||
"observability_impact": "Preview update latency visible to user; info bar shows live stats",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
},
|
||||
{
|
||||
"milestone_id": "M002",
|
||||
"id": "S02",
|
||||
"title": "Design Canvas Core (View 2)",
|
||||
"status": "pending",
|
||||
"risk": "medium — Konva.js setup, selection handles, undo/redo state management",
|
||||
"depends": [
|
||||
"S01"
|
||||
],
|
||||
"demo": "Create artboards in each shape, add/move/resize shapes, multi-select with alignment, undo/redo through history",
|
||||
"created_at": "2026-03-26T03:53:44.532Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Build View 2 Design Canvas with Konva.js — artboard setup, basic shapes, object panel, toolbar, alignment tools, undo/redo",
|
||||
"success_criteria": "- All artboard shapes render (rect, square, circle, oval, shield, pennant, custom)\n- Dimensions input with units toggle (inches/mm)\n- Basic shapes: rectangle, circle, ellipse, line (all styles)\n- Object panel: layer list, reorder, visibility toggle, lock toggle\n- Toolbar: select, shape tools, zoom, grid, rulers\n- Alignment: all 9 alignment + distribute H/V + center on artboard\n- Multi-select supported\n- Full undo/redo history stack",
|
||||
"proof_level": "integration — all canvas operations functional and visually correct",
|
||||
"integration_closure": "Canvas core complete; ready for text objects and imported vector rendering",
|
||||
"observability_impact": "Canvas frame rate; undo history depth",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
},
|
||||
{
|
||||
"milestone_id": "M002",
|
||||
"id": "S03",
|
||||
"title": "Text System + Font Loading",
|
||||
"status": "pending",
|
||||
"risk": "medium — opentype.js integration, font loading from volume, path extraction accuracy",
|
||||
"depends": [
|
||||
"S02"
|
||||
],
|
||||
"demo": "Add text objects, select fonts from picker, change size/spacing, convert text to paths — paths match original glyphs",
|
||||
"created_at": "2026-03-26T03:53:44.532Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Add text objects with opentype.js font rendering, font picker from fonts.json manifest, text-to-paths conversion",
|
||||
"success_criteria": "- Text objects addable via toolbar\n- Font picker loads from fonts.json manifest\n- Font size, letter spacing, line height adjustable\n- Text alignment: left, center, right\n- 'Convert to paths' produces accurate vector paths\n- Converted paths render identically to text preview",
|
||||
"proof_level": "integration — fonts load, render, and convert to paths correctly",
|
||||
"integration_closure": "Canvas feature-complete; all object types functional; ready for Human Checkpoint 2",
|
||||
"observability_impact": "Font loading status; path conversion node count",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
},
|
||||
{
|
||||
"milestone_id": "M003",
|
||||
"id": "S01",
|
||||
"title": "Export Flow (View 3) + DXF Generation",
|
||||
"status": "pending",
|
||||
"risk": "high — DXF scale accuracy and geometry quality",
|
||||
"depends": [],
|
||||
"demo": "Design a sign with text + imported vector, export as DXF, open in Inkscape/LightBurn with correct geometry and scale",
|
||||
"created_at": "2026-03-26T03:54:24.010Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Build View 3 Export UI with DXF/SVG/PNG generation, layer assignment, text-to-paths enforcement, pre-export validation, and download",
|
||||
"success_criteria": "- Format selector: DXF, SVG, PNG\n- DXF layer assignment UI\n- Text-to-paths enforcement warning and one-click convert\n- Pre-export validation: closed paths, open endpoints, node count, unconverted text\n- Correct real-world scale in DXF (inches/mm)\n- Default filename pattern applied\n- Download triggers browser file save",
|
||||
"proof_level": "integration + manual — DXF validated in external tools by human",
|
||||
"integration_closure": "Full end-to-end workflow complete: Import → Canvas → Export",
|
||||
"observability_impact": "Export generation time; validation warning counts",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
},
|
||||
{
|
||||
"milestone_id": "M003",
|
||||
"id": "S02",
|
||||
"title": "Docker Packaging + README",
|
||||
"status": "pending",
|
||||
"risk": "low — Docker packaging is well-understood pattern",
|
||||
"depends": [
|
||||
"S01"
|
||||
],
|
||||
"demo": "docker-compose up starts all services; Engine container runs independently; healthchecks pass",
|
||||
"created_at": "2026-03-26T03:54:24.010Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Create Docker Compose configuration with kerf-engine, kerf-app, kerf-server services, environment config, volumes, and healthchecks",
|
||||
"success_criteria": "- docker-compose.yml with all three services\n- Environment config via .env\n- Volumes: fonts, presets, uploads\n- Healthchecks on all services\n- Engine deployable standalone\n- Multi-stage Dockerfiles for production builds\n- README covers quick start, API reference, fonts, presets, embed",
|
||||
"proof_level": "operational — docker-compose up; all healthchecks green",
|
||||
"integration_closure": "Full stack deployable via single command",
|
||||
"observability_impact": "Container healthchecks; environment variable documentation",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
},
|
||||
{
|
||||
"milestone_id": "M003",
|
||||
"id": "S03",
|
||||
"title": "Embed Mode",
|
||||
"status": "pending",
|
||||
"risk": "medium — Shadow DOM + Konva.js + font loading interactions",
|
||||
"depends": [
|
||||
"S02"
|
||||
],
|
||||
"demo": "Embed snippet in plain HTML page; component renders; styles don't bleed; download works from embedded context",
|
||||
"created_at": "2026-03-26T03:54:24.010Z",
|
||||
"completed_at": null,
|
||||
"full_summary_md": "",
|
||||
"full_uat_md": "",
|
||||
"goal": "Build embed mode: bundled JS + Shadow DOM scoped styles + postMessage event communication with host page",
|
||||
"success_criteria": "- Single bundled kerf.js script\n- Renders inside host page div\n- Shadow DOM scoping — no style bleed in or out\n- postMessage events: onExport, onDownload\n- No host page dependencies\n- Test HTML page included for verification",
|
||||
"proof_level": "integration + manual — embedded in test page, verified by human",
|
||||
"integration_closure": "Project feature-complete; all requirements delivered",
|
||||
"observability_impact": "postMessage events documented; embed errors logged to console",
|
||||
"sequence": 0,
|
||||
"replan_triggered_at": null
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S01",
|
||||
"id": "T01",
|
||||
"title": "Repository scaffolding + Engine project setup",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Create the repo directory structure at /vmPool/r/repos/xpltdco/kerf/\n2. Set up engine/ as a Python project with pyproject.toml\n3. Pin dependencies: fastapi, uvicorn, opencv-python-headless, pypotrace, vtracer, python-multipart, Pillow\n4. Create engine/main.py with FastAPI app skeleton\n5. Create engine/requirements.txt or use pyproject.toml deps\n6. Add .gitignore, initial README.md\n7. Verify: pip install works, uvicorn starts the empty app",
|
||||
"estimate": "30min",
|
||||
"files": [
|
||||
"engine/pyproject.toml",
|
||||
"engine/main.py",
|
||||
"engine/requirements.txt",
|
||||
".gitignore",
|
||||
"README.md"
|
||||
],
|
||||
"verify": "cd engine && pip install -e . && uvicorn main:app --host 0.0.0.0 --port 8000 & sleep 3 && curl -s http://localhost:8000/docs | head -20",
|
||||
"inputs": [
|
||||
"GSD-INITIATE.md"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/pyproject.toml",
|
||||
"engine/main.py",
|
||||
"engine/.gitignore",
|
||||
"README.md"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S01",
|
||||
"id": "T02",
|
||||
"title": "OpenCV preprocessing pipeline",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Create engine/pipeline/preprocessing.py\n2. Implement preprocessing stages in order:\n - Grayscale conversion\n - Denoise (bilateral filter, tunable d/sigmaColor/sigmaSpace)\n - Contrast enhancement (CLAHE, tunable clipLimit/tileGridSize)\n - Threshold (Otsu auto-threshold default, manual override)\n - Edge detection (optional Canny, tunable thresholds)\n - Morphological ops (dilation/erosion, tunable kernel size/iterations)\n3. Each stage tunable via parameters dict\n4. Pipeline accepts raw image bytes, returns processed numpy array\n5. Add unit tests for each stage",
|
||||
"estimate": "45min",
|
||||
"files": [
|
||||
"engine/pipeline/__init__.py",
|
||||
"engine/pipeline/preprocessing.py",
|
||||
"engine/tests/test_preprocessing.py"
|
||||
],
|
||||
"verify": "cd engine && python -m pytest tests/test_preprocessing.py -v",
|
||||
"inputs": [
|
||||
"GSD-INITIATE.md"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/pipeline/preprocessing.py",
|
||||
"engine/tests/test_preprocessing.py"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S01",
|
||||
"id": "T03",
|
||||
"title": "Potrace vectorization (Mode A)",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Create engine/pipeline/vectorize.py\n2. Implement potrace_trace() function:\n - Accepts preprocessed numpy array (binary image)\n - Calls pypotrace with tunable params: turdsize, alphamax, opticurve, opttolerance\n - Returns SVG string\n3. Handle potrace bitmap creation from numpy array\n4. Test with a clean high-contrast test image\n5. Verify SVG output is well-formed",
|
||||
"estimate": "45min",
|
||||
"files": [
|
||||
"engine/pipeline/vectorize.py",
|
||||
"engine/tests/test_vectorize.py",
|
||||
"engine/tests/fixtures/"
|
||||
],
|
||||
"verify": "cd engine && python -m pytest tests/test_vectorize.py -v -k potrace",
|
||||
"inputs": [
|
||||
"engine/pipeline/preprocessing.py"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/pipeline/vectorize.py",
|
||||
"engine/tests/test_vectorize.py"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S01",
|
||||
"id": "T04",
|
||||
"title": "VTracer vectorization (Mode B)",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Add VTracer tracing to engine/pipeline/vectorize.py\n2. Implement vtracer_trace() function:\n - Accepts preprocessed image (can be color for VTracer)\n - Calls vtracer with tunable params: colormode, hierarchical, filter_speckle, color_precision, layer_difference, corner_threshold, length_threshold, splice_threshold\n - Returns SVG string\n3. Research vtracer Python bindings — may need vtracer pip package or subprocess\n4. Test with photographic/noisy input\n5. Compare Mode A vs Mode B output on same input",
|
||||
"estimate": "45min",
|
||||
"files": [
|
||||
"engine/pipeline/vectorize.py",
|
||||
"engine/tests/test_vectorize.py"
|
||||
],
|
||||
"verify": "cd engine && python -m pytest tests/test_vectorize.py -v -k vtracer",
|
||||
"inputs": [
|
||||
"engine/pipeline/preprocessing.py"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/pipeline/vectorize.py"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S01",
|
||||
"id": "T05",
|
||||
"title": "Wire up /engine/trace endpoint",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Create engine/api/routes.py with trace endpoint\n2. POST /engine/trace accepts multipart/form-data:\n - file (image), mode (potrace|vtracer), output_format (svg only for now), preset (ignored for now), params (JSON override)\n3. Wire pipeline: receive file → preprocess → vectorize (mode-dependent) → return SVG + metadata\n4. Response shape: {output: '<svg>...', format: 'svg', metadata: {path_count, node_count_total, open_paths, warnings, processing_ms}}\n5. Add basic metadata extraction from SVG output\n6. Integration test: upload PNG → get SVG back",
|
||||
"estimate": "45min",
|
||||
"files": [
|
||||
"engine/api/__init__.py",
|
||||
"engine/api/routes.py",
|
||||
"engine/main.py",
|
||||
"engine/tests/test_api.py"
|
||||
],
|
||||
"verify": "cd engine && python -m pytest tests/test_api.py -v",
|
||||
"inputs": [
|
||||
"engine/pipeline/preprocessing.py",
|
||||
"engine/pipeline/vectorize.py"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/api/routes.py",
|
||||
"engine/tests/test_api.py"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S02",
|
||||
"id": "T01",
|
||||
"title": "Post-processing: RDP simplification, island detection, open path repair",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Create engine/pipeline/postprocess.py\n2. Implement RDP path simplification:\n - Parse SVG path data into coordinate arrays\n - Apply Ramer-Douglas-Peucker algorithm with tunable epsilon\n - Return simplified path data\n3. Implement island detection:\n - Identify interior counter paths (holes in letters, enclosed shapes)\n - Flag with metadata for proper DXF nesting\n4. Implement open path detection:\n - Check if path start/end points match\n - Flag open paths in metadata\n - Optional auto-close with straight line segment\n5. Implement node count reporting per path\n6. Unit tests for each function",
|
||||
"estimate": "60min",
|
||||
"files": [
|
||||
"engine/pipeline/postprocess.py",
|
||||
"engine/tests/test_postprocess.py"
|
||||
],
|
||||
"verify": "cd engine && python -m pytest tests/test_postprocess.py -v",
|
||||
"inputs": [
|
||||
"engine/pipeline/vectorize.py"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/pipeline/postprocess.py",
|
||||
"engine/tests/test_postprocess.py"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S02",
|
||||
"id": "T02",
|
||||
"title": "Output format generators: DXF (AC1015+), JSON, SVG",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Create engine/output/dxf.py\n2. Generate AC1015+ DXF format:\n - Use ezdxf library (Python-native, well-maintained)\n - Convert SVG path data to DXF LWPOLYLINE/SPLINE entities\n - Handle bezier curves → polyline approximation\n - Proper island/counter-path nesting\n3. Create engine/output/json_output.py\n4. JSON format: array of path objects with bezier/polyline commands\n5. Update engine/output/svg.py for clean SVG serialization\n6. Test DXF output opens in Inkscape (if available) or validate structure",
|
||||
"estimate": "90min",
|
||||
"files": [
|
||||
"engine/output/__init__.py",
|
||||
"engine/output/dxf.py",
|
||||
"engine/output/json_output.py",
|
||||
"engine/output/svg.py",
|
||||
"engine/tests/test_output.py"
|
||||
],
|
||||
"verify": "cd engine && python -m pytest tests/test_output.py -v",
|
||||
"inputs": [
|
||||
"engine/pipeline/postprocess.py"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/output/dxf.py",
|
||||
"engine/output/json_output.py",
|
||||
"engine/output/svg.py"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S02",
|
||||
"id": "T03",
|
||||
"title": "Wire post-processing + /engine/simplify endpoint",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Integrate post-processing into /engine/trace pipeline\n2. Add output_format parameter routing (svg/dxf/json)\n3. Create /engine/simplify endpoint:\n - Accepts SVG file upload + epsilon + output_format\n - Parses SVG, applies RDP simplification, outputs result\n - Same response shape as /engine/trace\n4. Update metadata to include all fields: format, path_count, node_count_total, open_paths, warnings, processing_ms\n5. Integration tests for all format combinations",
|
||||
"estimate": "45min",
|
||||
"files": [
|
||||
"engine/api/routes.py",
|
||||
"engine/tests/test_api.py"
|
||||
],
|
||||
"verify": "cd engine && python -m pytest tests/test_api.py -v",
|
||||
"inputs": [
|
||||
"engine/pipeline/postprocess.py",
|
||||
"engine/output/dxf.py",
|
||||
"engine/output/json_output.py"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/api/routes.py (updated)"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S03",
|
||||
"id": "T01",
|
||||
"title": "Preset JSON definitions + GET /engine/presets + preset wiring",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Create engine/presets/ directory\n2. Define 5 preset JSON files: sign.json, patch.json, stencil.json, detailed.json, custom.json\n3. Each preset specifies: preprocessing params, vectorization mode + params, postprocessing params\n4. Create engine/presets/loader.py to load presets from directory\n5. Implement GET /engine/presets endpoint returning all presets\n6. Wire preset selection into /engine/trace — preset params as defaults, params field overrides\n7. Test: same image with different presets produces different output",
|
||||
"estimate": "45min",
|
||||
"files": [
|
||||
"engine/presets/sign.json",
|
||||
"engine/presets/patch.json",
|
||||
"engine/presets/stencil.json",
|
||||
"engine/presets/detailed.json",
|
||||
"engine/presets/custom.json",
|
||||
"engine/presets/loader.py",
|
||||
"engine/api/routes.py"
|
||||
],
|
||||
"verify": "cd engine && python -m pytest tests/ -v -k preset",
|
||||
"inputs": [
|
||||
"GSD-INITIATE.md",
|
||||
"engine/api/routes.py"
|
||||
],
|
||||
"expected_output": [
|
||||
"engine/presets/sign.json",
|
||||
"engine/presets/patch.json",
|
||||
"engine/presets/stencil.json",
|
||||
"engine/presets/detailed.json",
|
||||
"engine/presets/custom.json",
|
||||
"engine/presets/loader.py"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
},
|
||||
{
|
||||
"milestone_id": "M001",
|
||||
"slice_id": "S03",
|
||||
"id": "T02",
|
||||
"title": "Engine Dockerfile + healthcheck",
|
||||
"status": "pending",
|
||||
"one_liner": "",
|
||||
"narrative": "",
|
||||
"verification_result": "",
|
||||
"duration": "",
|
||||
"completed_at": null,
|
||||
"blocker_discovered": false,
|
||||
"deviations": "",
|
||||
"known_issues": "",
|
||||
"key_files": [],
|
||||
"key_decisions": [],
|
||||
"full_summary_md": "",
|
||||
"description": "1. Create docker/Dockerfile.engine (multi-stage build)\n2. Install system deps: libopencv, potrace libs\n3. Install Python deps from pyproject.toml\n4. Add healthcheck endpoint: GET /engine/health → {status: 'ok'}\n5. Configure Dockerfile HEALTHCHECK instruction\n6. Build and test container starts\n7. Verify API responds from inside container\n8. Ensure no App dependencies in engine image",
|
||||
"estimate": "30min",
|
||||
"files": [
|
||||
"docker/Dockerfile.engine",
|
||||
"engine/api/routes.py"
|
||||
],
|
||||
"verify": "cd /vmPool/r/repos/xpltdco/kerf && docker build -f docker/Dockerfile.engine -t kerf-engine:dev . && docker run --rm -d --name kerf-engine-test -p 8100:8000 kerf-engine:dev && sleep 5 && curl -s http://localhost:8100/engine/health && docker stop kerf-engine-test",
|
||||
"inputs": [
|
||||
"engine/pyproject.toml"
|
||||
],
|
||||
"expected_output": [
|
||||
"docker/Dockerfile.engine"
|
||||
],
|
||||
"observability_impact": "",
|
||||
"full_plan_md": "",
|
||||
"sequence": 0
|
||||
}
|
||||
],
|
||||
"decisions": [
|
||||
{
|
||||
"seq": 1,
|
||||
"id": "D001",
|
||||
"when_context": "",
|
||||
"scope": "architecture",
|
||||
"decision": "Engine/App architectural relationship",
|
||||
"choice": "Engine is standalone module, App is a consumer. Zero coupling — App calls Engine only via HTTP API.",
|
||||
"rationale": "Engine is proprietary IP that must be embeddable into future applications independently. Clean input/output contracts enable this.",
|
||||
"revisable": "No",
|
||||
"made_by": "human",
|
||||
"superseded_by": null
|
||||
},
|
||||
{
|
||||
"seq": 8,
|
||||
"id": "D002",
|
||||
"when_context": "",
|
||||
"scope": "architecture",
|
||||
"decision": "Build order and gating strategy",
|
||||
"choice": "Build Engine first (M001), then App canvas (M002), then Export+Deploy+Embed (M003). Human checkpoints gate each transition.",
|
||||
"rationale": "Brief explicitly mandates: validate engine output quality before building canvas UI, validate canvas before export/deploy. Engine is the hardest and most valuable piece.",
|
||||
"revisable": "No",
|
||||
"made_by": "human",
|
||||
"superseded_by": null
|
||||
}
|
||||
],
|
||||
"verification_evidence": []
|
||||
}
|
||||
482
GSD-INITIATE.md
Normal file
482
GSD-INITIATE.md
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
# Kerf — Project Brief
|
||||
|
||||
**Working Directory:** `/vmPool/r/repos/xpltdco/kerf`
|
||||
**Deployment Target:** Docker container (self-hostable or cloud-hosted)
|
||||
**Primary Use Case:** Raster-to-vector conversion engine + 2D sign/patch design canvas + DXF export workflow
|
||||
|
||||
---
|
||||
|
||||
## Vision
|
||||
|
||||
Kerf is a modular, Docker-deployed web application with two distinct but composable layers:
|
||||
|
||||
1. **The Kerf Engine** — a standalone, API-first raster-to-vector conversion module. This is proprietary IP. It must be built with clean, well-documented input/output contracts so it can be extracted and embedded into future applications independently of this one.
|
||||
|
||||
2. **The Kerf App** — a full-featured 2D design canvas and sign configuration workflow that consumes the Kerf Engine and delivers a complete end-to-end experience: upload an image or design from scratch, refine the vector output, compose a sign layout, and download a production-ready DXF file.
|
||||
|
||||
The two layers must remain architecturally decoupled. The Engine is not a feature of the App — the App is a consumer of the Engine.
|
||||
|
||||
---
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
/vmPool/r/repos/xpltdco/kerf/
|
||||
/engine/ ← Kerf Engine (standalone module, own package.json / pyproject)
|
||||
/app/ ← Kerf App frontend (React)
|
||||
/server/ ← Kerf App backend API (Python/FastAPI)
|
||||
/docker/ ← Dockerfiles and compose configs
|
||||
/docs/ ← Engine API docs, architecture notes
|
||||
README.md
|
||||
docker-compose.yml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 1: The Kerf Engine
|
||||
|
||||
### What It Is
|
||||
|
||||
A self-contained service that accepts raster image input and returns clean vector output. It also accepts existing vector input (SVG) for simplification passes. This is the hardest, most valuable piece of the entire project. Build and validate this first before touching the app canvas.
|
||||
|
||||
### Processing Pipeline
|
||||
|
||||
The engine runs a multi-stage pipeline internally:
|
||||
|
||||
```
|
||||
Input (raster or vector)
|
||||
→ Preprocessing (OpenCV)
|
||||
→ Vectorization (Potrace or VTracer, mode-dependent)
|
||||
→ Post-processing (path simplification, cleanup)
|
||||
→ Output (SVG, DXF, or JSON path data)
|
||||
```
|
||||
|
||||
#### Stage 1 — Preprocessing (raster inputs only)
|
||||
Uses OpenCV. The following operations are applied in sequence, each tunable via parameters:
|
||||
- Grayscale conversion
|
||||
- Denoise (bilateral filter — preserves edges better than Gaussian for this use case)
|
||||
- Contrast enhancement (CLAHE — adaptive histogram equalization)
|
||||
- Threshold (Otsu auto-threshold as default, manual override available)
|
||||
- Edge detection mode (optional — Canny, for line-art inputs)
|
||||
- Dilation/erosion (for cleaning up noisy inputs before tracing)
|
||||
|
||||
#### Stage 2 — Vectorization
|
||||
Two modes, selectable per request:
|
||||
|
||||
**Mode A: Potrace** (default)
|
||||
- Fast, deterministic, excellent for high-contrast logos and line art
|
||||
- Exposes Potrace tuning parameters: `turdsize`, `alphamax`, `opticurve`, `opttolerance`
|
||||
- Best for: clean logos, stencils, bold graphics, sign art
|
||||
|
||||
**Mode B: VTracer**
|
||||
- Better handling of photographic or noisy inputs, color-aware
|
||||
- Exposes VTracer params: `colormode`, `hierarchical`, `filter_speckle`, `color_precision`, `layer_difference`, `corner_threshold`, `length_threshold`, `splice_threshold`
|
||||
- Best for: complex images, multi-color inputs, detailed illustrations
|
||||
|
||||
The engine selects Mode A by default. The calling application (or user via preset) can override.
|
||||
|
||||
#### Stage 3 — Post-Processing
|
||||
- **Path simplification:** Ramer-Douglas-Peucker algorithm, `epsilon` tunable. Also applies to vector-only inputs (simplification-only mode).
|
||||
- **Island detection:** Identify and flag interior counter paths (letter holes, enclosed shapes) for proper nesting in DXF output
|
||||
- **Open path detection and repair:** Flag or auto-close open endpoints
|
||||
- **Node count reporting:** Return node count per path so the UI can warn the user if complexity is too high for practical cutting
|
||||
|
||||
#### Stage 4 — Output
|
||||
Supported output formats:
|
||||
- `SVG` — standard, browser-renderable
|
||||
- `DXF` — AC1015+ (AutoCAD 2000 compatible), via makerjs or direct entity generation
|
||||
- `JSON` — raw path data (array of bezier/polyline commands) for consuming applications to render or further process
|
||||
|
||||
All outputs include metadata:
|
||||
```json
|
||||
{
|
||||
"format": "svg",
|
||||
"path_count": 12,
|
||||
"node_count_total": 847,
|
||||
"open_paths": 0,
|
||||
"warnings": ["High node count — consider increasing simplification"],
|
||||
"processing_ms": 340
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Engine API Contract
|
||||
|
||||
The Engine exposes a clean HTTP API. This is the stable interface other applications will consume.
|
||||
|
||||
#### `POST /engine/trace`
|
||||
Trace a raster image to vector.
|
||||
|
||||
**Request:** `multipart/form-data`
|
||||
```
|
||||
file — image file (PNG, JPG, BMP, TIFF, WebP)
|
||||
mode — "potrace" | "vtracer" (default: potrace)
|
||||
output_format — "svg" | "dxf" | "json" (default: svg)
|
||||
preset — "sign" | "patch" | "stencil" | "detailed" | "custom" (default: sign)
|
||||
params — JSON string of override parameters (optional, merges over preset defaults)
|
||||
```
|
||||
|
||||
**Response:** `application/json`
|
||||
```json
|
||||
{
|
||||
"output": "<svg>...</svg>",
|
||||
"format": "svg",
|
||||
"metadata": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
#### `POST /engine/simplify`
|
||||
Simplify an existing SVG vector.
|
||||
|
||||
**Request:** `multipart/form-data`
|
||||
```
|
||||
file — SVG file
|
||||
epsilon — float, RDP simplification tolerance (default: 1.0)
|
||||
output_format — "svg" | "dxf" | "json"
|
||||
```
|
||||
|
||||
**Response:** same shape as `/engine/trace`
|
||||
|
||||
#### `GET /engine/presets`
|
||||
Returns the full list of available presets and their parameter values.
|
||||
|
||||
---
|
||||
|
||||
### Presets
|
||||
|
||||
Presets are named configurations that set all tuning parameters to sensible defaults for a given application. Users pick a preset first, then fine-tune from there.
|
||||
|
||||
| Preset | Description | Key Biases |
|
||||
|---|---|---|
|
||||
| `sign` | Metal signage, bold text cutouts | High contrast, aggressive simplification, low node count |
|
||||
| `patch` | Embroidered patches, fabric cutting | Medium detail, smooth curves, closed paths enforced |
|
||||
| `stencil` | Physical stencil cutting | Simplified, bridges flagged, no islands |
|
||||
| `detailed` | High-fidelity illustration work | Low simplification, maximum path fidelity |
|
||||
| `custom` | All params exposed, no defaults applied | — |
|
||||
|
||||
Presets are stored as JSON config files in `/engine/presets/` so new ones can be added without code changes.
|
||||
|
||||
---
|
||||
|
||||
### Engine Testing Checkpoint
|
||||
|
||||
**Stop here and validate the engine before building anything else.**
|
||||
|
||||
Prompt the human to:
|
||||
1. Submit a clean high-contrast PNG logo via the API and confirm the SVG output renders correctly in a browser
|
||||
2. Submit the same image in `dxf` output mode and open the result in Inkscape or LightBurn — confirm paths are closed and geometry is clean
|
||||
3. Submit a noisy or photographic input and compare Mode A vs Mode B output
|
||||
4. Submit an existing complex SVG and run it through `/engine/simplify` — confirm node count drops and shape is preserved
|
||||
5. Test each preset and confirm the outputs differ meaningfully
|
||||
|
||||
**Do not proceed to the App canvas until the human explicitly signs off on engine output quality.**
|
||||
|
||||
---
|
||||
|
||||
## Part 2: The Kerf App
|
||||
|
||||
### Overview
|
||||
|
||||
A React-based single-page application served by a FastAPI backend. Deployed as a Docker container. Functions both as a standalone web app (direct URL access) and as an embeddable component (script tag + div).
|
||||
|
||||
The App is composed of three views that function as a linear workflow with the ability to jump back:
|
||||
|
||||
```
|
||||
[1. Import & Convert] → [2. Design Canvas] → [3. Export]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### View 1: Import & Convert
|
||||
|
||||
This is the Kerf Engine's UI surface. The user arrives here first.
|
||||
|
||||
#### Import Options
|
||||
- **Upload raster image** (PNG, JPG, BMP, TIFF, WebP) — primary flow
|
||||
- **Upload existing vector** (SVG) — routes to simplification mode
|
||||
- **Start blank** — skip import, go directly to canvas with empty artboard
|
||||
|
||||
#### Vectorization Controls (shown after upload)
|
||||
Layout: image preview on the left, controls on the right, live output preview updates as controls change.
|
||||
|
||||
**Preset Selector**
|
||||
- Visual cards for each preset (icon + label + one-line description)
|
||||
- Selecting a preset populates all sliders with preset defaults
|
||||
- User can then adjust individual sliders freely
|
||||
|
||||
**Control Sliders / Toggles**
|
||||
All controls update the preview in real time (debounced, ~300ms):
|
||||
|
||||
| Control | Type | Description |
|
||||
|---|---|---|
|
||||
| Detail Level | Slider | Master control — maps to simplification epsilon. Low = more detail, High = simpler paths |
|
||||
| Threshold | Slider | Black/white cutoff for rasterized input preprocessing |
|
||||
| Noise Filter | Slider | Speckle/noise removal aggressiveness |
|
||||
| Smooth Curves | Slider | Curve fitting vs sharp corners |
|
||||
| Path Mode | Toggle | Outline only vs filled regions |
|
||||
| Color Mode | Toggle (VTracer only) | B&W vs color-aware tracing |
|
||||
| Advanced | Expandable panel | Exposes all raw Potrace/VTracer parameters for power users |
|
||||
|
||||
**Output Info Bar**
|
||||
Below the preview — shows live stats: path count, total node count, open path warnings, estimated file size. Color-coded: green (good), yellow (high complexity warning), red (open paths or unusable geometry detected).
|
||||
|
||||
**Action Buttons**
|
||||
- "Use This" — accepts current output and advances to the Design Canvas, carrying the vector data forward
|
||||
- "Re-import" — start over with a new file
|
||||
|
||||
---
|
||||
|
||||
### View 2: Design Canvas
|
||||
|
||||
A 2D design environment. Think Illustrator-lite meets a CNC layout tool. Built with **Konva.js + React** (react-konva).
|
||||
|
||||
#### Artboard Setup
|
||||
On first entering the canvas (or when starting blank), the user defines their artboard:
|
||||
|
||||
**Shape:**
|
||||
- Rectangle
|
||||
- Square
|
||||
- Circle
|
||||
- Oval/Ellipse
|
||||
- Shield (common patch shape)
|
||||
- Pennant
|
||||
- Custom (width + height freeform)
|
||||
|
||||
**Dimensions:**
|
||||
- Width and Height inputs (inches)
|
||||
- Units toggle: inches / mm
|
||||
- The artboard renders at a scaled representation — 1:1 real-world proportion maintained
|
||||
|
||||
#### Canvas Objects
|
||||
|
||||
All objects are layered, selectable, moveable, resizable, and deletable. Standard selection handles. Multi-select supported.
|
||||
|
||||
**Text Objects**
|
||||
- Add text via toolbar button
|
||||
- Font picker — reads from `/fonts/fonts.json` manifest (same pattern as the sign component: local `.otf` files, dynamic manifest, per-font warning flags)
|
||||
- Font size, letter spacing, line height
|
||||
- Alignment: left, center, right
|
||||
- Text renders using opentype.js for accurate glyph representation
|
||||
- Toggle: "Convert to paths" — flattens text to vector paths for export (required for DXF)
|
||||
|
||||
**Imported Vector Object**
|
||||
- The output from View 1 drops onto the canvas as a vector object
|
||||
- Fully moveable, scaleable, rotatable
|
||||
- Can be duplicated
|
||||
|
||||
**Basic Shape Objects**
|
||||
- Rectangle / Square
|
||||
- Circle / Ellipse
|
||||
- Line — with style variants:
|
||||
- Solid
|
||||
- Dashed (configurable dash/gap length)
|
||||
- Dotted
|
||||
- Double line
|
||||
- Centerline (long-short-long pattern, common in CAD)
|
||||
- Stroke weight control
|
||||
- Fill toggle (filled vs outline only)
|
||||
|
||||
**Object Panel**
|
||||
Right sidebar showing layer stack — all objects listed, reorderable via drag, visibility toggle per object, lock toggle per object.
|
||||
|
||||
#### Canvas Toolbar
|
||||
Top bar with:
|
||||
- Select tool
|
||||
- Text tool
|
||||
- Shape tools (rectangle, circle, line)
|
||||
- Zoom in / out / fit to screen
|
||||
- Undo / Redo (full history stack)
|
||||
- Grid toggle (snap to grid, configurable grid size)
|
||||
- Rulers toggle
|
||||
|
||||
#### Alignment Tools
|
||||
When one or more objects are selected:
|
||||
- Align left / center / right (relative to artboard or to selection)
|
||||
- Align top / middle / bottom
|
||||
- Distribute horizontally / vertically
|
||||
- Center on artboard (single click to center both axes)
|
||||
|
||||
---
|
||||
|
||||
### View 3: Export
|
||||
|
||||
Clean, focused export screen. No distractions.
|
||||
|
||||
#### File Format Selector
|
||||
- DXF (default — AC1015+)
|
||||
- SVG
|
||||
- PNG (raster export at specified DPI)
|
||||
|
||||
#### DXF-Specific Options
|
||||
- All text must be converted to paths before DXF export — if any unconverted text objects exist on canvas, warn the user and offer a one-click "Convert All Text to Paths" before proceeding
|
||||
- Layer assignment — assign canvas objects to named DXF layers (e.g., "cut", "engrave", "score")
|
||||
- Units: inches or mm
|
||||
|
||||
#### Filename
|
||||
- Default pattern: `[project-name]_[width]x[height].[ext]`
|
||||
- Editable before download
|
||||
|
||||
#### Download Button
|
||||
- Label: "Download File"
|
||||
- Shows spinner during generation
|
||||
- Triggers browser file download immediately on completion
|
||||
|
||||
#### Export Validation
|
||||
Before generating the file, run a pre-export check and display results:
|
||||
- ✅ All paths closed
|
||||
- ✅ No open endpoints
|
||||
- ⚠️ High node count (suggest simplification)
|
||||
- ❌ Unconverted text objects (block export until resolved)
|
||||
|
||||
---
|
||||
|
||||
## Embeddability
|
||||
|
||||
Kerf App can run in two modes:
|
||||
|
||||
**Standalone** — accessed directly via URL, full-page experience
|
||||
|
||||
**Embedded** — dropped into an existing website as a component:
|
||||
```html
|
||||
<script src="https://your-kerf-host.com/kerf.js"></script>
|
||||
<div id="kerf-app"></div>
|
||||
```
|
||||
|
||||
In embedded mode:
|
||||
- Renders within its container div
|
||||
- All styles scoped (Shadow DOM or prefixed classes)
|
||||
- Communicates back to host page via postMessage events (e.g., on file download, on export complete)
|
||||
- No host page dependencies required
|
||||
|
||||
---
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
### Services
|
||||
```yaml
|
||||
services:
|
||||
kerf-engine: # Python/FastAPI — vector processing
|
||||
kerf-app: # Node/React — frontend build served by nginx
|
||||
kerf-server: # Python/FastAPI — app backend API
|
||||
```
|
||||
|
||||
`kerf-engine` can be deployed independently if consuming apps only need the engine API.
|
||||
|
||||
### Environment Config
|
||||
```env
|
||||
KERF_ENGINE_URL=http://kerf-engine:8000
|
||||
KERF_FONTS_DIR=/app/fonts
|
||||
KERF_PRESETS_DIR=/app/presets
|
||||
KERF_MAX_UPLOAD_MB=20
|
||||
KERF_OUTPUT_FORMATS=svg,dxf,json
|
||||
```
|
||||
|
||||
### Volumes
|
||||
```
|
||||
./fonts → /app/fonts (font .otf files + fonts.json manifest)
|
||||
./presets → /app/presets (preset JSON configs)
|
||||
./uploads → /app/uploads (temp storage, auto-purged)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Repository README
|
||||
|
||||
Must cover:
|
||||
- What Kerf is (app + engine overview)
|
||||
- Quick start (docker-compose up)
|
||||
- Engine API reference (endpoint docs, request/response shapes, preset list)
|
||||
- How to add fonts (drop .otf + update fonts.json)
|
||||
- How to add presets (drop JSON config into /presets/)
|
||||
- Embed snippet for host site integration
|
||||
- Engine standalone usage (for developers consuming just the engine)
|
||||
- Known limitations
|
||||
- License
|
||||
|
||||
---
|
||||
|
||||
## Build Order
|
||||
|
||||
Build in this sequence. Do not skip ahead.
|
||||
|
||||
1. **Kerf Engine** — pipeline, API, presets → **Checkpoint 1: Engine validation (human review)**
|
||||
2. **Canvas + Artboard** — Konva setup, artboard shape/size, basic object manipulation → **Checkpoint 2: Canvas UI review (human plays with it)**
|
||||
3. **Text + Font system** — font manifest loading, opentype rendering, path conversion
|
||||
4. **Import flow** — View 1 UI, Engine integration, vector lands on canvas
|
||||
5. **Export flow** — View 3 UI, DXF generation, pre-export validation → **Checkpoint 3: DXF opened in LightBurn/Inkscape (human confirms geometry)**
|
||||
6. **Docker packaging** — compose file, env config, volumes
|
||||
7. **Embed mode** — script tag delivery, scoped styles, postMessage events → **Checkpoint 4: Embedded in a plain HTML test page (human confirms no style bleed, component works)**
|
||||
|
||||
---
|
||||
|
||||
## Testing & Review Checkpoints
|
||||
|
||||
### Checkpoint 1 — Engine Output Quality
|
||||
Human must:
|
||||
- Submit a clean logo PNG → confirm SVG output in browser
|
||||
- Submit same file as DXF → open in Inkscape or LightBurn, confirm closed paths
|
||||
- Submit a noisy photo → compare Potrace vs VTracer output
|
||||
- Submit a complex SVG → run simplification, confirm node count drops
|
||||
- Test `sign` and `detailed` presets, confirm outputs differ meaningfully
|
||||
|
||||
**Do not build the App canvas until this is signed off.**
|
||||
|
||||
### Checkpoint 2 — Canvas UI Review
|
||||
Human must:
|
||||
- Create an artboard in each available shape
|
||||
- Add text, change fonts, resize, move
|
||||
- Add shapes and lines, test all line styles
|
||||
- Test undo/redo stack
|
||||
- Test alignment tools against artboard
|
||||
|
||||
**Do not build the import/export flows until this is signed off.**
|
||||
|
||||
### Checkpoint 3 — DXF Export Validation
|
||||
Human must:
|
||||
- Design a sign with text and an imported vector object
|
||||
- Export as DXF
|
||||
- Open in Inkscape or LightBurn
|
||||
- Confirm all paths closed, geometry clean, no open endpoints, correct scale
|
||||
|
||||
**Do not proceed to Docker packaging until this is signed off.**
|
||||
|
||||
### Checkpoint 4 — Embed Mode Validation
|
||||
Human must:
|
||||
- Drop embed snippet into a plain HTML page
|
||||
- Confirm component renders correctly inside host page
|
||||
- Confirm no style bleed into surrounding page
|
||||
- Confirm download works from embedded context
|
||||
|
||||
---
|
||||
|
||||
## Key Technical Decisions
|
||||
|
||||
| Concern | Decision |
|
||||
|---|---|
|
||||
| Engine language | Python + FastAPI (OpenCV, Potrace, VTracer all native here) |
|
||||
| Frontend framework | React + react-konva |
|
||||
| Canvas library | Konva.js |
|
||||
| Font rendering | opentype.js — same object reused for preview and DXF path extraction |
|
||||
| Font loading | Local `.otf` files + `fonts.json` manifest, resolved relative to server font volume |
|
||||
| DXF generation | makerjs (handles opentype integration and DXF export natively) |
|
||||
| Raster tracing | Potrace (Mode A) + VTracer (Mode B), OpenCV preprocessing |
|
||||
| Path simplification | Ramer-Douglas-Peucker |
|
||||
| Vectorization API | REST over HTTP, JSON responses — engine is stateless |
|
||||
| Deployment | Docker Compose, engine as independent service |
|
||||
| Embed delivery | Single bundled JS + scoped CSS via Shadow DOM |
|
||||
|
||||
---
|
||||
|
||||
## What Success Looks Like
|
||||
|
||||
- Engine accepts a dirty raster logo and returns a clean, cuttable SVG or DXF with no manual cleanup required for a typical sign input
|
||||
- Engine runs as an independent API that a separate app can consume with no knowledge of the Kerf App
|
||||
- User can upload a logo, tune vectorization with intuitive sliders, place it on a correctly-sized artboard, add business name text, and download a DXF that 1:1 represents their design at real-world scale
|
||||
- DXF opens in LightBurn with zero geometry errors and is ready to cut
|
||||
- Entire app ships as a single `docker-compose up` command
|
||||
- Component can be embedded in a third-party site with two lines of HTML
|
||||
|
||||
---
|
||||
|
||||
*Engine first. Everything else is a cakewalk once the engine is solid.*
|
||||
43
REQUIREMENTS.md
Normal file
43
REQUIREMENTS.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Requirements — Kerf
|
||||
|
||||
> Auto-generated from GSD-INITIATE.md. Canonical source: `.gsd/gsd.db`
|
||||
|
||||
## Engine Requirements
|
||||
|
||||
| ID | Description | Status | Primary Owner | Validation |
|
||||
|----|-------------|--------|---------------|------------|
|
||||
| R001 | Engine accepts raster images (PNG, JPG, BMP, TIFF, WebP) and returns clean vector output (SVG, DXF, JSON) | active | M001/S01 | POST /engine/trace returns valid SVG/DXF/JSON for each format |
|
||||
| R002 | Engine accepts existing SVG input for simplification passes via /engine/simplify | active | M001/S02 | Node count drops, shape preserved on complex SVG input |
|
||||
| R003 | Preprocessing pipeline: grayscale, denoise (bilateral), CLAHE contrast, Otsu threshold, optional Canny edge, dilation/erosion — all tunable | active | M001/S01 | Each preprocessing param produces measurable output difference |
|
||||
| R004 | Mode A vectorization via Potrace with tunable params (turdsize, alphamax, opticurve, opttolerance) | active | M001/S01 | Potrace params affect output; clean SVG from high-contrast logo |
|
||||
| R005 | Mode B vectorization via VTracer with tunable params (colormode, hierarchical, filter_speckle, etc.) | active | M001/S01 | VTracer produces better output on noisy/photographic inputs |
|
||||
| R006 | Post-processing: RDP path simplification (epsilon tunable), island detection, open path detection/repair, node count reporting | active | M001/S02 | Simplification reduces nodes; islands flagged; open paths detected |
|
||||
| R007 | DXF output AC1015+ compatible, openable in Inkscape/LightBurn with clean geometry | active | M001/S02 | DXF opens in target apps with closed paths, no errors |
|
||||
| R008 | Preset system: sign, patch, stencil, detailed, custom — stored as JSON in /engine/presets/ | active | M001/S03 | GET /engine/presets returns all; each preset produces distinct output |
|
||||
| R009 | Engine API is stateless, REST over HTTP, JSON responses with metadata | active | M001/S01 | Concurrent requests succeed; no server-side session state |
|
||||
| R010 | Output metadata includes format, path_count, node_count_total, open_paths, warnings, processing_ms | active | M001/S02 | Metadata fields present and accurate in all responses |
|
||||
|
||||
## App Requirements
|
||||
|
||||
| ID | Description | Status | Primary Owner | Validation |
|
||||
|----|-------------|--------|---------------|------------|
|
||||
| R011 | React SPA with three views: Import & Convert → Design Canvas → Export | active | M002 | Navigation between views works, state carries forward |
|
||||
| R012 | View 1: Upload raster/vector, select preset, tune sliders with live preview, accept output | active | M002/S01 | Slider changes produce visible preview updates within 300ms debounce |
|
||||
| R013 | View 2: Konva.js 2D canvas with artboard shapes (rect, square, circle, oval, shield, pennant, custom) | active | M002/S02 | Each artboard shape renders at correct real-world proportions |
|
||||
| R014 | Canvas text objects: font picker from fonts.json manifest, opentype.js rendering, convert-to-paths | active | M002/S03 | Text renders with loaded font; path conversion produces matching vectors |
|
||||
| R015 | Canvas shape objects: rectangle, circle, ellipse, line (solid/dashed/dotted/double/centerline), stroke/fill | active | M002/S02 | Each shape type renders and is selectable/moveable/resizable |
|
||||
| R016 | Object panel: layer stack, reorder via drag, visibility toggle, lock toggle per object | active | M002/S02 | Layer order affects rendering; visibility/lock toggles work |
|
||||
| R017 | Alignment tools: align left/center/right/top/middle/bottom, distribute H/V, center on artboard | active | M002/S02 | Selected objects align correctly relative to artboard or selection |
|
||||
| R018 | Undo/redo with full history stack | active | M002/S02 | Actions reversible; redo restores after undo |
|
||||
| R019 | View 3: Export as DXF/SVG/PNG; DXF layer assignment; text-to-paths enforcement; pre-export validation | active | M003/S01 | Export produces valid files; validation catches open paths and unconverted text |
|
||||
| R020 | DXF export at real-world scale with correct units (inches/mm) | active | M003/S01 | DXF dimensions match artboard size in target application |
|
||||
|
||||
## Deployment & Integration Requirements
|
||||
|
||||
| ID | Description | Status | Primary Owner | Validation |
|
||||
|----|-------------|--------|---------------|------------|
|
||||
| R021 | Docker Compose deployment: kerf-engine, kerf-app, kerf-server as separate services | active | M003/S02 | docker-compose up starts all services healthy |
|
||||
| R022 | kerf-engine deployable independently for API-only consumers | active | M003/S02 | Engine container starts alone, API responds |
|
||||
| R023 | Embed mode: script tag + div, Shadow DOM scoped styles, postMessage events | active | M003/S03 | Component renders in host page; no style bleed; download works |
|
||||
| R024 | Font system: local .otf files + fonts.json manifest, volume-mounted | active | M002/S03 | Fonts load from volume; adding new font = drop file + update manifest |
|
||||
| R025 | Engine and App architecturally decoupled — App consumes Engine via HTTP API | active | M001 | Engine has zero imports from App; App calls Engine only via HTTP |
|
||||
Loading…
Add table
Reference in a new issue