Why the layer exists
The game is too big for an agent to keep in its context: 64 scene files, 185 quest assets and code in dozens of folders. It is also easy to break quietly. A quest's file name is both its lookup key and its save key, so renaming a shipped quest breaks saves. Two building definitions that share an id bring a saved piece back as the wrong object.
So I built a layer that gives Claude Code checked facts instead of guesses. It lives in the repository's ai/ folder, in three parts.
- Knowledge, in
ai/ai-docs: one doc for each code folder (31 of them), each pinned to the commit it was last checked against, plus the Unity-MCP operator guide and dated plans. - Build areas, in
ai/ai-work: tools for quests, dialogue, shops, saving and code conventions. - The gate and its tools, in
ai/tools/general: the verify gate, and the small tools an agent calls before it touches anything.
That is about 12.9k lines of Python, all of it standard library only, counted over the tracked .py files under ai/ with blank lines and comments included. Almost all of it is in my commits, and Claude Code co-authored most of them. The docs are another matter: both of us update them as the code changes, and the gate is what keeps them honest.
The verify gate
The gate is one command that runs 16 checks and prints one line for each. It is deterministic and needs no network, no clock and no Unity editor. One line per check is deliberate: every line a tool prints stays in the agent's context, so a report has a budget of 30 lines, and a test asserts it.
# the gate: 16 checks, one line each bash ai/tools/general/verify-gate.sh # before touching a file: its doc, the rules that name it, what is deferred python ai/tools/general/route.py <path> # one rule, or one section of a big doc, not the whole file python ai/tools/general/rules.py --rule R23 python ai/tools/general/docsection.py <doc> --toc # which mode applies: prints MCP-LIVE or FILE-ONLY bash ai/ai-docs/tools/mcp-quest-systems/check-mcp.sh # every quest, checked; one quest with its ids resolved; a new quest as a dry run python ai/ai-work/quest-system/tools/quest-lint/quest_lint.py python ai/ai-work/quest-system/tools/quest-inspect/quest_inspect.py "<quest name>" python ai/ai-work/quest-system/tools/quest-scaffold/quest_scaffold.py "<quest name>" --area "<area>" --objective ref:desc:manual --reward <item>:<count>
What the 16 checks cover:
- The tools' own tests, and the self-test of every tool in the build areas.
- Links. Every link and path in the docs resolves on disk.
- C# claims. The classes, enum members, methods and fields that the docs name exist in the code, and a
file:linecitation still points at the right text. A true claim at a stale line is reported as drift, not as a lie. - Content lints for quests, dialogue, shops and code conventions.
- Save guards. A rename that would let an old save load and silently drop its state, a component that should be saved but has no save id on its object, and two building definitions with the same id.
- Doc upkeep. The indexes are regenerated, each doc's pinned commit is checked against the code it covers, and docs have a size budget.
The ratchet
The gate does not demand a clean project. It keeps a reviewed baseline of known findings: a check passes when everything it finds is on its baseline, and anything new fails. So the gate can be green on a project with known problems and still catch every new one.
The baseline only shrinks, because a fixed bug has its entry removed. The code-convention, dialogue and shop lints have empty baselines, and the quest lint has one entry left, a quest that points at an item that does not exist yet. The gate also fails closed: if a check's output cannot be read, that check counts as failed.
What it cannot see
The staleness check reads committed history only, so a run before the commit cannot see a doc go stale. After a change to code that a doc covers, I run the gate again once it is committed. That rule was paid for on 2026-09-24.
Hooks
Four Claude Code hooks run in this project. They are cost control, not security: they guard against an agent reading a huge scene file by accident and paying for it in context.
- Read guard, before a Read. A Read with no line range of a heavy asset, such as a large scene or prefab, or of any very large file, is refused, and the message names a better route: a
git grep, an asset parser, a ranged read. It ran in warn mode first and has refused for real since 2026-10-09. - Bash guard, before a shell command. It warns about searches that walk past the project's ignore file, such as
grep -rfrom the project root. The command still runs. - Context guard, on each prompt. When a session's context passes 150k tokens, and every 100k after that, it tells the model to run
/checkpointand then/clear. - Session brief, at the start of a session. It prints what was being done, what comes next and whether Unity-MCP is reachable.
/checkpoint is the project's skill for ending a task safely. It writes the day's log and the resume notes, runs the gate and proposes a commit, so that /clear loses nothing. Every hook fails open: if one throws, the call goes through, because a hook must never break a session. The read guard is the only one that can refuse anything, and only in the cases above.
Did it help?
I measured it on the sessions of the person who works the art branch, not on my own: 76 sessions with API calls. Twenty started between 2026-09-10 and 2026-09-23, before the context guard and /checkpoint landed on 2026-09-25, and 56 ran between 2026-09-25 and 2026-10-11. Before, a call carried on average 419,791 tokens read from cache. After, it carried 188,720. The share of calls made with more than 150k tokens of context fell from 87% to 60%. Of the 50 later sessions that passed 150k, 45 ran /checkpoint afterwards.
That is two periods, not a controlled test, and I do not claim the guard alone did it.
Quest tools and Unity-MCP
Unity-MCP is a package by CoplayDev that runs a server inside the Unity editor and gives an agent tools to drive it: create objects, edit assets and scripts, read the console, press play, take a screenshot. I did not write it. I documented it against the live editor on 2026-06-27, when it exposed 43 tools.
The editor is usually closed, so the quest tools run in two modes.
- FILE-ONLY, with Unity closed. Every quest is a plain YAML asset, and the mistakes that cost the most time can be found on disk.
- MCP-LIVE, with the editor open. It adds what only the engine can do: compile after a script edit and read the console, play-test, drive a quest to completion and dump the state at runtime.
A preflight script prints which mode applies. Without the editor, a tool must skip the live steps and say so: it reports SKIPPED-UNVERIFIED(no-mcp) with the exact command that would verify them later. It never drops a step silently.
The seven quest tools read the YAML files, so they work in FILE-ONLY mode. The one exception is the --verify-mcp option of quest-lint, covered next.
quest-lintchecks every quest for the mistakes that cost time, such as a mistyped condition name, which makes an objective complete at once.quest-inspectdumps one quest with its item ids resolved to names,guid-resolveturns an asset name into its GUID and back, andquest-whogivesfinds the NPC who gives or completes a quest.quest-scaffoldwrites a new quest with a freshly minted, unique id, andquest-rename-migraterenames a quest or an objective reference and carries a note about saves. Both are dry runs unless asked to apply.quest-controlis one declarative front for the rest. Itswirestep always hands over to a person: linking a dialogue line to a quest is a UnityEvent listener, which neither editing the YAML nor MCP can author reliably.
Two perspectives that must agree
A parser written for the project is only as right as its reading of Unity's files, and Unity's own runtime is the ground truth. mcp-bridge is a small client that lets a FILE-ONLY tool ask the live editor. quest-lint --verify-mcp runs the normal lint and then asks the game's own code about each error, for example whether an item id resolves to an item. Each finding comes back confirmed, refuted or unverifiable, and a refuted one prints as a possible tool bug.
On 2026-07-06 all four of the linter's errors were confirmed and none was refuted. On the same day a quest scaffolded offline was accepted by Unity unchanged and found by the game's own quest lookup.
The agent checks its work by looking
The agent works in a throwaway sandbox scene, and only there: the project's rules forbid creating or changing objects in any other scene. After every script edit it waits for compilation and reads the console, and before it writes C# against a Unity API it checks the API by reflection, because a model's memory of Unity's APIs is often wrong.
The first run created a capsule, a cube and a sphere through MCP calls, took a game-view screenshot and read the PNG back to confirm they were there.

