Commit Graph
233 Commits
Author SHA1 Message Date
flanandClaude Sonnet 4.6 405413490b fix: RESET_PERSON now deletes managed Frigate files before clearing tracker
Previously reset_person wiped the local tracker but left existing Frigate
training files as orphans, causing the next run to upload a full new batch
on top of them. Now deletes all winnow-managed files from Frigate first so
the next run starts truly clean. Manually-added Frigate files are never
touched.

Also fixes a spurious warning when FRIGATE_URL is unset: the deletion step
is now skipped at info level rather than logging a misleading error. Moves
the deferred import to top-level and eliminates a double disk read.

Bumps to 0.4.1. Also fixes ruff lint violations in executor.py (import
sort, line length) and promotes the "winnow only touches files it uploaded"
callout to the README intro.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 19:58:08 +00:00
flanandClaude Sonnet 4.6 e67f2d9638 refactor: cleanup audit findings — dedup helpers, prune orphan scores, cache has_frigate_scores
- upload_tracker: extract _pick_mapped_file() private helper; get_lowest_quality_mapped_file
  and get_most_redundant_mapped_file are now one-liners over the same body
- upload_tracker: remove_frigate_file now also prunes the corresponding frigate_scores entry,
  preventing unbounded accumulation of orphaned score entries across replacement cycles
- frigate_api: get_frigate_face_counts delegates to get_all_frigate_person_files, eliminating
  the duplicated "name != 'train' and isinstance(files, list)" filter body
- executor: cache has_frigate_scores(name) as person_has_fscores before the per-file loop;
  refresh it after each remove_frigate_file call and after each scored upload, eliminating
  two redundant disk reads per at-cap file iteration
- executor: casefold() both sides of the recognize_face person-name comparison so a Frigate
  casing normalization or manual-registration casing mismatch does not silently suppress scoring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 18:55:18 +00:00
github-actions[bot] bbbac18207 chore: update lockfiles 2026-06-13 18:35:54 +00:00
flanandClaude Sonnet 4.6 6fcea587ff chore: bump version to 0.4.0, update changelog and all docs
Finalizes the 0.4.0 release:

- Version bumped to 0.4.0 in pyproject.toml
- CHANGELOG.md: add [0.4.0] section covering Frigate pre-upload scoring,
  quality replacement inversion, bootstrap fix, FRIGATE_SCORE_CEILING,
  ENABLE_FRIGATE_SCORES, removal of post-upload quality gate, and all
  doc/default corrections
- README.md: step 8 updated for dual-mode replacement, FRIGATE_SCORE_CEILING
  and ENABLE_FRIGATE_SCORES added to env var table, MIN_FACE_WIDTH and
  BLUR_THRESHOLD defaults corrected (50→90, 100→120)
- .env.example: FRIGATE_SCORE_THRESHOLD replaced with FRIGATE_SCORE_CEILING;
  QUALITY_REPLACEMENT line added; comments updated to match current semantics
- winnow/executor.py: bootstrap fix — recognize now called for all below-cap
  uploads when ENABLE_FRIGATE_SCORES=true (was gated on CEILING > 0)
- winnow/upload_tracker.py: frigate_scores schema comment corrected to
  pre-upload; get_most_redundant_mapped_file() added
- winnow/frigate_api.py: recognize_face returns (face_name, score)|None tuple
  so wrong-person scores never drive replacement or ceiling decisions
- winnow/config.py: FRIGATE_SCORE_THRESHOLD renamed to FRIGATE_SCORE_CEILING;
  ENABLE_FRIGATE_SCORES added
- tests/test_upload_tracker.py: 4 new tests for get_most_redundant_mapped_file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 18:35:11 +00:00
flanandClaude Sonnet 4.6 ed045f07dd fix: label InsightFace skip as "detection confidence" to distinguish from Frigate score
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:22:58 +00:00
flanandClaude Sonnet 4.6 110a45f467 perf: batch GET /api/faces; skip download on low confidence; batch gate tracker writes
Fetch all Frigate training files once before the upload loop instead of once
per person — for N people this reduces GET /api/faces calls from N to 1.
Falls back to per-person calls if the pre-fetch fails.

Check InsightFace detection confidence immediately after face enrichment,
before fetching the full-resolution image. Assets that fail MIN_CONFIDENCE
are skipped without downloading, saving potentially large image downloads.

Collapse the gate removal tracker writes from 3×N file ops into 2 total
via remove_and_reclassify_batch: one write to the uploaded tracker (remove
file mappings + remove from flat set) and one write to the rejected tracker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:22:08 +00:00
flanandClaude Sonnet 4.6 785c9d4a22 feat: quality gate on by default; fix replacement/gate conflict; gate-failed → rejected
Dynamic floor now always active once Frigate scores exist — new images must
score at least as well as the weakest image already in the set, with no
config required. FRIGATE_SCORE_THRESHOLD adds an explicit absolute floor on
top. Gate active state is surfaced in normal output for both cases.

