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,80 @@
# OpenSpec Change: enterprise-image-quantisation
- **Change ID**: enterprise-image-quantisation
- **Package**: `@suaveplan/image-quantisation`
- **Category**: media / image
- **Target path**: `packages/media/image/image-quantisation`
- **Status**: PROPOSED
- **Date**: 2026-05-21
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/image-quantisation.md`
- **Design**: ./design.md (not required for migration)
- **Spec delta**: ./specs/image-quantisation/spec.md
---
## Summary
Migrate `@suaveplan/image-quantisation` from `../suaveplan/packages/universal/image-quantisation/` into genesis at enterprise quality. Preserve the full upstream public API while adding Zod validation, structured errors, OTel instrumentation, property-based tests, and 100% coverage.
---
## Current-state audit (genesis)
The package did not exist on genesis prior to this change. This is a fresh migration.
---
## Source mapping
| Requirement | Source lines | Notes |
|---|---|---|
| Zero workspace deps | N/A | upstream used `tslib` + `image-q` only |
| OTel adapter | N/A | genesis-wide enhancement |
| Zod schemas | N/A | genesis-wide enhancement |
| `defineCodes` error registry | N/A | genesis-wide enhancement |
| 100% coverage | N/A | genesis-wide policy |
| Pool detection | upstream `src/analysis/pools.ts` | verbatim algorithm, restructured |
| Edge analysis | upstream `src/analysis/edges.ts` | verbatim algorithm, added 1×1 test |
| Complexity classification | upstream `src/analysis/classify.ts` | verbatim heuristics, clamped min colours |
| Quantisation | upstream `src/quantise/quantise.ts` | preserved `image-q` wrapper, added Zod + OTel |
| Adaptive pipeline | upstream `src/pipeline.ts` | preserved logic, added Zod + OTel |
| Spatial cleanup | upstream `src/spatial/cleanup.ts` | preserved logic, removed unreachable guard |
---
## Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
| `analyseComplexity` | ✅ | ✅ | |
| `detectPools` | ✅ | ✅ | |
| `poolDistribution` | ✅ | ✅ | |
| `analyseEdges` | ✅ | ✅ | |
| `colourKey` / `parseColourKey` | ✅ | ✅ | |
| `rgbDistance` / `weightedDistance` | ✅ | ✅ | |
| `quantise` | ✅ | ✅ | Added Zod validation + OTel |
| `quantiseAdaptive` | ✅ | ✅ | |
| `spatialCleanup` | ✅ | ✅ | |
| `countUniqueColours` | ✅ | ✅ | |
| `adaptivePipeline` | ✅ | ✅ | Added Zod validation + OTel |
| Zod schemas | 🚫 | ✅ | Genesis enhancement |
| OTel instrumentation | 🚫 | ✅ | Genesis enhancement |
| Structured errors | 🚫 | ✅ | Genesis enhancement |
| Property-based tests | 🚫 | ✅ | Genesis enhancement |
| 100% coverage | 🚫 | ✅ | Genesis enhancement |
---
## Impact
### Affected dependents
Run `bunx turbo ls --filter=...@suaveplan/image-quantisation` to enumerate.
- No dependents currently in genesis (verified by turbo ls).
---
## Deferred
None.

View File

@@ -0,0 +1,228 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: image-quantisation
**Package**: `@suaveplan/image-quantisation`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
## ADDED Requirements
### Requirement: Package provides image complexity analysis
The package SHALL analyse raw RGBA pixel buffers and classify image complexity into one of four tiers: pixel-art, vector-clean, vector-aa, or photographic.
#### Scenario: Solid-colour image classified as pixel-art
- **WHEN** a solid-colour 32×32 buffer is passed to `analyseComplexity`
- **THEN** the returned `tier` is `"pixel-art"`, `uniqueColours` is 1, and `recommendedColours` ≤ 32
**Verified by:** `src/analysis/classify/classify.test.ts::"classifies solid colour as pixel-art"`
#### Scenario: Gradient classified as photographic
- **WHEN** a smooth gradient 64×64 buffer is passed to `analyseComplexity`
- **THEN** the returned `tier` is `"photographic"`, `uniqueColours` > 100, and `recommendDithering` is `true`
**Verified by:** `src/analysis/classify/classify.test.ts::"classifies gradient as photographic"`
#### Scenario: Vector-clean tier detected
- **WHEN** a 32×32 image with 64 colours in 4×4 blocks is analysed
- **THEN** the returned `tier` is `"vector-clean"`
**Verified by:** `src/analysis/classify/classify.test.ts::"classifies vector-clean for moderate colours with medium pools"`
#### Scenario: Vector-aa tier detected
- **WHEN** a 64×64 image with 256 colours in 4×4 blocks is analysed
- **THEN** the returned `tier` is `"vector-aa"`
**Verified by:** `src/analysis/classify/classify.test.ts::"classifies vector-aa for high colour count with structure"`
---
### Requirement: Package provides pool detection and distribution
The package SHALL detect contiguous same-colour regions via 4-connected flood fill and compute pool size distribution statistics.
#### Scenario: Single pool for solid colour
- **WHEN** `detectPools` is called on an 8×8 solid red buffer
- **THEN** it returns exactly one pool with `size` 64 and colour `{r:255,g:0,b:0,a:255}`
**Verified by:** `src/analysis/pools/pools.test.ts::"finds 1 pool for a solid colour"`
#### Scenario: Pool distribution computed correctly
- **WHEN** `poolDistribution` is called on pools from a checkerboard
- **THEN** `singletonRatio` is 1 and `mean` is 1
**Verified by:** `src/analysis/pools/pools.test.ts::"detects high singleton ratio in checkerboard"`
---
### Requirement: Package provides edge analysis
The package SHALL analyse edges between adjacent pixels and return sharpness metrics.
#### Scenario: Solid image has zero hard edges
- **WHEN** `analyseEdges` is called on a solid-colour buffer
- **THEN** `hardEdges` is 0 and `meanEdgeDistance` is 0
**Verified by:** `src/analysis/edges/edges.test.ts::"solid image has zero hard edges"`
#### Scenario: 1×1 image has zero edges
- **WHEN** `analyseEdges` is called on a 1×1 buffer
- **THEN** `totalEdges` is 0, `sharpnessRatio` is 0, and `meanEdgeDistance` is 0
**Verified by:** `src/analysis/edges/edges.test.ts::"1x1 image has zero edges"`
---
### Requirement: Package provides colour quantisation
The package SHALL quantise RGBA pixel buffers to a target palette size using wrapped `image-q` algorithms.
#### Scenario: Gradient reduced to target colours
- **WHEN** `quantise` is called on a 32×32 gradient with `colours: 16`
- **THEN** `paletteSize` ≤ 16
**Verified by:** `src/quantise/quantise.test.ts::"reduces a gradient to target colour count"`
#### Scenario: All three algorithms supported
- **WHEN** `quantise` is called with `algorithm: "wuquant"`, `"neuquant"`, and `"rgbquant"`
- **THEN** each result has `paletteSize` ≤ 8
**Verified by:** `src/quantise/quantise.test.ts::"supports different algorithms"`
#### Scenario: Invalid dimensions throw structured error
- **WHEN** `quantise` is called with `width: 0`
- **THEN** it throws `QuantisationError` with code `QUANTISATION.INVALID_DIMENSIONS`
**Verified by:** `src/quantise/quantise.test.ts::"throws on invalid dimensions"`
---
### Requirement: Package provides spatial AA cleanup
The package SHALL merge small pools into nearest spatial neighbours below a colour distance threshold.
#### Scenario: Near-colour AA pixels merged
- **WHEN** `spatialCleanup` is called on an AA edge buffer with default options
- **THEN** the unique colour count drops from 3 to 2
**Verified by:** `src/spatial/cleanup/cleanup.test.ts::"merges near-colour AA pixels into spatial neighbours"`
#### Scenario: Pool size threshold respected
- **WHEN** `spatialCleanup` is called with `maxPoolSize: 0`
- **THEN** no merge occurs and colour count stays 3
**Verified by:** `src/spatial/cleanup/cleanup.test.ts::"does not merge pools above maxPoolSize"`
---
### Requirement: Package provides adaptive pipeline
The package SHALL orchestrate analysis → spatial cleanup → quantisation in a single call, selecting strategy based on complexity tier.
#### Scenario: Pixel-art skips quantisation
- **WHEN** `adaptivePipeline` is called on a solid-colour buffer
- **THEN** `quantisation` is `null` and `finalColours` is 1
**Verified by:** `src/pipeline/pipeline.test.ts::"skips quantisation for pixel-art"`
#### Scenario: Photographic images are quantised
- **WHEN** `adaptivePipeline` is called on a gradient
- **THEN** `quantisation` is not `null` and `finalColours` ≤ 256
**Verified by:** `src/pipeline/pipeline.test.ts::"quantises photographic images"`
#### Scenario: Spatial cleanup applied for vector-aa
- **WHEN** `adaptivePipeline` is called on a vector-aa image
- **THEN** `spatialCleanupApplied` is `true`
**Verified by:** `src/pipeline/pipeline.test.ts::"applies spatial cleanup for vector-aa images"`
---
### Requirement: Package validates options via Zod schemas
All public entry points that accept options SHALL validate them through exported Zod schemas before processing.
#### Scenario: Invalid options rejected by quantise
- **WHEN** `quantise` is called with `colours: 1`
- **THEN** it throws `QuantisationError` with code `QUANTISATION.PROCESSING_FAILED`
**Verified by:** `src/quantise/quantise.test.ts::"throws on invalid options"`
---
### Requirement: Package emits OpenTelemetry instruments
The package SHALL emit a counter and histogram for `quantise` and `adaptivePipeline` invocations.
#### Scenario: Instruments exist and are lazily initialised
- **WHEN** `quantise` is called
- **THEN** internal `getCounter()` and `getDuration()` functions return valid OTel instruments
**Verified by:** `src/quantise/quantise.test.ts::"reduces a gradient to target colour count"` (implicit via execution path)
---
### Requirement: Package defines stable error codes
The package SHALL declare error codes via `defineCodes("QUANTISATION", [...])` and throw `QuantisationError` with those codes.
#### Scenario: Every code is reachable
- **WHEN** invalid inputs are provided to `quantise` and `adaptivePipeline`
- **THEN** each of `INVALID_PIXEL_BUFFER`, `INVALID_DIMENSIONS`, and `PROCESSING_FAILED` is thrown at least once
**Verified by:** `src/quantise/quantise.test.ts::"throws on invalid dimensions"` AND `src/quantise/quantise.test.ts::"throws on mismatched buffer size"` AND `src/pipeline/pipeline.test.ts::"throws on invalid options"`
---
### Requirement: Package provides property-based tests
The package SHALL include fast-check property tests that verify invariants across random inputs.
#### Scenario: Palette size invariant
- **WHEN** random RGBA buffers and colour counts are quantised
- **THEN** `result.paletteSize` ≤ requested colours
**Verified by:** `src/property.test.ts::"quantise palette size <= requested colours"`
#### Scenario: Output dimensions invariant
- **WHEN** random RGBA buffers are quantised
- **THEN** output `width`, `height`, and `pixels.length` match input
**Verified by:** `src/property.test.ts::"quantise output has same dimensions as input"`
---
### Requirement: Package includes benchmarks
The package SHALL include mitata benchmarks for `quantise`, `analyseComplexity`, and `adaptivePipeline`.
#### Scenario: Benchmark file exists and compiles
- **WHEN** the benchmark file is inspected
- **THEN** `src/__benchmarks__/quantisation.bench.ts` exists and imports the public API
**Verified by:** manual inspection of `src/__benchmarks__/quantisation.bench.ts`

View File

@@ -0,0 +1,86 @@
# Tasks: enterprise-image-quantisation
**Change ID**: enterprise-image-quantisation
**Package**: `@suaveplan/image-quantisation`
**Target path**: `packages/media/image/image-quantisation`
---
## 1. Scaffold
- [x] Create directory `packages/media/image/image-quantisation/` with `src/`, `src/__benchmarks__/`, and feature subdirectories.
- [x] Write `package.json` at version `0.1.0` with `"type": "module"`, `"sideEffects": false`, full `exports` map.
- [x] Write `tsconfig.lib.json` extending `tsconfig.base.json`.
- [x] Write `vitest.config.ts` with istanbul thresholds at 100/100/100/100.
- [x] Write `vite.config.ts` with multi-entry lib build.
- [x] Add `image-q` to root `package.json` catalog.
## 2. Types + errors
- [x] Write `src/types/common.ts` — all domain types.
- [x] Write `src/types/common.md`.
- [x] Write `src/errors/image-quantisation-error.ts` — `QuantisationError`, `QUANTISATION_CODES`.
- [x] Write `src/errors/image-quantisation-error.md` and `.test.ts`.
## 3. Schemas
- [x] Write `src/schemas/options.schema.ts` — Zod schemas for all public options.
- [x] Write `src/schemas/options.schema.md`.
## 4. Analysis modules
- [x] Write `src/analysis/colour/colour.ts` + `.test.ts` + `.md`.
- [x] Write `src/analysis/edges/edges.ts` + `.test.ts` + `.md`.
- [x] Write `src/analysis/pools/pools.ts` + `.test.ts` + `.md`.
- [x] Write `src/analysis/classify/classify.ts` + `.test.ts` + `.md`.
## 5. Quantisation
- [x] Write `src/quantise/quantise.ts` — wraps `image-q` with Zod + OTel.
- [x] Write `src/quantise/quantise.test.ts` + `.md`.
## 6. Spatial cleanup
- [x] Write `src/spatial/cleanup/cleanup.ts` + `.test.ts` + `.md`.
## 7. Pipeline
- [x] Write `src/pipeline/pipeline.ts` — adaptive workflow with Zod + OTel.
- [x] Write `src/pipeline/pipeline.test.ts` + `.md`.
## 8. Benchmarks
- [x] Write `src/__benchmarks__/quantisation.bench.ts`.
## 9. Property tests
- [x] Write `src/property.test.ts` with fast-check invariants.
## 10. Root barrel + README
- [x] Write `src/index.ts` with explicit named re-exports.
- [x] Write `README.md`.
## 11. Gates
- [x] `bunx turbo lint --filter=@suaveplan/image-quantisation` — green.
- [x] `bunx turbo typecheck --filter=@suaveplan/image-quantisation` — green.
- [x] `bunx turbo test --filter=@suaveplan/image-quantisation` — green.
- [x] `bunx turbo test:coverage --filter=@suaveplan/image-quantisation` — 100/100/100/100.
- [x] `bunx turbo build --filter=@suaveplan/image-quantisation` — green.
- [x] `bun run docs:check` — 100%.
- [x] `bun scripts/openspec-conformance.ts packages/media/image/image-quantisation` — 0 errors.
## 12. Spec + archive
- [x] Write `openspec/changes/enterprise-image-quantisation/specs/image-quantisation/spec.md`.
- [x] Update `openspec/changes/enterprise-image-quantisation/proposal.md` with parity checklist.
- [x] Update `openspec/changes/enterprise-image-quantisation/tasks.md`.
- [x] Fold spec delta into `openspec/specs/image-quantisation/spec.md`.
- [x] Move `openspec/changes/enterprise-image-quantisation/` → `openspec/archive/2026-05-21-enterprise-image-quantisation/`.
## 13. Manifest updates
- [x] Run `bun scripts/folder-reorg/generate-manifest.ts`.
- [x] Update `STATUS.md` — flip `@suaveplan/image-quantisation` from `–` to `✅`.
- [x] Update `SUMMARY.md` with package description.