Merging branch 'dev' into main for release v(Nelson's Sparrow) - #48
Merged
Conversation
- Added a new `ProviderCoordinator` class to manage GPU/CPU state transitions and handle inference failures. - Introduced `ResilienceConfig` for configuring resilience behavior based on user settings. - Created `ResilientOnnxSession` to wrap `onnxruntime.InferenceSession`, allowing for session recreation on failure. - Updated `AnalysisPipeline` to utilize the new resilience features, including retrying errored images and preserving computed values. - Enhanced `QueueManager` to support retrying errored images during analysis. - Modified settings utility to include GPU resilience options. - Updated visualizer to indicate errored images and provide a UI option for re-attempting analysis on them. - Added CSS styles for errored images in the visualizer.
…roved session management
…nd UI improvements
…ports Previously every startup where the prior session didn't explicitly mark itself clean was treated as a crash, so PC reboots / logoffs / power loss all surfaced an alarming "did not shut down cleanly" dialog and generated a phantom crash report. Adds an `app_session_exit_reason` state machine (clean / os_shutdown / crash / unknown) and a new `shutdown_watch` module with platform-specific listeners (SIGTERM/SIGHUP on Linux, NSWorkspaceWillPowerOffNotification on macOS, hidden-window WM_QUERYENDSESSION/WM_ENDSESSION pump + console-ctrl fallback on Windows). The frontend now shows no dialog for `os_shutdown`, the original alarming wording for `crash`, and a softer prompt for `unknown` (SIGKILL, power loss, pre-upgrade installs). Crash reports include `exit_reason` so server-side analytics can drop the noise.
…ling-Xido1 Distinguish OS shutdowns from real crashes to suppress false reports
…ility and update test image sets
…tering - Implemented unit tests for the ratings module, covering profile thresholds, quality to rating mappings, and image display ratings. - Added tests for the reject move functionality, ensuring proper handling of file movements and undo operations. - Created tests for settings utility functions, validating coercion and sanitization of various input types. - Developed tests for speciesnet filtering functions, including box IoU calculations and detection filtering based on confidence scores.
Generated authentic settings.json fixtures by importing each tag's own
analyzer/settings_utils.py and calling save_persisted_settings() with a
seeded payload, then reading the resulting file back from disk.
Of the 17 release tags in this repo, only 5 contain settings_utils.py
(introduced at Lincolns-Sparrow, 2026-03-15). After dedup by unique
key-set:
- settings_v_Lincolns-Sparrow.json (oldest, no sanitizer)
- settings_v_Kentucky-Warbler.json (strict sanitizer, drops unknown keys)
- settings_v_Gambels-Quail.json (sanitizer + forward-compat passthrough;
same key-set as Lincolns-Sparrow, kept for value-level coercion coverage)
- settings_v_Gambels-Quail_minimal.json (counters-only edge variant)
- settings_v_Gambels-Quail_with_future_keys.json (variant used by
TestForwardCompatibility)
KEY_EVOLUTION.md documents tag-to-key-set mapping, value-level coercion
differences, monotonic-counter coverage, and forward-compat passthrough
per tag.
All 19 parametrized tests in tests/compat/test_settings_migration.py now
pass (previously skipped); all 44 tests/unit/test_settings.py tests still
pass.
The companion legacy_databases task is blocked: every ONNX/PyTorch model
in the repo is a Git LFS pointer and the LFS server in this environment
returns HTTP 502 on every fetch, so the pipeline cannot run at any tag.
legacy_databases/SCHEMA_NOTES.md documents the blocker, what was tried,
and exactly what to do to pick it up in a working LFS environment.
https://claude.ai/code/session_014JRoQ3ztrLkm1F7ZoywPqu
Agent-Logs-Url: https://github.com/SanjaySoniLV/ProjectKestrel/sessions/aca5cd48-96a2-47f9-aa4f-60ff530484b5 Co-authored-by: SanjaySoniLV <87775728+SanjaySoniLV@users.noreply.github.com>
…ure-generation test fixtures: legacy database schemas captured from release tags
…ELOPMENT.md and README.md to match.
…s-hAHdT Add legacy settings fixtures and schema evolution documentation
…ture time handling
…to perform full validation in the CI environment.
…-image traces Routes the ~140 free-form print/log calls through level-specific helpers in settings_utils, gated by the KESTREL_LOG_LEVEL env var (default INFO). Per-image and per-detection traces (decode-queue progress, SpeciesNet/SAM-HQ diagnostics, read_raw_full debug-meta, the [DEBUG] console.log lines in visualizer.js and culling.html) all drop to DEBUG so the runtime log file and crash reports surface real warnings and errors instead of trace noise. [bracket] area tags are preserved in messages for grep-filtering. Also fixes a latent import bug where kestrel_analyzer's relative-import fallbacks failed in the cli.py invocation path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lyzer/css/
Pure mechanical move — no semantic style changes. The 4,388-line stylesheet now
lives as one file per feature (base, layout, toolbar, grid, folder-tree, welcome,
timeline, legal-banner, tutorial) plus a dialogs/ subfolder (scene, settings,
analyze, live-analysis, feedback-consent, donate, tutorial-chooser, base).
visualizer.html now declares 18 <link> tags in cascade-preserving source order
(base.css first so :root vars are visible to the rest). PyInstaller specs swap
the single ('visualizer.css', '.') entry for ('css', 'css') so future additions
to the folder auto-bundle.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…uthentication headers
…vc-runtime from cross-platform requirements Version audit ahead of Nelson's Sparrow release: rawpy 0.26.1 -> 0.27.0 (newer CR3/CR2 demosaic improvements) pyinstaller 6.18.0 -> 6.20.0 (build tooling) pywebview 6.1 -> 6.2.1 (minor; JS bridge stability fixes) requests 2.33.0 -> 2.34.2 (security / patch) PyQt6 6.10.2 -> 6.11.0 (minor) msvc-runtime is Windows-only by definition; it never belonged in requirements.txt (generic / Linux). Kept in requirements-windows.txt. Held back (would benefit from a dedicated validation pass after release): onnxruntime 1.23.2 -> 1.26.0 (cross-minor; EP behavior changes) numpy 2.1.3 -> 2.4.4 (CLAUDE.md still references TF<2.2 caveat) opencv 4.11.0.86 -> 4.13.0.92 (cv2.imwrite history; see CLAUDE.md) pandas 2.2.2 -> 3.0.3 (major-version bump)
Brings the cli.py surface to parity with the "Advanced Analysis Settings"
section of the in-app Analyze Folders dialog (visualizer.html:651). Until
now only --gpu, --detection-threshold, --parallel-prefetch (and an advanced
--detector-name) were exposed.
New flags:
--wildlife-model-mode {fast,accurate}
GUI-aligned detector selector. Maps fast -> mdv1000-cedar,
accurate -> mdv5a. --detector-name still works and takes precedence.
--max-bird-crops INT (1-20)
--exposure-quality {lenient,balanced,aggressive}
--scene-time-threshold FLOAT (0-60s)
--thumbnail-max-width INT (400-2400 px)
--thumbnail-jpeg-compression FLOAT (0.50-1.00)
--wildlife / --no-wildlife
--species-detection / --no-species-detection
--retry-errored / --no-retry-errored
Pipeline plumbing:
AnalysisPipeline.process_folder() now accepts three optional override
parameters (exposure_quality, thumbnail_max_width,
thumbnail_jpeg_compression). When None, behavior is unchanged - they
fall back to settings.json. When passed, they override the persisted
values without mutating settings.json. The other knobs were already
process_folder parameters; they're now wired up to argparse.
Tests:
analyzer/tests/unit/test_cli_args.py - 35 unit tests covering argparse
defaults, per-flag parsing, enum rejection, --detector-name vs
--wildlife-model-mode precedence, full-command-line wiring through
main(), clamping at the CLI layer, and pipeline acceptance of the new
override kwargs.
The batch classifier preprocess+predict block was inlined twice (commits 0c1bfc8 / 4efb5de "Add batch processing for speciesnet classifier" appear to have been applied on top of each other). Both blocks ran classifier.preprocess_many + predict_many over the same animal_dets, and the second block's classifier_preds_by_idx = {} reset discarded the first block's work. Net effect: every image ran the SpeciesNet classifier through the ONNX batch twice, doubling per-image classifier inference time and erasing roughly half of the headline "batch processing" speedup. Removes the second duplicate block; keep only the first.
…sure correction The auto-bright branch called raw.postprocess() unconditionally and then immediately overwrote the result with a second exp_shift postprocess() when exp_correction != 0.0. The first call's RGB array was discarded but its demosaic cost (~1-2 seconds per 45MP RAW) was wasted on every culling preview where the user had any exposure slider movement. Branch on exp_correction first so postprocess() runs exactly once.
When the OS closes our window during reboot/logoff, shutdown_watch correctly writes app_session_exit_reason='os_shutdown'. But webview.start() then returns normally, the main() finally block runs _mark_session_clean_exit(), and that unconditionally clobbered the reason to 'clean'. Result: telemetry would still report 'clean' on every OS-driven shutdown, defeating the new exit-reason machinery added in fd84732. Skip the overwrite when the existing reason is already 'os_shutdown' (set by shutdown_watch) or 'crash' (set by the top-level crash handler).
_load() returned out of the candidate loop after touching the first file that existed, regardless of whether meta/sig were valid. So an official build where the first candidate path held a corrupt or empty attestation would silently fall through to legacy X-Kestrel-Key auth even though later candidates may have had a valid bundle. Also moved the try/except inside the loop so malformed JSON in one candidate no longer aborts the search across all paths.
…ION 2.0.2 -> 2.0.4 Updates analyzer/models/quality_normalization_data.csv to the v3 percentile table. The new curve shifts the lower tail upward (p0 0.0141 -> 0.0277) and slightly compresses the high tail (p99 0.9999 -> 0.9998), so the mapping of raw quality model output to percentile -> star rating moves accordingly. No code changes needed; ml/quality.py reads the file at load time. Existing kestrel_metadata.json files written by older builds keep their recorded kestrel_version, so downstream version-gating logic continues to flag pre-2.0.4 .kestrel folders as outdated and re-runs them against the new curve.
The original TF-era constraint (numpy<2.2) is gone — TensorFlow, torch, and torchvision were removed when SpeciesNet was inlined and SAM-HQ was migrated to ONNX Runtime. ONNX Runtime 1.23+ and the remaining ML stack (opencv, pandas, pillow) all support numpy 2.4.x. Local pre-build pytest (-m "not ui and not integration and not e2e") passes 312/313 against numpy 2.4.4 with the same single pre-existing test_reject_move case-sensitivity failure that fails on origin/dev.
Until now --api-probe loaded a synthetic 15-line probe HTML and the UI
test was excluded from CI entirely via -m "not ui". The probe proved the
JS<->Python bridge mechanism worked in isolation, but never proved that
the production visualizer.html + visualizer.js bundle actually saw
window.pywebview.api - which is the bug class we'd want to catch on a
frozen build before users do.
Changes:
1) visualizer.py: new --probe-target {synthetic,visualizer} flag
(default synthetic for back-compat). 'visualizer' mode spins up the
same local HTTP server the desktop app uses, points pywebview at
http://127.0.0.1:<port>/, and waits for the JS side to call
Api.report_bridge_ready. Probe payload now carries probe_target so
the result JSON tells you which mode produced it. The static-root
chdir logic was extracted into _chdir_to_static_root() so the probe
resolves _internal/visualizer.html on a frozen PyInstaller exe the
same way the real app does.
2) visualizer.js: one-line side-effect-free call to
window.pywebview.api.report_bridge_ready() right after apiReady
becomes true. Outside probe mode it returns a payload and the caller
ignores it; inside probe mode it fires the threading.Event the
Python side is waiting on.
3) tests/ui/test_pywebview_api.py: parametrized over both targets
('synthetic' on port 8799 with 30s deadline, 'visualizer' on port
8798 with 60s deadline - the real bundle has CSS/JS/taxonomy
payloads to fetch on boot). Conftest still skips the whole module
cleanly when no display / no webview.
4) Both dev CI workflows (Windows + macOS): added a "UI probe (real
visualizer.html) against frozen binary" step after --validate that
runs ProjectKestrel(.exe) --api-probe --probe-target visualizer and
asserts ok=true. continue-on-error so a probe miss doesn't block
artifact publish, but it surfaces as a workflow warning AND uploads
ui-probe-result.json as an artifact for postmortem.
PyQt6 has no imports anywhere in the codebase - the legacy PyQt GUI was removed when the app consolidated onto pywebview + visualizer.html. DEVELOPMENT.md and the test_session_lifecycle docstring already document that PyQt is gone; the package was just left pinned in all three requirements files. pywebview uses platform-default backends (EdgeWebView2 on Windows, WKWebView on macOS, GTK on Linux), not the optional Qt backend. PyInstaller specs don't reference PyQt6 in hiddenimports or excludes. Drops ~100MB from the dev pip install and shrinks PyInstaller's pickup surface (PyInstaller does scan installed-but-unused packages).
…s-Nfky2 Pre-release: package version audit + full CLI parity with Advanced Analysis Settings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I'm merging my work on branch 'dev' into main. Here are the key changes:
Performance
Resilience & crash handling
Species detection
Build & distribution
Code quality
Testing & CI
CLI (new in this PR)
Merging this into dev will trigger automatic build CI and enable the ability to run dev CI in the future; these CI builds should have a robust suite of tests now to make sure things are working.