You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fixed
Starting a fresh AI-batch source scan (e.g. of /downloads) no longer fails immediately with "ERROR: expected_revision is required to update existing batch …"._start_ai_batch_job creates — and bumps the revision of — the batch's durable store row before it unblocks the worker thread, but on a fresh (non-recover) run the worker's _run_ai_batch_import built a brand-new _ai_batch_initial_state, which carries no revision. Its first _ai_batch_commit therefore attempted an un-revisioned update of the already-existing row, which the ARCH-010 optimistic-concurrency (CAS) contract correctly rejects. The worker now loads the persisted row first (for the non-recover path too) and threads its current revision through every commit, so the scan proceeds and the stale-writer guard stays fully intact. The CAS contract itself is unchanged — an un-revisioned update of an existing batch is still refused; the fix is in the caller, which now passes the revision the state was last read at. Regression test: tests/test_ai_batch_retry_race.py::FreshBatchSourceScanRevisionTests.
Added
TEMPORARY boot-trust diagnostic (to be removed once the live cause is confirmed). When the folder-MBID resolver's Lidarr call is REJECTED by the outbound URL policy (ProviderOutcome.REJECTED), backend.lidarr_rgid_resolver now emits a single WARNING (once per process) capturing, from the live serving process's own job thread, why the configured-URL trust was not active there: configured_url("lidarr"), whether a resolver is registered and its value, backend.app_runtime.LIDARR_URL, the recomputed implicit_rule_for_url(configured_url) (would_trust_rule), the live _TRUSTED_RULE, and the id()s of the backend.app_runtime / backend.security / backend.provider_boundary module objects. It logs only non-secret base URLs (userinfo stripped) and rule/module ids — never the Lidarr API key — fires only on the rejection, and changes no behavior otherwise. Diagnoses whether the live cause is the empty-resolver shadow (fixed below) or a duplicate module instance; remove after the v0.13.3 deploy confirms it.
Fixed
A provider's configured-URL trust can no longer be lost in the long-lived serving process when its registered resolver comes back empty. The outbound boundary grants each operator-hosted provider implicit trust of its own configured URL (provider_boundary.configured_url), resolved either from a registered resolver (Plex, Lidarr) or, as a robust fallback, from the backend.app_runtime constant the service actually calls (_RUNTIME_URL_CONSTANTS: Lidarr, slskd, qBittorrent, yt-dlp PO). A registered resolver that returned "" — e.g. a module-level closure in a route module that loaded before the setting was present, which is exactly how the running server could differ from a fresh import app — previously shadowed that fallback, so the configured URL earned no trust and the provider's own call to a private-LAN address (the Lidarr folder-MBID resolver's 192.168.0.250:8686, ProviderOutcome.REJECTED, "blocked by outbound URL policy") was rejected. configured_url now falls through to the _RUNTIME_URL_CONSTANTS / sys.modules["backend.app_runtime"] value whenever the resolver yields nothing, so registering a resolver can only ADD a trust source, never remove the one the provider already had. Trust stays scoped to exactly the one configured scheme/host/port; the SSRF guard is unchanged for every other address and provider. New real-app-context regression tests (tests/test_lidarr_trust_boot_activation.py) boot the app the way the server does and assert the Lidarr configured URL is admitted through provider_boundary.opened end to end, including on a worker thread (the job engine runs jobs on threads), plus the resolver-fallthrough guard.