How trailstone fits a normal git workflow, with real cases

· by

An AI coding agents team workflow already has a shape: branch, let the agents work, commit, open a PR, push, CI. trailstone adds no step to it. It adds one file to the repo, .trailstone/decisions.yml, and one job: when a decision changes, the agents already working hear about it at their next edit, and a file still resting on the old decision cannot be pushed until someone re-checks it. This post walks the workflow in order, with the commands and the output we got running them, then six cases and one where you should not bother.

Setup, once

npm i -g trailstone            # or npx trailstone <command>
cd your-repo                   # run install INSIDE the repo
trailstone install
trailstone init --goal "ship the billing API"
git add .trailstone AGENTS.md && git commit -m "trailstone: ledger"
trailstone doctor

install touches a short list: four hook entries in ~/.claude/settings.json (and ~/.codex/hooks.json if you use Codex), this repo’s .git/hooks/pre-push, a line in .gitignore for the private ledger, a short block in AGENTS.md, and .cursor/hooks.json if you use Cursor. Nothing leaves your machine. A repo without .trailstone/decisions.yml is unaffected, so installing globally costs nothing on repos that never opt in. doctor tells you whether it is really watching. If a pre-push hook already exists, install prints the line to add instead of overwriting yours.

trailstone uninstall removes the hooks, the pre-push and the Cursor files. The ledger, the AGENTS.md block and the .gitignore line are yours to delete. It needs Node.js 20.17 or later, and there is no server or account. The only network call is a background git fetch of origin’s default branch, which TRAILSTONE_FETCH=0 turns off.

Branch: record a decision as part of the work

You are about to build sessions. You pick JWT in a header, so you say so, in the repo, with the files it governs:

$ trailstone decide "Sessions use JWT in an Authorization header, not cookies" \
    --why "the API is called from a CLI too" --scope src/auth/
d_e8a61a0d recorded. Commit .trailstone/decisions.yml to make it bind for everyone.
scope src/auth/ covers 2 tracked files — a reversal will flag all 2 to re-check.

Two habits make it useful. Name the alternative in the text (“X, not Y”), so a later reversal reads as a diff. And keep the scope tight: the line about the file count is there so you narrow it now, not after a reversal flags forty files. The agents can record decisions too. At the end of a turn that wrote a file and looked like it chose something, the Stop hook asks the agent once to record it as a proposal. A proposal binds nothing until you ratify it.

Commit and PR: the ledger line is in the diff

The ledger is a YAML list in the repo, so the decision lands in the same PR as the code that follows it:

- id: d_e8a61a0d
  at: 2026-09-29T20:22:57.121Z
  by: Dana
  decision: Sessions use JWT in an Authorization header, not cookies
  why: the API is called from a CLI too
  scope:
    - src/auth/

Your reviewer reads “JWT, not cookies” and its reason next to the auth code, with the normal tools: blame for who and when, review for whether it is right. There is nothing to sync, because git already does. Two branches that each add a decision produce an ordinary merge conflict in that file. Keep both entries. It is the easy kind of conflict, but it is still a manual resolve.

Agents: told before they edit

Claude Code, Codex and Cursor are told before they edit a governed file. This is what the edit hook injected in our run, before the agent’s first write to src/auth/session.ts:

# Trailstone — governing src/auth/session.ts
This file is governed by (recorded earlier, honor them):
  - Sessions use JWT in an Authorization header, not cookies [scope: src/auth/session.ts] (src/auth/)
  If what you are about to do contradicts one, say which one FIRST, and do not just comply. ...

That fires once per file per session, and again only if the rule governing the file changes, so it does not nag. Codex only runs new hooks after you trust them once in /hooks. Any MCP client can ask instead, through trailstone mcp, and agents without hooks read the block trailstone writes into AGENTS.md. Pushing the warning before the edit works in Claude Code, Codex and Cursor. Windsurf and Claude Desktop can only pull: the agent has to ask, and agents do not reliably remember to.

Changing your mind

A week in, cookies win. You never edit the old entry. You reverse it:

$ trailstone reverse d_e8a61a0d "Sessions use a signed HttpOnly cookie, not a JWT header" --why "XSS token theft"
d_5ba86a83 recorded (supersedes d_e8a61a0d). Commit .trailstone/decisions.yml to make it bind for everyone.
scope src/auth/ covers 2 tracked files — a reversal will flag all 2 to re-check.
now stale (2):
  src/auth/login.ts
  src/auth/session.ts

Those are the two tracked files in scope whose last commit is older than the reversal. Staleness is computed from git history and scope every time and never stored. The agent that already edited session.ts hears about it at its next edit:

⚠️ CHANGED WHILE YOU WORKED — a decision governing a file you already edited this session is no longer the one you were shown.
  - src/auth/session.ts: was "Sessions use JWT in an Authorization header, not cookies" → now "Sessions use a signed HttpOnly cookie, not a JWT header"

