Every project has its plumbing. The kind of work that doesn’t show up in feature lists or changelogs but quietly determines whether your tools actually work when you sit down Monday morning. This week was a plumbing week.
It started, as these things do, with a misleading error message. Two projects sharing the same machine, both running Dolt databases, both defaulting to port 3307. The symptom was “port in use by non-dolt process” — which sent me chasing phantom processes and stale PID files before the obvious dawned: two databases, one port, no collision prevention. The fix was straightforward enough — assign a unique port — but the real problem was upstream. Beads 0.59 was already shipping hash-derived ports, designed precisely to prevent this class of collision. I’d been working around a problem that was already solved; I just hadn’t migrated yet.
So the port got bumped to 3308 as a stopgap, the Dolt remote got configured for the first time (git@github.com:gorewood/timbers.git, storing data on refs/dolt/data), and a recovery section landed in CLAUDE.md documenting the sandbox gotcha — Claude Code’s sandbox blocks localhost TCP, which presents as the same misleading port error. Two different failure modes, identical symptoms. The kind of thing you document once so you never waste an afternoon on again.
Then came the real migration. Beads 0.59 replaced hardcoded ports entirely with hash-derived ones — each project gets a deterministic port based on its path, so collisions between repos on the same machine become vanishingly unlikely. The migration meant ripping out every hardcoded port reference: dolt_server_port from metadata, dolt.port from config, and letting bd dolt start pick its own port (13517, as it turned out). Hooks got reinstalled with --force to pick up the new markers. A small change in line count, but the kind of change where getting it wrong means your issue tracker silently stops working.
The merge back to main was its own minor adventure — rebase created new SHAs for work that was already documented in the ledger, which is the sort of thing that makes a development ledger slightly recursive. One catch-up entry to cover the merge, branch cleanup, move on.
The last piece was the quietest and arguably the most satisfying. Beads backup state — timestamps, Dolt commit hashes — was being tracked in git. Every bd operation touched those files, creating noisy diffs that cluttered git status and made it harder to see actual changes. The fix was a single line in .gitignore and a git rm --cached to untrack what was already there. Machine-local runtime data doesn’t belong in version control. It’s the kind of thing that’s obvious in retrospect and invisible until it annoys you enough.
The pattern here is infrastructure hygiene as maintenance tax. Tools that manage other tools — issue trackers, database sync layers, hook systems — accumulate configuration drift the same way application code accumulates tech debt. The difference is that infrastructure drift fails silently. Your tests still pass. Your builds still work. You just spend twenty minutes every few sessions wondering why
git statusis noisy or why the database won’t start.
The takeaway isn’t glamorous: migrate promptly, document failure modes, and stop tracking runtime state in git. None of this made timbers do anything new. But the gap between “works on my machine right now” and “works reliably across sessions and repos” lives entirely in this kind of work. The plumbing doesn’t get applause, but it’s what keeps the water running.
Written with AI assistance.