# Daybook — Issues Found During LLM-Posting Testing **Date:** 2026-06-10 **Context:** Found while smoke-testing the deployed instance (`192.168.1.36:8000`) and hardening the `daybook-posting` skill's `daybook_post.py` helper. Auth/ reachability items are tracked separately in `planning/next_steps/llm-posting-skill-plan.md` and not duplicated here. Severity key: **High** = blocks a stated feature · **Med** = degrades UX or correctness · **Low** = cosmetic. Status: **Open** / **Fixed** / **By design**. --- ## Open — Server-Side ### S1 · Multipart `metadata` field is not parsed as a JSON object — **High / Open** - **Symptom:** Any file upload (multipart) that includes a `metadata` field returns `HTTP 400 {"detail":"metadata must be an object"}`. - **Repro:** `python daybook_post.py test.svg --meta run_id=1` (or any `--file` + `--meta`). Text artifacts (JSON path) are unaffected — they send a real object. - **Root cause:** The multipart handler reads `metadata` as a raw form string and type-checks it against `dict` without `json.loads`. A stringified `{}` or `{"k":"v"}` therefore fails the check. - **Impact:** Machine-readable metadata (run_id, model, etc.) cannot be attached to image/pdf/binary artifacts — only to text artifacts. - **Fix:** In the multipart code path, `json.loads` the `metadata` form field before validating, mirroring the JSON path. Confirm `tags` is handled consistently (currently sent as a comma string and split server-side). - **Client workaround in place:** the helper now omits `metadata` from multipart when empty, so metadata-less uploads work. Non-empty metadata on uploads still needs this server fix. ### S2 · Users cannot delete or archive artifacts (artifacts are immutable) — **High / Open** - **Requirement:** Users need to be able to **archive** and **delete** artifacts — both as API endpoints and as controls in the web UI. - **Symptom:** There is no delete endpoint, and `content`/`kind`/`persona`/`day` are not editable. The malformed test deck `5df27410` and the session's test artifacts can only be superseded, never removed or hidden. - **Impact:** Test and mistake artifacts accumulate permanently in the feed with no way for a user to clean it up. - **Fix — two distinct actions:** - **Archive:** user-initiated, hides the artifact from the default feed but retains it and allows restore. Needs an archive/unarchive endpoint, a default feed filter that excludes archived items, and a UI control (plus an "archived" view). - **Delete:** user-initiated permanent removal (or a soft-delete tombstone). Needs a `DELETE` endpoint, a UI control with a confirm step, and removal of the stored content/raw file. - **Related:** consider allowing content replacement for an existing id (revision), which would also address re-posting a corrected artifact. ### S3 · No renderer for CSV / JSON / code — **Med / Open** - **Symptom:** These upload as download-only `binary` (e.g. `test.csv` → `c562128b`, kind `binary`). - **Impact:** Tabular/code artifacts aren't viewable inline. - **Fix:** Add a renderer, or formally bless the client's `--as-markdown` fenced- block convention as the interim path and document it. ### S4 · Favorites and project/group labels — **Med / Open** - **Requirement:** Users want to (a) mark artifacts as **favorites**, and (b) label artifacts as belonging to a **group / project**. The group/project label must be **settable at upload time** (and editable afterward). Favorite is primarily a UI toggle but should also be settable at upload. - **Design notes:** - **Keep this distinct from `tags`.** Tags are many free-form filter labels; a project/group is a first-class, usually single container used for navigation and grouping. Recommend a dedicated `project` (or `group`) field rather than overloading tags. `favorite` is a boolean, not a tag. - **Data model:** add `favorite: bool = false` and `project: str | null` to the artifact record. - **Scope — server / client / UI:** - **Server:** accept `favorite` and `project` on POST; add endpoints to toggle favorite and set/clear project after upload; support `?project=` and `?favorite=true` feed filters. - **Client (`daybook_post.py`):** add `--project NAME` and `--favorite` flags, sent as first-class JSON/form fields — NOT inside `metadata` (see S1, which breaks for multipart string fields). - **UI:** a favorite (star) toggle and a favorites view; group/filter the feed by project. --- ## Open — Web UI ### U1 · Feed does not refresh when new artifacts arrive — **Med / Open** - **Symptom:** The web UI renders a static snapshot of the feed. Artifacts posted after the page loads do not appear until the user manually reloads. - **Impact:** Agents publish continuously, but a human watching the feed silently misses new arrivals — which undercuts Daybook's purpose as a live journal of agent output. Observed throughout this session: each test post required a manual browser refresh to show up. - **Fix options (simplest first):** - Client-side polling of the list endpoint on an interval (e.g. every few seconds), diffing against the loaded set. - Server-sent events (SSE) for push updates — a natural fit for this publish-only, append-mostly model and lighter than WebSockets. - To avoid disrupting scroll position, surface a "N new artifacts — click to load" indicator rather than auto-injecting items at the top. --- ## By Design — Worth a Decision ### D1 · Strict rendering, no normalization — **By design** - **Behavior:** "Daybook does not fix malformed content." Wrong-shaped content is stored and rendered as-is. - **Observed consequence:** Markdown sent to `deck` (`5df27410`) was stored and rendered malformed — no error, just broken output. - **Implication:** A validating client is load-bearing. The server's per-kind structure rules and the client's pre-flight checks must stay in sync; if the server adds a kind, the client's validation/sniffing must follow. - **Optional improvement:** Consider rejecting obviously-malformed content with a `422` (e.g. `deck` with no `
`) so it fails loud at post time rather than rendering broken later. This complements, not replaces, the client guard. --- ## Fixed — Client (recorded for traceability) ### C1 · Empty `metadata` broke all multipart uploads — **Fixed** - The helper serialized empty `metadata` as the string `"{}"` into the multipart body, triggering S1's 400 even when no metadata was supplied. - **Fix:** omit empty optional fields (`metadata`, `tags`, `source_id`, `source_prompt`) from the multipart body, restoring the minimal known-good shape. ### C2 · `html_full` client validation was over-strict — **Fixed** - Originally required BOTH ``; an auto-sniffed full page lacking an explicit doctype was rejected. - **Fix:** require `` only (doctype recommended, not mandatory). --- ## Minor / Cosmetic ### M1 · `deck` title inference picks the first slide heading — **Low / Open** - Auto mode derived the title "Slide One" from a deck's first `

` (`ac6da43f`). Correct but awkward as a title. - **Fix:** for `deck`, prefer the filename over the first `

`. ### M2 · CSV content-type guessed as `application/vnd.ms-excel` on Windows — **Low / Open** - Python's `mimetypes` maps `.csv` to `application/vnd.ms-excel` on this host rather than `text/csv`. Harmless — the server still stores it as `binary` — but inconsistent with the earlier manual `text/csv` upload. - **Fix (optional):** force `text/csv` for `.csv` in the client's `content_type_for`. --- ## Test Artifacts Created (for cleanup once S2 lands) Auto-mode validation posts under persona `claude`, tag `auto-test`: `e7a769c5` (markdown), `2d162697` (html_full), `49291e85` (html_fragment), `ac6da43f` (deck), `3ad914cc` (image), `794d29e8` (pdf), `c562128b` (binary). Plus earlier manual smoke-test artifacts and the malformed deck `5df27410`.