blob: 0f8632cfc144024accb483a7de77d5f48081a734 [file]
#!/usr/bin/env node
// The analyser had no test, and three of its counters were wrong in the
// direction that reports a clean run.
//
// `blind` searched every preceding call for any observation, so one `observe`
// at the top of a trajectory made every later call unflaggable — twenty blind
// clicks reported BLIND 0. `abandoned` was documented as "the turn ended within
// one call of a refusal" and implemented as "the last call was a refusal".
// Refusal detection read one regex over rendered prose, so a wording change in
// the executor would have zeroed every refusal count, every dead end, and the
// whole failure-by-action table without changing a line of this file.
//
// A second reading found three more of the same kind, and they are the reason
// the fixtures below are built out of the renderer's real output rather than a
// plausible-looking imitation: the observation marker, the action vocabulary
// and the signature were all written from memory, and all three were wrong.
// `observation_id=` does not occur in the product; `left_click` was in neither
// action list; and the signature was key-order sensitive, so three byte-equal
// calls read as three schema guesses.
//
// So each shape below is asserted twice: once on a trajectory that must trip
// it, once on a neighbouring trajectory that must not. A counter that only ever
// goes up is not a measurement.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { CU_TOOL_ACTION_TYPES } from '@maka/core';
import {
analyseCalls,
blindCalls,
carriesObservation,
classify,
endedAbandoned,
parseJournal,
parseTrace,
signature,
targetKey,
} from './cu-trace-analyse.mjs';
/** One journal line in the shape `CuDebugRecord` is written in. */
function line(rawArgs, extra = {}) {
return JSON.stringify({
at: '2026-08-01T00:00:00.000Z',
kind: 'call',
ts: 1,
sessionId: 's',
turnId: 't',
toolCallId: 'c',
rawArgs,
modelFacingArgs: rawArgs,
durationMs: 1,
...extra,
});
}
/**
* A result carrying an observation, in the shape the product actually renders.
*
* This is `observationText` from `packages/runtime/src/computer-use-tools.ts`:
* one JSON object with an `observation_id` key. The previous fixture wrote
* `observation_id=obs_1`, a form with zero occurrences in the repository, and
* every assertion built on it passed while the analyser could not recognise a
* single real observation.
*/
const observed = (app, win = 1) => ({
resultText: JSON.stringify({
observation_id: 'obs_1',
app,
pid: 42,
window_id: win,
elements: [{ element_id: '0', role: 'AXButton', label: 'w' }],
}),
});
const refused = (code, text) => ({ error: code, resultText: text ?? `failed: ${code}` });
/**
* An action that was delivered but handed back no replacement tree — the other
* branch of the same renderer, verbatim.
*/
const consumed = {
resultText:
'computer.click_element delivered path=ax\nObservation consumed; call observe before the next action.',
};
test('a driver trace line is not a model decision', () => {
assert.equal(classify({ kind: 'driver', event: 'dispatch' }), null);
assert.notEqual(
classify({ kind: 'call', rawArgs: { action: 'observe' }, resultText: 'ok' }),
null,
);
});
test('the observation id is not part of what the model asked for', () => {
assert.equal(
signature({ action: 'click_element', element_id: '3', observation_id: 'obs_1' }),
signature({ action: 'click_element', element_id: '3', observation_id: 'obs_9' }),
);
assert.notEqual(
signature({ action: 'click_element', element_id: '3' }),
signature({ action: 'click_element', element_id: '4' }),
);
});
test('the order the keys were serialised in is not part of it either', () => {
// Three byte-identical calls, emitted with the keys in three orders. This
// used to produce three distinct signatures, so verbatim repetition was
// reported as `thrash: click_element×3 shapes` and `repeated: 0` — the model
// sending the same thing over and over, diagnosed as the model guessing at
// the schema, which is the exact confusion this file's header claims to have
// fixed.
const a = { action: 'click_element', app: 'a', window_id: 1, element_id: '3' };
const b = { element_id: '3', action: 'click_element', app: 'a', window_id: 1 };
const c = { window_id: 1, app: 'a', element_id: '3', action: 'click_element' };
assert.equal(signature(a), signature(b));
assert.equal(signature(b), signature(c));
const report = analyseCalls(
parseTrace([line(a, observed('a')), line(b, observed('a')), line(c, observed('a'))].join('\n')),
);
assert.equal(report.repeated.length, 1);
assert.deepEqual(report.thrash, []);
// And the same three calls with one field genuinely different are still three
// shapes, so the fix did not simply collapse everything into one bucket.
const varied = analyseCalls(
parseTrace(
[
line({ action: 'click_element', app: 'a', element_id: '3' }, observed('a')),
line({ action: 'click_element', app: 'a', element_id: '4' }, observed('a')),
line({ action: 'click_element', app: 'a', element_id: '5' }, observed('a')),
].join('\n'),
),
);
assert.deepEqual(varied.thrash, ['click_element×3 shapes']);
assert.equal(varied.repeated.length, 0);
});
test('nesting does not reintroduce key-order sensitivity', () => {
assert.equal(
signature({ action: 'left_click', coordinate: { x: 1, y: 2 } }),
signature({ action: 'left_click', coordinate: { y: 2, x: 1 } }),
);
assert.notEqual(
signature({ action: 'left_click', coordinate: { x: 1, y: 2 } }),
signature({ action: 'left_click', coordinate: { x: 2, y: 1 } }),
);
});
test('a target is an application and a window, not one or the other', () => {
assert.equal(targetKey({ app: 'a', window_id: 1 }), targetKey({ app: 'a', window_id: 1 }));
assert.notEqual(targetKey({ app: 'a', window_id: 1 }), targetKey({ app: 'a', window_id: 2 }));
assert.notEqual(targetKey({ app: 'a', window_id: 1 }), targetKey({ app: 'b', window_id: 1 }));
});
test('a refusal is read from the executor field, not from how it was worded', () => {
// The point of the fix: the code survives a rewrite of the prose.
const reworded = classify({
kind: 'call',
rawArgs: { action: 'secondary_action' },
error: 'dispatch_refused',
resultText: 'Sorry — that control would not accept the action.',
});
assert.equal(reworded.failed, 'dispatch_refused');
assert.equal(reworded.unclassified, false);
});
test('a journal with no error field still yields a code from the text', () => {
const older = classify({
kind: 'call',
rawArgs: { action: 'press_key' },
resultText: 'failed: reobserve_required',
});
assert.equal(older.failed, 'reobserve_required');
});
test('a failure with no code anywhere is counted, not passed over', () => {
// The condition the old regex turned into silence. It has to be visible as a
// number, because the alternative is a report of zero refusals on a run that
// refused everything.
const mystery = classify({
kind: 'call',
rawArgs: { action: 'click_element' },
resultText: 'the action was blocked',
});
assert.equal(mystery.failed, null);
assert.equal(mystery.unclassified, true);
const fine = classify({
kind: 'call',
rawArgs: { action: 'click_element' },
...observed('com.apple.calculator'),
});
assert.equal(fine.unclassified, false);
});
test('a result carrying a fresh tree is recognised by protocol, not by prose', () => {
// The marker is the JSON key the renderer writes. `observation_id=` is what
// this used to look for, and it never once occurred in the product.
assert.equal(carriesObservation(observed('a').resultText), true);
assert.equal(
carriesObservation(
'computer.click_element delivered\nFresh observation: {"observation_id":"obs_2","app_id":"a"}',
),
true,
);
assert.equal(carriesObservation('observation_id=obs_4\n\t0 AXWindow'), false);
assert.equal(
carriesObservation(
'computer.click_element delivered\nObservation consumed; call observe before the next action.',
),
false,
);
assert.equal(carriesObservation('clicked, and it worked'), false);
assert.equal(carriesObservation(undefined), false);
});
test('the observing and mutating vocabularies are the product enum, not a copy', () => {
// The regexes that used to stand in for this matched none of `left_click`,
// `type`, `key`, `wait` or `zoom`, and did match `click`, `type_text`,
// `drag` and `wait_for_text` — names with zero occurrences on the wire.
// Every action the tool accepts has to be a name this file recognises, and
// an action nobody has heard of has to be refused rather than counted.
//
// The invented name below is deliberately one of those regex ghosts rather
// than a plausible next action: `launch_app`, `window_action` and
// `element_sequence` stood here once and are on the wire now, so a name that
// reads like a real action is a test that expires the day the action ships.
for (const action of CU_TOOL_ACTION_TYPES) {
const [call] = parseTrace(line({ action, app: 'a', window_id: 1 }, observed('a')));
assert.deepEqual(call.malformed, [], `${action} is on the wire and must classify`);
assert.equal(call.action, action);
}
assert.ok(
!CU_TOOL_ACTION_TYPES.includes('type_text'),
'the invented action is on the wire, so this case no longer tests anything',
);
const [invented] = parseTrace(line({ action: 'type_text', app: 'a' }, observed('a')));
assert.deepEqual(invented.malformed, [
'action "type_text" is not on the maka_computer wire enum',
]);
});
test('twenty coordinate clicks with nothing observed are twenty blind calls', () => {
// Measured at 0 before the vocabulary came from the enum, because
// `left_click` matched neither list and so was neither observing nor
// mutating: the analyser walked past every one of them.
const calls = parseTrace(
Array.from({ length: 20 }, (_, n) =>
line(
{ action: 'left_click', app: 'a', window_id: 1, coordinate: { x: n, y: n } },
{ resultText: 'computer.left_click delivered' },
),
).join('\n'),
);
assert.equal(calls.length, 20);
assert.equal(blindCalls(calls).length, 20);
});
test('one observation does not excuse every click that follows it', () => {
// The trajectory the old implementation reported BLIND 0 on. Only the first
// click had a live tree; each one after it acted on a tree its own
// predecessor invalidated.
const calls = parseTrace(
[
line({ action: 'observe', app: 'a', window_id: 1 }, observed('a')),
...Array.from({ length: 4 }, (_, n) =>
line({ action: 'click_element', app: 'a', window_id: 1, element_id: String(n) }, consumed),
),
].join('\n'),
);
assert.equal(calls.length, 5);
assert.equal(blindCalls(calls).length, 3, 'the first click looked; the other three did not');
});
test('an action that hands back a tree leaves the next one sighted', () => {
// Maka attaches an observation to an action's result, so a run of clicks that
// each return one is not blind — and reporting it as blind would be an
// analyser that flags the product working correctly. Measured at BLIND 3
// against real renderer output before `carriesObservation` was fixed, which
// is why this fixture is `observationText`'s own JSON.
const calls = parseTrace(
[
line({ action: 'observe', app: 'a', window_id: 1 }, observed('a')),
...Array.from({ length: 4 }, (_, n) =>
line(
{ action: 'click_element', app: 'a', window_id: 1, element_id: String(n) },
observed('a'),
),
),
].join('\n'),
);
assert.equal(blindCalls(calls).length, 0);
});
test('an observation of one window says nothing about another', () => {
const calls = parseTrace(
[
line({ action: 'observe', app: 'a', window_id: 1 }, observed('a')),
line({ action: 'click_element', app: 'b', window_id: 1, element_id: '0' }, consumed),
].join('\n'),
);
assert.equal(blindCalls(calls).length, 1, 'the tree it holds describes a different application');
});
test('acting with nothing observed at all is blind', () => {
const calls = parseTrace(line({ action: 'click_element', app: 'a', element_id: '0' }, consumed));
assert.equal(blindCalls(calls).length, 1);
});
test('a turn that gave up one call after a refusal is abandoned', () => {
// Documented as "within one call", so the shape below has to trip it: a
// refusal, one more try, and then the turn stops.
const calls = parseTrace(
[
line({ action: 'secondary_action', app: 'a' }, refused('dispatch_refused')),
line({ action: 'observe', app: 'a' }, observed('a')),
].join('\n'),
);
assert.equal(endedAbandoned(calls), true);
});
test('a turn that recovered and went on working is not abandoned', () => {
const calls = parseTrace(
[
line({ action: 'secondary_action', app: 'a' }, refused('dispatch_refused')),
line({ action: 'observe', app: 'a', window_id: 1 }, observed('a')),
line({ action: 'click_element', app: 'a', window_id: 1, element_id: '0' }, observed('a')),
line({ action: 'observe', app: 'a', window_id: 1 }, observed('a')),
].join('\n'),
);
assert.equal(endedAbandoned(calls), false);
});
test('three argument shapes for one action is thrash; two is not', () => {
const shapes = (n) =>
analyseCalls(
parseTrace(
Array.from({ length: n }, (_, i) =>
line({ action: 'set_value', app: 'a', ['field' + i]: i }, observed('a')),
).join('\n'),
),
).thrash;
assert.deepEqual(shapes(2), []);
assert.deepEqual(shapes(3), ['set_value×3 shapes']);
});
test('a refusal answered by the same action again is a dead end', () => {
const same = analyseCalls(
parseTrace(
[
line({ action: 'secondary_action', app: 'a' }, refused('dispatch_refused')),
line({ action: 'secondary_action', app: 'a' }, refused('dispatch_refused')),
].join('\n'),
),
);
assert.equal(same.deadEnds, 1);
const moved = analyseCalls(
parseTrace(
[
line({ action: 'secondary_action', app: 'a' }, refused('dispatch_refused')),
line({ action: 'observe', app: 'a' }, observed('a')),
].join('\n'),
),
);
assert.equal(moved.deadEnds, 0);
});
test('the same call sent twice is reported once, and a different one is not reported', () => {
const twice = analyseCalls(
parseTrace(
[
line({ action: 'click_element', app: 'a', window_id: 1, element_id: '3' }, observed('a')),
line({ action: 'click_element', app: 'a', window_id: 1, element_id: '3' }, observed('a')),
].join('\n'),
),
);
assert.equal(twice.repeated.length, 1);
const varied = analyseCalls(
parseTrace(
[
line({ action: 'click_element', app: 'a', window_id: 1, element_id: '3' }, observed('a')),
line({ action: 'click_element', app: 'a', window_id: 1, element_id: '4' }, observed('a')),
].join('\n'),
),
);
assert.equal(varied.repeated.length, 0);
});
test('an unreadable line is dropped without taking the trajectory with it', () => {
const calls = parseTrace(
['{ not json', line({ action: 'observe', app: 'a' }, observed('a'))].join('\n'),
);
assert.equal(calls.length, 1);
});
test('but the unreadable line is counted, not forgotten', () => {
// Dropping it silently is how a journal truncated mid-write presents as a
// short, tidy trajectory with nothing wrong.
const journal = parseJournal(
['{ not json', line({ action: 'observe', app: 'a' }, observed('a')), '{"kind"'].join('\n'),
);
assert.equal(journal.calls.length, 1);
assert.deepEqual(journal.unreadable, [1, 3]);
assert.equal(journal.lines, 3);
});
test('a record in a shape this file does not know is refused, not counted as fine', () => {
// Measured on the analyser as it stood: twenty of these reported
// {"calls":20,"refusals":0,"blind":0,"actions":["?"]} and exit 0. The
// arguments are under `arguments`, the result under `result`, and the action
// is not on the wire — three disagreements with the shape read here, and the
// report was a clean bill of health for a corpus it had not read one field
// of. Nothing writes MAKA_CU_DEBUG_LOG yet, so this is the likeliest way the
// first producer to land will present.
const report = analyseCalls(
parseTrace(
Array.from({ length: 20 }, () =>
JSON.stringify({
kind: 'call',
arguments: { action: 'frobnicate' },
result: 'failed: dispatch_refused',
}),
).join('\n'),
),
);
assert.equal(report.calls, 20);
assert.equal(report.malformed.length, 40, 'two complaints per record');
assert.deepEqual(
[...new Set(report.malformed)].sort(),
['no rawArgs/modelFacingArgs object', 'no resultText, resultModelText or error field'].sort(),
);
});
test('a well-formed corpus raises no complaint, so the guard is a measurement', () => {
const report = analyseCalls(
parseTrace(
[
line({ action: 'observe', app: 'a', window_id: 1 }, observed('a')),
line({ action: 'click_element', app: 'a', window_id: 1, element_id: '0' }, observed('a')),
line(
{ action: 'set_value', app: 'a', window_id: 1, element_id: '1', value: 'x' },
consumed,
),
].join('\n'),
),
);
assert.deepEqual(report.malformed, []);
assert.equal(report.calls, 3);
});
test('a call with an action but no result at all is refused', () => {
const [call] = parseTrace(line({ action: 'observe', app: 'a' }));
assert.deepEqual(call.malformed, ['no resultText, resultModelText or error field']);
});
test('an error code with no result text is a complete record', () => {
// A refusal carries the structural code and may carry nothing else; that is
// the shape the executor writes, and it must not be mistaken for a record
// this file failed to read.
const [call] = parseTrace(line({ action: 'press_key', app: 'a' }, { error: 'dispatch_refused' }));
assert.deepEqual(call.malformed, []);
assert.equal(call.failed, 'dispatch_refused');
});