Maintenance-queue health + modal/tagging keyboard pass #80

Merged
bvandeusen merged 8 commits from dev into main 2026-06-07 10:31:01 -04:00
Owner

All CI-green on dev (run 670; 671 is a compose-only follow-up).

Maintenance queue — long runs no longer block (operator-flagged 24h failures)

  • Chunk + self-resume the library audit (f2e9ae0, alembic 0039): scan_library_for_rule ran one 2h pass that timed out and hogged the concurrency-1 queue. Now ~10-min chunks that persist a keyset cursor + matches and re-enqueue until done; recovery sweep measures last_progress_at so a progressing audit isn't killed.
  • Chunk + self-resume tag standardization (d9d502a): normalize_tags_task timed out at 40 min on a large back-catalog. Now 600s chunks + re-enqueue; UI is fire-and-forget ("Queued — runs in the background").
  • Dedicated maintenance_long lane (c217009, 68cda61): backups / audits / admin one-shots run on a separate worker so they can never starve the quick recovery sweeps / vacuum. Compose adds the worker; scoped to /images, and the dead /downloads mount is dropped.
  • Vacuum DiskFull → Postgres shm_size: 512m (64MB /dev/shm was too small for VACUUM). Backup timeout → 12→30/35 min (a183be7).

Modal / tagging keyboard pass

  • Re-fixed two regressions (22dc516): the tag-chip kebab (used the v-menu pattern that doesn't open inside the teleported modal — now uses the proven explicit pattern) and ESC-after-accept (now keys off the event origin, not a DOM-wide overlay query).
  • Dropdown scroll-into-view, fandom-dialog autofocus + select-to-confirm, Tab-to-accept, T// jump-to-tag, and a ? cheatsheet (1819caa).

Deploy note: needs docker-compose up -d (new worker + shm_size) and a fresh frontend bundle.

🤖 Generated with Claude Code

All CI-green on dev (run 670; 671 is a compose-only follow-up). ## Maintenance queue — long runs no longer block (operator-flagged 24h failures) - **Chunk + self-resume the library audit** (`f2e9ae0`, alembic 0039): `scan_library_for_rule` ran one 2h pass that timed out and hogged the concurrency-1 queue. Now ~10-min chunks that persist a keyset cursor + matches and re-enqueue until done; recovery sweep measures `last_progress_at` so a progressing audit isn't killed. - **Chunk + self-resume tag standardization** (`d9d502a`): `normalize_tags_task` timed out at 40 min on a large back-catalog. Now 600s chunks + re-enqueue; UI is fire-and-forget ("Queued — runs in the background"). - **Dedicated `maintenance_long` lane** (`c217009`, `68cda61`): backups / audits / admin one-shots run on a separate worker so they can never starve the quick recovery sweeps / vacuum. Compose adds the worker; scoped to `/images`, and the dead `/downloads` mount is dropped. - **Vacuum DiskFull** → Postgres `shm_size: 512m` (64MB `/dev/shm` was too small for VACUUM). **Backup timeout** → 12→30/35 min (`a183be7`). ## Modal / tagging keyboard pass - **Re-fixed two regressions** (`22dc516`): the tag-chip kebab (used the v-menu pattern that doesn't open inside the teleported modal — now uses the proven explicit pattern) and ESC-after-accept (now keys off the event origin, not a DOM-wide overlay query). - Dropdown scroll-into-view, fandom-dialog autofocus + select-to-confirm, Tab-to-accept, `T`/`/` jump-to-tag, and a `?` cheatsheet (`1819caa`). Deploy note: needs `docker-compose up -d` (new worker + shm_size) and a fresh frontend bundle. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
bvandeusen added 8 commits 2026-06-07 10:27:19 -04:00
fix(modal): tag-chip kebab + ESC-after-accept + autocomplete scroll-into-view
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 18s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m2s
22dc516dc7
Two regressions the operator re-flagged (the earlier "fixes" didn't work):

#711 tag-chip kebab: TagPanel's kebab used the #activator + v-bind="props"
v-menu pattern — the exact pattern SuggestionItem's own comment documents as
NEVER toggling inside the teleported ImageViewer modal. Extracted TagChip.vue
using the proven explicit pattern (activator="parent" + :open-on-click="false"
+ a manual v-model), mirroring the working suggestion kebab. Now opens.

#700 ESC-after-accept: the guard suppressed close whenever ANY non-tooltip
overlay was active anywhere in the DOM, so a stray overlay after accepting a
suggestion (focus drops to <body>) blocked Esc. Now key off the event origin —
only defer to an overlay when Esc is pressed from INSIDE its content
(ev.target.closest('.v-overlay__content')); a stray overlay no longer traps the
modal, and dialogs/menus still handle their own Esc.

A1: TagAutocomplete arrow-nav now scrollIntoView's the highlighted row — the
list is capped at 240px and arrowing past the fold left the active item
off-screen (operator-flagged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(modal): keyboard-friendly tagging — fandom dialogs, Tab-accept, jump hotkey, cheatsheet
CI / lint (push) Successful in 2s
CI / backend-lint-and-test (push) Successful in 27s
CI / frontend-build (push) Successful in 29s
CI / integration (push) Successful in 3m1s
1819caaf5b
Operator-requested modal/tagging keyboard improvements:
- A2/A3: fandom dialogs autofocus their autocomplete on open; in the character-
  creation FandomPicker, picking a fandom (keyboard Enter or click) confirms in
  one step. FandomSetDialog stays autofocus-only (its Save can trigger a merge).
- B5: Tab accepts the highlighted autocomplete row (standard convention).
- C9: T or / jumps focus to the tag input from anywhere in the modal.
- C8: ? toggles a keyboard cheatsheet (corner hint advertises it; Esc closes the
  cheatsheet first, then the viewer).

Builds on the same-batch regression fixes (kebab #711, ESC-after-accept #700,
autocomplete scroll-into-view). B6 (keep focus after add) is covered — the input
retains focus after adding a tag, and Esc now works after accepting a suggestion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(tags): time-box + self-resume the tag standardization (stop the 40-min timeout)
CI / lint (push) Failing after 2s
CI / backend-lint-and-test (push) Failing after 9s
CI / frontend-build (push) Successful in 18s
CI / integration (push) Failing after 2m17s
d9d502a60d
normalize_tags_task timed out at the 40-min hard limit on a large back-catalog
(the first run recases the whole booru vocabulary) — operator-flagged, and it
monopolized the concurrency-1 maintenance queue while doing so.

normalize_existing_tags now takes time_budget_seconds: the live run stops
cleanly at the budget and reports {partial, remaining}. The task runs 600s
chunks and re-enqueues itself until nothing remains (idempotent — commits per
group, so the next chunk skips already-canonical groups). Short chunks let the
recovery sweep and other maintenance tasks interleave instead of being blocked
for 40 minutes.

Frontend: the Standardize button is now fire-and-forget ("Queued — runs in the
background; re-run Preview to confirm") instead of poll-until-done, which would
have falsely reported "complete" after the first chunk.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scan_library_for_rule ran one 2-hour pass that timed out on large libraries and
held the concurrency-1 maintenance queue the whole time, starving vacuum/backup/
normalize (operator-flagged — it was the dominant entry in the 24h failures).

It now runs ~10-min chunks and re-enqueues itself until the library is
exhausted, matching the operator's preferred pattern (reasonable timeout → retry
queued → other things process between). New columns (alembic 0039):
resume_after_id persists the keyset cursor so a chunk continues where the last
left off; last_progress_at lets the recovery sweep tell a progressing multi-
chunk audit from a dead one (it now measures staleness from last_progress_at,
not started_at). Matches accumulate across chunks. soft/hard limits dropped
2h→15/16.7 min so the in-chunk budget fires first; a soft-limit backstop
re-enqueues to resume instead of erroring the whole run.

Tests: time-box → re-enqueue (status stays running); resume carries prior
matches and appends new ones. Existing full-scan tests unchanged (small sets
finish in one chunk).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(infra): bump Postgres shm_size (vacuum DiskFull) + raise DB-backup time limit
CI / lint (push) Failing after 3s
CI / backend-lint-and-test (push) Failing after 12s
CI / frontend-build (push) Successful in 24s
CI / integration (push) Failing after 2m23s
a183be7e6e
Two more maintenance-queue failures from the operator's 24h list:
- vacuum_analyze died with "could not resize shared memory segment to 67MB: No
  space left on device" — Docker's default /dev/shm is 64MB, too small for
  VACUUM (ANALYZE)'s parallel-worker shared memory. Set the postgres service
  shm_size: 512m.
- backup_db_task timed out at its 12-min limit once the DB grew; a pg_dump can't
  be chunked, so raise it to 30/35 min. (A long backup still briefly holds the
  concurrency-1 lane — the structural fix is a dedicated lane for long one-shots.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(tags): move _NORMALIZE_CHUNK_SECONDS above the decorator (syntax error)
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 21s
CI / backend-lint-and-test (push) Successful in 25s
CI / integration (push) Successful in 3m3s
f4f49d407e
The constant + comment landed BETWEEN @celery.task(...) and the function def,
which is a syntax error that broke the whole tasks.admin import (cascaded to
lint E999 + every backend/integration test). Move it above the decorator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(maintenance): dedicated maintenance_long lane for long one-shot tasks
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 24s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m3s
c217009425
Even chunked, a single concurrency-1 maintenance lane is fragile — a 30-min DB
backup or a multi-chunk library audit holds the slot and delays the quick
self-healing recovery sweeps / vacuum (operator-flagged 2026-06-07: long runs
must never block quick maintenance).

Route the long one-shots — backup.*, admin.* (normalize/re-extract/cascade-
delete), library_audit.* — to a new `maintenance_long` queue served by a
dedicated worker (concurrency 1), added to docker-compose (+ dev override). The
scheduler keeps the quick `maintenance` lane (sweeps, vacuum, cleanup) for
itself, so a backup can no longer starve a 5-min vacuum. UI queue list +
routing tests updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(compose): maintenance-long needs only /images; drop dead /downloads mounts
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 27s
CI / integration (push) Successful in 3m2s
68cda6114d
Operator-flagged: /downloads was never mapped in prod and everything worked —
confirmed nothing in the app references a filesystem /downloads (only the
unrelated /api/downloads route). Dropped the dead mount from web/worker/
scheduler, and scoped the new maintenance-long worker to just /images (backups
write to /images/_backups; audits + admin tasks all operate on /images).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bvandeusen merged commit 91265df3d6 into main 2026-06-07 10:31:01 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/FabledCurator#80