Files

651 lines
23 KiB
JavaScript
Raw Permalink Normal View History

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import {
chmodSync,
mkdirSync,
mkdtempSync,
readFileSync,
readdirSync,
renameSync,
unlinkSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { buildIndex } from '../app/src/main/indexer.ts';
import {
createPiProvider,
piSessionId,
PI_CANONICAL_TRANSCRIPT_MARKER,
} from '../packages/core/src/providers/pi.ts';
import { createProviderRegistry } from '../packages/core/src/providers/registry.ts';
const require = createRequire(import.meta.url);
const { DatabaseSync } = require('node:sqlite');
const TOOL_FIXTURE = new URL('./fixtures/pi/tool-session.jsonl', import.meta.url);
class TestDatabase {
constructor(dbPath) {
this.db = new DatabaseSync(dbPath);
}
pragma(statement) { this.db.exec(`PRAGMA ${statement}`); }
exec(sql) { return this.db.exec(sql); }
prepare(sql) { return this.db.prepare(sql); }
close() { return this.db.close(); }
}
class TransactionAwareTestDatabase extends TestDatabase {
get inTransaction() { return this.db.isTransaction; }
}
function writeFixture(piDir) {
const sessionPath = join(piDir, '--tmp-pi-real-tool--', 'session.jsonl');
mkdirSync(dirname(sessionPath), { recursive: true });
writeFileSync(sessionPath, readFileSync(TOOL_FIXTURE));
return sessionPath;
}
function invalidMessageLine(id = 'invalid-message') {
return JSON.stringify({
type: 'message',
id,
parentId: null,
timestamp: '2026-08-02T10:00:01.000Z',
message: null,
});
}
function indexOptions(home, piDir) {
return {
claudeDir: join(home, '.claude'),
codexDir: join(home, '.codex'),
providerRoots: { pi: piDir },
dbPath: join(home, '.obelisk', 'obelisk.sqlite'),
DatabaseImpl: TestDatabase,
};
}
test('app build indexes Pi through the registry and replays complete session snapshots', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-index-'));
const piDir = join(home, 'pi-sessions');
const sessionPath = writeFixture(piDir);
const options = indexOptions(home, piDir);
const provider = createPiProvider({ rootDir: piDir });
const sessionId = provider.discover({ lastCursor: () => null })[0].sessionId;
const first = buildIndex(options);
assert.deepEqual(first.affectedSessionIds, [sessionId]);
let db = new TestDatabase(options.dbPath);
assert.deepEqual(
db.prepare('SELECT id,title,source,message_count FROM sessions').all().map(row => ({ ...row })),
[{ id: sessionId, title: 'Tool probe', source: 'pi', message_count: 4 }],
);
assert.equal(
db.prepare("SELECT text FROM messages WHERE source='pi' AND role='assistant' AND content_type='text'").get().text,
'The read tool returned real-pi-tool-result.',
);
assert.deepEqual(
{ ...db.prepare("SELECT name,file_path FROM tool_calls WHERE session_id=?").get(sessionId) },
{ name: 'read', file_path: 'probe.txt' },
);
const schema = db.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='messages'").get().sql;
assert.doesNotMatch(schema, /\bpi\b/i);
db.close();
const header = readFileSync(TOOL_FIXTURE, 'utf8').split('\n')[0];
writeFileSync(sessionPath, `${header}\n`);
const replay = buildIndex({ ...options, changedPaths: [sessionPath] });
assert.deepEqual(replay.affectedSessionIds, [sessionId]);
db = new TestDatabase(options.dbPath);
assert.equal(db.prepare("SELECT COUNT(*) AS c FROM messages WHERE source='pi'").get().c, 0);
assert.equal(db.prepare('SELECT message_count FROM sessions WHERE id=?').get(sessionId).message_count, 0);
db.close();
});
test('an unreadable Pi directory does not block readable sessions on a fresh index', {
skip: process.platform === 'win32',
}, (t) => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-partial-inventory-'));
const piDir = join(home, 'pi-sessions');
writeFixture(piDir);
const lockedDir = join(piDir, 'locked');
mkdirSync(lockedDir, { recursive: true });
chmodSync(lockedDir, 0o000);
try {
try {
readdirSync(lockedDir);
t.skip('current user can read mode-000 directories');
return;
} catch {}
const options = indexOptions(home, piDir);
const first = buildIndex(options);
assert.equal(first.complete, false);
assert.equal(first.files, 1);
assert.deepEqual(first.incompleteProviders, ['pi']);
assert.ok(first.inventoryIssues.some((issue) => (
issue.provider === 'pi' && issue.path === lockedDir
)));
const db = new TestDatabase(options.dbPath);
assert.equal(db.prepare("SELECT COUNT(*) AS c FROM sessions WHERE source='pi'").get().c, 1);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(PI_CANONICAL_TRANSCRIPT_MARKER).c,
0,
);
db.close();
} finally {
chmodSync(lockedDir, 0o700);
}
const recovered = buildIndex(indexOptions(home, piDir));
assert.equal(recovered.complete, true);
const db = new TestDatabase(join(home, '.obelisk', 'obelisk.sqlite'));
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(PI_CANONICAL_TRANSCRIPT_MARKER).c,
1,
);
db.close();
});
test('an incomplete Pi identity census preserves committed provenance over a readable copy', {
skip: process.platform === 'win32',
}, (t) => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-partial-copy-'));
const piDir = join(home, 'pi-sessions');
const lockedDir = join(piDir, 'locked');
const committedPath = join(lockedDir, 'session.jsonl');
mkdirSync(lockedDir, { recursive: true });
writeFileSync(committedPath, readFileSync(TOOL_FIXTURE));
const options = indexOptions(home, piDir);
assert.equal(buildIndex(options).complete, true);
let db = new TestDatabase(options.dbPath);
const before = {
session: {
...db.prepare("SELECT id,title,message_count,jsonl_path FROM sessions WHERE source='pi'").get(),
},
messages: db.prepare(
"SELECT uuid,text,visibility FROM messages WHERE source='pi' ORDER BY uuid",
).all().map(row => ({ ...row })),
};
db.close();
const readablePath = join(piDir, 'readable', 'session.jsonl');
mkdirSync(dirname(readablePath), { recursive: true });
writeFileSync(
readablePath,
readFileSync(TOOL_FIXTURE, 'utf8').replaceAll(
'real-pi-tool-result',
'UNVERIFIED NEW',
),
);
chmodSync(lockedDir, 0o000);
try {
try {
readdirSync(lockedDir);
t.skip('current user can read mode-000 directories');
return;
} catch {}
const partial = buildIndex(options);
assert.equal(partial.complete, false);
assert.equal(partial.files, 0);
assert.deepEqual(partial.affectedSessionIds, []);
assert.ok(partial.inventoryIssues.some((issue) => (
issue.provider === 'pi' && issue.path === lockedDir
)));
db = new TestDatabase(options.dbPath);
const after = {
session: {
...db.prepare("SELECT id,title,message_count,jsonl_path FROM sessions WHERE source='pi'").get(),
},
messages: db.prepare(
"SELECT uuid,text,visibility FROM messages WHERE source='pi' ORDER BY uuid",
).all().map(row => ({ ...row })),
};
assert.deepEqual(after, before);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(readablePath).c,
0,
);
db.close();
} finally {
chmodSync(lockedDir, 0o700);
}
});
test('Pi canonical marker forces one provider-owned replay after projection changes', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-marker-'));
const piDir = join(home, 'pi-sessions');
writeFixture(piDir);
const options = indexOptions(home, piDir);
const sessionId = createPiProvider({ rootDir: piDir })
.discover({ lastCursor: () => null })[0].sessionId;
buildIndex(options);
let db = new TestDatabase(options.dbPath);
db.prepare("UPDATE messages SET text='stale Pi projection' WHERE source='pi'").run();
db.prepare('DELETE FROM index_state WHERE jsonl_path=?').run(PI_CANONICAL_TRANSCRIPT_MARKER);
db.close();
const replay = buildIndex(options);
assert.deepEqual(replay.affectedSessionIds, [sessionId]);
db = new TestDatabase(options.dbPath);
assert.equal(
db.prepare("SELECT COUNT(*) AS c FROM messages WHERE source='pi' AND text='stale Pi projection'").get().c,
0,
);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(PI_CANONICAL_TRANSCRIPT_MARKER).c,
1,
);
db.close();
});
test('a structurally invalid Pi file retries alone after a canonical replay', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-marker-retry-'));
const piDir = join(home, 'pi-sessions');
const validPath = writeFixture(piDir);
const options = {
...indexOptions(home, piDir),
DatabaseImpl: TransactionAwareTestDatabase,
};
const provider = createPiProvider({ rootDir: piDir });
const parseCalls = new Map();
const countedProvider = {
...provider,
parse(unit, cursor) {
parseCalls.set(unit.key, (parseCalls.get(unit.key) ?? 0) + 1);
return provider.parse(unit, cursor);
},
};
const providerRegistry = createProviderRegistry([countedProvider]);
buildIndex({ ...options, providerRegistry });
const badPath = join(piDir, '--tmp-pi-bad--', 'session.jsonl');
mkdirSync(dirname(badPath), { recursive: true });
writeFileSync(badPath, [
JSON.stringify({
type: 'session',
version: 3,
id: 'permanently-bad',
timestamp: '2026-08-02T10:00:00.000Z',
cwd: '/tmp/pi-bad',
}),
invalidMessageLine('permanently-bad-message'),
'',
].join('\n'));
let db = new TransactionAwareTestDatabase(options.dbPath);
db.prepare('DELETE FROM index_state WHERE jsonl_path=?').run(PI_CANONICAL_TRANSCRIPT_MARKER);
db.close();
parseCalls.clear();
const replay = buildIndex({ ...options, providerRegistry });
assert.equal(replay.files, 2);
assert.equal(replay.skipped, 1);
assert.equal(parseCalls.get(validPath), 1);
assert.equal(parseCalls.get(badPath), 1);
db = new TransactionAwareTestDatabase(options.dbPath);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(PI_CANONICAL_TRANSCRIPT_MARKER).c,
1,
);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?').get(badPath).c,
0,
);
db.close();
const retry = buildIndex({ ...options, providerRegistry });
assert.equal(retry.files, 1);
assert.equal(retry.skipped, 1);
assert.equal(parseCalls.get(validPath), 1, 'the successful session must not replay again');
assert.equal(parseCalls.get(badPath), 2, 'only the failed session remains retryable');
});
test('a failed Pi unit rolls back a force rebuild to the last complete snapshot', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-force-rollback-'));
const piDir = join(home, 'pi-sessions');
const sessionPath = writeFixture(piDir);
const options = {
...indexOptions(home, piDir),
DatabaseImpl: TransactionAwareTestDatabase,
};
assert.equal(buildIndex(options).complete, true);
let db = new TransactionAwareTestDatabase(options.dbPath);
const before = {
session: { ...db.prepare("SELECT id,title,message_count,jsonl_path FROM sessions WHERE source='pi'").get() },
messages: db.prepare("SELECT uuid,text,content_type FROM messages WHERE source='pi' ORDER BY uuid").all()
.map(row => ({ ...row })),
cursor: { ...db.prepare('SELECT mtime,lines_processed,cursor FROM index_state WHERE jsonl_path=?').get(sessionPath) },
lastBuild: { ...db.prepare("SELECT mtime,lines_processed FROM index_state WHERE jsonl_path='__last_build__'").get() },
};
db.close();
const fixtureHeader = readFileSync(TOOL_FIXTURE, 'utf8').split('\n')[0];
writeFileSync(sessionPath, `${fixtureHeader}\n${invalidMessageLine()}\n`);
const failed = buildIndex({ ...options, force: true });
assert.equal(failed.complete, false);
assert.equal(failed.reason, 'provider_failure');
assert.deepEqual(failed.affectedSessionIds, []);
assert.equal(failed.skippedFiles[0].provider, 'pi');
assert.equal(failed.skippedFiles[0].path, sessionPath);
assert.match(failed.skippedFiles[0].error, /Malformed Pi message at line 2/);
db = new TransactionAwareTestDatabase(options.dbPath);
const after = {
session: { ...db.prepare("SELECT id,title,message_count,jsonl_path FROM sessions WHERE source='pi'").get() },
messages: db.prepare("SELECT uuid,text,content_type FROM messages WHERE source='pi' ORDER BY uuid").all()
.map(row => ({ ...row })),
cursor: { ...db.prepare('SELECT mtime,lines_processed,cursor FROM index_state WHERE jsonl_path=?').get(sessionPath) },
lastBuild: { ...db.prepare("SELECT mtime,lines_processed FROM index_state WHERE jsonl_path='__last_build__'").get() },
};
db.close();
assert.deepEqual(after, before);
});
test('a temp force rebuild uses live Pi provenance before publishing an empty snapshot', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-temp-provenance-'));
const piDir = join(home, 'pi-sessions');
writeFixture(piDir);
const options = indexOptions(home, piDir);
assert.equal(buildIndex(options).complete, true);
const unavailableDir = `${piDir}.unavailable`;
renameSync(piDir, unavailableDir);
const tempDbPath = join(home, '.obelisk', 'obelisk.rebuild.tmp');
const rebuilt = buildIndex({
...options,
dbPath: tempDbPath,
preserveDbPath: options.dbPath,
force: true,
});
assert.equal(rebuilt.complete, false);
assert.equal(rebuilt.reason, 'incomplete_snapshot');
assert.deepEqual(rebuilt.incompleteProviders, ['pi']);
const live = new TestDatabase(options.dbPath);
assert.equal(live.prepare("SELECT COUNT(*) AS c FROM sessions WHERE source='pi'").get().c, 1);
live.close();
renameSync(unavailableDir, piDir);
});
test('an unavailable Pi root keeps replay pending on the missing session cursor', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-incomplete-marker-'));
const piDir = join(home, 'pi-sessions');
const sessionPath = writeFixture(piDir);
const options = indexOptions(home, piDir);
buildIndex(options);
let db = new TestDatabase(options.dbPath);
db.prepare(`
UPDATE messages
SET text='stale Pi projection'
WHERE uuid = (
SELECT uuid FROM messages WHERE source='pi' ORDER BY uuid LIMIT 1
)
`).run();
db.prepare('DELETE FROM index_state WHERE jsonl_path=?').run(PI_CANONICAL_TRANSCRIPT_MARKER);
db.close();
const unavailableDir = `${piDir}.unavailable`;
renameSync(piDir, unavailableDir);
const forced = buildIndex({ ...options, force: true });
assert.equal(forced.complete, false);
assert.equal(forced.reason, 'incomplete_snapshot');
assert.deepEqual(forced.incompleteProviders, ['pi']);
const unavailable = buildIndex(options);
assert.equal(unavailable.files, 0);
db = new TestDatabase(options.dbPath);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(PI_CANONICAL_TRANSCRIPT_MARKER).c,
1,
);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(sessionPath).c,
0,
);
assert.equal(
db.prepare("SELECT COUNT(*) AS c FROM messages WHERE text='stale Pi projection'").get().c,
1,
);
db.close();
renameSync(unavailableDir, piDir);
const replay = buildIndex(options);
assert.equal(replay.files, 1);
db = new TestDatabase(options.dbPath);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(PI_CANONICAL_TRANSCRIPT_MARKER).c,
1,
);
assert.equal(
db.prepare("SELECT COUNT(*) AS c FROM messages WHERE text='stale Pi projection'").get().c,
0,
);
db.close();
});
test('an unresolved Pi root keeps replay pending on the unresolved session cursor', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-unresolved-marker-'));
const piDir = join(home, 'pi-sessions');
const sessionPath = writeFixture(piDir);
const options = indexOptions(home, piDir);
buildIndex(options);
let db = new TestDatabase(options.dbPath);
db.prepare("UPDATE messages SET text='stale unresolved projection' WHERE source='pi'").run();
db.prepare('DELETE FROM index_state WHERE jsonl_path=?').run(PI_CANONICAL_TRANSCRIPT_MARKER);
db.close();
const unresolvedProvider = createPiProvider({ rootDir: 'relative-session-root' });
assert.equal(unresolvedProvider.rootResolution.requiresExplicitRoot, true);
const unresolved = buildIndex({
...options,
providerRegistry: createProviderRegistry([unresolvedProvider]),
});
assert.equal(unresolved.files, 0);
db = new TestDatabase(options.dbPath);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(PI_CANONICAL_TRANSCRIPT_MARKER).c,
1,
);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(sessionPath).c,
0,
);
assert.ok(
db.prepare("SELECT COUNT(*) AS c FROM messages WHERE text='stale unresolved projection'").get().c > 0,
);
db.close();
const replay = buildIndex(options);
assert.equal(replay.files, 1);
db = new TestDatabase(options.dbPath);
assert.equal(
db.prepare('SELECT COUNT(*) AS c FROM index_state WHERE jsonl_path=?')
.get(PI_CANONICAL_TRANSCRIPT_MARKER).c,
1,
);
assert.equal(
db.prepare("SELECT COUNT(*) AS c FROM messages WHERE text='stale unresolved projection'").get().c,
0,
);
db.close();
});
test('Pi identity marker retracts a legacy id through a non-selected identical copy', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-identity-marker-'));
const piDir = join(home, 'pi-sessions');
const selectedPath = writeFixture(piDir);
const copiedPath = join(piDir, 'z-copy', 'session.jsonl');
mkdirSync(dirname(copiedPath), { recursive: true });
writeFileSync(copiedPath, readFileSync(TOOL_FIXTURE));
const options = indexOptions(home, piDir);
const sourceHeader = JSON.parse(readFileSync(TOOL_FIXTURE, 'utf8').split('\n')[0]);
const legacyId = `pi:${sourceHeader.id}`;
const currentId = piSessionId(sourceHeader);
buildIndex(options);
let db = new TestDatabase(options.dbPath);
db.prepare("UPDATE sessions SET id=?, jsonl_path=? WHERE source='pi'").run(legacyId, copiedPath);
for (const table of ['messages', 'tool_calls', 'tool_results', 'summaries']) {
db.prepare(`UPDATE ${table} SET session_id=? WHERE session_id=?`).run(legacyId, currentId);
}
db.prepare('UPDATE index_state SET jsonl_path=? WHERE jsonl_path=?').run(copiedPath, selectedPath);
db.prepare('DELETE FROM index_state WHERE jsonl_path=?').run(PI_CANONICAL_TRANSCRIPT_MARKER);
db.close();
const replay = buildIndex(options);
assert.deepEqual(new Set(replay.affectedSessionIds), new Set([legacyId, currentId]));
db = new TestDatabase(options.dbPath);
assert.deepEqual(
db.prepare("SELECT id,jsonl_path FROM sessions WHERE source='pi'").all().map(row => ({ ...row })),
[{ id: currentId, jsonl_path: selectedPath }],
);
db.close();
});
test('app replay keeps Pi identity stable across migration and retracts replacement and unlink', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-provenance-'));
const piDir = join(home, 'pi-sessions');
const sessionPath = writeFixture(piDir);
const options = indexOptions(home, piDir);
const original = readFileSync(sessionPath, 'utf8');
const records = original.trimEnd().split('\n').map(line => JSON.parse(line));
buildIndex(options);
const stableId = piSessionId(records[0]);
records[0].version = 2;
writeFileSync(sessionPath, records.map(record => JSON.stringify(record)).join('\n') + '\n');
buildIndex({ ...options, changedPaths: [sessionPath] });
let db = new TestDatabase(options.dbPath);
assert.deepEqual(
db.prepare("SELECT id FROM sessions WHERE source='pi'").all().map(row => row.id),
[stableId],
);
db.close();
records[0].version = 3;
records[0].id = 'replacement-session';
const replacementId = piSessionId(records[0]);
writeFileSync(sessionPath, records.map(record => JSON.stringify(record)).join('\n') + '\n');
const replacement = buildIndex({ ...options, changedPaths: [sessionPath] });
assert.deepEqual(
new Set(replacement.affectedSessionIds),
new Set([stableId, replacementId]),
);
db = new TestDatabase(options.dbPath);
assert.deepEqual(
db.prepare("SELECT id FROM sessions WHERE source='pi'").all().map(row => row.id),
[replacementId],
);
db.close();
unlinkSync(sessionPath);
const removed = buildIndex({ ...options, changedPaths: [sessionPath] });
assert.deepEqual(removed.affectedSessionIds, [replacementId]);
db = new TestDatabase(options.dbPath);
assert.equal(db.prepare("SELECT COUNT(*) AS c FROM sessions WHERE source='pi'").get().c, 0);
assert.equal(
db.prepare('SELECT mtime FROM index_state WHERE jsonl_path=?').get(sessionPath).mtime,
0,
);
db.close();
});
test('passive Pi inventory retracts deleted sessions when its configured root remains readable', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-passive-delete-'));
const piDir = join(home, 'pi-sessions');
const sessionPath = writeFixture(piDir);
const options = indexOptions(home, piDir);
buildIndex(options);
unlinkSync(sessionPath);
const removed = buildIndex(options);
assert.equal(removed.files, 1);
const db = new TestDatabase(options.dbPath);
assert.equal(db.prepare("SELECT COUNT(*) AS c FROM sessions WHERE source='pi'").get().c, 0);
db.close();
});
test('a terminal malformed line follows Pi by publishing the valid replacement prefix', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-pi-torn-replacement-'));
const piDir = join(home, 'pi-sessions');
const sessionPath = writeFixture(piDir);
const options = indexOptions(home, piDir);
const records = readFileSync(TOOL_FIXTURE, 'utf8')
.trimEnd()
.split('\n')
.map(line => JSON.parse(line));
const oldId = piSessionId(records[0]);
buildIndex(options);
let db = new TestDatabase(options.dbPath);
assert.equal(
db.prepare("SELECT id FROM sessions WHERE source='pi'").get().id,
oldId,
);
const committedCursor = db.prepare(
'SELECT cursor FROM index_state WHERE jsonl_path=?',
).get(sessionPath).cursor;
db.close();
const replacementHeader = { ...records[0], id: 'torn-replacement' };
const replacementId = piSessionId(replacementHeader);
writeFileSync(sessionPath, `${JSON.stringify(replacementHeader)}\n{"type":"message"`);
const prefix = buildIndex({ ...options, changedPaths: [sessionPath] });
assert.deepEqual(
new Set(prefix.affectedSessionIds),
new Set([oldId, replacementId]),
);
db = new TestDatabase(options.dbPath);
assert.deepEqual(
db.prepare("SELECT id,message_count FROM sessions WHERE source='pi'").all().map(row => ({ ...row })),
[{ id: replacementId, message_count: 0 }],
);
assert.notEqual(
db.prepare('SELECT cursor FROM index_state WHERE jsonl_path=?').get(sessionPath).cursor,
committedCursor,
);
db.close();
records[0] = replacementHeader;
writeFileSync(sessionPath, `${records.map(record => JSON.stringify(record)).join('\n')}\n`);
const completed = buildIndex({ ...options, changedPaths: [sessionPath] });
assert.deepEqual(completed.affectedSessionIds, [replacementId]);
db = new TestDatabase(options.dbPath);
assert.deepEqual(
db.prepare("SELECT id,message_count FROM sessions WHERE source='pi'").all().map(row => ({ ...row })),
[{ id: replacementId, message_count: 4 }],
);
db.close();
});