Commit Graph

54 Commits

Author SHA1 Message Date
c2f9c993f2 chore: run the app from a venv and read configuration from a dotenv
Running the application depended on work_item/scripts/python, which is the
work-item helper's launcher: it probes for conda with `command -v`, cannot see a
lazily-defined shell function, and falls back to a bare system interpreter with
none of the dependencies installed. The application now installs into an ordinary
virtualenv instead.

That exposed two packaging faults. Pillow, numpy, and scipy were declared
test-only although the application imports them at runtime — thumbnails decode
through Pillow and the perceptual hash is a DCT over decoded pixels — and an
editable install failed outright because setuptools could not choose between
photo_pipeline, migrations, and work_item. Both are fixed here, and the OpenAI
client moves to a `vision` extra so a review-only install does not pull an API
client it never calls.

Config.from_env now reads `.env`, or the file named by PHOTO_PIPELINE_ENV_FILE,
before it looks at the environment. The file is parsed, never executed: KEY=value
lines, comments, optional quotes, no interpolation and no export. Anything already
exported wins, so the file is standing configuration and the shell stays the
override for one run. The archived CLI's names are mapped as aliases
(LLM_API_KEY/GEMINI_API_KEY, LLM_BASE_URL, LIBRARY), so an existing
photo_analyzer.env configures the application unchanged.

