daybook/issues/013-handover-to-llm-test-round-2.md
peter 292c386209 Add round-2 handover doc for the LLM test project
Covers everything shipped since the #007 handoff: the json/csv kinds,
.xlsx conversion, public sharing, syntax highlighting/table styling, and
the #008-#012 fixes. Prioritizes round-3 probe suggestions by risk, with
sharing (the new unauthenticated surface) and the #010 quota race (no
automated coverage) called out as the highest-value targets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 20:53:12 +01:00

121 lines
6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 013 — Handover to AGENT_DAYBOOK__LLM_TEST (round 2)
**Status:** For information — a handback covering everything shipped since
the round-1 handoff (#007).
Round 1 (#007) closed out issues #002–#006. Since then, your round-2
testing filed #008–#012 (all now fixed), and several new capabilities
landed. This doc summarizes all of it and points at where the fresh
surface area is worth probing.
All of this is committed on `main` (pushed to Forgejo) and live on the
local dev instance you test against (`http://127.0.0.1:8001`) — Django's
autoreloader has already picked it up, no restart needed on your end.
## New Capabilities To Exercise
These are new since #007 and largely untested by you yet:
1. **`json` and `csv` artifact kinds** (commit `e5d2f19`). Text kinds like
markdown. `json` renders as a collapsible tree (renderjson), `csv` as a
sortable/searchable table (Grid.js). Both validate server-side (`json`
must parse; `csv` only needs non-blank content) and preserve original
text as a `?raw=1` sidecar. Both vendored libs are served from
`/static/vendor/…`.
2. **`.xlsx` upload → `csv`** (commit `4b5ec09`). Uploading an `.xlsx` file
(multipart, like any file) is converted server-side via openpyxl into a
`csv` artifact rather than stored as `binary`. Active sheet only; cached
formula results only; `.xls` unsupported; corrupt file → `400`.
3. **Per-artifact public sharing** (commit `91ea19d`). Owner toggles a
share link from the feed UI; `GET /s/{share_token}` then serves that one
artifact with **no authentication at all**. `is_shared`/`share_url` now
appear in every artifact API response. Embedded images in a shared
artifact are also served unauthenticated while it's shared. **This is
the highest-value target for adversarial testing** — see below.
4. **Syntax-highlighted code + styled tables** (commits `af9ecf2`,
`696cfe7`). Markdown fenced code blocks get Pygments highlighting;
tables in markdown and `html_fragment` render styled. Cosmetic, low-risk.
5. **Posting script supports `json`/`csv`/`.xlsx`** (commit `e137725`).
`daybook_post.py --kind json/csv`, positional auto-detect of
`.json`/`.csv`/`.xlsx`.
6. **Light/dark UI toggle** (commit `9a3b381`). UI-chrome only, no API
surface — not relevant to API testing.
## Fixes Since #007 (issues you filed)
- **#008** — `source_name`/`source_id`/`source_prompt` now reject
non-string values with `400` instead of silently mangling them.
- **#009** — the `html_fragment` content-shape check is substring-based;
documented as a known limitation rather than switched to real HTML
parsing (deliberate — matches the client helper's own approach).
- **#010** — concurrent posts could race past quota; fixed with a
write-lock-before-read + atomic transaction. Verified live but **not**
covered by an automated test (SQLite shared-cache test DB behaves
differently — see the issue). Worth re-probing.
- **#011** — CDN snippets documented for `html_full`; `--theme` help typo
fixed.
- **#012** — posting script gained `json`/`csv` support.
## Suggested Round-3 Probes
Prioritized by where the new surface area is riskiest.
### Sharing (#91ea19d) — security boundary, probe hardest
- Can an **unshared** artifact ever be reached via `/s/<token>`? Brute/
guessed/empty/malformed tokens should all `404`.
- Does **unsharing immediately** kill the old link? (Confirmed manually,
but re-verify — and try racing an unshare against an in-flight request.)
- Is `share_token` ever leaked in a response for an artifact you *don't*
own, or in the list endpoint, or anywhere a non-owner could see it?
- Can a shared artifact's **embedded assets** be reached unauthenticated —
and, importantly, can an *un*shared artifact's assets be reached that way
(they must not)? Also: does an orphaned/unattached asset stay private
even when some unrelated artifact is shared?
- Does `?raw=1` on `/s/<token>` correctly serve raw source for the kinds
that support it, and not leak anything for kinds that don't?
- Cross-user: user A shares an artifact; can user B toggle/​unshare it, or
only view it?
### json / csv (#e5d2f19)
- `json`: valid-but-not-an-object payloads (bare array, string, number,
`null`); deeply nested / very large; unicode; the `</script>` injection
vector (confirmed handled for json — verify **csv** escapes its embedded
data block the same way, it's a separate code path).
- `csv`: ragged rows (fewer/more columns than the header), embedded commas/
quotes/newlines, duplicate column names, header-only, empty, a single
giant cell, BOM/encoding oddities.
- Quota accounting: json/csv now write a sidecar file — confirm size
accounting and eviction include/delete the sidecar (this generalized the
markdown-only logic; worth verifying it didn't regress markdown either).
### .xlsx conversion (#4b5ec09)
- Multi-sheet workbook — verify only the active sheet survives and the
others are silently dropped (documented behavior, confirm it's true).
- Formula cells never calculated by a real spreadsheet app (openpyxl
doesn't evaluate) → should be blank, not error.
- A non-`.xlsx` file renamed to `.xlsx`; a real `.xlsx` sent with a wrong/
generic content type; a huge sheet; empty workbook; `.xls` (must not
convert).
- Field mangling regression: this path rebuilds the payload dict manually
because multipart `QueryDict` + `{**data}` was silently list-wrapping
values (the bug that made this a multi-fix). Re-probe that title/tags/
source_* all come through correctly on `.xlsx` posts specifically.
### #010 quota race (no automated coverage)
- Fire N truly-concurrent posts at a tight quota; confirm final usage never
exceeds quota and no artifact is left half-created (row without files or
files without row).
As always: use the `test` / `testpwd` account, clean up what you create,
check `artifacts/views.py` before calling a surprise a bug (some, like
#009, are documented-by-design), and file anything real as a new numbered
issue (next is #014), linking back if it's a variant of a closed one.