Photo Analyzer
Integrated, restart-safe photo analysis, duplicate review, metadata, upload, and
archive workflow. Planning lives in INTEGRATED_PIPELINE_CONCEPT.md and
delivery_backlog/.
Application (photo_pipeline)
The target application lives in photo_pipeline/ (FastAPI + SQLAlchemy + Alembic).
Run it with:
python -m photo_pipeline migrate # apply database migrations
python -m photo_pipeline serve # start the API + static review UI (127.0.0.1:8000)
Configuration comes from PHOTO_PIPELINE_* environment variables (see
photo_pipeline/config.py); secrets are referenced, never logged.
Testing
One offline command runs the whole suite (unit, integration, and browser end-to-end); it needs no network and uses only deterministic synthetic fixtures:
work_item/scripts/python -m pytest tests -q
Browser end-to-end tests require a one-time Playwright browser install:
python -m playwright install chromium
Phase A acceptance gate
Phase A (Epic E01: shared identity, inventory, duplicates, thumbnails, review UI) is gated by a reproducible end-to-end suite:
work_item/scripts/python -m pytest tests/e2e tests/integration -q
tests/e2e/test_phase_a_pipeline.pylaunches the real API process against a fresh database and a deterministic fixture library, then drives scan,_IGNORE/exclusion, move reconciliation, exact/fuzzy duplicate review, thumbnail orientation, canonical selection, browser reload, and a full process restart — asserting durable API and database state after the restart.tests/story_traceability.jsonmaps every delivered story to its tests;tests/e2e/test_traceability.pyfails if a Phase A story loses coverage or a test file is left unexercised.
Phase B acceptance gate
Phase B (Epic E02: durable jobs, workflow shell, safety/analysis views) is proven through the real process and browser boundaries. One command runs the Phase B API, worker-recovery, and browser (Playwright) suites:
work_item/scripts/python -m pytest tests/e2e -m phase_b -q
tests/e2e/test_phase_b_pipeline.pylaunches the real server and durable worker as child processes and drives them only over HTTP/SSE: analysis start/progress, resumable SSE reconnect, the polling fallback, cancellation, per-asset error inspection, one-mutating-job rejection during read-only browsing, the NSFW→vision privacy gate, and durability across a full restart.tests/e2e/test_worker_kill.pykills a worker mid-item and proves a fresh worker resumes the fenced job (the "resume" journey).tests/e2e/test_analysis_browser.pystarts a job from the Analyze view and watches live progress arrive over the browser's real SSE adapter.
The full Phase B regression, including the unchanged Phase A gate, is the whole end-to-end suite:
work_item/scripts/python -m pytest tests/e2e -q
Phase C acceptance gate
Phase C (Epic E03: album evidence, naming policy, versioned proposals, Albums view) is proven through the real process and browser boundaries. One command runs the Phase C API and browser (Playwright) suites with the deterministic naming provider:
work_item/scripts/python -m pytest tests/e2e -m phase_c -q
tests/e2e/test_phase_c_pipeline.pydrives a real server over HTTP: evidence aggregation, generation through the deterministic naming fake (asserting the exact provider inputs and that no file path or asset ID ever reaches it), provider failure and retry, invalid names, path-separator sanitization, editing with optimistic versions, stale-evidence approval refusal, valid approval, and durability across a full restart.tests/e2e/test_albums_ui.pycovers the browser journeys: evidence display, editing, prompt validation, collision guidance, approval, stale conflict, and keyboard operation.- Both suites assert that no fixture path changes — Phase C proposes names and never renames.
The deterministic naming provider is enabled only by test configuration
(PHOTO_PIPELINE_FAKE_NAMING_LOG); without it the application falls back to the
offline naming-policy name. Phase A and B suites remain green in the full run above.