Questions that come up when you point Aixgo Code at a repository for the first time. Most of them are really one question in disguise: what should the gate be, and why won't the agent run until it is right?
For the GitHub Actions runtime, yes. For local development, no.
You can use Aixgo Code two ways:
- as a local CLI (
aixgo-code runandaixgo-code address) using your ownghauthentication, which reads the repo config, works in a git worktree, runs your gate, and opens or updates a pull request as you; - or through the reusable workflow that runs inside your own GitHub Actions, as
described in the README's install section, which now mints a per-job GitHub
App installation token for your own App's
[bot]identity.
The GitHub App is configured with only contents, pull requests, and
issues, and the reusable workflow proves that scope at runtime by expecting an
Actions-administration API call to fail.
That permission set also means the App cannot push edits under
.github/workflows/. When a change needs a workflow-file edit, make that commit
as a human or run the CLI locally under your own gh auth instead of the App.
Both engines ship an escape hatch: Codex has --dangerously-bypass-approvals-and-sandbox
and a danger-full-access sandbox mode, and Claude Code has
--dangerously-skip-permissions. Aixgo Code does not set any of them for
you, and the reason is worth stating plainly because reaching for them is
tempting.
Both vendors put "dangerous" in the name deliberately. Codex's own help says its bypass is "intended solely for running in environments that are externally sandboxed". A GitHub Actions runner is ephemeral, which protects the machine, but the things worth protecting are inside it: the model provider key, and whatever else the job holds. Ephemerality does not undo an exfiltrated secret.
The distinction that decides it is between a person running the CLI at their own desk and an unattended Action running a model. In the first case a human is present, watching, and can interrupt. In the second there is nobody, the trigger is an issue body that anyone with access to the tracker can write, and the run happens whether or not it is going well. That is the case that needs more constraint, not less, and a product that sells human-in-the-loop should not disable a safety control on the operator's behalf to make an unattended run convenient.
So when a change cannot be made under the constraints the runtime accepts, the run stops and a human does it. That is not a gap in the design; it is the design. The same rule already applies to workflow files, which the App deliberately cannot push.
AIXGO_SANDBOX exists as a knob so an adopter who has genuinely
externally sandboxed their runners can widen it themselves, with their eyes
open. Nothing in this repository sets it.
Because the sandbox could not start without it, and a sandbox that cannot start is worse than one that can.
The reusable workflow runs this before the engine:
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0Codex confines itself with a bundled bubblewrap, and bubblewrap builds its sandbox out of an unprivileged user namespace. Ubuntu 24.04, which is what a GitHub runner is, ships an AppArmor policy that forbids unprivileged processes from creating one. So the sandbox died at startup on every single run:
bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted
Every command the engine tried then failed, including pwd. It read nothing,
changed nothing, and still exited successfully, which is why this looked like an
agent that quietly did no work rather than one that was never able to start.
This was measured rather than guessed. On a runner, with the same bubblewrap binary and only this setting changing: restriction on, the error above; restriction off, the sandbox starts and runs commands; restriction back on, the error returns.
Read the setting name carefully, because it inverts easily. This enables the kernel feature the sandbox is built from. It is the opposite of the flags above: those turn the sandbox off, and this is what allows it to be on at all. The alternative was not "a safer run", it was an engine confined by nothing because its confinement never loaded.
It applies to the runner, which is an ephemeral VM that exists for this one job and is destroyed after it. Nothing about the host is changed and nothing about the App's permissions is widened.
Yes, once. aixgo-code init --workflow creates the six state labels through
your own gh auth, and writes the config, the caller workflow, and the install
self-test as local files.
Nothing is created behind your back and nothing is merged for you. You open that
setup pull request and merge it yourself, because the App holds no workflows
permission and cannot add its own workflow files.
Almost always because the gate does not pass, and the gate is the whole point. Aixgo Code will not declare a change done, or open a pull request, until the gate command exits zero. A loop with nothing to stop it wanders, breaks things, and still reports success, so a repo with no gate is refused and a repo whose gate cannot pass gets a change that stalls and asks for a human.
Three things trip up a first run, all of them about the gate.
If your repo's own checks fail on a clean checkout, the agent inherits that
failure and can never reach a green gate. This is more common than it sounds. A
real example from onboarding a repo: make fmt-check failed on main because
seventeen files had drifted out of gofmt formatting. CI had never caught it,
because the lint job ran a linter with no formatter enabled and the format check
was never wired into CI. The formatting slowly rotted while every check stayed
green.
The fix is not to weaken the gate to route around the breakage. It is to fix the
baseline (here, one gofmt -w) and then enforce it so it cannot drift again (here,
enabling the gofmt formatter in the existing linter, which CI already runs).
Now the gate is stable and the same check protects your humans too.
The lesson generalizes: onboarding is a good time to find out your baseline is not as green as you thought. Fix the baseline, do not lower the bar.
If the gate is weaker than what your CI actually enforces, the agent will produce a change that passes the gate and then fails your CI. That is the worst outcome: it looks done and is not.
The first live run on a repo made exactly this mistake. The configured gate was a plain build plus a slice of the tests, while the repo's CI also ran a linter and built across several language versions. The agent opened a clean-looking pull request that then failed CI on lint and on a version bump the build could not take. The change was fine; the gate was lying.
So set the gate to what your CI gates. If your CI runs a formatter, a linter, a vet step, a build, and tests, your gate should run those too.
The gate runs on one machine, in a worktree, before a pull request exists. A few kinds of check cannot run there and should stay in CI, which runs after a human merges:
- A build or test matrix across several language or runtime versions. The gate builds once, with the toolchain on the machine.
- Container image builds and anything that needs Docker.
- Tests that need an external service (a real database, a running server, a cloud emulator).
The distinction that matters for tests is whether they are self-contained. Tests
that spin up an in-process server (for example Go's httptest) run fine in the
gate. Tests that expect a service to already be listening on a port do not, so
leave those to CI.
The rule of thumb: put everything runnable on one machine in the gate, and leave the genuinely environmental checks to CI. A faithful single-machine subset is the goal, not a copy of every CI job.
Prefer the commands your repo already uses to know a change is good. If you have a
make check or an npm run check that runs standalone, use it. Otherwise compose
the runnable checks with && so the first failure stops the gate:
gate: <vet> && <lint> && <build> && <tests>A concrete Go example, after making sure each part is green on main:
gate: go vet ./... && golangci-lint run ./... && go build ./... && go test ./...Verify it passes on a clean checkout before you rely on it. If it does not, that is a baseline problem to fix (see question 1), not a reason to trim the gate.
No. It is told, in every turn, never to edit the gate command, its configuration, the CI workflow files, or any test in order to get a green result, and to stop and explain instead of forcing one. That rule is also enforced outside the prompt, because a model will otherwise route around it. If a change genuinely cannot pass the gate legitimately, the run stops and asks for a human rather than pushing through.
It stops and hands the issue or pull request back to you with a note, rather than opening or updating anything. The strongest action it can take is a pull request against a branch. It never merges, and it holds no deploy or production credentials.