v0.5.0: Trash (soft-delete) + Keep folder

Delete now moves an artifact to a recoverable Trash instead of hard-removing.

- Model: Artifact.deleted_at (nullable, indexed) = in Trash.
- Soft-delete everywhere: DELETE /api/artifacts/{id} sets deleted_at
  (idempotent 204); session POST /a/{id}/delete for the feed's hover Delete
  icon; POST /a/{id}/undelete restores; POST /feed/empty-trash purges.
- Trashed artifacts are off-quota (deleting frees quota) and eviction-protected
  (never evicted early); hidden from feed, sources, counts, and all API reads
  (list/detail/raw).
- Sidebar: "Deleted" and "Keep" system folders at the bottom (counts, hidden
  when empty); ?folder=deleted / ?folder=keep views reuse the row rendering.
  Deleted view has hover Undelete + an Empty Trash button. Keep = view over
  is_kept. Live-poll disabled in folder views.
- Docs (api.md delete section, CLAUDE.md), 10 new/updated tests. 150 passing;
  API soft-delete lifecycle verified live.

Purge is manual (Empty Trash) this release; auto purge-on-delete (background,
~3-day retention) is the planned fast-follow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
peter 2026-09-07 12:48:43 +01:00
parent 6c1823e65e
commit 4254164bef
14 changed files with 438 additions and 43 deletions

View file

@ -15,6 +15,7 @@ The core product principle is: agents publish content, not files. The server own
- Exception to the above: an artifact can be explicitly opted into public sharing (per-artifact, owner-toggled from the feed UI) via a high-entropy `share_token`, served unauthenticated at `GET /s/{share_token}`. Not exposed via the ingest API yet — UI-only. Turning sharing off immediately invalidates the old link. Images embedded in a shared artifact are also served unauthenticated while it's shared, so inline content doesn't break for a link recipient.
- User accounts can be created from Settings → Users (superuser-only in-app page) or via Django admin. Regular users are not staff and cannot access `/admin/`. Creating a user via Settings → Users also issues their API token immediately, shown once.
- `TagColor` (label color overrides) stays global/shared across all users.
- Deleting an artifact is a **soft-delete to Trash** (`deleted_at`), not a hard removal — recoverable via the "Deleted" sidebar folder (undelete), off-quota and eviction-protected while trashed, and purged permanently via "Empty Trash". `DELETE /api/artifacts/{id}` is soft too (idempotent); trashed artifacts are hidden from all API reads. A **Keep** sidebar folder is a view over `is_kept`. Both folders hide when empty. (Auto purge-on-delete is a planned fast-follow; currently purge is manual.)
- Use random 8-character artifact IDs.
- Start as a plain Django project; add Docker after core ingest and viewer work.
- Preserve original markdown as `.md` alongside rendered `.html`.