# Finish the Thread
A forced checkpoint in front of starting something new.
Fast models removed the cost of starting. Starting used to ration itself, because the first hundred lines were real work. Now they are free, so the only thing gating a new idea is attention, and attention loses that fight. The result is not more output, it is more open threads and less finished work.
This does not stop you from starting things. It makes abandonment a decision you have to say out loud instead of one that happens silently.
## How it works
Three pieces, because a checkpoint the model can forget to run is not a checkpoint.
| Piece | What it does |
|---|---|
| `hooks/finish-the-thread.sh` | A `UserPromptSubmit` hook. Runs on every prompt, outside the model's discretion. Fires on two things: a prompt that looks like a new build thread, and a prompt that sounds like something just got finished. Both inject the open items into context. |
| `skill/SKILL.md` | The protocol. Surface the open list, ask resume / park / override, wait for an answer, then write the ledger. |
| `commands/ledger.md` | `/ledger` for reading and editing the table by hand. |
The ledger lives at `~/.claude/finish-the-thread/ledger.md`, outside this repo, because it is data rather than config.
```markdown
| Started | Item | Status | Note |
|---|---|---|---|
| 2026-07-19 | Newsletter manager app | open | |
| 2026-06-02 | Voxel exporter rewrite | parked | got bored once the mesh path worked |
```
Status is `open`, `parked`, or `done`. Parking requires a one-line reason. That requirement is the actual intervention, everything else is bookkeeping. Writing "got bored" next to two weeks of work is the part that changes behaviour.
## Install
```sh
./scripts/install.sh
```
Symlinks into `~/.claude` so this repo stays the source of truth, and registers the hook in `~/.claude/settings.json`. Global rather than per-project on purpose: the moment worth catching is a brand-new folder, which by definition has no project config in it.
`--copy` installs copies instead of symlinks. `--uninstall` reverses everything and leaves the ledger alone.
Requires `jq`, and macOS or GNU `date`.
## Usage
```
/ledger print the table plus a status count
/ledger open only open rows, with how long each has been open
/ledger add Newsletter manager log a new thread
/ledger done Newsletter manager mark it finished
/ledger park Voxel exporter: got bored after the mesh path worked
/ledger reopen Voxel exporter
```
Seed it once with whatever you already have half-built. An empty ledger means the hook stays silent.
## The two triggers
**Starting.** Matches "let's build", "new project", "side project", "from scratch", and verbs paired with project-shaped nouns. Injects the open list and holds the checkpoint before any building work happens.
**Finishing.** Matches "shipped it", "that's done", "works now", "wrapped up". Injects the open list and asks whether to close a row.
The second one exists because the first was enforced and closing a thread was not. Left alone, the ledger only ever grows in the `open` direction, until it claims five things are unfinished when two of them shipped weeks ago. A ledger that lies is one you learn to click past, and it fails quietly rather than loudly, which is worse.
Both matchers are biased toward firing. A false positive costs a few lines of context the model is explicitly told it may ignore. A false negative costs the whole intervention. The finishing matcher additionally skips anything ending in a question mark, so "is it done yet" and "is it working now?" stay silent.
Edit the `match_new_thread` and `match_completion` functions in `hooks/finish-the-thread.sh` to adjust. If you symlinked, changes are live in the next session.
## What it deliberately does not do
No time estimates, no prioritisation scoring, no nagging about items already open, no commentary on focus or discipline. Override is always allowed and is never argued with. Surfacing the list is the entire intervention.