.env, *.env, .venv/, and *.egg-info/ are ignored: the real configuration file
holds a live provider key and must never be committed.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH4M1PHPwJsM9btcg8hzCs
2026-08-17 23:43:38 +02:00
74f4b1b640 US07-07: Automate Full Release Acceptance (#94) 2026-08-17 23:05:52 +02:00
05f34cdda2 US07-06: Validate Performance and Resource Bounds (#93) 2026-08-17 21:52:02 +02:00
9851e112a9 US07-05: Deliver Backup and Operational Recovery (#92) 2026-08-17 21:13:21 +02:00
d08d19c03c US07-04: Prove Concurrency and Crash Recovery (#86) 2026-08-17 11:09:29 +02:00
3fa35fe21e US07-03: Harden Media and Metadata Edge Cases (#85) 2026-08-17 01:12:57 +02:00
d2e44b5657 US07-02: Harden API Authorization and Path Boundaries (#84) 2026-08-16 23:07:36 +02:00
d143fee4d2 US07-01: Complete Donor Migration and Freeze the CLI Archive (#83) 2026-08-16 21:53:19 +02:00
9b7ee6b560 US06-06: Automate Phase F End-to-End Acceptance (#82) 2026-08-16 21:16:50 +02:00
ea65bb764c US06-05: Operate Archive and Restore in the Browser (#81) 2026-08-16 20:39:17 +02:00
fb8567284d US06-04: Plan and Execute Safe Restores (#80) 2026-08-16 19:49:21 +02:00
439eb9971e US06-03: Preserve Offline Identity and Review Evidence (#79) 2026-08-16 19:21:29 +02:00
b90d1718be US06-02: Transfer, Verify, and Remove Active Sources (#78) 2026-08-16 18:47:04 +02:00
b4b316acf5 US06-01: Configure and Preflight Archive Destinations (#77) 2026-08-16 17:38:59 +02:00
3ccef58796 US05-06: Automate Phase E End-to-End Acceptance (#76) 2026-08-16 17:05:13 +02:00
ac9943884d US05-05: Operate Uploads in the Browser (#75) 2026-08-16 16:08:32 +02:00
3c440f3d43 US05-04: Verify, Retry, and Resolve Uncertain Uploads (#74) 2026-08-16 15:31:23 +02:00
675876cad9 US05-03: Parse and Persist Uploader Outcomes (#73) 2026-08-16 15:02:33 +02:00
e4ae5da440 US05-02: Orchestrate Album Upload Batches (#72) 2026-08-16 13:06:37 +02:00
dcbd492a0d US05-01: Validate Credentials and Upload Readiness (#71) 2026-08-16 12:29:12 +02:00
799e58ca21 US04-06: Automate Phase D End-to-End Acceptance (#70) 2026-08-16 12:06:02 +02:00
8d96acd067 US04-05: Operate Rename Plans in the Browser (#69) 2026-08-16 11:52:10 +02:00
7103cb84bf US04-03: Apply and Verify Guarded Renames (#68) 2026-08-16 11:27:08 +02:00
daaa4ca67b US04-02: Journal Rename State and Preconditions (#67) 2026-08-15 15:04:49 +02:00
c7a616f4b5 US04-01: Build and Export Rename Plans (#66) 2026-08-15 14:51:47 +02:00
2d4dd7395c US03-05: Automate Phase C End-to-End Acceptance (#65) 2026-08-15 14:32:11 +02:00
703ed3167d US03-04: Review and Approve Proposals in the Browser (#64) 2026-08-15 14:07:47 +02:00
9681ced9f4 US03-03: Generate and Persist Versioned Proposals (#63) 2026-08-15 13:55:08 +02:00
c0a29ddca6 US03-02: Define Album Naming Policy (#62) 2026-08-15 13:42:49 +02:00
de4a9403cf US03-01: Aggregate Album Evidence (#61) 2026-08-15 13:10:31 +02:00
b9bcb47c68 US02-07: Automate Phase B End-to-End Acceptance (#60) 2026-08-14 12:53:45 +02:00
b59bc93fcb Merge pull request 'US02-06: Deliver Workflow, Safety, Library, Analysis, and Stats Views' (#59) from us/US02-06-workflow-safety-library-analysis-stats-views into main 2026-08-09 20:10:58 +02:00
344a41ca70 Merge pull request 'US02-05: Build the Static Application Shell' (#58) from us/US02-05-build-the-static-application-shell into main 2026-08-09 20:09:13 +02:00
457dd5bd90 US02-06: Deliver Workflow, Safety, Library, Analysis, and Stats Views
Ports the safety review and the Photo Analyzer Library/Analyze/Stats
experiences onto the shared API + service layer, and adds the Workflow
home, enforcing the pipeline gates and the one-mutating-job policy.

Backend
- migration 0005 + models: safety_reviews (append-only, latest row is the
  current decision) and analysis_results (donor photos schema re-keyed to
  asset_id).
- SafetyService: persist scores/decisions, review queue with filters, and
  the EXIF safety checkpoint (mutually-exclusive sfw/nsfw keyword written,
  read back, current_sha256 refreshed) that upload eligibility depends on.
- AnalysisService: the privacy gate — the vision provider is called ONLY for
  canonical, confirmed-SFW assets; nsfw/undecided are recorded skipped without
  a request. Provider is an injected adapter (real OpenAI-compatible Gemini
  call extracted from photo_analyzer.analyze_image; a fake in tests).
- LibraryService: Library search + Stats read model ported from webapp/query.py
  (LIKE search in place of FTS5; facets, top tags, years, albums, people).
- WorkflowService + GET /api/v1/workflow: per-stage readiness derived from the
  source tables — counts, blockers, last-run, action, and an active_job that
  drives read-only-during-jobs. Safety scoring and analysis run as durable jobs
  under the library_write lock via new domain handlers, so a second mutating
  job is refused.
- routes: workflow, safety (queue/counts/decisions/jobs), analysis
  (counts/results/jobs), library (assets/facets/stats).

Frontend
- five views (frontend/js/views.js) on the US02-05 shell: Workflow stepper
  (status text+icon, not colour alone; actions disabled with a reason while a
  job runs), Safety review (filter tabs, decide, persists across reload),
  Library (search + cards), Analyze (counts + live job log via the SSE
  adapter), Stats. Shared DOM helpers extracted to dom.js; Workflow is the home
  route.

Tests
- integration: provider-call privacy (nsfw never reaches the provider),
  sfw→nsfw flip drops analysis eligibility, decision persistence, one-mutating-
  job rejection, workflow counts, and the exiftool safety-keyword write/verify.
- e2e: Workflow cards, actions disabled+explained during a job, safety
  decide-persists-across-reload, Library search, Stats, Analyze counts.
- traceability map updated for US02-05 and US02-06.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 16:52:01 +02:00
ce5c8db5bc US02-05: Build the Static Application Shell
Add the reusable browser shell primitives Phase B views build on:

- store.js: observable store (get/set/subscribe)
- events.js: job activity adapter — SSE preferred, polling fallback,
  sharing the event `seq` as cursor so a transport switch drops nothing
- api.js: cancellable() (AbortController) + job endpoints; aborted
  requests reject with code "cancelled"

Make the SSE stream generically consumable: emit default `message`
events with the type in the JSON payload instead of `event: <type>`, so
a browser EventSource receives the open-ended type set (state:*,
claimed, …) via onmessage without enumerating it. The `event: done`
sentinel and resumable `id:` cursors are unchanged.

Tests: frontend/js/tests/ in-browser unit suite (api errors +
cancellation, store transitions, routing, SSE→polling fallback) served
over the app's static mount and driven by tests/e2e/test_frontend_shell.py,
which also asserts asset loading, deep-link + reload restore, JSON-only
/api/v1, and a clean console/network. Reuses the installed playwright —
no JS toolchain added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 15:47:52 +02:00
de20e6c8ce US02-04: Expose Job APIs and Activity Events (#57) 2026-07-16 20:15:57 +02:00
d054dcbe61 US02-03: Execute Jobs with Leases, Locks, and Recovery (#56) 2026-07-15 23:13:25 +02:00
0ebacfa544 US02-02: Persist and Coordinate Durable Jobs (#55) 2026-07-15 22:47:13 +02:00
22558a4efa US02-01: Extract Safety and Existing Web UI Donors (#54) 2026-07-15 22:37:47 +02:00
19d4ce3785 US01-07: Automate Phase A End-to-End Acceptance (#53) 2026-07-15 22:15:30 +02:00
3d5550b07f docs: clarify Kanban board limitation and label-as-board workflow (#52) 2026-07-15 22:04:28 +02:00
4b4c05339e US01-06: Review Inventory and Duplicates in the Browser (#51) 2026-07-15 21:44:48 +02:00
98e9cb2988 US01-05: Generate and Serve Managed Thumbnails (#50) 2026-07-15 21:27:40 +02:00
691d5a5651 US01-04: Detect and Decide Duplicates (#49) 2026-07-15 17:14:24 +02:00
c25d45d8f7 US01-03: Discover and Reconcile Stable Assets (#48) 2026-07-15 16:47:09 +02:00
a723b63d55 US01-02: Establish Application and Database Foundation (#47) 2026-07-15 16:33:25 +02:00
ffafa308de US01-01 follow-up: replace donor characterization iteration with YAML ledger + full suite (#46) 2026-07-15 16:13:00 +02:00
eed7c33da4 Merge pull request 'US01-01: Inventory and Characterize Donor Code' (#45) from us/US01-01-inventory-and-characterize-donor-code into main
Reviewed-on: #45
2026-07-13 16:50:42 +02:00
a0e1752ff1 US01-01: Inventory and Characterize Donor Code 2026-07-13 16:42:51 +02:00
43ffde71da chore: make work-item a reusable subproject 2026-07-13 16:21:28 +02:00