Skip to content

fix(providers): retire Antigravity to a non-runnable tombstone with a Codewhale-owned clear path (PROD-002) - #5739

Open
Hmbown wants to merge 6 commits into
mainfrom
fix/prod-002-antigravity-docs-20260830
Open

fix(providers): retire Antigravity to a non-runnable tombstone with a Codewhale-owned clear path (PROD-002)#5739
Hmbown wants to merge 6 commits into
mainfrom
fix/prod-002-antigravity-docs-20260830

Conversation

@Hmbown

@Hmbown Hmbown commented Aug 30, 2026

Copy link
Copy Markdown
Owner

What (PRD §4.4 PROD-002, ratified)

Antigravity is no longer a selectable provider anywhere. What remains is a non-runnable legacy tombstone so old config still parses, plus one Codewhale-owned clear path — nothing touches the user's Google/Antigravity session.

  • Deleted the private OAuth reader (crates/tui/src/agy_credentials.rs, incl. the state.vscdb reader and AGY_ADC_AUTH precedence) and the private cloud-code transport (crates/tui/src/client/cloud_code.rs, incl. the ignored live test and the /v1internal URL builder); removed WireDialect::GoogleCloudCode / RouteShape::CloudCode, the CloudCodeSystemPromptUnsupported message in all 15 locale packs, and every Antigravity branch in client.rs, role_placement.rs, turn_loop.rs.
  • ProviderKind::Antigravity stays as a deserialize-only tombstone (serde alias agy), removed from ProviderKind::ALL (42→41), catalog export, providers_sorted_for_display, parse_route_kind, golden route ids and the providers-export golden. Selection via CLI (--provider), TUI (/provider, picker, /model), config, env (ANTIGRAVITY_API_KEY, ANTIGRAVITY_BASE_URL/MODEL removed), and route resolution all fail closed before any I/O with one message that points Gemini users to google + GEMINI_API_KEY. doctor no longer reports it.
  • codewhale auth clear --provider antigravity|agy is the only surface that accepts the identity: it clears only [providers.antigravity]/[providers.agy], fallback entries, the top-level selection, and Codewhale's own secret-store slot, with secret rollback on save failure, and scrubs the sibling .bak via toml_edit. No external path is resolved, no env credential read, no revoke call.
  • Docs: docs/PROVIDERS.md gains one "Legacy Antigravity tombstone" section (clear command, no-session-alteration statement, google + GEMINI_API_KEY); antigravity rows removed from the selection list, wire/env, credential, and registry tables; docs/CONFIGURATION.md provider lists updated; website facts filters (web/scripts/facts-lib.mjs, web/lib/facts-drift.ts) now actually apply their EXCLUDED set.
  • Drift guard: scripts/check-provider-registry.py (+272) now parses ProviderKind::ALL, asserts no legacy id is selectable/documented, that ProviderKind::parse and ApiProvider::catalog derive from ALL, that the tombstone section exists exactly once with the safety copy and no login/OAuth guidance, and forbids ANTIGRAVITY_API_KEY, AGY_ADC_AUTH, cloudcode-pa, state.vscdb, [providers.antigravity] across the public docs/facts.

Evidence (level 2, post-merge of main 2f200ee03)

  • cargo test -p codewhale-configtest result: ok. 627 passed; 0 failed; 1 ignored
  • cargo test -p codewhale-tui --lib -- antigravity config::tests::api_provider provider_readtest result: ok. 138 passed; 0 failed
  • cargo test -p codewhale-cli antigravitytest result: ok. 3 passed; 0 failed (incl. antigravity_clear_removes_only_codewhale_owned_legacy_state, which plants an external-session trap file and asserts it is byte-identical afterwards, and …_restores_codewhale_secret_when_config_write_fails)
  • clippy -D warnings with the CI allow-list clean on config/tui/cli; cargo fmt --check clean; python3 scripts/check-provider-registry.py → passed.
  • Three independent adversarial reviews (boundary-correctness, safety-scope, completeness-docs): approve, no non-minor findings.

Stat: 54 files changed, 1289 insertions(+), 1276 deletions(-). HEAD: a44c68b57408288aafc1fb63df7f06e5512d4296.

Unproven (deliberately)

Real TUI picker//model//provider rendering with a legacy config on disk (unit-level only); codewhale doctor and auth clear against a real user config dir (temp-dir tests only); website build/deploy and facts regeneration beyond check-facts; npm wrapper/packaging/release artifacts; hosted CI (this PR); the full 11.6k-test tui suite (a 138-test filtered slice ran).

