| 1 | // Real Node/Bun host and reviewed source closure; fake core transport. |
| 2 | // Rust receipt/admission/membership/install qualification is a separate gate. |
| 3 | import { test } from 'node:test' |
| 4 | import assert from 'node:assert/strict' |
| 5 | import { spawnSync } from 'node:child_process' |
| 6 | import { createHash } from 'node:crypto' |
| 7 | import { existsSync, mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs' |
| 8 | import { tmpdir } from 'node:os' |
| 9 | import { dirname, join } from 'node:path' |
| 10 | import { fileURLToPath } from 'node:url' |
| 11 | import { activate, startHost, FIXTURES, IS_BUN } from './harness.mjs' |
| 12 | |
| 13 | const here = dirname(fileURLToPath(import.meta.url)) |
| 14 | const hash = (value) => createHash('sha256').update(value).digest('hex') |
| 15 | |
| 16 | function temporary(t) { |
| 17 | const root = mkdtempSync(join(tmpdir(), 'cw-composition-')) |
| 18 | t.after(() => rmSync(root, { recursive: true, force: true })) |
| 19 | return root |
| 20 | } |
| 21 | |
| 22 | test('canonical Native composition adopts existing contributions and withdraws their exact handles', async (t) => { |
| 23 | const host = await startHost() |
| 24 | t.after(() => host.stop()) |
| 25 | const data_dir = temporary(t) |
| 26 | const entry = join(FIXTURES, 'native-dsh-composition', 'native', 'index.mjs') |
| 27 | const { ref, result } = await activate(host, 'native-dsh-composition', entry, { data_dir }) |
| 28 | assert.equal(result.status, 'ok', result.diagnostic) |
| 29 | const admitted = host.registry.filter(item => item.op === 'register') |
| 30 | assert.deepEqual(new Set(admitted.map(item => item.kind)), new Set(['tool', 'command', 'prompt_section', 'skill_root'])) |
| 31 | assert.equal(admitted.find(item => item.kind === 'prompt_section').spec.description, 'hello from upstream') |
| 32 | assert.equal(admitted.find(item => item.kind === 'skill_root').spec.name, 'source/skills') |
| 33 | const tool = admitted.find(item => item.kind === 'tool') |
| 34 | const answer = await host.call('tool/call', { handle: tool.handle, input: {}, call_id: 'composition-call', deadline_ms: 5000, workspace: data_dir }) |
| 35 | assert.deepEqual(answer.structured, { text: 'hello from upstream', count: null }) |
| 36 | assert.deepEqual(await host.call('ext/deactivate', { owner: ref }), { disposed: true, leaked: [] }) |
| 37 | for (const item of admitted) assert.ok(host.registry.some(row => row.op === 'unregister' && row.handle === item.handle)) |
| 38 | }) |
| 39 | |
| 40 | function counterComposition(t, id) { |
| 41 | const root = temporary(t) |
| 42 | mkdirSync(join(root, 'source')) |
| 43 | const source = `export const inject = ['prompt']; export function apply(ctx, config) { ctx.prompt.registerSection({id:${JSON.stringify(id)},text:String(config.count)}) }` |
| 44 | writeFileSync(join(root, 'source', 'row.mjs'), source) |
| 45 | const layer = `- insert:\n - id: count\n name: ./row.mjs\n config:\n count: !!js '(globalThis.__CW_COMPOSITION_COUNT = (globalThis.__CW_COMPOSITION_COUNT ?? 0) + 1)'\n` |
| 46 | const spec = { version: 1, layers: [{ path: 'base.yml', sha256: hash(layer), source: layer }], modules: [{ name: './row.mjs', path: 'row.mjs', sha256: hash(source) }], files: { 'row.mjs': hash(source) } } |
| 47 | writeFileSync(join(root, 'composition.json'), JSON.stringify(spec)) |
| 48 | const entry = join(root, 'index.mjs') |
| 49 | writeFileSync(entry, `import {mountReviewedComposition} from '@codewhale/dsh-composition'; import spec from './composition.json' with {type:'json'}; export async function apply(ctx) {await mountReviewedComposition(ctx,new URL('./source/',import.meta.url).href,spec)}`) |
| 50 | return entry |
| 51 | } |
| 52 | |
| 53 | test('two compositions share exactly one Loader and one teardown keeps the other owner live', async (t) => { |
| 54 | const host = await startHost() |
| 55 | t.after(() => host.stop()) |
| 56 | const a = await activate(host, 'composition-a', counterComposition(t, 'note-a')) |
| 57 | const b = await activate(host, 'composition-b', counterComposition(t, 'note-b')) |
| 58 | assert.equal(a.result.status, 'ok', a.result.diagnostic) |
| 59 | assert.equal(b.result.status, 'ok', b.result.diagnostic) |
| 60 | const notes = host.registry.filter(item => item.op === 'register' && item.kind === 'prompt_section') |
| 61 | assert.deepEqual(notes.map(item => item.spec.description), ['1', '2'], 'duplicate global Loader observers evaluated a configuration twice') |
| 62 | assert.deepEqual(await host.call('ext/deactivate', { owner: a.ref }), { disposed: true, leaked: [] }) |
| 63 | assert.ok(!host.registry.some(item => item.op === 'unregister' && item.handle === notes[1].handle)) |
| 64 | const c = await activate(host, 'composition-c', counterComposition(t, 'note-c')) |
| 65 | assert.equal(c.result.status, 'ok', c.result.diagnostic) |
| 66 | assert.equal(host.registry.filter(item => item.op === 'register' && item.kind === 'prompt_section').at(-1).spec.description, '3') |
| 67 | }) |
| 68 | |
| 69 | test('trusted offline reviewer preserves expressions and never imports row modules', (t) => { |
| 70 | const root = temporary(t) |
| 71 | const sentinel = join(root, 'expression-executed') |
| 72 | const layer = JSON.stringify([{ insert: [{ id: 'unexecuted', name: './row.mjs', config: { __jsExpr: `process.getBuiltinModule('fs').writeFileSync(${JSON.stringify(sentinel)},'unsafe')` } }] }]) |
| 73 | const spec = { version: 1, layers: [{ path: 'base.json', sha256: hash(layer), source: layer }], modules: [{ name: './row.mjs', path: 'row.mjs', sha256: '0'.repeat(64) }], files: { 'row.mjs': '0'.repeat(64) } } |
| 74 | const result = spawnSync(process.execPath, [join(here, '..', 'dist', 'dsh-composition-review.mjs')], { input: JSON.stringify(spec), encoding: 'utf8', timeout: 5000, maxBuffer: 8 * 1024 * 1024 }) |
| 75 | assert.equal(result.status, 0, result.stderr) |
| 76 | const review = JSON.parse(result.stdout) |
| 77 | assert.deepEqual(review.entries[0].config, JSON.parse(layer)[0].insert[0].config) |
| 78 | assert.equal(existsSync(sentinel), false) |
| 79 | assert.equal(existsSync(join(root, 'row.mjs')), false, 'review did not need any row module to exist') |
| 80 | }) |
| 81 | |
| 82 | function moduleClosureComposition(t, rowSource, files, extraRows = {}) { |
| 83 | const root=temporary(t) |
| 84 | mkdirSync(join(root,'source')) |
| 85 | const rows={'row.mjs':rowSource,...extraRows} |
| 86 | for (const [name,source] of Object.entries({...rows,...files})) writeFileSync(join(root,'source',name),source) |
| 87 | const layer=JSON.stringify([{insert:Object.keys(rows).map((name,index)=>({id:`row-${index}`,name:`./${name}`,config:{}}))}]) |
| 88 | const closure=Object.fromEntries(Object.entries({...rows,...files}).map(([name,source])=>[name,hash(source)])) |
| 89 | const spec={version:1,layers:[{path:'base.json',sha256:hash(layer),source:layer}],modules:Object.entries(rows).map(([name,source])=>({name:`./${name}`,path:name,sha256:hash(source)})),files:closure} |
| 90 | writeFileSync(join(root,'composition.json'),JSON.stringify(spec)) |
| 91 | const entry=join(root,'index.mjs') |
| 92 | writeFileSync(entry,`import {mountReviewedComposition} from '@codewhale/dsh-composition';import spec from './composition.json' with {type:'json'};export async function apply(ctx){await mountReviewedComposition(ctx,new URL('./source/',import.meta.url).href,spec)}`) |
| 93 | return {root,entry} |
| 94 | } |
| 95 | |
| 96 | test('a transitive reviewed relative helper executes but an undeclared helper never does',async t=>{ |
| 97 | const host=await startHost();t.after(()=>host.stop()) |
| 98 | const good=moduleClosureComposition(t,"import {text} from './helper.mjs';export const inject=['prompt'];export function apply(ctx){ctx.prompt.registerSection({id:'nested',text})}",{'helper.mjs':"export const text='reviewed helper'"}) |
| 99 | const mounted=await activate(host,'closure-good',good.entry) |
| 100 | assert.equal(mounted.result.status,'ok',mounted.result.diagnostic) |
| 101 | assert.equal(host.registry.find(row=>row.op==='register' && row.kind==='prompt_section').spec.description,'reviewed helper') |
| 102 | const bad=moduleClosureComposition(t,"import './rogue.mjs';export function apply(){}",{}) |
| 103 | const sentinel=join(bad.root,'rogue-executed') |
| 104 | writeFileSync(join(bad.root,'source','rogue.mjs'),`import {writeFileSync} from 'node:fs';writeFileSync(${JSON.stringify(sentinel)},'bad')`) |
| 105 | const refused=await activate(host,'closure-undeclared',bad.entry) |
| 106 | assert.equal(refused.result.status,'failed') |
| 107 | assert.equal(existsSync(sentinel),false) |
| 108 | assert.deepEqual(await host.call('ext/deactivate',{owner:mounted.ref}),{disposed:true,leaked:[]}) |
| 109 | }) |
| 110 | |
| 111 | test('changed transitive module bytes and ambient bare dependencies refuse before execution',async t=>{ |
| 112 | const host=await startHost();t.after(()=>host.stop()) |
| 113 | const changed=moduleClosureComposition(t,"import './helper.mjs';export function apply(){}",{'helper.mjs':"export const original=true"}) |
| 114 | const sentinel=join(changed.root,'changed-executed') |
| 115 | writeFileSync(join(changed.root,'source','helper.mjs'),`import {writeFileSync} from 'node:fs';writeFileSync(${JSON.stringify(sentinel)},'bad')`) |
| 116 | const refused=await activate(host,'closure-changed',changed.entry) |
| 117 | assert.equal(refused.result.status,'failed') |
| 118 | assert.equal(existsSync(sentinel),false) |
| 119 | const ambient=moduleClosureComposition(t,"import 'js-yaml';export function apply(){}",{}) |
| 120 | const unavailable=await activate(host,'closure-ambient',ambient.entry) |
| 121 | assert.equal(unavailable.result.status,'failed') |
| 122 | }) |
| 123 | |
| 124 | |
| 125 | test('reviewed composition refuses a row creation failure instead of acknowledging an empty tree', async t => { |
| 126 | const host=await startHost();t.after(()=>host.stop()) |
| 127 | const entry=counterComposition(t,'creation-probe') |
| 128 | const source=String.raw`export const inject=['prompt'];export function apply(ctx){throw new Error('intentional row startup refusal')}` |
| 129 | // Full rows stay reviewed; the failure occurs inside activation, not the offline reviewer. |
| 130 | const root=dirname(entry) |
| 131 | writeFileSync(join(root,'source','row.mjs'),source) |
| 132 | const layer=JSON.stringify([{insert:[{id:'must-start',name:'./row.mjs',config:{}}]}]) |
| 133 | writeFileSync(join(root,'composition.json'),JSON.stringify({version:1,layers:[{path:'base.json',sha256:hash(layer),source:layer}],modules:[{name:'./row.mjs',path:'row.mjs',sha256:hash(source)}],files:{'row.mjs':hash(source)}})) |
| 134 | const result=await activate(host,'startup-refusal',entry) |
| 135 | assert.equal(result.result.status,'failed') |
| 136 | assert.equal(host.registry.filter(row=>row.op==='register').length,0) |
| 137 | }) |
| 138 | |
| 139 | |
| 140 | test('reviewed computed and URL imports retain query identity and exact JSON data', async t => { |
| 141 | const host=await startHost();t.after(()=>host.stop()) |
| 142 | const row=`export const inject=['prompt'];export async function apply(ctx){ |
| 143 | const name='./helper.mjs';const first=await import(name); |
| 144 | const url=new URL('./helper.mjs?variant=two',import.meta.url); |
| 145 | const queried=await import(url);const again=await import(String(url)); |
| 146 | const data=await import('./data.json',{with:{type:'json'}}); |
| 147 | ctx.prompt.registerSection({id:'dynamic',text:JSON.stringify({text:first.text,queryDistinct:first!==queried,queryCached:queried===again,protoData:Object.hasOwn(data.default,'__proto__'),value:data.default.__proto__.answer})}); |
| 148 | }` |
| 149 | const composition=moduleClosureComposition(t,row,{'helper.mjs':"export const text='computed reviewed helper'",'data.json':'{"__proto__":{"answer":42}}'}) |
| 150 | const mounted=await activate(host,'closure-computed',composition.entry) |
| 151 | if(IS_BUN){ |
| 152 | assert.equal(mounted.result.status,'failed') |
| 153 | assert.match(mounted.result.diagnostic,/Bun does not preserve reviewed module query or fragment identity/) |
| 154 | assert.match(mounted.result.diagnostic,/runtime = "node"/) |
| 155 | assert.equal(host.registry.filter(row=>row.op==='register').length,0) |
| 156 | return |
| 157 | } |
| 158 | assert.equal(mounted.result.status,'ok',mounted.result.diagnostic) |
| 159 | assert.deepEqual(JSON.parse(host.registry.find(row=>row.op==='register' && row.kind==='prompt_section').spec.description),{text:'computed reviewed helper',queryDistinct:true,queryCached:true,protoData:true,value:42}) |
| 160 | assert.deepEqual(await host.call('ext/deactivate',{owner:mounted.ref}),{disposed:true,leaked:[]}) |
| 161 | }) |
| 162 | |
| 163 | test('unreviewed computed imports refuse before module side effects', async t => { |
| 164 | const host=await startHost();t.after(()=>host.stop()) |
| 165 | const composition=moduleClosureComposition(t,"export async function apply(){const name='./rogue.mjs';await import(name)}",{}) |
| 166 | const sentinel=join(composition.root,'computed-rogue-executed') |
| 167 | writeFileSync(join(composition.root,'source','rogue.mjs'),`import {writeFileSync} from 'node:fs';writeFileSync(${JSON.stringify(sentinel)},'bad')`) |
| 168 | const mounted=await activate(host,'closure-computed-rogue',composition.entry) |
| 169 | assert.equal(mounted.result.status,'failed') |
| 170 | assert.equal(existsSync(sentinel),false) |
| 171 | assert.equal(host.registry.filter(row=>row.op==='register').length,0) |
| 172 | }) |
| 173 | |
| 174 | test('separate reviewed rows retain the same transitive module instance', async t => { |
| 175 | const host=await startHost();t.after(()=>host.stop()) |
| 176 | const row=id=>`import {next} from './helper.mjs';export const inject=['prompt'];export function apply(ctx){ctx.prompt.registerSection({id:${JSON.stringify(id)},text:String(next())})}` |
| 177 | const composition=moduleClosureComposition(t,row('first'),{'helper.mjs':'let count=0;export function next(){return ++count}'},{'second.mjs':row('second')}) |
| 178 | const mounted=await activate(host,'closure-shared-instance',composition.entry) |
| 179 | assert.equal(mounted.result.status,'ok',mounted.result.diagnostic) |
| 180 | const registrations=host.registry.filter(row=>row.op==='register' && row.kind==='prompt_section') |
| 181 | assert.deepEqual(registrations.map(row=>row.spec.description),['1','2']) |
| 182 | assert.deepEqual(await host.call('ext/deactivate',{owner:mounted.ref}),{disposed:true,leaked:[]}) |
| 183 | for(const row of registrations)assert.ok(host.registry.some(item=>item.op==='unregister' && item.handle===row.handle)) |
| 184 | }) |
| 185 | |
| 186 | |
| 187 | test('reviewed computed URL imports and JSON preserve data on both runtimes', async t => { |
| 188 | const host=await startHost();t.after(()=>host.stop()) |
| 189 | const row=`export const inject=['prompt'];export async function apply(ctx){ |
| 190 | const target=new URL('./helper.mjs',import.meta.url);const helper=await import(target); |
| 191 | const again=await import(String(target));const data=await import('./data.json',{with:{type:'json'}}); |
| 192 | ctx.prompt.registerSection({id:'computed-url',text:JSON.stringify({text:helper.text,cached:helper===again,protoData:Object.hasOwn(data.default,'__proto__'),value:data.default.__proto__.answer})}); |
| 193 | }` |
| 194 | const composition=moduleClosureComposition(t,row,{'helper.mjs':"export const text='reviewed URL helper'",'data.json':'{"__proto__":{"answer":42}}'}) |
| 195 | const mounted=await activate(host,'closure-computed-url',composition.entry) |
| 196 | assert.equal(mounted.result.status,'ok',mounted.result.diagnostic) |
| 197 | assert.deepEqual(JSON.parse(host.registry.find(row=>row.op==='register' && row.kind==='prompt_section').spec.description),{text:'reviewed URL helper',cached:true,protoData:true,value:42}) |
| 198 | assert.deepEqual(await host.call('ext/deactivate',{owner:mounted.ref}),{disposed:true,leaked:[]}) |
| 199 | }) |
| 200 |