The second run created a material, set its colour, assigned it to the cube and confirmed the result with a screenshot.

What it does not do
- The game has no Unity test cases. The editor's EditMode and PlayMode suites ran on 2026-07-06 with zero test cases, and I found no test attributes in the project's own code on 2026-10-27. The gate checks content, docs and conventions, not gameplay.
- The gate is run, not enforced. No CI and no git hook runs it.
- A shared home for verified tools,
ai/tools/unity/, is planned, not built. That folder does not exist yet. - Some save edits need the editor. Without it the save tools refuse and print the command to run later.
- The token measurement is thin. It is one person's sessions, in two periods.
How I built it
I built this layer as AI-orchestrated engineering, measured and reviewed. A tool starts as a SPEC in its folder, before any code is written. Every factual claim in a spec or a doc carries a tag for where it came from: seen in code, seen in a run, seen in the live editor, taken from a doc, or assumed. Each kind of change names the rung of a six-step ladder that it must reach, from reading the code to play-testing it in the editor, and runs that rung. Claude Code co-authored most of the commits that touched ai/.
The review was done by agents too, read-only, with the gate as the referee. On 2026-08-02 six reviewer agents went through the branch that reorganised the docs, each reading the source itself, and spot-checked about 947 claims. They found six claims the code contradicts, all in peripheral details, three stale facts (four places) and 14 imprecisions, and nearly every file-and-line citation was exact. The six, the stale facts and the imprecisions the review itemised were corrected the same day, each re-checked against the source first, and the gate was run again and passed. Minor nits were left open.