Files
stack/packages/plugins/tests/transcript.test.mjs
T
jason.woltjeandClaude Opus 5.5 70504d23a4 bus, cli, webui: token accounting per run, schema 3h usage_records (row 124, #1618)
Rocko's round 2 candidate e09313aba4d155a1c5af7bfb227ec397e96c0d57444782cd0b48593102bcc684
(38 paths, base 23533625d), approved by Dewey in #1618 comment 28923 (queue rev 1757).
Packet: agents/rocko/work/queue-124/r2/.

- Schema 3g to 3h: packages/bus/src/migrations/3g-3h.sql adds an append-only
  usage_records table; nothing earlier changes. usage_records is in timedTables.
- The transcript usage-source module reads token counts from Pi and Claude
  Code session transcripts and follows no symlink.
- `mosaic usage` in the CLI and a read-only usage section in the Console's
  Settings (one GET route; 400 on a query string).

Decision 123 go: Mos, mos-0737. Activation (3g to 3h) is a migrating run
with Jason present, in Mos's combined re-pin; landing changes nothing live.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 11:08:17 -05:00

152 lines
8.8 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, symlinkSync, linkSync } from 'node:fs';
import fs, { appendFileSync } from 'node:fs';
import { syncBuiltinESMExports } from 'node:module';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { readTurn } from '../usage-source/transcript/index.mjs';
function fixture(t, harness = 'pi') {
const root = mkdtempSync(join(tmpdir(), 'usage-source-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const dir = harness === 'pi' ? root : join(root, 'project');
mkdirSync(dir, { recursive: true });
const file = join(dir, 'session.jsonl');
const pi = { type: 'message', id: 'assistant1', message: { role: 'assistant', usage: { input: 12, output: 3, cacheWrite: 4, cacheRead: 5 } } };
const claude = { type: 'assistant', uuid: 'record1', message: { id: 'assistant1', role: 'assistant', usage: { input_tokens: 12, output_tokens: 3, cache_creation_input_tokens: 4, cache_read_input_tokens: 5 } } };
const entry = harness === 'pi' ? pi : claude;
const put = value => writeFileSync(file, JSON.stringify(value) + '\n');
put(entry);
return { root, file, entry, put, read: () => readTurn({ harness, root }) };
}
for (const harness of ['pi', 'claude-code']) test(`REQ-LIMIT-4 ${harness}: reports all token kinds and deduplicates assistant ids`, async t => {
const f = fixture(t, harness);
writeFileSync(f.file, [f.entry, f.entry].map(JSON.stringify).join('\n') + '\n');
const result = await f.read();
assert.equal(result.status, 'known');
assert.deepEqual(Object.values(result.entries), [{ input: 12, output: 3, cacheWrite: 4, cacheRead: 5 }]);
});
for (const harness of ['pi', 'claude-code']) for (const [name, change, code] of [
['cut line', f => writeFileSync(f.file, '{"type":'), 'transcript-parse'],
['invalid UTF-8', f => writeFileSync(f.file, Buffer.concat([Buffer.from('{"type":"user","text":"'), Buffer.from([0xff]), Buffer.from('"}\n')])), 'transcript-parse'],
['missing input', f => { delete f.entry.message.usage[f.entry.type === 'assistant' ? 'input_tokens' : 'input']; f.put(f.entry); }, 'transcript-usage'],
['negative count', f => { f.entry.message.usage[f.entry.type === 'assistant' ? 'output_tokens' : 'output'] = -1; f.put(f.entry); }, 'transcript-usage'],
['fractional count', f => { f.entry.message.usage[f.entry.type === 'assistant' ? 'output_tokens' : 'output'] = 0.5; f.put(f.entry); }, 'transcript-usage'],
['unsafe integer', f => { f.entry.message.usage[f.entry.type === 'assistant' ? 'output_tokens' : 'output'] = Number.MAX_SAFE_INTEGER + 1; f.put(f.entry); }, 'transcript-usage'],
['file size', f => writeFileSync(f.file, ' '.repeat(8 * 1024 * 1024 + 1)), 'transcript-size'],
['line size', f => writeFileSync(f.file, JSON.stringify({ type: 'user', text: 'x'.repeat(1024 * 1024) }) + '\n'), 'transcript-line'],
['symlink file', f => { const other = join(f.root, 'other'); writeFileSync(other, JSON.stringify(f.entry) + '\n'); rmSync(f.file); symlinkSync(other, f.file); }, 'transcript-path'],
['hardlink file', f => linkSync(f.file, join(f.root, 'linked')), 'transcript-path'],
]) test(`REQ-LIMIT-4 ${harness} ${name} is unknown, never zero or content`, async t => {
const f = fixture(t, harness); change(f);
assert.deepEqual(await f.read(), { status: 'unknown', code });
});
for (const harness of ['pi', 'claude-code']) test(`REQ-LIMIT-4 ${harness} a symlink directory anywhere in the root refuses`, async t => {
const f = fixture(t, harness), alias = join(f.root, 'alias');
symlinkSync(f.root, alias);
assert.deepEqual(await readTurn({ harness, root: alias }), { status: 'unknown', code: 'transcript-path' });
});
test('REQ-LIMIT-4 bounded directory entries refuse before scanning an arbitrary tree', async t => {
const f = fixture(t);
for (let n = 0; n < 513; n++) writeFileSync(join(f.root, `ignored-${n}`), '');
assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-entries' });
});
test('REQ-LIMIT-4 conflicting repeated usage refuses rather than double counting', async t => {
const f = fixture(t), second = structuredClone(f.entry); second.message.usage.input++;
writeFileSync(f.file, [f.entry, second].map(JSON.stringify).join('\n') + '\n');
assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-usage' });
});
test('REQ-LIMIT-4 oversized metadata refuses before reading any transcript bytes', async t => {
const f = fixture(t); writeFileSync(f.file, 'x'.repeat(8 * 1024 * 1024 + 1));
let reads = 0;
const original = fs.readSync;
fs.readSync = (...args) => { reads++; return original(...args); };
syncBuiltinESMExports();
try { assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-size' }); assert.equal(reads, 0); }
finally { fs.readSync = original; syncBuiltinESMExports(); }
});
test('REQ-LIMIT-4 a file growing after fstat cannot exceed the streaming byte budget', async t => {
const f = fixture(t);
let grew = false;
const original = fs.readSync;
fs.readSync = (...args) => {
if (!grew) { grew = true; appendFileSync(f.file, 'x'.repeat(8 * 1024 * 1024)); }
return original(...args);
};
syncBuiltinESMExports();
try { assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-size' }); }
finally { fs.readSync = original; syncBuiltinESMExports(); }
});
test('REQ-LIMIT-4 a same-size edit during a read is unknown rather than a stable snapshot', async t => {
const f = fixture(t);
const original = fs.readSync;
let edited = false;
fs.readSync = (...args) => {
if (!edited) { edited = true; const bytes = fs.readFileSync(f.file); fs.writeFileSync(f.file, bytes); }
return original(...args);
};
syncBuiltinESMExports();
try { assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-changed' }); }
finally { fs.readSync = original; syncBuiltinESMExports(); }
});
test('REQ-LIMIT-4 record count is bounded even when each record is tiny', async t => {
const f = fixture(t); writeFileSync(f.file, '{"type":"user"}\n'.repeat(20001));
assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-records' });
});
test('REQ-LIMIT-4 missing assistant usage, malformed JSON and conflicting identities are unknown', async t => {
const f = fixture(t);
for (const value of [{ type: 'message', id: 'a', message: { role: 'assistant' } }, { ...f.entry, id: '../bad' }]) {
f.put(value); assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-usage' });
}
writeFileSync(f.file, 'not-json\n'); assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-parse' });
});
test('REQ-LIMIT-4 caller roots, unsupported harnesses and non-object JSON never become zero usage', async t => {
assert.deepEqual(await readTurn({ harness: 'pi', root: 'relative' }), { status: 'unknown', code: 'transcript-path' });
const f = fixture(t);
assert.deepEqual(await readTurn({ harness: 'other', root: f.root }), { status: 'unknown', code: 'transcript-harness' });
for (const value of [null, [], 7]) { f.put(value); assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-parse' }); }
});
test('REQ-LIMIT-4 even a complete JSON record with no final newline is an unknown cut line', async t => {
const f = fixture(t); writeFileSync(f.file, JSON.stringify(f.entry));
assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-parse' });
});
test('REQ-LIMIT-4 final file opens are nonblocking before hostile file type is known', async t => {
const f = fixture(t), original = fs.openSync;
const flags = [];
fs.openSync = (path, mode, ...args) => {
if (String(path).endsWith('/session.jsonl')) flags.push(mode);
return original(path, mode, ...args);
};
syncBuiltinESMExports();
try {
assert.equal((await f.read()).status, 'known');
assert.equal(flags.length, 1);
assert.ok(flags[0] & fs.constants.O_NONBLOCK);
} finally { fs.openSync = original; syncBuiltinESMExports(); }
});
test('REQ-LIMIT-4 invalid transcript entry names refuse before file lookup', async t => {
const f = fixture(t);
writeFileSync(join(f.root, 'bad name.jsonl'), JSON.stringify(f.entry) + '\n');
assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-path' });
});
// Claude's project-directory descent is separate from root and session-file opens.
test('REQ-LIMIT-4 claude-code refuses a symlinked project directory', async t => {
const f = fixture(t, 'claude-code');
const project = join(f.root, 'project');
const target = join(f.root, '..', 'project-target-' + f.root.split('/').at(-1));
mkdirSync(target);
t.after(() => rmSync(target, { recursive: true, force: true }));
writeFileSync(join(target, 'session.jsonl'), JSON.stringify(f.entry) + '\n');
rmSync(project, { recursive: true });
symlinkSync(target, project);
assert.deepEqual(await f.read(), { status: 'unknown', code: 'transcript-path' });
});