# Tasks — Compress-SVG completion > Every checkbox MUST reflect work actually done in this change. Do not retroactively tick. The seven gaps closed here were never claimed by an earlier change; this is the first ticket that touches them. ## 0. Pre-flight (non-negotiable) - [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each) - [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest. ## Audit Note (2026-05-02) Status: **DEFERRED**. `packages/docs/compress-svg/src/` only contains `__benchmarks__`, `constants`, `core`, `parser`, `plugins`, `presets`, `types` plus `error-codes.ts`. None of the seven gaps are filled: no `optimizer/` (SvgOptimizer), no `structural/`, no `deduplicator/`, no `path-tokenizer/`, no `archive/` (SvgPacker / SvgUnpacker / SvgArchiveBuilder), no `browser/` unpacker, no `cli.ts` bin entry. This is a substantial migration estimated at ~6 000+ source lines verbatim; 0% is done. ## 1. Spec authoring - [ ] 1.1 Read `../suaveplan/packages/universal/compress-svg/src` end-to-end (no dedicated `enterprise/compress-svg.md` exists) - [ ] 1.2 Read `openspec/specs/compress-svg/spec.md` (does not exist yet — create on archive) - [ ] 1.3 Read `openspec/AGENTS.md` Sections 0a (parity), 13 (verification), 14 (archival), 14a (affected dependents) - [ ] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md` - [ ] 1.5 Fill in `## Source mapping` table — every requirement cites a line range OR appears in the "no source line" table with justification - [ ] 1.6 Draft `specs/compress-svg/spec.md` from `openspec/templates/spec-delta.md` covering all seven ADDED Requirements - [ ] 1.7 Every requirement in the spec delta carries a `**Verified by:**` line naming concrete `it(...)` titles - [ ] 1.8 `openspec change validate compress-svg-completion` exits with no errors ## 2. `SvgOptimizer` façade - [ ] 2.1 Port `optimizer/SvgOptimizer.ts` (L1-L483) and `optimizer/SvgOptimizer.types.ts` into `packages/docs/compress-svg/src/optimizer/svg-optimizer.ts` - [ ] 2.2 Define `CompressorAdapter` interface and ship a Node adapter (binds `zlib`) and a browser adapter (binds `CompressionStream`) - [ ] 2.3 Wire the optimizer to the existing plugin pipeline (`core/compress-svg`); accept a `SvgOptimizerConfig` selecting plugins, presets, and the dictionary/structural/dedup pre-passes - [ ] 2.4 Emit OTel histogram `compress_svg.optimizer.duration_ms` and counter `compress_svg.optimizer.bytes_saved` - [ ] 2.5 Throw `COMPRESS_SVG.OPTIMIZER_PIPELINE_FAILED` when any selected plugin throws; chain the cause - [ ] 2.6 Sibling `.md` (≥200 words, basic + advanced examples) ## 3. `SvgStructuralOptimizer` - [ ] 3.1 Port `structural/SvgStructuralOptimizer.ts` (L1-L458) and its `.types.ts` into `packages/docs/compress-svg/src/structural/svg-structural-optimizer.ts` - [ ] 3.2 Implement four rules: flatten single-child ``; collapse identical attribute sets; hoist common attributes to a parent ``; merge consecutive `` elements with identical `fill`/`stroke`/`style` - [ ] 3.3 Return a `StructuralOptimizationResult` with per-rule counts; expose each rule as a configurable boolean on `StructuralOptimizerConfig` - [ ] 3.4 Throw `COMPRESS_SVG.STRUCTURAL_INVALID_TREE` on parser-rejectable input - [ ] 3.5 Sibling `.md` ## 4. `SvgDeduplicator` - [ ] 4.1 Port `deduplicator/SvgDeduplicator.ts` (L1-L398) into `packages/docs/compress-svg/src/deduplicator/svg-deduplicator.ts` - [ ] 4.2 Replace `node:crypto.createHash('sha256')` with `@suaveplan/crypto-hash` `sha256` for browser/server parity - [ ] 4.3 Hoist a `` block at the document root with `` for each duplicate; replace duplicates with `` - [ ] 4.4 Return a `DeduplicationReport` (`candidatesScanned`, `duplicatesFound`, `bytesSaved`, `symbolMap`) - [ ] 4.5 Throw `COMPRESS_SVG.DEDUPE_HASH_COLLISION` when two distinct subtrees produce the same short hash (extremely rare; guarded for safety) - [ ] 4.6 Sibling `.md` ## 5. `PathTokenizer` - [ ] 5.1 Port `PathTokenizer.ts` (L1-L559) into `packages/docs/compress-svg/src/path-tokenizer/path-tokenizer.ts` - [ ] 5.2 Export `tokenizePath(d): PathToken[]` and `formatTokens(tokens: PathToken[]): string`; round-trip property test on 10 000 random valid paths - [ ] 5.3 Throw `COMPRESS_SVG.PATH_TOKEN_PARSE_FAILED` on malformed `d` strings; carry the byte offset of the first invalid character - [ ] 5.4 Sibling `.md` ## 6. Binary archive (`SvgPacker` / `SvgUnpacker`) - [ ] 6.1 Port `SvgPacker.ts` (L1-L2931) into `packages/docs/compress-svg/src/archive/svg-packer.ts` - [ ] 6.2 Port `archive/SvgArchiveBuilder.ts` (L1-L177) and `archive/SvgArchiveUnpacker.ts` (L1-L182) into `packages/docs/compress-svg/src/archive/svg-archive-{builder,unpacker}.ts` - [ ] 6.3 Preserve the legacy on-disk layout byte-for-byte (version header, manifest table, content-addressed payload table, checksum trailer) - [ ] 6.4 Throw `COMPRESS_SVG.ARCHIVE_VERSION_MISMATCH`, `COMPRESS_SVG.ARCHIVE_CHECKSUM_FAILED`, `COMPRESS_SVG.ARCHIVE_PAYLOAD_NOT_FOUND` at the right call sites - [ ] 6.5 Round-trip property test: pack a directory of N SVGs → unpack → byte-equal originals - [ ] 6.6 Sibling `.md` for each implementation file ## 7. Browser unpacker - [ ] 7.1 Port `browser/SvgArchiveUnpackerBrowser.ts` (L1-L440) and `browser/SvgUnpackerBrowser.ts` (L1-L1152) into `packages/docs/compress-svg/src/browser/` - [ ] 7.2 Replace every `Buffer`, `node:fs`, `node:zlib`, `node:crypto` import with Web-Streams / `globalThis.crypto` equivalents - [ ] 7.3 Static import scan in test asserts no Node-only API on the `./browser` subpath - [ ] 7.4 Throw `COMPRESS_SVG.BROWSER_UNSUPPORTED_ENVIRONMENT` when `globalThis.crypto?.subtle` or `CompressionStream` is missing - [ ] 7.5 Sibling `.md` for each implementation file ## 8. CLI bin - [ ] 8.1 Port `cli.ts` (L1-L271) into `packages/docs/compress-svg/src/cli/cli.ts` - [ ] 8.2 Wire `bin` field in `package.json`: `"suaveplan-svg-optimize": "./dist/cli/cli.js"` - [ ] 8.3 Build the CLI atop `@suaveplan/cli` (argument parser, command router, output formatter, spinner, JSON reporter) - [ ] 8.4 Flags: `--in `, `--out `, `--preset `, `--archive `, `--dedupe`, `--structural`, `--dry-run`, `--quiet`, `--json` - [ ] 8.5 Throw `COMPRESS_SVG.CLI_INVALID_GLOB` when no files match `--in`; exit code 2 (POSIX EX_USAGE) - [ ] 8.6 Sibling `.md` ## 9. Error codes - [ ] 9.1 Register `COMPRESS_SVG.*` codes in `error-codes.ts`: `OPTIMIZER_PIPELINE_FAILED`, `STRUCTURAL_INVALID_TREE`, `DEDUPE_HASH_COLLISION`, `PATH_TOKEN_PARSE_FAILED`, `ARCHIVE_VERSION_MISMATCH`, `ARCHIVE_CHECKSUM_FAILED`, `ARCHIVE_PAYLOAD_NOT_FOUND`, `BROWSER_UNSUPPORTED_ENVIRONMENT`, `CLI_INVALID_GLOB` - [ ] 9.2 Every code thrown by at least one production path; no orphan codes ## 10. Subpath exports - [ ] 10.1 `package.json` `exports` map gains `./optimizer`, `./structural`, `./deduplicator`, `./path-tokenizer`, `./archive`, `./browser`, `./cli` - [ ] 10.2 Each subpath is tree-shakeable (`"sideEffects": false` already set; verify by build artefact size) - [ ] 10.3 `tests/dep-graph.test.ts` pins the subpath surface ## 11. Tests - [ ] 11.1 `src/optimizer/svg-optimizer.test.ts` — pipeline orchestration, plugin selection, `OptimizationResult` shape, batch mode - [ ] 11.2 `src/optimizer/svg-optimizer.spec-conformance.test.ts` — injectable `CompressorAdapter` round-trip; pipeline-failed error code; OTel histogram emitted - [ ] 11.3 `src/structural/svg-structural-optimizer.test.ts` — every rule observable; per-rule counter accuracy - [ ] 11.4 `src/structural/svg-structural-optimizer.spec-conformance.test.ts` — invalid-tree error code - [ ] 11.5 `src/deduplicator/svg-deduplicator.test.ts` — cross-file dedup, ``/`` emission - [ ] 11.6 `src/deduplicator/svg-deduplicator.spec-conformance.test.ts` — `DeduplicationReport` shape; injected hash honoured; collision error code - [ ] 11.7 `src/path-tokenizer/path-tokenizer.test.ts` — every SVG path command; round-trip property test on 10 000 random paths - [ ] 11.8 `src/path-tokenizer/path-tokenizer.spec-conformance.test.ts` — malformed-path error code; absolute/relative flag preserved - [ ] 11.9 `src/archive/svg-packer.test.ts` — binary header, manifest, payload table, content-addressed dedup, version round-trip - [ ] 11.10 `src/archive/svg-packer.spec-conformance.test.ts` — version-mismatch / checksum-failed / payload-not-found error codes - [ ] 11.11 `src/browser/svg-unpacker-browser.test.ts` — browser unpacker round-trip with the same fixtures as the Node unpacker - [ ] 11.12 `src/browser/svg-unpacker-browser.spec-conformance.test.ts` — static-import scan asserts no Node API; missing-`globalThis.crypto` error path - [ ] 11.13 `src/cli/cli.test.ts` — argument parsing, glob expansion, in-place vs out-dir vs archive modes, dry-run, JSON reporter - [ ] 11.14 `src/cli/cli.spec-conformance.test.ts` — invalid-glob error code; POSIX exit codes - [ ] 11.15 `tests/dep-graph.test.ts` — subpath exports resolve, no Node API on `./browser` - [ ] 11.16 Every `it(...)` carries `{ timeout: 5000 }` (or 30000 for archive round-trip tests) ## 12. Documentation - [ ] 12.1 Sibling `.md` for every implementation `.ts` (≥200 words, basic + advanced examples, full API reference) - [ ] 12.2 README links every co-located `.md` under `## Modules` - [ ] 12.3 Update `CLAUDE.md` `compress-svg` package listing to reflect the restored surface ## 13. Pre-archive verification (the 5-gate triple) - [ ] 13.1 `bun run check` — Biome 0/0/0 across the monorepo - [ ] 13.2 `bunx turbo lint typecheck test --filter=@suaveplan/compress-svg` — green - [ ] 13.3 `bunx turbo test:coverage --filter=@suaveplan/compress-svg` — 100/100/100/100 - [ ] 13.4 `bun scripts/openspec-conformance.ts @suaveplan/compress-svg` — exit 0, no `error` findings - [ ] 13.5 `bunx turbo build --filter=@suaveplan/compress-svg` — green ## 14. Pre-archive 7-row checklist (AGENTS.md Section 13) - [ ] 14.1 Generics threaded; `expectTypeOf` assertions on `OptimizationResult`, `DeduplicationReport`, `StructuralOptimizationResult`, `PathToken[]` - [ ] 14.2 Zod schemas wired at every declared boundary (`SvgOptimizerConfig`, `StructuralOptimizerConfig`, `DeduplicationConfig`, `SvgArchiveManifest`, CLI args) - [ ] 14.3 Every `COMPRESS_SVG.*` code thrown by at least one production path; no orphans - [ ] 14.4 Strict-mode behaviors tested both on and off (e.g. `dedupe: true` vs `false`; archive with vs without checksum) - [ ] 14.5 OTel histograms / counters asserted with a real in-memory `MeterProvider` - [ ] 14.6 Lifecycle dedup tested where claimed: `SvgDeduplicator` idempotent on identical input; `SvgPacker` archive output stable byte-for-byte across runs (deterministic) - [ ] 14.7 No retroactive tick — every box above reflects work done in this change ## 15. Archive - [ ] 15.1 Fold spec delta into `openspec/specs/compress-svg/spec.md` (creating the file) - [ ] 15.2 Move `openspec/changes/compress-svg-completion/` → `openspec/archive/-compress-svg-completion/` - [ ] 15.3 Refresh `MANIFEST.md` if the package's exports list materially changed - [ ] 15.4 Update `STATUS.md` if the package was previously listed as Pending