Repository navigation
Finish
This page tells how the finish slice of the core plugin claudetto works.
A user reads it to set a goal with /goal and to learn when Claude gets a note or a deny at the end of work.
A contributor reads it before an edit of slices/finish/.
The slice holds the old goal slice, the stop hook, and the box hook.
None of them makes a model call.
A goal is one check command.
The Stop hook runs the check each time Claude tries to stop.
If the check exits 0, the stop goes on and the goal is cleared.
If the check fails, the stop is blocked, and Claude gets the tail of the output.
The stop is blocked at most GOAL_BLOCKS_MAX (5) times in a row.
Then the stop goes on, a line tells you why, and the goal stays set.
A passed check clears the count.
The state is in .claudetto/goal.json of the project root.
The file holds the check command.
The hook reads the file at each stop, so the goal survives a compaction, a /clear, and a resume of the session.
/goal answers with no model turn.
A plugin skill cannot replace the built-in /goal, because the plugin name is its prefix.
So a command.run hook answers in its place.
| Text | Effect |
|---|---|
| A command | Sets the check, and starts a turn. It replaces a goal that is already set. |
status or nothing |
Shows the check. |
clear |
Clears the goal. |
A pair of backticks around the command is cut.
The hook runs the command with sh -c in the project root.
A check ends after RUN_TIMEOUT_MS (300,000 ms) and counts as failed.
The hook keeps the last OUTPUT_TAIL_CHARS (1,500) characters of the output, because the error is at the end.
The box hook runs on Edit and Write of a tasks.md.
It compares the boxes of the file before and after the call.
A box that changes from - [ ] to - [x] needs a passed check in the session record.
A task line that names check commands in backticks, such as just lab, needs one of them to have passed.
A line that names none needs any passed just check or bun test run.
Without it the hook denies the call and names the task id.
A change of the text of a task, with the box as it was, passes.
A tick that passes adds a cost line to the context: the cost of the session and the change of each plan share since the first reading.
The skill apply ends its report with these figures.
The Stop hook also blocks a stop when the turn checked a task box of a tasks.md and that file still has an open box.
The turn is the rows of the session after the last user row that holds text.
The block names the next open task and tells Claude to go on with it, or to ask you with AskUserQuestion when a blocker stops it.
The stop is blocked at most GOAL_BLOCKS_MAX (5) times in a row, with its own count.
Then the stop goes on, and a line tells you why.
A stop with no tick in the turn is never blocked, and it clears the count.
The block applies only in a session whose record shows that /claudetto:apply ran.
The record shows it as a user row with the tag <command-name>/claudetto:apply</command-name>, or as a Skill call of claudetto:apply.
A tick in another session never blocks.
The block waits while an agent of $.agent.list() is pending, running, or waiting.
A failing $.agent.list() adds no block.
A failing /goal check blocks first.
The Stop hook never blocks for a note.
It sets a note for the slice Notes in two cases:
- The last reply lists work as not done, not fixed, paused, or for later, and names no block that stopped it, such as a sandbox deny. The note names the item.
- The last reply says "ready" or "done", and a file that the session edited is not committed, or a task box that an
Editof the session ticked has no passed check. The note names the files and the boxes.
The rules match on the form of the reply.
The tests in tests/claudetto/finish/reply.test.ts hold a known-bad reply for each incident row and known-good replies that must set no note.
- The
setupsnippet setsmodelProposedGoalstodisabled, so the model proposes no goal. - The
promptslice leaves out thegoal_statusattachment.
| Hook | Filter | What it does |
|---|---|---|
classic.Stop |
None | Runs the check and blocks the stop until it exits 0, up to the cap. Blocks a stop of a turn that checked a task box while a box is open. Sets a note for a reply that defers work or says done with no proof. |
tool.call |
Edit, Write
|
The box hook for a tasks.md. |
command.run |
/goal |
Answers the command. |
A failure of the command.run hook answers Goal: <message>, so the built-in /goal never runs.
claudetto
- Start
- Slices
- Add-Ons
- Reference
- Rationale
- Evidence
- Development
Claude Code