Commit Graph
100 Commits
Author SHA1 Message Date
flanandClaude Sonnet 4.6 4856a6d36f fix: raise MIN_FACE_WIDTH default from 50 to 90px (8k pixel floor)
50px crops produce ~2,500–4,225 total pixels — well below Frigate's own
camera capture range of 16k–50k px. 90px guarantees ≥8,100 total pixels
even when face margins are fully clipped by image edges.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:45:12 +00:00
flanandClaude Sonnet 4.6 82dbc8502a feat: merge crop-dimension-trace into dev
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:32:27 +00:00
flanandClaude Sonnet 4.6 6311227763 chore: regenerate uv.lock for 0.3.2 version bump
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:30:14 +00:00
flanandClaude Sonnet 4.6 19416cc7b8 feat: record crop pixel dimensions and add TRACE_CROP_SIZE lookup
Store (width, height) of each face crop in the tracker at upload time
alongside the existing blur score. Expose TRACE_CROP_SIZE=<px> to look
up which Immich asset produced a crop with that pixel dimension, making
it straightforward to trace unexpected or low-quality images visible in
Frigate back to their source.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:20:54 +00:00
flanandClaude Sonnet 4.6 80c5b563b2 Merge main: bump transformers lower bound to >=5.12.0 in variant files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:54:02 +00:00
flanandClaude Sonnet 4.6 de1df19642 chore: bump transformers lower bound to >=5.12.0 in variant pyproject files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:53:56 +00:00
flanandClaude Sonnet 4.6 467f258fa5 chore: bump transformers lower bound to >=5.12.0
Was >=4.57.6; installed version is 5.12.0. Prevents users from
accidentally resolving the old 4.x series.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:52:27 +00:00
flanandClaude Sonnet 4.6 36a5fb56b6 fix: revert ruff-action to v3 (v4 major tag does not exist)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:47:31 +00:00
flanandClaude Sonnet 4.6 f90af65cd2 fix: revert setup-uv to v7 (v8 major tag does not exist)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:45:02 +00:00
flanandClaude Sonnet 4.6 cd3fad8afe chore: bump astral-sh actions to latest major versions
ruff-action v3 → v4, setup-uv v7 → v8

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:42:43 +00:00
flanandClaude Sonnet 4.6 102827537e Merge dev: bump docker actions to Node.js 24-compatible versions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:40:48 +00:00
flanandClaude Sonnet 4.6 aa12c79f9d chore: bump docker actions to Node.js 24-compatible versions
setup-qemu-action v3 → v4, build-push-action v6 → v7
Required before June 16 when Node.js 20 actions are forced to Node.js 24

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:40:37 +00:00
flanandClaude Sonnet 4.6 18f3171667 Bump version to 0.3.1 and update CHANGELOG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:25:30 +00:00
flanandClaude Sonnet 4.6 1fdb35727e Merge dev: fix CI lint, lockfile workflow, release disk exhaustion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:24:51 +00:00
flanandClaude Sonnet 4.6 870560de1a Fix CI: lint line-length, lockfile detached HEAD, release disk exhaustion
- quality.py:122: split long tuple line to satisfy E501 (146 → ≤120)
- update-lockfile.yml: add branches filter so tag pushes don't trigger
  the workflow (tag checkout is detached HEAD; git push has no target)
- release.yml: split four Docker build steps into parallel jobs (build-gpu,
  build-cpu, build-rocm, build-intel), each with its own runner; previously
  all four ran in one job and exhausted disk after GPU+CPU builds, leaving
  ROCm and Intel cancelled

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 14:23:33 +00:00
flanandClaude Sonnet 4.6 3e8dddb050 Bump version to 0.3.0 and update CHANGELOG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:52 +00:00
flanandClaude Sonnet 4.6 12789e83f2 Fix four quality-replacement bugs found by code re-audit
C1/C4: Cap blur-score computation at 1440 px before calling assess_quality
so scores are always on the same Laplacian scale as the embedding path
(which operates on Immich preview thumbnails). Also converts the image to
RGB before scoring and stores 0.0 on assess_quality failure so files
uploaded without a score remain eligible for future quality replacement
instead of occupying a slot permanently.

C2: Fall back to the tracker's mapped-filename set as the pre-upload
baseline when the Frigate GET /api/faces endpoint is unreachable at upload
start. Previously, uploads that succeeded during a partial API outage were
never mapped in frigate_files, leaving get_tracked_frigate_file_count
permanently under-counting those files and allowing Frigate to exceed
MAX_AUTO_IMAGES over time.

C3: Track min_quality_score_for_slot when a quality-replacement delete
succeeds but the subsequent upload fails. This ensures the freed slot can
only be filled by a candidate that beats the deleted file's score, not just
the next file in iteration order (which could be lower quality than what
was deleted).

Add get_tracked_frigate_filenames() to upload_tracker and expand tracker
tests to cover the new function and exclude-parameter behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:52 +00:00
flanandClaude Sonnet 4.6 8ee370406b Update README to reflect current pipeline and features
- Pipeline rewritten as 8 steps: separates thumbnail pass (quality filter +
  embeddings) from full-res download and crop; adds quality replacement
  logic at the upload step
