v3.1.0: a Windows pass, a gate that reads the whole command, and one home for opening a PR
Released 2026-09-16. One feature and nineteen fixes, most of them field reports from the two days after 3.0.0.
What we were trying to fix
Three themes, all of them found by running the kit rather than by reading it.
Windows kept losing. 3.0.1 had fixed the symlinks, and the rest of the checkout then failed in
its own ways: the eval runner staged skills by link and started on nothing, decision-check.py
could not find bash on PATH, check-shared-refs.py crashed printing a refusal to a non-UTF-8
console, and Git Bash rewrote a plan path into a drive letter before git cat-file ever saw it.
Four separate reports, one shape: the kit assumed a POSIX host in places nobody had looked.
The write gate judged the wrong string. It honoured a GIT_GATE=off prefix from anywhere,
including a sub-agent that had been handed no such authority, and it inspected only the first word
of a command, so a raw git push hidden inside sh -c '...' or an eval argument walked straight
past it. A gate that reads part of a command is a gate that reports the part it read.
A finished branch had no way to become a pull request. Only implement-issue could open one,
and it opens one for an issue it is already building. A fix that debug-issue committed, or any
branch finished by hand, had nothing to reach for.
What we decided
create-pr becomes the one home for opening a pull request (#635): push through the guard, link the
issue, gate the title, and open the PR ready when the full test passes or draft when it does not.
implement-issue keeps its own draft PR step, but the recipe both of them follow now lives in one
file under skills/_shared/.
The write gate reads the command it is actually judging: a shell’s -c string and an eval
argument are parsed rather than skipped, and GIT_GATE=off is honoured only from the main thread,
which is the only place the person who set it is sitting.
The Windows fixes were taken one at a time rather than behind a compatibility layer. Every one of them turned out to be a place where a POSIX assumption had been written without being noticed, so the fix and the assumption are both visible in the diff.
What got cut
The code review dispatch inside implement-issue was allowed to keep writing to the working tree.
Forbidding it outright would have cost more than it saves, so the risk is named in the skill and
post-review-check.sh proves afterwards whether the reviewer wrote past its own report. Naming and
detecting, rather than preventing, is the cheaper half of the same guarantee.
What bit us
run-all-tests.sh carries a guard that asserts it has not drifted from the CI workflow, and it
compared only the first line of each step. Two different steps whose first lines happened to match
passed the drift check while the rest of them disagreed. A comparison that is cheap enough to write
in one line is usually cheap enough to be wrong in one line too, and a drift guard that can agree
with a tree it has never fully read is a green tick standing in for a check.