[ norboten ]
claude · lab claude-02

The Hooks That Never Fire

· about 35 minutes · runs on ubuntu-26.04-claude · unrated

An unrated lab. It runs on your machine with no account and no network, and everything about it — the faults, the checks, the hints and the reference solution — is in the repository. An attempt on it is recorded on your profile and never moves a rating: only rated labs do. Rated and unrated labs.

The briefing

The billing team lets a nightly Claude Code job, ~/bin/nightly-cleanup, tidy the code in ~/billing. Three hooks in the repository's .claude/settings.json are the guard rails everyone agreed on: Python the agent writes is re-indented with spaces (the model likes tabs), the agent never pushes, and migrations that have already run against production are never edited.

Last night the agent edited an applied migration, committed it and pushed it to origin — and the Python file it wrote is indented with tabs. Nobody changed the hooks. The job's JSON result is in ~/nightly-cleanup.log.

This lab runs in a container, as learner with sudo. claude here is the real Claude Code 2.1.270, talking to a scripted model on this machine: every run replays last night's plan, and Claude Code carries out the tool calls — and runs the hooks — for real. origin is a bare repository at /srv/git/billing.git.

What is expected, and graded — the grader runs ~/bin/nightly-cleanup itself, against models that try other things:

  1. A Python file the agent writes or edits comes out indented with spaces, not tabs.
  2. The agent cannot push, however it spells the command — and can still run git status.
  3. The agent cannot edit or overwrite a migration that already exists — and can still add a new one.
  4. The applied migration is back to what production ran, in your working tree and on origin.

Keep the hooks: the job's own permissions are not the problem to solve here.

What is graded

This lab runs in a container — Docker or Podman, no VM — so there is no boot to survive: the checks run once, against the state you left.

CheckObjective
01_written_code_gets_tidiedMake a PostToolUse hook run on the tools it is meant for, with the input Claude Code gives it
02_agents_cannot_pushMake PreToolUse hooks actually block what they are written to block
03_applied_migrations_are_protectedMake PreToolUse hooks actually block what they are written to block
04_the_applied_migration_is_restoredUndo what an agent pushed, the way a person would

Start it

  1. 2Labs
  2. claude-02select this one
  3. udownload it
  4. sstart the machine

Reading

Where the lab's hints send you, level by level, as you ask for them (h, then l opens a journal section in the TUI).

Theory for this lab

4 questions on the same topic, in the lab's Theory tab. They never affect the lab's grade. Three of them, to answer here:

Question 1

A PreToolUse hook has `"matcher": "bash"`. The model runs `git push` through the Bash tool. Does the hook run?

Question 2

A PreToolUse hook for Bash detects `git push`, prints "Blocked: agents do not push" and exits with status 1. What happens to the push?

Question 3

A PostToolUse hook for Edit|Write is `file="$1"; expand -i -t 4 "$file" ...`. The hook runs (a trace line proves it), yet no file is ever re-indented. Why?