Skip to content

Repository files navigation

AI–Human Code Review Workflow (Gitea + sbx)

Sandboxed AI agents (Claude Code or Codex) open PRs against a local Gitea; you review in the Gitea UI, merge, then sync to GitHub. The sandbox gets no host credentials — only Gitea's port, with a per-agent token baked in.

sbx agent (claude|codex) → gitea (local :3000) → you review → merge → sync → GitHub (origin)

Each tool acts as its own Gitea user (claude, codex), so PRs stay attributable. Both run the same agent-side gitea-* scripts; only instruction delivery differs (a Claude skill vs. an inlined ~/.codex/AGENTS.md, since Codex has no skills).

Prerequisites

Docker + sbx (Docker Sandboxes), plus git, curl, jq on the host.

Setup

Everything is reachable through one wf command; source env.sh puts it on PATH (with tab completion) and keeps the raw operator/ scripts available too.

docker compose up -d              # Gitea at :3000 (data persists in volume gitea_data)
                                  # complete the first-run wizard, create your user
source env.sh
wf setup <user> <pass>            # your token + agent users 'claude' & 'codex' → ./.gitea-workflow
wf policy allow                   # open Gitea's egress port for sandboxes
wf build                          # bake the Claude template; add --codex for the Codex one

To bake extra plugins/skills/tools into the template, edit bootstrap.sh (it runs in the builder with WORKFLOW_AGENT_KIND set) and allow its install domains via wf policy first.

Per-task flow

wf register [repo]                        # once per repo: create in Gitea, add `gitea` remote, push
cd /path/to/repo && wf new [--codex]      # spawn + attach an agent sandbox (add a name for a 2nd)

Inside the sandbox the agent opens and iterates on the PR (driven by the open-pr skill):

gitea-pr <base> "<title>" "<body>"   # push branch + open PR
gitea-push                           # push follow-ups after you review; the PR updates

Review in the Gitea UI. After merging, sync back to GitHub from the host:

wf sync <branch>                     # fast-forward the merged branch → origin

Worth knowing:

  • The PR's base branch must already exist in Gitea, or PR creation returns 404.
  • wf new makes a read-only --clone sandbox (host tree untouched); run it again for parallel agents. GITEA_SBX_CLONE=0 bind-mounts instead.
  • wf register grants both agent users write and installs a squash merge-message template so merges yield clean commits. It prompts to push the current branch or the whole repo (GITEA_PUSH_SCOPE=branch|all to skip); LFS blobs are never uploaded.

Reference

wf help [command] for details. Subcommands forward to operator/ scripts / Makefile targets:

Command Does
wf setup <user> <pass> Create your token + agent users/tokens; write ./.gitea-workflow.
wf build [--codex] (Re)build a template gitea-workflow-<kind>:v1.
wf policy allow|deny|show Manage the Gitea egress rule.
wf register [repo] Register a repo in Gitea (remote, push, merge template, agent write).
wf new [--codex] [name] [repo] Spawn + attach a sandbox agent.
wf sync [branch] [repo] Fast-forward a merged branch → origin.
wf prune Remove stopped sbx sandboxes.
wf wipe Tear down the installation.

Agent-side commands (baked in, on PATH in the sandbox): gitea-pr, gitea-push, gitea-api <method> <path> [json] (authed API — read/post review comments), gitea-pr-watch (poll for reviewer activity), gitea-version.

How it works

Three layers keep the sandbox isolated:

  1. Egresssbx default-denies; wf policy allow opens Gitea's port only.
  2. Config + instructions — baked into the template: ~/.gitea-workflow, the gitea-* scripts, and PR instructions in the tool's native format (Claude open-pr skill + CLAUDE.md, or an inlined ~/.codex/AGENTS.md).
  3. Wire auth — token sent as an Authorization: token header, never in the URL.

Two gotchas baked around by build-template.sh:

  • localhost vs host.docker.internal. Agents reach host.docker.internal:3000, but the sbx proxy matches policy against localhost, so the egress rule is localhost:3000 while GITEA_URL stays host.docker.internal:3000.
  • Two config files. Host ./.gitea-workflow targets localhost:3000; the baked copy targets host.docker.internal:3000. Same token, different reachable URL.

Security. Each agent token lives in its template's image layers and in ./.gitea-workflow (gitignored). Keep both local — never sbx template save --output to share, and rebuild after rotating a token.

Troubleshooting

Symptom Fix
Blocked by network policy: domain localhost:3000 wf policy allow (rule is localhost:3000).
401 invalid token from a sandbox Source ~/.gitea-workflow, or rebuild the template.
Push works but PR returns 404 Base branch doesn't exist in Gitea — use an existing base.
curl prints 000 No connection — $GITEA_URL is empty (config not sourced).
Script changes not reflected in the sandbox The template is a snapshot — wf build again.
wf sync "fast-forward failed" Branch diverged — inspect git log HEAD..gitea/<branch> --oneline.
Lost the API token Re-run wf setup (delete the old one at /user/settings/applications).

About

Opiniated workflow to facilitate development with the help of agents and review their work

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages