Skip to content

tutorial

Chapter 1 of 4

Chapter 1 — The Safety Net

by Rod Rivera Published

Install the starter pack, meet the lint, and wire up the git hook — with a plain-English explanation of what those words even mean.

We are going to set up the safety net before building anything. This is deliberate. A net installed after the fall is a decoration.

Get the pack

git clone https://github.com/RasaHQ/rasa-community-resources.git
mkdir juniper && cd juniper
git init

cp -R ../rasa-community-resources/starter-pack/CLAUDE.md .
cp -R ../rasa-community-resources/starter-pack/.claude .
cp -R ../rasa-community-resources/starter-pack/scripts .
cp -R ../rasa-community-resources/starter-pack/hooks .

Four things just landed in your empty project:

WhatJob
CLAUDE.mdThe rulebook Claude Code reads automatically — version doctrine plus the five silent traps, so the assistant can’t “helpfully” reproduce them
.claude/Six skills (build playbooks) and two roles (a builder, a reviewer). Browse them on GitHub — they are readable Markdown, useful to humans too
scripts/lint_mantle.pyThe lint. One file, standard library only, no installation needed
hooks/The git hook and its installer

.claude starts with a dot, so your file browser hides it. ls -a proves it’s there.

Meet the lint

A linter is a program that reads your files and points at known mistakes — named after picking lint off clothing. This one has nine checks, and it has a property worth pausing on: every check corresponds to a failure a real, shipped Mantle project actually experienced. Not hypothetical style rules. Scar tissue, mechanized.

Run it right now, in your nearly-empty project:

python3 scripts/lint_mantle.py
[engine-version-pin] pyproject.toml: missing pyproject.toml
[secret-hygiene] .gitignore: '.env' is not gitignored — one 'git add -A'
    away from publishing credentials
[env-example] .env.example: missing .env.example — a new user has no way
    to discover which credentials this project needs

FAILED — 3 finding(s) across 9 check(s)

This failure is the success. Three findings on an empty project means the checker is alive, and — read them again — each finding tells you what’s wrong, why it matters, and what to do. That is the contract every message in this lint keeps. You never get a bare “error 47”.

You can also run one check at a time, or list them all:

python3 scripts/lint_mantle.py --list
python3 scripts/lint_mantle.py --check secret-hygiene

If you’re curious how any check works, the answer is one click away: each check in lint_mantle.py is a short function whose docstring tells the story of the failure it encodes. The file is meant to be read.

Wire the hook

A git hook is a small program git runs automatically at a key moment. The one we install runs at pre-commit — the instant before git saves a snapshot of your work:

./hooks/install-hooks.sh
Installed: .git/hooks/pre-commit
Every commit now runs: python3 scripts/lint_mantle.py

From now on, committing a known trap gets you this instead of a saved bug:

COMMIT BLOCKED by lint_mantle.py (exit 1).
Each finding above names its fix. The checks encode real, silent
failures — fixing them now is cheaper than debugging them at train time.

Two things to know about the guard, because trust requires understanding:

  1. It is not a cage. git commit --no-verify bypasses it. The etiquette: if you bypass, say why in the commit message. The lint only complains about mistakes that are silent at runtime, so a bypass usually means shipping a bug you won’t meet again for weeks — make it a decision, not an accident.
  2. It refuses to run blind. If scripts/lint_mantle.py goes missing, the hook blocks the commit rather than waving it through — a safety net with a hole in it is worse than none, because you stop looking down. (You can read this logic in hooks/pre-commit — it’s 25 lines.)

The installer is also polite: if you already had a pre-commit hook from something else, it refuses to overwrite it and tells you to merge them yourself.

Check your understanding

Before Chapter 2, you should be able to answer:

  • Why did the lint fail on an empty project, and why was that good news?
  • What moment does a pre-commit hook run at, and what does ours do there?
  • Where would you look to understand what the secret-hygiene check actually scans for? (Answer: its function in lint_mantle.py — the docstrings are the documentation.)

Net installed. Time to build something worth catching.