- Persistence note updated to cover rejected IDs and RETRY_REJECTED
- Auto mode stopping threshold documented (20%/10% of median pairwise distance)
- Add OUTPUT_DIR env var (was undocumented)
- Local Install section notes interactive vs auto mode behaviour
- QUALITY_REPLACEMENT description tightened

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:52 +00:00
flanandClaude Sonnet 4.6 5ab2cf2ac5 Polish build flow and README formatting
- update-lockfile.yml: rename workflow to "Update lockfiles"
- release.yml: add explicit uv python install 3.13 for consistency with other workflows
- docker-publish.yml: rename cpu cache scope from linux/amd64-cpu to cpu (now multi-arch)
- README: add GitHub release version badge and License badge
- README: shorten QUALITY_REPLACEMENT table cell

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:52 +00:00
flanandClaude Sonnet 4.6 608ade8499 Fix build flow: lockfile bot, CPU multi-arch, paths-ignore, release guard
- update-lockfile.yml: regenerate all four lockfiles (main + cpu/rocm/intel
  variants) on any pyproject change; add variant pyproject files to trigger
- docker-publish.yml: add lockfiles to paths-ignore so the bot commit does
  not trigger a second Docker build; remove redundant CONTRIBUTING/SECURITY
  entries already covered by **.md; add QEMU to build-cpu; set CPU image to
  linux/amd64,linux/arm64 to match release
- release.yml: inline lockfile generation now covers all variants; add
  workflow_dispatch guard that fails if not dispatched from main

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:52 +00:00
flanandClaude Sonnet 4.6 7e90b8e669 Add OCI image labels, issue template config, paths-ignore, and metadata fixes
- Dockerfile: ARG VERSION + OCI labels (title, description, source, licenses, version)
- release.yml: pass VERSION build-arg to all four image builds
- docker-publish.yml: extend paths-ignore to cover community files
- .github/ISSUE_TEMPLATE/config.yml: disable blank issues, link to Discussions and wiki
- README.md: add Getting Help section
- pyproject.toml: expand description; add System Administrators audience classifier

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:52 +00:00
flanandClaude Sonnet 4.6 694e9af127 Add community scaffolding: CONTRIBUTING, SECURITY, issue templates, PR template
- CONTRIBUTING.md: dev-branch workflow, uv setup, test/lint commands
- SECURITY.md: private disclosure to holden@arch.fyi
- .github/ISSUE_TEMPLATE/bug_report.yml: structured form with image tag, versions, logs
- .github/ISSUE_TEMPLATE/feature_request.yml: problem/solution/alternatives form
- .github/PULL_REQUEST_TEMPLATE.md: checklist enforcing dev branch + passing CI
- pyproject.toml: add Changelog and Documentation URLs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:51 +00:00
flanandClaude Sonnet 4.6 2cee73fd6f Bump version to 0.3.0 and update CHANGELOG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:38 +00:00
flanandClaude Sonnet 4.6 3da5f67c21 Fix four quality-replacement bugs found by code re-audit
C1/C4: Cap blur-score computation at 1440 px before calling assess_quality
so scores are always on the same Laplacian scale as the embedding path
(which operates on Immich preview thumbnails). Also converts the image to
RGB before scoring and stores 0.0 on assess_quality failure so files
uploaded without a score remain eligible for future quality replacement
instead of occupying a slot permanently.

C2: Fall back to the tracker's mapped-filename set as the pre-upload
baseline when the Frigate GET /api/faces endpoint is unreachable at upload
start. Previously, uploads that succeeded during a partial API outage were
never mapped in frigate_files, leaving get_tracked_frigate_file_count
permanently under-counting those files and allowing Frigate to exceed
MAX_AUTO_IMAGES over time.

C3: Track min_quality_score_for_slot when a quality-replacement delete
succeeds but the subsequent upload fails. This ensures the freed slot can
only be filled by a candidate that beats the deleted file's score, not just
the next file in iteration order (which could be lower quality than what
was deleted).

Add get_tracked_frigate_filenames() to upload_tracker and expand tracker
tests to cover the new function and exclude-parameter behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:38 +00:00
flanandClaude Sonnet 4.6 ac249e6242 Update README to reflect current pipeline and features
- Pipeline rewritten as 8 steps: separates thumbnail pass (quality filter +
  embeddings) from full-res download and crop; adds quality replacement
  logic at the upload step
- Persistence note updated to cover rejected IDs and RETRY_REJECTED
- Auto mode stopping threshold documented (20%/10% of median pairwise distance)
- Add OUTPUT_DIR env var (was undocumented)
- Local Install section notes interactive vs auto mode behaviour
- QUALITY_REPLACEMENT description tightened

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:38 +00:00
flanandClaude Sonnet 4.6 0e2915f564 Polish build flow and README formatting
- update-lockfile.yml: rename workflow to "Update lockfiles"
- release.yml: add explicit uv python install 3.13 for consistency with other workflows
- docker-publish.yml: rename cpu cache scope from linux/amd64-cpu to cpu (now multi-arch)
- README: add GitHub release version badge and License badge
- README: shorten QUALITY_REPLACEMENT table cell

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:38 +00:00
flanandClaude Sonnet 4.6 cf760df930 Fix build flow: lockfile bot, CPU multi-arch, paths-ignore, release guard
- update-lockfile.yml: regenerate all four lockfiles (main + cpu/rocm/intel
  variants) on any pyproject change; add variant pyproject files to trigger
- docker-publish.yml: add lockfiles to paths-ignore so the bot commit does
  not trigger a second Docker build; remove redundant CONTRIBUTING/SECURITY
  entries already covered by **.md; add QEMU to build-cpu; set CPU image to
  linux/amd64,linux/arm64 to match release
- release.yml: inline lockfile generation now covers all variants; add
  workflow_dispatch guard that fails if not dispatched from main

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:38 +00:00
flanandClaude Sonnet 4.6 7613fdcd5b Add OCI image labels, issue template config, paths-ignore, and metadata fixes
- Dockerfile: ARG VERSION + OCI labels (title, description, source, licenses, version)
- release.yml: pass VERSION build-arg to all four image builds
- docker-publish.yml: extend paths-ignore to cover community files
- .github/ISSUE_TEMPLATE/config.yml: disable blank issues, link to Discussions and wiki
- README.md: add Getting Help section
- pyproject.toml: expand description; add System Administrators audience classifier

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:38 +00:00
flanandClaude Sonnet 4.6 b786199a4f Add community scaffolding: CONTRIBUTING, SECURITY, issue templates, PR template
- CONTRIBUTING.md: dev-branch workflow, uv setup, test/lint commands
- SECURITY.md: private disclosure to holden@arch.fyi
- .github/ISSUE_TEMPLATE/bug_report.yml: structured form with image tag, versions, logs
- .github/ISSUE_TEMPLATE/feature_request.yml: problem/solution/alternatives form
- .github/PULL_REQUEST_TEMPLATE.md: checklist enforcing dev branch + passing CI
- pyproject.toml: add Changelog and Documentation URLs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 06:52:38 +00:00
flanandClaude Sonnet 4.6 d2bc7a94b1 Merge dev into main: NOTICES + 0.2.13
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 05:55:59 +00:00
flanandClaude Sonnet 4.6 0ce67a7570 Add NOTICES file for if_curator MIT attribution; bump to 0.2.13
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 05:55:51 +00:00
flanandClaude Sonnet 4.6 650dadd102 Cap quality replacement against tracked files only, not total Frigate count
Previously, effective_count and the jobs.py cap check used the total
Frigate file count (including manually-added files), so any file a user
curated by hand ate into winnow's managed quota. Now:

- get_tracked_frigate_file_count() returns len(frigate_files) from the
  tracker — only files winnow uploaded and reconciled
- effective_count in the upload loop uses this tracker count so
  manually-added files are invisible to the cap
- jobs.py capacity check uses len(frigate_files) instead of the live
  Frigate API count or cached frigate_count
- Frigate API call for known_frigate_files_at_start is now only used
  for the post-upload reconciliation diff, not for cap enforcement

Side-effect: fixes audit bug #1 — an unreachable Frigate GET no longer
zeroes effective_count and bypasses the cap, because the cap is now
read from the always-available local tracker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 05:35:26 +00:00
flanandClaude Sonnet 4.6 1615e2e77e Merge feature/quality-replacement into dev
Adds quality replacement for Frigate face training images:
- When a person is at MAX_AUTO_IMAGES cap, replace the lowest-quality
  mapped Frigate file if a better candidate is available
- Quality score is laplacian blur variance from the quality filtering
  pipeline (stored on asset, propagated through tracker)
- Frigate filename mapping uses post-person batch reconciliation:
  poll after all uploads complete, map by filename timestamp order
  (documented race condition limitation in code)
- QUALITY_REPLACEMENT env var (default true) to disable the feature
- Bug fix: f-string TypeError when no mapped files exist (worst=None)
- CI: more aggressive runner disk cleanup to fix NVIDIA build OOM

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 05:18:26 +00:00
flanandClaude Sonnet 4.6 785ac3cbc6 Add QUALITY_REPLACEMENT to config tests; expand CI disk cleanup
Config tests now verify QUALITY_REPLACEMENT defaults to True and
respects the QUALITY_REPLACEMENT=false env override.

CI: replace minimal disk cleanup with more aggressive removal
(Android SDK ~14GB, Swift, CodeQL, docker system prune) so the
NVIDIA GPU image build no longer exhausts runner disk space.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 05:18:15 +00:00
flanandClaude Sonnet 4.6 12bccaa631 Fix TypeError when worst mapped file is None in quality replacement
When no mapped files exist for a person, worst is None and the old
f-string tried to subscript it before the conditional was evaluated.
Extracted worst_score_str as a local variable to avoid the crash.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 05:14:15 +00:00
flanandClaude Sonnet 4.6 11e7d4aad7 Implement quality score tracking and batch Frigate file mapping
- Track laplacian blur score through quality filtering pipeline
  (quality.py: blur_score on QualityResult; diversity.py: store on asset;
   executor.py: read via quality_score key)
- Replace per-file polling with post-person batch reconciliation:
  after all uploads for a person complete, poll Frigate (up to 15s)
  until the expected number of new files appear, then map by filename
  timestamp order (Frigate FIFO queue = upload order = timestamp order)
- Document race condition limitation: concurrent external uploads cause
  the batch to be skipped entirely (safe but files go unmapped); noted
  in code as requiring a Frigate API fix (return filename on upload)
- Add two assess_quality integration tests for blur_score

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 04:59:12 +00:00
flanandClaude Sonnet 4.6 4c8c219598 Fix Intel GPU Docker build: libze-intel-gpu1 renamed to level-zero
Intel renamed libze-intel-gpu1 to level-zero in their graphics repository,
breaking the amd64 Intel GPU image build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 04:09:18 +00:00
flanandClaude Sonnet 4.6 9f92e1c919 Fix Intel GPU Docker build: libze-intel-gpu1 renamed to level-zero
Intel renamed libze-intel-gpu1 to level-zero in their graphics repository,
breaking the amd64 Intel GPU image build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 04:08:22 +00:00
flanandClaude Sonnet 4.6 615c3c3cc6 Fix ruff import ordering in executor.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 03:53:28 +00:00
flanandClaude Sonnet 4.6 a0083cb9fe Fix three bugs found by code audit
- Delete failure retry loop: when delete_frigate_person_files() fails,
  remove the file from the tracker so the next candidate targets a
  different worst file rather than re-attempting the same failed delete.

