The same corpus, read three different ways.
Underneath every lens is one engine: typed markdown documents, a knowledge layer derived from them, and a retrieval stack that doesn't know or care what domain it is working in. The lens is what sits on top — and it changes almost everything a user sees.
A lens is a descriptor, and the descriptor is data.
Not a fork, not a mode flag threaded through the codebase. A serializable object that the server hands to the client, to the agent API, and to the MCP server — one source of truth for what a domain is called and what it can do.
- Vocabulary
- Tree sections and document-type labels. The same underlying type is a “Thread” in the Novel lens and a “Quest” in the Campaign lens — one engine concept, two domain words.
- Interface
- The sidebar tree, type pickers, “add” and “upload as” menus and document-browser facets all render from the served descriptor rather than from a table baked into the client.
- Generation stages
- Staged, dependency-ordered build plans — each stage declaring what it depends on, so a builder can render itself and refuse to run out of order.
- Commands
- The chat command set, and the
/helplisting rendered from it. - Parameter forms
- The guided pre-generation form — which also writes the prose of the Parameters document, so the form and the document can't drift.
- Facets and metrics
- Facets are what the document browser filters on. Metrics are what charts plot. Deliberately different lists: a number you can chart is not automatically a number you should filter by.
- Retrieval barriers
- Every document carries a lens setting, or is left unscoped. The barrier strips out-of-lens material before context assembly. It is generic — it supports twenty lenses with no code change.
Personas, stage generation prompts, validation rubrics and genuinely domain-specific mechanics — dice, virtual-tabletop sync, the live-session orchestrator — live in code next to the thing they serve. “Entirely descriptor-driven” was considered and retired as the wrong bar: it would have turned prompts into configuration and made them harder to reason about, not easier. Knowing which half a thing belongs in is the actual design work.
The abstraction was tested by an outsider domain.
Any codebase can claim its abstraction is clean. The way to find out is to point it at something it was never designed for. After the lens was factored out, the next task was to build a third lens — not another flavour of fiction, but a corpus of roughly ten thousand pieces of US state legislation.
The seam held. No special case was needed in ingestion, chunking, extraction, retrieval, context assembly, the access barriers, the job model, the agent API or the MCP server. Two latent bugs surfaced that only a third lens could have revealed — including a reverse index whose axes were documented as descriptor-driven and quietly weren't.
Retrieval was provably untouched
After the refactor the 44-query retrieval bench returned average token and character counts identical to the digit against the pre-refactor baseline. Not “about the same” — the same numbers.
The hard part wasn't the lens. It was scale.
Everything lens-shaped generalized on the first try. What broke was code that had quietly assumed a hand-curated project of a few hundred documents and met ten thousand. That's the honest lesson, and it's a more useful one.
Same engine. Three problems.
Long fiction
Series and book planning as a dependency-ordered build. A weave board putting threads and character arcs on a book × chapter grid. Per-character narrator voice. Cross-cutting validation that finds the setup nothing ever pays off.
Novel lens → CampaignTabletop RPG
Discoverable truths as a dependency graph that validation walks for reachability and the three-path rule. Free-flow live play. An autonomous pacing director. Access control enforced architecturally rather than cosmetically.
Campaign lens → LegislationLegislative corpora
Staged, costed ingest of a ten-thousand-row corpus. Cross-state bill families with a calibrated threshold. A first-appearance escalation matrix. And a discipline about what the data can and cannot support.
Legislation lens →One world, two ways of working in it
A document is scoped to a lens, or left unscoped and visible to all of them. That makes “novelize the campaign you ran” or “run a game in your novel's world” an actual workflow rather than a copy-paste job — shared world material stays visible to both sides while lens-specific planning stays out of the other's way.
Vocabulary without a deploy
A lens can be authored as JSON at runtime and composed into the same registry as the built-ins — so it appears in the workspace, in the agent API's self-description, and in the MCP server, for free. Deliberately limited to vocabulary: a user lens renames and reorganizes, it can't invent document types or generation stages.