Files
msd-core/tests/codebuddy-upgrades.test.cjs
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

130 lines
6.3 KiB
JavaScript

// docs-guard-exempt: codebuddy.ai/docs/... is an external URL citation in a comment, not a repo path.
'use strict';
/**
* CodeBuddy capability UPGRADES — ADR-1239 / #2098 (EoS/codebuddy).
*
* Drives the user-reachable surface (spawned `bin/install.js` via
* `runMinimalInstall`) plus a direct negotiation-contract check to prove the
* two real upgrades CodeBuddy contributes as part of the EoS migration:
*
* UPGRADE 1 — extended hook events: CodeBuddy's `extendedHookEvents` was
* previously `[]` (none of the 4 extended events were wired). This PR
* wires all four — `SubagentStop`/`Stop`/`PreCompact`/`SubagentStart` —
* into `extendedHookEvents` (mirrors qwen #2092 / kimi exactly — same 4
* events), so a live install registers all four as hooks in settings.json
* routed through msd-context-monitor.js, giving CodeBuddy the
* subagent-lifecycle + stop/compact hooks it previously lacked.
*
* UPGRADE 2 — dispatch.background: CodeBuddy's capability.json already
* declares `dispatch.background: true`, which legitimately EXCEEDS the
* `declarative-cli` profile baseline (`false`, per
* `PROFILE_BASELINES['declarative-cli']` in src/host-integration.cts).
* NO agent-file/frontmatter change is involved — the CodeBuddy CLI has no
* background-dispatch frontmatter field (agentMode/enabledAutoRun are
* IDE-only; verified via codebuddy.ai/docs/cli/sub-agents). This is
* surfaced purely through the negotiated contract: a documented `true`
* value must survive negotiation without a downgrade warning.
*/
const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { runMinimalInstall } = require('./helpers/install-shared.cjs');
const { cleanup } = require('./helpers.cjs');
const {
profileOf,
negotiateHostCapabilities,
PROFILE_BASELINES,
} = require('../msd-core/bin/lib/host-integration.cjs');
const CODEBUDDY_CAP = JSON.parse(
fs.readFileSync(path.join(__dirname, '..', 'capabilities', 'codebuddy', 'capability.json'), 'utf8'),
);
const CODEBUDDY_AXES = CODEBUDDY_CAP.runtime.hostIntegration;
// ---------------------------------------------------------------------------
// UPGRADE 1: extended hook events — all 4 newly-wired events' live-install coverage
// ---------------------------------------------------------------------------
test('capabilities/codebuddy/capability.json extendedHookEvents contains exactly the 4 documented events', () => {
const events = CODEBUDDY_CAP.runtime.extendedHookEvents;
assert.deepEqual(events, ['SubagentStop', 'Stop', 'PreCompact', 'SubagentStart']);
assert.equal(events.length, 4);
});
test('codebuddy --global: settings.json wires all 4 newly-added extended hook events to the MSD context-monitor hook (UPGRADE 1)', (t) => {
const { configDir, root } = runMinimalInstall({ runtime: 'codebuddy', scope: 'global' });
t.after(() => cleanup(root));
const settingsPath = path.join(configDir, 'settings.json');
assert.ok(fs.existsSync(settingsPath), `${settingsPath} must exist`);
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
// All 4 events were newly wired by this PR — codebuddy's extendedHookEvents
// was previously [], so none of these existed before #2098.
const NEWLY_WIRED_EXTENDED_EVENTS = ['SubagentStop', 'Stop', 'PreCompact', 'SubagentStart'];
const entries = NEWLY_WIRED_EXTENDED_EVENTS.map((eventName) => {
const eventHooks = settings.hooks && settings.hooks[eventName];
assert.ok(Array.isArray(eventHooks) && eventHooks.length > 0,
`settings.hooks.${eventName} must exist and be non-empty`);
const entry = eventHooks[0].hooks[0];
assert.ok(entry.command.includes('msd-context-monitor'),
`${eventName} command must reference msd-context-monitor.js, got: ${entry.command}`);
assert.equal(entry.timeout, 10, `${eventName} entry must have timeout 10`);
return entry;
});
const [stopEntry, ...restEntries] = entries;
for (const entry of restEntries) {
assert.equal(entry.command, stopEntry.command,
'all 4 newly-wired extended events must wire the same msd-context-monitor command');
}
});
test('a runtime whose extendedHookEvents omits SubagentStart does NOT get one (descriptor-gated, not a global default)', (t) => {
const { configDir, root } = runMinimalInstall({ runtime: 'claude', scope: 'global' });
t.after(() => cleanup(root));
const settingsPath = path.join(configDir, 'settings.json');
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
assert.ok(
settings.hooks && Array.isArray(settings.hooks.SubagentStop) && settings.hooks.SubagentStop.length > 0,
'claude must have SubagentStop wired (sanity — proves hooks ARE configured)',
);
assert.ok(
!settings.hooks || settings.hooks.SubagentStart === undefined,
"claude must NOT have SubagentStart wired — it is not in claude's extendedHookEvents",
);
});
// ---------------------------------------------------------------------------
// UPGRADE 2: dispatch.background negotiation — documented true survives,
// exceeding the declarative-cli profile baseline. No agent-file/frontmatter
// change; caller-side invocation param only (no code path emits it).
// ---------------------------------------------------------------------------
test("codebuddy classifies as the 'declarative-cli' profile, whose baseline dispatch.background is false", () => {
assert.equal(profileOf(CODEBUDDY_AXES), 'declarative-cli');
assert.equal(PROFILE_BASELINES['declarative-cli'].dispatch.background, false,
'sanity: the declarative-cli baseline is false — codebuddy legitimately exceeds it');
});
test('negotiateHostCapabilities surfaces codebuddy\'s documented dispatch.background:true with no downgrade warning (UPGRADE 2)', () => {
assert.equal(CODEBUDDY_AXES.dispatch.background, true,
'sanity: the descriptor declares dispatch.background: true (documented, not undocumented)');
const { effective, warnings } = negotiateHostCapabilities(CODEBUDDY_AXES);
assert.equal(effective.dispatch.background, true,
'a documented true value must survive negotiation, exceeding the declarative-cli baseline of false');
assert.ok(
!warnings.some((w) => w.includes('dispatch.background')),
`no warning may be raised for the documented dispatch.background axis, got: ${JSON.stringify(warnings)}`,
);
});