Quality replacement now pre-checks the gate threshold before deleting the
worst image. If the candidate would fail the gate, replacement is skipped
entirely rather than creating a net slot loss.

Gate-failed assets are reclassified as rejected (moved from uploaded_asset_ids
to rejected_asset_ids) so they are excluded from future runs without wasting
API calls on re-upload. RESET_PERSON still clears rejected records for a true
full reset. RETRY_REJECTED can recover them if the threshold is later lowered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:10:30 +00:00
flanandClaude Sonnet 4.6 e0a5d98df6 fix: surface dynamic gate floor in normal output
When the dynamic threshold (min stored Frigate score) raises the effective
gate floor above the configured FRIGATE_SCORE_THRESHOLD, print it as a dim
info line rather than only logging at DEBUG/VERBOSE level.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:06:19 +00:00
flanandClaude Sonnet 4.6 ef5934d1af fix: stale frigate mapping reconciliation, recognize opt-out, cold start notice
- At upload start, diff tracker vs live Frigate file list and remove any
  mappings for files no longer present; corrects effective_count so manually
  deleted files don't permanently consume quota slots
- Add ENABLE_FRIGATE_SCORES config (default true); when false, skips all
  recognize_face calls and falls back to blur scores for quality replacement
- Print a dim notice when FRIGATE_SCORE_THRESHOLD is set but pre_run_count
  is zero, so users know the gate is deferred to the next run

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:51:44 +00:00
flanandClaude Sonnet 4.6 903d7f1054 fix: quality gate disabled at threshold 0; batch deletions; summary shows net count
- FRIGATE_SCORE_THRESHOLD=0.0 now fully disables the quality gate including the
  dynamic floor; a positive value is required to activate either
- Post-reconcile gate deletions are batched into one API call per person instead
  of one call per file
- Per-person summary reports gate removals and net uploaded count when the gate
  fires; grand summary includes total removed across all people
- .env.example comment updated to match the corrected opt-in behaviour

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:41:31 +00:00
flanandClaude Sonnet 4.6 f322eba380 feat: dynamic Frigate score threshold from stored set minimum
At the start of each person's upload phase, compute the minimum stored
Frigate recognition score across all currently mapped files. Use
max(config_threshold, dynamic_min) as the effective gate threshold so
new uploads must score at least as well as the weakest image already
in the training set.

Prevents overtraining well-recognised people: if all 80 images score
≥0.85, the dynamic threshold becomes ~0.85 and new additions that
score below that are removed rather than diluting a good training set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:32:47 +00:00
flanandClaude Sonnet 4.6 26b598db98 feat: post-upload quality gate via FRIGATE_SCORE_THRESHOLD
When FRIGATE_SCORE_THRESHOLD > 0, images that score below the threshold
after upload are deleted from Frigate and removed from the tracker.
Skipped when pre_run_count == 0 (cold start — no class mean to compare
against yet). Deletion happens after reconciliation so the Frigate
filename is known. Disabled by default (0.0).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:29:29 +00:00
flanandClaude Sonnet 4.6 03be6ce2cb feat: store Frigate recognition scores and use them for quality replacement
After each successful upload, call POST /api/faces/recognize to get
Frigate's own confidence score (0-1) for the uploaded crop. Store it
in the tracker as frigate_scores alongside the existing blur score.

When quality replacement activates and frigate_scores are present,
use them for the replacement comparison instead of blur scores — an
image Frigate recognizes poorly is a worse training image than one it
recognizes well, regardless of sharpness. Falls back to blur scores
on first run before any frigate_scores are populated.

Also surfaces frigate_score in TRACE_CROP_SIZE output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:21:58 +00:00
flanandClaude Sonnet 4.6 ab641847b2 fix: raise BLUR_THRESHOLD default from 100 to 120 to match Frigate's floor
Frigate classifies images with Laplacian variance < 120 as "very blurry"
and its own docs recommend avoiding blurry training data. Winnow was
accepting images in the 100-120 range that Frigate considers too blurry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:56:52 +00:00
flanandClaude Sonnet 4.6 2b1d9e8b8a chore: bump version to 0.3.3, update changelog and lockfile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:45:12 +00:00
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
github-actions[bot] c0c2d88941 chore: update lockfiles 2026-06-13 15:33:25 +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
github-actions[bot] 3ae6a86684 chore: update lockfiles 2026-06-13 14:53:14 +00:00
github-actions[bot] 621c310d5b chore: update lockfiles 2026-06-13 14:52:59 +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
github-actions[bot] 0f6b17f76f chore: update lockfiles 2026-06-13 14:26:04 +00:00
github-actions[bot] c61e4dc698 chore: update lockfiles 2026-06-13 14:26:03 +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>
v0.3.1
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
github-actions[bot] b29b98fa29 chore: update lockfiles 2026-06-13 06:53:48 +00:00
github-actions[bot] 40c2a850bc chore: update lockfiles 2026-06-13 06:53:22 +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>
v0.3.0
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