fix(cleanup): library scans survive navigation, reconnect on return
CI / frontend-build (push) Successful in 24s
CI / lint (push) Successful in 2s
CI / backend-lint-and-test (push) Successful in 11s
CI / integration (push) Successful in 2m56s

The transparency / single-color audit cards held the run + poll timer in
local component state, so navigating away destroyed both and onMounted never
reconnected — the Celery scan kept running and writing LibraryAuditRun, but
the UI forgot it. Now each card, on mount, fetches its rule's latest run
(GET /api/cleanup/audit?rule=<rule>&limit=1) and rehydrates: shows progress +
resumes polling if still running, or shows the completed result (ready/applied/
error) so the operator can act on it after returning. Adds the ?rule= filter
to the audit-history endpoint + cleanup store latestAuditForRule().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 16:55:20 -04:00
parent 91b0145bc8
commit 5a6a95682d
5 changed files with 58 additions and 6 deletions
+8 -5
View File
@@ -154,12 +154,15 @@ async def audit_history():
limit = min(int(request.args.get("limit", "20")), 100)
except ValueError:
return _bad("invalid_limit")
# Optional rule filter so a card can reconnect to ITS latest run on mount
# (?rule=transparency&limit=1) — the audit survives navigation; the UI
# rehydrates from this rather than losing the in-flight scan.
rule = request.args.get("rule") or None
async with get_session() as session:
rows = (await session.execute(
select(LibraryAuditRun)
.order_by(LibraryAuditRun.id.desc())
.limit(limit)
)).scalars().all()
stmt = select(LibraryAuditRun).order_by(LibraryAuditRun.id.desc())
if rule is not None:
stmt = stmt.where(LibraryAuditRun.rule == rule)
rows = (await session.execute(stmt.limit(limit))).scalars().all()
return jsonify({"runs": [_serialize_audit_run(r) for r in rows]})