Edit-safety hooks
Agents edit code and say “done”. The hooks make Tessera check each edit as it happens and hold the agent at the end of its turn until the breakage it caused is fixed. They never call a model, and everything stays on your machine.
Set up
Section titled “Set up”tessera init # detects your agents, shows the diff, asksFor Claude Code there is also a plugin, which needs no files in the project:
/plugin marketplace add iamsaquib8/tessera/plugin install tessera@tesseraUse the plugin or init for Claude Code, not both. If both are wired,
Tessera checks each event once and gives both runs the same answer.
Cursor and Copilot CLI also run the hooks in .claude/settings.local.json,
so with Claude Code and one of them set up, each of their events reaches
Tessera twice. Tessera tells the client from the event’s fields (not from
the hook file it came through), answers in that client’s shape, checks the
event once and repeats the answer to the second run, and counts one end of
turn once.
What happens
Section titled “What happens”| Moment | What Tessera does | When it stops the agent |
|---|---|---|
| Session start | Records what was already changed, and refreshes the index in the background | Never |
| Before an edit | If the edit touches a function with callers in other files, adds a short note (at most 600 characters, about 150 tokens): the callers, and the tests that reach it. Once per function per session | Never |
| After an edit | Re-indexes the edited files and runs verify over every file edited so far: calls to names that do not exist, wrong argument counts, broken imports, call sites a rename left behind |
Only when a problem is still there after an edit that fixed nothing; a rename done over several edits is never interrupted |
| End of turn | Runs verify over everything changed since the session started |
While high-confidence problems remain, at most three times per turn; problems the agent stops with after that never block it again in the session |
Only high-confidence findings in TypeScript, JavaScript and Java block.
Findings in other languages are not shown by the hooks; run
tessera verify --changed to see them.
Only problems the agent introduced block. Before the index learns of the
agent’s first edit, Tessera notes every problem it already holds; a finding
that was there then (the same check, file and message, wherever its line
moved) never stops the agent, even in a file it edits. When the agent adds
another call site of the same problem, the problem counts as its own again.
Files that were already changed before the session started are not checked
at the end of a turn unless the agent changes them again. tessera verify --changed and --diff (for CI) still report everything.
The end-of-turn check answers within 40 seconds (its hook is given 60), however large the change: every problem is reported, and on a very large change the list of tests that reach it may be cut short.
A problem in Tessera itself never stops the agent: if the index is missing or busy, or anything fails, the hook prints nothing (or the agent’s no-op answer) and exits 0.
Agents
Section titled “Agents”| Agent | Before an edit | After an edit | End of turn | Wired by |
|---|---|---|---|---|
| Claude Code (also with DeepSeek) | note | report, then block | block | plugin, or .claude/settings.local.json |
| Codex CLI | note | report, then block | block | .codex/hooks.json (approve with /hooks) |
| Qwen Code | – | report, then block | block | .qwen/settings.json |
| Gemini CLI | – | report | retry | .gemini/settings.json |
| GitHub Copilot CLI | – | report | block | .github/hooks/tessera.json |
| Cursor | – | report | follow-up message | .cursor/hooks.json |
| Augment (Auggie CLI) | – | report | block | .augment/settings.local.json, which runs the scripts init writes in .augment/hooks/ |
| Google Antigravity | – | – | continue with the problems | .agents/hooks.json |
| Everything else | – | – | – | tessera init --git-hook |
A dash means the agent’s documented hook contract has no way to say it: Qwen’s and Antigravity’s pre-tool hooks would have to approve the edit, Copilot’s can only allow or deny, and Antigravity’s post-tool hook cannot return feedback. The sources are in agents.md.
A turn that ends in an error, an interruption or a step limit is never held;
Tessera checks only turns the agent finished. Antigravity always gets a
decision at the end of a turn (continue with the problems, otherwise
stop), as its contract requires.
The git backstop
Section titled “The git backstop”tessera init --git-hookinstalls a pre-commit hook that runs tessera verify --staged. A commit
whose changes break a call site or an import is refused, whichever agent (or
person) made it. git commit --no-verify skips it once. An existing
pre-commit hook is never replaced; init prints the line to add instead.
verify --staged checks the staged files as they are in the working tree:
when only part of a file’s changes is staged, the unstaged part is checked
too.
What the hooks did: tessera stats
Section titled “What the hooks did: tessera stats”$ tessera statsLast 30 days: 14 session(s) (claude 212, cursor 31) 96 edits checked, 40 pre-edit notes 11 reports, 3 post-edit blocks, 5 stop blocks 17 problems fixed by the agent, 1 released at the stop limit, 0 still open post-edit hook: p50 64 ms, p95 180 ms over 96 runs ...Each hook run appends one line to .tessera/events.jsonl: the event, the
kind of answer (none, note, report, block), how many problems the agent had
open and how many the edit fixed, and how long the hook took. It holds no
paths, names, messages or code, it is never sent anywhere, and the index
directory’s .gitignore keeps it out of commits. --days N changes the
window; --json gives the same numbers to scripts. The oldest half of the
log is dropped past 4 MB.
Use it to judge the hooks on your own code: how often a block led to a fix,
how often a problem was released at the stop limit (worth a look: a false
positive or an intended break, which tessera accept records), and whether
the hooks stay fast.
Session files and turning it off
Section titled “Session files and turning it off”Each session’s state is in .tessera/sessions/<id>.json, next to the index
(git-ignored): the files edited, the notes already shown, the problems still
open, and counts of notes, reports and blocks. Files untouched for a week are
removed.
To turn the hooks off, remove Tessera’s entries from the hook file in the
table above (for Augment, also the tessera-*.sh scripts in .augment/hooks/), or
uninstall the plugin. tessera init only ever adds entries,
so your own hooks are untouched either way.