git mv packages/business -> packages/organization (@mosaic/organization). The old package stays as one-line re-export shims of each moved module, with src/business.mjs re-exporting src/organization-config.mjs and src/cli.mjs forwarding to the moved executable. BusinessError is OrganizationConfigError (one constructor, name kept BusinessError); OrganizationError stays distinct. The bus adapter gains organization-context.mjs (busOrganization), with business.mjs (busBusiness) as the same function. Import consumers, manifests, the lockfile, scripts/mosaic, stack-copy.sh COPY_PATHS (both roots), seat sandbox RUNNER_FILES and the copy closure walker (organization-imports.mjs, both roots required) move to the new path. v1 serialization, v1 writers and the stage A fixtures are unchanged; the stage A contract tests stay where they were and pass as before.
24 lines
2.6 KiB
JavaScript
24 lines
2.6 KiB
JavaScript
// REQ-LIMIT-1: pure launch-policy checks; no sandbox, Docker or session process.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {join} from 'node:path';
|
|
import {readFileSync,writeFileSync} from 'node:fs';
|
|
import {createLauncher} from '../src/launcher.mjs';
|
|
import {initializeControl} from '../../processing-control/src/state.mjs';
|
|
import {fixture,tmp,withoutHostCredentials} from './helpers.mjs';
|
|
import {systemFor,writeJson} from '../../organization/tests/helpers.mjs';
|
|
import {launchLogFile} from '../../seat/src/session.mjs';
|
|
test('each launch rereads limits and model selections; lowered limits keep sessions and log exact hit',async t=>{
|
|
const root=tmp(t),f=fixture(root,'acme',doc=>{doc.roles.coder.vars.model='claude-sonnet-5-5';return doc;}),file=join(root,'config/businesses/acme.json'),ops=[];
|
|
withoutHostCredentials(f.env,{piKey:true});
|
|
initializeControl(f.dataRoot);
|
|
const host={business:'acme',beforeClose:()=>{},op:async x=>{ops.push(x);if(x.op==='identity')return {business:'acme',role:'pm'};if(x.op==='mayLaunch')return {allow:true};if(x.op==='authorizeLaunch')throw Object.assign(Error('fixture authorization stop'),{code:'fixture-policy'});return {};}};
|
|
const launcher=createLauncher({host,system:JSON.parse(readFileSync(f.config,'utf8')),env:f.env,bwrap:'/usr/bin/true',log:()=>{}});
|
|
const session={business:'acme',instance:'reviewer',run:'fixture-existing',harness:'pi',provider:'zai',model:'claude-sonnet-5-5'};launcher.registry.add(session);
|
|
const update=limits=>{f.doc.launch.limits=limits;writeJson(file,f.doc);};
|
|
for(const [limits,scope] of [[{total:0},'total'],[{providers:{zai:1}},'provider'],[{models:{zai:{'claude-sonnet-5-5':1}}},'model']]){update(limits);assert.deepEqual(await launcher.fromSession('fixture-cap','coder'),{ok:false,error:'capacity-full'});const event=JSON.parse(readFileSync(launchLogFile(f.dataRoot,'acme'),'utf8').trim().split('\n').at(-1));assert.equal(event.limit.scope,scope);assert.equal(event.limit.running,1);assert.deepEqual(launcher.registry.sessions,[session]);}
|
|
update({total:2});assert.equal((await launcher.fromSession('fixture-cap','coder')).error,'fixture-policy');
|
|
f.doc.launch.models['claude-sonnet-5-5'].provider='another-provider';update({models:{zai:{'claude-sonnet-5-5':1}}});assert.equal((await launcher.fromSession('fixture-cap','coder')).error,'fixture-policy');assert.equal(launcher.registry.sessions[0].provider,'zai');
|
|
writeFileSync(file,'invalid JSON');assert.equal((await launcher.fromSession('fixture-cap','coder')).error,'organization-refused');assert.deepEqual(launcher.registry.sessions,[session]);
|
|
});
|