# Making the rules unskippable

Stating a rule is not enforcing it. Every channel this skill used to rely on is
optional at the moment the code gets written:

| Channel | How it is missed |
|---|---|
| `SKILL.md` | an agent may never open it |
| MCP server instructions | some clients never surface the `instructions` field; a long-running server keeps whatever build it started with, so a rule released today reaches nobody until every client restarts |
| Tool description | only read if that tool is called |
| Code review | the reviewer is the same agent that wrote the code |

That is not a hypothetical list — it is the post-mortem of an entity that
shipped with seven restated default options on every field, a global enum name
on a purely local enum, and four bare `export class Accounts {}` stand-ins that
registered nothing. The author's agent had none of the rules in context, and
the gate that would have caught all of it was never run.

So the rules are attached to **events** instead. Install them into an
application repo with:

```
node node_modules/suppa-mcp-2/skills/suppa-entity-code/scripts/install-entity-code-guards.mjs .
```

Re-running is safe: managed regions are fenced with markers and replaced, hooks
the repo configured itself are left alone, and `--dry-run` prints the plan.

## The five layers

Each one covers a way the previous one is missed.

### 1. Before the write — `PreToolUse`

`Write`/`Edit` on a `*.entity.ts` / `*.seed.ts` path injects the rules digest
into the agent's context as `additionalContext`. It does **not** block, and it
deliberately sends no `permissionDecision`: a guard must never approve a write
the user's own permission settings would gate.

Cost of it firing: a few hundred tokens on exactly the turns that need them.

### 2. After the write — `PostToolUse`

The gate runs over the file's application root (nearest `package.json`). A
failure exits `2`, which puts the findings — with the rules attached — in front
of the agent as the next thing it reads. `PostToolUse` cannot undo the write;
this makes the violation the agent's immediate problem instead of a surprise at
review time.

If the gate cannot run (no `typescript` in the target repo), the guard says so
as context and exits `0`. A guard that walls off unrelated edits gets
uninstalled, and then it protects nothing.

### 3. Before the turn ends — `Stop`

If any schema file in the working tree is changed and its root fails the gate,
the turn is refused. This is the layer that stops "I will fix the gate findings
later" — there is no later. `stop_hook_active` is honoured, so a turn that is
already inside a Stop continuation is never blocked twice.

### 4. Before the commit — `.githooks/pre-commit`

The same gate over **staged** schema files, exit `1`. This catches the case the
hooks cannot: a human editing by hand, an agent in an editor with no hook
support, a `--dangerously-skip-permissions` run with hooks disabled.

The installer sets `core.hooksPath=.githooks` only when it is unset. If the repo
already uses husky or lefthook, it says so and leaves the setting alone — chain
the call from the existing hook:

```sh
node node_modules/suppa-mcp-2/skills/suppa-entity-code/scripts/entity-code-guard.mjs --pre-commit
```

### 5. Before the merge — CI

`.gitlab/suppa-schema-gate.yml` runs `--ci` over the repo on merge-request
pipelines. This is the copy nothing local can skip — no hooks, no
`--no-verify`, no "my agent said it was fine". If you install only one layer,
install this one.

## Agents in editors without hooks

`AGENTS.md` and `CLAUDE.md` get a managed section carrying the same rules.
Copilot, Cursor, Gemini CLI and Claude Code all read these files, and they are
in the repo, so they travel with the code rather than with a client's config.

## What the guard does, by mode

```
entity-code-guard.mjs --pre-tool-use    hook: rules into context (stdin: hook JSON)
                      --post-tool-use   hook: gate the written file, exit 2 on failure
                      --stop            hook: refuse to end the turn while a change fails
                      --pre-commit      git: refuse the commit, exit 1 on failure
                      --ci [roots…]     pipeline / human: one run, exit 1 on failure
                      --print-rules     the digest, for a doc or a human
```

Every mode exits `0` on a repo with no schema files, no git, or no compiler, and
says why. The blocking split is the CLI's own: errors always block, and so do the
authoring-rule warnings (`W607`, `W609`-`W613`, `W615`-`W617`).

## The rules themselves live in one file

`scripts/house-rules.mjs` exports `HOUSE_RULES_DIGEST`. The server instructions,
the guard's hook output, the failure report and this skill all quote it, and
`test/entityCodeHouseRules.test.ts` fails the build if a copy drifts. That test
exists because the rules were once written out by hand in five places, adding
one rule updated three of them, and the tool description went on telling agents
there were four.
