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:
peter 2026-09-02 13:38:59 +01:00
parent c1ea5c7990
commit 07328a3412
13 changed files with 635 additions and 35 deletions

View file

@ -83,22 +83,28 @@ Required fields:
| Field | Notes |
|---|---|
| `source` | Required. |
| `source` | Required. Max 80 characters. |
| `kind` | One of `html_fragment`, `html_full`, `markdown`, `deck`. |
| `title` | Required. |
| `content` | Required text content. |
| `title` | Required. Max 200 characters. |
| `content` | Required text content. Must match the shape `kind` implies — see below. |
`content` shape is validated per `kind`: `html_fragment` must not contain
`<html`, `<!doctype`, or `<body` (body-only HTML, wrapped in a theme
server-side); `html_full` must contain `<html`; `deck` must contain
`<section`; `markdown` must not be blank or whitespace-only. A mismatch
returns `400`.
Optional fields:
| Field | Notes |
|---|---|
| `theme` | Defaults by kind. Common values: `report`, `tasks`, `deck`, `base`. |
| `theme` | Defaults by kind. Common values: `report`, `tasks`, `deck`, `base`. Max 40 characters. |
| `tags` | Array or comma-separated string. |
| `tag_colors` | Object mapping tag name → `#RRGGBB`. Sets a colour override for those tags. |
| `source_prompt` | Prompt or instruction that led to the artifact. |
| `source_type` | Defaults to `agent`. |
| `source_name` | Defaults to `source`. |
| `source_id` | Optional stable source ID. |
| `source_name` | Defaults to `source`. Max 120 characters. |
| `source_id` | Optional stable source ID. Max 120 characters. |
| `metadata` | Optional object. |
### Tag colours
@ -332,28 +338,11 @@ Editable fields:
| `metadata` | Object. Replaces existing metadata. |
You can only PATCH artifacts your own token owns; others return `404`.
`tags`, if present, must be a string or an array — any other type returns
`400` rather than silently clearing existing tags.
## Delete Artifact
Endpoint:
```http
DELETE /api/artifacts/{id}
Authorization: Bearer <your-personal-token>
```
Removes the artifact's database row and its files (primary file, markdown
sidecar, and any attached assets). Returns `204` with no body on success.
You can only delete artifacts your own token owns; others return `404`, as
does a second delete of the same id.
```bash
curl -X DELETE "http://127.0.0.1:8001/api/artifacts/a1b2c3d4" \
-H "Authorization: Bearer <your-personal-token>"
```
Not editable yet:
Not editable — sending any of these returns `400` naming the field, rather
than silently ignoring it:
| Field | Reason |
|---|---|
@ -383,6 +372,26 @@ Invoke-RestMethod `
-Body $body
```
## Delete Artifact
Endpoint:
```http
DELETE /api/artifacts/{id}
Authorization: Bearer <your-personal-token>
```
Removes the artifact's database row and its files (primary file, markdown
sidecar, and any attached assets). Returns `204` with no body on success.
You can only delete artifacts your own token owns; others return `404`, as
does a second delete of the same id.
```bash
curl -X DELETE "http://127.0.0.1:8001/api/artifacts/a1b2c3d4" \
-H "Authorization: Bearer <your-personal-token>"
```
## Rendered And Raw Content
Rendered browser view:

View file

@ -8,6 +8,11 @@ The helper is intentionally zero-dependency and uses only Python's standard libr
## Configuration
Set these as real process/shell environment variables — the helper reads
`os.environ` directly and has no `.env` file support. Copying values into a
`.env` file in the working directory does nothing; export/`$env:`/`set`
them, or set them in your agent harness's own env config.
```text
DAYBOOK_URL=http://127.0.0.1:8001
DAYBOOK_TOKEN=your-personal-api-token