- Interactive mode quality replacement: _configure_person() never set
  config["quality_replacement"], causing the executor to always default
  to False and silently skip all uploads for at-cap interactive jobs.
  Now mirrors auto_configure by reading Config.QUALITY_REPLACEMENT.

- Silent mapping loss on API flap: after a successful upload, if the
  post-upload GET /api/faces returns None (transient API failure),
  the file was silently left unmapped. Now logs a warning so users
  know quality replacement won't target that file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 03:52:04 +00:00
flanandClaude Sonnet 4.6 129c74720e Add quality replacement for Frigate face training images
When a person is at MAX_AUTO_IMAGES, winnow now replaces the
lowest-quality mapped training image in Frigate if a higher-confidence
candidate is available, keeping the training set always optimised.

Only files winnow uploaded (tracked via frigate_files mapping) are ever
replaced — manually added Frigate training images are never touched.
A concurrent-upload race condition is detected per-file: if N>1 new
files appear after one upload, the mapping is skipped rather than
guessed, logging at INFO level. The per-file snapshot approach is
retained over a batch approach because wrong mappings (which a batch
approach risks on race) are worse than no mapping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 03:44:36 +00:00
flanandClaude Sonnet 4.6 3e68128ac5 Merge dev into main — release v0.2.12
ROCm (AMD GPU) and Intel GPU support. Full notes in CHANGELOG.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 02:34:16 +00:00
flanandClaude Sonnet 4.6 927ad3a68c Merge feature/rocm-intel-support into dev
Adds ROCm (AMD GPU) and Intel GPU support as new :rocm and :intel image
variants. Full changelog in CHANGELOG.md under [0.2.12].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 02:32:18 +00:00
flanandClaude Sonnet 4.6 b2be19e259 feat: add ROCm (AMD GPU) and Intel GPU support (v0.2.12)
New image variants:
- :rocm  — InsightFace via ROCmExecutionProvider, SigLIP via PyTorch ROCm 6.3
- :intel — InsightFace via OpenVINOExecutionProvider (onnxruntime-openvino);
           Intel GPU compute runtime auto-installed from Intel graphics repo;
           OPENVINO_DEVICE=GPU opts into Arc/iGPU inference (default: CPU)

Also adds:
- pyproject-rocm.toml + uv-rocm.lock, pyproject-intel.toml + uv-intel.lock
- compose.yml device passthrough snippets for AMD and Intel
- CI: build-rocm and build-intel jobs in docker-publish.yml; all four
  variants built and tagged in release.yml
- README reworked: cleaner structure, GPU variant quick-start examples,
  OPENVINO_DEVICE env var documented
- CHANGELOG entry and version bump to 0.2.12

Fix: IntPrompt in dict literal was eagerly evaluated in the no-embedding
fallback path of _get_strategy_choice, prompting users for a custom count
regardless of which strategy they picked.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 02:31:09 +00:00
flan 8103801ec8 chore: sync dev to main (0.2.11 release)
- fix: resolve onnxruntime/onnxruntime-gpu conflict clobbering GPU support
- perf: stream thumbnail download in bounded batches to cap peak RAM
- fix: log thumbnail fetch failures; restore get() for duplicate-ID safety
- ci: cancel in-progress Docker builds on superseding push
- ci: enforce GHCR package visibility public after each push
- docs: README and wiki updated with memory guidance and GPU troubleshooting
2026-06-12 23:40:17 +00:00
flanandClaude Sonnet 4.6 a281b4b896 docs: add mem_limit guidance for CPU users in README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 23:40:07 +00:00
flanandClaude Sonnet 4.6 5345798dc1 ci: enforce GHCR package visibility public after each push
GHCR packages default private on first creation. Add a best-effort
gh api PATCH call at the end of both the multi-arch merge job and the
cpu build job so any new package version is immediately public without
requiring a manual UI step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:53:26 +00:00
flanandClaude Sonnet 4.6 046004a5d0 fix: log thumbnail fetch failures and restore get() for duplicate-ID safety
Two small correctness fixes from post-commit code review:

- except Exception: continue swallowed network/auth errors silently; add
  logger.debug so systematic failures are diagnosable in winnow.log
- batch_images.pop() regressed duplicate-asset-ID handling: if Immich
  returns the same asset ID twice within the same 32-item batch window
  (pagination edge case), the second occurrence got None and its embedding
  was silently dropped. Switching back to .get() matches the old
  thumbnail_map.get() behaviour. Peak memory is still bounded to _BATCH
  images because batch_images goes out of scope between batches.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:51:44 +00:00
flanandClaude Sonnet 4.6 86ee9a5ba2 ci: cancel in-progress Docker builds when a newer push supersedes them
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:09:03 +00:00
flanandClaude Sonnet 4.6 248b7a6270 docs: add batched thumbnail memory fix to 0.2.11 changelog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:08:02 +00:00
flanandClaude Sonnet 4.6 5b25b0df06 perf: stream thumbnail download in bounded batches to cap peak RAM
Previously all candidate thumbnails (up to 3000) were loaded into a
single dict before any processing started. At ~5 MB per decoded preview
image, 472 candidates = ~2.4 GB of thumbnail data alone, easily
exhausting a 4 GB container memory limit on CPU.

