chore: clear dead test and allowlist leftovers of dropped runtimes
Some checks failed
Tests / PR mergeability (push) Successful in 18s
Tests / Base branch health (push) Successful in 9s
Tests / Detect test scope (push) Successful in 16s
Tests / lint-tests (push) Failing after 1m43s
Tests / plugin-validate (push) Successful in 58s
Tests / test (ubuntu-latest, 24, shard 1/3) (push) Failing after 19s
Tests / test (ubuntu-latest, 24, shard 2/3) (push) Failing after 20s
Tests / test (ubuntu-latest, 24, shard 3/3) (push) Failing after 20s
Tests / test (ubuntu-latest, 24) (push) Failing after 18s
Tests / test (inert CI) (push) Has been skipped
Tests / QA loop walk (smell ratchet) (push) Failing after 19s
Tests / Coverage gate (merged shards) (push) Has been skipped
Tests / Publish emitted-baseline artifact (push) Has been skipped
Duplicate auto-close sweep / sweep (push) Successful in 19s
CI timeout budget report / report (push) Failing after 14s
Close Draft PRs (sweep) / Sweep open draft PRs (push) Successful in 9s
Dismiss Unauthorized PR Approvals / dismiss-unauthorized-approval (push) Successful in 9s
Tests / conformance test (macos-latest, 24) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 1/3) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 2/3) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 3/3) (push) Has been cancelled
Tests / Required tests (push) Has been cancelled
Some checks failed
Tests / PR mergeability (push) Successful in 18s
Tests / Base branch health (push) Successful in 9s
Tests / Detect test scope (push) Successful in 16s
Tests / lint-tests (push) Failing after 1m43s
Tests / plugin-validate (push) Successful in 58s
Tests / test (ubuntu-latest, 24, shard 1/3) (push) Failing after 19s
Tests / test (ubuntu-latest, 24, shard 2/3) (push) Failing after 20s
Tests / test (ubuntu-latest, 24, shard 3/3) (push) Failing after 20s
Tests / test (ubuntu-latest, 24) (push) Failing after 18s
Tests / test (inert CI) (push) Has been skipped
Tests / QA loop walk (smell ratchet) (push) Failing after 19s
Tests / Coverage gate (merged shards) (push) Has been skipped
Tests / Publish emitted-baseline artifact (push) Has been skipped
Duplicate auto-close sweep / sweep (push) Successful in 19s
CI timeout budget report / report (push) Failing after 14s
Close Draft PRs (sweep) / Sweep open draft PRs (push) Successful in 9s
Dismiss Unauthorized PR Approvals / dismiss-unauthorized-approval (push) Successful in 9s
Tests / conformance test (macos-latest, 24) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 1/3) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 2/3) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 3/3) (push) Has been cancelled
Tests / Required tests (push) Has been cancelled
This commit is contained in:
@@ -30,49 +30,16 @@ const SUPPORTED_RUNTIMES = installModule.allRuntimes;
|
||||
const RUNTIME_INSTALL_CONTRACTS = {
|
||||
claude: { surface: 'flat-skills', settings: true, hooksPackageJson: true },
|
||||
antigravity: { surface: 'flat-skills', settings: true, hooksPackageJson: true },
|
||||
augment: { surface: 'flat-skills', settings: true, hooksPackageJson: true },
|
||||
cline: { surface: 'clinerules', settings: false, hooksPackageJson: false },
|
||||
codebuddy: { surface: 'flat-skills', settings: true, hooksPackageJson: true },
|
||||
// codex/cursor/windsurf: #2717 stages their .js hooks via dedicated paths
|
||||
// codex/cursor: #2717 stages their .js hooks via dedicated paths
|
||||
// (skipSharedHooksInstall / the !isCodex gate keep them out of the shared
|
||||
// bundle) and writes the marker beside those scripts, so hooks/package.json
|
||||
// is expected for all three. Measured on this tree: codex stages 3 .js hooks,
|
||||
// cursor 6, windsurf 2 — each with the marker.
|
||||
// is expected for both. Measured on this tree: codex stages 3 .js hooks,
|
||||
// cursor 6 — each with the marker.
|
||||
codex: { surface: 'flat-skills', settings: false, hooksPackageJson: true, codexConfig: true },
|
||||
copilot: { surface: 'flat-skills', settings: false, hooksPackageJson: false, copilotInstructions: true },
|
||||
cursor: { surface: 'flat-skills', settings: false, hooksPackageJson: true },
|
||||
gemini: { surface: 'commands-msd', settings: true, hooksPackageJson: true },
|
||||
hermes: { surface: 'hermes-skills', settings: true, hooksPackageJson: true },
|
||||
kimi: { surface: 'kimi-skills-agents', settings: false, hooksPackageJson: false },
|
||||
// #2454: Kimi Code (Node CLI) has NO custom named subagents (per official
|
||||
// docs), so its install surface is skills-only (flat-skills), NOT
|
||||
// kimi-skills-agents. The kimi-agents YAML layout is Python kimi-cli only.
|
||||
'kimi-code': { surface: 'flat-skills', settings: false, hooksPackageJson: false },
|
||||
// #2305: Kilo's native plugin spawns the staged guard hooks, so it receives
|
||||
// the shared hooks bundle + the CommonJS package.json marker, like OpenCode.
|
||||
// (#1821 excluded Kilo on the false premise that it had no plugin surface.)
|
||||
kilo: { surface: 'flat-command', settings: false, hooksPackageJson: true },
|
||||
// #2329: OpenCode discovers commands from the PLURAL `commands/` dir — the
|
||||
// singular `command/` (still correct for Kilo) made all /msd-* commands
|
||||
// invisible to OpenCode. commandDirName overrides the flat-command default.
|
||||
// #2329: OpenCode discovers commands from the PLURAL `commands/` dir.
|
||||
opencode: { surface: 'flat-command', settings: true, hooksPackageJson: true, commandDirName: 'commands' },
|
||||
// #2102 Stage 1/2: pi is a PLUGIN-ONLY install (hostBehaviors.pluginOnlyInstall)
|
||||
// for commands/agents/skills — NO commands/, agents/, or skills/ dir. pi's
|
||||
// /msd command is registered programmatically by the native extension
|
||||
// (extensions/msd.cjs) and dispatches via a bounded subprocess to
|
||||
// msd-tools.cjs; it has no host-read markdown surface. Stage 2 (adversarial-
|
||||
// review fix): pi's native extension DOES spawn the shared hooks/*.js bundle
|
||||
// as bounded subprocesses (session_start/before_agent_start/session_before_
|
||||
// compact bridges) and its /msd tokenizer requires hooks/lib/git-cmd.js, so
|
||||
// `hostBehaviors.skipSharedHooksInstall` was removed — pi now receives
|
||||
// hooks/ + hooks/lib/ + the {"type":"commonjs"} package.json marker, exactly
|
||||
// like OpenCode and (since #2305) Kilo — architecturally identical:
|
||||
// hooksSurface:'none' + a native plugin that spawns the staged hooks — NOT
|
||||
// like ZCode (no plugin surface, where the same hooks are dead weight).
|
||||
pi: { surface: 'plugin-only', settings: false, hooksPackageJson: true },
|
||||
qwen: { surface: 'flat-skills', settings: true, hooksPackageJson: true },
|
||||
trae: { surface: 'flat-skills', settings: false, hooksPackageJson: false },
|
||||
windsurf: { surface: 'global-artifacts-noop', settings: false, hooksPackageJson: true },
|
||||
// #1821: ZCode (hooksSurface:none, no plugin surface) no longer receives the
|
||||
// dead hook scripts or the CommonJS package.json marker.
|
||||
zcode: { surface: 'flat-skills', settings: false, hooksPackageJson: false },
|
||||
@@ -237,40 +204,31 @@ function assertFreshInstallContract(runtime, targetDir) {
|
||||
const contract = RUNTIME_INSTALL_CONTRACTS[runtime];
|
||||
assert.ok(contract, `missing runtime install contract for ${runtime}`);
|
||||
// #3023: the shared hooks bundle's staged directory name is per-runtime
|
||||
// (hostBehaviors.sharedHooksDirName; pi renames it to `msd-hooks/` to avoid
|
||||
// pi's own host-reserved `hooks/`) — resolve it the same way the installer
|
||||
// (hostBehaviors.sharedHooksDirName) — resolve it the same way the installer
|
||||
// does rather than hardcoding 'hooks', which is only the default.
|
||||
const hooksDirName = installModule.resolveSharedHooksDirName(runtime);
|
||||
|
||||
if (contract.workflowPayload !== false) {
|
||||
assert.equal(
|
||||
fs.readFileSync(path.join(targetDir, 'msd-core', 'VERSION'), 'utf8'),
|
||||
pkg.version,
|
||||
`${runtime} should install the package VERSION`
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, 'msd-core', 'bin', 'msd-tools.cjs')),
|
||||
`${runtime} should install the MSD tool payload`
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, 'msd-file-manifest.json')),
|
||||
`${runtime} should write the install manifest`
|
||||
);
|
||||
assert.equal(
|
||||
fs.readFileSync(path.join(targetDir, 'msd-core', 'VERSION'), 'utf8'),
|
||||
pkg.version,
|
||||
`${runtime} should install the package VERSION`
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, 'msd-core', 'bin', 'msd-tools.cjs')),
|
||||
`${runtime} should install the MSD tool payload`
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, 'msd-file-manifest.json')),
|
||||
`${runtime} should write the install manifest`
|
||||
);
|
||||
|
||||
const manifest = JSON.parse(fs.readFileSync(path.join(targetDir, 'msd-file-manifest.json'), 'utf8'));
|
||||
assert.equal(manifest.version, pkg.version, `${runtime} manifest should record the package version`);
|
||||
assert.equal(manifest.mode, 'full', `${runtime} manifest should record a full install`);
|
||||
assert.ok(
|
||||
manifest.files['msd-core/VERSION'],
|
||||
`${runtime} manifest should track the installed VERSION file`
|
||||
);
|
||||
} else {
|
||||
assert.equal(
|
||||
fs.existsSync(path.join(targetDir, 'msd-core')),
|
||||
false,
|
||||
`${runtime} should not install the MSD workflow payload`
|
||||
);
|
||||
}
|
||||
const manifest = JSON.parse(fs.readFileSync(path.join(targetDir, 'msd-file-manifest.json'), 'utf8'));
|
||||
assert.equal(manifest.version, pkg.version, `${runtime} manifest should record the package version`);
|
||||
assert.equal(manifest.mode, 'full', `${runtime} manifest should record a full install`);
|
||||
assert.ok(
|
||||
manifest.files['msd-core/VERSION'],
|
||||
`${runtime} manifest should track the installed VERSION file`
|
||||
);
|
||||
|
||||
if (contract.surface === 'flat-skills') {
|
||||
if (runtime === 'codex') {
|
||||
@@ -295,131 +253,33 @@ function assertFreshInstallContract(runtime, targetDir) {
|
||||
// the same flat-skills surface as the other runtimes.
|
||||
assertHasMsdDirectory(targetDir, 'skills');
|
||||
}
|
||||
} else if (contract.surface === 'hermes-skills') {
|
||||
// Hermes layout uses prefix: '' — skill dirs have bare stem names (no msd- prefix).
|
||||
// Assert that the category dir contains at least one skill dir with SKILL.md.
|
||||
const hermesMsdDir = path.join(targetDir, 'skills', 'msd');
|
||||
const hermesSkillCount = fs.existsSync(hermesMsdDir)
|
||||
? fs.readdirSync(hermesMsdDir, { withFileTypes: true })
|
||||
.filter(e => e.isDirectory() && fs.existsSync(path.join(hermesMsdDir, e.name, 'SKILL.md')))
|
||||
.length
|
||||
: 0;
|
||||
assert.ok(hermesSkillCount > 0, `skills/msd should contain generated MSD entries (got ${hermesSkillCount})`);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, 'skills', 'msd', 'DESCRIPTION.md')),
|
||||
'Hermes should install the nested MSD category description'
|
||||
);
|
||||
} else if (contract.surface === 'flat-command') {
|
||||
// #2329: dir name is per-runtime (opencode='commands', kilo stays 'command').
|
||||
// #2329: dir name is per-runtime (opencode='commands').
|
||||
const commandDirName = contract.commandDirName || 'command';
|
||||
assert.ok(
|
||||
listDirNames(targetDir, commandDirName).some((name) => name.startsWith('msd-') && name.endsWith('.md')),
|
||||
`${runtime} should install flattened command markdown files in ${commandDirName}/`
|
||||
);
|
||||
} else if (contract.surface === 'plugin-only') {
|
||||
// #2102 Stage 1/2: pi — PLUGIN-ONLY install for commands/agents/skills
|
||||
// (hostBehaviors.pluginOnlyInstall). pi's /msd command is registered
|
||||
// programmatically by the native extension and dispatches via a bounded
|
||||
// subprocess to msd-tools.cjs — pi has no host-read markdown surface, so
|
||||
// NO commands/, agents/, or skills/ dir is written. The extension DOES
|
||||
// spawn the shared hooks bundle as bounded subprocesses (Stage 2
|
||||
// adversarial-review fix — hooksSurface:'none' no longer implies
|
||||
// skipSharedHooksInstall for pi, mirroring OpenCode), so the bundle + the
|
||||
// git-cmd.js tokenizer helper ARE part of the artifact surface now. #3023:
|
||||
// that bundle is staged under `msd-hooks/` for pi (hooksDirName above), not
|
||||
// the generic `hooks/` — pi reserves `hooks/` for its own deprecated
|
||||
// extension location.
|
||||
// #2470: the dest filename comes from pi's descriptor, and must satisfy
|
||||
// pi's isExtensionFile() auto-discovery filter (.ts/.js only) — otherwise
|
||||
// the file installs but pi never loads it and /msd never registers.
|
||||
const piNativePlugin = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, '..', 'capabilities', 'pi', 'capability.json'), 'utf8')
|
||||
).runtime.hostBehaviors.nativePlugin;
|
||||
assert.ok(
|
||||
piNativePlugin.file.endsWith('.ts') || piNativePlugin.file.endsWith('.js'),
|
||||
`${runtime}'s extension "${piNativePlugin.file}" must end in .ts or .js for pi to discover it (#2470)`
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, piNativePlugin.dir, piNativePlugin.file)),
|
||||
`${runtime} should install the native extension file at ${piNativePlugin.dir}/${piNativePlugin.file}`
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, hooksDirName, 'msd-ensure-canonical-path.js')),
|
||||
`${runtime} should install the shared ${hooksDirName}/ bundle (spawned by the native extension's event bridges)`
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, hooksDirName, 'lib', 'git-cmd.js')),
|
||||
`${runtime} should install ${hooksDirName}/lib/git-cmd.js (the /msd command tokenizer)`
|
||||
);
|
||||
assert.equal(
|
||||
fs.existsSync(path.join(targetDir, 'commands')),
|
||||
false,
|
||||
`${runtime} should NOT install a commands/ dir (plugin-only, no host-read markdown surface)`
|
||||
);
|
||||
assert.equal(
|
||||
fs.existsSync(path.join(targetDir, 'agents')),
|
||||
false,
|
||||
`${runtime} should NOT install an agents/ dir (plugin-only, no named-dispatch toolkit)`
|
||||
);
|
||||
assert.equal(
|
||||
fs.existsSync(path.join(targetDir, 'skills')),
|
||||
false,
|
||||
`${runtime} should NOT install a skills/ dir (plugin-only)`
|
||||
);
|
||||
} else if (contract.surface === 'commands-msd') {
|
||||
assert.ok(
|
||||
listDirNames(targetDir, path.join('commands', 'msd')).length > 0,
|
||||
`${runtime} should install commands/msd entries`
|
||||
);
|
||||
} else if (contract.surface === 'kimi-skills-agents') {
|
||||
assertHasMsdDirectory(targetDir, 'skills');
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, 'agents', 'msd.yaml')),
|
||||
'Kimi should install the root agent YAML'
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, 'agents', 'msd.md')),
|
||||
'Kimi should install the root agent prompt'
|
||||
);
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(targetDir, 'agents', 'subagents', 'msd-executor.yaml')),
|
||||
'Kimi should install MSD subagent YAML'
|
||||
);
|
||||
} else if (contract.surface === 'clinerules') {
|
||||
// #787: Cline now uses the .clinerules/ directory form (rules at msd.md).
|
||||
assert.match(
|
||||
fs.readFileSync(path.join(targetDir, '.clinerules', 'msd.md'), 'utf8'),
|
||||
/MSD workflows live in `msd-core\/workflows\/`/,
|
||||
'Cline should install .clinerules/msd.md guidance'
|
||||
);
|
||||
} else if (contract.surface === 'global-artifacts-noop') {
|
||||
assert.equal(
|
||||
fs.existsSync(path.join(targetDir, 'skills')),
|
||||
false,
|
||||
`${runtime} should not install unsupported global skills artifacts`
|
||||
);
|
||||
assert.equal(
|
||||
fs.existsSync(path.join(targetDir, 'workflows')),
|
||||
false,
|
||||
`${runtime} should not install unsupported global workflow artifacts`
|
||||
);
|
||||
}
|
||||
|
||||
if (contract.surface !== 'kimi-skills-agents' && contract.surface !== 'global-artifacts-noop' && contract.surface !== 'plugin-only') {
|
||||
if (runtime === 'antigravity') {
|
||||
// #3738: antigravity agents install under the sandboxed home's
|
||||
// .gemini/config root (see the flat-skills branch above), not targetDir.
|
||||
const agySandboxHomeForAgents = path.join(path.dirname(targetDir), 'home');
|
||||
assert.ok(
|
||||
listDirNames(path.join(agySandboxHomeForAgents, '.gemini', 'config'), 'agents').some((name) => name.startsWith('msd-')),
|
||||
`${runtime} full install should install agents`
|
||||
);
|
||||
} else {
|
||||
assert.ok(
|
||||
listDirNames(targetDir, 'agents').some((name) => name.startsWith('msd-')),
|
||||
`${runtime} full install should install agents`
|
||||
);
|
||||
}
|
||||
if (runtime === 'antigravity') {
|
||||
// #3738: antigravity agents install under the sandboxed home's
|
||||
// .gemini/config root (see the flat-skills branch above), not targetDir.
|
||||
const agySandboxHomeForAgents = path.join(path.dirname(targetDir), 'home');
|
||||
assert.ok(
|
||||
listDirNames(path.join(agySandboxHomeForAgents, '.gemini', 'config'), 'agents').some((name) => name.startsWith('msd-')),
|
||||
`${runtime} full install should install agents`
|
||||
);
|
||||
} else {
|
||||
assert.ok(
|
||||
listDirNames(targetDir, 'agents').some((name) => name.startsWith('msd-')),
|
||||
`${runtime} full install should install agents`
|
||||
);
|
||||
}
|
||||
|
||||
assert.equal(
|
||||
@@ -428,9 +288,9 @@ function assertFreshInstallContract(runtime, targetDir) {
|
||||
`${runtime} settings.json presence should match the runtime contract`
|
||||
);
|
||||
// #2544: the CommonJS marker lives in the shared hooks dir (the dir MSD
|
||||
// fills with its own .js scripts — `msd-hooks/` for pi, `hooks/` for every
|
||||
// other runtime, #3023), never at the config root — that file is user-owned
|
||||
// territory on OpenCode/Kilo and was being clobbered on every install.
|
||||
// fills with its own .js scripts — `hooks/` unless a runtime overrides it,
|
||||
// #3023), never at the config root — that file is user-owned
|
||||
// territory on OpenCode and was being clobbered on every install.
|
||||
assert.equal(
|
||||
fs.existsSync(path.join(targetDir, hooksDirName, 'package.json')),
|
||||
contract.hooksPackageJson,
|
||||
@@ -449,14 +309,6 @@ function assertFreshInstallContract(runtime, targetDir) {
|
||||
'Codex should install config.toml with the MSD marker'
|
||||
);
|
||||
}
|
||||
|
||||
if (contract.copilotInstructions) {
|
||||
assert.match(
|
||||
fs.readFileSync(path.join(targetDir, 'copilot-instructions.md'), 'utf8'),
|
||||
/MSD Configuration/,
|
||||
'Copilot should install managed copilot instructions'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe('installer migration install integration', { concurrency: false }, () => {
|
||||
@@ -618,11 +470,7 @@ describe('installer migration install integration', { concurrency: false }, () =
|
||||
assert.match(output, /Installing for /);
|
||||
assert.match(output, /Installer migrations/);
|
||||
assert.match(output, /removed\s+hooks\/statusline\.js/);
|
||||
if (runtime === 'kimi') {
|
||||
assert.match(output, /Generated Kimi root agent/);
|
||||
} else {
|
||||
assert.match(output, /Installed workflow assets/);
|
||||
}
|
||||
assert.match(output, /Installed workflow assets/);
|
||||
assert.match(output, /Done!/);
|
||||
assert.equal(fs.existsSync(path.join(targetDir, 'hooks/statusline.js')), false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user