chore: retire ml-backfill-daily beat + the spent purge-legacy action (operator-approved)
- ml-backfill-daily: the CPU tag_and_embed backfill raced the GPU agent's daily embed backfill for the same NULL-embedding images at ~100x the cost (B1 audit verdict, milestone #124). The backfill TASK stays — the manual /api/ml/backfill button remains the deliberate CPU fallback pending B3. - purge-legacy: one-time IR-migration cleanup, dry-run verified 0 targets on the live library before removal (A2 audit, milestone #123). Fully retired per rule 22: tile, store action, route, service fn, tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
@@ -390,63 +390,6 @@ async def test_prune_unused_commit_deletes_and_returns_count(client, db):
|
||||
assert "byebye" in body["sample_names"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purge_legacy_dry_run_counts_by_kind_and_prefix(client, db):
|
||||
db.add_all([
|
||||
Tag(name="BlenderKnight:Hannah_BJ_Loops", kind=TagKind.archive),
|
||||
Tag(name="BlenderKnight:May Animation", kind=TagKind.post),
|
||||
Tag(name="SomeArtist", kind=TagKind.artist),
|
||||
# IR `source` kind fell back to general during migration —
|
||||
# caught by the source:* name prefix, not by kind.
|
||||
Tag(name="source:patreon", kind=TagKind.general),
|
||||
Tag(name="blonde hair", kind=TagKind.general), # must survive
|
||||
])
|
||||
await db.commit()
|
||||
|
||||
resp = await client.post(
|
||||
"/api/admin/tags/purge-legacy", json={"dry_run": True},
|
||||
)
|
||||
body = await resp.get_json()
|
||||
assert body["count"] == 4
|
||||
assert body["by_kind"] == {"archive": 1, "post": 1, "artist": 1}
|
||||
assert body["by_prefix"] == {"source:*": 1}
|
||||
assert "blonde hair" not in body["sample_names"]
|
||||
assert "source:patreon" in body["sample_names"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_purge_legacy_commit_deletes_only_legacy(client, db):
|
||||
from sqlalchemy import func, select
|
||||
|
||||
db.add_all([
|
||||
Tag(name="arch1", kind=TagKind.archive),
|
||||
Tag(name="source:fanbox", kind=TagKind.general),
|
||||
Tag(name="keepme", kind=TagKind.general),
|
||||
Tag(name="char1", kind=TagKind.character),
|
||||
])
|
||||
await db.commit()
|
||||
|
||||
resp = await client.post(
|
||||
"/api/admin/tags/purge-legacy", json={"dry_run": False},
|
||||
)
|
||||
body = await resp.get_json()
|
||||
assert body["deleted"] == 2 # arch1 + source:fanbox
|
||||
|
||||
# The plain general + character tags survive.
|
||||
keep = (await db.execute(
|
||||
select(Tag.name).where(Tag.name.in_(["keepme", "char1"]))
|
||||
)).scalars().all()
|
||||
assert set(keep) == {"keepme", "char1"}
|
||||
# No archive/post/artist or source:* left.
|
||||
gone = (await db.execute(
|
||||
select(func.count()).select_from(Tag).where(
|
||||
(Tag.kind.in_([TagKind.archive, TagKind.post, TagKind.artist]))
|
||||
| (Tag.name.like("source:%"))
|
||||
)
|
||||
)).scalar_one()
|
||||
assert gone == 0
|
||||
|
||||
|
||||
# --- Tier-A: POST /posts/prune-bare + /posts/reconcile-duplicates ---
|
||||
# These two routes share _run_dry_run_op with the tag prunes (DRY pass,
|
||||
# task #753); cover their apply path + reconcile's source_id passthrough so
|
||||
|
||||
Reference in New Issue
Block a user