
What I let the model keep
I started Fatelock to find out whether a language model could run an open-ended choose-your-own-adventure game. Not a fixed story with a thin model skin, but one where the choices were effectively limitless and the model kept the world straight while you played. Prompt it well enough, verbose enough, correctly enough, and the whole apparatus of a narrative engine becomes a system prompt.
That was the bet. It lost. This post is about what happened after I stopped pretending it might not: I built the deterministic game, then let the model back in through the smallest opening I could find, and then had to decide what the thing I let back in was actually worth.
I took the variable out
By Path E the model was gone from the runtime. I want to be honest about why, because it is the wrong reason, or at least not the whole one.
It wasn't only that the model had failed. It had, repeatedly and specifically, and the earlier posts in this series are the catalogue. But a bad turn could be the model confabulating, or the engine misrouting, or the contract between them being wrong, and I had no way to tell which. I had two systems failing at once. So I did the obvious experimental thing: I removed the probabilistic component entirely and made the engine prove it could be a game with nothing stochastic touching it. --no-render was the control condition.
It was also always the plan to put the model back. That matters for how this story ends. The model didn't lose the game because I gave up on models. It sat out the middle of the project so I could see what the game was when it was only mine.
I had also begun to suspect, by then, that the model was going to end up as flavor rather than decider. If that was true, then the game underneath had to be good on its own terms. Not good enough to tolerate, but good enough that you'd play it even if the prose never appeared. So the requirement wasn't "works without the model." It was "works without the model and isn't boring."
What the control condition showed
What I found when I looked at the deterministic game was in the previous two posts, and it's worth one sentence here: the engine had its own failures, and they were worse in a way because they were silent. A library session that lowered your law skill. A picker that routed your choice to an unrelated scene. A fragment bank where most of the content could never appear. None of those were the model's fault. All of them were mine.
That is the pivot that actually matters, and I didn't see it at the time. Removing the model didn't remove the problem. It changed the problem's species, from confabulation to a wrong value produced on purpose, every time, and therefore testable. A model that hallucinates costs you trust. A deterministic bug costs you time, and then it costs you nothing, because it's the same bug tomorrow and you can pin it down.
I let one thing back in
Once the deterministic game passed its own gate, I brought the model back. I brought back exactly one thing: prose.
No state. No choices. No routes. No NPCs. No endings. The one call returns a string. The runner records metadata about that string, its source and model and token count and latency, whether it retried or fell back, and never parses the string into anything. If the call fails, the scene has a hand-written prose_template, and the player does not notice.
The typed contracts from Path C are gone, and the way they went matters. They weren't narrowed. They were deleted. A system that keeps a model with a small typed franchise, a JSON envelope a verifier can inspect, is not the same as a system that keeps the model and forbids it to propose anything at all. Fatelock is the second kind. The model's only guarantee is that its output is not canonical. It cannot even be wrong in a way that matters, because the only thing that reads it is the screen.
Even the presentation had the same lesson
There is one more beat, and it is the one that makes the whole pattern hard to miss.
After all of that, I still had a place where I expected the model to carry something: continuity. Long games get repetitive, and the newest way to be repetitive was to have every month assembled from the same beats read like the same month. So I gave the renderer a digest of the story so far and a list of things to avoid reusing. Prompt-level memory. If the model just varied the telling, the repetition would soften.
The first rendered playtest showed the sections were invisible. The engine was still serving the same scenes, and the presentation lens the renderer read from was returning the same value, your body / institutional, nearly every turn. Telling the model to vary the telling did not vary the contract it was given. The fix had to be engine-side: the assembler now penalizes recently used lenses itself, and the worst identical-contract recurrence fell from fourteen to five.
That is the 4,000-token system prompt again, one layer down. Prompting is not a guarantee, and it is not a guarantee even when the thing you are prompting has already been stripped of every authority but style.
What the model is actually for now
So what does it do? It turns a set of assembled beats into two or three paragraphs of second-person present-tense prose. Every playthrough reads slightly different in its sentences. The outcomes are identical, because the outcomes were decided before the model saw anything.
That is pleasantly acceptable. It is also optional. The game is playable, and I would argue fully itself, with --no-render. The model is a coat of paint on a house that was already standing, and the honest accounting is that I spent more time on the paint than the paint is worth: model selection, provider routing, reasoning-token budgets, prompt versioning, telemetry, evals, fallback behavior. For a game one person will play, the flavor is not worth its own maintenance.
I did not get the thing I originally wanted. The version of this idea where the model is the game never arrived. What arrived was flavor that makes each run read differently on top of a machine that does not change its mind. I am not going to pretend that is the same as the goal. But I am also not going to pretend it is nothing. It is the fragment of the original idea that survived, and it survived because it was the only part of the job the model could actually be trusted to do.
Where a guarantee can live
I want to be careful about what this proves, because it is easy to overstate.
I don't think this means language models cannot be part of interactive fiction, or that no one should try. I think it means something narrower and more useful: a model can generate a plausible version of almost anything, but it cannot guarantee a specific one. If what you need is a specific one, a canon, an invariant, an authorial vision, then the guarantee has to live somewhere that is not the model. In Fatelock it ended up living in human authorship. Everywhere else in this blog, it lived in code.
For me, the specific thing I wanted, a game whose choices were limitless and whose world stayed straight, is on the wrong side of that line. I have stopped trying to move it. That is a statement about where guarantees live, not a verdict on the tool. The tool is very good at proposals. It is not a commitment device, and I spent months discovering that the hard way.
The AgentOps version of the lesson is blunt: you cannot leave a model in the loop to run a system like this, because the thing that made it work was every part of the system that wasn't the model. That is not a claim that the problem is unsolvable. It is a claim about where the burden ended up, and where I would put it next time.
The ledger
Fatelock is finished. The project ran from June to September 2026. Phases 0 through 4 are closed, the tree is clean, and the game runs with or without the model. The one thing I never reconciled is small and real: two validators still police the same state, and nothing says which one wins. I found that while auditing the finished thing rather than while building it.
I am not going to call the project a success. I am not going to call it a total loss, either. I built a deterministic game I don't particularly want to play, on top of a language model I no longer believe can do the job I hired it for, and I now know both of those things for certain instead of suspecting them. That is what an experiment is for. Proving something is a failure doesn't mean the experiment wasn't worth running.
Start here: The rules the model couldn't remember.