A new session opening the same file gets the stale warning plus the new rule. Then you re-check each flagged file, and there are two ways to clear it:

  • The file needs rework: change it and commit. Any commit to a flagged file clears the flag.
  • The file still holds: trailstone validate <old-id> --scope src/auth/login.ts records that you re-read it. Ours printed cleared 1 stale flag: src/auth/login.ts.

A flag means “built on the old decision”, not “broken”. In real use most flagged files still comply and clear in seconds. The flag exists to force the re-check that finds the one that does not. It flags. It does not fix code. And “any commit clears it” includes an unrelated commit, so a passing commit is not proof the reversal was addressed. The stale message says so, and the reconcile step is still on a person or an agent.

Push and CI: the guard

install writes a pre-push hook that runs trailstone stale. With one file left stale, our push looked like this:

$ git push origin main
⚠️ STALE — these files were last committed BEFORE a decision governing them was reversed. Re-validate before building on them:
  - src/auth/login.ts — was: Sessions use JWT ... → now: Sessions use a signed HttpOnly cookie ... [decision d_e8a61a0d]
  ...
error: failed to push some refs to '...'

Exit 1, push refused. After the validate above, stale printed trailstone: clean. and the push went through. For CI, the repo ships a GitHub Action that runs the same check. It compares commit dates, so it needs the full history:

- uses: actions/checkout@v4
  with: { fetch-depth: 0 }
- uses: vishwamdhavale/trailstone@v0.3.7

Blocking happens only on an exact scope match (a path, a directory or a glob), never a guess. A false flag is worse than a missed one. The push guard and the CI check work with any tool, because git does not care what wrote the code. The README has the full CLI.

Six cases, pain first

Three agents in worktrees

You run three agents on three branches. Halfway through, you decide something on main. None of them has that commit, so none of them knows. trailstone reads the ledger on main as well as the checkout’s own, so each agent hears about it at its next edit. In our worktree runs it reached 15 of 15 running agents. The setup side is in running parallel agents in git worktrees.

A teammate’s clone that has not pulled

A teammate pushes “cookies, not JWT”. Your clone, and the agent in it, are behind. A pushed CLAUDE.md edit reached 0 of 3 unpulled clones in our test. trailstone reached 3 of 3, because its hooks fetch origin’s default branch in the background. Across separate clones it reached 9 of 9. Small samples, run by us.

Claude Code, Codex and Cursor on one repo

Each tool has its own rules file and its own habits. All three are told through their own hooks, and the ledger is the one source. See CLAUDE.md vs AGENTS.md for the rules-file side.

An auth or session switch

The example above. The files that already assumed the old rule stay green and finished, and wrong. Reversal names them.

An API format change

“Timestamps are epoch ms, not ISO” lands mid-task. Record the decision with the endpoint files as scope (for example --scope src/api/events.ts). Reverse the ISO decision, and every agent about to touch those files is told. This is the case in the demo on the home page: with CLAUDE.md 0 of 3 agents switched, with trailstone 3 of 3.

Swapping a dependency

Moving from one date library to another means every file that imported the old one now rests on a superseded choice. Scope the decision to the modules that use it, not to src/. A whole-directory scope is an alarm you learn to ignore.

When not to use it

You run one agent at a time, in one checkout, and your rules never change: “use pnpm”, “tests live next to the code”. Write them in CLAUDE.md or AGENTS.md. In our tests with up to 100 such rules, a plain rules file worked about as well as trailstone and costs less. An interactive Claude Code session also noticed a CLAUDE.md edit made in its own checkout, 4 of 4 times. The gap is only where the agent’s checkout never sees the change. Two limits to know: it does not follow file renames (re-scope after a git mv), and it only watches the files a decision names, so a README describing a reversed approach is on you.

What happened on a real project

We ran trailstone on a real product repo, for two days, then on a second project. The numbers are small and ours.

  • On the first project, we reversed a decision on main while three agents worked in worktrees. All 3 of 3 learned of the reversal: two through the edit hook, one through the Stop check. None of the three implemented it. The new rule needed an entry in shared code that none of their tasks owned, so each named the conflict and handed it to the human instead of building on the old rule. That is the honest limit: trailstone carries the knowledge of a reversal to every running agent, and it does not assign the work. A reversal that lands in shared code needs an owner. Today the push guard is what finds one.
  • Real use found bugs the evals never hit, and we fixed them: a ledger with no trailing newline that a reversal appended onto, merging two entries; hooks that ignored a file in a repo other than the session’s; and three install snags.
  • On the second project, one agent built a feature over three phases, spawning sub-agents. Sub-agents start with no context, and asking what governed each file stopped one from treating a free local model as a paid-tier fallback. That is one catch in one repo. No decision was reversed mid-work there. It also showed that agents re-scope a decision by reversing it with identical text, and that edits made through shell scripts skip the edit hook. The first is fixed in 0.3.7 (reverse <id> --scope). The second was still open when we last checked.

Try it

npx trailstone demo runs the loop on a throwaway repo in about ten seconds. For the reasoning, see why trailstone exists, and architecture decision records for AI coding agents for how a superseded ADR becomes a reversal. The home page has the numbers and the demo video.