Learning AI-DLC building a game, part 12: guards and hooks

Part 12 of the AI-DLC game series: AI-DLC's guards in this project; four fences lowered by the Guard Policy stood aside 984 times, while the presence check and the integrity check stayed up and refused eight actions recorded in the build log

This is part 12 of a series on learning AI-DLC by building a game, World Rift Conflict, in Godot with an AWS backend. Part 1 set it up, and part 11 looked back at the first full run. This one is a deep-dive: the guards, the part of AI-DLC that can say no.

Parts so far:

TL;DR

  1. AI-DLC runs as Claude Code hooks: 16 of them on 8 events. Most only record. One checks every tool call against the guards.
  2. The guards come in two kinds. Four are fences that the Guard Policy can lower. Two stay up whatever the policy says: the human-presence check at the approval gates and the integrity check on AI-DLC's own records.
  3. Every piece of work in this project ran with Guard Policy off, the default of each scope I used. The fences stood aside 984 times, and the audit has one row for each.
  4. The refusals I actually hit came from what stays up: eight in my build log. The integrity check matches words, so a GitHub comment that said "audit" was refused.
  5. No agent worked around a refusal. Twice the refusal message itself named the way out.

Hooks: where AI-DLC runs

AI-DLC has no daemon. It is a set of Claude Code hooks, commands that Claude Code runs at fixed moments. In this project's .claude/settings.json there are 16, on 8 events:

EventWhat AI-DLC does there
Before a tool callchecks the guards, hands the stage rules to a helper agent, folds usage
After a tool callwrites the audit row, fires the sensors, syncs the state, rebuilds the stage graph
When I send a messagerecords that a human took a turn
Session start and endloads where the work stands; closes the session record
Before compactionchecks the state file and leaves a recovery note
When a helper agent stopslogs the agent
When a turn endskeeps the workflow going until the engine says it is done

Most of these only watch. Across the six pieces of work in this project, the audit holds 2,035 rows for artifacts written, 419 sensor firings and 675 human turns. The guards are the hooks that can stop something, and they run before the tool call, so a refused action never happens.

Two kinds of no

The first kind is the fences. Each enforces one ordering rule that the stage instructions also state in words. The hook source says why each was added: the prose lost. One comment reads, "a conductor generated the code first and backfilled the plan".

FenceWhat it stops
Plan approvalcode written before I approve the plan in Code Generation
Review freezeedits to a reviewed document after the review receipt, which would void the review
Reviewer read scopea reviewer of one unit reading other units' files
State transitiona helper agent running the workflow's lifecycle and routing commands

The second kind stays up. The human-presence check refuses an approval unless I sent a message since the gate was shown. That is what the 675 human turns are for: an agent cannot approve its own gate. The integrity check protects AI-DLC's own records: the audit trail, the plan approval record, the hooks and the runtime. Its source is plain about it: "no Guard Policy word, lowered fence, or presence bypass turns it off."

Guard Policy off: 984 stand-asides

Each scope sets a default Guard Policy. poc, bugfix, mvp and classic, the four scopes this project used, all say guard_policy: off, and I never changed it. The mvp scope file says what that means: "plan approval, review freeze, state transition, and reviewer read scope are lowered for undirected work. Human presence stays up."

A lowered fence does not go silent. When its rule finds an action it would have stopped, it stands aside: the action goes ahead, the person gets one line, and the audit gets a GUARD_STOOD_ASIDE row. The decision table in the engine says it this way: "Never 'are you sure': the switch is already off."

Counted from the audit of all six pieces of work:

FenceStood aside
Plan approval709
Review freeze165
Reviewer read scope108
State transition2
Total984
  • 778 were in Code Generation. The rest were in the design stages, where the review freeze and the reviewer scope apply.
  • 640 were actions by helper agents, mostly the developer agent editing code and the reviewers reading. Those lines went to the main session, not to me, so I saw very few of them.
  • The mvp run alone had 712.

