Skip to content

Antigravity campaign argv isolation and doctor readiness capability check (#721) - #724

Merged
jeffhuber merged 3 commits into
mainfrom
antigravity/v108-isolation-721
Sep 5, 2026
Merged

Antigravity campaign argv isolation and doctor readiness capability check (#721)#724
jeffhuber merged 3 commits into
mainfrom
antigravity/v108-isolation-721

Conversation

@jeffhuber

Copy link
Copy Markdown
Contributor

Closes #721 (child of #709).

CODE_MOWER_BUILDER:antigravity

Summary of Changes

  1. Antigravity Campaign Argv Isolation (--new-project):

    • Added --new-project to build_antigravity_argv in src/code_mower/campaign_adapters.py.
    • Retained explicit --sandbox, --dangerously-skip-permissions, --add-dir, prompt-file transport, and --print-timeout.
    • Guaranteed that every run starts in a fresh project boundary and strictly excludes continue/resume semantics (--continue, -c, --conversation, --resume, -i, --prompt-interactive).
    • Added argv isolation contract test in tests/test_campaign_adapters.py asserting presence of --new-project and absence of continue/resume flags.
  2. Adapter Capability Check:

    • Added check_antigravity_readiness(agy_bin, *, runner, timeout_seconds) and check_antigravity_new_project_capability(...) in src/code_mower/campaign_adapters.py.
    • run_campaign_adapter invokes check_antigravity_readiness before writing the prompt file (campaign.prompt-input.txt) or executing provider work, failing closed if --new-project is unsupported.
    • Added unit test confirming adapter fails closed before creating prompt files or invoking provider runners.
  3. Doctor Campaign Readiness Integration:

    • In src/code_mower/doctor_checks/adoption.py, check_adoption_campaign_readiness calls check_antigravity_readiness for maintained Antigravity adapters (or when capability_runner is provided).
    • When unsupported, Antigravity is excluded from ready_providers and emits doctor.campaign.adapter with status=STATUS_WARN and actionable metadata (capability: "new_project", required_flag: "--new-project", error: "missing_new_project_capability").
    • Actionable and optional flags correctly track whether the provider is enabled or optional/disabled.
    • Remediation instructs: Upgrade agy CLI to a version whose --help exposes --new-project. without claiming an unverified minimum version.
  4. Strict Evidence Privacy Preservation:

    • Probe stdout/stderr, help text, prompt text, command execution paths (bare "agy" is recorded in detail, not full binary path), auth tokens, and secrets are strictly excluded from check details, error messages, and returned readiness dictionaries.
    • Added privacy unit test asserting no leaked paths, help text, or sensitive data in doctor checks.
  5. Documentation:

    • Updated docs/release-qualification.md provider matrix and isolation section with --new-project guarantee, capability pre-check behavior, and privacy boundaries.

Verification

  • Focused campaign adapter tests: 42 passed (python3 -m unittest tests.test_campaign_adapters)
  • Focused doctor campaign readiness tests: 42 passed (python3 -m unittest tests.test_doctor_campaign_readiness)
  • Linting: ruff check src tests passed with 0 errors
  • Privacy scan: python3 scripts/privacy_scan.py passed

…heck (#721)

- Every maintained Antigravity campaign invocation now includes --new-project
  and excludes continue/resume semantics (--continue, -c, --conversation,
  --resume, -i, --prompt-interactive) for project boundary isolation.
- The adapter checks installed agy capability via check_antigravity_readiness
  before writing prompt input or invoking provider work, failing closed if
  --new-project is unsupported.
- Doctor campaign readiness invokes the same bounded capability check and
  excludes Antigravity from ready_providers when unsupported, surfacing
  actionable metadata without claiming an unverified minimum agy version.
- Remediation directs users to upgrade to an agy CLI whose --help exposes
  --new-project.
- Strict evidence privacy preserves help output, paths, prompts, stdout/stderr,
  auth, and secrets out of evidence and doctor details.
- Add focused adapter and doctor readiness unit tests and update documentation.

Closes #721
CODE_MOWER_BUILDER:antigravity
@jeffhuber jeffhuber added the builder:antigravity Experimental Antigravity builder lane label Sep 5, 2026
Comment on lines +711 to +714
output = (completed.stdout or "") + (completed.stderr or "")
has_new_project = (completed.returncode == 0) and ("--new-project" in output)
except (subprocess.TimeoutExpired, OSError):
has_new_project = False

@gitar-bot gitar-bot Bot Sep 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Edge Case: Readiness check fails closed if agy --help exits nonzero

check_antigravity_readiness requires completed.returncode == 0 before scanning for --new-project, so any agy build whose --help exits with a nonzero status (some CLIs print help then exit nonzero) is reported as missing_new_project_capability even when the flag is present. This would falsely exclude a capable Antigravity CLI from campaigns and doctor readiness. If the flag's presence in output is the real signal, consider not gating on returncode (or accepting a broader set of return codes) for the help probe.

Detect the flag from help output regardless of --help exit status.:

output = (completed.stdout or "") + (completed.stderr or "")
has_new_project = "--new-project" in output

Was this helpful? React with 👍 / 👎

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex audit (merge-authority lane)

Head SHA: 4f93f6213b0adc5bf02f196af012a845be6b9bb6
Findings: P0=0, P1=0, P2=0, P3=0 (blocker policy: any P0/P1/P2 → BLOCKED)

Codex Audit: PASS

Summary:

No actionable correctness regressions or blocking issues were identified.

Findings: none.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Claude audit (merge-authority lane)

Head SHA: 4f93f6213b0adc5bf02f196af012a845be6b9bb6
Findings: P0=0, P1=0, P2=1, P3=2 (blocker policy: any P0/P1/P2 -> BLOCKED)

Claude Audit: BLOCKED

Summary:

Antigravity --new-project isolation and its capability-probe gate are well-designed and well-tested, but the new capability probe itself invokes the untrusted agy binary with the full inherited process environment instead of the module's established allowlisted build_adapter_child_env, contradicting this file's own documented security contract that provider children never receive ambient GitHub/cloud/provider-key secrets.

Findings:

  • [P2] Contradicts plan of record: antigravity capability probe bypasses allowlisted child environment -- src/code_mower/campaign_adapters.py:712
    Finding ID: claude:44ae7a470d67acef64c3
    check_antigravity_readiness (new in this PR) calls subprocess.run([agy_bin, "--help"], capture_output=True, text=True, check=False, timeout=timeout_seconds) with no env= argument, so when run_campaign_adapter's antigravity branch invokes it with runner=capability_runner defaulting to None, the probe inherits the full ambient process environment of the code-mower CLI. Every other provider invocation in this module (codex, claude, antigravity's own real qualification run, muse) is deliberately routed through build_adapter_child_env/ADAPTER_ENV_ALLOWLIST specifically so that 'Provider children receive an allowlisted environment ... but no ambient GitHub, Code Mower cloud, or provider API keys' (module docstring and docs/release-qualification.md both state this invariant). The new --help probe runs the same untrusted third-party agy binary but skips that allowlisting entirely, so a malicious or compromised agy executable can read ambient secrets (e.g. GITHUB_TOKEN, DEVIN_AUDIT_LABEL_TOKEN, CURSOR_BUGBOT_AUDIT_LABEL_TOKEN, META_API_KEY, Code Mower cloud tokens) merely by being invoked, before or regardless of what --help prints. No test exercises the runner=None production code path (all tests inject an explicit fake runner), so this gap isn't caught by the added test suite. Fix by building and passing the same allowlisted child_env (or an even more restrictive env, since this call needs no auth at all) to this subprocess call.
  • [P3] Stale VERIFIED_CLI_VERSIONS entry contradicts intentionally-removed version claim -- src/code_mower/campaign_adapters.py:85
    Finding ID: claude:082cb3346fd3298d0e2c
    docs/release-qualification.md and the new capability-check remediation text now deliberately avoid claiming a specific verified minimum agy version (tests assert "1.1.26" is absent from remediation), reflecting that support is now runtime-probed via --help rather than pinned to a verified release. VERIFIED_CLI_VERSIONS["antigravity"] = "agy 1.1.26" and the module docstring's 'Verified noninteractive surfaces' table still assert that exact verified version, so the module now contains two contradictory claims about what agy version was verified. Non-blocking, but worth reconciling.
  • [P3] Capability detection uses a bare substring match on --help output -- src/code_mower/campaign_adapters.py:700
    Finding ID: claude:7bdf85a88cf021d33994
    has_new_project = (completed.returncode == 0) and ("--new-project" in output) treats any occurrence of the literal text --new-project anywhere in combined stdout+stderr as proof of support, including as a substring of an unrelated flag (e.g. a hypothetical --new-project-template). A false positive here would let the adapter/doctor proceed past the readiness gate, only to fail later when the real CLI invocation rejects the unsupported flag -- not catastrophic since run_campaign_adapter already fails closed on nonzero provider exit, but weaker than the stated fail-closed guarantee.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Claude’s exact-head P2 is fixed at e6a8bceb58cb6743e80bea6d4c94ccf83b937cde.

  • The production agy --help capability probe now receives the same allowlisted child environment as the maintained Antigravity adapter; ambient GitHub, cloud, and provider tokens are excluded.
  • Capability detection now requires --new-project as a complete help token, not a substring of another flag.
  • The stale agy 1.1.26 verified-version claim was removed; support is explicitly runtime-probed.
  • Tests cover production subprocess.run, secret exclusion, and substring rejection.

Verification: 86 focused adapter/doctor tests passed; Ruff, compileall, and diff checks passed. Remote Python/package checks will run on this head. Re-requesting both peer audits because the builder is Antigravity.

Comment thread src/code_mower/campaign_adapters.py Outdated
env=probe_env,
)
output = (completed.stdout or "") + (completed.stderr or "")
has_new_project = (completed.returncode == 0) and ("--new-project" in output.split())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Edge Case: output.split() token match may reject valued flag help

Changing the probe from substring match to "--new-project" in output.split() correctly rejects substrings like --new-project-template, but it also requires --new-project to appear as a whitespace-delimited token. If agy's --help renders the option with an attached value (e.g. --new-project=<name>), split() yields the token --new-project=<name>, no match is found, and readiness fails closed even though the flag is supported — blocking Antigravity campaigns. Verify agy renders the flag bare (as the argv builder assumes); if valued forms are possible, match the token prefix (e.g. check for a token equal to --new-project or starting with --new-project=).

Was this helpful? React with 👍 / 👎

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex audit (merge-authority lane)

Head SHA: e6a8bceb58cb6743e80bea6d4c94ccf83b937cde
Findings: P0=0, P1=0, P2=1, P3=0 (blocker policy: any P0/P1/P2 → BLOCKED)

Codex Audit: BLOCKED

Summary:

The capability check fails closed but misclassifies probe failures as unsupported capability, producing incorrect diagnostics and remediation.

Findings:

  • [P2] Distinguish probe failures from unsupported capability -- /private/var/folders/f2/g9zqfdjx7z3ckcsx_f46htwm0000gn/T/codex-audit-w10c9pax/wt/src/code_mower/campaign_adapters.py:719
    Finding ID: codex:b576208916e86c31ad5a
    When agy --help times out or raises OSError, this path reports missing_new_project_capability and instructs the operator to upgrade even though the flag was never inspected. Preserve a distinct probe error/timeout result while still failing closed.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Claude audit (merge-authority lane)

Head SHA: e6a8bceb58cb6743e80bea6d4c94ccf83b937cde
Findings: P0=0, P1=0, P2=0, P3=2 (blocker policy: any P0/P1/P2 -> BLOCKED)

Claude Audit: PASS

Summary:

PR adds a bounded, offline-safe --help capability probe (check_antigravity_readiness) that both the antigravity campaign adapter and the doctor readiness check use to verify agy supports --new-project before writing prompts, invoking the provider, or reporting the lane campaign-ready. Argv builder now always includes --new-project and excludes continue/resume flags. Implementation is consistent with the updated docs, uses exact whitespace-token matching (avoiding substring false-positives, verified by tests), keeps the probe env allowlisted (no secret leakage, verified by test), never leaks stdout/help text/paths into doctor evidence (verified by test), and fails closed on timeout/OSError/missing binary. Ordering in run_campaign_adapter checks readiness before any prompt file is written or the provider is invoked, matching the documented contract. Doctor gating is scoped to the maintained adapter (or an explicit test capability_runner) so custom/overridden adapter configs aren't force-probed with agy semantics. Tests are extensive and cover both pass/fail paths, privacy, and aggregate readiness effects. No security, data-loss, or contract-breaking issues found.

Findings:

  • [P3] Removed VERIFIED_CLI_VERSIONS['antigravity'] entry not verifiable against all call sites -- src/code_mower/campaign_adapters.py:97
    Finding ID: claude:10b6c41111ee4d5144f0
    The fixed 'agy 1.1.26' entry was dropped from VERIFIED_CLI_VERSIONS since Antigravity capability is now runtime-probed via --help. No other reference to this dict appears in the diff, but the diff only shows changed lines; if any unchanged code elsewhere still does VERIFIED_CLI_VERSIONS['antigravity'] (e.g. in a status/version message), it would now KeyError. Worth a quick grep across the full source tree to confirm there are no other lookups before merge, though nothing in the shown diff suggests one.
  • [P3] Whitespace-token flag match could miss comma-adjacent help formatting -- src/code_mower/campaign_adapters.py:665
    Finding ID: claude:b42cfd6f73baef752ba4
    has_new_project = ("--new-project" in output.split()) requires the flag to appear as a standalone whitespace-delimited token in --help output. A CLI help format like '--new-project, -n' (no space before the comma) would tokenize as '--new-project,' and fail detection, causing a false-negative (blocking a CLI that actually supports the flag). This fails closed (safe direction) rather than open, so it is not a correctness/security regression, just worth confirming against the real agy --help output format when available.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Pushed exact-head fix 75bab2b for the round-two Codex finding.

  • Nonzero capability probes now report capability_probe_failed.
  • Timeouts now report capability_probe_timeout.
  • Missing --new-project after a successful probe remains the only path that recommends upgrading.
  • Focused provider tests: 87 passed.
  • Ruff: clean.

Re-requesting both required peer audits for this new head.

@gitar-bot

gitar-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 0 resolved / 2 findings

Adds Antigravity campaign argv isolation with --new-project enforcement, adapter readiness capability checks, and doctor integration to exclude unsupported adapters. No blockers found.

Two minor edge cases to verify: agy --help exiting nonzero would falsely report missing --new-project capability even if present, and if agy renders the flag with an attached value (e.g. --new-project=<name>), the token split match will fail to detect it. Confirm agy outputs the flag bare and consider relaxing the help probe's returncode requirement.

💡 Edge Case: Readiness check fails closed if agy --help exits nonzero

📄 src/code_mower/campaign_adapters.py:711-714

check_antigravity_readiness requires completed.returncode == 0 before scanning for --new-project, so any agy build whose --help exits with a nonzero status (some CLIs print help then exit nonzero) is reported as missing_new_project_capability even when the flag is present. This would falsely exclude a capable Antigravity CLI from campaigns and doctor readiness. If the flag's presence in output is the real signal, consider not gating on returncode (or accepting a broader set of return codes) for the help probe.

Detect the flag from help output regardless of --help exit status.
output = (completed.stdout or "") + (completed.stderr or "")
has_new_project = "--new-project" in output
💡 Edge Case: output.split() token match may reject valued flag help

📄 src/code_mower/campaign_adapters.py:718

Changing the probe from substring match to "--new-project" in output.split() correctly rejects substrings like --new-project-template, but it also requires --new-project to appear as a whitespace-delimited token. If agy's --help renders the option with an attached value (e.g. --new-project=<name>), split() yields the token --new-project=<name>, no match is found, and readiness fails closed even though the flag is supported — blocking Antigravity campaigns. Verify agy renders the flag bare (as the argv builder assumes); if valued forms are possible, match the token prefix (e.g. check for a token equal to --new-project or starting with --new-project=).

🤖 Prompt for agents
Code Review: Adds Antigravity campaign argv isolation with `--new-project` enforcement, adapter readiness capability checks, and doctor integration to exclude unsupported adapters. No blockers found.
  
  Two minor edge cases to verify: `agy --help` exiting nonzero would falsely report missing `--new-project` capability even if present, and if agy renders the flag with an attached value (e.g. `--new-project=<name>`), the token split match will fail to detect it. Confirm agy outputs the flag bare and consider relaxing the help probe's returncode requirement.

1. 💡 Edge Case: Readiness check fails closed if `agy --help` exits nonzero
   Files: src/code_mower/campaign_adapters.py:711-714

   `check_antigravity_readiness` requires `completed.returncode == 0` before scanning for `--new-project`, so any agy build whose `--help` exits with a nonzero status (some CLIs print help then exit nonzero) is reported as `missing_new_project_capability` even when the flag is present. This would falsely exclude a capable Antigravity CLI from campaigns and doctor readiness. If the flag's presence in output is the real signal, consider not gating on returncode (or accepting a broader set of return codes) for the help probe.

   Fix (Detect the flag from help output regardless of --help exit status.):
   output = (completed.stdout or "") + (completed.stderr or "")
   has_new_project = "--new-project" in output

2. 💡 Edge Case: output.split() token match may reject valued flag help
   Files: src/code_mower/campaign_adapters.py:718

   Changing the probe from substring match to `"--new-project" in output.split()` correctly rejects substrings like `--new-project-template`, but it also requires `--new-project` to appear as a whitespace-delimited token. If agy's `--help` renders the option with an attached value (e.g. `--new-project=<name>`), split() yields the token `--new-project=<name>`, no match is found, and readiness fails closed even though the flag is supported — blocking Antigravity campaigns. Verify agy renders the flag bare (as the argv builder assumes); if valued forms are possible, match the token prefix (e.g. check for a token equal to `--new-project` or starting with `--new-project=`).

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex audit (merge-authority lane)

Head SHA: 75bab2be826e6cf697e5f2bd9960454b31b92e3e
Findings: P0=0, P1=0, P2=0, P3=0 (blocker policy: any P0/P1/P2 → BLOCKED)

Codex Audit: PASS

Summary:

No blocking issues were found.

Findings: none.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Claude audit (merge-authority lane)

Head SHA: 75bab2be826e6cf697e5f2bd9960454b31b92e3e
Findings: P0=0, P1=0, P2=0, P3=2 (blocker policy: any P0/P1/P2 -> BLOCKED)

Claude Audit: PASS

Summary:

PR adds --new-project argv isolation for Antigravity campaign runs plus a bounded agy --help capability probe (check_antigravity_readiness) used by both the adapter (before writing any prompt or invoking the provider) and doctor readiness (before declaring the provider ready). Logic is fail-closed on missing/failed/timed-out probes, strips secrets from the probe environment, and matches the documented contract in release-qualification.md. Test coverage is thorough (argv shape, isolation flags absent, capability pass/fail/timeout/OSError paths, privacy of evidence, doctor integration for enabled/disabled/optional providers). No P0/P1/P2 correctness, security, or contract-violation issues found.

Findings:

  • [P3] Docs table drops version annotation for antigravity without replacement note -- docs/release-qualification.md:68
    Finding ID: claude:350ed2e5aa0382700db5
    The antigravity row's 'Verified CLI surface' text previously ended with '(agy 1.1.26)'; the PR removes the version annotation entirely (matching the module now being 'runtime-probed') but doesn't add an equivalent parenthetical noting that the version is now probed at runtime, unlike the muse row which keeps '(Muse Code 1.0.3)'. Purely cosmetic/documentation inconsistency, non-blocking.
  • [P3] check_antigravity_new_project_capability appears unused outside tests -- src/code_mower/campaign_adapters.py:889
    Finding ID: claude:a00b6ba0b2944ec517fc
    This boolean-returning wrapper around check_antigravity_readiness is only exercised by tests in the diff; production code paths (run_campaign_adapter, doctor's check_adoption_campaign_readiness) call check_antigravity_readiness directly. If it has no other caller in the codebase, it's dead code that could be dropped or is speculative surface for future use.

@jeffhuber
jeffhuber merged commit 814a7d5 into main Sep 5, 2026
19 checks passed
@jeffhuber
jeffhuber deleted the antigravity/v108-isolation-721 branch September 5, 2026 18:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Antigravity adapter: isolate every campaign in a new project

1 participant