fix(#3738): antigravity global skills/agents install to ~/.gemini/config (#3921)

* test(#3738): antigravity global skills/agents must resolve under ~/.gemini/config

Regression tests (RED first): --skills-root and gsd-tools query surfaces,
install-plan dest dirs, and converter skills-path rewrite.

* fix(#3738): antigravity global skills/agents install to ~/.gemini/config

Antigravity's machine-local discovery scans ~/.gemini/config/{skills,agents};
the configHome (~/.gemini/antigravity) is deprecated for artifacts. Declare the
ADR-1239 skills/agents 'home' override on the antigravity global layout — the
same mechanism codex uses (.agents) — and divert ~/.claude/skills/ references
in converted global content to ~/.gemini/config/skills/. configHome, settings,
probe/migration semantics, and the local .agents layout are unchanged.

* fix(#3738): retire deprecated configHome artifacts via installer migration 010

Next install converges an existing antigravity install: manifest-managed
skills/gsd-*/ and agents/gsd-*.md under the configHome (a location AGY does
not scan) are removed — modified files backed up first, unmanifested and
non-gsd entries preserved — and now-empty containers retired. Global scope
only; the local .agents surface is live. Docs + inventory updated.

* fix(#3738): converter sync in bin/install.js, harness emit-root coverage, migration baseline

- bin/install.js converter gains the same ~/.claude/skills → ~/.gemini/config/
  rewrite as src (ADR-1508 dual copy must stay in sync).
- Parity-manifest walk covers home-override emit roots (extraEmitRootsFor) so
  antigravity's emitted skills/agents stay differential-visible at their new
  install root; install-tree fixture regen confirms an unchanged key set.
- skills-from-commands rule declares the antigravity converter as a
  runtime-scoped transform; one ack fragment covers the identity-classed
  workflow whose antigravity copy embeds the old skills path.
- Migration 010 checksum baseline + home-override set doc updated; existing
  tests updated to the #3738 contract (global dest, golden parity via layout
  dest, integration expectations).

* fix(#3738): tolerate an absent extra emit root on baseline-side measurement

The base tree's installer predates the home override, so <HOME>/.gemini/config
does not exist there; walk() threw ENOENT and the in-job baseline build failed.
An absent extra root is the legitimate pre-override shape — skip it.

* fix(#3738): review findings — manifest agents root, bare skills-path rewrite, guard comment

- writeManifest resolves the agents-kind home override (_kindDestDirSafe), so
  the manifest records agents at their actual install root and drift detection
  keeps working (isolated review finding 1, major).
- Converter bare forms ~/.claude/skills and $HOME/.claude/skills (no trailing
  slash) divert to ~/.gemini/config/skills instead of falling through to the
  retired configHome path (finding 2).
- real-home-guard comment updated: antigravity's global agents kind is the
  first agents-kind home override (finding 3, doc-only).
- Regression tests for both behavioral findings.

* chore(#3738): changeset fragment (pr number backfilled after PR creation)

* chore(#3738): backfill changeset PR number (3921)

* fix(#3738): sandbox HOME in tests that install antigravity global artifacts

antigravity is the first home-override runtime in the golden-parity and
skills-wrapper suites (codex is not in their runtime lists), so those tests
never needed HOME sandboxing — the real-home guard now (correctly) refuses
their un-sandboxed global installs on CI, where HOME is the passwd home.

* fix(#3738): stop the K3 sequential-sandbox env leak; sandbox L2's home-override plans

K3's two back-to-back sandboxHome calls leave HOME pointing at the first
sandbox once the after-hooks restore (each call saves the env as it found
it, so the second saves the first's sandbox as 'original'). On the windows
matrix that leaked gsd-k3-qwen-* home into the L2 property, whose
antigravity/global run then (correctly) refused via the #3712 real-home
guard — antigravity is the runtime that made L2's plan escape into
os.homedir(). K3 now manages the env with a single restore; L2 sandboxes
HOME per run, mirroring L1.

* fix(#3738): L2 property's HOME sandbox must exist on disk

The #3712 guard's sandbox exemption fails closed when identify(effectiveHome)
is 'absent' — L2 never created its configDir, so on the windows matrix (tmpdir
under the real home) the antigravity/global run refused even with HOME
sandboxed. Create the per-run sandbox dir and clean it up.

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-27 02:24:03 -04:00
committed by GitHub
parent e20744eacb
commit 39673ae9ff
29 changed files with 936 additions and 55 deletions

View File

@@ -680,6 +680,21 @@ function _kindDestDir(layout, kindName, targetDir) {
return path.join(kind.home || targetDir, kind.destSubpath);
}
/**
* #3738: scope-aware, layout-resolving wrapper over _kindDestDir for callers
* that have (runtime, configDir, scope) rather than a resolved Layout — the
* writeManifest agents surface being the first. Never throws: a runtime whose
* layout cannot be resolved (unknown id, descriptor error) keeps the caller's
* own fallback rather than losing the manifest.
*/
function _kindDestDirSafe(runtime, configDir, scope, kindName) {
try {
return _kindDestDir(resolveRuntimeArtifactLayout(runtime, configDir, scope), kindName, configDir);
} catch {
return null;
}
}
/**
* #3664 — warn (never refuse) when `--config-dir` points the install at a
* directory whose agent destination already holds FOREIGN (non-GSD) agent
@@ -2331,7 +2346,8 @@ function convertClaudeAgentToCopilotAgent(content, isGlobal = false) {
/**
* Apply Antigravity-specific content conversion — path replacement + command name conversion.
* Path mappings depend on install mode:
* Global: ~/.claude/ → ~/.gemini/antigravity/, ./.claude/ → ./.agents/
* Global: ~/.claude/skills/ → ~/.gemini/config/skills/ (#3738),
* ~/.claude/ → ~/.gemini/antigravity/, ./.claude/ → ./.agents/
* Local: ~/.claude/ → .agents/, ./.claude/ → ./.agents/
* Applied to ALL Antigravity content (skills, agents, engine files).
* @param {string} content - Source content to convert
@@ -2340,6 +2356,19 @@ function convertClaudeAgentToCopilotAgent(content, isGlobal = false) {
function convertClaudeToAntigravityContent(content, isGlobal = false) {
let c = content;
if (isGlobal) {
// #3738: global skills install under ~/.gemini/config/skills (the dir AGY
// scans for global discovery), so skills-path references must divert there
// — BEFORE the configHome rewrite below, which is correct for gsd-core
// runtime-file references (settings, workflows, VERSION) but wrong for the
// skills dir itself. Mirrors src/runtime-artifact-conversion.cts (ADR-1508
// keeps bin/install.js hand-authored; the two copies must stay in sync).
c = c.replace(/\$HOME\/\.claude\/skills\//g, '$HOME/.gemini/config/skills/');
c = c.replace(/~\/\.claude\/skills\//g, '~/.gemini/config/skills/');
// Bare skills form (no trailing slash) — must also precede the generic
// slash rule, which would otherwise divert it to the retired configHome
// path ($HOME/.gemini/antigravity/skills).
c = c.replace(/\$HOME\/\.claude\/skills\b/g, '$HOME/.gemini/config/skills');
c = c.replace(/~\/\.claude\/skills\b/g, '~/.gemini/config/skills');
c = c.replace(/\$HOME\/\.claude\//g, '$HOME/.gemini/antigravity/');
c = c.replace(/~\/\.claude\//g, '~/.gemini/antigravity/');
// Bare form (no trailing slash) — must come after slash form to avoid double-replace
@@ -9636,7 +9665,14 @@ function writeManifest(configDir, runtime = DEFAULT_RUNTIME, options = {}) {
const resolvedScope = options.scope === 'local' ? 'local' : 'global';
const codexSkillsDir = _resolveSkillsRootDir(runtime, configDir, resolvedScope);
const codexSkillsManifestPrefix = _hostBehaviors(runtime).skillsManifestPrefix || 'skills/';
const agentsDir = path.join(configDir, 'agents');
// #3738: resolve the ACTUAL agents-install dir honoring an agents-kind `home`
// override (antigravity global → $HOME/.gemini/config/agents), mirroring
// _resolveSkillsRootDir for skills. Hardcoding configDir/agents left the
// manifest blind to the whole agents surface the moment the override landed —
// no drift detection, no patch backup. Falls back to <configDir>/agents.
const agentsDir = _kindDestDirSafe(runtime, configDir, resolvedScope, 'agents')
|| _kindDestDirSafe(runtime, configDir, resolvedScope, 'kimi-agents')
|| path.join(configDir, 'agents');
const manifest = {
// Schema version of this DOCUMENT (#2872) — distinct from `version`
// below, which is the GSD package version. Absent ⇒ a pre-#2872 (v1)