PTENES
MODULE 2.3

🚧 Rules & Hooks — the Fences

The third foundation layer: limits. Rule is a “do not enter” sign — a strong suggestion, but not guaranteed. Hook is a locked door — deterministic, always/never. The production rule: where it hurts (PII, money, database writes), use a hook, not a soft rule.

8
Topics
~55
Minutes
Intermediate.
Level
Practical
Type
0%
0 of 0 topics read · Section 1 of 8

Detailed content

1

🪧 Rule = strong suggestion (non-deterministic)

A rule is a strong suggestion to the model—not a guarantee. It's the “do not enter” sign by the side of the road: most people respect it, but nothing physically stops anyone who decides to enter. Rules are non-deterministic: the model follows them most of the time, but can slip, especially deep into a long conversation.

🌱 New here?

Deterministic = always happens the same way, without relying on judgment. Nondeterministic = depends on the model "deciding" at the time, so it varies. A rule written in the CLAUDE.md is nondeterministic: it’s text that influences, not code that enforces.

💡 When one rule is enough

For preferences and common sense — "prefer tables to paragraphs," "always use a formal tone with clients" — the rule is perfect: cheap, flexible, and easy to adjust. The problem is trusting it where a slip-up is costly.

Why learn

Because knowing that a rule is “soft” calibrates your confidence. You stop treating every limit as unbreakable and save the heavy artillery (hooks) for what truly cannot fail. Understanding this distinction is at the heart of the Guardrails layer.

Key concepts

Rule
strong suggestion
Non-deterministic
can slip
“Do not enter” sign
the analogy
Good for preferences
where mistakes are cheap
2

🔒 Hook = deterministic (always/never)

One hook (hook) is deterministic: it always does or never lets you do it without relying on the model’s judgment. It’s the locked door — no matter how deep into the conversation the OS is, the hook fires the same way. “Never let me send an email with my birth certificate attached” becomes a hook, not a rule.

RULE — strong suggestion don’t enter can sometimes get through HOOK — deterministic locked: always/never ✗ blocked the same risky action (red arrow): the rule leaks; the hook lock

How to read: the same risky action (red arrow) hits both fences. With the rule (dashed fence), it can slip through; with the hook (solid, locked gate), it’s blocked every time. That’s why what really hurts becomes a hook.

Why learn

Because the rule/hook distinction is the OS’s most important security decision. PII, money, and database writes can’t depend on the model “remembering”—they need a mechanism that always fires. Knowing how to turn a boundary into a hook is what makes the OS safe enough for real use.

Key concepts

Hook (hook)
deterministic
Always / never
fires the same way
Locked door
the analogy
Where it hurts
PII, money, database
3

📑 Breaking up the bloated CLAUDE.md into rules/

Remember the discipline from Module 2.1 — keep the CLAUDE.md concise? When it starts to bloat with “always do X / never do Y,” it’s time to graduate these lines into a folder rules/ dedicated. The identity stays short; the rules get an organized home in rules/always.md e rules/never.md.

📗 rules/always.md

What the OS should always do.

  • ✓"Always convert amounts to CAD."
  • ✓"Always cite the policy source."

📕 rules/never.md

What the OS never should do.

  • ✗"I never mix personal expenses with business expenses."
  • ✗"I never promise anything outside the playbook."

💡 Promote means move, not duplicate

When moving a rule to rules/, remove it from the CLAUDE.md and leave only a pointer there (“see rules/”). Duplicating is the fastest way to create conflicting rules.

Why learn

Because this is how the foundation grows without turning into a mess: the identity stays lean, and the rules become an auditable set. Separating always of never also makes explicit what is “always” — a candidate for becoming a deterministic hook.

Key concepts

Promote
move to rules/
always.md
what to always do
never.md
what never to do
Move > duplicate
avoids contradictions
4

⚡ Reflex hooks: block PII on push

The most classic example of a hook is the reflex hook (reflex hook): a mechanism that triggers right away when faced with a dangerous trigger. The author’s example: if you try to upload something with PII to GitHub — Social Security number (SIN), credit card — the reflex blocks immediately and you see “action blocked.”

🌱 New here?

PII (Personal Identifiable Information) is any data that identifies you: CPF/SIN, credit card number, certificates. Push to GitHub = send your files to a remote repository in the cloud. Put the two together: a push with PII can publicly expose sensitive data—exactly what the reflex hook prevents.

🛟 Reflex, not memory

The point of the reflex is that it doesn’t depend on the model remembering. Even if the OS “forgets” the rule deep into a long session, the hook stays on guard. It’s the difference between ask care and ensure care.

Why learn

Because the most costly errors are the silent ones—a PII leak you discover only later. A reflex hook turns “I hope it doesn’t happen” into “it can’t happen.” This is the layer that makes the OS safe for real tasks involving sensitive data.

Key concepts

Reflex hook
fires immediately
PII
data that identifies you
Action blocked
immediate feedback
Reflex > memory
doesn’t depend on the model
5

⚙️ settings.json: ban write commands

The harness comes with a file, ready to use settings.json. That's where you create permission hooks: ask Claude Code to ban write commands — for example, never write to a specific database table — unless there is an explicit override. It adds these commands to a Bash blocklist, and that's it: dangerous writes are prohibited by default.

🌱 New here?

O Bash is the terminal through which the OS runs commands on your computer. The settings.json has a section permissions.deny that refuses certain commands before they run. Override = an exception you deliberately allow. That way, the default is safe, and risk exists only when you decide.

