Files
captoys/good-fences/test/lend-the-mower.test.ts
T
dckcandClaude Opus 5.5 9b12943be4 test(good-fences): show which mower the neighbor gets
The chat and load arrows drew `things: { mower }` whether I lent the
raw mower or the governed one. Name objects by the reply arrow that
introduced them, so the diagram reads `things: { mower: governed }`
and lending the raw mower would fail the trace comparison.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 23:27:13 -05:00

144 lines
4.2 KiB
TypeScript

/** @file The lend-the-mower journey, traced and compared with its sequence diagram. */
import './lockdown.ts';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import test from 'node:test';
import { makeFencePage } from '../src/fence.ts';
import { makePlayground } from '../src/governor.ts';
import { aboutMower, makeMowerFactory } from '../src/mower.ts';
import { makeNeighbor } from '../src/neighbor.ts';
import { makeCompartmentLoader } from '../src/tenant-loader.ts';
import { makeYard } from '../src/yard.ts';
import {
type CausalSequenceViz,
makeCausalSequenceTracer,
md,
mmd,
} from '../tools/sequence-diagram-actor-sim.ts';
const designDoc = new URL('../docs/lend-the-mower.md', import.meta.url);
/** a tenant that pushes until something stops it, or gives up after 20 */
const tipsyTenant = `(env) => {
env.mower.start();
try {
for (let i = 0; i < 20; i += 1) env.mower.push();
} catch (err) {
return err.message;
}
return 'mowed 20 cells';
}`;
/** diagram-local names, bound by the reply arrows that introduce them */
const local = new WeakMap<object, string>();
const bind = (name: string) => (result: unknown) => {
local.set(result as object, name);
return name;
};
/** `{ a, b: { c: governed } }`: keys, nested objects by their keys, and
* objects with a diagram-local name by that name */
const shape = (record: object): string =>
`{ ${Object.entries(record)
.map(([key, value]) => {
if (value === null || typeof value !== 'object') return key;
const name = local.get(value);
if (name) return `${key}: ${name}`;
return Array.isArray(value) ? key : `${key}: ${shape(value)}`;
})
.join(', ')} }`;
const quoted = (result: unknown) => `'${String(result)}'`;
const viz = harden({
lend: {
label: args => {
const { webkey, limit } = args[0] as { webkey: string; limit: number };
return `lend({ webkey: '${webkey}', limit: ${limit}, say })`;
},
},
mower: { result: bind('mower') },
govern: {
args: args => `${local.get(args[0] as object)}, ${String(args[1])}`,
result: bind('governed'),
},
chat: {
label: args => `chat(${shape(args[0] as object)})`,
result: quoted,
},
complete: {
label: args =>
`complete([${(args[0] as object[]).map(shape).join(', ')}])`,
result: () => 'code',
},
load: {
args: args => `code, ${shape(args[1] as object)}`,
result: () => 'tenant',
},
run: { result: quoted },
}) satisfies CausalSequenceViz;
test('a governed mower stops a tipsy tenant at the fence', async () => {
const lines = (await readFile(designDoc, 'utf8')).split('\n');
const section = md.skipToH(2, 'Lend a governed mower to a tipsy tenant')(lines);
const diagram = md.eachFence('mermaid', section).next().value;
if (!diagram) throw Error('lend-the-mower Mermaid block not found');
const documented = mmd.extractArrows(diagram);
const ES = makeCausalSequenceTracer(viz);
const yard = makeYard({ width: 8, height: 8 });
const factory = makeMowerFactory(ES, {
yard,
size: { width: 8, height: 8 },
mintKey: () => 'mower-key',
});
const key = factory.make({ x: 4, y: 4 });
const mower = factory.mower(key);
const prompts: unknown[] = [];
const model = harden({
complete(messages: readonly unknown[]) {
prompts.push(messages);
return tipsyTenant;
},
});
const playground = makePlayground(ES);
const loader = makeCompartmentLoader(ES);
const neighbor = makeNeighbor(ES, { model, loader });
const fence = makeFencePage(ES, { factory, playground, neighbor });
ES.declareParticipants({
F: fence,
MF: factory,
P: playground,
M: mower,
Y: yard,
N: neighbor,
L: model,
TL: loader,
});
ES.declareMade({ G: [playground, 'govern'], T: [loader, 'load'] });
ES.start(fence);
const outcome = ES(fence).lend({
webkey: key,
limit: 2,
say: 'you can use this mower for the weekend',
});
assert.deepEqual(ES.snapshot(), documented);
assert.equal(outcome, 'fence: 2 cells from start');
assert.deepEqual(yard.cutCells(), ['4,4', '4,3', '4,2']);
assert.deepEqual(prompts, [
[
{
role: 'user',
content: 'you can use this mower for the weekend',
about: { mower: aboutMower },
},
],
]);
});