Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
# 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` |