Fix issues #002-#006 found by the LLM test harness
- #006 (high, data loss): make_room_for() no longer deletes unkept artifacts on a doomed request - checks whether eviction can possibly succeed before deleting anything. Also fixed a related latent bug found while testing: the function returned evicted Artifact objects after calling .delete() on them, so their .id was always None (Django clears pk on delete); now returns ids captured beforehand. - #003: added server-side field-length validation (source/title/ source_name/source_id/theme) on POST and PATCH, matching the model's own max_length limits, which were previously unenforced on the ingest path entirely. - #004: ported daybook_post.py's content-shape validation (html_fragment/html_full/deck/markdown) to the server, closing the gap for any client that doesn't go through the helper script. - #005: PATCH now rejects wrong-typed `tags` (previously silently wiped existing tags) and rejects immutable fields (content/source/kind/day) with 400 instead of silently no-op'ing them. - #002: deleted the misleading skills/daybook-posting/.env.example (a zero-dependency script that never reads .env files) and clarified SKILL.md/docs/llm_posting_helper.md that these must be real process/shell env vars. Adds tests/unit/ for storage-layer tests. 46 tests passing (was 25). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
c1ea5c7990
commit
07328a3412
13 changed files with 635 additions and 35 deletions
|
|
@ -76,8 +76,10 @@ def user_quota_bytes(user):
|
|||
def make_room_for(user, incoming_bytes):
|
||||
"""Evict oldest unkept artifacts until `incoming_bytes` fits under quota.
|
||||
|
||||
Returns the list of evicted artifacts (already deleted). Raises QuotaExceeded
|
||||
if even removing every unkept artifact leaves no room.
|
||||
Returns the ids of evicted artifacts (already deleted - Django clears an
|
||||
instance's pk on .delete(), so ids are captured beforehand). Raises
|
||||
QuotaExceeded, without deleting anything, if even evicting every unkept
|
||||
artifact wouldn't free enough room.
|
||||
"""
|
||||
quota = user_quota_bytes(user)
|
||||
if incoming_bytes > quota:
|
||||
|
|
@ -87,21 +89,27 @@ def make_room_for(user, incoming_bytes):
|
|||
if used + incoming_bytes <= quota:
|
||||
return []
|
||||
|
||||
evicted = []
|
||||
unkept = (
|
||||
unkept = list(
|
||||
Artifact.objects.filter(owner=user, is_kept=False).order_by("created_at")
|
||||
)
|
||||
# Check whether eviction can possibly succeed before deleting anything -
|
||||
# otherwise a doomed request would still destroy every unkept artifact on
|
||||
# its way to failing anyway.
|
||||
evictable_total = sum(artifact_total_bytes(artifact) for artifact in unkept)
|
||||
if used - evictable_total + incoming_bytes > quota:
|
||||
raise QuotaExceeded("Quota is full of kept artifacts; free space to post.")
|
||||
|
||||
evicted = []
|
||||
for artifact in unkept:
|
||||
if used + incoming_bytes <= quota:
|
||||
break
|
||||
# Freeing an artifact also frees the assets embedded in it.
|
||||
used -= artifact_total_bytes(artifact)
|
||||
evicted_id = artifact.id
|
||||
delete_artifact_files(artifact)
|
||||
artifact.delete()
|
||||
evicted.append(artifact)
|
||||
evicted.append(evicted_id)
|
||||
|
||||
if used + incoming_bytes > quota:
|
||||
raise QuotaExceeded("Quota is full of kept artifacts; free space to post.")
|
||||
return evicted
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue