To move focus out of the editor, press Escape and then Tab.
Blog
· 5 min read

The rules the model never sees you write

Select a paragraph, ask for the grammar to be fixed, and a language model will do that. It will also, if nothing stops it, do a few things you didn't ask for.

It'll tidy up the spacing inside a formula. It'll see a line that reads #banana and put a space after the hash, because that's what a heading looks like. It'll rewrite the wording of a task and hand it back unticked. Each of those looks like a sensible correction if you assume you're reading ordinary prose, and each one breaks something once you know what those characters mean in this app.

So every AI request Snownotes sends carries a set of house rules alongside whatever you typed.

Your note isn't only prose

The text we hand the model is Markdown, and parts of it are doing real work that a model trained mostly on prose will read as a typo.

Math
$...$ and $$...$$ hold LaTeX, which the app renders as a formula with KaTeX. A model asked to proofread will respace the braces or "fix" a backslash, and the formula stops rendering.
Tags
A line that is just #banana is a tag, not a heading missing its space. Add that space and the tag becomes a heading and drops out of every tag filter.
Todos
- [ ] and - [x] are checkboxes. Rewording an item must never change whether it's ticked.
Inline marks
Highlight, subscript, superscript, and the underline tag are formatting somebody chose, not stray punctuation waiting to be cleaned up.

Internal links have their own version of this. A link to another note carries an id that means nothing to a reader, so rewriting the visible label is fine and touching the target breaks the link. On top of that there's a rule that applies to every edit regardless of what's in the note: leave heading levels, list markers, indentation, table pipes, and code fences alone unless that's what was asked for, and don't touch code unless the instruction is about the code.

Written out as a list this all looks like small print. It matters because of what happens without it: you'd have to reread the whole note after every edit to check nothing had been quietly reformatted, and an assistant you have to audit that closely isn't saving you much.

Working out what's a formula is the hard bit

The math rule has a wrinkle worth pulling out, because it's where this kind of rule usually goes wrong.

A dollar sign on its own tells you nothing. "$5 for coffee, $10 for lunch" is money. "range $5-$8" is money. If you treat every dollar sign as the start of a formula you end up protecting text that was never a formula, and, worse, telling the model that somebody's shopping list is mathematical notation it must reproduce exactly.

So the test is narrower. It looks for $$ anywhere, or a matching pair of single $ signs with no space pressed up against either one and no digit straight after the closing sign. That last condition is what keeps the coffee out. It's the same test the Markdown importer uses when it decides what to turn into a formula, which is the point - the rule we send the model and the rule the app applies have to agree, or we're warning the model about something the app never created.

One file, not fifteen prompts

The rules live in a single module and get composed into each prompt from there. Before that they were copy-pasted: the list of things to preserve existed in one prompt only, and the math rule had been pasted into two others.

That's the real argument for doing it this way. Duplicated prompt text isn't untidy so much as unreliable, because the copies stop agreeing and nothing tells you they have. Rules do change: ours used to instruct every model to avoid LaTeX entirely, which was correct advice back when the app couldn't render it and became wrong the day math support shipped. Leaving one stale copy behind would have meant a working feature quietly suppressed on whichever screen still carried the old text, for as long as nobody thought to check.

The rules are tested too, though not by checking their wording, which would only pin the phrasing in place. The tests check the things that matter: that every prompt which writes into a note carries the preservation block, that it's there for every kind of block and not just paragraphs, and that the formula detection says yes to formulas and no to the coffee.

What this actually is

It's tempting to file prompt work under writing - find a better phrasing, get a better result - and some of it is that.

Most of what these rules do, though, is state facts about the data: this substring is meaningful, that marker carries state, this id is not for humans. That's closer to the validation you'd write at any other boundary in a program, and it wants the same treatment. Define it once, compose it in, and have a test fail when someone adds a sixteenth prompt and forgets.

There's one more rule in that file worth its own post, which is which language the answer comes back in. It turned out to be two rules.