Row 138 round 2, Rocko's candidate e3f9a400 (43 paths) on26a0b0fab. Dewey approved (comment 28754) after round 1 changes (comment 28752): D1-D5 mutants killed, the 17 touched test files run singly passed 124/124. Rocko's own run: 753 tests and 59/59 mutants killed. The bus gains the project adapter through runtime.mjs, the way it already injects the organization, so broker.mjs takes no new imports and refuses with project-not-configured when no adapter is set. The project socket timeout is 120s. Roles gain record.write. The schema moves from 3e to 3f; the live migration follows MIGRATION.md and needs Mos's window. Sage's landing gate ran onc842686d5plus the candidate: 25 steps, all rc 0 (15 packages, 10 script suites), network sage-r138_default removed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
48 lines
3.0 KiB
JavaScript
48 lines
3.0 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { typedTools, callTool } from '../src/tools.mjs';
|
|
import { resolvedFor, fakeToolSocket } from './helpers.mjs';
|
|
import { fixture } from '../../bus/tests/spec-fixtures.mjs';
|
|
|
|
test('every role sees read_spec, record writer follows its grant, citation schemas stay strict', t => {
|
|
for (const role of ['coder', 'reviewer', 'pm', 'chief']) {
|
|
const tools = typedTools({ resolved: resolvedFor(t, role) });
|
|
const read = tools.find(t => t.name === 'read_spec'), write = tools.find(t => t.name === 'write_record');
|
|
assert.deepEqual([read.verb, read.action], ['spec.read', null]);
|
|
assert.deepEqual(read.parameters.required, ['project']);
|
|
assert.deepEqual(Object.keys(read.parameters.properties), ['project', 'commit']);
|
|
assert.equal(read.parameters.additionalProperties, false);
|
|
assert.deepEqual([write.verb, write.action], ['record.write', 'record.write']);
|
|
assert.deepEqual(write.parameters.required, ['project', 'path', 'record']);
|
|
assert.equal(write.parameters.additionalProperties, false);
|
|
assert.equal(Object.hasOwn(write.parameters.properties, 'author'), false);
|
|
assert.equal(new RegExp(write.parameters.properties.path.pattern).test('spec/north-star.md'), false);
|
|
assert.equal(new RegExp(write.parameters.properties.path.pattern).test('docs/design.md'), true);
|
|
const raise = tools.find(t => t.name === 'raise_decision').parameters;
|
|
assert.equal(raise.additionalProperties, false);
|
|
assert.equal(raise.properties.spec_path.const, 'spec/north-star.md');
|
|
assert.equal(new RegExp(raise.properties.spec_commit.pattern).test('a'.repeat(40)), true);
|
|
assert.equal(raise.required.includes('spec_commit'), false);
|
|
}
|
|
const restricted = resolvedFor(t, 'coder', doc => { doc.roles.coder.vars['limits.authority'] = ['message.send']; });
|
|
const tools = typedTools({ resolved: restricted });
|
|
assert.ok(tools.some(t => t.name === 'read_spec'));
|
|
assert.ok(!tools.some(t => t.name === 'write_record'));
|
|
});
|
|
|
|
test('typed socket forwards read content/history and agent record writes, and cannot write a spec', async t => {
|
|
const f = await fixture(t);
|
|
const tools = typedTools({ resolved: { definition: 'coder', limits: { tools: [], authority: {
|
|
withinRole: ['record.write'], crossRole: [],
|
|
} } } });
|
|
const byName = new Map(tools.map(tool => [tool.name, tool]));
|
|
const server = await fakeToolSocket(t, f.root, (name, args) =>
|
|
f.broker.request(f.coder, { verb: byName.get(name).verb, args }));
|
|
const spec = await callTool(server.path, 'read_spec', { project: 'stack' });
|
|
assert.equal(spec.commit, f.commit); assert.equal(spec.log[0].commit, f.commit);
|
|
assert.match(spec.content, /North.star/i);
|
|
const record = await callTool(server.path, 'write_record', { project: 'stack', path: 'docs/tool.md', record: '# Tool\n' });
|
|
assert.match(record.commit, /^[0-9a-f]{40}$/);
|
|
await assert.rejects(callTool(server.path, 'write_record', { project: 'stack', path: 'spec/north-star.md', record: '# Bad' }), /project-spec-human-only/);
|
|
});
|