'use strict'; const assert = require('assert'); const crypto = require('crypto'); const fs = require('fs'); const os = require('os'); const path = require('path'); const { applyMultiHarnessPlan, createMultiHarnessPlan, normalizeGuidedInstallRequest, preflightManagedPlan, } = require('../../scripts/lib/multi-harness-setup'); const { createInstallState } = require('../../scripts/lib/install-state'); let passed = 0; let failed = 0; async function test(name, fn) { try { await fn(); console.log(` ✓ ${name}`); passed += 1; } catch (error) { console.log(` ✗ ${name}`); console.log(` Error: ${error.message}`); failed += 1; } } function tempDir(prefix) { return fs.mkdtempSync(path.join(os.tmpdir(), prefix)); } function writeFile(filePath, content) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, content, 'utf8'); } function sha256(content) { return crypto.createHash('sha256').update(content).digest('hex'); } function stateOperation(destinationPath, overrides = {}) { return { kind: 'copy-file', moduleId: 'core', sourceRelativePath: 'rules/security.md', destinationPath, strategy: 'preserve-relative-path', ownership: 'managed', scaffoldOnly: false, ...overrides, }; } function stateOperationFrom(operation) { return stateOperation(operation.destinationPath, { kind: operation.kind, moduleId: operation.moduleId || 'core', sourceRelativePath: operation.sourceRelativePath || 'rules/security.md', strategy: operation.strategy || (operation.kind === 'merge-json' ? 'merge-json' : 'preserve-relative-path'), ownership: operation.ownership || 'managed', scaffoldOnly: Boolean(operation.scaffoldOnly), }); } function managedPlan(root, operations, owned = []) { const installStatePath = path.join(root, '.kimi-code', 'ecc-install-state.json'); const plan = { adapter: { id: 'kimi-project', target: 'kimi', kind: 'project' }, installStatePath, operations, target: 'kimi', targetRoot: root, }; plan.statePreview = createInstallState({ adapter: plan.adapter, installStatePath, operations: operations.map(stateOperationFrom), request: {}, resolution: {}, source: { manifestVersion: 1 }, targetRoot: root, }); if (owned.length > 0) { writeManagedState(plan, { operations: owned.map(destinationPath => stateOperation(destinationPath, { contentSha256: sha256(fs.readFileSync(destinationPath)), })), }); } return plan; } function writeManagedState(plan, overrides = {}) { const state = createInstallState({ adapter: plan.adapter, installStatePath: plan.installStatePath, operations: [], request: {}, resolution: {}, source: { manifestVersion: 1 }, targetRoot: plan.targetRoot, }); const nextState = { ...state, ...overrides, target: { ...state.target, ...(overrides.target || {}) }, operations: overrides.operations || state.operations, }; writeFile(plan.installStatePath, `${JSON.stringify(nextState, null, 2)}\n`); return nextState; } (async () => { console.log('\n=== Multi-harness guided setup tests ===\n'); await test('normalizes provider-specific options without inventing shared semantics', () => { assert.deepStrictEqual(normalizeGuidedInstallRequest({ harnesses: ['kimi', 'claude', 'kimi'], claudeHooks: 'minimal', claudeScope: 'local', profile: 'developer', }), { harnesses: ['claude', 'kimi'], claudeHooks: 'minimal', claudeScope: 'local', dryRun: false, json: false, profile: 'developer', yes: false, }); assert.throws( () => normalizeGuidedInstallRequest({ harnesses: ['kimi'], claudeScope: 'user' }), /Claude.*selected/i ); assert.throws( () => normalizeGuidedInstallRequest({ harnesses: ['codex'], profile: 'core' }), /Kimi.*selected/i ); }); await test('classifies missing, identical, managed, and JSON merge destinations', () => { const root = tempDir('ecc-guided-preflight-'); try { const sourceSame = path.join(root, 'sources', 'same.md'); const sourceManaged = path.join(root, 'sources', 'managed.md'); const destinationSame = path.join(root, 'same.md'); const destinationManaged = path.join(root, 'managed.md'); const destinationJson = path.join(root, 'mcp.json'); writeFile(sourceSame, 'same\n'); writeFile(sourceManaged, 'new\n'); writeFile(destinationSame, 'same\n'); writeFile(destinationManaged, 'old\n'); writeFile(destinationJson, '{"other":true}\n'); const plan = managedPlan(root, [ { kind: 'copy-file', sourcePath: sourceSame, destinationPath: destinationSame }, stateOperation(destinationManaged, { sourcePath: sourceManaged }), { kind: 'merge-json', destinationPath: destinationJson, mergePayload: { ecc: true } }, { kind: 'copy-file', sourcePath: sourceSame, destinationPath: path.join(root, 'new.md') }, ], [destinationManaged]); const result = preflightManagedPlan(plan); assert.deepStrictEqual(result.operations.map(item => item.classification), [ 'identical', 'managed-update', 'json-merge', 'create', ]); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('rejects valid install-state from a different managed target identity', () => { const root = tempDir('ecc-guided-forged-target-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, 'AGENTS.md'); writeFile(source, 'ecc\n'); writeFile(destination, 'user\n'); const plan = managedPlan(root, [ stateOperation(destination, { sourcePath: source }), ]); writeManagedState(plan, { target: { id: 'cursor-project', target: 'cursor' }, operations: [stateOperation(destination)], }); assert.throws( () => preflightManagedPlan(plan), /install-state.*target identity|does not belong.*Kimi/i ); assert.strictEqual(fs.readFileSync(destination, 'utf8'), 'user\n'); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('rejects install-state with mismatched canonical root or state path', () => { const root = tempDir('ecc-guided-forged-paths-'); const otherRoot = tempDir('ecc-guided-forged-other-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, 'AGENTS.md'); writeFile(source, 'ecc\n'); writeFile(destination, 'user\n'); const plan = managedPlan(root, [ stateOperation(destination, { sourcePath: source }), ]); for (const target of [ { root: otherRoot }, { installStatePath: path.join(otherRoot, 'ecc-install-state.json') }, ]) { writeManagedState(plan, { target, operations: [stateOperation(destination)], }); assert.throws( () => preflightManagedPlan(plan), /install-state.*(root|path).*does not match/i ); } } finally { fs.rmSync(root, { recursive: true, force: true }); fs.rmSync(otherRoot, { recursive: true, force: true }); } }); await test('rejects install-state ownership claims outside the canonical target root', () => { const root = tempDir('ecc-guided-forged-containment-'); const outside = tempDir('ecc-guided-forged-outside-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, 'AGENTS.md'); writeFile(source, 'ecc\n'); writeFile(destination, 'user\n'); const plan = managedPlan(root, [ stateOperation(destination, { sourcePath: source }), ]); writeManagedState(plan, { operations: [stateOperation(path.join(outside, 'AGENTS.md'))], }); assert.throws( () => preflightManagedPlan(plan), /install-state.*outside|outside the install root/i ); } finally { fs.rmSync(root, { recursive: true, force: true }); fs.rmSync(outside, { recursive: true, force: true }); } }); await test('refuses an unowned differing managed-target file', () => { const root = tempDir('ecc-guided-collision-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, 'AGENTS.md'); writeFile(source, 'ecc\n'); writeFile(destination, 'user\n'); assert.throws( () => preflightManagedPlan(managedPlan(root, [ { kind: 'copy-file', sourcePath: source, destinationPath: destination }, ])), /unowned.*AGENTS\.md/i ); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('same-target state without a content digest cannot claim a user file', () => { const root = tempDir('ecc-guided-forged-same-target-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, 'AGENTS.md'); writeFile(source, 'ecc\n'); writeFile(destination, 'user\n'); const operation = stateOperation(destination, { sourcePath: source }); const plan = managedPlan(root, [operation]); writeManagedState(plan, { operations: [stateOperation(destination)] }); assert.throws( () => preflightManagedPlan(plan), /unverified ownership|content digest|unowned existing file/i ); assert.strictEqual(fs.readFileSync(destination, 'utf8'), 'user\n'); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('managed ownership requires an exact operation identity and content digest', () => { const root = tempDir('ecc-guided-managed-digest-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, 'AGENTS.md'); writeFile(source, 'new ecc\n'); writeFile(destination, 'old ecc\n'); const operation = stateOperation(destination, { sourcePath: source }); const plan = managedPlan(root, [operation]); writeManagedState(plan, { operations: [stateOperation(destination, { contentSha256: sha256('old ecc\n'), })], }); assert.strictEqual( preflightManagedPlan(plan).operations[0].classification, 'managed-update' ); writeManagedState(plan, { operations: [stateOperation(destination, { contentSha256: sha256('old ecc\n'), sourceRelativePath: 'rules/other.md', })], }); assert.throws( () => preflightManagedPlan(plan), /operation identity|unverified ownership|unowned existing file/i ); writeManagedState(plan, { operations: [stateOperation(destination, { contentSha256: sha256('different bytes\n'), })], }); assert.throws( () => preflightManagedPlan(plan), /content digest|unverified ownership|unowned existing file/i ); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('refuses a conflicting key in an unowned JSON merge destination', () => { const root = tempDir('ecc-guided-json-collision-'); try { const destination = path.join(root, 'mcp.json'); writeFile(destination, JSON.stringify({ mcpServers: { github: { command: 'user-owned-server' } }, })); assert.throws( () => preflightManagedPlan(managedPlan(root, [ { kind: 'merge-json', destinationPath: destination, mergePayload: { mcpServers: { github: { command: 'ecc-server' } } }, }, ])), /unowned JSON.*mcpServers\.github\.command/i ); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('rejects symlinked managed ancestors during batch preflight', () => { const root = tempDir('ecc-guided-symlink-root-'); const outside = tempDir('ecc-guided-symlink-outside-'); try { const source = path.join(root, 'source.md'); const linkedDirectory = path.join(root, 'rules'); writeFile(source, 'ecc\n'); fs.symlinkSync(outside, linkedDirectory, process.platform === 'win32' ? 'junction' : 'dir'); assert.throws( () => preflightManagedPlan(managedPlan(root, [ { kind: 'copy-file', sourcePath: source, destinationPath: path.join(linkedDirectory, 'security.md'), }, ])), /outside the install root|symlinked path/i ); assert.strictEqual(fs.existsSync(path.join(outside, 'security.md')), false); } finally { fs.rmSync(root, { recursive: true, force: true }); fs.rmSync(outside, { recursive: true, force: true }); } }); await test('rejects an unwritable Kimi destination during preflight', () => { const root = tempDir('ecc-guided-unwritable-'); try { const destination = path.join(root, '.kimi-code', 'rules', 'security.md'); const accessChecks = []; const accessError = new Error('permission denied'); accessError.code = 'EACCES'; assert.throws( () => preflightManagedPlan(managedPlan(root, [ { kind: 'copy-file', destinationPath: destination }, ]), { accessSync(candidatePath, mode) { accessChecks.push({ candidatePath, mode }); throw accessError; }, }), error => ( /Kimi destination is not writable by the current user/i.test(error.message) && error.message.includes(root) ) ); assert.deepStrictEqual(accessChecks, [{ candidatePath: root, mode: fs.constants.W_OK | fs.constants.X_OK, }]); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('real filesystem preflight rejects an unwritable project root', () => { if (process.platform === 'win32' || (typeof process.getuid === 'function' && process.getuid() === 0)) { return; } const root = tempDir('ecc-guided-real-permissions-'); const projectRoot = path.join(root, 'project'); fs.mkdirSync(projectRoot, { mode: 0o755 }); try { fs.chmodSync(projectRoot, 0o555); assert.throws( () => preflightManagedPlan(managedPlan(projectRoot, [ { kind: 'copy-file', destinationPath: path.join(projectRoot, '.kimi-code', 'rules', 'security.md'), }, ])), /Kimi destination is not writable by the current user/i ); assert.strictEqual(fs.existsSync(path.join(projectRoot, '.kimi-code')), false); } finally { fs.chmodSync(projectRoot, 0o755); fs.rmSync(root, { recursive: true, force: true }); } }); await test('preflights every selected harness before applying any mutation', async () => { const events = []; const request = normalizeGuidedInstallRequest({ harnesses: ['claude', 'codex', 'kimi'], claudeHooks: 'standard', claudeScope: 'user', profile: 'core', }); await assert.rejects( () => createMultiHarnessPlan(request, { previewClaude: async () => events.push('preview:claude'), previewCodex: async () => events.push('preview:codex'), createManagedPlan: async () => ({ target: 'kimi' }), preflightManaged: async () => { events.push('preview:kimi'); throw new Error('unowned collision'); }, }), /collision/ ); assert.deepStrictEqual(events, ['preview:claude', 'preview:codex', 'preview:kimi']); }); await test('refuses a copy-file destination created after preview but before apply', async () => { const root = tempDir('ecc-guided-late-copy-collision-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, '.kimi-code', 'rules', 'security.md'); writeFile(source, 'ecc\n'); const plan = managedPlan(root, [stateOperation(destination, { sourcePath: source })]); const preview = preflightManagedPlan(plan); const result = await applyMultiHarnessPlan({ harnesses: [{ id: 'kimi', preview }], request: { harnesses: ['kimi'] }, }, { preflightManaged(candidatePlan) { const latestPreview = preflightManagedPlan(candidatePlan); writeFile(destination, 'user\n'); return latestPreview; }, }); assert.strictEqual(result.status, 'failed'); assert.match(result.failure.message, /unowned existing file/i); assert.deepStrictEqual(result.retryHarnesses, ['kimi']); assert.strictEqual(fs.readFileSync(destination, 'utf8'), 'user\n'); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('refuses a late unowned copy even when its bytes match the ECC source', async () => { const root = tempDir('ecc-guided-late-identical-copy-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, '.kimi-code', 'rules', 'security.md'); writeFile(source, 'ecc\n'); const plan = managedPlan(root, [stateOperation(destination, { sourcePath: source })]); const preview = preflightManagedPlan(plan); const result = await applyMultiHarnessPlan({ harnesses: [{ id: 'kimi', preview }], request: { harnesses: ['kimi'] }, }, { preflightManaged(candidatePlan) { const latestPreview = preflightManagedPlan(candidatePlan); writeFile(destination, 'ecc\n'); return latestPreview; }, }); assert.strictEqual(result.status, 'failed'); assert.match(result.failure.message, /destination changed after Kimi preflight/i); assert.deepStrictEqual(result.retryHarnesses, ['kimi']); assert.strictEqual(fs.readFileSync(destination, 'utf8'), 'ecc\n'); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('refuses conflicting JSON created after preview but before apply', async () => { const root = tempDir('ecc-guided-late-json-collision-'); try { const destination = path.join(root, '.kimi-code', 'mcp.json'); const operation = stateOperation(destination, { kind: 'merge-json', mergePayload: { mcpServers: { github: { command: 'ecc-server' } } }, sourceRelativePath: '.mcp.json', strategy: 'merge-json', }); const plan = managedPlan(root, [operation]); const preview = preflightManagedPlan(plan); const result = await applyMultiHarnessPlan({ harnesses: [{ id: 'kimi', preview }], request: { harnesses: ['kimi'] }, }, { preflightManaged(candidatePlan) { const latestPreview = preflightManagedPlan(candidatePlan); writeFile(destination, JSON.stringify({ mcpServers: { github: { command: 'user-server' } }, })); return latestPreview; }, }); assert.strictEqual(result.status, 'failed'); assert.match(result.failure.message, /unowned JSON.*mcpServers\.github\.command/i); assert.deepStrictEqual(result.retryHarnesses, ['kimi']); assert.deepStrictEqual(JSON.parse(fs.readFileSync(destination, 'utf8')), { mcpServers: { github: { command: 'user-server' } }, }); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('refuses an install-state file created after preview instead of overwriting it', async () => { const root = tempDir('ecc-guided-late-state-collision-'); try { const source = path.join(root, 'source.md'); const destination = path.join(root, '.kimi-code', 'rules', 'security.md'); writeFile(source, 'ecc\n'); const plan = managedPlan(root, [stateOperation(destination, { sourcePath: source })]); const preview = preflightManagedPlan(plan); const unexpectedState = '{"user":"owned"}\n'; const result = await applyMultiHarnessPlan({ harnesses: [{ id: 'kimi', preview }], request: { harnesses: ['kimi'] }, }, { preflightManaged(candidatePlan) { const latestPreview = preflightManagedPlan(candidatePlan); writeFile(plan.installStatePath, unexpectedState); return latestPreview; }, }); assert.strictEqual(result.status, 'failed'); assert.match(result.failure.message, /unowned or changed install-state/i); assert.deepStrictEqual(result.retryHarnesses, ['kimi']); assert.strictEqual(fs.existsSync(destination), false); assert.strictEqual(fs.readFileSync(plan.installStatePath, 'utf8'), unexpectedState); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); await test('applies in catalog order and reports partial completion with an exact retry set', async () => { const plan = { harnesses: [ { id: 'claude', preview: {} }, { id: 'codex', preview: {} }, { id: 'kimi', preview: {} }, ], request: { harnesses: ['claude', 'codex', 'kimi'] }, }; const events = []; const result = await applyMultiHarnessPlan(plan, { applyClaude: async () => { events.push('claude'); return { action: 'installed' }; }, applyCodex: async () => { events.push('codex'); throw new Error('verification failed'); }, applyManaged: async () => { events.push('kimi'); return { applied: true }; }, }); assert.deepStrictEqual(events, ['claude', 'codex']); assert.strictEqual(result.status, 'partial'); assert.deepStrictEqual(result.completed.map(item => item.id), ['claude']); assert.strictEqual(result.failure.id, 'codex'); assert.deepStrictEqual(result.retryHarnesses, ['codex', 'kimi']); }); await test('a late Kimi permission failure retries only Kimi', async () => { const plan = { harnesses: [ { id: 'claude', preview: {} }, { id: 'codex', preview: {} }, { id: 'kimi', preview: {} }, ], request: { harnesses: ['claude', 'codex', 'kimi'] }, }; const events = []; const result = await applyMultiHarnessPlan(plan, { applyClaude: async () => { events.push('claude'); return { action: 'installed' }; }, applyCodex: async () => { events.push('codex'); return { action: 'installed' }; }, applyManaged: async () => { events.push('kimi'); const error = new Error('permission denied'); error.code = 'EACCES'; throw error; }, }); assert.deepStrictEqual(events, ['claude', 'codex', 'kimi']); assert.strictEqual(result.status, 'partial'); assert.deepStrictEqual(result.completed.map(item => item.id), ['claude', 'codex']); assert.strictEqual(result.failure.id, 'kimi'); assert.deepStrictEqual(result.retryHarnesses, ['kimi']); }); console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exitCode = failed > 0 ? 1 : 0; })();