daybook/docs/llm_posting_helper.md
peter 07328a3412 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>
2026-09-02 13:38:59 +01:00

3.2 KiB

LLM Posting Helper

Agents should publish to Daybook with skills/daybook-posting/scripts/daybook_post.py instead of hand-building HTTP, JSON, curl, or PowerShell multipart requests.

The project also keeps scripts/daybook_post.py as a compatibility wrapper.

The helper is intentionally zero-dependency and uses only Python's standard library. It works on Windows and Linux.

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.

DAYBOOK_URL=http://127.0.0.1:8001
DAYBOOK_TOKEN=your-personal-api-token
DAYBOOK_SOURCE=agent

DAYBOOK_TOKEN is a personal per-user token, from Settings → Account in the app — not a token shared across users. Override these per environment.

Commands

Check the instance:

python skills/daybook-posting/scripts/daybook_post.py --check

Post Markdown from a file:

python skills/daybook-posting/scripts/daybook_post.py --kind markdown --title "Build Report" --content-file report.md --tags build,report

Post Markdown from stdin:

cat report.md | python skills/daybook-posting/scripts/daybook_post.py --kind markdown --title "Build Report"

Post a deck:

python skills/daybook-posting/scripts/daybook_post.py --kind deck --title "Deployment Plan" --content-file deck.html

deck.html must contain top-level <section>...</section> blocks.

Post an image/PDF/binary file:

python skills/daybook-posting/scripts/daybook_post.py --file diagram.svg --title "Architecture Diagram"
python skills/daybook-posting/scripts/daybook_post.py --file report.pdf --title "Generated Report"

Render CSV/JSON/code as Markdown today:

python skills/daybook-posting/scripts/daybook_post.py --file results.csv --as-markdown --title "Results CSV"

Dry run:

python skills/daybook-posting/scripts/daybook_post.py --kind markdown --title "Draft" --content-file report.md --dry-run

The artifact URL is printed as the final stdout line.

Validation Guardrails

The helper validates common LLM mistakes before sending:

Kind/input Validation
deck Must contain <section.
html_fragment Must not contain <!doctype, <html, or <body.
html_full Must contain <!doctype and <html.
markdown Must not be blank.
.svg upload Forces image/svg+xml.
.pdf upload Forces application/pdf.

Metadata

Use repeatable --meta key=value flags:

python skills/daybook-posting/scripts/daybook_post.py \
  --kind markdown \
  --title "Run Summary" \
  --content-file summary.md \
  --meta run_id=run-001 \
  --meta model=gpt-5.5

Or pass a JSON object:

python skills/daybook-posting/scripts/daybook_post.py --kind markdown --title "Run Summary" --content-file summary.md --meta-json '{"run_id":"run-001"}'

Skill Wrapper

A thin LLM-facing wrapper lives at:

skills/daybook-posting/SKILL.md

Its key instruction is: do not hand-build requests; use scripts/daybook_post.py.