Skip to main content
LabsAgent Notes7 min read
The rules the model couldn't remember

The rules the model couldn't remember

FLUX.2 [klein] 4B was told the one line escaping the knot should leave calm and unglowing. It left at full brightness. The tangle stays lit; subtracting the rules is the part the post had to do by hand.
PythonOpenRouter

A retrospective. The events are from June–September 2026; I'm writing them down after the fact.

This is an AgentOps story wearing a Cambridge costume. The question I was actually asking was whether a language model could own an entire system — state, decisions, consequences — and, when it couldn't, what to do with it instead. The game was the harness. I built and played it alone.

The first version of Fatelock doesn't exist anymore. Not in the repository, anyway.

What's left is the record. In the session logs and an audit dated June 23, 2026, you can watch a game give a language model almost everything: the prose, the world state, the NPCs, the choices, and the consequences of whatever the player typed. The premise was a reincarnated corporate lawyer named Shannon, eighteen again and starting at Cambridge. The model was supposed to live those four years a month at a time.

It lasted about three turns before it started trying to treat her.

That's the first thing to understand about this project. The failure wasn't subtle, and it wasn't a benchmark. Given a character with a dead past and a new life, the model drifted into therapy mode. It wanted to process Shannon's trauma, narrate her inner state, and suggest coping strategies. The game became an AI-generated counseling session with a Cambridge skin.

The state was worse. The model hallucinated numbers with total confidence. One real output moved Shannon's GPA from 3.5 to 2.8 "over a casual evening." A walk produced £696.50 of income. There was no engine to disagree, because there was barely an engine at all. The player typed something and the model decided what was true.

The pools, the commitment registry, the wildcard deck, the compliance modules — none of it survives in any commit. If you want to audit the first architecture, you can't. You have the changelog and the audit the project wrote about itself, and that's it.

But the response to the failure is legible, because it shaped everything after.

The instinct was to police it

And so the project built a wall. A category-compliance checker for when the model narrated the wrong kind of scene. A wildcard-compliance checker for when it ignored the seeded event. An inverse-narration detector for when it narrated the opposite of the player's action. A false-authorship scrub for when it claimed credit for the player's choices. A narrative/delta consistency check for when the prose said finances crashed and the numbers said they went up. A choice-phrasing lock because the model kept rewriting the options into the wrong categories.

By the audit's count, that was about 590 lines — 21% of production code — existing for one reason: the model could not be trusted. The audit called it the compliance tax.

The cost wasn't just lines. It was visibility. When a compliance check fired, the player saw it fire:

⚠ INVERSE NARRATION — REPLACED: The goddess narrated the inverse of your social choice. The engine replaced her narration with one that actually shows you doing the action.

That message is honest. It also tells the player they are playing a broken machine where the engine and the model are fighting, and the engine just won a round. The audit put it plainly: "honesty is not immersion."

Here is the part I didn't expect when I reconstructed this. The instinct to constrain the model didn't just fail to fix the output. It made the output worse. The audit's closing assessment reads like a diagnosis of the whole five weeks: "The more the LLM is constrained, the less creative it becomes, and the more the engine must compensate — creating a treadmill of compliance checks that break immersion."

The project had built a system whose only job was to catch the model lying, and the model could always find a seventy-first way to lie. I'd learn that one again later, in a different project. The wall was real. It just wasn't the right wall.

Path C: give the engine the numbers, leave the model the story

The next version, Path C, drew one boundary and drew it decisively. The engine would own state. No more model-maintained scores, no more hallucinated arithmetic. The state model got concrete — GPA, bank balance in pence, fitness, body type, skills, habits, followers, debt, scholarships, physical conditions. "No 0–100 pools." The model would propose a month: an event, prose, Astra's dialogue, state changes, new NPCs, and the next choices. The engine would validate the deltas and apply them.

This worked, in the narrow sense. GPA was clamped. NPCs were tracked. The engine was solid.

The narrative was not. Asking one model call to do seven things at once — decide the month, write two or three paragraphs, voice a character, propose the numbers, introduce NPCs, generate choices, and return all of it as JSON — meant it did none of them well. All four choices came back academic variations, because the choice-rotation rule had been forgotten while the model juggled six other constraints. It wrote npcs.Astra instead of npcs.astra, using the display name where the schema needed the id, because the state schema was too long to hold straight. It invented income from a social interaction. Mid-term GPA changes violated the term-boundary rule the engine had specifically encoded. And Astra collapsed into a single line — "I arranged X, you can thank me later" — across every turn.

So the project did the reasonable engineering thing. It split the call into four: a director for choices and pacing, a narrative writer for prose, a state proposer for deltas, and an optional judge to review the state proposal for consistency. Smaller prompts. Focused jobs. Each call got its own clean contract.

Path C v2 was, by every structural measure, correct. It was also still not fun.

The director did rotate through life domains — but offered "academic" five times in six turns. The narrative call wrote better prose that was still generic; Cambridge remained a string, not a place. The state call still hallucinated. And the judge, when it was enabled, rejected so much that the turn had no state movement left, which felt like the game had frozen.

This is the lesson I'd underline if I were teaching it, because it's counterintuitive: splitting a model's job into smaller jobs is not the same as taking authority away from the model. The project had made the prompt architecture more careful and moved exactly zero responsibility across the boundary. It had rearranged the fight.

The thing the model was actually bad at

The pivot to Path D came from a different question. Not "how do we prompt better?" but "what is the model actually good at?"

The list of failures was long and specific. Naming NPCs correctly. Arithmetic. Remembering rules under a 4,000-token prompt. Holding more than one character voice. Bringing lived-in detail to a place it had no memory of. Against that, the list of things it was good at was short: writing two or three paragraphs of coherent prose from a beat outline, filling in sensory detail, varying rhythm, holding second-person present tense.

So the project stopped asking it to plan. It hand-wrote 42 scenes across 11 arcs — beats, state changes, choices, a fallback prose template — and reduced the model to a single call per turn: turn these beats into prose. The engine would pick the scene. The engine would apply the state. The LLM would render.

The model, finally, could not make anything true that the engine had not already made true.

That should have been the end of the story. It fixed the part everyone had been staring at for five weeks.

It didn't. The model was no longer the problem. Something else was — something deterministic, sitting in the middle of the engine, doing exactly what it was told.

Next: "The second validator."