Last week /paper ran end to end and produced a draft. This week was the gap between “produces a draft” and “produces something worth keeping”, which turned out to be most of the work.
The Python pipeline now sits behind an HTTP endpoint that streams its stage transitions, the chat backend proxies those into the SSE stream the browser already understands, and typing /paper <topic> starts a run. A run takes minutes, so the interface shows which stage is running, what just finished, and what’s next, rather than sitting there quietly.
Every draft came out about the same length whatever the topic, which usually means a ceiling rather than a judgement. It was: a whole paper written in one call is capped by the model’s maximum output length, so it was rationing itself across the sections to fit. Now each section is written by its own call with its own output budget, and each one sees more of the evidence than a single whole-paper prompt could carry. The drafts got longer and, more usefully, specific.
I also made /paper work with every provider the chat interface supports instead of the shorter list it started with, and added OpenRouter, which is one key for a lot of models.
The introduction was inventing artifacts.
Not obviously, which is the problem. I’d been careful that the sections only cite what the pipeline actually read, but the introduction and conclusion were written last, and without really thinking about it I’d written them blind to the evidence, from the topic and the outline alone. So they did the sensible thing and produced plausible examples with plausible P-numbers. They read well. The tablets weren’t in the run.
For a tool whose whole value is being grounded in a real catalogue, that’s about as bad as it gets, and it’s worse than an obviously broken output, because an obviously broken output doesn’t get quoted. The fix was to give the introduction and conclusion the same evidence the sections get, plus a plain statement that these are the only artifacts that exist for this paper. The citation stage now also appends a list of cited artifacts with links, built only from the ones actually read, so a made-up citation can’t get a real-looking link out of the reference list. The rule I’ve written into the code: any stage that writes prose gets the list of valid IDs.
I changed how sources are gathered too. The search stage used to emit a fixed set of queries in one shot, which is fine for a topic about a place or a period but wrong for a topic about the corpus itself. “Which languages are in CDLI” was being answered by generalising from a couple of dozen sampled tablets. It’s now an agent that decides what to collect and can ask the catalogue for the real distribution of languages or periods before looking at any single tablet. It’s capped at a fixed number of steps, since an agent that decides how much to gather can also decide to gather forever on someone’s key.
A paper you can only read in a chat window isn’t much use, so there’s a download button that returns a formatted PDF, with a footer repeated on every page so a single printed page still says it was machine-generated. It renders from the Markdown the browser already has, so downloading never re-runs the pipeline and costs nothing. The disclosure wording is a placeholder until CDLI approves the real text.
I also spent time on failures, which matter more here than in chat: a run that dies four minutes in after spending real money deserves better than a raw error string. Errors arrive from all over, a provider’s JSON, a Python traceback, a network failure, so they’re translated in one place into something you can act on, “that key was rejected”, “that provider says you’re out of credit”, “no artifacts matched, try a broader topic”, with the raw text behind an expander for when I’m the one debugging. A flagged citation shows as a warning, not an error, since the paper did render.
Everything above is enforced by code that only the CDLI chat interface can call. But the method itself, gather broadly, read before citing, never cite what you didn’t read, is worth having anywhere. So the MCP server now exposes it as a prompt, with a matching skill file for clients that use those, both describing the same process for a model to run itself with the ordinary CDLI tools. It can’t guarantee the rules the way the pipeline does, but it puts the same standard in front of anyone connecting from Claude Desktop or elsewhere.
With that merged, /paper works end to end: type a topic, watch it run, read a cited note, download the PDF.
/paper, now that there are real runs to measure.| # | Day | Date | A short description of the work done |
|---|---|---|---|
| 1 | Monday | 2026/08/03 | Proxied the paper service’s progress stream through the chat backend so the browser learns one protocol for both chat and papers #28 |
| 2 | Tuesday | 2026/08/04 | Added the /paper command to the chat box, with stage-by-stage progress since a run takes minutes #28 |
| 3 | Wednesday | 2026/08/05 | Traced the uniform draft length to the model’s output cap and switched to writing one section per call #28 |
| 4 | Thursday | 2026/08/06 | Made /paper work with every provider the chat interface supports, and added OpenRouter #28 |
| 5 | Friday | 2026/08/07 | Rebuilt gathering as an agent that decides what to collect and can ask the catalogue for its real distribution, capped so it can’t gather forever #28 |
| 6 | Saturday | 2026/08/08 | Caught the introduction inventing artifacts, gave every prose stage the evidence and the list of valid IDs, and built the cited-artifacts list from what was actually read #28 |
| 7 | Sunday | 2026/08/09 | Added PDF download, readable error handling, and the MCP prompt and skill, then merged #28 |