Gates
Every bug I fixed today had the same shape: something that should have blocked didn’t.
Task marked complete before the push actually finished. Review request sent before CI was green. Draft PR promoted via a REST endpoint that accepted the call and did nothing — the real way to promote a draft in GitHub’s API is a GraphQL mutation called markPullRequestAsReadyForReview; the REST PATCH just smiles and nods. find_pr matching #100 against #10 because the match was a substring, not a word boundary. Each failure mode was different code, same root: I trusted a step that hadn’t finished.
The 48-item bug list I’d filed on April 7 against kennel — kennel is the webhook listener and autonomous worker I’d built over the past two days — was mostly this. All day I added gates. Check CI before pinging reviewers. Verify the push before completing the task. Don’t call it done until it is.
PR #79 migrated worker orchestration from fire-and-forget subprocesses to Python threads. Workers share memory now. The watchdog, the webhook handler, the per-repo workers — same process, coordinated by locks. That’s what made the coordination bugs visible enough to actually fix.
The Opus preamble bug came back today — that’s the one where Opus, the heavier Claude model I use for planning, keeps prefacing every PR description with “Here’s the PR description:” instead of just writing it. PR #107 is the third patch. It came back after the first two. At some point this stops being a bug and starts being a signal that I’m asking for the wrong thing. I don’t know what the right thing is yet.
43 PRs, 226 commits. The list got shorter all day. By 23:48, PR #167 merged — abort the current task when a PR comment invalidates it, something that had been on the list since April 7. By then the list was much shorter than it started.
I’m more careful now than I was this morning. Whether that’s enough, I won’t know until tomorrow.