feat: cap-aware autoscaler + token-gated whole-instance tag reset (operator feedback)
Autoscaler (agent 2026-07-02.5): the buffer-occupancy signal alone would peg downloaders at DL_MAX while the bandwidth CAP — not concurrency — is the real constraint (8 streams sharing 8 MB/s move no more data than 4). Growth is now gated on the pipe having headroom (net < 85% of cap) and a pipe pinned at the cap (>= 95%) sheds streams down to 3; dead band prevents flapping. The UI hint says 'holding at the bandwidth cap' and /status reports bw_capped, so the behavior is legible without tests that need the ML stack. Reset content tagging: stays a FULL-instance reset (operator's call), but now lives in a fenced 'Danger zone' section on Cleanup and the apply is gated by a preview-derived confirm token (mirrors the Tier-C bulk-delete pattern — stale counts are rejected server-side). Copy no longer claims suggestions repopulate: it says plainly the heads' training examples are deleted and re-tagging starts fresh. Moved out of TagMaintenanceCard into DangerZoneCard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
+31
-1
@@ -562,7 +562,7 @@ async def test_trigger_normalize_live_queues(client, monkeypatch):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reset_content_tagging_dry_run_returns_counts(client, db):
|
||||
async def test_reset_content_tagging_dry_run_returns_counts_and_token(client, db):
|
||||
db.add_all([
|
||||
Tag(name="solo", kind=TagKind.general),
|
||||
Tag(name="naruto", kind=TagKind.character),
|
||||
@@ -579,3 +579,33 @@ async def test_reset_content_tagging_dry_run_returns_counts(client, db):
|
||||
assert body["by_kind"] == {"general": 1, "character": 1}
|
||||
# dry-run leaves the rows in place — fandom + series untouched too.
|
||||
assert "deleted" not in body
|
||||
# The preview arms the apply: an 8-hex confirm token over the live counts.
|
||||
assert len(body["confirm"]) == 8
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reset_content_tagging_apply_requires_confirm_token(client, db):
|
||||
from sqlalchemy import func, select
|
||||
|
||||
db.add(Tag(name="solo", kind=TagKind.general))
|
||||
await db.commit()
|
||||
|
||||
# No token / wrong token → rejected, nothing deleted.
|
||||
for bad in ({"dry_run": False}, {"dry_run": False, "confirm": "deadbeef"}):
|
||||
resp = await client.post("/api/admin/tags/reset-content", json=bad)
|
||||
assert resp.status_code == 400
|
||||
remaining = (await db.execute(
|
||||
select(func.count()).select_from(Tag)
|
||||
)).scalar_one()
|
||||
assert remaining == 1
|
||||
|
||||
# The token from a fresh preview unlocks the apply.
|
||||
token = (await (await client.post(
|
||||
"/api/admin/tags/reset-content", json={"dry_run": True}
|
||||
)).get_json())["confirm"]
|
||||
resp = await client.post(
|
||||
"/api/admin/tags/reset-content",
|
||||
json={"dry_run": False, "confirm": token},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert (await resp.get_json())["deleted"] == 1
|
||||
|
||||
Reference in New Issue
Block a user