A Writing Desk That Keeps Its Receipts

Scriptorium is free and open source. It steers a voice instead of replacing one, and it can prove what it published.

Eric Garcia, PhD

Eric Garcia, PhD

September 14, 2026 · 8 min read

A writer's desk at night, lit by a brass lamp, with a city skyline and a spire in the window behind it. An open laptop shows a markdown file in a tab labeled "draft.md": the page reads "A Writing Desk That Keeps Its Receipts" and, beneath it, "Scriptorium is free and open source. It steers a voice instead of replacing one, and it can prove what it published." A side panel titled "History" lists five commits with short hashes: "Publish to Substack 4d2f9e 2 hours ago", "Revise section 6 9a7c3b yesterday", "Address notes e3f1d0 2 days ago", "Rework opening 7b4e1a 3 days ago", "Initial draft c1a9f6 1 week ago". A long paper receipt curls from a stack of labeled archive boxes at the right, across the desk and off its front edge, printed with a timestamped ledger: "2026-09-10 14:22 Edit draft.md 7b4e1a", "2026-09-10 16:03 Run checks ✓ 0 issues", "2026-09-10 17:41 Revise section 4 e3f1d0", "2026-09-11 09:12 Add figure a9c3b7", "2026-09-11 11:08 Final pass 4d2f9e", "2026-09-11 12:00 Publish to Substack ✓ Sucess", "2026-09-11 12:01 Publish to LinkedIn ✓ Sucess", "2026-09-11 12:01 Update site ✓ Sucess". The archive drawers are labeled "DRAFTS", "CORRECTIONS", "VERSIONS", "PUBLICATIONS", "RECEIPTS". On the left, a stack of books has spines reading "IDEAS", "DRAFTS", "REVISIONS", "PUBLICATION", "A LONGER CONVERSATION". A coffee mug reads "Good writing remembers." A card pinned to the wall reads "Same words. A longer life." An open notebook with a fountain pen lists "Notes: 1. clearer example, 2. tighten transition, 3. check citation, 4. more concrete, 5. end with what it's for". A printed page of the same essay lies in front of the laptop, and a dark notebook at the right corner is embossed "A SMALL SYSTEM FOR A LARGER LIFE".
The draft on the screen can always be redone. The tape can't.

Start with the promise in its strongest form, because the argument only counts if it survives that. Paste three of your essays into a model. Say "like this." Describe what you want to say. Out comes a draft that is fluent, structured, and plausibly yours, in the time it takes to make coffee.

Grant all of it. The models are good. The prose is clean. The hours saved are real, and a person who writes for a living has every reason to want them. So take the promise as given, and then watch what happens over ten pieces instead of one.

The voice drifts. Not toward anything wrong, just toward the middle: the model's mean, which is the average of everyone it has read. The corrections you made last Tuesday are gone, because the chat is gone, and Tuesday's version of "like this" was never written down anywhere the next session could find it. And when a piece finally goes out under your name, there is no record of what the tool did on your behalf: which words it changed, which it retyped, which it dropped on the way to the page.

None of these is a generation problem. The generation was fine. They are problems of custody: whose voice this is, what record exists of what was done in your name, and whether the words that reached the reader are the words you wrote. That is the interesting problem in writing with a model, and it is the one that scriptorium, a writing desk I have been building since August, is built around.1

The room and the furniture

A scriptorium was the room where texts were composed and copied. The name is a way of saying what is shared and what is not. The room's furniture, the skills, the templates, the tools, the starter voices, is public, in a repository anyone can read and fork. Your desk is a separate, private repository that holds your pieces, your drafts, your logs, and your trained voices, and it mounts the furniture as a git submodule.

That boundary is the whole trick, and it is not a security feature so much as an honesty one. Your writing keeps its own history, in a repository only you hold. The framework can change under you and your drafts do not move. And the desk's log is append-only, which sounds like a small rule until you ask what a log is for. A log you can revise is a story. A log you cannot is evidence.

Steering, not training

The word "train" is doing a lot of quiet work in the promise. What most people mean by "train it on my voice" isn't training. No weights move. What you can do, and what scriptorium does, is steer: give the model structured context that describes the voice, and hold that context somewhere it can be read, corrected, and refused.

A style, on this desk, is a folder. There is a constitution, a page of prose that says what the voice is, what it does, and what it does not do. There are knobs, a short config of the mechanical settings a skill can read without interpretation: formality, sentence length, person, the words to avoid. There are exemplars, passages that embody the voice, which are the actual steering signal. And there is a corrections file, append-only, where every "changed X to Y because" accumulates as you draft.

You train a style the way you refine anything else here: you draft, you correct, the corrections pile up. Then, deliberately, a gated pass distills them into the constitution. The constitution is never silently rewritten; it changes when you say so, in a commit you can read. Each piece picks exactly one voice and does not blend it. The essay voice on this desk was seeded from a single talk in September. Its corrections file has grown to a few hundred lines since, and the constitution has not changed, because nothing in that file has yet been folded in through the gate.2

The point of all this structure isn't that it's clever. It's that the voice becomes an artifact. You can diff it. You can see the day it changed and the correction that changed it. And when a draft comes back sounding like the model's mean, there is a file to point at and say: not that.

The transport

The place where custody is easiest to lose is the one nobody thinks about, which is the trip from the draft to the page.

Substack has no public write API. To publish, something has to drive the editor. The first version of the desk did the obvious thing: it baked the essay into a script and had the agent type it in. Tens of thousands of characters of your prose, retyped by the model that had just helped you write them. The failure mode of that design is quiet. One wrong character is indistinguishable from an intended edit. Nothing downstream can catch it, and what you publish is a typo in your own voice.

The fix is to take the agent out of the transport entirely. The composed page goes to disk, from disk to the system pasteboard, and from the pasteboard into the editor by a real paste, the same keystroke you would use. The bytes travel disk to pasteboard to browser to editor and are never retyped by anything. That requires a real browser, which is why the desk is honest in its README about needing one and about running on macOS today, with the one platform-specific function named so that a Linux or Windows port is a small, well-scoped contribution rather than a rewrite.3

Then the desk checks. After a publish, it reads the live document back, computes a digest of its text block by block, computes the same digest from the draft on disk, and compares. They should be identical. Not close, not "looks right": identical. That check is what makes the transport trustworthy, and it is the bar any new platform has to clear before its transport becomes the default. A tool that says "published" and means "sent" is not keeping a receipt. It is keeping a hope.

Six sessions at one desk

On the second of September, six Claude sessions worked this desk at the same time.4 That day taught the desk most of what it knows about being shared.

Nothing owned by one piece was damaged. A piece's folder has exactly one owner, and its log and corrections are append-only, so six sessions could write six pieces and never collide. All the damage landed on the shared singletons. A dashboard that every session read whole, edited its own block, and wrote back whole was silently overwritten three times in one afternoon. A two-file commit went out carrying four files of another session's unfinished work, because the git index is shared too and a bare commit sweeps in whatever anyone has staged. A fixed localhost port, plus a swallowed bind error, served one session's code to another session's browser.

The fixes are boring, and that is the point. The dashboard is generated from one fragment per piece, so two sessions updating two pieces write two files. Commits are scoped by path, which is the only form that ignores what someone else has staged. A session takes a lease before a long edit and says what it is doing; the lease is advisory, stops nobody, and does not pretend otherwise. What it buys is that the next session finds out in one call that this piece is already in someone's hands, and whose. The port is derived from the session and fails loudly when taken. And every push runs the test suite first, on an export of the exact commit being pushed rather than on the working tree, because a working tree full of other sessions' uncommitted files can make a broken commit look whole.

None of these rules is interesting. Each one is there because something specific went wrong on a specific day, and the log says which. That is the other thing an append-only log is for: not just proving what you wrote, but explaining why the desk is shaped the way it is to whoever sits at it next.

One piece, many readers

A piece written once on the desk goes to several places: a Substack, a LinkedIn article, and one or more websites. The websites are the canonical copies, and they render the piece themselves. If each site owns its own markdown pipeline, they disagree. This isn't hypothetical. One site rendered footnotes and images correctly while another turned inline bold into literal asterisks, from the same file.5

So the desk has a companion, quire, named for the gathering of folded leaves that becomes a book. It turns a scriptorium content bundle into React components, and it owns exactly one thing: what the markup means. The site owns what it looks like. A publish is an upload to a content store, not a deploy, and every site that reads the store reads the same piece the same way. It is the smaller of the two repositories, and the one that made the sites stop arguing.

What it is for, and why it is free

This desk was built for one writer, and the writer is me. That is worth saying plainly, because a tool built for its author is either narrow or honest, and the way to tell is whether it admits what it needs. Scriptorium needs Claude Code and a git repository for the writing loop, and a real browser and a Mac for publishing to Substack. The README says so up front rather than implying it runs everywhere.

Inside those limits, it is for anyone whose writing has to stay theirs. Writers who want a voice that holds across a year of pieces instead of a week. Researchers who need the citation checked against the shelf, the quote checked against the source, and a record of when each check ran. Engineers who would rather read plain Python and markdown than trust a product, and who can extend a skill in a day. And, since MuffinLabs is a consultancy, it is a showcase of how the company works: a public framework, a private instance, evidence at every step. That shape is what MuffinLabs builds for teams. The framework is free and open source because the value was never in the code. It is in the discipline the code enforces, and discipline is not something you can keep to yourself.6

The tool's job was supposed to be writing. Two hundred and fifty-five commits later, its actual job is remembering: what you said last time, what you decided, what went out under your name and whether it matched.7 The prose you can always redo. The receipts you cannot.

Footnotes

  1. The framework is at github.com/muffin-labs/scriptorium and the renderer at github.com/muffin-labs/scriptorium-quire, both under the MIT license. ↩

  2. The style model is documented in scriptorium's style guide. The essay voice was seeded on September 9, 2026, as a peer of the talk voice; its constitution says it "is new and mostly hypothesis," which is the right thing for a constitution to say about itself. ↩

  3. The README's Platform support section names the one macOS-specific function, set_clipboard_html, and the requirement it has to meet: the HTML must land under the HTML clipboard flavor, not as plain text, which pastes, looks like it worked, and silently drops every heading, italic, and link. ↩

  4. From the desk's own working rules, Working alongside other sessions, which records the count for September 2, the three dashboard overwrites, the swept commit measured the next day, and the port collision. The rules are in the private instance, not the public framework; the fixes are in both. ↩

  5. The incident is recorded in quire's README under What it is for. The renderer's package is named scriptorium-quire because the short name was already taken on npm; in prose it stays quire. ↩

  6. A page describing both repositories, and the shape they make together, is at muffinlabs.ai/scriptorium. ↩

  7. Counted with git rev-list --count main on the framework on September 14, 2026: 255 commits, the first on August 4. Seventy of them landed on September 10 and seventy-six on September 11, which were the two days the publishing transport and the shared-desk rules were built. ↩

Eric Garcia, PhD

Eric Garcia, PhD

Founder & Principal Consultant

PhD in Machine Learning from UW. Former Spotify ML engineer. 15+ years building production ML systems.

Learn more about Eric →

Need Help with Production ML?

We help companies build ML systems that actually work.

Learn About Our ML Consulting