A gate must separate the artifact from a description of it

The class

A gate scans text for a pattern that identifies a violation. The violation and a description of the violation share that pattern. So the gate fires on documentation, comments, commit messages, work journals, and eventually on its own rulebook.

The failure is not a bad regex. It is a missing distinction: the scanner has no model of which regions of its input are the artifact and which are prose about the artifact. Tightening the pattern moves the false positive around; only masking the non-artifact regions removes it.

Both directions cost real work:

So the mask needs an exception list of regions that look like data but are executed: ${...} interpolations, -c payloads, heredocs fed to a shell.

The seven measured instances (session 260, 2026-08-05/06)

# Gate Fired on Fix
1 kimi agent-level gate (check_kimi_agent_level.py) a context.json work-journal entry that described a violation exempt the journal; its markdown twin was already exempt
2 requirement-id format gate (check_req_format.py) a code comment naming the id it had just rejected rewrite the comment without spelling the literal id
3 RUL-026 workflow guard (workflow-default-sonnet.sh) agent() inside the script's own meta description string character-level code mask; ${...} stays code
4 test-observation guard (force-test-observation.sh) |pytest inside a quoted grep alternation — a search read as a pipeline quote mask for invocation detection only
5 test-observation guard, again the commit message fixing instance 4, which quoted the command mask heredoc bodies, except those fed to a shell
6 forbidden-doubles Stop hook (forbidden-doubles-stop.sh) _check_no_mocks / _check_mocks — the names of the checks enforcing the ban closed list of enforcement identifiers
7 forbidden-doubles Stop hook, again MOCK_IMPORT, the issue code for flagging exactly this scope the exemption to the SENTENCE, not the token

Instance 5 is the sharpest statement of the class: the gate blocked the commit that fixed the gate, because the commit message quoted the command.

Instances 6 and 7 add the cost that is easy to miss. That hook is a reminder, not a blocker, so each false fire is cheap — but it fired twice in three turns while a rule inventory was being written, i.e. precisely while the policy was being enforced. A gate that cries wolf during its own enforcement is one you learn to dismiss, which is more expensive than the noise.

Instance 7 also shows why token-level exemption is not enough: a sentence that names the mechanism then has to say what it catches, so the bare word lives in the description. Scoping the exemption to the sentence fixes it without licensing a real violation in the next one.

What to do when writing a gate

  1. Decide what the artifact region is before writing the pattern. For code: not strings, not comments. For shell: not quoted arguments, not heredoc payloads. For a repo scan: not the journal, not the docs, not the rulebook.
  2. List the executed-but-data-shaped exceptions and keep them as artifact: ${...}, -c, bash <<EOF. Each one is an escape hatch if you miss it, so each one gets its own test.
  3. Write the negative corpus first, and as large as the positive one. A gate that only ever fires is as dishonest as one that never does. Both hook test modules here follow that rule (tests/scripts/test_workflow_default_sonnet.py, tests/scripts/test_force_test_observation.py).
  4. Test the gate against its own documentation. If a gate cannot survive scanning the page that explains it, it will fire on the next person who writes about it.

Instrumented mechanisms

Related: RUL-014 (a ruling must resolve to a mechanism — and a mechanism that misfires is not yet a mechanism), RUL-024 (instance 4's fix exposed a pre-existing hole in the same regex; it was closed in the same change, not noted).