Module 3: Closing the loop · Lesson 6 of 7 · 3 min read
When the agent satisfies the measure, not the goal
Spot the change that passes your check and misses the point, anchor checks outside themselves, and order the loop so it never hands out blocked work.
Correct arithmetic, worthless result
The agent harness playbook gives the purest example. A check that a study plan’s steps add up to its declared budget can be satisfied by declaring the budget to be thirty minutes. Nothing is lying — the arithmetic is right and the plan is worthless. The fix is an external anchor: a plan named “three months” must declare a budget in a band that three months could mean. This site holds its own study plans to that rule. Finding the anchor is the real design work, and every internal-consistency check has this shape.
The same failure inside a store
The Store doc describes a single
source of truth with clear rules for updates, and the state-store skill turns
that into three principles: one store, state is read-only, pure reducer updates.
The store-shape guardrail question encodes them as rules — and rule 4 is the
instructive one. Every _REQUEST action needs a _SUCCESS and a _FAIL, and
the reducer must handle all three. An agent satisfies it with
case FOO_FAIL: return state;. The rule passes, the error is swallowed, and the
UI hangs on a spinner forever. The rule measured the presence of a case label,
not the handling of an error.
The anchor is behaviour, not text. Rule 3 shows how: reducer purity can be
proved by running the reducer against deep-frozen state, where a mutation
throws a TypeError in strict mode. Tighten rule 4 the same way — run the
_FAIL branch and assert something observable changed:
const next = reducer(loadingState, { type: "FOO_FAIL", payload: "offline" });
if (next === loadingState || next.isLoading !== false) {
throw new Error("FOO_FAIL must end loading and record the error");
}
The heuristic rules — no entity under two keys, no .filter( over store data in
a component — have false positives. The question’s warning applies: a false
positive the agent cannot fix correctly is one it will fix incorrectly, and
the diff shows you how much the rule was really measuring.
Give the loop a dependency order
The second source of gamed checks is the loop itself. Weighted priorities alone hand the agent work it cannot do: a study plan is worth more than a quiz question, so a naive loop asks for the plan, and the agent, unable to fill it honestly, produces a plan that satisfies the letter of the check. Declare that plans depend on a populated question bank and the loop asks for questions instead. In the playbook’s words, most of what looks like model dishonesty is a loop handing out blocked work.
Read the diff for it
After any round that turned a check green, ask:
- Did the behaviour the rule stands for change, or only the text it matches?
- Did the agent edit a test, the checker, a threshold or a baseline?
- Did it move code to dodge a count?
And put the exit in the prompt:
If a rule cannot be satisfied without changing what it checks,
stop and tell me which rule and why. Do not work around it.
Encode it, then practise it
SKILL.md that encodes Store for a coding agent. Install it into your agent, or download it for any Agent-Skills-compatible runtime.
npx skills add grvpanchal/elegant --skill state-store
Practise it
Guardrail a store's shape
Encode "this store is well shaped" as a script, then discover which of your rules an agent can satisfy without doing what you meant.
Course contents (7 lessons)
Module 1: What a skill is
- A skill, a guardrail and a loop done
- Anatomy of a SKILL.md done
- Install a skill and let paths activate it done
Module 2: Guardrails before skills
Module 3: Closing the loop