dtrailer
v1.0.0
Published
A tiny, offline, zero-account CLI for the Decision Trailer convention: a `Decision: PREFIX-N` git commit trailer plus structured decision records in .dtrailer/decisions/.
Maintainers
Readme
dtrailer
dtrailer is a tiny, offline CLI for keeping why next to what in a git
repo. It implements the Decision Trailer convention: a Decision: PREFIX-N
git commit trailer, backed by structured, lifecycle-tracked decision records
under .dtrailer/decisions/. No account, no server, no network calls — it
just reads and writes files in your working tree.
The primary use case is an AI coding agent (or a human) recording the
reasoning behind a change as it happens, so that reasoning is addressable by
ID, greppable, and citable directly in the commit that acted on it — instead
of evaporating at the end of the conversation. It works in any repo, with
zero setup beyond dtrailer init.
Quickstart
npx dtrailer init --prefix DEC--prefix is required and yours to choose — it's just the ID prefix for
this repo's decisions (DEC-1, DEC-2, ...). There's no default and no
brand attached to it.
npx dtrailer decision new \
--title "Use SQLite for local storage" \
--reasoning "Simpler operationally for a single-writer local tool than running Postgres."DEC-1
Decision: DEC-1The second line is the exact commit trailer to paste into your commit message:
Switch local storage to SQLite
Decision: DEC-1dtrailer never touches git itself — it only mints the ID and hands you the
trailer string. Committing is on you (or your agent).
Commands
All commands are non-interactive and scriptable. Add --json where
supported for structured output an agent can parse directly.
| Command | Description |
|---|---|
| dtrailer init --prefix XXX | Initialize .dtrailer/ in the current repo. |
| dtrailer decision new --title <t> --reasoning <r> [--supersedes ID] | Create a new proposed decision. Prints the ID and its commit trailer. |
| dtrailer decision accept <ID> | Transition a decision proposed -> accepted. |
| dtrailer decision supersede <ID> --title <t> --reasoning <r> | Create a new decision that replaces <ID>, linking both records. |
| dtrailer decision update <ID> --reasoning <r> | Update the reasoning of a proposed decision. Blocked once accepted or superseded. |
| dtrailer decision list [--status proposed\|accepted\|superseded] | List decisions. |
| dtrailer decision show <ID> | Show one decision record. |
| dtrailer decision search "<keyword>" | Search titles and reasoning across all decisions. |
| dtrailer inbox add "<note>" | Append a dated, unnumbered note to .dtrailer/inbox.md — no ID minted. |
| dtrailer fix-ids | Detect and renumber duplicate IDs from offline merge collisions. Idempotent. |
| dtrailer validate [--trailers <file>] | Lint all decision records; optionally check Decision: trailers in a commit-message file. Exit 0/1, for CI. |
Run dtrailer <command> --help for full flag details, or see
SPEC.md for the precise file and trailer format.
Status lifecycle
proposed -> accepted
proposed -> superseded
accepted -> supersededNo other transition is valid. Once accepted, a decision's title and
reasoning are immutable; the only way forward from there is superseded.
Why an agent should use this
- Cheap to check first:
dtrailer decision search "<topic>"before proposing something, so an agent (or teammate) doesn't relitigate a decision already on record. - Cheap to record when unsure:
dtrailer inbox add "..."costs nothing and mints no ID — for a decision-shaped thought not yet worth a full record. - Citable in the commit itself: the printed
Decision: PREFIX-Ntrailer goes straight into the commit message, so the reasoning travels with the code in ordinarygit log, not a separate system that can drift from it.
Reversing a decision
To undo or walk back an accepted decision, create a new decision with
dtrailer decision supersede <ID> --title <t> --reasoning <r> (or dtrailer
decision new --supersedes <ID> ...) and explain the reversal in
--reasoning. Never edit or delete an accepted record directly — this is
the CLI's defined mechanism for reversal, not a suggestion.
What this deliberately doesn't do
- No git operations of any kind.
dtrailernever runsgit commit,git log, or touches.git/. It only reads and writes files under.dtrailer/; committing (and trailer placement) is entirely up to you or your agent. - No network calls, telemetry, or auto-update checks.
- No confirmation gate on transitions — a local file is just a file.
- No images, no web UI, no daemon/watch mode.
Spec vs. implementation license
The spec — the Decision: trailer syntax and the .dtrailer/ file
formats, as documented in SPEC.md — is released under CC0
1.0 Universal: no rights reserved, no trademark, no restriction on use,
forking, renaming, or extending it. Anyone can implement it independently.
This CLI (the TypeScript implementation in this repo) is released under
the MIT License (see LICENSE). MIT covers this reference
implementation only — it says nothing about the convention itself, which is
unencumbered per the above.
Development
npm install
npm run build # compile to dist/
npm test # compile + run the test suite