Development reports

Dev Log: 2026-05-21

Laura's report came in plain: she'd seen merge SHAs sitting in pending with no obvious next action, and no clean way to tell timbers yes, I saw it, no, it doesn't need a doc. That's the kind of friction that doesn't break anything — it just slowly teaches agents that the tool is lying to them. Which is worse.

A downstream test run had surfaced four of these in a week. Empty-file merges showing up as "you forgot something" when there was nothing to forget. No skip path for bot authors, with an autofix pipeline about to start generating exactly that kind of traffic. No way to say "skipped, on purpose, here's why" without reaching for --no-verify and pretending. And no debug knob when an agent wanted to know why a commit was classified as pending in the first place.

The fixes were straightforward in shape but tangled in the code — storage.go and skipcount.go sit underneath all four behaviors, so the work landed as one commit rather than five. Interactive staging would have been more theater than hygiene.

The interesting moment was the skip-authors design. I'd started with a dedicated .timbers/skip-authors file — clean, single-purpose, easy to document. Got pushed back on it: more config files is the wrong direction. Fold it into .timbersignore as an author: line prefix and you mirror the .gitignore family, keep one source of truth for repo skip config, and reuse the existing parser. The classifyTimbersIgnoreLine function ended up emitting both rule shapes from one pass. The collision case — a literal path starting with author: — is theoretical enough to document and move on. Better idiom beat better isolation.

The other thread was a real bug, not friction. An entry in a downstream project got stranded locally because someone pushed between git commit and timbers log. The protocol said commit-then-log, but it didn't bold the rule against pushing in between, and timbers log itself gave no signal — even though the data to detect it was sitting right there in @{u}. So now IsPushedToUpstream runs after WriteEntry, and if the anchor's already on upstream when the entry lands, you get a warning. The protocol text got rewritten with an explicit never push between callout, and the shared workflow prose moved into internal/protocol so the full PRIME doc and the MCP subset compose from the same source instead of drifting.

The reviewer earned their keep on this one. Caught an AckedSet double-scan I'd written into filterCommits — fixed by threading it parallel to docSet so pending checks stay single-pass. Caught a dead pushedMsg parameter. On round two, flagged that the protocol sanity test was only checking prose and would miss a reordered checklist; an explicit ordering-position assertion went in. None of these were load-bearing on their own. Together they're the difference between "it works" and "it'll still work in three months."

The pattern worth naming: when a tool's silence is the bug, the fix usually isn't more output — it's making the existing data legible at the right moment. The push-before-log warning didn't need new state. It needed one well-placed check against information git already had.

v0.22.0 cut clean. Changelog drafted, site examples synced, landing-page badge bumped, tag pushed, GH Actions handling the binary. Additive only, no breaking changes, which is exactly what downstream projects need to test without a migration tax. The agent-DX guide picked up the four new surfaces — ack, the author: globs (including the GitHub-bot prefix workaround for filepath.Match's character-class quirks), TIMBERS_DEBUG, and the merge-skip behavior — because shipping pending-detection changes without doc coverage means they land hidden.

What this doesn't solve: the protocol is still prose, and prose drifts. The ordering-position test catches one specific regression, not the general class. Good enough for now; worth watching.

Solid ground. The kind of release where the work was small in each piece and the value is in the bundle — four points of friction smoothed at once, before the next pipeline starts pushing on them.


Drafted with AI assistance from session entries.