Found and fixed during a full local Docker build validation (amd64/arm64, all
four fleet targets, AI bundle installs, QA harness) and the follow-up bug
sweep requested afterward. None of the affected scripts run in CI, so these
had been silently broken indefinitely.
- docker/feature-manifest.json: pythonVersion was a flat "3.11", but the
amd64 base (Ubuntu 24.04) ships Python 3.12 while arm64 (Debian bookworm)
ships 3.11. Changed to a per-arch object matching the file's existing
convention.
- tests/qa/api-sweep.mts and verify-ai.mts: bare "@snapotter/shared" import
can't resolve since tests/ is not a pnpm workspace member, making both
silently unrunnable via their own documented command on any fresh
checkout. Switched to a relative import.
- tests/qa/generate-ledger.mts: wrote to docs/qa/ without creating the
directory first; docs/ is gitignored except COMMUNITY_GUIDE.md, so a fresh
checkout threw ENOENT.
- Seven QA Playwright spec files (input-preview, settings,
settings-extended, multifile, output-preview, pipeline-ui, smoke) had
~115 fixture() calls using directory names that don't exist. Resolved
every call programmatically against the real fixture tree.
- packages/ai/src/bridge.ts: AI dispatcher restart (happens on every bundle
install) was falsely counted as a crash, risking permanent dispatcher
disable after enough legitimate restarts within the crash window. Added a
shuttingDown flag checked at all three recordCrash() call sites.
- packages/image-engine/src/operations/auto-enhance.ts: image-enhancement
hung 40+ seconds on large RAW photos (confirmed on a real 20.2MP file) in
Sharp's .clahe() step, whose cost scales with total pixel count regardless
of tile size. Added a 16-megapixel cap above which CLAHE is skipped;
verified against the real file (40+s -> 2.0s) with no regression to other
RAW formats or normal-sized images. Fixing this surfaced a second,
smaller bug where the saturation step's CLAHE compensation boost was
keyed off the raw toggle instead of whether CLAHE actually ran.
- Two QA-harness robustness gaps closed per "fix everything, even the small
bugs": the passport-photo/erase-object input-preview tests now skip
cleanly with a clear reason on a container without their AI bundle
installed, and docker-compose.qa.yml's hardcoded project/container name
(the actual root cause of a mid-validation container swap between two
concurrent sessions) is now parameterized via QA_PROJECT_NAME.
Full validation report is local-only per repo convention.
The binary search found the right quality but sharp(buffer).toBuffer()
re-encoded at default quality 80, inflating the output (e.g. 50KB target
producing 90KB). Replaced buffer-wrapping with proper Sharp pipelines
that include .toFormat() with the proven quality. Also added progressive
dimension reduction when quality alone cannot reach the target, and
tightened tolerance to only accept at-or-below-target results.
Add colorBlindness() operation with 8 simulation matrices (Vienot/Machado)
for protanopia, deuteranopia, tritanopia, protanomaly, deuteranomaly,
tritanomaly, achromatopsia, and blue cone monochromacy.
- Fix resize 20% failure rate: add Zod refine requiring at least one
dimension, enforce integer/max constraints, clamp percentage scaling
to minimum 1px, and guard against missing metadata in withoutEnlargement
- Fix PostHog init race condition: move consent check before async import
so frontend events (search, pageview) are no longer silently dropped
- Fix identify() passing nested $set/$set_once wrappers instead of flat
properties, so version person property now appears on PostHog profiles
- Add error_code and error_message to failed tool_used analytics events
for debugging tool failures from PostHog
- Unit: 1,353 tests (42 files) — +256 new tests covering AI bridge
modules, image-engine sharpen/optimize-for-web, Zustand stores, and
icon-map validation
- Integration: 1,640 tests (57 files) — +826 new tests across all
tool routes, pipeline/progress/batch infrastructure, user-files,
edit-metadata, and a 321-test cross-format matrix
- E2E-Docker: 389 passing (20 spec files) — 6 new spec files for
batch processing, format conversion, layout, optimization,
watermark/overlay, and pipeline chains. Tests verified against fresh
Docker container with all 6 AI bundles installed.
Bug fixes discovered during testing:
- fix(compress): SVG/BMP/exotic formats crashed Sharp encoder — added
format-safety fallback to PNG
- fix(rate-limit): increase default login attempt limit from 10 to 500
per minute — previous value caused false test failures and is too
restrictive for a self-hosted app
- fix(auth.setup): wait for consent button visibility before clicking
to prevent flaky E2E-Docker auth setup
- Add Cloudflare Pages deployment for landing page (snapotter.com) and
docs (docs.snapotter.com)
- Create deploy-landing.yml and update deploy-docs.yml workflows
- Update CI to ignore apps/landing/** paths
- Fix logo transparency (remove white background) across all apps
- Recreate social-preview.png with SnapOtter branding
- Update all docs URLs from GitHub Pages to docs.snapotter.com
- Update VitePress config: light theme default, fix llms.txt paths
- Add .vitepress/cache/ and .env.* to gitignore
Move sanitizeValue, parseExif, parseGps, parseXmp into the shared
image-engine package so both strip-metadata and edit-metadata can
reuse them. Includes 13 unit tests covering all four functions.