No provider calls were made, no user session altered, no deploy, no publish, no real credentials handled.

No-Issue: ratified PRD PROD-002 retirement (codewhale-ops PRODUCT_PRD.md §4.4); supersedes the prior writer's uncommitted work (snapshot ef88c88cb)

🤖 Generated with Claude Code

https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX

CodeWhale Bot added 6 commits August 30, 2026 10:07
…avity tombstone work

Snapshot only — not yet compiled/tested end to end. Retains the prior
writer's deletion of agy_credentials.rs and client/cloud_code.rs, the
ProviderKind compatibility tombstone, docs/locale/web facts edits, and the
provider-registry drift checker tightening. Finished in follow-up commits.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
…ity-docs-20260830

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

# Conflicts:
#	docs/PROVIDERS.md
…tone

`ProviderKind::parse` is catalog-only (ALL excludes Antigravity), so the
registry alias `agy` no longer resolved anywhere: `parse_config_identity`
matched only ids/config keys before falling back to `parse`, returning
None for `agy`. That let `config set providers.agy.base_url` mint a
custom `[providers.agy]` table that serde folds back onto the legacy
slot, and left `/provider agy` / `CODEWHALE_PROVIDER=agy` on the generic
unknown-provider path instead of the tombstone message.

`parse_config_identity` now falls through to `parse_retired_alias`,
which consults aliases only for registry entries absent from ALL, so
catalog aliases keep precedence and `parse("agy")` stays None.

Tests: the legacy `[providers.agy]` spelling parses onto the tombstone
slot with no extras table; every `set_value` spelling bails with the
google + GEMINI_API_KEY message and writes nothing; the tombstone is
absent from ALL, names_hint, auth methods, and env vars.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
…ctor and /model

`Config::api_provider()` never returned `ApiProvider::Antigravity`: since
`ApiProvider::parse` rejects the retired identity, a persisted
`provider = "antigravity"` fell through the base-URL sniff to DeepSeek,
so the fail-closed branches in `DeepSeekClient::from_parts`,
`deepseek_api_key_with_secret_store_mode`, and
`resolve_runtime_route_for_identity` were unreachable and a legacy config
would silently run as DeepSeek past `validate()`. The legacy selection
(either spelling) now resolves to its own tombstone identity.

`credential_state_for_provider` classifies the tombstone as Legacy so a
leftover `[providers.antigravity].api_key` never reads as Saved and
`/model` lists no candidate. `codewhale doctor` iterates
`doctor_api_key_providers()`, which filters the retired slot out of the
API Keys rows.

Tests: env selection via CODEWHALE_PROVIDER, `/provider antigravity|agy`,
and `provider setup` all return the tombstone naming google +
GEMINI_API_KEY; with ANTIGRAVITY_API_KEY and AGY_ADC_AUTH set and a
legacy api_key in the table, readiness is Legacy, the inventory has no
Antigravity candidate, and no client can be constructed.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
…ift guard

- CHANGELOG (root + tui): Unreleased entry for the non-runnable
  tombstone, the scope of `codewhale auth clear --provider antigravity`
  (Codewhale-owned state only; never a Google/Antigravity session), and
  the supported `google` + GEMINI_API_KEY Gemini route.
- config.example.toml: add the `[providers.google]` example so Gemini
  users have an example-config path.
- scripts/check-provider-registry.py: also scan README.md and
  config.example.toml for retired identities and forbidden markers,
  require the `google` example table with GEMINI_API_KEY, and assert the
  runtime contract in source (retired-alias resolution in
  `parse_config_identity`, Legacy readiness for the tombstone, and the
  filtered doctor API Keys loop).

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

@codewhale-agent codewhale-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Codewhale reviewed the changed lines in this pull request. This is a comment-only review, not an approval.

Left 1 comment on changed lines.

{"blocker":1,"concern":0,"nit":0,"model":"skipped","policy":"default"}

Comment thread config.example.toml
# Provider aliases: google, gemini, google-gemini, ai-studio
# Env var aliases: GEMINI_API_KEY, GOOGLE_API_KEY, GEMINI_BASE_URL, GOOGLE_BASE_URL
[providers.google]
# api_key = "YOUR_GEMINI_API_KEY"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Blocker This changed line looks like a hardcoded secret.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 30, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-30T18:22:36.958873Z a44c68b PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 32s —— View job


