fix(audit-g4): status-enum miss batch
CI / lint (push) Successful in 3s
CI / backend-lint-and-test (push) Successful in 14s
CI / frontend-build (push) Successful in 19s
CI / intimp (push) Successful in 3m48s
CI / intapi (push) Successful in 7m37s
CI / intcore (push) Successful in 8m18s

Five extension-miss findings from the 2026-06-02 audit, where a status
value was added on one side but a downstream consumer didn't pick it up.

- download_service._phase3_persist: explicit branches for
  ImportResult.status in ('failed','refreshed'). For 'failed' (archive
  probe crash from _import_archive), unlink the source file so the
  filesystem scanner doesn't re-import and re-crash on the same
  archive forever. 'refreshed' is currently unreachable from the
  download path (no deep=True) but matches the importer's documented
  contract; treat as 'attached'.

- gallery-dl backfill auto-complete now gates on dl_result.success +
  no error_type, not just return_code==0 + files_downloaded==0.
  VALIDATION_FAILED exits the subprocess with returncode=0 and
  files_downloaded=0 when every file was quarantined, matching the
  prior predicate exactly and zeroing the operator's armed backfill
  budget on the FIRST quarantine run instead of decrementing.

- attach_in_place archive dispatch now threads artist + source_row
  through _import_archive (and _import_media for archive members)
  and _supersede. The path-walk fallback (_resolve_artist) is still
  used by filesystem-import; the download path now binds
  ImageProvenance to the explicit subscription Source instead of
  rediscovering by (artist_id, platform).

- Three FE handlers now recognize status:'deferred' from
  /api/sources/<id>/check: SubscriptionsTab.onCheck (was toasting
  "event #undefined"), SubscriptionsTab.checkAll (was counting
  deferred as queued), DownloadEventRow.onRetry (was saying
  "re-queued" when nothing was). Pattern matches DownloadsTab.onRetryAll
  which already had it.

- celery_signals._queue_for now maps backup/admin/library_audit
  prefixes to 'maintenance' (matching task_routes). TaskRun.queue
  was returning 'default' for those rows, so per-queue dashboard
  filters and per-queue threshold overrides (added in G3) silently
  missed them.
This commit is contained in:
2026-06-02 16:04:59 -04:00
parent e30f50e6fe
commit 4bff1d8558
5 changed files with 124 additions and 22 deletions
@@ -412,6 +412,17 @@ function onArtistCreated(artist) {
async function onCheck(source) {
try {
const body = await store.checkNow(source.id)
// Audit 2026-06-02: /api/sources/<id>/check returns 202 with
// `{status:'deferred', cooldown_until}` when the platform is in
// cooldown — the previous handler treated this as success and
// toasted "event #undefined", masking that nothing was enqueued.
if (body?.status === 'deferred') {
toast({
text: 'Check deferred — platform in cooldown',
type: 'info',
})
return
}
toast({
text: `Check enqueued (event #${body.download_event_id})`,
type: 'success',
@@ -465,17 +476,23 @@ async function onBackfill(source) {
async function checkAll(group) {
let ok = 0
let conflict = 0
let deferred = 0
for (const s of group.sources) {
if (!s.enabled) continue
try {
await store.checkNow(s.id)
ok += 1
const body = await store.checkNow(s.id)
// Audit 2026-06-02: deferred (202 + cooldown_until) used to be
// counted as queued, inflating the success tally and hiding
// that the cooldown actually held the work back.
if (body?.status === 'deferred') deferred += 1
else ok += 1
} catch (e) {
if (e?.body?.download_event_id) conflict += 1
}
}
const parts = []
if (ok) parts.push(`${ok} queued`)
if (deferred) parts.push(`${deferred} deferred (cooldown)`)
if (conflict) parts.push(`${conflict} already running`)
toast({
text: parts.join(', ') || 'Nothing to check (no enabled sources)',