A deterministic core, with reasoning at the edges

Open three pull requests and you can tell three people wrote them. One convention, three readings of it. What fixes that is not a better shared document, it is a script that owns the decidable half.

Open three pull requests from last week and you can tell, without reading the names, that three different people wrote them. Same convention behind all three, three readings of it, nobody wrong on purpose.

Writing the convention down is the obvious answer, and it has a ceiling. A document is something a person can skim, disagree with, or quietly not apply, and nothing checks. Putting it in the prompt is worse, because that is the least durable place a rule can live: true for one window, one person, one afternoon. The rung above a rule you can read is a rule you can execute.

Splitting a repeated task by what a machine can decide A repeated task divides into two lanes. The upper lane is the checkable half: a script that produces the same answer on every run. The lower lane is the judgment half, handled by the model, which produces a different answer each time. Both feed a gate, and the script decides whether the result passes. one task, done often the checkable half a script: same answer every run the judgment half the model: a different answer every run the gate the script decides merged
The split is not by difficulty, it is by decidability. Anything a machine can rule on moves left and stops varying. What is left is the judgment you actually wanted, and it still has to pass. Illustrative — the shape of the argument, not measured data.

Split by decidability, not difficulty

Take any task your team does repeatedly and ask of each rule in it:

Can a machine decide whether this was followed?

That question, not difficulty, is the line. “No em dashes in the prose” is trivial for a person and trivial for a machine, so it belongs in a script. “Is this argument any good” is hard for both and belongs nowhere near one.

What ends up on the left of that line stops varying. What is left on the right is the judgment you actually brought the assistant in for.

A worked example

This site enforces its own house rules. The rules came from measuring what the old archive did, and they are the kind of thing that would rot in a document inside a month: a prose budget, no em dashes, at least one code block, at least one diagram, never more than three prose paragraphs in a row.

None of that needs judgment. All of it is decidable, so all of it is a script.

PROSE_MAX = 8500        # measured: the longest archive post is 8,234 of prose
MAX_MONOLOGUE = 3       # consecutive prose paragraphs before something must break it

def check(path):
    text = open(path).read()
    body, blocks, prose = split(text)        # front matter and fenced code excluded
    fails = []

    if len(prose) > PROSE_MAX:
        fails.append(f"prose is {len(prose)} chars, over the {PROSE_MAX} budget")
    if prose.count(""):
        fails.append("em dash in prose: use a comma, a colon or a full stop")
    if not blocks:
        fails.append("no code blocks")
    if "{% include fig-" not in body:
        fails.append("no diagram")

The script is boring on purpose. It has no opinions. It counts characters, looks for substrings, and returns a list of failures. Run it a thousand times and it answers identically a thousand times.

What stays with the model

The same house rules include things no counter can settle. Whether an example is the right one. Whether the angle is fresh or has been written a hundred times. Whether the reason given for a rule is the real reason.

Those live in a document the assistant reads, and they are phrased as judgment rather than as checks:

---
name: write-post
description: Draft or revise a post so it passes the house rules first time.
---

The subject is the idea. A tool is the worked example.

Be specific and generous with examples: name the product, show its real
config, paste its real output. Just do not let it become the topic.

Two artifacts, one workflow. The script owns everything decidable and never gets it wrong. The document owns everything that needs a reason and never pretends to be enforcement.

The part that makes it stick

Splitting the work is only half of it. The other half is that the script runs whether anyone remembers it or not:

$ git push
pre-push: checking 1 post(s)
    prose 3593 / 8500   code blocks 2   diagrams 1   longest prose run 2
    PASS

A script nobody runs is a document with extra steps. Part 6 and part 7 are about the places to attach one so that running it is not a decision anyone makes.

When not to write the script

The pattern has an obvious failure mode, which is encoding a rule you have not actually settled. A check that fires on something you are still arguing about teaches people to route around checks, and that habit is expensive to get back.

write the script when leave it as judgment when
the rule has survived a few real cases you thought of it this morning
a violation is obvious once pointed out reasonable people still disagree
you have said it more than twice it is genuinely one-off
the check is cheap to run checking costs more than the error

The measured version of this: every threshold in that script came from counting the archive, not from taste. 8500 is not a round number someone liked. It is the longest real post, plus a little air.

A rule you can defend with a number is a rule that survives someone disagreeing with it.


If your team has the same conventions written in four places and enforced in none, that’s the work I do.


Working through this in your own team?

I help engineering teams adopt AI coding assistants without giving up engineering rigour, and design the architecture underneath. If that's on your plate, let's talk.