/agents and passes it only to its own gh or
git child. A wrapper whose token cannot be minted exits 1 before running anything, so it never falls back to
another login.
Who acts as whom
Coordinator commands
In a pipe or
$(...), pass --silent: without it pnpm prints a $ infisical run ... line to stdout. There is no
GH_TOKEN=... gh form: an empty GH_TOKEN (a failed mint) makes gh fall back to the owner’s stored login.
The private Figentra OS repo
figentra/os is in both apps’ installations. Each app has one installation for the figentra organization, so the
installation ids already in Infisical cover it:
pnpm bot:gh -- pr list -R figentra/ospnpm pr:merge -R figentra/os 3pnpm bot:git -- -C ~/Projects/figentra-os push origin HEAD. pnpm runs scripts from the stackra root, so name the directory with-C.pnpm bot:review -- pr diff 3 -R figentra/os
What the guards deny
.agents/hooks/guard-bash.mjs (Claude Code) checks the command, every $(...) and backtick body (inside double
quotes too), and every sh -c or eval string. It denies:
ghat command position, also behindsudo,env,xargs,VAR=assignments or a full path, whateverGH_TOKENis set to;- network
gitat command position:push,fetch,pull,clone,ls-remote,submodule,subtree,remote update|show|pruneandarchive --remote, for every repository, public or private. Each goes throughpnpm bot:git --. A bot push tomainis denied too, with the branch read in the checkout the push runs in (after acd,-C <dir>,--git-diror--work-tree; an unknown$VARtarget counts asmain); - pack plumbing, bare or through
pnpm bot:git:send-pack,receive-pack,fetch-packandupload-pack; - a merge outside
pnpm pr:merge:pnpm bot:gh -- pr merge ...,pnpm bot:gh -- api .../pulls/<n>/mergewith any method, and themergePullRequest/enablePullRequestAutoMergeGraphQL mutations.bot-gh.mjsrefuses them itself as well; - anything that could print or store a token:
pnpm bot:token, a$GH_TOKENor$GITHUB_TOKENreference,pnpm bot:gh -- auth ...,pnpm bot:review -- auth ..., andgit credential fill|approve|rejector agit credential-<helper>(credential-osxkeychain getincluded), bare or throughpnpm bot:git; they answer with the owner’s token.bot-gh.mjsrefusesgh auth,git credentialandgit credential-*itself as well.
git (status, log, diff, add, commit, switch, rebase, worktree, remote -v) needs no login and stays allowed.
A plain git commit carries the developer’s git name, so builders commit with pnpm bot:git -- commit.
The OpenCode guard (.opencode/plugins/guard/decide.ts) is stricter. Executors run no gh, and no git push,
fetch, pull, clone, ls-remote, submodule, subtree, remote update, remote show, remote prune, archive with
--remote, pack plumbing, credential or credential-<helper>. They also run no pnpm bot:* and no pnpm pr:merge, and the same substitution bodies are checked.
Honest limit
The guards are an accident guard, not isolation. They do not follow aliases, shell functions, variables used as a command name,npx or pnpm dlx. Agents run as the owner’s macOS user, so the keychain and ~/.config/gh are
readable by any process an agent starts. The structural fix is owner steps 5 and 6 below: after them, an agent’s
plain gh or git finds no owner credential to fall back to. Full isolation needs agents to run as a separate OS
user, or on the EC2 agent host, where the owner’s login does not exist.
What the owner does
figentra/stackrabranch protection (checked 2026-10-21, nothing to change).mainis protected and requires theQuality Gatecheck for non-admins, and there are no rulesets. The bot merges through that protection: it is not an admin, so the gate must be green, andpnpm pr:mergenever passes--admin. If “Restrict who can push to matching branches” is ever turned on, add thestackra-botapp to the allowed list, or every merge fails.figentra/oshas no branch protection. It is private on the free plan, and GitHub answers “Upgrade to GitHub Pro or make this repository public” for its rules. Nothing on GitHub stops a direct push to itsmain; only the guards andpnpm pr:mergedo. To protect it, move the organization to GitHub Team, then requireQuality Gateonmainas on stackra.- App permissions are enough, nothing to change.
stackra-botholds contents, pull requests, issues, actions and workflows: write, and checks and statuses: read. That covers push, PR, squash merge and branch delete.stackra-reviewerholds pull requests: write and contents: read. The bot cannot read classic branch protection (administration: read is not granted), and does not need to. - Private notes repo.
figentra/stackra-notesis in neither installation, so an agent cannot fetch or push it. Add it to thestackra-botinstallation (organization Settings, GitHub Apps, stackra-bot, Repository access), and agents push withpnpm bot:git -- -C ~/Projects/stackra-notes push. - Move the owner’s
ghlogin out of the default config (structural fix). In the owner’s own terminal:GH_CONFIG_DIR=~/.config/gh-owner gh auth login, thengh auth logoutfor the default config. Then addalias gh='GH_CONFIG_DIR=~/.config/gh-owner gh'to the owner’s own shell profile. Agent shells do not load that alias, so their plainghfinds no login. - Remove
gh auth git-credentialfrom the global git config agents see (structural fix). Rungit config --global --unset-all credential.https://github.com.helperand the same forcredential.https://gist.github.com.helper. Then erase any GitHub entry the macOS keychain helper stored:printf 'protocol=https\nhost=github.com\n\n' | git credential-osxkeychain erase. For the owner’s own pushes, add to the shell profile:alias ogit='git -c credential.helper= -c "credential.helper=!GH_CONFIG_DIR=$HOME/.config/gh-owner gh auth git-credential"'.pnpm bot:gitsets its own helper and keeps working. - Approve only in the GitHub UI. An owner approval is an APPROVED pull request review on the head commit whose
body ends with
owner-approval, submitted from the web or mobile app with 2FA. A counted reviewerAI-REVIEW: FAILon the same commit still blocks the merge.