Files
stack/scripts/case2/launch.mjs
T
jason.woltje 04e1ce24cc row 227 R2 (#1728): run test-case2.sh standalone, without the slot controller
Remove the R1 slot-controller ties from the C2-14 fixture: the ROW227_CONTROL_ROOT
check, the native permit wait in bindLaunch, the fixture-cleanup.json write, the
priority.mjs preload and the owned-bwrap.sh override. The launcher uses its own bwrap.

The shell runs the driver from an explicit env -i environment (PATH, LANG, a sandbox
HOME, XDG dirs and TMPDIR, MOSAIC_CONFIG, no global git config) as its own process
group. On a signal the driver gets 3 s by the clock, then its group and any process
whose cwd is in the sandbox get SIGKILL, inside the landing gate's 5 s grace.

The fixture credentials are defined only in scripts/case2/fixture-credentials.mjs.
The new scripts/case2/leak-check.mjs reads them from there and never prints them.
2026-11-02 15:41:17 -06:00

131 lines
8.0 KiB
JavaScript

// C2-14 fixture: the real host/launcher with C2-06's scripted citation adapter. No model binary runs.
import { chmodSync, mkdirSync, writeFileSync, existsSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { BusError, writeGrantEvent } from '../../packages/bus/src/broker.mjs';
import { Store } from '../../packages/bus/src/store.mjs';
import { startBroker } from '../../packages/bus/src/runtime.mjs';
import { grantDecision } from '../../packages/bus/tests/host-fixtures.mjs';
import { launchDoc } from '../../packages/business/tests/helpers.mjs';
import { fakeHarnesses, fixture } from '../../packages/cli/tests/helpers.mjs';
import { bootConfig, loadSystem } from '../../packages/cli/src/config.mjs';
import { createLauncher } from '../../packages/cli/src/launcher.mjs';
import { SESSION_CREDENTIALS } from '../../packages/seat/src/session.mjs';
import * as library from '../../packages/library/src/index.mjs';
import { FAKE_ZAI_KEY } from './fixture-credentials.mjs';
const ADAPTER = fileURLToPath(new URL('./citation-adapter.mjs', import.meta.url));
const REPLY_ADAPTER = fileURLToPath(new URL('../../packages/harness/tests/fixtures/citation-adapter.mjs', import.meta.url));
const ROTATE = '2099-01-01';
const AT = '2026-10-24T00:00:00.000Z';
const shell = value => "'" + String(value).replaceAll("'", "'\\''") + "'";
// A fixture grant is part of the brief. It uses the normal store shape; no executor/network mint or
// role-policy modification occurs. This is deliberately distinct from the row287 grant acceptance.
function seedGrant(dataRoot, storeRoot, token) {
const dir = join(storeRoot, 'acme', 'bean');
mkdirSync(dir, { recursive: true, mode: 0o700 });
writeFileSync(join(dir, 'gitea'), token + '\n', { mode: 0o600 });
const store = new Store(dataRoot);
try {
const decision = grantDecision(store, { business: 'acme', grant: {
method: 'gitea', agent: 'bean', target: { projects: ['books'] }, rotateBy: ROTATE,
} });
store.run('INSERT INTO gitea_users(business,agent,user,id,nonce,decision) VALUES(?,?,?,?,?,?)',
'acme', 'bean', 'acme-bean', 1, '2270227022702270', decision);
store.run('INSERT INTO gitea_grants(business,agent,user,token_id,token_name,projects,rotate_by,decision) VALUES(?,?,?,?,?,?,?,?)',
'acme', 'bean', 'acme-bean', 1, 'fixture-227', '["books"]', ROTATE, decision);
writeGrantEvent(store, AT, 'acme', { operation: 'grant.gitea', decision }, decision);
writeGrantEvent(store, AT, 'acme', { action: 'grant.applied', method: 'gitea', agent: 'bean',
grantDecision: decision, user: 'acme-bean', userId: 1, tokenId: 1, forks: [],
repositories: [{ project: 'books', repository: 'fixture', level: 'write' }] }, 'bean');
} finally { store.close(); }
}
export async function launchFixture(root, entry, { fetchToken, seatToken, humanToken, ca }) {
const configDir = join(root, 'config'), storeRoot = join(configDir, 'secrets', 'agents');
const slotDir = join(configDir, 'secrets', 'acme');
mkdirSync(slotDir, { recursive: true, mode: 0o700 });
for (const [slot, token] of [[library.SLOT, fetchToken], [library.HUMAN_SLOT, humanToken]])
writeFileSync(join(slotDir, slot), token + '\n', { mode: 0o600 });
const plan = join(root, 'citation-plan.json');
writeFileSync(plan, '[]\n', { mode: 0o600 });
const f = fixture(root, 'acme', doc => {
// The unused factory roles remain valid, but no tracker is configured or polled.
delete doc.tracker;
doc.projects = { books: entry };
doc.roles.chief.credentials = { gitea: { file: join(slotDir, library.SLOT), rotateBy: ROTATE } };
doc.roles.bean = { definition: 'companion',
credentials: { gitea: { store: true, rotateBy: ROTATE } },
identity: { name: 'Bean fixture', email: '[email protected]', gitea: { url: new URL(entry.remote).origin, user: 'acme-bean' } },
publish: ['books'], vars: { harness: 'pi', model: 'claude-sonnet-5-5', thinking: 'high' } };
doc.launch = { ...launchDoc('chief', ['bean'], { opus: 1, sonnet: 1 }), threadIdleSeconds: 3600 };
doc.plugins = { ...doc.plugins, channel: ['webui'] };
doc.replyLimits = { requireCitation: true, citationsPerReply: 20 };
return doc;
});
chmodSync(configDir, 0o700);
for (const key of SESSION_CREDENTIALS.flat()) delete f.env[key];
// Joined P3 keeps its real preflight; this fake route owns only its made-up ZAI key.
for (const key of ['OPENAI_API_KEY', 'OPENROUTER_API_KEY', 'ZAI_API_KEY']) delete f.env[key];
f.env.ZAI_API_KEY = FAKE_ZAI_KEY;
seedGrant(f.dataRoot, storeRoot, seatToken);
const system = loadSystem({ env: f.env });
const { trackers, ...boot } = bootConfig({ system, businessId: 'acme', env: f.env });
if (trackers !== undefined) throw new Error('acceptance fixture must not configure tracker polling');
const command = (kind, url, opts) => {
const c = library.syncCommand(kind, url, opts);
return { argv: c.argv, env: { ...c.env, GIT_SSL_CAINFO: ca } };
};
const runtime = await startBroker({ ...boot, librarySync: { command, intervalMs: 3_600_000, configDir } });
const hooks = [], cleanups = [], caps = new Map(), records = [], logs = [];
let closing = null;
const close = () => (closing ??= (async () => {
try { for (const hook of hooks) await hook(0); }
finally {
try { await runtime.close(); }
finally {
for (const cleanup of cleanups.reverse()) await cleanup();
const directory = join(f.dataRoot, 'control', 'runtimes');
const remaining = existsSync(directory) ? readdirSync(directory) : [];
if (remaining.length) throw new Error('owned-runtime-cleanup-incomplete');
}
}
})());
try {
const ops = {
identity: m => runtime.identity(m.cap), authorizeLaunch: m => runtime.authorizeLaunch(m.cap, m.instance),
refuse: m => runtime.refuse(m.cap, m.code, m.subject), endLaunch: m => runtime.endLaunch(m.record),
credentialStatus: m => runtime.credentialStatus(m.business, m.role), threadsState: m => runtime.threadsState(m.business),
threadIdle: m => runtime.threadIdle(m.record), mayLaunch: m => runtime.mayLaunch(m),
landHeld: m => runtime.landHeld(m.record), landHolders: m => runtime.landHolders(m.business),
landRelease: m => runtime.landRelease(m.record), sessionClears: m => runtime.sessionClears(m.business),
lifecycleRequests: m => runtime.lifecycleRequests(m.business),
};
const wrap = fn => async value => {
try { return await fn(value); }
catch (e) { throw Object.assign(new Error('fixture host refused'), { code: e instanceof BusError || /^[a-z-]+$/.test(e?.code ?? '') ? e.code : 'fixture-host-refused' }); }
};
const host = { business: 'acme', path: runtime.path, dataRoot: f.dataRoot,
op: wrap(m => { if (!Object.hasOwn(ops, m.op)) throw new BusError('invalid-host-request'); return ops[m.op](m); }),
bindLaunch: wrap(async record => {
const bound = runtime.bindLaunch(record);
records.push(record); caps.set(record.thread, bound.cap);
return bound;
}),
beforeClose: fn => void hooks.push(fn),
};
const adapter = join(root, 'citation-adapter.sh');
writeFileSync(adapter, `cp ${shell(plan)} "$(dirname "$MOSAIC_TURN_MARKER")/fixture-steps.json"\nexec ${shell(process.execPath)} ${shell(ADAPTER)}\n`, { mode: 0o644 });
const launcher = createLauncher({ host, system, env: f.env,
makeLoader: await fakeHarnesses({ after: fn => cleanups.push(fn) }, { pi: adapter }, write =>
write('channel', 'webui', `export * from ${JSON.stringify(new URL('../../packages/plugins/channel/webui/index.mjs', import.meta.url).href)};`)),
readOnly: [ADAPTER, REPLY_ADAPTER, plan], harnessVersion: '0.85.1',
sessionDefaults: { pollInterval: 50 }, threadTickMs: 50, log: line => logs.push(line),
});
await launcher.listen();
return { ...f, runtime, launcher, logs, records, plan, cap: thread => caps.get(thread), close,
business: boot.businesses.acme, setPlan: steps => writeFileSync(plan, JSON.stringify(steps) + '\n') };
} catch (e) { await close(); throw e; }
}