Files
msd-core/tests/adapter-declarative-equivalence.test.cjs
Tom Boucher 8f2ebbe9bf feat(#1928): remove sunset Gemini CLI runtime, redirect to Antigravity (#1996)
* feat(#1928): remove sunset gemini cli runtime, redirect to antigravity

Google sunset Gemini CLI on 2026-06-18; Antigravity CLI is its official successor (already a first-class GSD runtime). Remove the gemini runtime from the enum (16->15), aliases, labels, config-home fragment, install path, converters (convertClaudeToGemini{Markdown,Toml,Agent}, convertSlashCommandsToGeminiMentions), capability descriptor, gemini-extension.json, RULESET.GEMINI.*, and the interactive menu (renumbered, no gap).

--gemini now prints an explicit deprecation notice citing the 2026-06-18 sunset and redirects to --antigravity (no silent alias, per the issue's Hyrum's-Law rejection). Antigravity is preserved throughout: its GEMINI.md contextFileName, .gemini/antigravity config home, the shared convertGeminiToolName/claudeToGeminiTools tool vocabulary, and the 'gemini' hookEvents dialect it declares. GEMINI.md retargeted as Antigravity's context file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(#1928): backfill changeset PR number (#1996)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(#1928): drop Gemini CLI from issue templates (review nit)

Removes the sunset Gemini CLI runtime from the two GitHub issue-template
runtime lists that the removal PR missed, per @davesienkowski's review nit:
- feature_request.yml: 'Applicable runtimes' checkbox (a user could otherwise
  request a feature for a runtime GSD no longer supports)
- bug_report.yml: 'Runtime' dropdown + the stale ~/.gemini/settings.json
  retrieval-help line

Leaves the post-removal templates fully consistent with the Antigravity redirect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 13:32:51 -04:00

103 lines
4.7 KiB
JavaScript

'use strict';
/**
* Equivalence test for the declarative embedding adapter (ADR-1239 Phase C-1,
* AC1 / #1680).
*
* The declarative adapter NAMES + BOUNDS today's projection path behind
* `HostIntegrationInterface`. This test pins the three load-bearing properties:
*
* 1. KIND — every runtime's adapter classifies as `kind: 'declarative'` and
* echoes its runtime id.
* 2. DELEGATION (the byte-identity link) — `install`/`uninstall` delegate
* IN-PROCESS to install-engine's `installRuntimeArtifacts` /
* `uninstallRuntimeArtifacts` with the EXACT args passed through. Because
* the adapter calls the SAME engine functions `bin/install.js` uses, its
* output is byte-identical to today's install — the 15-runtime byte
* identity is gated by `tests/golden-install-parity.test.cjs` (which
* exercises these engine functions end-to-end). Asserting the delegation
* link here proves the adapter never diverges from the reference path.
* 3. FAIL-CLOSED CONSTRUCTION — missing/invalid runtime throws (no silent
* default adapter).
*
* Behavioral tests only; delegation verified via module-ref monkeypatch (the
* install-engine.cts:31-38 stub-compatible pattern — Node module cache shares
* the one module object, so patching it here is seen by the adapter).
*/
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { createDeclarativeAdapter } = require('../gsd-core/bin/lib/adapter-declarative.cjs');
const installEngine = require('../gsd-core/bin/lib/install-engine.cjs');
const registry = require('../gsd-core/bin/lib/capability-registry.cjs');
const RUNTIMES = Object.keys(registry.runtimes);
test('declarative adapter: kind === "declarative" + runtime echoed, for all 15 runtimes', () => {
assert.ok(RUNTIMES.length >= 15, `expected ≥15 runtimes in registry, got ${RUNTIMES.length}`);
for (const r of RUNTIMES) {
const adapter = createDeclarativeAdapter({ runtime: r });
assert.strictEqual(adapter.kind, 'declarative', `${r}: kind must be 'declarative'`);
assert.strictEqual(adapter.runtime, r, `${r}: adapter must echo the runtime id`);
assert.strictEqual(typeof adapter.install, 'function', `${r}: install must be a function`);
assert.strictEqual(typeof adapter.uninstall, 'function', `${r}: uninstall must be a function`);
}
});
test('declarative adapter.install delegates in-process to installRuntimeArtifacts with exact args (byte-identity link)', () => {
const original = installEngine.installRuntimeArtifacts;
try {
for (const r of RUNTIMES) {
const adapter = createDeclarativeAdapter({ runtime: r });
let captured = null;
installEngine.installRuntimeArtifacts = function (...args) { captured = args; return undefined; };
const resolveAttribution = () => 'attr-' + r;
const intent = {
configDir: '/tmp/adapter-eq/' + r,
scope: 'global',
resolvedProfile: { profile: r },
resolveAttribution,
};
assert.doesNotThrow(() => adapter.install(intent), `${r}: install threw`);
assert.ok(captured, `${r}: install did not delegate to installRuntimeArtifacts`);
assert.deepStrictEqual(
captured,
[r, '/tmp/adapter-eq/' + r, 'global', { profile: r }, resolveAttribution],
`${r}: install delegation args diverged from the engine signature`,
);
}
} finally {
installEngine.installRuntimeArtifacts = original;
}
});
test('declarative adapter.uninstall delegates in-process to uninstallRuntimeArtifacts with exact args', () => {
const original = installEngine.uninstallRuntimeArtifacts;
try {
for (const r of RUNTIMES) {
const adapter = createDeclarativeAdapter({ runtime: r });
let captured = null;
installEngine.uninstallRuntimeArtifacts = function (...args) { captured = args; return undefined; };
assert.doesNotThrow(() => adapter.uninstall({ configDir: '/tmp/adapter-eq/' + r, scope: 'local' }), `${r}: uninstall threw`);
assert.ok(captured, `${r}: uninstall did not delegate to uninstallRuntimeArtifacts`);
assert.deepStrictEqual(
captured,
[r, '/tmp/adapter-eq/' + r, 'local'],
`${r}: uninstall delegation args diverged from the engine signature`,
);
}
} finally {
installEngine.uninstallRuntimeArtifacts = original;
}
});
test('createDeclarativeAdapter: throws on missing/invalid runtime (fail-closed construction)', () => {
for (const bad of ['', undefined, null]) {
assert.throws(
() => createDeclarativeAdapter({ runtime: bad }),
TypeError,
`runtime=${JSON.stringify(bad)} must throw TypeError`,
);
}
assert.throws(() => createDeclarativeAdapter({}), TypeError, 'missing runtime must throw TypeError');
});