Route selection moves into one helper, pickRoute(proxyOf), that both checks call: the primary preflight, and the fallback with its own preflight only when the primary's fails, with the same messages. The final check passes proxyFor and is otherwise unchanged. The first check of a proxy launch passes a stand-in session proxy (marker key PENDING_ADMISSION_KEY and the configured loopback base URL), which reaches only the preflights, then runs gate() with a null session proxy on the predicted route only. The round 1 union over both routes is gone. The managed fixture takes extra (more harnesses switched on), real (harnesses loaded unfaked) and hostEnv. Seven tests in proxy-launch-order.test.mjs.
244 lines
15 KiB
JavaScript
244 lines
15 KiB
JavaScript
// Vikunja #774 F1 (row 332 review): with system.proxy set, a launch that can never start refuses in
|
|
// the first check, before authorizeLaunch, the workspace and checkouts, and the P2 modelPreflight
|
|
// that may issue a key. The first check predicts the route from the harness preflights on a
|
|
// stand-in key (R2); only the session proxy key match waits for the admitted key, in the final
|
|
// check. Real launcher, bus runtime and runner (managed P2 fixture).
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { DatabaseSync } from 'node:sqlite';
|
|
import { PENDING_ADMISSION_KEY } from '../src/launcher.mjs';
|
|
import { sandboxProblem } from '../../seat/src/sandbox.mjs';
|
|
import { workspaceDir } from '../../seat/src/session.mjs';
|
|
import { managedLaunchFixture } from './model-managed-launch-fixture.mjs';
|
|
|
|
const skip = sandboxProblem() ?? false;
|
|
function editBusiness(f, update) {
|
|
const path = join(f.root, 'config', 'businesses', 'acme.json');
|
|
const document = JSON.parse(fs.readFileSync(path, 'utf8'));
|
|
update(document);
|
|
fs.writeFileSync(path, JSON.stringify(document) + '\n', { mode: 0o600 });
|
|
}
|
|
// A capability the bus issued to a claimed chief, as a launched chief holds.
|
|
function chief(f) {
|
|
const cap = f.bus.broker.bindLaunch({ business: 'acme', role: 'chief', run: 'chief-774', harness: f.f.doc.roles.chief.vars.harness });
|
|
f.bus.broker.request(cap, { verb: 'role.claim' });
|
|
return cap;
|
|
}
|
|
// The refusal came from the first check: nothing after it ran.
|
|
function refusedFirst(f, before, preflights, bindings = 0, instance = 'coder') {
|
|
assert.equal(f.trace.preflightAttempts, preflights, 'no modelPreflight for a launch the first check refuses');
|
|
assert.deepEqual(f.trace.ops.slice(before).filter(op => ['authorizeLaunch', 'credentialStatus', 'modelPreflight'].includes(op)), [],
|
|
'no authorizeLaunch, credentialStatus or modelPreflight after the first check');
|
|
assert.equal(fs.existsSync(join(workspaceDir(f.f.dataRoot, 'acme', instance), 'stack')), false, 'no checkout clone or fetch');
|
|
assert.equal(f.trace.bindings.length, bindings, 'no runner bound for the refused launch');
|
|
}
|
|
const refusal = (f, code) => f.launchLog().filter(e => e.event === 'refused' && e.code === code);
|
|
const claudeCoder = doc => {
|
|
doc.launch.models['claude-opus-5-5'].provider = 'anthropic';
|
|
doc.roles.coder.vars.model = 'claude-opus-5-5';
|
|
};
|
|
|
|
test('#774 F1 proxy capacity-full refuses in the first check: no modelPreflight, checkout or authorizeLaunch; the ordinary launch before it goes live', { skip, timeout: 60000 }, async t => {
|
|
const f = await managedLaunchFixture(t);
|
|
const cap = chief(f);
|
|
const ordinary = await f.launcher.fromSession(cap, 'reviewer');
|
|
assert.equal(ordinary.ok, true, JSON.stringify(ordinary));
|
|
assert.ok(f.sessions().find(s => s.run === ordinary.result.run)?.live, 'the ordinary proxy launch is live');
|
|
assert.equal(f.trace.preflightAttempts, 1, 'the ordinary proxy launch makes one P2 admission');
|
|
editBusiness(f, doc => { doc.launch.limits.total = 1; });
|
|
const before = f.trace.ops.length;
|
|
assert.deepEqual(await f.launcher.fromSession(cap, 'coder'), { ok: false, error: 'capacity-full' });
|
|
refusedFirst(f, before, 1, 1);
|
|
assert.deepEqual(refusal(f, 'capacity-full').map(e => e.limit), [{ scope: 'total', limit: 1, running: 1 }]);
|
|
f.valueFree();
|
|
});
|
|
|
|
test('#774 F1 proxy limit-reached refuses in the first check: no modelPreflight, checkout or authorizeLaunch', { skip, timeout: 60000 }, async t => {
|
|
const f = await managedLaunchFixture(t);
|
|
editBusiness(f, doc => {
|
|
doc.plugins = { ...doc.plugins, 'limit-check': ['token-limit'] };
|
|
doc.launch.limits.tokens = { instances: { coder: { limit: 0, since: '2026-10-18T00:00:00.000Z' } } };
|
|
});
|
|
const before = f.trace.ops.length;
|
|
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'limit-reached' });
|
|
refusedFirst(f, before, 0);
|
|
assert.equal(refusal(f, 'limit-reached')[0]?.limit?.check, 'token-limit');
|
|
assert.equal(f.runDirectories().length, 0);
|
|
f.valueFree();
|
|
});
|
|
|
|
test('#774 F1 proxy sandbox-unavailable refuses in the first check: no modelPreflight, checkout or authorizeLaunch', { skip, timeout: 60000 }, async t => {
|
|
const f = await managedLaunchFixture(t, { bwrap: '/nonexistent-774/bwrap' });
|
|
const before = f.trace.ops.length;
|
|
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'sandbox-unavailable' });
|
|
refusedFirst(f, before, 0);
|
|
assert.equal(f.runDirectories().length, 0);
|
|
f.valueFree();
|
|
});
|
|
|
|
test('#774 F1 proxy launch.account declared with an upstream credential refuses in the first check, before modelPreflight', { skip, timeout: 60000 }, async t => {
|
|
const f = await managedLaunchFixture(t, { harness: 'claude-code' });
|
|
editBusiness(f, doc => {
|
|
claudeCoder(doc);
|
|
doc.launch.account = { name: 'jason-max', credential: 'CLAUDE_CODE_OAUTH_TOKEN' };
|
|
});
|
|
const before = f.trace.ops.length;
|
|
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'account-credential-missing' });
|
|
refusedFirst(f, before, 0);
|
|
assert.equal(refusal(f, 'account-credential-missing')[0]?.why, 'account jason-max needs the declared proxy credential');
|
|
assert.equal(f.runDirectories().length, 0);
|
|
f.valueFree();
|
|
});
|
|
|
|
// The key match needs the admitted key, so it stays in the final check: after modelPreflight,
|
|
// before newRun and spawn. The fixture makes P2 admit a key equal to the account name.
|
|
test('#774 F1 proxy session key equal to launch.account.name still refuses account-is-credential in the final check', { skip, timeout: 60000 }, async t => {
|
|
const f = await managedLaunchFixture(t, { harness: 'claude-code', admit: value => ({ ...value, key: 'jason-max' }) });
|
|
editBusiness(f, doc => {
|
|
claudeCoder(doc);
|
|
doc.launch.account = { name: 'jason-max', credential: 'proxy' };
|
|
});
|
|
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'account-is-credential' });
|
|
assert.equal(f.trace.preflightAttempts, 1, 'the first check passed and P2 admitted a key');
|
|
assert.equal(f.trace.ops.filter(op => op === 'authorizeLaunch').length, 1);
|
|
assert.equal(refusal(f, 'account-is-credential')[0]?.why, 'launch.account.name is the value of session proxy key; it names the account, never its credential');
|
|
assert.equal(f.trace.bindings.length, 0);
|
|
assert.equal(f.sessions().length, 0);
|
|
assert.equal(f.runDirectories().length, 0, 'refused before newRun');
|
|
f.valueFree();
|
|
});
|
|
|
|
// Row 333: under a daily key quota the bus refuses mayLaunch key-quota-no-model-key for an agent
|
|
// with no model key. In the first check that is the one answer that waits for P2 to issue a key.
|
|
test('#774 F1 proxy launch of an agent with no key yet passes the first check under a daily key quota, and both checks ask mayLaunch', { skip, timeout: 60000 }, async t => {
|
|
const f = await managedLaunchFixture(t);
|
|
editBusiness(f, doc => {
|
|
doc.plugins = { ...doc.plugins, 'limit-check': ['token-limit'] };
|
|
doc.launch.limits.tokens = { keys: { '000000000774': { limit: 1000000, since: '2026-10-18T00:00:00.000Z', reset: '00:00', timeZone: 'UTC' } } };
|
|
});
|
|
assert.equal(f.bus.credentials.modelRecord('acme/chief'), null, 'chief holds no model key yet');
|
|
const answer = await f.launcher.launchPm();
|
|
assert.ok(f.sessions().find(s => s.run === answer.run)?.live, 'the launch goes live');
|
|
assert.equal(f.trace.successfulPreflights, 1);
|
|
assert.equal(f.trace.ops.filter(op => op === 'mayLaunch').length, 2, 'mayLaunch in the first and the final check');
|
|
f.valueFree();
|
|
});
|
|
|
|
// #774 R2: the first check gates the route the final check picks. The coder's primary is the real
|
|
// claude-code module on claude-opus-5-5 (provider anthropic); MOSAIC_CLAUDE_BIN decides whether its
|
|
// preflight passes. The stated fallback is pi on glm-5.3-flash (provider zai).
|
|
const NO_CLAUDE = '/nonexistent-774/claude';
|
|
const NO_CLAUDE_WHY = `MOSAIC_CLAUDE_BIN ${NO_CLAUDE} isn't an executable file at an absolute path`;
|
|
const routed = (t, bin) => managedLaunchFixture(t, { harness: 'claude-code', extra: ['pi'], real: ['claude-code'], hostEnv: { MOSAIC_CLAUDE_BIN: bin } });
|
|
const withFallback = (doc, harness = 'pi', model = 'glm-5.3-flash') => {
|
|
claudeCoder(doc);
|
|
doc.roles.coder.vars = { ...doc.roles.coder.vars, 'fallback.harness': harness, 'fallback.model': model, 'fallback.thinking': 'high' };
|
|
};
|
|
const full = (doc, provider, model) => { doc.launch.limits.models = { ...doc.launch.limits.models, [provider]: { ...doc.launch.limits.models?.[provider], [model]: 0 } }; };
|
|
const launched = (f, run) => f.launchLog().find(e => e.event === 'launch' && e.run === run);
|
|
|
|
test('#774 R2 a healthy primary at capacity zero refuses capacity-full in the first check, though the fallback has room', { skip, timeout: 60000 }, async t => {
|
|
const f = await routed(t, process.execPath);
|
|
editBusiness(f, doc => { withFallback(doc); full(doc, 'anthropic', 'claude-opus-5-5'); });
|
|
const before = f.trace.ops.length;
|
|
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'capacity-full' });
|
|
assert.deepEqual(refusal(f, 'capacity-full').map(e => e.limit), [{ scope: 'model', limit: 0, running: 0, provider: 'anthropic', model: 'claude-opus-5-5' }]);
|
|
refusedFirst(f, before, 0);
|
|
assert.equal(f.runDirectories().length, 0);
|
|
f.valueFree();
|
|
});
|
|
|
|
test('#774 R2 a failed primary preflight with a stated fallback launches on the fallback, route fallback, reason harness-binary-missing', { skip, timeout: 60000 }, async t => {
|
|
const f = await routed(t, NO_CLAUDE);
|
|
editBusiness(f, doc => withFallback(doc));
|
|
const answer = await f.launcher.fromSession(chief(f), 'coder');
|
|
assert.equal(answer.ok, true, JSON.stringify(answer));
|
|
assert.deepEqual(answer.result, { run: answer.result.run, instance: 'coder', harness: 'pi', model: 'glm-5.3-flash' });
|
|
assert.ok(f.sessions().find(s => s.run === answer.result.run)?.live, 'the fallback launch is live');
|
|
const event = launched(f, answer.result.run);
|
|
assert.deepEqual([event?.harness, event?.route, event?.routeReason, event?.provider, event?.model], ['pi', 'fallback', 'harness-binary-missing', 'zai', 'glm-5.3-flash']);
|
|
assert.equal(f.trace.successfulPreflights, 1);
|
|
assert.equal(f.trace.ops.filter(op => op === 'authorizeLaunch').length, 1);
|
|
f.valueFree();
|
|
});
|
|
|
|
test('#774 R2 a failed primary preflight at capacity zero still launches on the fallback: the first check gates the predicted route', { skip, timeout: 60000 }, async t => {
|
|
const f = await routed(t, NO_CLAUDE);
|
|
editBusiness(f, doc => { withFallback(doc); full(doc, 'anthropic', 'claude-opus-5-5'); });
|
|
const answer = await f.launcher.fromSession(chief(f), 'coder');
|
|
assert.equal(answer.ok, true, JSON.stringify(answer));
|
|
assert.ok(f.sessions().find(s => s.run === answer.result.run)?.live, 'the fallback launch is live');
|
|
const event = launched(f, answer.result.run);
|
|
assert.deepEqual([event?.harness, event?.route, event?.routeReason], ['pi', 'fallback', 'harness-binary-missing']);
|
|
assert.equal(refusal(f, 'capacity-full').length, 0);
|
|
f.valueFree();
|
|
});
|
|
|
|
test('#774 R2 a failed primary preflight and a fallback at capacity zero refuse capacity-full for the fallback in the first check', { skip, timeout: 60000 }, async t => {
|
|
const f = await routed(t, NO_CLAUDE);
|
|
editBusiness(f, doc => { withFallback(doc); full(doc, 'zai', 'glm-5.3-flash'); });
|
|
const before = f.trace.ops.length;
|
|
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'capacity-full' });
|
|
assert.deepEqual(refusal(f, 'capacity-full').map(e => e.limit), [{ scope: 'model', limit: 0, running: 0, provider: 'zai', model: 'glm-5.3-flash' }]);
|
|
refusedFirst(f, before, 0);
|
|
assert.equal(f.runDirectories().length, 0);
|
|
f.valueFree();
|
|
});
|
|
|
|
test('#774 R2 when both preflights fail the first check refuses with the final check\'s combined code and message', { skip, timeout: 60000 }, async t => {
|
|
const f = await routed(t, NO_CLAUDE);
|
|
editBusiness(f, doc => withFallback(doc, 'claude-code', 'claude-sonnet-5-5'));
|
|
const before = f.trace.ops.length;
|
|
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'harness-binary-missing' });
|
|
assert.deepEqual(refusal(f, 'harness-binary-missing').map(e => e.why), [`fallback claude-code: ${NO_CLAUDE_WHY} (primary claude-code: ${NO_CLAUDE_WHY})`]);
|
|
refusedFirst(f, before, 0);
|
|
assert.equal(f.runDirectories().length, 0);
|
|
f.valueFree();
|
|
});
|
|
|
|
test('#774 R2 a failed primary preflight with no fallback refuses harness-binary-missing in the first check', { skip, timeout: 60000 }, async t => {
|
|
const f = await routed(t, NO_CLAUDE);
|
|
editBusiness(f, claudeCoder);
|
|
const before = f.trace.ops.length;
|
|
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'harness-binary-missing' });
|
|
assert.deepEqual(refusal(f, 'harness-binary-missing').map(e => e.why), [NO_CLAUDE_WHY]);
|
|
refusedFirst(f, before, 0);
|
|
assert.equal(f.runDirectories().length, 0);
|
|
f.valueFree();
|
|
});
|
|
|
|
// Both preflights of the first check saw the stand-in key; none of it reaches the evidence.
|
|
test('#774 R2 the stand-in key never reaches the launch log, the authorization record, session.json or the data root', { skip, timeout: 60000 }, async t => {
|
|
assert.equal(typeof PENDING_ADMISSION_KEY === 'string' && PENDING_ADMISSION_KEY.length > 0, true);
|
|
const f = await routed(t, NO_CLAUDE);
|
|
editBusiness(f, doc => withFallback(doc));
|
|
const answer = await f.launcher.fromSession(chief(f), 'coder');
|
|
assert.equal(answer.ok, true, JSON.stringify(answer));
|
|
assert.ok(f.sessions().find(s => s.run === answer.result.run)?.live, 'the fallback launch is live');
|
|
const absent = (what, value) => assert.equal(String(value).includes(PENDING_ADMISSION_KEY), false, `the stand-in key reached ${what}`);
|
|
const log = f.launchLog();
|
|
assert.ok(launched(f, answer.result.run), 'the launch log holds the launch');
|
|
absent('the launch log', JSON.stringify(log));
|
|
const db = new DatabaseSync(join(f.f.dataRoot, 'bus', 'bus.sqlite'), { readOnly: true });
|
|
let events;
|
|
try { events = db.prepare('SELECT kind, subject, body FROM events ORDER BY seq').all(); } finally { db.close(); }
|
|
assert.equal(events.filter(e => e.kind === 'action.allowed' && JSON.parse(e.body).action === 'role.launch').length, 1, 'one authorization record for the launch');
|
|
absent('the bus events', JSON.stringify(events));
|
|
const session = join(f.runDirectories().find(d => d.endsWith(`/${answer.result.run}`)), 'session.json');
|
|
assert.ok(fs.existsSync(session), 'the run has a session.json');
|
|
absent('session.json', fs.readFileSync(session, 'utf8'));
|
|
absent('the host log', JSON.stringify(f.logs));
|
|
absent('the trace', JSON.stringify(f.trace));
|
|
// Every regular file under the data root, the bus database and its WAL included.
|
|
const files = fs.readdirSync(f.f.dataRoot, { recursive: true, withFileTypes: true }).filter(e => e.isFile()).map(e => join(e.parentPath, e.name));
|
|
assert.ok(files.length > 0);
|
|
for (const file of files) {
|
|
let bytes;
|
|
try { bytes = fs.readFileSync(file); } catch (e) { if (e.code === 'ENOENT') continue; throw e; }
|
|
absent(file, bytes);
|
|
}
|
|
f.valueFree();
|
|
});
|