✓ Safe default

  • ✓Writing in sensitive tables: denied.
  • ✓Destructive Bash commands: on the blocklist.
  • ✓You allow access case by case, intentionally.

✗ Without the hook

  • ✗One wrong command deep in the context erases data.
  • ✗You trust that the model “won’t do it.”
  • ✗The mistake only shows up when it’s too late.

Why learn

Because the settings.json is where "never write here" stops being persuasive text and becomes a machine rule. It’s the easiest hook to install and one of the most rewarding: it protects your data from a single careless command.

Key concepts

settings.json
comes out of the box
permissions.deny
refuses commands
Ban writing
safe default
Override saved
your exception
6

🔍 PII pre-commit hook

A cousin of the reflex hook is the pre-commit hook: a checker that runs before every commit and gives it one last check. The author’s practice: whenever something is going to be sent to GitHub, the pre-commit checks twice if there's nothing specific to it—PII—in that content. If there is, the commit is blocked.

git commitfiles to send 🔍 pre-commit hook scans for PII clean ✓ has PII ✗ blocked 🐙 GitHub private backup blocked action remove the PII and try again

How to read: every commit goes through the checker (cyan). A clean file goes to GitHub (green); a file with PII is blocked (red) and returned. The backup happens — but only for what’s safe.

Why learn

Because you’ll want to version and back up the OS on GitHub (Track 5), and a backup without safeguards is a leak waiting to happen. The PII pre-commit hook lets you back up with peace of mind: it ensures sensitive data never crosses the boundary—combining with the .gitignore of what’s private.

Key concepts

Pre-commit
runs before sending
Double-check
final check
Blocks the commit
if it finds PII
+ .gitignore
double guardrail
7

🧪 Done check: worst mistake → never rule + 1 reflex

The done criteria for this layer are simple and powerful: think of the worst possible mistake your domain and make sure it’s written as a clear never-rule in rules/never.md — and that you have identified at least 1 automatic reflex (hook) for what hurts the most.

✅ The 3-step done check

1

Name the worst mistake: “what, if it happened, would I be unable to undo or defend?”.

2

Write it as a clear never-rule in rules/never.md (text, the soft fence).

3

Reinforce with 1 reflection deterministic (settings.json or pre-commit) — the locked door.

⚠️ The mistake of stopping at the rule

Writing the never-rule and thinking you're done is the trap. A rule is a suggestion; if the worst mistake is irreversible, it requires with a deterministic reflex on top. Rule + reflex = the double fence that actually holds.

Why learn

Because it’s an actionable criterion you can apply to any domain in 5 minutes. It forces the right question ("what must not fail?") and the right-sized answer (text for everything else, a hook for the irreversible). It’s exactly the done-check that the /os-coach is charged in Track 4.

Key concepts

Worst mistake
the irreversible part
Never-rule
writing and clear
1 reflection
the supporting hook
Double fence
rule + reflex
8

⚡ Practical: settings.json with write deny rules

Time to install a real locked door. Below is a .claude/settings.json copy-run: it denies Bash write/destructive commands (permission hook) and adds a PreToolUse hook that blocks database writes unless overridden. Change only the sections between <brackets>.

🎯 Objective

Leave this section with a real deterministic hook: the OS physically rejects dangerous write commands, without relying on you to “remember” the rule.

Save to .claude/settings.json — copy-run

json
{
  "permissions": {
    "deny": [
      "Bash(rm:*)",
      "Bash(git push:*)",
      "Bash(psql:*)",
      "Bash(sqlite3:*)",
      "Bash(<sua-cli> write:*)",
      "Bash(<sua-cli> delete:*)"
    ]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' | grep -qiE '(INSERT|UPDATE|DELETE|DROP|TRUNCATE).*<tabela_sensivel>' && [ \"$ALLOW_DB_WRITE\" != \"1\" ] && { echo 'Escrita no banco bloqueada (gancho). Use ALLOW_DB_WRITE=1 para override.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}

✔️ How to verify

  • 1.Ask the OS to run a git push or one rm — should be refused through the list deny.
  • 2.Ask for a command that writes to the <sensitive_table> — the PreToolUse hook must block it with the message (exit 2).
  • 3.Repeat with ALLOW_DB_WRITE=1 in the environment — it now passes (explicit override working).
  • 4.One SELECT (read) is NOT blocked—the hook targets writing only.

💡 Why this is a hook, not a rule

Nothing here depends on the model "deciding" to obey: the deny e o exit 2 from the hook is code that runs before for the command to run. It’s the locked door from Topic 2, now in a file. Illustrative example — adjust the commands and pattern to your harness and database.

Why learn

Because it turns all the theory about fences into real protection in minutes. With this file, you already have the “1 reflex” from the Topic 7 done-check—and the foundation ready for the production OSs in Track 5, where money and sensitive data make this mandatory.

Key concepts

permissions.deny
refuses commands
PreToolUse hook
runs before Bash
exit 2 = blocks
deterministic
Override
only when you want

✅ Module summary

✓
Rule = strong suggestion; hook = deterministic — “do not enter” sign vs. locked door.
✓
Promote the bloated CLAUDE.md to rules/ — always.md and never.md; move, don’t duplicate.
✓
Reflex hooks and PII pre-commit — block immediately; reflex > memory.
✓
settings.json: ban writes unless overridden — safe pattern with your exception.
✓
Done-check: worst mistake → never-rule + 1 reflex — the double fence that holds.

You’ve completed the OS foundation!

With Identity, Substrate, and Fences ready, the next track provides capacity: Skills, Tools, and Agents. → Track 3 · Technique II 🤖