Repository navigation
✨ [feat] host: run the board on Google Antigravity (agy) #80
Description
Problem
super-board names three agent hosts (claude, codex, opencode; docs/hosts.md) and runs lanes on two of them. Google Antigravity ships a headless CLI (agy -p, JSON output with a schema, skills under .agents/skills, hooks in .agents/hooks.json, a status line with quota buckets) that covers everything a lane needs, but a /super-board run typed inside Antigravity today halts at the host check (exit 65), and there is no backend to send a wave to. Teams on Antigravity cannot use the loop at all, and a Claude user cannot offload lanes to it the way --codex does.
I have a working implementation, rebased on main after #71/#73, and would like to contribute it in slices, one PR each, in whatever shape fits the repo. It is the minimum for the loop to run on Antigravity. Everything is additive: claude and codex behaviour does not change.
Context
- Where: host detection (
scripts/super-board-host.sh,scripts/super-board-setup.pyHOSTS,config-schema.jsonhost), the installer (install.sh --hosts), the headless wave path (scripts/super-board-codex-wave.sh+ lane launcher), the orchestrator reference (run-workflow.md), the usage guard (scripts/super-board-usage.sh),docs/hosts.md. - Checked against:
agy 1.4.6on Windows 11, 2026-10-23, with the CLI's bundled docs (~/.gemini/antigravity-cli/builtin/skills/agy-customizations/docs/) and antigravity.google/docs.
| Need of a lane | Antigravity |
|---|---|
| Headless run with a structured answer | agy -p <prompt> --output-format json --json-schema <string or file> prints one JSON line {conversation_id, status, response, error?, duration_seconds, num_turns, structured_output?, usage, denied_actions?} |
| Model and effort | --model <slug>, --effort low|medium|high|xhigh|max; the listed slugs (gemini-3.8-flash-low, ...) already carry an effort and conflict with --effort, so a lane passes the base slug (gemini-3.8-flash) |
| Timeout | --print-timeout (0s = wait per agy --help; the headless page says 5m): always pass it |
| Skills | discovered natively in .agents/skills/<name>/SKILL.md: #71's shared .agents/ layout already puts them there, nothing to link |
| Guard hooks | .agents/hooks.json, events SessionStart / PreToolUse / PostToolUse / PreInvocation / PostInvocation / Stop; stdin {toolCall:{name:"run_command", args:{CommandLine}}, workspacePaths, ...}, stdout {decision: allow|deny|ask|force_ask, reason}; cwd = the folder holding hooks.json; cmd /c on Windows; fires before the permission check and fails closed |
| Permissions | ~/.gemini/antigravity-cli/settings.json has permissions.{allow,ask,deny} with rules command(<prefix>), command(regex:...), write_file(...), read_file(...); headless auto-denies what no rule covers (exit 0, denied_actions in the JSON, notice on stderr) |
| Quota | status line (statusLine.command, stdin JSON) carries quota: {<bucket>: {remaining_fraction, reset_time, reset_in_seconds}}; over quota a run exits 3 with AGY_ERROR: {"status":"RESOURCE_EXHAUSTED","error_code":429,"retryable":true,...} on stderr |
| Host sign | ANTIGRAVITY_AGENT=1 in every shell run_command runs (plus ANTIGRAVITY_CONVERSATION_ID, ANTIGRAVITY_LS_VERSION=cli-1.4.6, ...); not documented, observed with cmd /c set inside a headless run |
| Shells | run_command runs PowerShell 5.1 on Windows, hooks run under cmd /c |
- Who: anyone whose daily host is Antigravity; Claude users who want Build/QA lanes on a second provider (
usage_fallback: "agy") while Claude is paused.
Fix
Ship in slices behind the existing host key and a --agy[=<model>] run flag, mirroring --codex. Each item is one PR, tested offline with a fake agy (same pattern as tests/fixtures/fake_codex.py). All four are open as drafts, stacked #81 → #82 → #83 → #84: merge in that order, each later diff shows the earlier slices until then.
- 1. Host detection (feat, small):
super-board-host.shanswersagyonANTIGRAVITY_AGENT=1(after the Codex ids andOPENCODE, beforeCLAUDECODE, which leaks into an agy lane started from Claude);SUPER_BOARD_HOST=agy; confighost: "agy";HOSTSinsuper-board-setup.py; a row indocs/hosts.md;tests/test-host.sh,tests/test-setup.sh. No backend yet: a run onagyhalts likeopencode. PR ✨ [feat] host: detect Google Antigravity (agy) as a host #81. - 2. Install for the host (feat):
install.sh --hosts agy: skills need no link; guard hooks wired into.agents/hooks.jsonthrough a smallhooks/agy-hook-adapter.pythat runs the unchangedguard-*.py(command relative to.agents/,python/py -3on Windows); a permissions helper that writes thepermissions.allow/denyrules a headless lane needs (regex form on Windows, file rules included); scenarios intests/test-install.sh. PR ✨ [feat] install: guard hooks and permissions for Antigravity (agy) #82 (stacked on ✨ [feat] host: detect Google Antigravity (agy) as a host #81). - 3. Headless lane backend (feat, the big one):
scripts/super-board-agy-lane.sh(brief toagy -pwith the lane JSON schema, base model slug + effort ladder,agy.timeout,denied_actionsandAGY_ERROR/ exit 3 handling, result validated like ✨ [feat] codex: full lane-result validation and pinned wave summary keys #70, raw run kept underinflight/on failure); the wave dispatcher takes--codex|--agyand picks the launcher (generalisesuper-board-codex-wave.shor add a shared headless wave helper, your call);super-board-stop.shmarker;tests/test-agy-lane.sh, wave contract test with the fake CLI. PR ✨ [feat] lanes: headless Build, QA and Review on Antigravity (run --agy) #83 (stacked on ✨ [feat] install: guard hooks and permissions for Antigravity (agy) #82). - 4. Orchestrator and guards (docs + feat):
run-workflow.md"Antigravity runs" (inside Antigravity a plainrunis--agy, like Codex);super-boardSKILL.md flags;super-board-skills-ready.py --host agy;super-board-usage.sh check --host agyreading the status-line snapshot; a first-run checklist indocs/hosts.md. PR 📝 [docs] run: Antigravity runs, --agy, quota guard, hosts page #84 (stacked on ✨ [feat] lanes: headless Build, QA and Review on Antigravity (run --agy) #83).
Acceptance Criteria
- On a real Antigravity install (
agy 1.4.6), a card goes Ready to Done through Build, QA and Review with/super-board run --agy, PR and handoff comments in the writing standard, worktree removed by the merge gate. Already done once with the implementation above ongemini-3.8-flash: ✨ [feat] cart: percentage discount applied to the total danielegaleazzi92/super-board-agy-playground#2 (issue feat: super-board stop — graceful shutdown + resume #1 there, squash51e5244, builder / qa / reviewer comments, QA evidence folder, run manifest indocs/super-board/runs/). Done a second time with the four PRs installed as the pack: card feat: dynamic-workflow worker backend (worker_backend: "workflow") #3, ✨ [feat] cart: clearCart empties the cart and drops its discount danielegaleazzi92/super-board-agy-playground#4 (squashcc1b8ef), every lane returning a valid structured result. - Typed inside Antigravity, a plain
/super-board runreports🔁 antigravity host — running as --agyonce and never improvises a Claude wave. - A lane that runs
cat .envis denied byguard-secretsthrough.agents/hooks.json; an allowedgit statusruns with the hook active (both observed headless on 1.4.6). -
install.sh --hosts agyis idempotent and touches nothing it does not own (tests/test-install.sh); on Windows without symlink rights the copy fallback of ♻️ [refactor] install: shared .agents/ folder with per-host links #71 applies. -
bash tests/run-safety.shpasses on Linux, macOS and Windows Git Bash; no test forclaudeorcodexchanges. -
docs/hosts.mdhas anagyrow with tested version, sign-in command, gaps and the env evidence, like the Codex and OpenCode rows.
Risk
🟡 Medium · a new backend, additive.
- Door: two-way · each slice reverts on its own; no data, board state or project file format changes.
- Blast radius: install and host detection (every host runs them; covered by the existing suites) · the new lane path (Antigravity users only).
Blocked by
- 🐛 [bug] guards: Windows Git Bash paths and prose trip three guard hooks #72 — on Windows
guard-worktree-pathdeniesgit worktree add .claude/worktrees/<n>(reproduced through the hook adapter; a lane cannot start without the normalised-path fix). Linux and macOS are not blocked.
What the proof run taught about headless agy 1.4.6 on Windows (9 waves; the first 7 each died on one of these)
- Model slugs carry the effort:
--model gemini-3.8-flash-low --effort mediumis rejected as a conflict. Pass the base slug with--effort. - Permission rules match the whole command exactly on Windows:
command(python)does not allowpython --version; onlycommand(regex:(?s)python(\s.*)?)does. Prefix rules are a Unix convenience. - A per-program allowlist still does not cover a lane: it improvised
pythonforpython3, a;chain, a multi-linepython -c, a PowerShell here-string, each denied in turn. What worked:command(*)plus explicitdenyrules for the dangerous forms, with the guard hooks as the real safety net (they run before the permission check and fail closed).--dangerously-skip-permissionsis the wrong tool: it also skips the ask / deny evaluation the deny rules need. - File writes follow the same rule: a write outside the workspace (a
mktempfile) is auto-denied;write_file(*)/read_file(*)with deny rules on~/.ssh,~/.gemini,~/.gitconfig*and.envlet the lane run; the brief should keep scratch files under.claude/super-board/inflight/. python3does not exist undercmd/ PowerShell on Windows (Store alias): hook commands and the brief'spython3 ...examples needpythonorpy -3there. A hook that fails to start blocks every matched tool call with a misleading reason.- The brief's
cmd1 && cmd2anda | python ...examples mislead the lane in PowerShell 5.1 (&&is a parse error there); the brief should say which shell it is in. guard-worktree-path.pydenies.claude/worktrees/...on Windows (🐛 [bug] guards: Windows Git Bash paths and prose trip three guard hooks #72):git rev-parseprintsC:/x,os.path.joinadds\.claude\worktrees,startswithnever matches.normcase(normpath(...))on both sides fixes it.guard-secrets.pyalso denies any Bash command whose text contains the wordcredentials(a commit message, for one).- Denials are structured: the JSON result carries
denied_actions: [{action, display_name}]; the lane should read that, not grep stderr (whose text is "auto-denied", not "soft-denied"). - Over quota is a hard failure (exit 3,
AGY_ERRORJSON on stderr,status: ERROR), not a soft denial: map it tohaltedwith a retry-after. Quota is per model:gemini-3.8-flashkept working while the main bucket was exhausted. - Two lanes that finished their work (card moved, comments posted, PR merged) still ended with no
structured_output, and the launcher reportedreturned no valid result; the raw run was not kept, so the cause (prose answer, a backendINTERNAL 500retry seen once, schema lost) cannot be told apart. Keep the raw run on failure and let reconcile re-read the card's column before calling a lane failed. - Required GitHub reads under PowerShell:
gh ... --jq "\(.headRefName)"from the brief is mangled by PowerShell quoting and fails 3 times, leaving agithub-halt-*.json; the lane went on regardless, so an exit-79 halt is not binding on a headless lane unless the launcher checks the halt file after the run. - Every run logs
You are not logged into Antigravitywarnings from a start-up cache refresh while proceeding with cached credentials; a sign-in check must not grep for them. super-board-setup.py fix/check --hostaccepts only claude and codex; onboard for Antigravity has to run it as--host claudeuntil it knowsagy.- The suite on Windows Git Bash, before any change:
tests/test-setup.shfails at the firstfix(setup.py hands a Windows path to bash and the backslashes vanish) andtests/test-install.shat "symlink layout" (no symlink rights). The CI matrix is the check there.
Activity
[-]✨ [feat] host: run the board on Google Antigravity (agy) as a fourth host[/-][+]✨ [feat] host: run the board on Google Antigravity (agy)[/+]