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

12 KiB

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 <g>; collapse identical attribute sets; hoist common attributes to a parent <g>; merge consecutive <path> 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 <defs> block at the document root with <symbol id="dedup-{shortHash}"> for each duplicate; replace duplicates with <use href="#dedup-{shortHash}"/>
  • 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 <glob>, --out <dir>, --preset <name>, --archive <path>, --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, <symbol>/<use> 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/<yyyy-mm-dd>-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