The Axis Nobody Wrote Down
Earlier in this track, the typed pointer was defended as the cheap half to generalize: a kind plus an identifier, so that a future pipeline could point at a different kind of material without renegotiating the seam. That bet has been tested twice. It passed the first test without anyone noticing. The second test found an assumption the shape had been carrying all along without saying so.
The first test came before this quest was even published. A second pipeline turns a published essay into a narrated reading, and it points at site content. A new kind, named in the kind field and carrying fields of its own, with nothing on the calling side changed — exactly what the field was for.
The second test arrived on 2026-08-27 with a third production character: one topic, explained. Its input is shaped like a research notebook. You might hand it a conversation, a web page, an uploaded document, a note in your own words, several of those at once, or nothing but the topic itself. And {kind, id} cannot say that, because the shape had quietly encoded two facts about its target: that it is one thing, and that it already exists somewhere to be fetched. A topic is neither. A topic addresses a set, and the set may be empty.
What Changed, and How Little
The contract grew one optional field. A pointer may carry sources, a list in which every entry is typed — conversation, url, file, or note — and the registry of source kinds is the gate an unknown entry fails at. Two flags on each pipeline decide who may use it: whether it accepts sources, and whether its pointer may leave the identifier blank. Both default to off. The conversation pipeline behaves byte for byte as before, and its tests assert that it refuses a source list outright, because a pipeline whose whole axis is one conversation must not quietly grow a second source of material.
Notice where the change did not go. The request still carries no material: a source set is a set of references, dereferenced into the working directory at run time like any other pointer. Uploaded files land in the engine's store addressed by their content hash, and a session on a laptop reads them through the engine exactly as it reads a conversation, never from a stale local copy. And the workshop still parses nothing — no document-conversion dependency arrived. The pipeline puts the bytes where the authoring session can open them, and the session opens them itself.
A Gate That Needs an Artifact, Not an Exit Code
Because nothing parses the sources, nothing can claim they were read. So this pipeline's first gate after the brief, before any script exists, is a sources stage whose record has to be evidence that every attachment was actually opened — not a parser's success code. The same pipeline treats every source as material and never as a draft. A conversation about the topic is read for its order, its keywords, and the angles its participants engaged with, and deliberately not for its depth or its sentences. The script is written from zero.
Two small invariants fell out of building it, and both exist to keep an existing guarantee from eroding. Uploads travel as a raw body with the filename in a header rather than as a multipart form, because the command-line client is standard-library only on purpose, and a hand-rolled multipart encoder is how that promise would have ended. And the compose screen reads the list of pipelines and their flags from the engine instead of keeping its own copy, because a hardcoded copy of a gate drifts from the gate — and the drift arrives as a rejection nobody at the keyboard can explain.