audit-g5: architectural debt — 4 bundles (A/B/C/D) #53
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Group 5 of the 2026-06-02 multi-system drift audit. Four sub-bundles of architectural debt fixes:
Bundle A — small cleanups (
75c63e1)auth_type_forreplaces hardcoded('discord','pixiv')tuple indownload_service— a 7th token-platform now picks the right credential path automatically.recompute_centroid.delayso the target's centroid reflects its new image set immediately (was: 24h drift window via dailylist_drifted).backfill_thumbnailsfinally onbeat_schedule(daily) — task docstring claimed periodic Beat but the entry was never registered; library got no self-healing thumbnail repair.modal.createAndAddpushes a kind='fandom'tag intotagsStore.fandomCacheso FandomPicker sees it on next open instead of needing a full page reload..webpfromcleanup_service.unlink(thumbnailer only writes .jpg/.png)effective_datefrom/api/gallery/scrollresponse (no FE consumer reads it)store.recentMinute→store.recentRunsacross 3 consumers (data is last-200-runs, not last-60s; the name lied)Bundle B — race-poisoning in 3 sites (
8d75ade)Three SELECT-then-INSERT sites that lost the race under concurrent writers / recovery-sweep replays, poisoning the outer transaction with an unrecoverable
IntegrityError. All three now usebegin_nestedsavepoint + IntegrityError rollback + re-SELECT, mirroringimporter._get_or_create.importer._capture_attachment(PostAttachment.sha256UNIQUE)TagService.find_or_create(partial uniqueness on(name, kind, COALESCE(fandom_id, -1)))ArtistService.find_or_create(had IntegrityError handling but usedsession.rollback()— now savepoint, so the surrounding request's progress isn't lost)Bundle C — Fernet key safety on partial restore (
4df9817+ conftest fix at9f7261b)CredentialCrypto._load_or_create_keywas silently minting a new Fernet key whenever the file was missing. Failure mode the audit flagged: partial restore where DB was restored but/images/secrets/was lost → working-looking system where every authenticated download fails AUTH_ERROR until operator re-uploads every credential by hand.Two opt-ins now required for auto-create:
bootstrap_ok=Truekwarg (tests, scripts), orCURATOR_BOOTSTRAP_NEW_KEY=1env var (operator first-time-setup opt-in)Otherwise raises
MissingCredentialKeyso the app fails fast at startup. Loud log on key creation. Docstring corrected (was: "images/data root" / actual:/images/secrets/credential_key.b64).tests/conftest.pysets the env var at module load so existing test suites work unchanged.Operator note: if your running container has
/images/secrets/credential_key.b64already present, no behavior change. If somehow the file is missing on the next deploy, the app refuses to start with a clear message telling the operator to restore from backup OR set the env var.Bundle D — ErrorType chip on FailingSourcesCard (
f05aaa7)Alembic
0032addsSource.error_type(varchar(32), indexed)._update_source_healthstamps it onstatus='error'and clears on'ok'.SourceRecord.to_dictexposes it.FailingSourcesCard.vuerenders a colored chip next to the consecutive-failures count, with a tooltip explaining the suggested operator action for each ErrorType category. Color reflects triage intent:auth_error)rate_limited/timeout/network_error/partial/tier_limited)not_found/access_denied/validation_failed/unsupported_url/http_error/unknown_error)The audit flagged this: backend computed 13 ErrorType categories but only the free-text
last_errorreached the operator. Bulk-triage by class ("allauth_error→ rotate cookies", "12rate_limited→ just wait") required opening Logs per row.Test Plan
(discord,pixiv)token platform if one ever lands — confirmauth_type_forroutes the credential correctlyrecompute_centroidtask fires (visible in System Activity)backfill_thumbnailsBeat tick or trigger manually — confirm library missing-thumbnail rows are repairedkind:fandomflow — confirm FandomPicker shows it without a page reload/images/secrets/credential_key.b64exists in your deployed container before this rolls out (so the app boot doesn't trip the new safety check)Pending in Group 5 (not in this PR)
SIGLIP_VERSIONvsMLSettings.embedder_model_version): needs design decision before fixing.?image=Nstrips on close?)./api/tagsvs/api/admin/tags) — has 1 FE caller + 3 tests on the admin path; deserves its own consolidation change.Audit reference
This closes Bundles A/B/C/D of Group 5 (plan task
Scribe #551). After merge, G5.1 + G5.4 are the remaining open findings.🤖 Generated with Claude Code
Five small G5 findings from the 2026-06-02 audit. Each is local and follows an established FC pattern. - download_service: replace hardcoded ('discord','pixiv') tuple with auth_type_for(platform) == 'token'. A 7th token-platform now picks up the right credential path without touching this site. - /api/tags/<source_id>/merge enqueues recompute_centroid.delay after merge so the target's centroid reflects its new image set immediately. Daily list_drifted catches it within 24h, but eager recompute closes the suggestion-quality dip in the meantime. - backfill_thumbnails added to beat_schedule (daily). The task docstring claimed periodic Beat but the entry was never registered, so the library got no self-healing thumbnail repair; only the manual admin-UI button fired it. - modal.createAndAdd pushes a kind='fandom' tag into tagsStore.fandomCache so FandomPicker sees the new fandom on next open. Was: cache-gated load (length===0) skipped refetch, new fandom invisible until full page reload. - cleanup cluster: - Drop .webp from cleanup_service.unlink — thumbnailer only writes .jpg/.png; the third tuple member was dead code. - Drop effective_date from /api/gallery/scroll response — no FE consumer reads it. Service still computes the attribute for timeline ordering; this just trims the JSON. - Rename store.recentMinute → store.recentRuns across the systemActivity store + three consumers (SystemActivitySummary, QueuesTable, SystemActivityTab). The data is the last 200 runs (not actually "last minute"), so the name lied. NOT in this bundle: the duplicate tag-merge endpoint (/api/tags vs /api/admin/tags) is harder — has 1 FE caller and 3 tests on the admin variant; consolidation is its own change.Alembic 0032 adds Source.error_type (varchar(32), indexed). _update_source_health stamps it alongside last_error on status='error' and clears it on 'ok'. SourceRecord/to_dict exposes it. FailingSourcesCard renders a colored chip next to the consecutive- failures count, with a tooltip explaining the suggested operator action. Color reflects intent: - warning (yellow) — operator action needed (auth_error) - info (blue) — backend-paced (rate_limited / timeout / network_error / partial / tier_limited) - error (red) — likely terminal without intervention (not_found / access_denied / validation_failed / unsupported_url / http_error / unknown_error) Audit 2026-06-02: the backend computed 13 ErrorType categories but only the free-text last_error reached the operator. Bulk-triage by class ("all auth_error → rotate cookies", "12 rate_limited → just wait") required opening Logs per row.