I did not check the 984 rows one by one, so I cannot say how many were harmless. What I can say is what they mean. With the policy off, the fences became a record, not a stop. If I want a fence to hold, I ask for it, for one piece of work or for the project. For example, "turn the review freeze check back on".

What still said no

The audit has no row for a refusal that I could find, so this list comes from my build log and may be incomplete. Each one came from a check that stays up, or from a fence whose rule had nothing to stand aside for, because no approval existed yet.

  1. Deleting .claude after the install (2026-10-25). "AIDLC runtime records and hooks belong to the harness … use the official updater or an external terminal."
  2. The part 1 draft (2026-10-26). A whole-file write was refused. The text described AI-DLC's hooks and records, the likely match, but the exact rule was not identified. The part 2 draft, written after that workflow ended, went through.
  3. Two shell commands that said "audit" (2026-10-27): a GitHub issue comment and a build-log append, both in a shell heredoc. "The audit trail under aidlc/spaces/<space>/intents/<intent>/audit/ is written only by the framework's own tools…" Neither one touched the audit trail.
  4. A reviewer who could not run the tests (2026-10-28). A hook blocked its shell after the first command, "because the recovery question is open". The current message for this case reads: "Code changes wait while AI-DLC's recovery question is open."
  5. Reading code before the plan existed (2026-10-30). In Code Generation, until the plan file was written, even cat and grep were refused: "Code generation cannot run mutation-capable shell commands for unit u2-accounts-profile because … code-generation-plan.md is missing or empty". The guard cannot tell a harmless shell command from one that writes, so it refuses them all. The file tool still worked.
  6. An erased approval (2026-10-31). The agent rewrote the questions file whole, which erased the engine's Plan Approval section with it. Then every shell command was refused: "code-generation-questions.md has no Plan Approval section". The message named the way back: print the same approval fingerprint, write the section back, and ask me the plan question again. I approved it again.
  7. A log file named after an audit (2026-11-05). The agent could not run the pre-push script, because a new log file was called pre-push-check-audit.log. A PR description passed in a heredoc was refused the same day, because it said "audits".
  8. Three edits to scripts/pre-push.sh (2026-11-06). Refused with the harness-records message. The rule that matched was not identified. The agent asked me, and I made the edit by hand.

A check that reads words

Numbers 3 and 7 have the same cause, and number 8 may too: scripts/pre-push.sh mentions "audit" three times, though its refusal showed a different message. The integrity check reads the command's text, not what the command will do. A GitHub comment about an audit looks, to it, like a write to the audit trail.

The source says so itself:

Defense in depth for harness-owned runtime records and hooks, not a sandbox. Hooks and tool calls run as the same user, so an agent with unrestricted execution can always find a path around a lexical check. The outer boundary is the harness's permission model and the person's review of what the agent runs.

So the check is a tripwire, not a wall, and it trips on innocent text. The fix each time was simple: write the text to a file and pass the file, as in gh pr create --body-file, or use the editor tool instead of a heredoc. What mattered more is that no agent looked for a way around. In number 8, the agent stopped and asked me, which is what a tripwire is for.

What I would tell myself

  • Know your Guard Policy. It comes from the scope. With off, the four fences only record. That suited a solo project where I read every gate. A team project may want strict.
  • The useful refusals came with a remedy. The plan guard's message named the exact commands to recover the approval. A refusal that says what to do next costs minutes. The ones that only say no cost a search.
  • Keep the word "audit" out of shell commands. Put long text in a file. That one habit would have avoided two of the eight refusals here.
  • Read the audit, not your memory. I would have guessed a few dozen stand-asides, not 984.

What is next

The UI work from part 11 is still in progress: six units are built, including a layout that fits the window, a world map, flags and a new proposals panel, but none of it is released yet. The next milestone post comes when that piece of work closes. Until then, more deep-dives like this one, from the same records.