* 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>
76 lines
3.7 KiB
JavaScript
76 lines
3.7 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
/**
|
|
* Standalone golden-fixture generator for tests/golden-install-tree (#2267 Phase 2).
|
|
*
|
|
* This is a BUILD-TIME generation script — NOT a test run. It imports the
|
|
* canonical buildInstallTree builder from tests/helpers/install-shared.cjs,
|
|
* which in turn reuses buildParityManifest's exact exclusion set (issue
|
|
* #2266) so the file-set fixtures here and the golden-install-parity content
|
|
* fixtures never diverge on which files they cover. The authoritative test
|
|
* gate remains `gsd-test run`, never a local `node --test`.
|
|
*
|
|
* Usage: node scripts/gen-install-tree-fixtures.cjs [runtime ...]
|
|
*/
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const { execFileSync } = require('node:child_process');
|
|
|
|
const ROOT = path.resolve(__dirname, '..');
|
|
// buildInstallTree (and the exclusion constants it reuses from
|
|
// buildParityManifest) is the canonical single source of truth in
|
|
// tests/helpers/install-shared.cjs (issue #2266/#2267) — this generator does
|
|
// not keep its own inline copy of the walk/exclusion logic.
|
|
const { runMinimalInstall, RUNTIME_META, buildInstallTree, extraEmitRootsFor, BUILD_SCRIPT } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
|
|
const FIXTURE_DIR = path.join(ROOT, 'tests', 'fixtures', 'install-tree');
|
|
|
|
function cleanup(root) {
|
|
try { fs.rmSync(root, { recursive: true, force: true }); } catch { /* best effort */ }
|
|
}
|
|
|
|
// Regenerate the fixture for every runtime in RUNTIME_META. Needed when a
|
|
// SHARED gsd-core payload file (e.g. model-catalog.json, capability-registry)
|
|
// changes content — its path appears in every runtime's manifest, so all
|
|
// fixtures must be recaptured together. Usage:
|
|
// node scripts/gen-install-tree-fixtures.cjs [runtime ...]
|
|
// With no args, regenerates ALL runtimes. With args, only the named runtimes.
|
|
const targets = process.argv.slice(2).length > 0 ? process.argv.slice(2) : Object.keys(RUNTIME_META);
|
|
fs.mkdirSync(FIXTURE_DIR, { recursive: true });
|
|
|
|
// Build hooks/dist before capturing so fixtures are complete even in a clean
|
|
// checkout (hooks/dist is gitignored + built; DEFECT.HOOKS-DIST-SCOPED-CI). The
|
|
// test harness's before() hook does the same; without it a fresh checkout omits
|
|
// every hooks/* path and this generator silently writes short fixtures.
|
|
execFileSync(process.execPath, [BUILD_SCRIPT], { stdio: 'pipe' });
|
|
|
|
for (const runtime of targets) {
|
|
if (!Object.prototype.hasOwnProperty.call(RUNTIME_META, runtime)) {
|
|
process.stderr.write(`[gen] unknown runtime '${runtime}' (not in RUNTIME_META) — skipping\n`);
|
|
continue;
|
|
}
|
|
const { configDir, root } = runMinimalInstall({ runtime, scope: 'global' });
|
|
let actual;
|
|
try {
|
|
actual = buildInstallTree(configDir, root, extraEmitRootsFor(runtime, 'global', root));
|
|
} finally {
|
|
cleanup(root);
|
|
}
|
|
const fixturePath = path.join(FIXTURE_DIR, `${runtime}.json`);
|
|
fs.writeFileSync(fixturePath, JSON.stringify(actual, null, 2) + '\n', 'utf8');
|
|
process.stdout.write(`[gen] ${runtime}: wrote ${actual.length} paths -> ${fixturePath}\n`);
|
|
}
|
|
|
|
// Also regenerate the claude LOCAL legacy-layout fixture (claude-local.json).
|
|
// This layout is distinct from the global install (commands/gsd-*.md +
|
|
// agents/gsd-*.md) and has its own snapshot assertion in the test harness.
|
|
const { configDir: localConfigDir, root: localRoot } = runMinimalInstall({ runtime: 'claude', scope: 'local' });
|
|
let localActual;
|
|
try {
|
|
localActual = buildInstallTree(localConfigDir, localRoot);
|
|
} finally {
|
|
cleanup(localRoot);
|
|
}
|
|
const localFixturePath = path.join(FIXTURE_DIR, 'claude-local.json');
|
|
fs.writeFileSync(localFixturePath, JSON.stringify(localActual, null, 2) + '\n', 'utf8');
|
|
process.stdout.write(`[gen] claude-local: wrote ${localActual.length} paths -> ${localFixturePath}\n`);
|