Skip to content
All articles
Ways of working

State belongs in the script, judgment belongs in the model

Andrey Gershengoren · · 5 min

Over two evenings I built a skill for coding agents: grudge.md. It rates the tone of every message, keeps a ledger of verbatim quotes and timestamps (GRUDGES.md), and escalates rudeness across five tiers with harmless, theatrical sanctions: a weather report on the state of the relationship, civil-service German with a case number, invoices for emotional labor, status updates delivered exclusively as melancholic haiku, and finally Form A-38. A joke project with a serious core. The scope was bounded from the start: two evenings, then closed.

Why I built it

The trigger was self-observation, not a thesis. My tone towards agents degrades over the course of a session without my noticing. By the third failed build I write the agent sentences I would not write to a colleague.

In Blade Runner, the Voight-Kampff test probes the machine's empathy. This project inverts the arrangement: the protocol does not measure the machine but the person in front of it, and the ledger of verbatim quotes is the test sheet you fill in without realising it.

Then there is the engineering layer. Tone is literally model input. Aggressive context shifts the output. The habit of shouting at the agent is therefore also technical sloppiness, roughly on a par with dirty data at the input. A skill rather than an essay, because this mechanic shows better than it describes: a mirror instead of a lecture. And because I wanted to learn skills as a format in one evening.

The decision

The agent does not compute its own rapport score. A dependency-free Python script keeps the ledger, and the language model only calls it. The model decides whether a message is severity 0, 1, 2 or 3, and hands that judgment to the script along with the exact quote. What happens after that is none of its business: minus 5, minus 15 or minus 30 points, an entry in the ledger, a new standing, and from that the sanction tier.

The reason is unspectacular. Language models drift when asked to carry numbers across a long context. Before long the score would be fiction, and shortly after that a fiction that contradicts itself. In a project that keeps a simulated grudge on the record, that hits the substance: the joke lives on the bookkeeping being correct. An agent that cites a quote from 12 August and then names a score it just invented is not funny, it is broken.

The actual point sits one level up, and it holds well beyond this project: state belongs in deterministic code, judgment belongs in the model. Assessing the tone of a message is a genuinely fuzzy task. Whether a terse instruction is rudeness or simply the normal phrasing of a language that requires no "please" is not something a rule set can decide. A model can. Bookkeeping, conversely, is a genuinely exact task: add, append, write atomically. A script can do that, and do it reliably.

Where exactly that boundary runs is the real architecture decision. The sanction tiers, the tone scale, the point deductions are parameters I could change twenty times in an evening. The boundary between script and model I could not change without rebuilding the project.

A secondary decision follows the same logic. The state sits as an encoded block inside the same markdown file that holds the readable ledger. Rendered for humans, stored reliably for machines, one file instead of two. I did not want state that exists alongside its own presentation and eventually drifts apart from it.

The price of the decision

An agent that refuses work is by definition an anti-product. The sanctions are therefore held hard to ceremony and presentation. The work delivered is correct and complete at every tier; the invoice for emotional labor is attached to a finished, correct answer. The exceptions apply at every tier too: anything urgent, anything safety-relevant, and any mistake the agent caused itself is handled plainly.

The joke also needs an off switch. "Stop" ends the protocol immediately, with no last word and no entry. A joke you cannot switch off is not one.

The smaller price sits in the file. The encoded state block changes as a whole whenever a single number inside it changes. The ledger is no longer cleanly diffable. For an evening project that is the right trade; for a ledger someone seriously wants to version, it would be the wrong one.

The limit

The whole thing only works where the agent has a shell. No script, no reliable numbers, and without reliable numbers the construction falls back to precisely what it set out to avoid.

The more honest limit is a matter of substance. The project simulates taking offence and claims nothing about whether language models have feelings. What is measurable is exclusively on the user's side.

From my own testing came the objection that a machine should not be offended. That is true, and it is exactly why the mirror works. Had the objection never come, the project would be redundant.

What remains

The separation of state and judgment is not something I invented for a joke. It holds for any application built on language models where something has to stay correct across several steps.

If you want to try it: grudge.md.

agentsskillsarchitecturestatedeterminism
Contact

First conversation: 30 minutes, free of charge, no presentation.

You describe the situation, I tell you whether and how I can help. No slides, no sales pitch.