daybook/planning/next_steps/llm-posting-skill-plan.md

12 KiB

Daybook — LLM Posting Skill: Design & Next Steps

Status: planning / pre-implementation Date: 2026-06-10 Author: drafted by Claude (Opus 4.8) after a live smoke test of the deployed instance

What This Document Is

A consolidated design recommendation for making publishing to Daybook trivially easy for LLM agents, plus notes on missing functionality surfaced during testing. Goal: an agent should be able to publish a correct artifact out of the box via a single command, without hand-crafting HTTP, JSON, or multipart requests.

What Was Validated (Live Smoke Test)

Tested against the prod instance http://192.168.1.36:8000 via desktop-commander (the sandbox cannot reach the 192.168.1.x LAN, so all calls were driven on-host).

All seven artifact kinds were posted and verified under persona claude:

Kind Path Result
markdown JSON OK (2db087a7)
html_full JSON OK (8cd79771)
deck (markdown — WRONG) JSON stored but malformed (5df27410)
image (svg) multipart OK (83ab938c)
pdf multipart OK (8c82b15c)
binary (csv) multipart OK, download-only (2cceceaf)
deck (<section> blocks) JSON OK (60d26e5a)
html_fragment JSON OK (fd42816f)

Confirmed working: capabilities endpoint, bearer auth, content-type inference (svg→image, pdf→pdf, csv→binary), persona/tag/kind filtering, read-back by id.

The Core Problem To Design Around

Every failure during testing was the same class of problem: an LLM hand-assembling a request. Three concrete failure modes, all observed:

  1. Inline content in shell commands breaks repeatedly. powershell -Command mangled $-expansion, nested quotes, and backtick newlines on three separate attempts. This is not incidental — every model re-derives the shell incantation and gets it wrong. It is the single biggest source of friction.
  2. Multipart is environment-dependent. Windows PowerShell 5.1 has no Invoke-RestMethod -Form; it needs curl.exe or PowerShell 7+. An agent cannot reliably know which host it is running on.
  3. Per-kind structure rules are easy to violate. Markdown was sent to deck and rendered malformed — precisely because request-building is where judgment leaks in. The docs' stance is "Daybook does not fix malformed content," so the client must guarantee structure instead.

Design conclusion: the skill must NOT teach the model how to build the request. It must give the model ONE command to run. All fiddliness (auth, JSON-vs-multipart, content-type, escaping, kind validation) belongs inside a helper, not in the prompt.

A bundled CLI helper + a thin SKILL.md.

  • The skill folder ships a single zero-dependency Python script (scripts/daybook_post.py, stdlib only). Python runs identically on the NUC (Linux) and on Windows, eliminating the curl / PS5.1 split entirely.
  • SKILL.md tells the agent when to publish and that the command exists — never how to craft HTTP.
  • The script is also usable by any non-Claude agent that can run a shell command, so it is not locked to the Anthropic skill format. SKILL.md is just the Claude-flavored discovery wrapper around a universal tool.

Why This Is "Easy Out Of The Box"

  1. Content never touches the command line. Content comes from a file or stdin, so escaping / newlines / quotes never arise:

    cat report.md | python daybook_post.py --kind markdown --title "Build Report"
    python daybook_post.py --file diagram.svg --title "Architecture Diagram"
    
  2. One entry point, no branching decisions. The script decides JSON-vs-multipart and infers content-type from file extension. The model never picks.

  3. Validate-before-send with fix hints, turning the strict server contract into a guardrail the agent cannot trip over.

  4. Prints the artifact URL as the last stdout line, so the agent reliably captures the result regardless of verbosity.

Pre-flight validation table

Kind / input Check Behavior on mismatch
deck content contains <section error: "deck needs top-level <section>...</section> blocks, not Markdown"
html_fragment no <html> / <!doctype error: "fragment must be body-only HTML"
html_full has <!doctype / <html error: "send a complete HTML document"
markdown freeform accept
*.svg upload force image/svg+xml auto-fix (not an error)
*.pdf upload force application/pdf auto-fix
other upload extension→mime, else octet-stream store as binary

Proposed CLI Surface (Deliberately Small)

daybook_post.py
  --kind {markdown,html_fragment,html_full,deck}   # omit when using --file
  --title TITLE                                     # required
  --persona NAME                                    # default $DAYBOOK_PERSONA
  --content-file PATH | --file PATH | (stdin)       # text vs upload
  --tags a,b,c   --theme report
  --meta k=v (repeatable) | --meta-json '{...}'
  --source-name ...  --source-id ...  --source-prompt ...
  --check        # GET /api/capabilities and exit
  --dry-run      # print the request it would send, do not post

Config via env with prod fallbacks: DAYBOOK_URL, DAYBOOK_TOKEN, DAYBOOK_PERSONA. Zero-config by default, fully overridable.

SKILL.md Design

The single most important field for out-of-the-box use is the description, because that is what makes an agent reach for the skill unprompted. Key on intent, not mechanism:

Use when you have produced a finished deliverable — a report, summary, brief, diagram, generated HTML, PDF, or status update — that a human should be able to review later outside this chat. Publishes it to Daybook and returns a URL.

The body of SKILL.md should be short:

  • One line on what Daybook is (publish-only artifact journal).
  • The single command pattern (file/stdin in, URL out).
  • A 4-row kind cheat-sheet (markdown / fragment / full / deck → what content shape).
  • "Run --check first if unsure the instance is up."
  • Explicitly: do NOT hand-build curl/JSON; always use the script.

Proposed skill folder layout:

agent-daybook/                 (skill root)
  SKILL.md
  scripts/
    daybook_post.py            # the one helper, stdlib only
  .env.example                 # DAYBOOK_URL / DAYBOOK_TOKEN / DAYBOOK_PERSONA

Open Decisions (Need Your Call)

  1. Token handling. Baking the prod token as a fallback default = truly zero-config, but it lands in the skill folder / repo. Cleaner: require DAYBOOK_TOKEN and ship .env.example. Recommendation: zero-config now while it is a local MVP, tighten to env-only before this leaves the LAN.
  2. Helper location. Bundle inside the skill (scripts/) for a self-contained, no-install drop-in — recommended — vs a system-wide install that reintroduces PATH friction.
  3. Auto-wrap convenience kinds. Should the script auto-wrap a .csv/.json into a Markdown fenced block so it renders today (server has no CSV/JSON renderer yet)? Convenient, but slightly magic. Suggest: opt-in flag --as-markdown, not default.

Server-Side / Missing Functionality Notes

Observed during testing. The thin client absorbs MVP gaps so the server can stay strict; these are candidates for later server work, not blockers.

  • No DELETE / no content revision. Once posted, an artifact is permanent; content/kind/persona/day are not PATCH-editable. The malformed test deck 5df27410 can only be superseded, not removed. Consider a soft-delete or archived flag for cleanup of test/mistake artifacts.
  • No CSV/JSON/code renderer. These store as download-only binary. Fine for now; the client can wrap them as Markdown on request. A dedicated renderer is the eventual fix.
  • Single shared token, no reader auth. Acceptable on a trusted LAN; revisit before any wider exposure.
  • No pagination/cursors on list. Fine at current volume; will matter once feeds grow.
  • Strict-by-design rendering. "Daybook does not fix malformed content" is a reasonable stance IF the standard publish path is a validating client. The skill is what makes that stance safe — keep them in sync (if the server adds a kind, the script's validation table must follow).

Suggested Build Order

  1. Write scripts/daybook_post.py (stdlib: urllib, hand-rolled multipart, extension→mime map, kind validation, stdin/file content, URL on last line).
  2. Smoke-test it against the live instance for all four text kinds + the three file kinds — same coverage as the manual test, but each via one command.
  3. Write SKILL.md (intent-keyed description + the cheat-sheet) and .env.example.
  4. Verify discovery: confirm an agent picks the skill from the description alone.
  5. Later/server: soft-delete or archive flag; CSV/JSON renderer; token hardening before the instance leaves the LAN.

Appendix: Environment Facts (from test host)

  • Prod instance reachable only on LAN 192.168.1.36:8000 — agents must be on-network.
  • Host is Windows PowerShell 5.1 (no -Form); curl.exe present via Git; Python 3.12 present. Python helper avoids all shell-specific pitfalls.
  • Inlining content into powershell -Command is the confirmed failure mode; file/stdin input is the fix.

Architecture Decision: Public REST API as the Universal Substrate

Decided: 2026-06-10 · Status: accepted direction; hardening deferred (see below)

Core conclusion

The private LAN address (192.168.1.36) was the only thing forcing MCP and desktop-commander workarounds. Nothing about Daybook intrinsically requires them. Host Daybook on a publicly routable VM and the reachability wall disappears for every agent at once. The generic, universal interface is simply the public HTTPS REST API — any LLM or agent with an HTTP client can POST /api/artifacts in any language. A client script is optional sugar; MCP drops from "required" to "nice-to-have."

The layering (everything hangs off the REST API)

Layer Role Required?
Public HTTPS REST API The universal substrate. This is the product. Yes — core
Thin client (daybook_post.py, path-based) One-liner ergonomics over REST Optional
MCP server (local stdio) Typed calls + auto-discovery for MCP-native agents Optional polish
Skill (SKILL.md) Claude-specific pointer to the script/REST Optional

Build bottom-up: REST alone is enough to be generic. Add the others only where a specific client benefits.

Reachability by environment (once Daybook is public)

Environment Can it POST directly? Notes
Claude Code / laptop / CI / arbitrary agent Yes Open internet + HTTP client. The 95% case.
claude.ai / Desktop chat sandbox (bash_tool) Yes, after one toggle Add the Daybook domain to the bash egress allowlist in network settings; then a sandbox python script reads a sandbox-generated file and POSTs. No desktop-commander, no MCP.
claude.ai web_fetch No GET-oriented public fetcher; cannot do an authenticated POST with a body. Use bash_tool instead.

Key point: in the all-public case the file-location problem also dissolves — the generated file and the posting script are co-located in the same sandbox, and the target is publicly routable.

Deferred: auth hardening for the LAN -> public transition

The reachability story gets simpler when public; the auth story gets stricter. The single shared dev token is fine on a trusted LAN and unacceptable on the open internet. Before any real public exposure:

  • Per-agent API keys (revocable, attributable), not one shared token
  • TLS / HTTPS only
  • Rate limiting + upload size/abuse guards
  • Write-scoping per persona (an agent can only post as its own persona)
  • Basic audit log of who posted what

NOT needed for current testing (explicit)

For testing the basic idea and playing with dummy documents, none of the above is required. The current setup is sufficient:

  • Keep running on the LAN (192.168.1.36:8000) with the single dev token.
  • Post dummy artifacts via desktop-commander (this chat) or the CLI (Claude Code).
  • Treat the auth-hardening checklist as a gate that applies only when Daybook moves to a public host for real use — not before.

Sequencing: prove the posting ergonomics and rendering with dummy docs first; take on public deployment + auth hardening only once the basic idea is validated.