Now thumbnails are downloaded and processed in batches of 32. Each
image is pop()'d from the batch dict immediately after embedding so the
decoder memory is released before the next batch starts. Peak in-flight
thumbnail memory is now bounded to ~32 × 8 MB = ~256 MB regardless of
candidate pool size. GPU users benefit too — faster first results and
lower host RAM pressure during large runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:07:40 +00:00
flanandClaude Sonnet 4.6 82d057b235 fix: resolve onnxruntime/onnxruntime-gpu conflict clobbering GPU support
insightface 1.0.1 added a hard dep on the CPU onnxruntime package.
Combined with an incorrect override-dependencies entry in 0.2.10 that
forced onnxruntime (no platform marker) unconditionally, both packages
were installed into the venv on x86_64 Linux — the CPU package landed
last and overwrote onnxruntime-gpu, removing CUDAExecutionProvider
from the provider list.

Fix: declare the two packages as conflicting in uv's resolver so only
the correct one is installed per environment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 17:45:05 +00:00
flanandClaude Sonnet 4.6 fa6dc01366 chore: sync dev to main (0.2.10 release)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 17:01:01 +00:00
flanandClaude Sonnet 4.6 567e568c47 docs: correct CPU embedding speed estimate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 17:00:37 +00:00
flanandClaude Sonnet 4.6 32e4235384 docs: add 0.2.10 changelog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 16:51:53 +00:00
flanandClaude Sonnet 4.6 85e499a677 fix: audit — score falsy-zero, crop person mismatch, adaptive cap, suppress_output, ldconfig glob, scheduler sleep
- immich_api: `score or confidence` treated 0.0 score as falsy; use explicit None check
- diversity: same falsy-zero fix in _get_face_confidence
- diversity: _crop_face_from_thumbnail scale loop now filters by person_id (was
  using first person's imageWidth/imageHeight regardless of target in group photos)
- jobs: partially-trained auto mode kept limit="auto" for adaptive stopping, then
  caps result to remaining capacity (was converting to int, silently disabling FPS
  adaptive threshold and early-stop)
- embeddings: _suppress_output finally block wraps first dup2 in try/finally so
  stderr is always restored even if stdout restore raises OSError
- Dockerfile: ldconfig find uses python3.* glob instead of hardcoded python3.13
- scheduler: sleep until next_run instead of fixed 60s; eliminates late-fire jitter
  and unnecessary wakeups on long schedules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 16:48:24 +00:00
flanandClaude Sonnet 4.6 ad4d212df0 chore: bump version to 0.2.10
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 16:38:40 +00:00
flanandClaude Sonnet 4.6 31e7ca5af0 fix: Frigate face count API structure and Immich 401 error message
Frigate /api/faces response has person names as top-level keys with
lists of filenames — {person: [file, ...], "train": [...]}. The old
code incorrectly looked inside data["train"] as if it were a dict of
persons, causing 'list object has no attribute items' on every run.

Immich get_people() now checks for 401 before raise_for_status() and
logs a clear "API key invalid or expired" message instead of the raw
requests exception string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:58:29 +00:00
flanandClaude Sonnet 4.6 4f6967258f docs: update .env.example and README to reflect VERBOSE and current defaults
- .env.example: add VERBOSE, remove active AUTO_MODE=true (now TTY-detected),
  comment out FORCE_CPU/ENABLE_CACHE default values, update CRON_SCHEDULE
  section to document all three modes
- README: clarify that log file is always DEBUG regardless of VERBOSE

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:50:51 +00:00
flanandClaude Sonnet 4.6 882af37d8e feat: VERBOSE=true enables DEBUG-level console output
The log file already captures DEBUG unconditionally. This env var wires
the same to the Rich console handler for troubleshooting without needing
to read the log file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:49:28 +00:00
flanandClaude Sonnet 4.6 cad053e88f fix: add PYTHONPATH=/app so winnow entry point finds its package
uv sync runs before winnow/ is COPY'd into the build stage, so the wheel
uv builds contains only dist-info (no Python files). The console_scripts
entry point sets sys.path[0] to its own directory (/app/.venv/bin), not
/app, so 'from winnow.cli import main' fails at container startup.

PYTHONPATH=/app makes the package importable regardless of how Python
is invoked (script, -m, entry point, docker exec). This is preferable
to re-ordering the COPY layers, which would bust the heavy uv sync cache
on every winnow/ source change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:44:56 +00:00
flanandClaude Sonnet 4.6 3db52c2141 fix+logging: model load logging, CPU fallback, log level audit
Bugs fixed (from high-effort review):
- InsightFace CPU fallback now passes providers=['CPUExecutionProvider'] and
  wraps in _suppress_output() so broken GPU drivers don't cause fallback to
  try the same broken provider again, and C-extension noise stays suppressed
- scheduler.py: BaseException → Exception (KeyboardInterrupt already re-raised;
  winnow has no sys.exit() calls, so SystemExit would not occur, but Exception
  is the correct scope)
- compose.yml: fix inverted AUTO_MODE comment (docker run -it enables
  interactive mode via TTY, not non-interactive)

Model loading logging (embeddings.py):
- InsightFace: disk cache check, "not cached — downloading now (~300 MB)",
  "loading into memory on GPU/CPU...", "ready on GPU/CPU (Xs)"
- SigLIP: same treatment; cache path derived dynamically from model_name
  via HuggingFace slug convention (models--org--model) so it stays correct
  if the model variant ever changes

Logging level audit (INFO/DEBUG/WARNING/ERROR):
- diversity.py: internal algo steps (clustering, medoids, adaptive threshold,
  auto-stop decision) → DEBUG; final selection summaries stay INFO
- immich_api.py: "Fetching assets" and "Retained N assets" → DEBUG (callers
  already print this to the console via rprint)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:23:44 +00:00
flanandClaude Sonnet 4.6 af8bae1c45 fix: embedding cache key mismatch, scheduler path/exception, log handler leak
embeddings.py: face embedding cache used 'immich' for lookup but
'insightface' for storage, so the cache was never hit for locally-
computed embeddings. Unified to 'insightface'/'siglip' throughout.
This affects all users since ENABLE_CACHE now defaults to true.

scheduler.py: INSIGHTFACE_HOME=/models/.insightface was having
'.insightface' appended again, making buffalo_l check always report
'will download'. Also catch BaseException (not just Exception) so a
SystemExit from a library call can't silently kill all future runs.

log_config.py: handlers.clear() abandoned open FileHandler fds on
each scheduled main() call. Close each handler properly before removal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:10:44 +00:00
flanandClaude Sonnet 4.6 ac9e9530f3 fix: prompt for retry_rejected in interactive mode instead of silently applying env var
RETRY_REJECTED was silently read from the environment in _configure_person,
bypassing user control in interactive sessions. Now prompts the user with
the env var value as the default, so the setting is visible and overridable.

All other env vars in the interactive path are already correct:
YEARS_FILTER is a prompt default, ONLY_PEOPLE/SKIP_PEOPLE/MIN_FACE_COUNT
are auto_configure-only, and TRAINING_MODE/STRATEGY/OBJECT_CLASS are
always prompted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:03:16 +00:00
flanandClaude Sonnet 4.6 015db63d19 feat: empty CRON_SCHEDULE keeps container alive for manual docker exec
Three container lifetime modes via CRON_SCHEDULE:
  unset          — run once on startup, exit
  empty string   — sleep infinity; use docker exec -it winnow winnow
  cron expression — run on startup, then on schedule

This replaces the need for a separate MANUAL_MODE env var. The empty
string is a natural "I want the container alive but unscheduled" signal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:01:00 +00:00
flanandClaude Sonnet 4.6 2804b21f9e refactor: auto-detect headless mode via TTY; AUTO_MODE becomes an override
The primary use case is headless Docker, so auto mode is now the default
whenever stdin has no TTY. Interactive mode activates when a terminal is
present (docker run -it, local shell). AUTO_MODE=true remains as an
explicit override for scripting with a pseudo-TTY.

Removes AUTO_MODE=true, stdin_open, tty, and FORCE_CPU=false from
compose.yml — none are needed for headless operation. Updates README
and the interactive-mode hint in the CLI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 14:59:09 +00:00
flanandClaude Sonnet 4.6 981a86f28a fix: register all nvidia pip lib dirs with ldconfig; improve GPU warnings
The static LD_LIBRARY_PATH only covered cudnn and cuda_runtime — missing
cublas, cufft, curand, cusolver, cusparse, nvjitlink, etc. onnxruntime-gpu
needs libcublasLt.so at minimum, so GPU mode silently fell back to CPU.
Replace with a one-shot ldconfig call over every nvidia site-packages lib/
dir, which covers all packages regardless of what gets installed.

Also: remove the ambiguous directory="" from preload_dlls (use auto-search
default) and add a clear warning when CUDAExecutionProvider is absent so
the user sees actionable guidance instead of silent CPU fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 14:54:31 +00:00
flanandClaude Sonnet 4.6 ee585d4bae feat: add :cpu tag for amd64 CPU-only image
Introduces a VARIANT=gpu|cpu build arg to the Dockerfile. The cpu
variant uses ubuntu:22.04 (no CUDA base), installs torch+cpu and
onnxruntime (no GPU deps) via a separate pyproject-cpu.toml / uv-cpu.lock,
and is published as :cpu (dev-cpu on the dev branch) via a new
build-cpu CI job. Saves ~2 GB over the default GPU image.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 14:50:12 +00:00
flanandClaude Sonnet 4.6 99185c2da9 fix: auto-detect missing TTY and fall back to auto mode
When stdin has no TTY (Docker without -it), IntPrompt/Confirm raise
EOFError and crash the container into a restart loop. Treat a non-TTY
stdin the same as AUTO_MODE=true so headless runs work without any env
var configuration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 14:48:46 +00:00
flanandClaude Sonnet 4.6 196b0a5147 fix: INSIGHTFACE_HOME path, entrypoint, caching default, CRON_SCHEDULE opt-in
- Dockerfile: ENV INSIGHTFACE_HOME=/models → /models/.insightface to
  match compose.yml and .env.example; the old value caused InsightFace
  to store models at /models/models/buffalo_l (double-appended subdir)
- entrypoint.sh: use /app/.venv/bin/winnow (installed entry point)
  instead of python -m winnow.cli
- config.py: ENABLE_CACHE default false → true; embedding cache is
  always beneficial in practice; users can opt out with ENABLE_CACHE=false
- compose.yml: comment out CRON_SCHEDULE so scheduling is opt-in;
  flip ENABLE_CACHE to commented opt-out to reflect new default
- README.md: update ENABLE_CACHE default documentation to true
- tests/test_config.py: update default assertion to match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 05:36:47 +00:00
flanandClaude Sonnet 4.6 125ce54c7f fix: stale __version__, scheduler import order, quality test coverage
- __init__.py: derive __version__ from importlib.metadata instead of
  a hardcoded "0.1.0" that was six releases out of date
- scheduler.py: move winnow.cli import to module top (no more noqa);
  clean up redundant bool variables in check_models
- tests/test_quality.py: 17 tests covering all five quality check
  functions individually plus assess_quality integration cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 05:33:38 +00:00
flanandClaude Sonnet 4.6 f50d3fe1ab refactor: rename logging.py, in-process scheduler, trim .gitignore
- winnow/logging.py → winnow/log_config.py: avoids shadowing the stdlib
  logging module; log file renamed from immich_export.log to winnow.log
- scheduler.py: run main() in-process instead of subprocess.run so
  InsightFace and SigLIP models stay resident in memory across scheduled
  runs (hundreds of MB load, previously reloaded every run)
- .gitignore: replaced 200-line boilerplate with ~30 project-relevant
  patterns; removed Django/Flask/Redis/RabbitMQ/Scrapy/etc. noise
- .python-version: untracked (redundant with requires-python in
  pyproject.toml; kept in .gitignore for local pyenv users)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 05:30:37 +00:00
flanandClaude Sonnet 4.6 af92fe5fcc fix: bugs and code quality in embeddings, jobs, diversity, config
- embeddings: initialize ctx_id=-1 before try block so the except
  handler cannot NameError; move insightface_home out of try for the
  same reason
- embeddings: replace contextlib.redirect_stdout/stderr (Python-level
  only) with fd-level dup2 suppression — actually silences C extension
  noise from InsightFace during model loading
- jobs: fix frigate_count==0 falling through `or` chain; use explicit
  `is not None` check so a real zero is not treated as missing data
- diversity: thread person_id through select_diverse_assets →
  _select_by_embedding → _get_face_bbox / _get_face_confidence /
  _crop_face_from_thumbnail so group-photo assets embed the target
  person's face rather than whichever person is listed first
- config: replace type() hack for ConfigManager with a proper class

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 05:24:35 +00:00
flanandClaude Sonnet 4.6 4cac3d28d2 fix: onnxruntime-gpu is x86_64-only; use onnxruntime on arm64
onnxruntime-gpu has no arm64 wheels (manylinux_2_27_x86_64 /
manylinux_2_28_x86_64 only). uv sync --frozen failed on the arm64
image with exit code 2. Gated onnxruntime-gpu behind the x86_64
marker; arm64 and non-Linux use the CPU onnxruntime package. Added
required-environments so the lockfile is solved for both platforms.
Removed onnxruntime-gpu from override-dependencies (it had no marker
support and blocked arm64 resolution).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 05:05:25 +00:00
flanandClaude Sonnet 4.6 21208f8331 docs: update Python version to 3.13 in README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:51:08 +00:00
flanandClaude Sonnet 4.6 209e077055 fix: add-apt-repository with GNUPGHOME, true arm64 base; license to AGPLv3
curl|gpg --dearmor was silently dropping the deadsnakes key (gpg exits 0
on bad input), leaving apt unable to find python3.13. Reverted to
add-apt-repository with GNUPGHOME=$(mktemp -d) to isolate gpg from any
pre-existing agent socket. Removed --platform=\$BUILDPLATFORM from the
arm64 base so the image contains real arm64 binaries. License updated
from MIT to AGPL-3.0-or-later.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:48:28 +00:00
flan e7c1fc5d9b docs: revert Wiki label back to Docs in README 2026-06-12 04:39:31 +00:00
flan 14a36f2aea docs: rename Docs label to Wiki in README 2026-06-12 04:39:02 +00:00
flanandClaude Sonnet 4.6 8ec69b7f56 docs: clean up README badges
Title first, then badges on one consistent line. Removed redundant
Release and Lint badges. Fixed docs links to point to the wiki.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:38:01 +00:00
flanandClaude Sonnet 4.6 7bd66c4218 fix: use curl+gpg for deadsnakes PPA, arm64 base back to ubuntu:24.04
Ubuntu 26.04 ships Python 3.14, not 3.13. Reverted arm64 to ubuntu:24.04.
Both architectures now add the deadsnakes PPA by fetching the GPG key via
curl and piping through gpg --dearmor — no gpg-agent, safe under QEMU.
Removes the per-arch conditional and ARG TARGETARCH dependency in RUN commands.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:36:12 +00:00
flanandClaude Sonnet 4.6 619a96b8c3 fix: re-declare ARG TARGETARCH in each Dockerfile stage
Docker automatic platform ARGs are only in scope for FROM instructions.
$TARGETARCH in RUN commands was always empty, so the deadsnakes PPA
conditional never ran and python3.13 could not be found on the Ubuntu
22.04 CUDA base.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:30:00 +00:00
flanandClaude Sonnet 4.6 9e2a09cda9 chore: bump to v0.2.5, document CUDA 13.3 and torchvision changes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:25:31 +00:00
flan a3405108c9 build(deps): bump nvidia/cuda to 13.3.0-cudnn-runtime-ubuntu22.04
build(deps): bump nvidia/cuda from 12.9.2-cudnn-runtime-ubuntu22.04 to 13.3.0-cudnn-runtime-ubuntu22.04
2026-06-12 00:21:14 -04:00
flanandClaude Sonnet 4.6 34d4780554 feat: upgrade to Python 3.13, arm64 base to Ubuntu 26.04
All platforms now use Python 3.13. amd64 installs via deadsnakes PPA on
the Ubuntu 22.04 CUDA base; arm64 gets Python 3.13 natively from Ubuntu
26.04. Verified cp313 wheels exist for onnxruntime-gpu 1.26.0 and
torch 2.12.0+cu126. uv.lock regenerated under CPython 3.13.5.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:18:50 +00:00
flan 67b05502b3 Merge pull request #10 from sudolulo/dependabot/uv/python-deps-9abea99da4
build(deps): bump the python-deps group with 4 updates
2026-06-12 00:14:32 -04:00
flanandClaude Sonnet 4.6 3087d59558 fix: bump arm64 base to Ubuntu 26.04
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:04:20 +00:00
flanandClaude Sonnet 4.6 c59acb5e31 fix: use Ubuntu 24.04 for arm64 base to avoid deadsnakes PPA under QEMU
add-apt-repository ppa:deadsnakes/ppa fails on arm64 in GitHub Actions
because QEMU emulation doesn't support the GPG agent. Ubuntu 24.04 ships
Python 3.12 natively so the PPA is not needed. amd64 (CUDA/Ubuntu 22.04
base) still uses the PPA. PPA install is now gated on TARGETARCH=amd64.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 04:02:49 +00:00
flanandClaude Sonnet 4.6 7b4722d3f2 release: v0.2.3 — security fixes and CI improvements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 03:53:16 +00:00
flanandClaude Sonnet 4.6 444932c369 fix: remove API key from plain-text config file, add workflow permissions
- config.py: stop writing API_KEY to .immich_config.json; direct users
  to .env instead. Resolves CodeQL py/clear-text-storage-sensitive-data.
- test.yml, lint.yml: add permissions: contents: read to satisfy
  actions/missing-workflow-permissions scanner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 03:51:55 +00:00
flanandClaude Sonnet 4.6 8f2a6d3163 docs: move to GitHub wiki (https://github.com/sudolulo/winnow/wiki)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 03:48:53 +00:00
flanandClaude Sonnet 4.6 b98fc94179 ci: remove lockfile verify race condition
uv lock --check in a separate job races against the update-lockfile
bot. Replace with uv lock inline in release.yml and drop the pre-job
from docker-publish.yml entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 03:43:05 +00:00
flanandClaude Sonnet 4.6 759579fc30 feat: confidence scores, Frigate-authoritative capacity cap
- Store Immich face confidence scores per asset in frigate_uploaded_ids.json
- Add frigate_api.py: query GET /api/faces to count trained images per person
- Record Frigate training count as frigate_count in tracker for offline fallback
- MAX_AUTO_IMAGES cap now uses live Frigate count → cached frigate_count → local uploaded count
- Startup summary shows last known Frigate training count per person
- Migrate by_person entries from flat list to {asset_ids, scores, frigate_count} dict

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 03:33:40 +00:00
flanandClaude Sonnet 4.6 0e416176c6 docs: clarify use case includes people who have never been to your property
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 02:14:26 +00:00
flanandClaude Sonnet 4.6 e856cba6d7 docs: explain use case for infrequent visitors
Adds context that winnow is especially useful for people who aren't
around enough for Frigate's live detections to supply adequate training
data on their own.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 02:14:16 +00:00
flanandClaude Sonnet 4.6 e4d5f603d8 docs: remove TrueNAS references, make volume paths platform agnostic
- compose.yml: generic /path/to/winnow/... placeholder paths
- docs/setup.md: replace TrueNAS section with generic GPU passthrough guide
- docs/faq.md: remove platform-specific path example
- README.md: soften crop quality disclaimer, remove TrueNAS compose note
- CHANGELOG.md: scrub TrueNAS mentions from release notes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 02:13:29 +00:00
flanandClaude Sonnet 4.6 0a67699db4 docs: add early stage disclaimer and manual review note
Advises users to review Frigate uploads after a run and remove bad
crops manually. Links to GitHub Issues for feedback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 02:12:12 +00:00
flanandClaude Sonnet 4.6 f9b9cea84f docs: add lint fix to 0.2.1 changelog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 02:11:35 +00:00
flanandClaude Sonnet 4.6 4b6bce9b74 fix: resolve ruff lint errors in scheduler and tests
- scheduler.py: sort import block (I001)
- tests/test_config.py: remove unused os, pytest imports (F401)
- tests/test_upload_tracker.py: remove unused json, os, tempfile imports (F401)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 02:11:16 +00:00
flanandClaude Sonnet 4.6 c3bf81d490 fix: direct venv python in entrypoint, fix INSIGHTFACE_HOME path, add badges
- entrypoint.sh: replace `uv run` with `/app/.venv/bin/python` to skip
  uv's sync check which was re-downloading ruff and rebuilding the package
  on every container startup
- compose.yml, .env.example: fix INSIGHTFACE_HOME /models → /models/.insightface;
  InsightFace appends models/ to root, so /models produced /models/models/buffalo_l
- README.md: add Immich and Frigate badges from upstream
- CHANGELOG.md, pyproject.toml, uv.lock: bump to 0.2.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 02:10:01 +00:00
flanandClaude Sonnet 4.6 4529a63082 fix: add WORKDIR /app to runtime stage in Dockerfile
Runtime stage was missing WORKDIR, so uv run started from / and
couldn't find the .venv or pyproject.toml, causing "No module named
'winnow'" on container startup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 02:01:32 +00:00