Files
nexu-io__open-design/apps/daemon/tests/plugins-strategy-package.test.ts
open-design-crew[bot] c8f6db60c0 perf(od-next): streamline tool work and reinforce protocol output (#7804)
* perf(od-next): streamline tool use and media input prompts

* fix(od-next): drop the runtime-state literal from the core tool rule

The v2.1.1 core prompt named `open-design-runtime-state` inside the
"not a tool name" bullet. In the two post-optimization eval batches the
model called that exact name as a tool in 5 of 29 deliveries (1 of 30
before the sentence existed): a negated mention raises the name's
salience right where the model is about to emit the runtime-state block.

Keep the rule, drop the literal: protocol blocks are structured text
output, not tools. Bump core asset to 2.1.2 and the bundled package to
2.0.2; orchestration is untouched and stays 2.0.1.

* fix(od-next): carry protocol output guidance into tool optimization

* fix(od-next): preserve run workspace scope in dynamic skill reads

---------

Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com>
Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>
2026-09-08 11:48:16 +00:00

313 lines
13 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { chmod, cp, mkdtemp, readFile, rm, symlink, unlink, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import Database from 'better-sqlite3';
import type { InstalledPluginRecord } from '@open-design/contracts';
import { migratePlugins } from '../src/plugins/persistence.js';
import { resolvePluginFolder } from '../src/plugins/registry.js';
import {
StrategyPackageAssetPathError,
createBundledStrategyBindingV2,
loadBundledStrategyPromptAssetsV2,
} from '../src/plugins/strategy-package.js';
import { resolvePluginSnapshot } from '../src/plugins/resolve-snapshot.js';
const SOURCE = path.resolve(
import.meta.dirname,
'../../../plugins/_official/scenarios/od-next-strategy',
);
const REGISTRY = {
skills: [],
designSystems: [],
craft: [],
atoms: [],
scenarios: [],
};
let db: Database.Database;
let tmpDir: string;
beforeEach(async () => {
tmpDir = await mkdtemp(path.join(os.tmpdir(), 'od-strategy-package-'));
db = new Database(':memory:');
db.exec(`
CREATE TABLE projects (id TEXT PRIMARY KEY, name TEXT);
CREATE TABLE conversations (id TEXT PRIMARY KEY, project_id TEXT, title TEXT);
`);
migratePlugins(db);
db.prepare('INSERT INTO projects (id, name) VALUES (?, ?)').run('project-1', 'Project 1');
});
afterEach(async () => {
db.close();
await rm(tmpDir, { recursive: true, force: true });
});
async function resolveStrategyRecord(folder = SOURCE): Promise<InstalledPluginRecord> {
const resolved = await resolvePluginFolder({
folder,
folderId: 'od-next-strategy',
sourceKind: 'bundled',
source: folder,
trust: 'bundled',
});
if (!resolved.ok) throw new Error(resolved.errors.join('; '));
return resolved.record;
}
describe('bundled OD Next strategy package identity', () => {
it('reads only the explicit manifest, skill, core, orchestration, selected profile, its resources, and reference', async () => {
const plugin = await resolveStrategyRecord();
const prototype = createBundledStrategyBindingV2({ plugin, taskType: 'prototype' });
const repeated = createBundledStrategyBindingV2({ plugin, taskType: 'prototype' });
const hyperframes = createBundledStrategyBindingV2({ plugin, taskType: 'hyperframes' });
expect(repeated).toEqual(prototype);
expect(prototype.assetDigests.map((asset) => asset.path)).toEqual([
'./SKILL.md',
'./assets/core-system-prompt.md',
'./assets/general-orchestration.md',
'./assets/task-profiles/prototype.md',
'./assets/task-profiles/prototype/device-frames/android.html',
'./assets/task-profiles/prototype/device-frames/iphone.html',
'./assets/task-profiles/prototype/device-frames/neutral.html',
'./assets/task-profiles/prototype/layout.css',
'./open-design.json',
'./references/task-profile-mapping.md',
]);
// Resources travel with the profile that declares them only.
expect(hyperframes.assetDigests.map((asset) => asset.path)).toEqual([
'./SKILL.md',
'./assets/core-system-prompt.md',
'./assets/general-orchestration.md',
'./assets/task-profiles/hyperframes.md',
'./open-design.json',
'./references/task-profile-mapping.md',
]);
expect(prototype.selectedTaskProfile).toEqual(expect.objectContaining({
taskType: 'prototype',
version: '2.2.0',
path: './assets/task-profiles/prototype.md',
}));
expect(hyperframes.packageHash).not.toBe(prototype.packageHash);
expect(hyperframes.selectedTaskProfile.sha256).not.toBe(
prototype.selectedTaskProfile.sha256,
);
});
it('decodes the prototype device shells as task resources and locks them into the package identity', async () => {
const plugin = await resolveStrategyRecord();
const binding = createBundledStrategyBindingV2({ plugin, taskType: 'prototype' });
const assets = loadBundledStrategyPromptAssetsV2({ plugin, binding });
expect(assets.taskResources.map((resource) => resource.path)).toEqual([
'./assets/task-profiles/prototype/device-frames/iphone.html',
'./assets/task-profiles/prototype/device-frames/android.html',
'./assets/task-profiles/prototype/device-frames/neutral.html',
'./assets/task-profiles/prototype/layout.css',
]);
for (const resource of assets.taskResources) {
if (resource.path.endsWith('layout.css')) {
expect(resource.text).toContain('OD-LAYOUT-PRIMITIVES v1');
expect(resource.text).toContain('@layer od-layout');
expect(resource.text).not.toMatch(/color\s*:|font-family|border-radius|box-shadow/);
continue;
}
expect(resource.text).toContain('data-phone-shell');
expect(resource.text).toContain('APP CONTENT START');
expect(resource.text).toContain('class="phone-content"');
}
expect(loadBundledStrategyPromptAssetsV2({
plugin,
binding: createBundledStrategyBindingV2({ plugin, taskType: 'hyperframes' }),
}).taskResources).toEqual([]);
// A shell edit moves the prototype package hash exactly like a rule-card
// edit, and leaves a profile that does not declare the shell untouched.
const folder = path.join(tmpDir, 'strategy');
await cp(SOURCE, folder, { recursive: true });
const copied = await resolveStrategyRecord(folder);
const prototypeBaseline = createBundledStrategyBindingV2({ plugin: copied, taskType: 'prototype' });
const hyperframesBaseline = createBundledStrategyBindingV2({ plugin: copied, taskType: 'hyperframes' });
const shellPath = path.join(folder, 'assets/task-profiles/prototype/device-frames/neutral.html');
await writeFile(shellPath, `${await readFile(shellPath, 'utf8')}\n<!-- edited -->\n`);
expect(createBundledStrategyBindingV2({ plugin: copied, taskType: 'prototype' }).packageHash)
.not.toBe(prototypeBaseline.packageHash);
expect(createBundledStrategyBindingV2({ plugin: copied, taskType: 'hyperframes' }).packageHash)
.toBe(hyperframesBaseline.packageHash);
expect(() => loadBundledStrategyPromptAssetsV2({ plugin: copied, binding: prototypeBaseline }))
.toThrow(/no longer matches/i);
});
it('changes for a declared byte but ignores an undeclared file', async () => {
const folder = path.join(tmpDir, 'strategy');
await cp(SOURCE, folder, { recursive: true });
const plugin = await resolveStrategyRecord(folder);
const baseline = createBundledStrategyBindingV2({ plugin, taskType: 'prototype' });
await writeFile(path.join(folder, 'undeclared.txt'), 'not part of identity');
expect(createBundledStrategyBindingV2({ plugin, taskType: 'prototype' }).packageHash)
.toBe(baseline.packageHash);
const referencePath = path.join(folder, 'references/task-profile-mapping.md');
const reference = await readFile(referencePath);
const changedReference = Buffer.from(reference);
changedReference[0] = changedReference[0] === 0x23 ? 0x20 : 0x23;
await writeFile(referencePath, changedReference);
expect(createBundledStrategyBindingV2({ plugin, taskType: 'prototype' }).packageHash)
.not.toBe(baseline.packageHash);
await writeFile(referencePath, reference);
const corePath = path.join(folder, 'assets/core-system-prompt.md');
const core = await readFile(corePath);
core[0] = core[0] === 0x23 ? 0x20 : 0x23;
await writeFile(corePath, core);
expect(createBundledStrategyBindingV2({ plugin, taskType: 'prototype' }).packageHash)
.not.toBe(baseline.packageHash);
});
it.skipIf(process.platform === 'win32' || process.getuid?.() === 0)(
'normalizes an unreadable declared asset into the narrow identity error',
async () => {
const folder = path.join(tmpDir, 'strategy');
await cp(SOURCE, folder, { recursive: true });
const plugin = await resolveStrategyRecord(folder);
const corePath = path.join(folder, 'assets/core-system-prompt.md');
await chmod(corePath, 0o000);
try {
expect(() => createBundledStrategyBindingV2({
plugin,
taskType: 'prototype',
})).toThrow(/unavailable/i);
} finally {
await chmod(corePath, 0o600);
}
},
);
it.each(['../escape.md', '/absolute.md'])('rejects declaration path %s', async (assetPath) => {
const plugin = await resolveStrategyRecord();
const strategy = (plugin.manifest.od as Record<string, unknown>)['strategy'] as {
assets: { core: { path: string } };
};
const mutated: InstalledPluginRecord = {
...plugin,
manifest: {
...plugin.manifest,
od: {
...plugin.manifest.od,
strategy: {
...strategy,
assets: {
...strategy.assets,
core: { ...strategy.assets.core, path: assetPath },
},
},
},
},
};
expect(() => createBundledStrategyBindingV2({
plugin: mutated,
taskType: 'prototype',
})).toThrow(StrategyPackageAssetPathError);
});
it.skipIf(process.platform === 'win32')('rejects a declared symlink that escapes the plugin root', async () => {
const folder = path.join(tmpDir, 'strategy');
await cp(SOURCE, folder, { recursive: true });
const outside = path.join(tmpDir, 'outside.md');
await writeFile(outside, 'outside bytes');
const corePath = path.join(folder, 'assets/core-system-prompt.md');
await unlink(corePath);
await symlink(outside, corePath);
const plugin = await resolveStrategyRecord(folder);
expect(() => createBundledStrategyBindingV2({
plugin,
taskType: 'prototype',
})).toThrow(/symbolic link|escape/i);
});
it.skipIf(process.platform === 'win32')('rejects an intermediate symlink before reading a declared asset', async () => {
const folder = path.join(tmpDir, 'strategy');
await cp(SOURCE, folder, { recursive: true });
const plugin = await resolveStrategyRecord(folder);
const outsideAssets = path.join(tmpDir, 'outside-assets');
await cp(path.join(folder, 'assets'), outsideAssets, { recursive: true });
await rm(path.join(folder, 'assets'), { recursive: true });
await symlink(outsideAssets, path.join(folder, 'assets'));
expect(() => createBundledStrategyBindingV2({
plugin,
taskType: 'prototype',
})).toThrow(/symbolic link|escape/i);
});
});
describe('hash-gated internal strategy activation and snapshot persistence', () => {
it('keeps ordinary apply closed and persists a verified binding only through the internal owner', async () => {
const plugin = await resolveStrategyRecord();
const unavailableThroughRegistry = resolvePluginSnapshot({
db,
body: { pluginId: 'od-next-strategy' },
projectId: 'project-1',
registry: REGISTRY,
});
expect(unavailableThroughRegistry).toEqual(expect.objectContaining({
ok: false,
body: { error: expect.objectContaining({ code: 'plugin-not-found' }) },
}));
const ordinary = resolvePluginSnapshot({
db,
body: { pluginId: 'od-next-strategy' },
projectId: 'project-1',
plugin,
registry: REGISTRY,
});
expect(ordinary).toEqual(expect.objectContaining({
ok: false,
body: { error: expect.objectContaining({ code: 'strategy-inactive' }) },
}));
const activated = resolvePluginSnapshot({
db,
body: { pluginId: 'od-next-strategy' },
projectId: 'project-1',
registry: REGISTRY,
internalStrategyActivation: { taskType: 'prototype', plugin },
});
expect(activated).toEqual(expect.objectContaining({ ok: true, created: true }));
if (!activated || !activated.ok) throw new Error('expected strategy snapshot');
expect(activated.snapshot.strategy).toEqual(expect.objectContaining({
id: 'od-next-strategy',
version: '2.0.4',
packageHash: expect.stringMatching(/^[a-f0-9]{64}$/),
selectedTaskProfile: expect.objectContaining({ taskType: 'prototype' }),
}));
expect(activated.applyResult?.appliedPlugin.strategy).toEqual(
activated.snapshot.strategy,
);
});
it('normalizes missing declared content into a structured fail-closed result', async () => {
const folder = path.join(tmpDir, 'strategy');
await cp(SOURCE, folder, { recursive: true });
const plugin = await resolveStrategyRecord(folder);
await unlink(path.join(folder, 'assets/core-system-prompt.md'));
const result = resolvePluginSnapshot({
db,
body: { pluginId: 'od-next-strategy' },
projectId: 'project-1',
registry: REGISTRY,
internalStrategyActivation: { taskType: 'prototype', plugin },
});
expect(result).toEqual(expect.objectContaining({
ok: false,
body: { error: expect.objectContaining({ code: 'strategy-content-invalid' }) },
}));
});
});