Files
stack/packages/seat/tests/compactions-budget.test.mjs

147 lines
9.0 KiB
JavaScript

// Row 270 round 2 (264c, #1775): the read budget (B1, duckworth-0223, sage-3031,
// mos-1352) and Claude history from before the ledger (B3). Round 3: the cap on
// streams one collection opens (B4, duckworth-0226). The streams are Pi
// 0.85.1's shapes, written by hand as in compactions.test.mjs. A stream over
// the budget is a sparse file: truncate() makes the hole, so no test writes
// the bytes it reads past.
import { test } from "node:test";
import assert from "node:assert/strict";
import { appendFileSync, chmodSync, mkdtempSync, readFileSync, rmSync, truncateSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { collectCompactions, LEDGER, READ_BYTES, readBudget, readStreams, STREAMS } from "../src/compactions.mjs";
import { sessionStore } from "../src/session.mjs";
import { compactions as pi } from "../../plugins/harness/pi/index.mjs";
const MiB = 1 << 20;
function setup(t) {
const root = mkdtempSync(join(tmpdir(), "seat-compactions-budget-"));
t.after(() => rmSync(root, { recursive: true, force: true }));
const store = sessionStore(root, "acme", "th1", "s1");
let tick = 0;
const now = () => new Date(Date.UTC(2026, 9, 22, 12, 0, tick++)).toISOString();
const collect = (harness = "pi", budget = undefined, streams = undefined) =>
collectCompactions(store, { name: harness, compactions: { pi }[harness] }, { now, ...(budget === undefined ? {} : { budget }), ...(streams === undefined ? {} : { streams }) });
const records = () => readFileSync(join(store.dir, LEDGER), "utf8").trim().split("\n").map((l) => JSON.parse(l));
return { root, store, collect, records };
}
const header = (id) => ({ type: "session", version: 3, id, timestamp: "2026-10-22T11:00:00.000Z", cwd: "/w" });
const compaction = (id, timestamp) => ({ type: "compaction", id, parentId: null, timestamp, summary: "s", firstKeptEntryId: "k", tokensBefore: 100 });
const line = (e) => `${JSON.stringify(e)}\n`;
const at = (n) => `2026-10-22T12:00:0${n}.000Z`;
test("the budget is the setting's whole number of bytes from 1 MiB to 1 GiB, and the default for anything else", () => {
assert.equal(READ_BYTES, 64 * MiB);
for (const v of [MiB, 8 * MiB, 1024 * MiB]) assert.equal(readBudget(v), v);
for (const v of [undefined, null, "8388608", MiB + 0.5, -1, 0, MiB - 1, 1024 * MiB + 1, NaN, Infinity, {}, [MiB], true]) {
assert.equal(readBudget(v), READ_BYTES, JSON.stringify(v) ?? String(v));
}
});
test("a stream past the default budget is not-collected with a lower bound, and a later collection catches up", (t) => {
const f = setup(t);
const path = join(f.store.session, "a.jsonl");
const whole = line(header("a")) + line(compaction("c1", "2026-10-22T11:01:00.000Z"));
writeFileSync(path, whole);
// A hole past the default budget, with no newline in it: a line still being written.
truncateSync(path, READ_BYTES + 2 * MiB);
assert.deepEqual(f.collect(), { count: 1, known: false, reason: "not-collected", at: at(0) });
// The cursor stops at the last whole line read.
assert.deepEqual(f.records().filter((r) => r.kind === "cursor").map((r) => r.bytes), [Buffer.byteLength(whole)]);
// The long line ends up as c2: the bytes past the cursor are read again, from the cursor.
truncateSync(path, Buffer.byteLength(whole));
appendFileSync(path, line(compaction("c2", "2026-10-22T11:02:00.000Z")));
assert.deepEqual(f.collect(), { count: 2, known: true, reason: null, at: at(1) });
});
test("a budget smaller than the streams counts over several collections, once each, and is known when it catches up", (t) => {
const f = setup(t);
const h = Buffer.byteLength(line(header("a"))), c = Buffer.byteLength(line(compaction("c1", "2026-10-22T11:01:00.000Z")));
writeFileSync(join(f.store.session, "a.jsonl"), line(header("a")) + line(compaction("c1", "2026-10-22T11:01:00.000Z")) + line(compaction("c2", "2026-10-22T11:02:00.000Z")));
writeFileSync(join(f.store.session, "b.jsonl"), line(header("b")) + line(compaction("c3", "2026-10-22T11:03:00.000Z")));
// One byte past a's first compaction: a stops there and b isn't reached; then all of c2 and b's
// header; then c3.
const budget = h + c + 1;
assert.deepEqual(f.collect("pi", budget), { count: 1, known: false, reason: "not-collected", at: at(0) });
assert.deepEqual(f.collect("pi", budget), { count: 2, known: false, reason: "not-collected", at: at(1) });
assert.deepEqual(f.collect("pi", budget), { count: 3, known: true, reason: null, at: at(2) });
assert.deepEqual(f.records().filter((r) => r.kind === "compaction").map((r) => [r.entry, r.stream]), [["c1", "a.jsonl"], ["c2", "a.jsonl"], ["c3", "b.jsonl"]]);
});
test("a line split across reads counts once", (t) => {
const f = setup(t);
// A message that ends just short of the first read's end, so c1 crosses it.
const first = line(header("a"));
const pad = { type: "message", id: "m1", parentId: null, timestamp: "2026-10-22T11:00:01.000Z", message: { role: "user", content: "" } };
pad.message.content = "x".repeat(MiB - 100 - Buffer.byteLength(first) - Buffer.byteLength(line(pad)));
writeFileSync(join(f.store.session, "a.jsonl"), first + line(pad) + line(compaction("c1", "2026-10-22T11:01:00.000Z")));
assert.equal(Buffer.byteLength(first + line(pad)), MiB - 100);
assert.deepEqual(f.collect(), { count: 1, known: true, reason: null, at: at(0) });
});
// B3 (Vikunja 466): Claude Code runs before this ledger left history in claude-projects/ that
// can't be counted. The first collection is harness-unsupported, and it stays so.
test("Claude history from before the ledger makes the first Pi collection harness-unsupported, for good", (t) => {
const f = setup(t);
writeFileSync(join(f.store.projects, "old.jsonl"), "{}\n");
writeFileSync(join(f.store.session, "a.jsonl"), line(header("a")) + line(compaction("c1", "2026-10-22T11:01:00.000Z")));
assert.deepEqual(f.collect(), { count: null, known: false, reason: "harness-unsupported", at: at(0) });
assert.deepEqual(f.collect(), { count: 1, known: false, reason: "harness-unsupported", at: at(1) });
});
test("an empty claude-projects/ is no Claude history, and one filled after the ledger exists isn't checked", (t) => {
const f = setup(t);
writeFileSync(join(f.store.session, "a.jsonl"), line(header("a")) + line(compaction("c1", "2026-10-22T11:01:00.000Z")));
assert.deepEqual(f.collect(), { count: 1, known: true, reason: null, at: at(0) });
// A Claude run after the ledger exists collects for itself (compactions.test.mjs).
writeFileSync(join(f.store.projects, "later.jsonl"), "{}\n");
assert.deepEqual(f.collect(), { count: 1, known: true, reason: null, at: at(1) });
});
test("a claude-projects/ that can't be read records nothing, and the next collection checks again", (t) => {
const f = setup(t);
writeFileSync(join(f.store.projects, "old.jsonl"), "{}\n");
chmodSync(f.store.projects, 0o000);
try {
assert.throws(() => f.collect(), { code: "EACCES" });
assert.throws(() => readFileSync(join(f.store.dir, LEDGER)), { code: "ENOENT" });
} finally {
chmodSync(f.store.projects, 0o700);
}
assert.deepEqual(f.collect(), { count: null, known: false, reason: "harness-unsupported", at: at(1) });
});
test("the stream cap is the setting's whole number from 32 to 65536, and the default for anything else", () => {
assert.equal(STREAMS, 1024);
for (const v of [32, 2048, 65536]) assert.equal(readStreams(v), v);
for (const v of [undefined, null, "2048", 40.5, -1, 0, 31, 65537, NaN, Infinity, {}, [64], true]) {
assert.equal(readStreams(v), STREAMS, JSON.stringify(v) ?? String(v));
}
});
test("more streams than the cap is not-collected, with cursors only for the streams opened, and a larger cap catches up", (t) => {
const f = setup(t);
const names = ["a", "b", "c", "d"];
names.forEach((n, i) => writeFileSync(join(f.store.session, `${n}.jsonl`), line(header(n)) + line(compaction(`c${n}`, `2026-10-22T11:0${i}:00.000Z`))));
const cursors = () => f.records().filter((r) => r.kind === "cursor").map((r) => r.stream);
assert.deepEqual(f.collect("pi", undefined, 3), { count: 3, known: false, reason: "not-collected", at: at(0) });
assert.deepEqual(cursors(), ["a.jsonl", "b.jsonl", "c.jsonl"]);
// The same cap again opens the same three, which haven't moved: no new lines but the collected one.
assert.deepEqual(f.collect("pi", undefined, 3), { count: 3, known: false, reason: "not-collected", at: at(1) });
assert.deepEqual(cursors(), ["a.jsonl", "b.jsonl", "c.jsonl"]);
assert.deepEqual(f.collect("pi", undefined, 4), { count: 4, known: true, reason: null, at: at(2) });
assert.deepEqual(cursors(), ["a.jsonl", "b.jsonl", "c.jsonl", "d.jsonl"]);
});
test("the default cap opens STREAMS streams and writes no more cursor lines than that", (t) => {
const f = setup(t);
for (let i = 0; i <= STREAMS; i++) writeFileSync(join(f.store.session, `e${String(i).padStart(4, "0")}.jsonl`), "");
assert.deepEqual(f.collect(), { count: null, known: false, reason: "not-collected", at: at(0) });
const cursors = f.records().filter((r) => r.kind === "cursor");
assert.equal(cursors.length, STREAMS);
assert.equal(cursors.at(-1).stream, "e1023.jsonl");
});