Files
2026-05-27 23:29:53 +01:00

5.5 KiB
Raw Permalink Blame History

Proposal: Enterprise-grade migration of @suaveplan/emblem-solver

  • Change ID: enterprise-emblem-solver
  • Package: @suaveplan/emblem-solver
  • Status: PROPOSED
  • Tier: universal (zero browser/Node/React runtime deps)
  • Category / plugin: media / image
  • Target path: packages/media/image/emblem-solver/
  • Design: ./design.md
  • Spec delta: ./specs/emblem-solver/spec.md

Summary

Migrate @suaveplan/emblem-solver from upstream (/mnt/s/web/suaveplan/packages/universal/emblem-solver, v1.0.3) onto genesis at enterprise quality, version 0.1.0. The package decomposes an RGBA pixel grid (pixel-art sprite emblem) into the minimum number of axis-aligned coloured rectangles and emits a compact SVG by layering them with z-index so that overlap optimisations reduce the rect count below what any non-overlapping cover can achieve.

The actual upstream implementation is a layered, multi-strategy solver — not a single-pass scan-line — and the migration preserves that algorithmic depth verbatim while adding Zod input validation, an EMBLEM_SOLVER.* error code registry, injectable Rng, and OTel instrumentation.

Why

Genesis ships no emblem decomposition tool. Downstream consumers (packer-browser, future game-server emblem packing, future client-side guild/clan badge editors) need an enterprise-grade SVG converter for pixel-art emblems. Upstream proves the algorithm works against well-understood game-emblem fixtures (chessboard, donut, cross, T-shape, circle, star, flag, shield, crest); we port those fixtures verbatim as the algorithmic regression suite.

Hand-rolling a replacement would be worse than porting. The upstream package is small (~2k LOC) and self-contained: zero @suaveplan/* runtime deps. It fits cleanly into genesis without dragging a dependency train.

What changes

A net-new universal-tier package at packages/media/image/emblem-solver/. No upstream consumer exists on genesis (grep -r "@suaveplan/emblem-solver" packages/ returns nothing), so this is purely additive.

Affected dependents

  • None on genesis today. Downstream packages will start importing the public surface listed below.

Parity Checklist

Capability Upstream Migrated Notes
convert(pixels, w, h, options) pure entry point ✅ ✅
analyseGrid exported ✅ ✅
parseRGBABuffer + fromIndexedGrid ✅ ✅
buildSVG ✅ ✅
4-connected island flood-fill ✅ ✅
Dual-axis histogram maximal-rect sweep ✅ ✅
Strip decomposition (h+v, picks smaller) ✅ ✅
Enhanced greedy cover (3-key tiebreak) ✅ ✅
Per-island independent cover ✅ ✅
Overlap optimiser (bbox-merge, extend-to-subsume, 3→2 cross replace) ✅ ✅
Adjacent-rect merge pass ✅ ✅
Redundancy elimination across z-stacked layers ✅ ✅
Heap's-algorithm permutations + factorial ✅ ✅
Static orderings (area-desc / area-asc / island-aware / perimeter-desc) ✅ ✅
auto mode: exhaustive ≤ N!, heuristics+random sampling above ✅ ✅ Random branch now uses injectable Rng (default Math.random)
ConvertResult.colourAnalysis exposed ✅ ✅
Solid / fully-transparent / single-pixel edge cases ✅ ✅
Sprite fixtures (chessboard, donut, cross, T, circle, star, flag, shield, crest, stripes) ✅ ✅ Ported verbatim
Zod-validated ConvertOptions 🚫 ⬆️ Enhanced — runtime schema + EmblemSolverError(INVALID_OPTIONS)
Structured error hierarchy 🚫 ⬆️ New EMBLEM_SOLVER.* registry via defineCodes
OTel counter + histogram on convert 🚫 ⬆️ New
Deterministic RNG injection 🚫 ⬆️ New — replaces the lone Math.random shuffle site
Property-based tests via fast-check 🚫 ⬆️ New on geometric kernels (rect cover correctness, SVG round-trip)
Co-located .md per impl file partial ✅ Per CLAUDE.md doc rule
Subpath exports (./types, ./errors, ./svg, ./io, ./grid) 🚫 ⬆️ New for tree-shaking

No upstream feature is dropped.

Impact

  • Code: ~1.8k LOC pure TypeScript in packages/media/image/emblem-solver/src/.
  • Build: Standard universal-tier vite build + dts emit.
  • Tests: 100/100/100/100 via provider: 'istanbul'. Upstream's snapshot fixtures (chessboard8x8, cross3x3, cross6x6, tShape, donut5x5, circle16, flagCrosses, multiIslandStripes, shieldEmblem32, crest12x12, star) ported verbatim as the deterministic regression set.
  • Docs: README at package root + sibling .md per impl file.
  • STATUS.md: flips @suaveplan/emblem-solver – → ✅. Counter 289 → 290 migrated; 204 → 203 pending.
  • Manifest: regenerated via bun scripts/folder-reorg/generate-manifest.ts.

Risks & mitigation

Risk Mitigation
auto mode blows up for >8 colours Hard maxPermutations cap (default 40320 = 8!), heuristic fallback identical to upstream; Zod refines maxPermutations ≥ 1
Non-determinism from Math.random Injectable Rng (defaults to Math.random for back-compat); tests pass a seeded fake to lock the random-shuffle branch
Coverage gaps in the heuristic branch Dedicated tests construct >8-colour fixtures to exercise the heuristic + sampling path
Floating-point density / score determinism All scoring is deterministic given inputs; no Date.now / module-scope Math.random