One line

A writing environment for academic papers, where the memory layer is what makes the interface cognitively accessible, what stops the AI putting words in your mouth, and what lets the finished paper account for how it was written.

What it is

Publishing is how a researcher is measured, and the act of writing a paper sits heaviest on the people with the most to say and the hardest time saying it in this form — researchers with dyslexia, with executive function disorders, and the very large number writing in a second language. AI is obviously part of the answer. It is also, at the moment, a machine that fabricates citations in prose indistinguishable from your own.

Typeset divides a paper into fragments in CockroachDB and acts on one at a time, so a rewrite is a small, checkable transformation of text you wrote rather than a conversation that drifts. Anything you would otherwise hold in your head — what this paragraph is meant to do, which reference supports it, whether you already asked for this and said no — is a row in the database and a thing on the screen.

That is one architecture serving two ends. Externalising working memory is what makes the interface chunked and low-friction; it is also what makes the agent cheap, because a fragment plus a retrieved note is a smaller prompt than a conversation. The accessibility case and the token case are the same design.

And the memory layer is not storage behind the agent. It is what the agent is checked against: a citation is legitimate because there is a row for it in bib_references; a name is invented because it appears in no row of modules; a rewrite is worth a second look because you rejected one like it in rewrite_validations.

It is also what the agent is accountable for afterwards. The Review tab exports How this document was written — a typeset record of every word the author typed, pasted or moved, every instruction given to a model, every answer returned including the refused ones, and every citation with what cites it. Journals increasingly demand this disclosure and almost nobody can honestly produce it, because the evidence never existed. Here the evidence is the schema, recorded transactionally as the work happened. A chat log cannot produce this document: it does not know which of its output survived into the paper, and it knows nothing at all about what the author typed.


CockroachDB tools, and what the agent did with them

Distributed Vector Indexing

Embeddings (Amazon Titan Text Embeddings v2, 1024-dim, normalised) on three tables, each with CREATE VECTOR INDEX over it, and three searches that use them:

  • bib_references.embedding — suggesting a citation. The agent embeds the fragment being edited and searches the author's own bibliography for references that fit, returning each with its cosine distance and the terms it shares with the text. Suggestions only; the author picks. Accepting one makes it a key the guard permits, so the rows that suggest a citation are the rows that authorise it — one mechanism, not two that can disagree.
  • modules.embedding — choosing the context, when context is needed at all. A rewrite used to travel with every module title in the file, which is bounded by the document rather than by the edit. It now travels with the k nearest fragments by meaning — and only when the instruction is about the paper rather than the passage. Grammar, translation, register and tightening are answerable from the fragment alone, so they retrieve nothing. Retrieval was costing 506 tokens against a 198-token fragment; making it conditional roughly halved the cost of an edit and made it exactly flat.
  • rewrite_validations.embedding — recalling a decision. Before running, the agent searches the instructions the author has already judged and warns when one is about to be repeated, showing the earlier wording and what became of it. It warns and never blocks. This is the memory changing what the agent does next, not merely recording what it did. Vector data sits in the same transaction as the operational rows, so a reference and its embedding cannot disagree. Embedding is never on the critical path: rows commit first, vectors follow, and embed_hash stops a sync after every typing pause from re-embedding the paper. A cluster without vector support runs the whole application unchanged and loses only search by meaning; a missing index degrades to a scan rather than an error.

Managed MCP Server

https://cockroachlabs.cloud/mcp over streamable HTTP, OAuth 2.1 with PKCE, read-only. .mcp.json is committed and carries no credential; the API-key form for headless agents goes in .mcp.local.json, which is gitignored.

Claude Code connects to the paper's memory and answers questions the app deliberately has no screen for — which references am I citing nowhere, where did I overrule the checks, does an edit cost more as the paper grows. Six worked examples with their SQL are in MCP.md.

The app does not use MCP at runtime and should not: it has its own pooled connection and its own guardrails, and routing an author's keystrokes through an agent's tool call would be slower and less safe. MCP is how the developer's agent reaches the memory, which is what the tool is for.

Not used

ccloud CLI — provisioning and operations, which would be padding on an authoring tool. Agent Skills Repo — the natural next step: the fragment schema and the citation-integrity rules would package cleanly as skills.


AWS services, and how

  • AWS Lambda — the whole backend is one function: LaTeX compilation, model calls, versioning, embeddings and the rewrite guard. Container image with a TeX distribution.
  • Amazon Bedrock — every model call. Claude Sonnet for splitting a document into fragments, rewriting one, and formatting a BibTeX entry; Amazon Titan Text Embeddings v2 for the vectors.
  • Amazon S3 — the frontend is one static HTML file, served as a static website. ---

