CodeAFFix the build before you push

Fix the build before you push

A git pre-push hook runs the tests, and when they fail, codeaf do fixes the code and the next push carries the fix.

Broken code should not leave your laptop.
  • a pre-push gate on a small repo
  • a fix step in a deploy script
  • a commit hook for a failing lint

A pre-push hook is the last door out of your laptop. In this playbook the hook runs the tests first. When they fail, it hands the fix to codeaf do, which plans the fix, makes it and has a checker judge it with nobody watching. The hook commits what comes back and stops the push, and the next push carries the fix out. The hook only trusts the result when codeaf do exits 0 and the tests pass again.

Steps

Our textkit repo has slugs and word wrapping, a remote, and five tests. A teammate's commit, slug: drop apostrophes (it's -> its), also dropped digits from slugs, so two tests fail. Save this hook as .git/hooks/pre-push and make it executable:

#!/bin/sh
# Push only green code. When the tests fail, CodeAF fixes the code and commits the fix.
python3 -m unittest -q 2>/dev/null && exit 0
echo "pre-push: tests fail; handing the fix to codeaf"
out=$(codeaf do "The unit tests fail after the last commit. Fix the code, not the tests, until python3 -m unittest passes." --json --timeout 15m 2>.git/codeaf-fix.log)
code=$?
echo "$out" | jq -r '"codeaf: exit '"$code"' · ok=\(.ok) · stop=\(.stop) · $\(.spend_usd*1000|round/1000)"'
[ "$code" -eq 0 ] && python3 -m unittest -q 2>/dev/null || { echo "pre-push: not fixed; push stopped"; exit 1; }
git commit -qam "fix: $(echo "$out" | jq -r '.answer' | head -1 | cut -c1-72)"
echo "pre-push: fixed in $(git rev-parse --short HEAD); run git push again"
exit 1
  1. Push as usual:
git push origin main
  1. The hook stops the push, fixes, commits, and prints pre-push: fixed in ea7b4fe; run git push again. Push again:
git push origin main

What you see

The first push prints pre-push: tests fail; handing the fix to codeaf, then codeaf: exit 0 · ok=true · stop=done · $0.006, then the fixed line, then error: failed to push some refs to '../textkit-remote.git'.

The hook exits 1 even after a good fix. A pre-push hook cannot add a commit to the push already in flight, so it commits the fix and stops; the second push carries it. The fix commit is one line, ea7b4fe fix: Fixed slugify bug from last commit: char class [^a-zA-Z]+ dropped digits, a change from [^a-zA-Z]+ to [^a-zA-Z0-9]+.

In our run

The first push, with the fix inside it, took 14.5 seconds, and the fix cost $0.006. The brief said "fix the code, not the tests": the regex was the only change, and the teammate's apostrophe change stayed.

Make it yours

  • Point the gate elsewhere. Swap the python3 -m unittest lines for your own test, lint or type check command, and say the same command in the brief.
  • Give it a deadline. The --timeout 15m flag bounds how long the hook holds your push.
  • Read the fix before it leaves. The hook commits on its own, so run git show HEAD before the second push. .git/codeaf-fix.log holds the crew line: crew: bugfix · worker glm-5.3-flash (openrouter) · planner claude-opus-5.5 · checker glm-5.3-flash · $0.006 (est $0.047).

Go further

Headless mode: Hand CodeAF one job from a script, a CI job, a cron line or another agent, and get back one JSON object and an exit code.

Coming soon: describe the org you want and CodeAF builds it: teams, managers, budgets, standing orders.