/** * MSD Agent Installation Validation Tests (#1371) * * Validates that MSD detects missing or incomplete agent installations and * surfaces warnings through init commands and health checks. When agents are * not installed, Task(subagent_type="msd-*") silently falls back to * general-purpose, losing specialized instructions. */ const { test, describe, beforeEach, afterEach } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('fs'); const os = require('os'); const path = require('path'); const { runMsdTools, createTempProject, cleanup } = require('./helpers.cjs'); const AGENTS_DIR_NAME = 'agents'; const MODEL_PROFILES = require('../msd-core/bin/lib/model-profiles.cjs').MODEL_PROFILES; const EXPECTED_AGENTS = Object.keys(MODEL_PROFILES); /** * Create a fake MSD install directory structure that mirrors what the installer * produces. msd-tools.cjs lives at /msd-core/bin/msd-tools.cjs, * so the agents dir is at /agents/. * * We use --cwd to point at the project, and MSD_INSTALL_DIR env to override * the agents directory location for testing. */ function _createAgentsDir(configDir, agentNames = []) { const agentsDir = path.join(configDir, AGENTS_DIR_NAME); fs.mkdirSync(agentsDir, { recursive: true }); for (const name of agentNames) { fs.writeFileSync( path.join(agentsDir, `${name}.md`), `---\nname: ${name}\ndescription: Test agent\ntools: Read, Bash\ncolor: cyan\n---\nAgent content.\n` ); } return agentsDir; } function createCompleteCodexAgents(configDir) { const agentsDir = path.join(configDir, '.codex', AGENTS_DIR_NAME); fs.mkdirSync(agentsDir, { recursive: true }); const files = {}; for (const name of EXPECTED_AGENTS) { fs.writeFileSync(path.join(agentsDir, `${name}.md`), `# ${name}\n`); fs.writeFileSync(path.join(agentsDir, `${name}.toml`), `name = "${name}"\n`); files[`agents/${name}.md`] = {}; files[`agents/${name}.toml`] = {}; } fs.writeFileSync( path.join(configDir, '.codex', 'msd-file-manifest.json'), JSON.stringify({ files }), ); return agentsDir; } // ─── Init command agent validation ────────────────────────────────────────── describe('init commands: agents_installed field (#1371)', () => { let tmpDir; beforeEach(() => { tmpDir = createTempProject(); }); afterEach(() => { cleanup(tmpDir); }); // Point the SDK at the repo's agents/ dir (sibling of msd-core/) via the // MSD_AGENTS_DIR override. The SDK side of init resolves agents from // MSD_AGENTS_DIR or the runtime config dir (~/.claude/agents for Claude); it // does NOT walk up from cwd like the CJS-era code did. Without this override // these tests would only pass on a dev machine with ~/.claude/agents/ // populated — which masked the divergence on Linux CI where that path is // absent. See sdk/src/query/QUERY-HANDLERS.md ("subprocess vs in-process // path resolution") and sdk/src/query/helpers.ts:resolveAgentsDir. const REPO_AGENTS_DIR = path.resolve(__dirname, '..', 'agents'); test('init execute-phase includes agents_installed=true when agents exist', () => { const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); fs.mkdirSync(phaseDir, { recursive: true }); const result = runMsdTools('init execute-phase 1 --raw', tmpDir, { MSD_AGENTS_DIR: REPO_AGENTS_DIR }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(typeof output.agents_installed, 'boolean', 'init execute-phase must include agents_installed field'); assert.strictEqual(output.agents_installed, true, 'agents_installed should be true when MSD_AGENTS_DIR has msd-*.md files'); }); test('init plan-phase includes agents_installed=true when agents exist', () => { const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); fs.mkdirSync(phaseDir, { recursive: true }); const result = runMsdTools('init plan-phase 1 --raw', tmpDir, { MSD_AGENTS_DIR: REPO_AGENTS_DIR }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(typeof output.agents_installed, 'boolean', 'init plan-phase must include agents_installed field'); assert.strictEqual(output.agents_installed, true); }); test('init plan-phase reports the complete project-local Codex installation', () => { const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); const globalHome = path.join(tmpDir, 'global-codex'); fs.mkdirSync(phaseDir, { recursive: true }); fs.writeFileSync( path.join(tmpDir, '.planning', 'config.json'), JSON.stringify({ runtime: 'codex' }), ); createCompleteCodexAgents(tmpDir); const canonicalRoot = fs.realpathSync(tmpDir); const localAgentsDir = path.join(canonicalRoot, '.codex', AGENTS_DIR_NAME); _createAgentsDir(globalHome, EXPECTED_AGENTS); const result = runMsdTools('init plan-phase 1 --raw', tmpDir, { CODEX_HOME: globalHome }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.project_root, canonicalRoot); assert.strictEqual(output.agent_runtime, 'codex'); assert.strictEqual(output.agents_dir, localAgentsDir); assert.strictEqual(output.agents_installed, true); assert.deepStrictEqual(output.missing_agents, []); }); test('init reports codex when the session signal is present', () => { const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); fs.mkdirSync(phaseDir, { recursive: true }); const result = runMsdTools('init plan-phase 1 --raw', tmpDir, { CODEX_SANDBOX: 'seatbelt' }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.agent_runtime, 'codex'); }); test('init honors explicit config runtime over detection', () => { const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); fs.mkdirSync(phaseDir, { recursive: true }); fs.writeFileSync( path.join(tmpDir, '.planning', 'config.json'), JSON.stringify({ runtime: 'claude' }), ); const result = runMsdTools('init plan-phase 1 --raw', tmpDir, { CODEX_SANDBOX: 'seatbelt' }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.agent_runtime, 'claude'); }); test('init is unaffected in a non-codex session', () => { const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); fs.mkdirSync(phaseDir, { recursive: true }); const result = runMsdTools('init plan-phase 1 --raw', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.agent_runtime, 'claude'); }); test('init honors MSD_RUNTIME over detection', () => { const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); fs.mkdirSync(phaseDir, { recursive: true }); const result = runMsdTools('init plan-phase 1 --raw', tmpDir, { MSD_RUNTIME: 'opencode', CODEX_SANDBOX: 'seatbelt', }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.agent_runtime, 'opencode'); }); test('init execute-phase includes missing_agents list when agents are missing', () => { const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-setup'); fs.mkdirSync(phaseDir, { recursive: true }); const result = runMsdTools('init execute-phase 1 --raw', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.ok(Array.isArray(output.missing_agents), 'init execute-phase must include missing_agents array'); }); test('init quick includes agents_installed field', () => { const result = runMsdTools(['init', 'quick', 'test description', '--raw'], tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(typeof output.agents_installed, 'boolean', 'init quick must include agents_installed field'); }); }); // ─── Health check: agent installation ─────────────────────────────────────── describe('validate health: agent installation check W010 (#1371)', () => { let tmpDir; beforeEach(() => { tmpDir = createTempProject(); // Write minimal project files so health check doesn't fail on E001-E005 fs.writeFileSync( path.join(tmpDir, '.planning', 'PROJECT.md'), '# Project\n\n## What This Is\nTest\n\n## Core Value\nTest\n\n## Requirements\nTest\n' ); fs.writeFileSync( path.join(tmpDir, '.planning', 'ROADMAP.md'), '# Roadmap\n\n### Phase 1: Setup\n' ); fs.writeFileSync( path.join(tmpDir, '.planning', 'STATE.md'), '# Session State\n\n## Current Position\n\nPhase: 1\n' ); fs.writeFileSync( path.join(tmpDir, '.planning', 'config.json'), JSON.stringify({ model_profile: 'balanced', commit_docs: true, workflow: { nyquist_validation: true }, }, null, 2) ); fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-setup'), { recursive: true }); }); afterEach(() => { cleanup(tmpDir); }); test('health check reports healthy when agents are installed (repo layout)', () => { // In the repo, agents/ exists as a sibling of msd-core/, so the // health check should find them via the msd-tools.cjs path resolution const result = runMsdTools('validate health --raw', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); // Should not have W010 warning about missing agents const w010 = (output.warnings || []).find(w => w.code === 'W010'); assert.ok(!w010, 'Should not warn about missing agents when agents/ dir exists with files'); }); }); // ─── Codex project-local validation status ────────────────────────────────── describe('Codex project-local validation status', () => { let tmpDir; let globalHome; beforeEach(() => { tmpDir = createTempProject(); globalHome = path.join(tmpDir, 'global-codex'); fs.writeFileSync( path.join(tmpDir, '.planning', 'config.json'), JSON.stringify({ runtime: 'codex' }), ); _createAgentsDir(globalHome, EXPECTED_AGENTS); }); afterEach(() => { cleanup(tmpDir); }); test('validate agents and health use a complete project-local Codex installation', () => { createCompleteCodexAgents(tmpDir); const localAgentsDir = path.join(fs.realpathSync(tmpDir), '.codex', AGENTS_DIR_NAME); const env = { CODEX_HOME: globalHome }; const validateResult = runMsdTools('validate agents --raw', tmpDir, env); assert.ok(validateResult.success, `validate agents failed: ${validateResult.error}`); const validateOutput = JSON.parse(validateResult.output); assert.strictEqual(validateOutput.agents_dir, localAgentsDir); assert.strictEqual(validateOutput.agents_found, true); assert.deepStrictEqual(validateOutput.missing, []); assert.deepStrictEqual(validateOutput.incomplete, []); const healthResult = runMsdTools('validate health --raw', tmpDir, env); assert.ok(healthResult.success, `validate health failed: ${healthResult.error}`); const healthOutput = JSON.parse(healthResult.output); assert.ok(!(healthOutput.warnings || []).some(warning => warning.code === 'W010')); }); test('an empty project-local Codex directory remains authoritative for validate agents and health', () => { fs.mkdirSync(path.join(tmpDir, '.codex', AGENTS_DIR_NAME), { recursive: true }); fs.writeFileSync(path.join(tmpDir, '.codex', 'msd-file-manifest.json'), JSON.stringify({ files: {} })); const localAgentsDir = path.join(fs.realpathSync(tmpDir), '.codex', AGENTS_DIR_NAME); const env = { CODEX_HOME: globalHome }; const validateResult = runMsdTools('validate agents --raw', tmpDir, env); assert.ok(validateResult.success, `validate agents failed: ${validateResult.error}`); const validateOutput = JSON.parse(validateResult.output); assert.strictEqual(validateOutput.agents_dir, localAgentsDir); assert.strictEqual(validateOutput.agents_found, false); assert.deepStrictEqual(validateOutput.installed, []); assert.deepStrictEqual(validateOutput.incomplete, []); assert.deepStrictEqual(validateOutput.missing, EXPECTED_AGENTS); const healthResult = runMsdTools('validate health --raw', tmpDir, env); assert.ok(healthResult.success, `validate health failed: ${healthResult.error}`); const healthOutput = JSON.parse(healthResult.output); assert.ok((healthOutput.warnings || []).some(warning => warning.code === 'W010')); }); }); // ─── Agent file format detection (.toml / MSD_AGENTS_DIR) ──────────────────────────────────── describe('checkAgentsInstalled: agent file formats and MSD_AGENTS_DIR', () => { let tmpDir; beforeEach(() => { tmpDir = createTempProject(); }); afterEach(() => { cleanup(tmpDir); }); test('agents_installed=true when agents exist as .toml (Codex format) (#278)', () => { const agentsDir = path.join(tmpDir, 'codex-agents'); fs.mkdirSync(agentsDir, { recursive: true }); for (const name of EXPECTED_AGENTS) { fs.writeFileSync( path.join(agentsDir, `${name}.toml`), `name = "${name}"\ndescription = "Test agent"\n` ); } const result = runMsdTools('validate agents --raw', tmpDir, { MSD_AGENTS_DIR: agentsDir }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.strictEqual(output.agents_found, true, 'agents_found must be true when agents exist as .toml (Codex format)'); assert.deepStrictEqual(output.missing, [], 'missing must be empty when all agents exist as .toml'); }); test('MSD_AGENTS_DIR env var overrides default agents directory', () => { // Create a custom agents dir in a subdirectory const customAgentsDir = path.join(tmpDir, 'custom-agents'); fs.mkdirSync(customAgentsDir, { recursive: true }); // Put one agent there as .md (standard format) fs.writeFileSync( path.join(customAgentsDir, `${EXPECTED_AGENTS[0]}.md`), `---\nname: ${EXPECTED_AGENTS[0]}\ndescription: Test agent\n---\nAgent content.\n` ); const result = runMsdTools('validate agents --raw', tmpDir, { MSD_AGENTS_DIR: customAgentsDir }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); // The custom dir path should be reported assert.strictEqual(output.agents_dir, customAgentsDir, 'agents_dir must reflect MSD_AGENTS_DIR override'); }); }); describe('validate agents subcommand (#1371)', () => { let tmpDir; beforeEach(() => { tmpDir = createTempProject(); }); afterEach(() => { cleanup(tmpDir); }); test('validate agents returns status with agent list', () => { const result = runMsdTools('validate agents --raw', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); assert.ok('agents_dir' in output, 'Must include agents_dir path'); assert.ok('installed' in output, 'Must include installed array'); assert.ok('missing' in output, 'Must include missing array'); assert.ok('agents_found' in output, 'Must include agents_found boolean'); }); test('validate agents lists all expected agent types', () => { const result = runMsdTools('validate agents --raw', tmpDir); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); // The expected agents come from MODEL_PROFILES keys assert.ok(output.expected.length > 0, 'Must have expected agents'); }); }); // ─── Sandbox posture drift, at the COMMAND (#3897 rung 3, ADR-3473 §8.3) ───── // // tests/agent-install-check.test.cjs's T28 already proves checkCodexSandboxPosture // itself detects a drifted sandbox_mode. That is a UNIT row — it drives the // helper directly. Before verify.cts wired the helper into cmdValidateAgents, // T28 stayed green while `msd-tools validate agents` (the actual command a // user/CI runs) never surfaced the drift at all — the ADR-3180 Decision 4(b) // failure shape ("asserting at a helper's return, not at the consumer's // output"). This row drives the real CLI end to end and asserts on ITS JSON, // so a future regression that leaves the helper correct but un-wired from the // command fails here even if T28 stays green. describe('validate agents surfaces sandbox posture drift at the command (#3897 rung 3)', () => { let tmpDir; let agentsDir; beforeEach(() => { tmpDir = createTempProject(); agentsDir = path.join(tmpDir, 'codex-agents'); fs.mkdirSync(agentsDir, { recursive: true }); }); afterEach(() => { cleanup(tmpDir); }); test('validateAgentsSurfacesSandboxDriftAtTheCommand_3897: `validate agents` reports the drifted agent/expected/found via sandbox_posture', () => { // agents/msd-executor.md (the real repo role source — checkCodexSandboxPosture // reads it directly, it is not fixture-overridable) declares tools that derive // workspace-write. Install a drifted TOML claiming read-only instead. fs.writeFileSync( path.join(agentsDir, 'msd-executor.toml'), 'name = "msd-executor"\ndescription = "Executes plans"\nsandbox_mode = "read-only"\n' + "developer_instructions = '''\nExecute.\n'''\n", ); const env = { MSD_AGENTS_DIR: agentsDir, MSD_RUNTIME: 'codex' }; const result = runMsdTools('validate agents --raw', tmpDir, env); assert.ok(result.success, `validate agents failed: ${result.error}`); assert.strictEqual(result.exitCode, 0, 'model-posture drift is report-only, not fatal — sandbox posture must match that precedent'); const output = JSON.parse(result.output); assert.ok( output.sandbox_posture, `expected \`validate agents\` output to carry a sandbox_posture key, got keys: ${Object.keys(output).join(', ')}`, ); assert.strictEqual(output.sandbox_posture.ok, false); const violation = output.sandbox_posture.violations.find((v) => v.agent === 'msd-executor'); assert.ok(violation, 'the command output must name the drifted agent'); assert.strictEqual(violation.expected, 'workspace-write'); assert.strictEqual(violation.found, 'read-only'); }); }); // ─── Bug #1058: validate agents detects manifest-backed .md/.toml pair drift ── describe('bug #1058: validate agents detects manifest-backed .md/.toml pair drift', () => { // All real agents/msd-*.md files in the repo (used to populate fixtures) const REPO_AGENTS_DIR_1058 = path.resolve(__dirname, '..', 'agents'); /** * Copy all msd-*.md files from the repo agents dir into destDir. */ function copyAgentMdFiles(destDir) { const files = fs.readdirSync(REPO_AGENTS_DIR_1058).filter(f => /^msd-.*\.md$/.test(f)); for (const file of files) { const src = path.join(REPO_AGENTS_DIR_1058, file); const dst = path.join(destDir, file); fs.copyFileSync(src, dst); } } /** * Build a msd-file-manifest.json whose files map includes both .md and .toml * entries for every EXPECTED_AGENTS entry. */ function buildManifestBothPairs(agents) { const files = {}; for (const name of agents) { files[`agents/${name}.md`] = 'deadbeef'; files[`agents/${name}.toml`] = 'deadbeef'; } return { version: '1.0.0', files }; } let tmpDir; afterEach(() => { if (tmpDir) { cleanup(tmpDir); tmpDir = null; } }); test('agents_found=false and incomplete non-empty when .toml files are absent but manifest expects them', () => { // Arrange: all .md files present, manifest says both .md and .toml are expected, // but NO .toml files are created on disk. tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-bug-1058-missing-toml-')); const agentsDir = path.join(tmpDir, 'agents'); fs.mkdirSync(agentsDir, { recursive: true }); // Copy real .md files copyAgentMdFiles(agentsDir); // Write manifest with both .md and .toml entries but no .toml files on disk fs.writeFileSync( path.join(tmpDir, 'msd-file-manifest.json'), JSON.stringify(buildManifestBothPairs(EXPECTED_AGENTS), null, 2), ); // Act const result = runMsdTools('validate agents --raw', tmpDir, { MSD_RUNTIME: 'codex', MSD_AGENTS_DIR: agentsDir, }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); // Assert: the completeness check must catch the missing .toml files assert.strictEqual( output.agents_found, false, `Expected agents_found=false when manifest-tracked .toml files are absent, got agents_found=${output.agents_found}`, ); assert.ok( Array.isArray(output.incomplete), 'Expected output.incomplete to be an array', ); // Every agent whose .toml is absent must appear in incomplete (sorted deep-equal) assert.deepStrictEqual( [...output.incomplete].sort(), [...EXPECTED_AGENTS].sort(), `Expected incomplete to equal full EXPECTED_AGENTS set; got ${JSON.stringify(output.incomplete)}`, ); // missing (entirely-absent .md) must be empty — these agents are not missing, only incomplete assert.deepStrictEqual( output.missing, [], `Expected missing=[] when all .md files are present; got ${JSON.stringify(output.missing)}`, ); }); test('agents_found=false and incomplete non-empty when .md files are absent but manifest expects them', () => { // Arrange: only .toml files present on disk, manifest says both .md and .toml are expected, // but NO .md files exist. This is the opposite-side pair-drift case. tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-bug-1058-missing-md-')); const agentsDir = path.join(tmpDir, 'agents'); fs.mkdirSync(agentsDir, { recursive: true }); // Create ONLY .toml files — deliberately skip .md files for (const name of EXPECTED_AGENTS) { fs.writeFileSync(path.join(agentsDir, `${name}.toml`), `name = "${name}" `); } // Write manifest with both .md and .toml entries fs.writeFileSync( path.join(tmpDir, 'msd-file-manifest.json'), JSON.stringify(buildManifestBothPairs(EXPECTED_AGENTS), null, 2), ); // Act const result = runMsdTools('validate agents --raw', tmpDir, { MSD_RUNTIME: 'codex', MSD_AGENTS_DIR: agentsDir, }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); // Assert: missing .md files must surface as incomplete (pair-drift detected symmetrically) assert.strictEqual( output.agents_found, false, `Expected agents_found=false when manifest-tracked .md files are absent, got agents_found=${output.agents_found}`, ); assert.ok( Array.isArray(output.incomplete), 'Expected output.incomplete to be an array', ); assert.ok( output.incomplete.length > 0, `Expected incomplete to be non-empty when .md files are absent; got ${JSON.stringify(output.incomplete)}`, ); }); test('agents_found=true and incomplete=[] when both .md and .toml files are present', () => { // Arrange: all .md AND .toml present, manifest says both expected tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-bug-1058-complete-pair-')); const agentsDir = path.join(tmpDir, 'agents'); fs.mkdirSync(agentsDir, { recursive: true }); // Copy real .md files copyAgentMdFiles(agentsDir); // Create .toml files for each expected agent for (const name of EXPECTED_AGENTS) { fs.writeFileSync(path.join(agentsDir, `${name}.toml`), `name = "${name}"\n`); } // Write manifest with both .md and .toml entries fs.writeFileSync( path.join(tmpDir, 'msd-file-manifest.json'), JSON.stringify(buildManifestBothPairs(EXPECTED_AGENTS), null, 2), ); // Act const result = runMsdTools('validate agents --raw', tmpDir, { MSD_RUNTIME: 'codex', MSD_AGENTS_DIR: agentsDir, }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); // Assert: complete pair => no false positive assert.strictEqual( output.agents_found, true, `Expected agents_found=true when both .md and .toml files are present; got ${output.agents_found}`, ); assert.deepStrictEqual( output.incomplete, [], `Expected incomplete=[] when all manifest-tracked files are present; got ${JSON.stringify(output.incomplete)}`, ); }); test('no spurious incomplete flags when no manifest is present (protects claude/bundled installs)', () => { // Arrange: all .md files present, NO manifest file at all tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-bug-1058-no-manifest-')); const agentsDir = path.join(tmpDir, 'agents'); fs.mkdirSync(agentsDir, { recursive: true }); // Copy real .md files copyAgentMdFiles(agentsDir); // Explicitly do NOT write msd-file-manifest.json // Act const result = runMsdTools('validate agents --raw', tmpDir, { MSD_RUNTIME: 'codex', MSD_AGENTS_DIR: agentsDir, }); assert.ok(result.success, `Command failed: ${result.error}`); const output = JSON.parse(result.output); // Assert: without a manifest, completeness check must no-op (incomplete empty) assert.deepStrictEqual( output.incomplete, [], `Expected incomplete=[] when no manifest present; got ${JSON.stringify(output.incomplete)}`, ); // agents_found should reflect plain file presence (all .md files copied => true) assert.strictEqual( output.agents_found, true, `Expected agents_found=true when all .md files are present and no manifest; got ${output.agents_found}`, ); }); });