Against the judging criteria

Agentic Memory Design. The schema is the document's structure, not a blob store: fragments with their notes, versions, references with parsed fields, a validation record per attempt, and author_edits for what the human did with their own hands. Vector search closes the loop — the memory is retrievable, and past decisions shape new ones rather than sitting in a log.

Technical Implementation. Two parser implementations — Python for what lands in a file, JavaScript so the browser can edit without a round trip — held together by a parity suite generated from Python. 286 backend tests, 84 auth tests, 321 UI checks, 124 parity checks, 51 page checks, one command from a clean clone. Structural edits are proved text-preserving rather than assumed: dividing a .bib by entry, moving a heading inside its markers, reordering sections all assert the document is byte-identical afterwards and drop the change if it is not. Failures degrade rather than break — no vector support, no database, Bedrock unavailable, a missing route all have a defined and tested behaviour. tests/test_docs.py even re-counts the suites and fails if the README's numbers have drifted.

Real-World Impact. Three problems, one architecture. Researchers who find writing disproportionately costly get an interface that chunks the work and never asks them to hold a paper in their head. Every researcher using AI gets a paper that cannot cite work they do not have. And the research community gets something it currently has no mechanism for at all: traceability of a written contribution.

Science assumes provenance for its data — where it came from, who touched it, what was done to it. Its prose has never had that, and the arrival of capable language models has made the absence urgent. A finished .tex looks identical whether a passage was typed by the author, pasted from a chat window, or rewritten by a model. Typeset's exported record distinguishes them, because the distinction was recorded at the moment it happened. It is generated from the editor's own history rather than recalled weeks later, and it states its own limits explicitly — a disclosure that overstates its coverage is worse than none.

Production Readiness. Nothing enters the document unaccepted; every change is an undoable version. The guard fails closed — a rewrite that trips a check is discarded before display — and the citation check fails closed on a database error rather than open, because an outage that silently disabled it would be the worst failure available. Recording is best-effort, so losing an audit write never costs an author their work. Secrets are gitignored and tested for. Every MCP request is logged and traced by CockroachDB Cloud.

Creativity & Originality. Two ideas. First, an agent's memory is not only what it reads from — it is what it is checked against, which turns the database from context into evidence. Second, the interface that is kindest to a tired human brain and the interface that is cheapest in tokens turn out to be the same interface, because both are solved by externalising working memory.


Where this goes next

The project's direction is one idea applied repeatedly: the right context for an edit is a property of the edit, not of the document. It has been applied twice already — from every module title in the file, to the four nearest fragments by meaning, to nothing at all when the instruction is a grammar fix. Each round produced better answers for less money, because the context that was dropped was never doing any work.

What that leaves is a system whose largest single cost is no longer the paper. An edit is 594 tokens and 346 of them are the system prompt, identical on every call. The next round is therefore: cache it; stop calling a model for deterministic transformations that a regex does exactly and for free; scope below the fragment for single-sentence edits; replace retrieval-for- terminology with a glossary; batch the mechanical passes; and start measuring the output side, which none of the numbers above include.

Furthest out, and the reason this belongs in a submission about memory: models of our own, trained for exactly this task. The work here is a closed set of narrow transformations — grammar, translation, register, tightening, BibTeX formatting — on short inputs. That is what small purpose-trained models are good at, and the saving is already quantified above: 346 of the 594 tokens in an edit are a constant system prompt stating rules a fine-tuned model would simply have. Trained into the weights rather than resent on every call, an edit drops to 248 tokens, on a model that is also cheaper per token and fast enough to make a grammar pass feel instant.

The training data is already accumulating in CockroachDB. rewrite_validations stores every attempt as (instruction, input fragment, output, verdict, author's judgement) — supervised fine-tuning data in the shape it is needed, including the refused outputs, which is the half most datasets lack. Amazon SageMaker to train, Amazon Bedrock Custom Model Import to serve it behind the same call the Lambda makes today. The memory layer is not only what the agent reads and is checked against; it is the corpus the next agent is trained on.

Three constraints stated rather than glossed: the citation checks stay regardless, because the guarantee is that a citation was verified and not that a model was trustworthy; a fine-tune needs volume this project does not yet have, so the claim is unproven rather than merely unimplemented; and training on unpublished research needs consent that storing a draft did not grant, which points at per-author adaptation or explicit opt-in.

Every one of these decisions is made from recorded evidence rather than intuition. The app stores Bedrock's real token counts and which rule chose the context on every single edit, so tools/measure_context.py --from-db can say whether a change helped on this author's actual usage.

Built With

Share this project:

Updates