Timbers, the Ledger
The second ledger every commit needs — one line of what, why, and how, searchable forever · ~12 min read ~– min read · Suggested by Bob engineerpm
Git remembers what changed. It does not remember why. Timbers is the second ledger every commit needs — one line of what, why, and how, drafted by the agent right after each commit and reviewed by you. The result is a searchable design-level record that the next contributor — human or agent — can actually read. This cairn covers what the tool is, what your agent does with it on your behalf, and the moments where your direct attention matters.
What git can’t see
A commit message can carry the what and, with effort, the why for this commit. It rarely carries the design-level why or the how in a way that survives the rush. Timbers does not replace commit messages; it complements them. The commit stays terse; the timbers entry carries the rest.
What the agent runs (and why it matters to you)
A handful of commands cover the daily surface; you rarely type any. timbers prime bootstraps a new session. timbers log records an entry against the latest commit; the post-commit hook surfaces the draft for you. timbers query runs when you ask “why did we do X six months ago?” timbers pending lists commits without entries and must read 0 before you wrap a session. Data lives at .timbers/, committed alongside the code.
A timbers entry is one line each in three slots: what, why, how. Terse on purpose. If a draft drifts toward paragraphs, you are starting a cairn instead.
The post-commit ritual
After every code commit, the agent writes the entry. You scan the why in the post-commit hook output and either accept or sharpen it — that is the human’s slot in the loop. The agent gets what and how right reliably and why most of the time; the 10–20% it gets a little off is where your attention pays for itself.
Before any session handoff or end-of-day push, timbers pending must read 0. A non-zero count means a commit landed without its reasoning. Treat it like a build error.
What fires when the agent commits
Two hooks touch timbers, both wired through .githooks/. The pre-commit hook is a gate that bails if state looks wrong but does not write an entry. The post-commit hook is a soft reminder. Neither substitutes for the explicit timbers log the agent writes afterward. Do not run timbers hooks install — it writes to .git/hooks/, which git ignores when core.hooksPath = .githooks/. Edit .githooks/ directly and commit.
Beads versus timbers
Two ledgers, two jobs. Beads is forward-looking — work to do, anchored on issue ID, read by the next session deciding what to do. Timbers is backward-looking — work done, anchored on commit SHA, immutable once logged, read by the next contributor wondering why. Beads is the inbox; timbers is the outbox.
A useful test: if reading it tomorrow would change what you do next, it is a bead. If reading it next quarter would change how you understand a past decision, it is a timbers entry.
Don’t let secrets leak into the ledger
Both ledgers are searchable and forever. Timbers entries deserve the same posture as commit messages: no credentials, customer data, private URLs, or vendor account numbers. The usual slip is the agent quoting an error message into the how slot that contains a session token. Read the draft before you push. If something already landed, file a bead and rotate the credential — do not rewrite history.
Where timbers streams to
Timbers does not stop at the repo. On every push, the Conduit publisher reads new entries into the team’s lakehouse, where they surface as devblog drafts, sprint-report inputs, and ADR seeds. One line of why, drafted once and reviewed by you, becomes both the answer to “why did we move that handler” six months out and part of the team’s narrative record next sprint.
- How We Build Here — The trail's opening cairn. Why we keep recording reasoning even when typing got cheap.
- The Workshop — The trail's tool map. Timbers is one of the required three.
- Beads, the Backbone — Read together if the inbox-vs-outbox split is new to you.
- Timbers documentation — Public docs for the CLI: install, command reference, integration patterns.
- Conduit — The team's internal knowledge site, fed by timbers entries from every repo.
Open the git log for a healthy codebase that has been worked on for a year. Skim the messages. Some commits explain themselves: “fix off-by-one in pager retry timer.” Most do not: “fix bug.” “address review.” “wip.” “tweak.” “okay actually fix it.” That is not a discipline failure. It is what happens when commit messages are the only place reasoning gets recorded — they collapse to fit the moment, and the moment is always rushed.
Timbers fills that gap. Right after each commit, your agent writes one line of what it changed, why the change happened, and how it carried it out. The ledger is searchable, indexable, and git-native. Six months later, when you ask the agent “why did we move that handler out of the polling package?”, the answer comes back in seconds — the agent runs timbers query, finds the entry, and quotes the why.
What git can’t see
A commit message can carry the what. With effort, it can carry the why. It rarely carries the how in a way that survives the rush. And the why it does carry is the why-for-this-commit, not the why-for-the-design — the architectural reason that justified the change in the first place.
Both layers matter. The commit-level why tells the next reader why a single line moved. The design-level why explains the choice the line embodies. A commit message is the wrong size for both. It has room for one of the two, and what gets cut tends to be whichever the author has less time to articulate.There is a separate, deeper read on this. The standalone cairn Timbers walks through how a typical “fix bug” commit message conceals reasoning that is genuinely useful to the next contributor, and what timbers entries look like for the same commits.
Timbers does not replace commit messages; it complements them. The commit message stays terse. The timbers entry carries the rest.
What the agent runs (and why it matters to you)
A handful of commands cover timbers’ daily surface. You will rarely type any of them yourself — the agent runs them as it executes the loop. Knowing what each one does keeps you grounded in what is happening on your behalf, and lets you reason about what you are seeing in the agent’s output.
timbers prime— bootstrap context for a new agent session. The agent runs this when it starts work in a repo that has timbers wired up; you may also see it during compaction recovery.timbers log "what" --why "..." --how "..."— record an entry, anchored to the latest commit. The agent runs this after every commit. You read its draft (the post-commit hook surfaces it) and adjust the why if it is shallow.timbers query— search past entries. When you ask the agent things like “why did we do X six months ago?” it runs the query and summarizes. You can run it directly when you want to inspect raw entries.timbers pending— list commits without entries yet. The agent watches this internally; you watch its 0/non-zero state at handoff. It must read 0 before you wrap a session.
The data lives at .timbers/ in the repo. On Strike, those files are committed and travel with the code, the same way .beads/issues.jsonl does. Push the branch, and the reasoning rides along with the diff. Full command reference at the public timbers documentation.
A timbers entry is one line each in three slots: what, why, how. Terse on purpose — the point is to have it at all, not to write an essay. The agent’s drafts are usually exactly this size; if you ever find yourself sharpening one into paragraphs, you are starting a cairn instead.
The post-commit ritual
After every code commit, the agent writes the entry. You will not type timbers log yourself most days — you will see the entry in the post-commit hook output, scan the why, and either accept it or sharpen it. That is the human’s slot in this loop. The why is the part you brought to the work, and the agent’s draft is the place to verify it landed.
You said “ship the fix”; the agent’s responsibility is to commit it and leave the audit trail behind it. The actual command the agent runs looks like this:
timbers log "Reorder ECO completion checker before grace-period probe" \
--why "Late-clone detection was missing reactivation on same-poll bursts" \
--how "Move the completion-check call ahead of the probe; add unit"
The agent gets what right reliably, how nearly always, and why most of the time. The 10–20% of whys it gets a little off are exactly where your attention is worth its cost. Read the draft, adjust the why when it is shallow or wrong, move on.
Before any session handoff or end-of-day push, timbers pending must read 0. This is non-negotiable. A pending entry means a commit landed without its reasoning recorded; the next reader pays the cost. Treat timbers pending like a build error.
What fires when the agent commits
When the agent commits on your behalf, two hooks fire that touch timbers. Both are wired through .githooks/ (shared via core.hooksPath); neither is installed by running timbers hooks install. Knowing what each one does helps you read the agent’s commit output without surprise.
Pre-commit hook. After the beads pre-commit hook (which does the auto-export of the issue graph to JSONL and stages it, among other bookkeeping) and the lint-staged pass, the hook runs timbers hook run pre-commit. This is a gate: a non-zero exit blocks the commit. It does not write an entry — it checks state and bails if something looks wrong (for example, the agent has pending entries from earlier commits that would conflict with this one).
Post-commit hook. Runs timbers hook run post-commit. This is a soft reminder; a non-zero exit is ignored. It surfaces “this commit needs an entry” so the agent acts on it as the next step in the loop.
The combination is intentional. The pre-commit gate keeps state coherent; the post-commit reminder keeps the entry from being skipped. Neither hook substitutes for the explicit timbers log the agent writes after the commit — that step has to carry real reasoning, and reasoning has to come from whoever made the choice.Why have the agent write the entry instead of automating it entirely? Because why is the value-add, and a fully-automated entry that says “Reorder completion checker (15 lines moved)” is a worse version of the diff. The reasoning is the part that has to come from the actor — agent or, when you decide it matters more than the agent has captured, you.
Do not run timbers hooks install. It writes to .git/hooks/, which git ignores when core.hooksPath is set to .githooks/. Edit .githooks/pre-commit and .githooks/post-commit directly and commit the change. The same rule applies to bd hooks install, for the same reason.
Beads versus timbers
Two ledgers, two jobs. The team’s mental model:
| Beads | Timbers | |
|---|---|---|
| Direction | Forward-looking | Backward-looking |
| Holds | Work to do | Work that was done |
| Anchor | Issue ID | Commit SHA |
| Lifecycle | Created → claimed → closed | Logged once; immutable |
| Read by | The next session deciding what to do | The next contributor wondering why |
Beads is the inbox. Timbers is the outbox. Both are first-class; both are required. They overlap exactly once, at issue close: when the agent runs bd close <id>, the timbers entry that documents the merge commit is what proves the work happened the way it was claimed. Past that overlap, the two ledgers do not cross.
A useful test when you are unsure where something belongs: if reading it tomorrow morning would change what you do next, it is a bead. If reading it next quarter would change how you understand a past decision, it is a timbers entry. Most things are obvious; the 10% that are not are usually beads.
Don’t let secrets leak into the ledger
Both ledgers are searchable and both are forever. Timbers entries deserve the same posture as commit messages and PR descriptions: never let credentials, customer data, private URLs, vendor account numbers, or anything you would not paste into a public Slack channel land in them.
The slip is rarely deliberate. The usual mode is the agent quoting an error message into the how slot, and the error message contains a session token or a failing URL with a customer identifier. Read the draft before you push — this is one of the few places where the human’s eye is the last line of defense. If something already landed, ask the agent to file a bead and rotate the credential; do not try to rewrite history.
Where timbers streams to
Timbers does not stop at the repo. On every push, the Conduit publisher reads the new entries and feeds them into the team’s lakehouse, where they become operational data alongside other event streams. The same entries surface as devblog drafts, sprint-report inputs, and ADR seeds — the team’s internal knowledge layer.
You do not have to know any of that to get a good entry recorded — the agent and the hook plumbing handle the mechanics. But it is useful to know that one line of why, drafted once and reviewed by you, becomes both the answer to “why did we move that handler” six months from now and part of the team’s narrative record next sprint. The Conduit trail (/trails/conduit/) is the deep read on that pipeline. The standalone Timbers cairn is the deep read on the tool itself.
Summary
- Git carries what; timbers carries why and how. The second ledger exists because commit messages collapse under time pressure.
- One line each in three slots. What, why, how. Terse on purpose. If a draft drifts toward paragraphs, it is becoming a cairn instead.
- The agent writes the entry; you sharpen the why. The post-commit hook surfaces the draft. Read it; adjust if shallow; move on. That is your slot in the loop.
- Pre-commit gates state; post-commit reminds. Neither hook writes the entry — the agent does that as a follow-up step the loop expects.
timbers pendingmust read 0 before any handoff. Treat a non-zero count as a build error. The agent watches it; you confirm it at the close.- Beads is the inbox; timbers is the outbox. Forward-looking work versus backward-looking record.
- Don't log secrets. Both ledgers are searchable and forever.
- Entries stream into Conduit. Your one line of why seeds devblogs, sprint reports, and the team's narrative record.
- If you had to argue for a stricter rule than ours — say, that every commit must have a timbers entry before the next commit can be made — what would you gain and what would you lose?
- The agent gets the why right most of the time but not always. What is your own quick check that the why the agent wrote is the why you actually had?
- Beads and timbers are both git-native and both committed. Conduit pulls from timbers but not from beads. Where would Conduit benefit from also pulling beads, and where would it actively hurt?
- How We Build Here — The trail's opening cairn. The "old engineering rituals still apply, the participants are just faster" framing covers why we keep recording reasoning even when typing got cheap.
- The Workshop — The trail's tool map. Timbers is one of the required three; this cairn is the deep read on the second of them.
- Beads, the Backbone — The companion cairn on beads. Read together if the inbox-vs-outbox split is new to you.
- Timbers (standalone) — The deeper read on the tool itself: data model, query patterns, why a separate ledger turns out to matter for design-level reasoning.
- Conduit Trail — Three-part series on the publisher and consumer architecture that turns timbers entries into devblogs, ADRs, and sprint reports across all our projects.
- Timbers documentation — Public docs for the timbers CLI: install, full command reference, integration patterns. The canonical source for anything this cairn does not explain.
- Conduit — The team's internal knowledge site, fed by timbers entries from every repo. See the Conduit trail (above) for the architectural deep read.
Generated by Cairns · Agent-powered with Claude