v0.3.0
A new gate at step 9: housebroken branch.
Three defects on one pull request were facts about the branch rather than about the patch, and git could have printed all three before anything left the machine. A test file committed 100755 among 100644 siblings, which GitHub renders in the diff. A corrected sentence that never left the working tree, because git reset --soft leaves the index at the previous commit, so the rebuilt commits carried the exact wording a reviewer had just rejected. And a documentation sentence claiming the command removes "only the files the plugin generated", which the code falsifies for two file names.
branch-check.sh refuses a dirty working tree, a file mode that disagrees with the file's own siblings, a committed file the repository's own .gitignore excludes, a working artifact, and a branch that is not on top of its base. It prints every absolute claim the added prose makes, and asks for each to be falsified against the code. Those it warns about rather than refuses, because some absolutes are true.
Its ignore check shipped inert in the first draft, because git check-ignore skips tracked files without --no-index. The sabotage proof in its test caught it before the gate ever ran, which is the argument for writing those proofs.
Step 4 gains the two rules a script cannot hold. Swap a file's content from git show <base>:<path> rather than switching branches: a checkout refused because the tree is dirty leaves the patched file in place, and the arm then reports success under the label RED. And after rebuilding commits, grep the committed blob for the change, never the file on disk.
Step 5 gains one more: a project whose own lint aborts on its own configuration is a fact to state, not permission to skip linting the files you changed.