Claude finished reviewing PR #5739

  • Gather context (diff against origin/main, read CLAUDE.md/AGENTS.md)
  • Understand the request (code review of Antigravity retirement PR)
  • Review correctness bugs / panics / non-exhaustive matches
  • Review provider/model/route safety (v0.8.65 EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608 invariant)
  • Review reuse/simplification/dead code
  • Review test coverage
  • Review security (secret handling)
  • Post final review

Summary

This is a careful, well-scoped retirement of the Antigravity provider to a non-runnable tombstone. I read the full diff against origin/main (54 files) and traced every entry point that could touch the tombstone: CLI (--provider, auth clear), TUI (/provider, /model, picker, config set), env vars (CODEWHALE_PROVIDER, ANTIGRAVITY_*, AGY_ADC_AUTH), route resolution (RouteResolver::route, parse_route_kind), doctor, and config.validate(). Every one fails closed before I/O, and each closure point is backed by a matching unit test (crates/config/src/tests.rs, crates/tui/src/config/tests.rs, crates/tui/src/commands/groups/core/provider.rs, crates/cli/src/lib.rs). No dangling references remain to the deleted agy_credentials/cloud_code modules, WireDialect::GoogleCloudCode, or RouteShape::CloudCode — confirmed via grep across crates/.

1. Correctness / non-exhaustive matches — No issues found. All match arms over WireDialect/RolePlacement that previously handled GoogleCloudCode were removed cleanly and compile as exhaustive (verified by reading every match site in client.rs, role_placement.rs, prepared.rs). The clear_legacy_antigravity_config rollback path (crates/cli/src/lib.rs:2536) correctly restores in-memory config and the secret-store slot on either a secret-delete failure or a store.save() failure, and the added test antigravity_clear_restores_codewhale_secret_when_config_write_fails exercises the failure branch directly (forces a config-write failure by making the target path a directory).

2. Provider/model/route safety (EPIC #2608) — No violations. RouteResolver::route (crates/config/src/route/resolver.rs:151) rejects ProviderKind::Antigravity from req.explicit_provider only — never inferred from a model-string prefix or freeform text. ProviderKind::parse excludes retired kinds by construction (only iterates Self::ALL), while parse_config_identity explicitly documents why it still resolves the agy alias (so auth clear can name the tombstone) via a separate parse_retired_alias helper gated to non-catalog entries. Config::api_provider() has a clear comment explaining why the legacy selection must resolve to its own ApiProvider::Antigravity identity rather than falling through to a base-URL sniff (which could otherwise silently run a legacy provider = "antigravity" config as DeepSeek).

3. Reuse / simplification — One minor nit: initial_stream_error_user_message and preview_request_error_user_message in crates/tui/src/core/engine/turn_loop.rs:53-66 still take a _locale_tag: &str parameter that is now entirely unused (the only consumer, CloudCodeSystemPromptUnsupported localization, was removed). Not a bug, but the parameter and its locale_tag arguments at the two call sites (turn_loop.rs:1373, preview.rs:480) could be dropped for a slightly cleaner diff. Non-blocking.

4. Tests — Coverage is unusually thorough for a retirement PR: alias parsing (antigravity/agy/AGY case-insensitivity), every CLI subcommand rejection, /provider and /model command rejection, config.validate(), CODEWHALE_PROVIDER env rejection, credential-state classification (Legacy, never Saved even with a leftover api_key), model-inventory exclusion, client construction failure, and two auth-clear tests that plant an external-session trap file and byte-diff it afterward to prove no external state is touched. scripts/check-provider-registry.py adds structural + prose-content drift guards tying docs, code, and website facts together. I did not find gaps worth calling out.

5. Security — Secret handling in clear_legacy_antigravity_config snapshots the prior secret before any mutation, only ever touches the antigravity keyring slot, and rolls back correctly on either failure path, including a check for concurrent modification during rollback (returns an error rather than silently overwriting). ANTIGRAVITY_API_KEY/AGY_ADC_AUTH are no longer read anywhere (confirmed by grep and by the new test retired_antigravity_credentials_are_never_read_and_no_client_is_built).

Good choices worth calling out: the parse_retired_alias/parse_config_identity split keeps the "selectable catalog never includes the tombstone" invariant structurally enforced rather than relying on scattered exclusion checks, and the .invalid TLD default base URL (https://legacy-antigravity.invalid) is a nice belt-and-suspenders touch against any residual code path that might attempt a real network call.

No blocking findings. CI was still in progress (CI, Codewhale PR Review, OpenHarmony pending) at review time with 0 failures so far.
· Branch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant