Ship-readiness review of main...3.3-dev (multi-agent + codex verify pass)
surfaced 10 confirmed defects; all fixed with red-first tests (+24):
- config: masked list secrets restored by identity match, not index —
deleting/reordering a notification provider no longer corrupts the
survivor's credentials; unresolvable masks return 400 instead of guessing
- statics: mount dist/fonts — self-hosted Inter 404'd in every production
build and the whole UI fell back to OS fonts
- boot_overlay: chown replaced trees to the app user (crash-loop under
UMASK=077); snapshot-and-restore around the EXDEV in-place fallback so a
mid-copy failure can't gut /app/module
- updater: rollback refuses to swap without the retained signed backup
bundle — reverts to image version honestly instead of half-swapping
- qbittorrent: credential-failure latch (cleared on settings save) plus
single-flighted auth sharing the negative result with queued waiters —
failed logins no longer accumulate into a WebUI IP ban
- download_client: holder refcount registered before auth (no mid-login
close), stale clients accumulated in a list (no pool leak on rapid
settings saves), holder released on cancellation during __aenter__
- entrypoint: ownership marker written only after chown -R succeeds
- aria2: torrents_info provides num_seeds/num_leechs/eta/added_on and maps
states to qB vocabulary (UI showed undefined / NaNhNaNm / raw states)
- program API: deprecated GET aliases for /restart /start /stop /shutdown
(3.2 automation compatibility)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
Two things dominated container start:
- The LLM SDKs (anthropic + openai + google-genai) imported at module load
via parser.analyser/__init__ → llm.py, costing ~0.7s on every boot even
though the LLM parser is off by default. Move those three heavy imports
into the provider branch of __init__ and each _parse_* method, so they
load only when an LLMParser is actually built. Measured: importing
parser.analyser drops 782ms → 203ms; none of the SDKs are in sys.modules
after the startup import chain. httpx/httpx_socks stay module-level (cheap,
and the proxy tests patch them by module path).
- entrypoint.sh ran `chown -R /app/data /app/config` unconditionally every
start — O(files), which a large poster cache on a NAS turns into the
dominant startup cost. Walk the tree only when ownership needs fixing
(first run, changed PUID/PGID, or a wrong-owned volume root), tracked by a
marker; the legacy bangumi.json migration chowns its one file explicitly so
the gate can't miss it. /home/ab (image-internal, tiny) is still chowned
every time.
Also regenerates the auto-imports dts for onResponseError (exported for the
401 test in the previous commit).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
- update: snapshot DB before promote; rollback restores it only when the
schema actually advanced, never from a stale on-disk fallback; snapshots
are standalone (no WAL sidecars); dependency sync moved to apply time
with a staged venv, plus boot-time venv reconcile after rollback;
entrypoint restart loop forwards signals so docker stop stays graceful
- downloader: AddResult carried through the client protocol; qB "Fails."
only classified duplicate when the magnet hash is confirmed present;
aria2 partial-success batches report ADDED, magnet followedBy gids are
migrated and metadata stubs skipped in torrents_info; renamed paths
persisted per gid so the rename loop converges
- core: AppContext lifecycle serialized under one lock with running state
derived from the scheduler; setup wizard starts background loops on
completion
- parser: LLM path gains episode_type classification (LLM results only),
timeout/cache/concurrency/breaker controls, proxy-aware SDK clients
closed after a grace period on config reload
- webui: useApi returns an {ok} discriminant; config save and RSS refresh
surface failures instead of false success
- migrations: per-statement guards so a crash mid-migration recovers
Implemented via Codex (P2-P10 from tasks/todo.md), then reviewed with a
multi-agent verify pass; 10 confirmed review findings fixed on top.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
Adds one-click update from the settings page so routine updates no
longer require pulling a new Docker image.
- CI attaches update-bundle-<version>.zip (+ .sha256) to each release:
backend module tree, built webui, and a manifest; the image bakes
/app/IMAGE_VERSION as the baseline
- boot_overlay.py (shipped in the image, outside /app/module so it can
never overwrite its own deciding logic) runs from entrypoint before
the app starts: if a persisted overlay in /app/config/updates is
newer than the image it tree-replaces /app/module and /app/dist and
runs `uv sync` when the lockfile changed; if the image is newer it
clears the stale overlay. Applied via the config volume, so updates
survive container recreate; a newer pulled image always wins
- GET/POST /api/v1/update/{check,apply,rollback} (auth-gated, repo
pinned to EstrellaXD/Auto_Bangumi): check queries GitHub releases
with a stable/beta channel; apply downloads, verifies sha256, stages,
promotes current->backup, then exits the process so Docker restarts
into the overlay; rollback swaps the backup back. Progress streams
over the existing SSE channel
- Settings card (Log page) shows current/latest, release notes, channel
toggle, update + rollback, with a restart:unless-stopped requirement
note; compose examples updated
Requires restart: unless-stopped for self-restart (documented; no
Docker-socket fallback). 1043 backend tests, mypy 0, ruff clean; 135
frontend tests, vue-tsc clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTnuEgR78cBk32bLNKar3z
Reduces container startup time on low-spec devices (TrueNAS etc.) by limiting recursive chown to /app/data, /app/config, /home/ab instead of /app.
Closes#969 (related performance).
Replace pip + requirements.txt with uv for dependency management.
- Multi-stage Dockerfile using ghcr.io/astral-sh/uv builder image
- CI updated to use astral-sh/setup-uv@v4
- Ruff config moved to [tool.ruff.lint] (fixes deprecation)
- Transitive deps removed, missing direct deps added (requests, PySocks, urllib3)
- Database layer migrated to async (AsyncSession + aiosqlite)
- Tests updated to match async database interface
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>