Files
obelisk/tests/pi-parse.test.mjs
T
SaladDay 2589384e68 feat(core): add first-class Pi session indexing (#23)
Pi cannot be read as another linear JSONL stream. Its history is a tree with
a durable leaf, orphan roots, branch summaries, and two compaction forms, so
the active context is something the format states rather than something line
order implies. The adapter keeps those semantics inside itself and projects
the result into the existing canonical tables.

Sessions are keyed by (normalized header cwd, header id) rather than by path,
because Pi's --session-id lookup is project-local: two projects may reuse an
id, while a move or an identical copy is still one session. Discovery covers
both layouts Pi writes and fingerprints each file by mtime, ctime, size and
inode, so a rewrite that preserves mtime is not read as unchanged.

Abandoned branches are preserved rather than dropped. Visibility becomes
three-state -- visible, inactive, hidden -- and helpers return only visible
rows until includeInactive asks for the superseded path, labeling every row
so a caller knows which it holds. Usage counts all three, because an
abandoned call still spent tokens; message_count reports only the visible
transcript.

A committed MIT-licensed oracle transcribed from Pi 0.83.0 pins the context
algorithms, and a fixed-seed differential runs 512 generated sessions against
it on every test run. Schema changes are additive.
2026-08-04 23:33:01 +08:00

2151 lines
77 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
mkdirSync,
mkdtempSync,
readFileSync,
renameSync,
statSync,
symlinkSync,
unlinkSync,
utimesSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { DatabaseSync } from 'node:sqlite';
import {
createPiProvider,
piSessionId,
resolveDefaultPiRoot,
} from '../packages/core/src/providers/pi.ts';
import { createProviderRegistry } from '../packages/core/src/providers/registry.ts';
import { assembleSessionDetail } from '../packages/core/src/session-detail.ts';
import { persist } from '../packages/core/src/persist.ts';
import { createQueryApi } from '../packages/core/src/query.ts';
const SCHEMA = readFileSync(new URL('../packages/core/src/schema.sql', import.meta.url), 'utf8');
const FIXTURES = new URL('./fixtures/pi/', import.meta.url);
function drain(generator) {
const values = [];
let step = generator.next();
while (!step.done) {
values.push(step.value);
step = generator.next();
}
return { values, cursor: step.value };
}
function fixture(name) {
return readFileSync(new URL(name, FIXTURES), 'utf8');
}
function writeSession(content, { root, relativePath = 'project/session.jsonl' } = {}) {
const sessionRoot = root ?? mkdtempSync(join(tmpdir(), 'obelisk-pi-'));
const path = join(sessionRoot, relativePath);
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, content);
return { root: sessionRoot, path };
}
function parseOnly(root) {
const provider = createPiProvider({ rootDir: root });
const units = provider.discover({ lastCursor: () => null });
assert.equal(units.length, 1);
return { provider, unit: units[0], ...drain(provider.parse(units[0], null)) };
}
function jsonl(records, trailingNewline = true) {
return records.map(record => JSON.stringify(record)).join('\n') + (trailingNewline ? '\n' : '');
}
function header(overrides = {}) {
return {
type: 'session',
version: 3,
id: 'pi-test-session',
timestamp: '2026-08-02T10:00:00.000Z',
cwd: '/tmp/pi-test-project',
...overrides,
};
}
function userEntry(id, parentId, text, timestamp = '2026-08-02T10:00:01.000Z') {
return {
type: 'message',
id,
parentId,
timestamp,
message: { role: 'user', content: text, timestamp: Date.parse(timestamp) },
};
}
test('Pi root resolution follows official precedence and rejects ambiguous relative roots', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-home-'));
const projectCwd = join(home, 'project');
mkdirSync(projectCwd, { recursive: true });
assert.deepEqual(resolveDefaultPiRoot({ env: {}, homeDir: home, cwd: projectCwd }), {
root: join(home, '.pi', 'agent', 'sessions'),
requiresExplicitRoot: false,
});
const agentDir = join(home, 'custom-agent');
const absoluteSessions = join(home, 'absolute-sessions');
mkdirSync(agentDir, { recursive: true });
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({ sessionDir: absoluteSessions }));
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: absoluteSessions,
requiresExplicitRoot: false,
});
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({ sessionDir: '.pi/relative-sessions' }));
const relativeSetting = resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
});
assert.equal(relativeSetting.requiresExplicitRoot, true);
assert.equal(relativeSetting.root, join(agentDir, 'sessions'));
assert.doesNotMatch(relativeSetting.root, /relative-sessions/);
const relativeEnv = resolveDefaultPiRoot({
env: { PI_CODING_AGENT_SESSION_DIR: 'cwd-relative-sessions' },
homeDir: home,
cwd: projectCwd,
});
assert.equal(relativeEnv.requiresExplicitRoot, true);
assert.equal(relativeEnv.root, join(home, '.pi', 'agent', 'sessions'));
const envSessions = join(home, 'env-sessions');
assert.deepEqual(resolveDefaultPiRoot({
env: {
PI_CODING_AGENT_SESSION_DIR: envSessions,
PI_CODING_AGENT_DIR: agentDir,
},
homeDir: home,
cwd: projectCwd,
}), {
root: envSessions,
requiresExplicitRoot: false,
});
const originalSessionDir = process.env.PI_CODING_AGENT_SESSION_DIR;
process.env.PI_CODING_AGENT_SESSION_DIR = 'cwd-relative-sessions';
try {
const unresolved = createPiProvider();
assert.equal(unresolved.descriptor.requiresExplicitRoot, true);
assert.deepEqual(unresolved.watchRoots(unresolved.descriptor.defaultRoot), []);
assert.deepEqual(unresolved.discover({ lastCursor: () => null }), []);
const explicitlySelected = createPiProvider({
rootDir: join(process.cwd(), '.pi', 'agent', 'sessions'),
});
assert.equal(explicitlySelected.rootResolution.requiresExplicitRoot, false);
} finally {
if (originalSessionDir === undefined) delete process.env.PI_CODING_AGENT_SESSION_DIR;
else process.env.PI_CODING_AGENT_SESSION_DIR = originalSessionDir;
}
});
test('Pi project settings override global sessionDir using the official launch cwd', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-project-settings-'));
const agentDir = join(home, 'agent');
const projectCwd = join(home, 'project');
const globalSessions = join(home, 'global-sessions');
const projectSessions = join(home, 'project-sessions');
mkdirSync(join(projectCwd, '.pi'), { recursive: true });
mkdirSync(agentDir, { recursive: true });
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({ sessionDir: globalSessions }));
writeFileSync(join(projectCwd, '.pi', 'settings.json'), JSON.stringify({
sessionDir: projectSessions,
}));
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: projectSessions,
requiresExplicitRoot: false,
});
writeFileSync(join(projectCwd, '.pi', 'settings.json'), JSON.stringify({
sessionDir: '.pi/project-sessions',
}));
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: join(projectCwd, '.pi', 'project-sessions'),
requiresExplicitRoot: false,
});
});
test('malformed Pi settings fall back to the remaining official settings scope', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-malformed-settings-'));
const agentDir = join(home, 'agent');
const projectCwd = join(home, 'project');
const projectSettingsDir = join(projectCwd, '.pi');
const globalSessions = join(home, 'global-sessions');
const projectSessions = join(home, 'project-sessions');
mkdirSync(agentDir, { recursive: true });
mkdirSync(projectSettingsDir, { recursive: true });
writeFileSync(join(agentDir, 'settings.json'), '{broken');
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: join(agentDir, 'sessions'),
requiresExplicitRoot: false,
});
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({
sessionDir: globalSessions,
}));
writeFileSync(join(projectSettingsDir, 'settings.json'), '{broken');
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: globalSessions,
requiresExplicitRoot: false,
});
writeFileSync(join(agentDir, 'settings.json'), '{broken');
writeFileSync(join(projectSettingsDir, 'settings.json'), JSON.stringify({
sessionDir: projectSessions,
}));
assert.deepEqual(resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
}), {
root: projectSessions,
requiresExplicitRoot: false,
});
});
test('non-string Pi sessionDir values never select the default corpus', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-invalid-settings-'));
const agentDir = join(home, 'agent');
const projectCwd = join(home, 'project');
mkdirSync(join(projectCwd, '.pi'), { recursive: true });
mkdirSync(agentDir, { recursive: true });
writeFileSync(join(projectCwd, '.pi', 'settings.json'), '{}');
for (const sessionDir of [false, 0]) {
writeFileSync(join(agentDir, 'settings.json'), JSON.stringify({ sessionDir }));
const resolved = resolveDefaultPiRoot({
env: { PI_CODING_AGENT_DIR: agentDir },
homeDir: home,
cwd: projectCwd,
});
assert.equal(resolved.requiresExplicitRoot, true);
assert.match(resolved.reason, /sessionDir must be a string/);
}
});
test('Pi discovery never certifies an invalid session root as empty', () => {
const parent = mkdtempSync(join(tmpdir(), 'obelisk-pi-invalid-root-'));
const root = join(parent, 'sessions');
writeFileSync(root, 'not a directory');
let status = 'unknown';
const units = createPiProvider({ rootDir: root }).discover({
lastCursor: () => null,
reportCompleteInventory: () => { status = 'complete'; },
reportIncompleteInventory: () => { status = 'incomplete'; },
});
assert.deepEqual(units, []);
assert.equal(status, 'incomplete');
});
test('Pi tool fixture projects canonical messages, usage, file paths, raw lines, and persistence', () => {
const { root, path } = writeSession(fixture('tool-session.jsonl'));
const { provider, unit, values, cursor } = parseOnly(root);
const byKind = kind => values.filter(record => record.kind === kind);
assert.match(cursor, /^\d+(?:\.\d+)?:0:pi-snapshot-v1:/);
assert.deepEqual(values[0], { kind: 'delete-session', sessionId: unit.sessionId });
assert.deepEqual(
byKind('message').map(record => [record.role, record.content_type, record.text]),
[
['user', 'text', 'Read probe.txt and report it'],
['assistant', 'tool_use', null],
['toolResult', 'tool_result', 'real-pi-tool-result\n'],
['assistant', 'text', 'The read tool returned real-pi-tool-result.'],
],
);
assert.equal(byKind('message')[1].input_tokens, 17);
assert.equal(byKind('message')[1].output_tokens, 5);
const toolUseMessage = byKind('message')[1];
assert.deepEqual(byKind('tool_call').map(record => ({
id: record.id,
name: record.name,
file_path: record.file_path,
})), [{
id: `${toolUseMessage.uuid}:tool`,
name: 'read',
file_path: 'probe.txt',
}]);
assert.equal(byKind('tool_result')[0].file_path, 'probe.txt');
assert.equal(byKind('session')[0].title, 'Tool probe');
assert.equal(byKind('session')[0].message_count, 4);
assert.equal(byKind('session')[0].countMode, 'total');
const raw = provider.raw({
source: 'pi',
messageUuid: byKind('message')[1].uuid,
session: { id: byKind('session')[0].id, jsonl_path: path },
agentId: null,
cursor,
});
assert.match(raw.text, /"type":"toolCall"/);
assert.equal(raw.messageText, null);
const direct = assembleSessionDetail(values);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const persisted = assembleSessionDetail({
session: db.prepare('SELECT * FROM sessions').get(),
messages: db.prepare('SELECT * FROM messages ORDER BY timestamp,uuid').all(),
toolCalls: db.prepare('SELECT * FROM tool_calls').all(),
toolResults: db.prepare('SELECT * FROM tool_results').all(),
summaries: db.prepare('SELECT * FROM summaries').all(),
});
assert.deepEqual(persisted, direct);
db.close();
});
test('raw Pi lookup resolves the exact canonical block and rejects replacement provenance', () => {
const longText = 'long-block-'.repeat(1100);
const written = writeSession(jsonl([
header({ id: 'raw-block-session' }),
userEntry('raw-user', null, 'raw prompt'),
{
type: 'message',
id: 'raw-assistant',
parentId: 'raw-user',
timestamp: '2026-08-02T10:00:02.000Z',
message: {
role: 'assistant',
content: [
{ type: 'thinking', thinking: 'exact private thought' },
{ type: 'text', text: longText },
{ type: 'toolCall', id: 'raw-call', name: 'read', arguments: { path: 'raw.ts' } },
],
errorMessage: 'exact synthetic error',
responseModel: 'probe',
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
},
},
]));
const { provider, values, cursor } = parseOnly(written.root);
const session = values.find(record => record.kind === 'session');
const messages = values.filter(record => (
record.kind === 'message'
&& record.role === 'assistant'
));
const lookup = message => provider.raw({
source: 'pi',
messageUuid: message.uuid,
session: { id: session.id, jsonl_path: written.path },
agentId: null,
cursor,
});
assert.equal(lookup(messages.find(message => message.content_type === 'thinking')).messageText, 'exact private thought');
assert.equal(lookup(messages.find(message => message.content_type === 'text')).messageText, longText);
assert.equal(messages.find(message => message.content_type === 'text').text.length, 10000);
assert.equal(lookup(messages.find(message => message.content_type === 'tool_use')).messageText, null);
assert.equal(lookup(messages.find(message => message.content_type === 'error')).messageText, 'exact synthetic error');
assert.equal(provider.raw({
source: 'pi',
messageUuid: `${session.id}:entry:000002:message:block:9999`,
session: { id: session.id, jsonl_path: written.path },
agentId: null,
cursor,
}), null);
assert.equal(provider.raw({
source: 'pi',
messageUuid: messages[0].uuid,
session: { id: session.id, jsonl_path: written.root },
agentId: null,
cursor,
}), null);
writeFileSync(written.path, jsonl([
header({ id: 'raw-block-session' }),
userEntry('replacement-user', null, 'replacement lure'),
userEntry('replacement-second', 'replacement-user', 'unrelated same-header replacement'),
]));
assert.equal(lookup(messages[0]), null);
});
test('structured tools remain branch-local when Pi reuses a native tool id', () => {
const records = [
header({ id: 'branch-tool-id-reuse' }),
userEntry('root', null, 'shared prompt'),
{
type: 'message',
id: 'active-call',
parentId: 'root',
timestamp: '2026-08-02T10:00:02.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'shared-native-id',
name: 'read',
arguments: { path: 'active.ts' },
}],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
},
},
{
type: 'message',
id: 'active-result',
parentId: 'active-call',
timestamp: '2026-08-02T10:00:03.000Z',
message: {
role: 'toolResult',
toolCallId: 'shared-native-id',
toolName: 'read',
content: [{ type: 'text', text: 'ACTIVE RESULT' }],
isError: false,
timestamp: Date.parse('2026-08-02T10:00:03.000Z'),
},
},
{
type: 'message',
id: 'abandoned-call',
parentId: 'root',
timestamp: '2026-08-02T10:00:04.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'shared-native-id',
name: 'read',
arguments: { path: 'abandoned.ts' },
}],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:04.000Z'),
},
},
{
type: 'message',
id: 'abandoned-result',
parentId: 'abandoned-call',
timestamp: '2026-08-02T10:00:05.000Z',
message: {
role: 'toolResult',
toolCallId: 'shared-native-id',
toolName: 'read',
content: [{ type: 'text', text: 'ABANDONED RESULT' }],
isError: true,
timestamp: Date.parse('2026-08-02T10:00:05.000Z'),
},
},
{
type: 'leaf',
id: 'select-active',
parentId: 'abandoned-result',
targetId: 'active-result',
timestamp: '2026-08-02T10:00:06.000Z',
},
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const activeToolUse = values.find(record => (
record.kind === 'message'
&& record.content_type === 'tool_use'
&& record.visibility === 'visible'
));
const inactiveToolUse = values.find(record => (
record.kind === 'message'
&& record.content_type === 'tool_use'
&& record.visibility === 'inactive'
));
const calls = values.filter(record => record.kind === 'tool_call');
const results = values.filter(record => record.kind === 'tool_result');
const activeCall = calls.find(record => record.file_path === 'active.ts');
const inactiveCall = calls.find(record => record.file_path === 'abandoned.ts');
const activeResult = results.find(record => record.content === 'ACTIVE RESULT');
const inactiveResult = results.find(record => record.content === 'ABANDONED RESULT');
assert.equal(calls.length, 2);
assert.deepEqual(
(({ id, message_uuid, file_path }) => ({ id, message_uuid, file_path }))(activeCall),
{
id: `${activeToolUse.uuid}:tool`,
message_uuid: activeToolUse.uuid,
file_path: 'active.ts',
},
);
assert.deepEqual(
(({ id, message_uuid, file_path }) => ({ id, message_uuid, file_path }))(inactiveCall),
{
id: `${inactiveToolUse.uuid}:tool`,
message_uuid: inactiveToolUse.uuid,
file_path: 'abandoned.ts',
},
);
assert.deepEqual(
(({ tool_use_id, content, file_path, is_error }) => ({
tool_use_id,
content,
file_path,
is_error,
}))(activeResult),
{
tool_use_id: activeCall.id,
content: 'ACTIVE RESULT',
file_path: 'active.ts',
is_error: 0,
},
);
assert.deepEqual(
(({ tool_use_id, content, file_path, is_error }) => ({
tool_use_id,
content,
file_path,
is_error,
}))(inactiveResult),
{
tool_use_id: inactiveCall.id,
content: 'ABANDONED RESULT',
file_path: 'abandoned.ts',
is_error: 1,
},
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
assert.equal(db.prepare('SELECT COUNT(*) AS count FROM tool_calls').get().count, 2);
assert.equal(db.prepare('SELECT COUNT(*) AS count FROM tool_results').get().count, 2);
assert.deepEqual(
db.prepare('SELECT file_path FROM tool_calls ORDER BY file_path').all().map(row => row.file_path),
['abandoned.ts', 'active.ts'],
);
const query = createQueryApi(db);
assert.deepEqual(query.fileHistory('abandoned.ts'), []);
assert.deepEqual(
query.fileHistory('abandoned.ts', { includeInactive: true })
.map(record => [record.toolCall.id, record.visibility]),
[[inactiveCall.id, 'inactive']],
);
assert.deepEqual(query.failures(unit.sessionId), []);
assert.deepEqual(
query.failures({ sessionId: unit.sessionId, includeInactive: true })
.map(record => [record.toolCall.id, record.visibility]),
[[inactiveCall.id, 'inactive']],
);
db.close();
});
test('an empty retainedTail checkpoint does not link a later result to discarded tool scope', () => {
const records = [
header({ id: 'checkpoint-tool-scope' }),
{
type: 'message',
id: 'discarded-call',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'discarded-native-id',
name: 'read',
arguments: { path: 'discarded.ts' },
}],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:01.000Z'),
},
},
{
type: 'compaction',
id: 'empty-checkpoint',
parentId: 'discarded-call',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'No retained tool context.',
firstKeptEntryId: 'discarded-call',
tokensBefore: 10,
retainedTail: [],
},
{
type: 'message',
id: 'unresolved-result',
parentId: 'empty-checkpoint',
timestamp: '2026-08-02T10:00:03.000Z',
message: {
role: 'toolResult',
toolCallId: 'discarded-native-id',
toolName: 'read',
content: [{ type: 'text', text: 'UNRESOLVED RESULT' }],
isError: false,
timestamp: Date.parse('2026-08-02T10:00:03.000Z'),
},
},
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
const discardedCalls = values.filter(record => record.kind === 'tool_call');
assert.equal(discardedCalls.length, 1);
const discardedCall = discardedCalls[0];
assert.equal(
values.find(record => (
record.kind === 'message'
&& record.uuid === discardedCall.message_uuid
)).visibility,
'inactive',
);
assert.equal(values.filter(record => record.kind === 'tool_result').length, 0);
assert.equal(
values.find(record => record.kind === 'message' && record.text === 'UNRESOLVED RESULT').visibility,
'visible',
);
});
test('a legacy compaction does not link a visible result to an inactive tool call', () => {
const records = [
header({ id: 'legacy-tool-scope' }),
{
type: 'message',
id: 'discarded-call',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'discarded-native-id',
name: 'read',
arguments: { path: '/secret/inactive' },
}],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:01.000Z'),
},
},
userEntry('kept-user', 'discarded-call', 'kept context', '2026-08-02T10:00:02.000Z'),
{
type: 'compaction',
id: 'legacy-compaction',
parentId: 'kept-user',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Discard the tool call.',
firstKeptEntryId: 'kept-user',
tokensBefore: 10,
},
{
type: 'message',
id: 'standalone-result',
parentId: 'legacy-compaction',
timestamp: '2026-08-02T10:00:04.000Z',
message: {
role: 'toolResult',
toolCallId: 'discarded-native-id',
toolName: 'read',
content: [{ type: 'text', text: 'VISIBLE STANDALONE FAILURE' }],
isError: true,
timestamp: Date.parse('2026-08-02T10:00:04.000Z'),
},
},
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const discardedCall = values.find(record => record.kind === 'tool_call');
const callMessage = values.find(record => (
record.kind === 'message'
&& record.uuid === discardedCall.message_uuid
));
const resultMessage = values.find(record => (
record.kind === 'message'
&& record.text === 'VISIBLE STANDALONE FAILURE'
));
assert.equal(callMessage.visibility, 'inactive');
assert.equal(resultMessage.visibility, 'visible');
assert.equal(values.filter(record => record.kind === 'tool_result').length, 0);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.deepEqual(query.failures(unit.sessionId), []);
assert.deepEqual(query.fileHistory('/secret/inactive'), []);
assert.deepEqual(
query.fileHistory('/secret/inactive', { includeInactive: true })
.map(record => record.visibility),
['inactive'],
);
db.close();
});
test('real-model-derived fixture preserves error, image, cache, and reasoning semantics', () => {
const { root } = writeSession(fixture('real-model-session.jsonl'));
const { values } = parseOnly(root);
const messages = values.filter(record => record.kind === 'message');
assert.deepEqual(messages.map(record => [record.role, record.content_type, record.text]), [
['user', 'text', 'Reply with the probe marker'],
['user', 'image', '[image image/png; base64 chars=8]'],
['assistant', 'error', '404 not found'],
['user', 'text', 'Try the responses protocol'],
['assistant', 'text', 'REAL_PI_PROBE_OK'],
]);
const success = messages.at(-1);
assert.equal(success.input_tokens, 4818);
assert.equal(success.output_tokens, 20);
assert.equal(messages.some(record => record.content_type === 'thinking'), false);
assert.equal(values.find(record => record.kind === 'session').message_count, 5);
});
test('durable leaf and null leaf control active visibility without deleting physical evidence', () => {
const source = writeSession(fixture('harness-source.jsonl'));
const parsed = parseOnly(source.root);
const parsedSource = parsed.values;
const sourceMessages = parsedSource.filter(record => record.kind === 'message');
assert.equal(sourceMessages.length, 6, 'complete retainedTail must not be projected twice');
assert.deepEqual(
sourceMessages.filter(record => record.visibility === 'visible').map(record => record.text),
['Harness first user turn'],
);
assert.equal(
parsedSource.find(record => record.kind === 'session').message_count,
1,
);
assert.equal(assembleSessionDetail(parsedSource).messages.length, 1);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, parsed.unit, parsed.provider.parse(parsed.unit, null));
assert.equal(createQueryApi(db).sessions({ source: 'pi' })[0].message_count, 1);
db.close();
const nullLeaf = writeSession(fixture('harness-null-leaf.jsonl'));
const parsedNull = parseOnly(nullLeaf.root).values;
const physical = parsedNull.find(record => record.kind === 'message');
assert.equal(physical.visibility, 'inactive');
assert.equal(parsedNull.find(record => record.kind === 'session').message_count, 0);
assert.equal(
parsedNull.find(record => record.kind === 'session').title,
'This physical message is no longer active',
);
assert.deepEqual(assembleSessionDetail(parsedNull).messages, []);
});
test('an empty latest session name clears it and falls back to the physical first user', () => {
const records = [
header({ id: 'physical-session-title' }),
{
...userEntry('physical-user', null, ''),
message: {
role: 'user',
content: [
{ type: 'image', mimeType: 'image/png', data: 'AAAA' },
{ type: 'text', text: 'physical' },
{ type: 'text', text: 'fallback' },
],
timestamp: Date.parse('2026-08-02T10:00:01.000Z'),
},
},
{
type: 'session_info',
id: 'named',
parentId: 'physical-user',
timestamp: '2026-08-02T10:00:02.000Z',
name: ' Named session ',
},
{
type: 'session_info',
id: 'blank-name',
parentId: 'named',
timestamp: '2026-08-02T10:00:03.000Z',
name: ' ',
},
{
type: 'leaf',
id: 'null-leaf',
parentId: 'blank-name',
targetId: null,
timestamp: '2026-08-02T10:00:04.000Z',
},
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.equal(values.find(record => record.kind === 'session').title, 'physical fallback');
assert.equal(
values.find(record => record.kind === 'message' && record.text === 'physical').visibility,
'inactive',
);
});
test('retainedTail replaces complete ancestors when its compaction is on the active branch', () => {
const records = fixture('harness-source.jsonl')
.trim()
.split('\n')
.map(line => JSON.parse(line))
.filter(record => record.type !== 'leaf');
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const messages = values.filter(record => record.kind === 'message');
const visibleText = messages
.filter(record => record.visibility === 'visible')
.map(record => record.text);
assert.equal(messages.length, 8, 'physical ancestors and active retained context are both preserved');
assert.deepEqual(visibleText, [
'Harness retained user turn',
'Harness retained assistant turn',
'Harness post-compaction user turn',
'Harness post-compaction assistant turn',
]);
assert.equal(
messages.find(record => record.text === 'Harness first user turn').visibility,
'inactive',
);
assert.equal(
messages.find(record => record.text === 'Harness retained user turn' && !record.uuid.includes(':tail:')).visibility,
'inactive',
);
assert.equal(values.find(record => record.kind === 'summary').visibility, 'visible');
assert.deepEqual(assembleSessionDetail(values).messages.map(message => message.text), visibleText);
assert.deepEqual(
messages.reduce(
(total, message) => ({
input: total.input + (message.input_tokens ?? 0),
output: total.output + (message.output_tokens ?? 0),
}),
{ input: 0, output: 0 },
),
{ input: 369, output: 69 },
'retained context copies must not duplicate model usage',
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.equal(query.search('Harness first').length, 0);
assert.deepEqual(query.thread(unit.sessionId).map(message => message.text), visibleText);
db.close();
});
test('legacy compaction exposes only firstKeptEntryId ancestors and later descendants', () => {
const records = [
header({ id: 'first-kept-context' }),
userEntry('old-root', null, 'old root'),
userEntry('old-near', 'old-root', 'old near', '2026-08-02T10:00:02.000Z'),
userEntry('kept-start', 'old-near', 'kept start', '2026-08-02T10:00:03.000Z'),
userEntry('kept-end', 'kept-start', 'kept end', '2026-08-02T10:00:04.000Z'),
{
type: 'compaction',
id: 'legacy-compaction',
parentId: 'kept-end',
timestamp: '2026-08-02T10:00:05.000Z',
summary: 'Earlier context summary',
firstKeptEntryId: 'kept-start',
tokensBefore: 100,
},
userEntry('post-compaction', 'legacy-compaction', 'post compaction', '2026-08-02T10:00:06.000Z'),
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const messages = values.filter(record => record.kind === 'message');
assert.deepEqual(
messages.filter(record => record.visibility === 'visible').map(record => record.text),
['kept start', 'kept end', 'post compaction'],
);
assert.deepEqual(
messages.filter(record => record.visibility === 'inactive').map(record => record.text),
['old root', 'old near'],
);
assert.equal(values.find(record => record.kind === 'summary').visibility, 'visible');
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.equal(query.search('old near').length, 0);
assert.deepEqual(
query.thread(unit.sessionId).map(message => message.text),
['kept start', 'kept end', 'post compaction'],
);
db.close();
});
test('a later legacy compaction can retain context across an earlier compaction', () => {
const records = [
header({ id: 'nested-compaction-context' }),
userEntry('root', null, 'root'),
userEntry('near-first', 'root', 'near first', '2026-08-02T10:00:02.000Z'),
{
type: 'compaction',
id: 'first-compaction',
parentId: 'near-first',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'First summary',
firstKeptEntryId: 'near-first',
tokensBefore: 50,
},
userEntry('after-first', 'first-compaction', 'after first', '2026-08-02T10:00:04.000Z'),
{
type: 'compaction',
id: 'second-compaction',
parentId: 'after-first',
timestamp: '2026-08-02T10:00:05.000Z',
summary: 'Second summary',
firstKeptEntryId: 'root',
tokensBefore: 100,
},
userEntry('after-second', 'second-compaction', 'after second', '2026-08-02T10:00:06.000Z'),
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['root', 'near first', 'after first', 'after second'],
);
assert.deepEqual(
values
.filter(record => record.kind === 'summary' && record.visibility === 'visible')
.map(record => record.content),
['First summary', 'Second summary'],
);
});
test('a retained-tail checkpoint bounds a later legacy compaction like Pi 0.83 storage', () => {
const records = [
header({ id: 'mixed-compaction-context' }),
userEntry('root', null, 'discarded root'),
{
type: 'compaction',
id: 'checkpoint',
parentId: 'root',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'Checkpoint summary',
firstKeptEntryId: 'root',
tokensBefore: 50,
retainedTail: [{
role: 'user',
content: 'checkpoint tail',
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
}],
},
userEntry('after-checkpoint', 'checkpoint', 'discarded after checkpoint', '2026-08-02T10:00:03.000Z'),
{
type: 'compaction',
id: 'later-legacy',
parentId: 'after-checkpoint',
timestamp: '2026-08-02T10:00:04.000Z',
summary: 'Later legacy summary',
firstKeptEntryId: 'root',
tokensBefore: 100,
},
userEntry('head', 'later-legacy', 'active head', '2026-08-02T10:00:05.000Z'),
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['active head'],
);
assert.deepEqual(
values
.filter(record => record.kind === 'summary' && record.visibility === 'visible')
.map(record => record.content),
['Later legacy summary'],
);
});
test('legacy checkpoint fork accepts a truncated parent before firstKeptEntryId', () => {
const records = [
header({ id: 'legacy-checkpoint-fork' }),
userEntry('kept-start', 'omitted-parent', 'kept start'),
userEntry('kept-end', 'kept-start', 'kept end', '2026-08-02T10:00:02.000Z'),
{
type: 'compaction',
id: 'legacy-compaction',
parentId: 'kept-end',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Omitted source context summary',
firstKeptEntryId: 'kept-start',
tokensBefore: 100,
},
userEntry('post-compaction', 'legacy-compaction', 'post compaction', '2026-08-02T10:00:04.000Z'),
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['kept start', 'kept end', 'post compaction'],
);
assert.equal(values.find(record => record.kind === 'summary').visibility, 'visible');
});
test('checkpoint fork materializes retainedTail exactly once and reconnects later parent identity', () => {
const { root, path } = writeSession(fixture('harness-checkpoint-fork.jsonl'));
const { provider, unit, values, cursor } = parseOnly(root);
const messages = values.filter(record => record.kind === 'message');
assert.deepEqual(messages.map(record => [record.content_type, record.text]), [
['text', 'Harness retained user turn'],
['thinking', 'retained reasoning'],
['text', 'Harness retained assistant turn'],
['text', 'Harness post-compaction user turn'],
['text', 'Harness post-compaction assistant turn'],
]);
assert.equal(messages[1].input_tokens, null);
assert.equal(messages[2].input_tokens, null);
assert.equal(messages[2].output_tokens, null);
assert.equal(messages[4].input_tokens, 123);
assert.equal(messages[4].output_tokens, 23);
assert.equal(messages[3].parent_uuid, messages[2].uuid);
assert.equal(values.filter(record => record.kind === 'summary').length, 1);
assert.deepEqual(assembleSessionDetail(values).messages.map(message => message.text), [
'Harness retained user turn',
'Harness retained assistant turn',
'Harness post-compaction user turn',
'Harness post-compaction assistant turn',
]);
const raw = message => provider.raw({
source: 'pi',
messageUuid: message.uuid,
session: { id: unit.sessionId, jsonl_path: path },
agentId: null,
cursor,
});
assert.doesNotMatch(raw(messages[1]).text, /"retainedTail"/);
assert.equal(JSON.parse(raw(messages[1]).text).role, 'assistant');
assert.equal(raw(messages[1]).messageText, 'retained reasoning');
assert.equal(raw(messages[2]).messageText, 'Harness retained assistant turn');
});
test('retained-tail raw evidence never exposes hidden sibling messages', () => {
const records = [
header({ id: 'retained-raw-visibility' }),
userEntry('root', null, 'discarded root'),
{
type: 'compaction',
id: 'checkpoint',
parentId: 'root',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'Checkpoint summary',
firstKeptEntryId: 'root',
tokensBefore: 50,
retainedTail: [
{
role: 'user',
content: 'visible retained evidence',
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
},
{
role: 'custom',
content: 'HIDDEN RETAINED SIBLING',
display: false,
timestamp: Date.parse('2026-08-02T10:00:03.000Z'),
},
],
},
];
const { root } = writeSession(jsonl(records));
const { provider, unit, values } = parseOnly(root);
const visible = values.find(record => (
record.kind === 'message'
&& record.visibility === 'visible'
&& record.text === 'visible retained evidence'
));
const hidden = values.find(record => (
record.kind === 'message'
&& record.visibility === 'hidden'
));
assert.ok(visible);
assert.ok(hidden);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db, {
providerRegistry: createProviderRegistry([provider]),
});
const raw = query.raw(visible.uuid);
assert.equal(raw.text, JSON.stringify(records[2].retainedTail[0]));
assert.doesNotMatch(raw.text, /HIDDEN RETAINED SIBLING/);
assert.equal(query.raw(hidden.uuid), null);
db.close();
});
test('a retained tail beginning with a tool result remains standalone active evidence', () => {
const records = [
header({ id: 'retained-tool-result' }),
{
type: 'message',
id: 'physical-call',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'assistant',
content: [{
type: 'toolCall',
id: 'split-call',
name: 'read',
arguments: { path: 'probe.txt' },
}],
usage: { input: 5, output: 2 },
timestamp: 1785664801000,
},
},
{
type: 'compaction',
id: 'checkpoint',
parentId: 'physical-call',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'The preceding tool turn was split.',
firstKeptEntryId: 'physical-call',
tokensBefore: 100,
retainedTail: [{
role: 'toolResult',
toolCallId: 'split-call',
toolName: 'read',
content: [{ type: 'text', text: 'RETAINED RESULT' }],
isError: false,
timestamp: 1785664802000,
}],
},
userEntry('after-checkpoint', 'checkpoint', 'after', '2026-08-02T10:00:03.000Z'),
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['RETAINED RESULT', 'after'],
);
assert.deepEqual(
assembleSessionDetail(values).messages.map(message => message.text),
['RETAINED RESULT', 'after'],
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
assert.deepEqual(
assembleSessionDetail({
session: db.prepare('SELECT * FROM sessions').get(),
messages: db.prepare('SELECT * FROM messages ORDER BY timestamp,uuid').all(),
toolCalls: db.prepare('SELECT * FROM tool_calls').all(),
toolResults: db.prepare('SELECT * FROM tool_results').all(),
summaries: db.prepare('SELECT * FROM summaries').all(),
}).messages.map(message => message.text),
['RETAINED RESULT', 'after'],
);
db.close();
});
test('full tree marks abandoned messages inactive while indexing every summary and explicit user bash action', () => {
const records = [
header(),
userEntry('root-user', null, 'shared root'),
userEntry('abandoned-user', 'root-user', 'abandoned branch'),
{
type: 'message',
id: 'abandoned-assistant',
parentId: 'abandoned-user',
timestamp: '2026-08-02T10:00:03.000Z',
message: {
role: 'assistant',
content: [{ type: 'text', text: 'discarded answer' }],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: Date.parse('2026-08-02T10:00:03.000Z'),
},
},
{
type: 'custom_message',
id: 'inactive-custom',
parentId: 'abandoned-assistant',
timestamp: '2026-08-02T10:00:03.500Z',
customType: 'probe',
content: 'superseded extension context',
display: true,
},
{
type: 'branch_summary',
id: 'branch-summary',
parentId: 'root-user',
timestamp: '2026-08-02T10:00:04.000Z',
fromId: 'abandoned-assistant',
summary: 'The abandoned branch tried a discarded answer.',
},
{
type: 'custom_message',
id: 'hidden-custom',
parentId: 'branch-summary',
timestamp: '2026-08-02T10:00:05.000Z',
customType: 'probe',
content: 'hidden extension context',
display: false,
},
{
type: 'custom_message',
id: 'visible-custom',
parentId: 'hidden-custom',
timestamp: '2026-08-02T10:00:06.000Z',
customType: 'probe',
content: 'visible extension context',
display: true,
},
{
type: 'message',
id: 'bash',
parentId: 'visible-custom',
timestamp: '2026-08-02T10:00:07.000Z',
message: {
role: 'bashExecution',
command: 'printf probe',
output: 'x'.repeat(12_000),
exitCode: 0,
cancelled: false,
truncated: true,
fullOutputPath: '/tmp/must-not-be-read',
excludeFromContext: true,
timestamp: Date.parse('2026-08-02T10:00:07.000Z'),
},
},
{
type: 'compaction',
id: 'compaction',
parentId: 'bash',
timestamp: '2026-08-02T10:00:08.000Z',
summary: 'Earlier active work.',
firstKeptEntryId: 'root-user',
tokensBefore: 42,
},
userEntry('active-user', 'compaction', 'active request', '2026-08-02T10:00:09.000Z'),
];
const { root } = writeSession(jsonl(records));
const { provider, unit, values } = parseOnly(root);
const messages = values.filter(record => record.kind === 'message');
assert.equal(messages.find(record => record.text === 'abandoned branch').visibility, 'inactive');
assert.equal(messages.find(record => record.text === 'abandoned branch').is_meta, 0);
assert.equal(messages.find(record => record.text === 'superseded extension context').visibility, 'inactive');
assert.equal(messages.find(record => record.text === 'hidden extension context').visibility, 'hidden');
assert.equal(messages.find(record => record.text === 'visible extension context').visibility, 'visible');
const bash = messages.find(record => record.role === 'bashExecution');
assert.equal(bash.type, 'user');
assert.equal(bash.content_type, 'bash_execution');
assert.equal(bash.is_meta, 0);
assert.equal(bash.text.length, 10_000);
assert.match(bash.text, /\[Output truncated\. Full output: \/tmp\/must-not-be-read\]$/);
assert.equal(values.filter(record => record.kind === 'tool_call').length, 0);
assert.deepEqual(
values.filter(record => record.kind === 'summary').map(record => record.source),
['pi:branch_summary', 'pi:compaction'],
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.equal(query.search('abandoned').length, 0);
assert.equal(query.search('abandoned', { includeInactive: true })[0].message.visibility, 'inactive');
assert.equal(query.search('superseded extension').length, 0);
assert.equal(query.search('superseded extension', { includeInactive: true }).length, 0);
assert.equal(
query.search('superseded extension', {
includeInactive: true,
includeMeta: true,
})[0].message.visibility,
'inactive',
);
assert.equal(query.search('active request').length, 1);
assert.equal(query.thread(unit.sessionId).some(message => message.text === 'abandoned branch'), false);
assert.equal(
query.thread(unit.sessionId, { includeInactive: true })
.find(message => message.text === 'abandoned branch').visibility,
'inactive',
);
db.close();
});
test('inactive Pi summaries remain available for accounting and explicit historical queries', () => {
const records = [
header({ id: 'summary-visibility' }),
userEntry('root', null, 'shared root'),
userEntry('abandoned', 'root', 'abandoned prompt'),
{
type: 'branch_summary',
id: 'abandoned-summary',
parentId: 'abandoned',
timestamp: '2026-08-02T10:00:03.000Z',
fromId: 'abandoned',
summary: 'ABANDONED SUMMARY SECRET',
usage: { input: 9, output: 2, cacheRead: 3, cacheWrite: 4 },
},
userEntry('active', 'root', 'active prompt', '2026-08-02T10:00:04.000Z'),
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const summary = values.find(record => record.kind === 'summary');
assert.equal(summary.visibility, 'inactive');
assert.equal(summary.input_tokens, 16);
assert.deepEqual(assembleSessionDetail(values).summaries, []);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.deepEqual(query.summaries(unit.sessionId), []);
assert.equal(
query.summaries({ sessionId: unit.sessionId, includeInactive: true })[0].visibility,
'inactive',
);
assert.deepEqual(
assembleSessionDetail({ summaries: db.prepare('SELECT * FROM summaries').all() }).summaries,
[],
);
assert.deepEqual(
{ ...db.prepare('SELECT content,visibility,input_tokens,output_tokens FROM summaries').get() },
{
content: 'ABANDONED SUMMARY SECRET',
visibility: 'inactive',
input_tokens: 16,
output_tokens: 2,
},
);
db.close();
});
test('lowercase read, edit, and write arguments.path populate call and matching result file paths', () => {
const records = [header()];
let parent = null;
for (const [index, name] of ['read', 'edit', 'write'].entries()) {
const callEntry = `call-entry-${index}`;
const callId = `call-${index}`;
records.push({
type: 'message',
id: callEntry,
parentId: parent,
timestamp: `2026-08-02T10:00:0${index + 1}.000Z`,
message: {
role: 'assistant',
content: [{ type: 'toolCall', id: callId, name, arguments: { path: `src/${name}.ts` } }],
model: 'probe',
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0 },
timestamp: 1785664800000 + index,
},
});
records.push({
type: 'message',
id: `result-entry-${index}`,
parentId: callEntry,
timestamp: `2026-08-02T10:00:1${index + 1}.000Z`,
message: {
role: 'toolResult',
toolCallId: callId,
toolName: name,
content: [{ type: 'text', text: `${name} result` }],
isError: false,
timestamp: 1785664801000 + index,
},
});
parent = `result-entry-${index}`;
}
const { root } = writeSession(jsonl(records));
const { values } = parseOnly(root);
assert.deepEqual(
values.filter(record => record.kind === 'tool_call').map(record => record.file_path),
['src/read.ts', 'src/edit.ts', 'src/write.ts'],
);
assert.deepEqual(
values.filter(record => record.kind === 'tool_result').map(record => record.file_path),
['src/read.ts', 'src/edit.ts', 'src/write.ts'],
);
});
test('tool-result nested model usage is retained on its canonical message', () => {
const records = [
header(),
{
type: 'message',
id: 'result',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'toolResult',
toolCallId: 'nested-call',
toolName: 'agent',
content: [{ type: 'text', text: 'nested result' }],
usage: { input: 11, output: 7, cacheRead: 3, cacheWrite: 2 },
isError: false,
timestamp: 1785664801000,
},
},
];
const { values } = parseOnly(writeSession(jsonl(records)).root);
const result = values.find(record => record.kind === 'message');
assert.equal(result.input_tokens, 16);
assert.equal(result.output_tokens, 7);
});
test('compaction and branch-summary model usage survives canonical persistence', () => {
const records = [
header({ id: 'summary-usage' }),
userEntry('user', null, 'summarize this'),
{
type: 'branch_summary',
id: 'branch-summary',
parentId: 'user',
timestamp: '2026-08-02T10:00:02.000Z',
fromId: 'user',
summary: 'Branch summary',
usage: { input: 10, output: 4, cacheRead: 2, cacheWrite: 3 },
},
{
type: 'compaction',
id: 'compaction',
parentId: 'branch-summary',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Compaction summary',
firstKeptEntryId: 'user',
tokensBefore: 100,
usage: { input: 20, output: 5, cacheRead: 6, cacheWrite: 7 },
},
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values.filter(record => record.kind === 'summary').map(record => ({
source: record.source,
input: record.input_tokens,
output: record.output_tokens,
})),
[
{ source: 'pi:branch_summary', input: 15, output: 4 },
{ source: 'pi:compaction', input: 33, output: 5 },
],
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
assert.deepEqual(
db.prepare('SELECT input_tokens,output_tokens FROM summaries ORDER BY timestamp').all()
.map(row => ({ ...row })),
[
{ input_tokens: 15, output_tokens: 4 },
{ input_tokens: 33, output_tokens: 5 },
],
);
db.close();
});
test('retained-tail summaries keep distinct canonical identities through persistence', () => {
const records = [
header({ id: 'retained-summary-identities' }),
userEntry('root', null, 'summarize retained context'),
{
type: 'compaction',
id: 'checkpoint',
parentId: 'root',
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Outer compaction',
firstKeptEntryId: 'root',
tokensBefore: 100,
retainedTail: [
{
role: 'branchSummary',
summary: 'First retained summary',
timestamp: Date.parse('2026-08-02T10:00:01.000Z'),
},
{
role: 'branchSummary',
summary: 'Second retained summary',
timestamp: Date.parse('2026-08-02T10:00:02.000Z'),
},
],
},
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
const retained = values.filter(record => (
record.kind === 'summary'
&& record.source === 'pi:branch_summary'
));
assert.deepEqual(retained.map(record => record.content), [
'First retained summary',
'Second retained summary',
]);
assert.equal(new Set(retained.map(record => record.id)).size, 2);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
assert.deepEqual(
db.prepare("SELECT content FROM summaries WHERE source='pi:branch_summary' ORDER BY timestamp")
.all().map(row => row.content),
retained.map(record => record.content),
);
const byTimestamp = (left, right) => String(left.timestamp).localeCompare(String(right.timestamp));
assert.deepEqual(
assembleSessionDetail({
summaries: db.prepare('SELECT * FROM summaries ORDER BY timestamp').all(),
}).summaries.sort(byTimestamp),
assembleSessionDetail(values).summaries.sort(byTimestamp),
);
db.close();
});
test('malformed physical JSON lines follow Pi while structural errors fail atomically', () => {
const complete = jsonl([header(), userEntry('user', null, 'no newline')], false);
assert.equal(parseOnly(writeSession(complete).root).values.filter(record => record.kind === 'message').length, 1);
const completedCorruption = [
JSON.stringify(header()),
JSON.stringify(userEntry('before', null, 'before malformed line')),
'{broken}',
JSON.stringify(userEntry('after', 'before', 'after malformed line')),
'',
].join('\n');
assert.deepEqual(
parseOnly(writeSession(completedCorruption).root).values
.filter(record => record.kind === 'message')
.map(record => record.text),
['before malformed line', 'after malformed line'],
);
const terminalGarbage = `${jsonl([header(), userEntry('user', null, 'prefix')])}not-json`;
assert.deepEqual(
parseOnly(writeSession(terminalGarbage).root).values
.filter(record => record.kind === 'message')
.map(record => record.text),
['prefix'],
);
const invalidValue = writeSession(`${JSON.stringify(header())}\n42\n`);
const valueProvider = createPiProvider({ rootDir: invalidValue.root });
const valueUnit = valueProvider.discover({ lastCursor: () => null })[0];
assert.throws(
() => drain(valueProvider.parse(valueUnit, null)),
/Malformed Pi JSONL value at line 2/,
);
const invalidMessage = writeSession(jsonl([
header(),
{
type: 'message',
id: 'invalid-message',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: null,
},
]));
const messageProvider = createPiProvider({ rootDir: invalidMessage.root });
const messageUnit = messageProvider.discover({ lastCursor: () => null })[0];
assert.throws(
() => drain(messageProvider.parse(messageUnit, null)),
/Malformed Pi message/,
);
});
test('official v1 migration preserves canonical identities and v2 hookMessage becomes custom metadata', () => {
const v1 = [
{
type: 'session',
id: 'legacy-v1',
},
{
type: 'message',
timestamp: '2026-08-02T10:00:01.000Z',
message: { role: 'user', content: 'legacy user', timestamp: 1785664800000 },
},
{
type: 'compaction',
timestamp: '2026-08-02T10:00:02.000Z',
summary: 'legacy summary',
firstKeptEntryIndex: 1,
tokensBefore: 100,
},
{
type: 'message',
timestamp: '2026-08-02T10:00:03.000Z',
message: { role: 'user', content: 'legacy tail', timestamp: 1785664803000 },
},
];
const written = writeSession(`${v1.map(record => JSON.stringify(record)).join('\n\n')}\n`);
const firstParse = parseOnly(written.root);
const first = firstParse.values;
const migrated = [
{ ...v1[0], version: 3 },
{ ...v1[1], id: 'random-migrated-1', parentId: null },
{
...v1[2],
id: 'random-migrated-2',
parentId: 'random-migrated-1',
firstKeptEntryId: 'random-migrated-1',
firstKeptEntryIndex: undefined,
},
{ ...v1[3], id: 'random-migrated-3', parentId: 'random-migrated-2' },
];
writeFileSync(written.path, jsonl(migrated));
const secondParse = parseOnly(written.root);
const second = secondParse.values;
assert.deepEqual(
first.filter(record => record.kind === 'message').map(record => record.uuid),
second.filter(record => record.kind === 'message').map(record => record.uuid),
);
assert.deepEqual(
first.filter(record => record.kind === 'summary').map(record => record.id),
second.filter(record => record.kind === 'summary').map(record => record.id),
);
const firstSession = first.find(record => record.kind === 'session');
const secondSession = second.find(record => record.kind === 'session');
assert.equal(firstSession.version, 'session-v1');
assert.equal(secondSession.version, 'session-v3');
assert.equal(firstSession.started_at, null);
assert.equal(firstSession.project, null);
assert.equal(firstSession.id, secondSession.id);
assert.ok(first.filter(record => record.kind === 'message').every(record => record.cwd === null));
const rawAfterMigration = firstParse.provider.raw({
source: 'pi',
messageUuid: first.find(record => record.kind === 'message').uuid,
session: { id: firstSession.id, jsonl_path: written.path },
agentId: null,
cursor: secondParse.cursor,
});
assert.deepEqual(JSON.parse(rawAfterMigration.text), migrated[1].message);
assert.equal(rawAfterMigration.messageText, 'legacy user');
const v2 = [
header({ version: 2, id: 'legacy-v2' }),
{
type: 'message',
id: 'hook',
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: {
role: 'hookMessage',
customType: 'legacy-hook',
content: 'extension context',
display: true,
timestamp: 1785664800000,
},
},
];
const hook = parseOnly(writeSession(jsonl(v2)).root).values.find(record => record.kind === 'message');
assert.deepEqual(
(({ type, role, content_type, is_meta, visibility }) => ({ type, role, content_type, is_meta, visibility }))(hook),
{ type: 'system', role: 'custom', content_type: 'custom', is_meta: 1, visibility: 'visible' },
);
});
test('invalid tree identity, cycle, and leaf target fail closed', () => {
const cases = [
{
pattern: /Duplicate Pi entry id/,
records: [header(), userEntry('same', null, 'one'), userEntry('same', 'same', 'two')],
},
{
pattern: /cycle/,
records: [header(), userEntry('a', 'b', 'a'), userEntry('b', 'a', 'b')],
},
{
pattern: /cycle/,
records: [
header(),
userEntry('a', 'b', 'a'),
userEntry('b', 'a', 'b'),
{
type: 'compaction',
id: 'unrelated-compaction',
parentId: null,
timestamp: '2026-08-02T10:00:03.000Z',
summary: 'Must not mask the cycle',
firstKeptEntryId: 'a',
tokensBefore: 10,
},
],
},
{
pattern: /leaf target .* does not exist/,
records: [
header(),
userEntry('a', null, 'a'),
{ type: 'leaf', id: 'leaf', parentId: 'a', targetId: 'missing', timestamp: '2026-08-02T10:00:02.000Z' },
],
},
];
for (const { records, pattern } of cases) {
const { root } = writeSession(jsonl(records));
const provider = createPiProvider({ rootDir: root });
const unit = provider.discover({ lastCursor: () => null })[0];
assert.throws(() => drain(provider.parse(unit, null)), pattern);
}
});
test('missing parents form official Pi orphan roots without exposing inactive branches', () => {
const records = [
header({ id: 'orphan-root' }),
userEntry('inactive-root', null, 'inactive root'),
userEntry('orphan', 'omitted-parent', 'orphan root', '2026-08-02T10:00:02.000Z'),
userEntry('orphan-child', 'orphan', 'orphan child', '2026-08-02T10:00:03.000Z'),
];
const { provider, unit, values } = parseOnly(writeSession(jsonl(records)).root);
assert.deepEqual(
values
.filter(record => record.kind === 'message' && record.visibility === 'visible')
.map(record => record.text),
['orphan root', 'orphan child'],
);
assert.equal(
values.find(record => record.kind === 'message' && record.text === 'inactive root').visibility,
'inactive',
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
persist(db, unit, provider.parse(unit, null));
const query = createQueryApi(db);
assert.deepEqual(query.thread(unit.sessionId).map(message => message.text), [
'orphan root',
'orphan child',
]);
assert.deepEqual(assembleSessionDetail(values).messages.map(message => message.text), [
'orphan root',
'orphan child',
]);
db.close();
});
test('discovery deduplicates identical same-project copies and rejects divergent copies', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-copies-'));
const original = fixture('tool-session.jsonl');
writeSession(original, { root, relativePath: 'a/session.jsonl' });
writeSession(original, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
assert.equal(provider.discover({ lastCursor: () => null }).length, 1);
writeSession(`${original}${JSON.stringify({
type: 'session_info',
id: 'divergent-name',
parentId: '9db96a87',
timestamp: '2026-08-02T09:42:39.000Z',
name: 'Diverged copy',
})}\n`, { root, relativePath: 'b/session.jsonl' });
const divergent = provider.discover({ lastCursor: () => null });
assert.equal(divergent.length, 2);
for (const unit of divergent) {
assert.throws(() => drain(provider.parse(unit, null)), /Divergent Pi session copies/);
}
});
test('discovery follows readable Pi JSONL file symlinks without retracting provenance', () => {
const written = writeSession(jsonl([
header({ id: 'symlink-session' }),
userEntry('symlink-user', null, 'symlink evidence'),
]));
const provider = createPiProvider({ rootDir: written.root });
const original = provider.discover({ lastCursor: () => null })[0];
const parsed = drain(provider.parse(original, null));
const targetDir = mkdtempSync(join(tmpdir(), 'obelisk-pi-symlink-target-'));
const target = join(targetDir, 'session.jsonl');
renameSync(written.path, target);
symlinkSync(target, written.path);
const units = provider.discover({
lastCursor: key => key === written.path ? parsed.cursor : null,
changedPaths: [written.path],
indexedSessions: () => [{
sessionId: original.sessionId,
jsonlPath: written.path,
}],
});
assert.equal(units.some(unit => unit.meta?.kind === 'pi-tombstone'), false);
assert.equal(units.length, 1);
assert.equal(units[0].sessionId, original.sessionId);
assert.equal(
drain(provider.parse(units[0], null)).values
.find(record => record.kind === 'message').text,
'symlink evidence',
);
});
test('a malformed Pi file does not force unrelated unchanged sessions to reparse', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-bad-file-scope-'));
const damaged = writeSession(jsonl([
header({ id: 'damaged-session' }),
userEntry('damaged-user', null, 'damaged source'),
]), { root, relativePath: 'a/session.jsonl' });
const unchanged = writeSession(jsonl([
header({ id: 'unchanged-session' }),
userEntry('unchanged-user', null, 'unchanged source'),
]), { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const cursors = new Map();
const indexed = [];
for (const unit of provider.discover({ lastCursor: () => null })) {
cursors.set(unit.key, drain(provider.parse(unit, null)).cursor);
indexed.push({ sessionId: unit.sessionId, jsonlPath: unit.key });
}
writeFileSync(damaged.path, '{broken}\n');
const units = provider.discover({
lastCursor: key => cursors.get(key) ?? null,
indexedSessions: () => indexed,
});
assert.deepEqual(units.map(unit => unit.key), [damaged.path]);
assert.equal(units.some(unit => unit.key === unchanged.path), false);
assert.throws(() => drain(provider.parse(units[0], null)), /Empty Pi session/);
});
test('a moved session with an unreadable identity cannot retract its last good snapshot', () => {
const written = writeSession(jsonl([
header({ id: 'moved-torn-session' }),
userEntry('moved-user', null, 'last good evidence'),
]));
const provider = createPiProvider({ rootDir: written.root });
const original = provider.discover({ lastCursor: () => null })[0];
const parsed = drain(provider.parse(original, null));
const movedPath = join(written.root, 'moved', 'session.jsonl');
mkdirSync(dirname(movedPath), { recursive: true });
renameSync(written.path, movedPath);
writeFileSync(movedPath, '{torn header\n');
let incomplete = false;
const units = provider.discover({
lastCursor: key => key === written.path ? parsed.cursor : null,
indexedSessions: () => [{
sessionId: original.sessionId,
jsonlPath: written.path,
}],
reportIncompleteInventory: () => { incomplete = true; },
});
assert.equal(incomplete, false, 'a bad identity is not an incomplete filesystem traversal');
assert.deepEqual(units.map(unit => unit.key), [movedPath]);
assert.equal(units[0].retractSessionIds, undefined);
assert.equal(units.some(unit => unit.meta?.kind === 'pi-tombstone'), false);
assert.throws(() => drain(provider.parse(units[0], null)), /Empty Pi session/);
});
test('a bad selected copy forces an unchanged valid duplicate to refresh provenance', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-bad-copy-'));
const content = fixture('tool-session.jsonl');
const second = writeSession(content, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const initialUnit = provider.discover({ lastCursor: () => null })[0];
const initial = drain(provider.parse(initialUnit, null));
const cursors = new Map([[second.path, initial.cursor]]);
const first = writeSession(content, { root, relativePath: 'a/session.jsonl' });
const selected = provider.discover({
lastCursor: key => cursors.get(key) ?? null,
})[0];
assert.equal(selected.key, first.path);
const selectedParse = drain(provider.parse(selected, null));
cursors.set(first.path, selectedParse.cursor);
writeFileSync(first.path, '{broken}\n');
const recovery = provider.discover({
lastCursor: key => cursors.get(key) ?? null,
indexedSessions: () => [{
sessionId: initialUnit.sessionId,
jsonlPath: first.path,
}],
});
const survivor = recovery.find(unit => unit.key === second.path);
assert.ok(survivor, 'the valid copy must bypass its unchanged cursor');
assert.equal(survivor.sessionId, initialUnit.sessionId);
assert.equal(
drain(provider.parse(survivor, null)).values
.find(record => record.kind === 'session').jsonl_path,
second.path,
);
});
test('the same official project-local session id remains distinct across cwd namespaces', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-project-ids-'));
const firstHeader = header({ id: 'shared-custom-id', cwd: '/tmp/pi-project-a' });
const secondHeader = header({ id: 'shared-custom-id', cwd: '/tmp/pi-project-b' });
writeSession(jsonl([
firstHeader,
userEntry('project-a-user', null, 'project A evidence'),
]), { root, relativePath: 'a/session.jsonl' });
writeSession(jsonl([
secondHeader,
userEntry('project-b-user', null, 'project B evidence'),
]), { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const units = provider.discover({ lastCursor: () => null });
assert.equal(units.length, 2);
assert.deepEqual(
new Set(units.map(unit => unit.sessionId)),
new Set([piSessionId(firstHeader), piSessionId(secondHeader)]),
);
const db = new DatabaseSync(':memory:');
db.exec(SCHEMA);
for (const unit of units) persist(db, unit, provider.parse(unit, null));
assert.deepEqual(
new Set(db.prepare("SELECT id FROM sessions WHERE source='pi'").all().map(row => row.id)),
new Set([piSessionId(firstHeader), piSessionId(secondHeader)]),
);
assert.equal(createQueryApi(db).search('project evidence').length, 2);
db.close();
});
test('identity migration retracts a legacy row attached to a non-selected identical copy', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-identity-migration-'));
const content = fixture('tool-session.jsonl');
const first = writeSession(content, { root, relativePath: 'a/session.jsonl' });
const second = writeSession(content, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const initialUnit = provider.discover({ lastCursor: () => null })[0];
const initial = drain(provider.parse(initialUnit, null));
const sourceHeader = JSON.parse(content.split('\n')[0]);
const legacyId = `pi:${sourceHeader.id}`;
const migration = provider.discover({
lastCursor: key => key === first.path ? initial.cursor : null,
indexedSessions: () => [{ sessionId: legacyId, jsonlPath: second.path }],
});
assert.equal(migration.length, 1);
assert.equal(migration[0].key, first.path);
assert.equal(migration[0].sessionId, piSessionId(sourceHeader));
assert.deepEqual(migration[0].retractSessionIds, [legacyId]);
assert.deepEqual(
drain(provider.parse(migration[0], null)).values
.filter(record => record.kind === 'delete-session')
.map(record => record.sessionId),
[legacyId, piSessionId(sourceHeader)],
);
});
test('unlinking a deduplicated Pi copy reparses the surviving source instead of deleting the session', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-copy-move-'));
const content = fixture('tool-session.jsonl');
const first = writeSession(content, { root, relativePath: 'a/session.jsonl' });
const second = writeSession(content, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const original = provider.discover({ lastCursor: () => null })[0];
const parsed = drain(provider.parse(original, null));
unlinkSync(first.path);
const survivors = provider.discover({
lastCursor: key => key === first.path ? parsed.cursor : null,
changedPaths: [first.path],
indexedSessions: () => [{ sessionId: original.sessionId, jsonlPath: first.path }],
});
assert.equal(survivors.length, 1);
assert.equal(survivors[0].key, second.path);
assert.deepEqual(survivors[0].retractSessionIds, undefined);
assert.equal(
drain(provider.parse(survivors[0], null)).values.find(record => record.kind === 'session').jsonl_path,
second.path,
);
});
test('replacing one Pi copy preserves the original identity when another copy survives', () => {
const root = mkdtempSync(join(tmpdir(), 'obelisk-pi-copy-replace-'));
const content = fixture('tool-session.jsonl');
const first = writeSession(content, { root, relativePath: 'a/session.jsonl' });
const second = writeSession(content, { root, relativePath: 'b/session.jsonl' });
const provider = createPiProvider({ rootDir: root });
const original = provider.discover({ lastCursor: () => null })[0];
drain(provider.parse(original, null));
writeFileSync(first.path, jsonl([
header({ id: 'replacement-copy' }),
userEntry('replacement-user', null, 'replacement'),
]));
const units = provider.discover({
lastCursor: () => null,
changedPaths: [first.path],
indexedSessions: () => [{ sessionId: original.sessionId, jsonlPath: first.path }],
});
assert.deepEqual(
units.map(unit => ({ path: unit.key, sessionId: unit.sessionId, retract: unit.retractSessionIds })),
[
{
path: first.path,
sessionId: piSessionId(header({ id: 'replacement-copy' })),
retract: undefined,
},
{ path: second.path, sessionId: original.sessionId, retract: undefined },
],
);
});
test('changed-path discovery bypasses cursors while an unchanged passive pull is skipped', () => {
const first = writeSession(fixture('tool-session.jsonl'));
const provider = createPiProvider({ rootDir: first.root });
const unit = provider.discover({ lastCursor: () => null })[0];
const { cursor } = drain(provider.parse(unit, null));
assert.deepEqual(provider.discover({ lastCursor: () => cursor }), []);
assert.deepEqual(
provider.discover({ lastCursor: () => cursor, changedPaths: [first.path] }).map(candidate => candidate.key),
[first.path],
);
});
test('Pi snapshot cursors detect same-identity and replacement writes that preserve mtime', () => {
const written = writeSession(jsonl([
header({ id: 'preserved-mtime' }),
userEntry('before', null, 'before'),
]));
const provider = createPiProvider({ rootDir: written.root });
const initialUnit = provider.discover({ lastCursor: () => null })[0];
const initial = drain(provider.parse(initialUnit, null));
const replaceAtSameMtime = (content) => {
const before = statSync(written.path);
const replacement = `${written.path}.replacement`;
writeFileSync(replacement, content);
utimesSync(replacement, before.atime, before.mtime);
renameSync(replacement, written.path);
utimesSync(written.path, before.atime, before.mtime);
};
replaceAtSameMtime(jsonl([
header({ id: 'preserved-mtime' }),
userEntry('after', null, 'after!'),
]));
const sameIdentity = provider.discover({
lastCursor: () => initial.cursor,
indexedSessions: () => [{ sessionId: initialUnit.sessionId, jsonlPath: written.path }],
});
assert.equal(sameIdentity.length, 1);
assert.equal(sameIdentity[0].sessionId, initialUnit.sessionId);
const updated = drain(provider.parse(sameIdentity[0], initial.cursor));
replaceAtSameMtime(jsonl([
header({ id: 'replacement-mtime' }),
userEntry('replacement', null, 'replacement'),
]));
const replacement = provider.discover({
lastCursor: () => updated.cursor,
indexedSessions: () => [{ sessionId: initialUnit.sessionId, jsonlPath: written.path }],
});
assert.equal(replacement.length, 1);
assert.equal(replacement[0].sessionId, piSessionId(header({ id: 'replacement-mtime' })));
assert.deepEqual(replacement[0].retractSessionIds, [initialUnit.sessionId]);
});
test('incomplete Pi inventories never infer deletion from changed paths', () => {
const parent = mkdtempSync(join(tmpdir(), 'obelisk-pi-incomplete-'));
const unreadableRoot = join(parent, 'sessions');
writeFileSync(unreadableRoot, 'not a directory');
const indexedPath = join(unreadableRoot, 'project', 'session.jsonl');
const provider = createPiProvider({ rootDir: unreadableRoot });
assert.deepEqual(provider.discover({
lastCursor: () => null,
changedPaths: [unreadableRoot],
indexedSessions: () => [{ sessionId: 'pi:preserve-me', jsonlPath: indexedPath }],
}), []);
});
test('a file disappearing during discovery marks the inventory incomplete without throwing', () => {
const written = writeSession(jsonl([
header({ id: 'discovery-race' }),
userEntry('message', null, 'vanishing source'),
]));
const provider = createPiProvider({ rootDir: written.root });
let incomplete = false;
const units = provider.discover({
lastCursor: () => null,
indexedSessions: () => {
unlinkSync(written.path);
return [];
},
reportIncompleteInventory: () => { incomplete = true; },
});
assert.deepEqual(units, []);
assert.equal(incomplete, true);
});
test('indexed provenance retracts a replaced identity and emits an unlink tombstone', () => {
const written = writeSession(jsonl([header({ id: 'before' }), userEntry('a', null, 'before')]));
const provider = createPiProvider({ rootDir: written.root });
const beforeUnit = provider.discover({ lastCursor: () => null })[0];
const before = drain(provider.parse(beforeUnit, null));
const beforeSession = before.values.find(record => record.kind === 'session').id;
writeFileSync(written.path, jsonl([header({ id: 'after' }), userEntry('b', null, 'after')]));
const afterUnit = provider.discover({
lastCursor: () => before.cursor,
changedPaths: [written.path],
indexedSessions: () => [{ sessionId: beforeSession, jsonlPath: written.path }],
})[0];
assert.deepEqual(afterUnit.retractSessionIds, [beforeSession]);
const after = drain(provider.parse(afterUnit, null));
const afterSession = after.values.find(record => record.kind === 'session').id;
assert.notEqual(afterSession, beforeSession);
assert.deepEqual(
after.values.filter(record => record.kind === 'delete-session').map(record => record.sessionId),
[beforeSession, afterSession],
);
unlinkSync(written.path);
const tombstones = provider.discover({
lastCursor: () => after.cursor,
changedPaths: [written.path],
indexedSessions: () => [{ sessionId: afterSession, jsonlPath: written.path }],
});
assert.equal(tombstones.length, 1);
assert.deepEqual(tombstones[0].retractSessionIds, [afterSession]);
const tombstone = drain(provider.parse(tombstones[0], after.cursor));
assert.deepEqual(tombstone.values, [{ kind: 'delete-session', sessionId: afterSession }]);
assert.equal(tombstone.cursor, '0:0');
});
test('session identity combines Pi project-local header id with stable cwd namespace', () => {
const source = JSON.parse(fixture('tool-session.jsonl').split('\n')[0]);
const migrated = {
...source,
version: source.version === 3 ? 2 : 3,
timestamp: '2030-01-01T00:00:00.000Z',
parentSession: '/tmp/parent.jsonl',
};
assert.equal(piSessionId(source), piSessionId(migrated));
assert.notEqual(piSessionId(source), piSessionId({ ...source, id: 'different-session' }));
assert.notEqual(piSessionId(source), piSessionId({ ...source, cwd: '/tmp/other-project' }));
});
test('long linear Pi trees validate without repeated prefix walks', () => {
const records = [header({ id: 'long-linear' })];
let parentId = null;
for (let index = 0; index < 5000; index++) {
const id = `entry-${index}`;
records.push(userEntry(id, parentId, `message ${index}`));
parentId = id;
}
const { values } = parseOnly(writeSession(jsonl(records)).root);
assert.equal(values.filter(record => record.kind === 'message').length, 5000);
});