Add session version field and migrate-on-load strategy

This commit is contained in:
Mario Zechner 2025-12-25 22:52:04 +01:00
parent 013b6814e4
commit 3239a4a11a

View file

@ -16,16 +16,20 @@ Context is built by scanning linearly, applying compaction ranges.
## Proposed Format (Tree)
Each entry has a `uuid` and `parentUuid` field (null for root):
Each entry has a `uuid` and `parentUuid` field (null for root). Session header includes `version` for future migrations:
```jsonl
{"type":"session","uuid":"a1b2c3","parentUuid":null,"id":"...","cwd":"..."}
{"type":"session","version":2,"uuid":"a1b2c3","parentUuid":null,"id":"...","cwd":"..."}
{"type":"message","uuid":"d4e5f6","parentUuid":"a1b2c3","message":{"role":"user",...}}
{"type":"message","uuid":"g7h8i9","parentUuid":"d4e5f6","message":{"role":"assistant",...}}
{"type":"message","uuid":"j0k1l2","parentUuid":"g7h8i9","message":{"role":"user",...}}
{"type":"message","uuid":"m3n4o5","parentUuid":"j0k1l2","message":{"role":"assistant",...}}
```
Version history:
- **v1** (implicit): Linear format, no uuid/parentUuid
- **v2**: Tree format with uuid/parentUuid
The **last entry** is always the current leaf. Context = walk from leaf to root via `parentUuid`.
Using UUIDs (like Claude Code does) instead of indices because:
@ -312,33 +316,75 @@ With tree structure, could support:
## Migration
### File Format
### Strategy: Migrate on Load + Rewrite
Add `uuid` and `parentUuid` fields to all entries. Existing sessions get generated UUIDs with linear parentage:
When loading a session, check if migration is needed. If so, migrate in memory and rewrite the file. This is transparent to users and only happens once per session file.
```typescript
function migrateSession(content: string): string {
const lines = content.trim().split('\n');
const uuids: string[] = [];
const CURRENT_VERSION = 2;
function loadSession(path: string): SessionEntry[] {
const content = readFileSync(path, 'utf8');
const entries = parseEntries(content);
return lines.map((line, i) => {
const entry = JSON.parse(line);
const uuid = generateUuid();
uuids.push(uuid);
entry.uuid = uuid;
entry.parentUuid = i === 0 ? null : uuids[i - 1];
return JSON.stringify(entry);
}).join('\n');
const header = entries.find(e => e.type === 'session');
const version = header?.version ?? 1;
if (version < CURRENT_VERSION) {
migrateEntries(entries, version);
writeFileSync(path, entries.map(e => JSON.stringify(e)).join('\n') + '\n');
}
return entries;
}
function migrateEntries(entries: SessionEntry[], fromVersion: number): void {
if (fromVersion < 2) {
// v1 → v2: Add uuid/parentUuid, convert firstKeptEntryIndex
const uuids: string[] = [];
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
const uuid = generateUuid();
uuids.push(uuid);
entry.uuid = uuid;
entry.parentUuid = i === 0 ? null : uuids[i - 1];
// Update session header version
if (entry.type === 'session') {
entry.version = CURRENT_VERSION;
}
// Convert compaction index to UUID
if (entry.type === 'compaction' && 'firstKeptEntryIndex' in entry) {
entry.firstKeptEntryUuid = uuids[entry.firstKeptEntryIndex];
delete entry.firstKeptEntryIndex;
}
}
}
// Future migrations: if (fromVersion < 3) { ... }
}
```
Migrated sessions work exactly as before (linear path).
### What Gets Migrated
| v1 Field | v2 Field |
|----------|----------|
| (none) | `uuid` (generated) |
| (none) | `parentUuid` (previous entry's uuid, null for root) |
| (none on session) | `version: 2` |
| `firstKeptEntryIndex` | `firstKeptEntryUuid` |
Migrated sessions work exactly as before (linear path). Tree features become available.
### API Compatibility
- `buildSessionContext()` returns same structure
- `branch()` still works, just uses UUIDs
- Existing hooks continue to work
- Old sessions auto-migrate on first load
## Complexity Analysis
@ -361,7 +407,7 @@ Abandoned branches remain in file but don't affect context building performance.
## Example: Full Session with Branching
```jsonl
{"type":"session","uuid":"ses1","parentUuid":null,"id":"abc","cwd":"/project"}
{"type":"session","version":2,"uuid":"ses1","parentUuid":null,"id":"abc","cwd":"/project"}
{"type":"message","uuid":"m1","parentUuid":"ses1","message":{"role":"user","content":"Build a CLI"}}
{"type":"message","uuid":"m2","parentUuid":"m1","message":{"role":"assistant","content":"I'll create..."}}
{"type":"message","uuid":"m3","parentUuid":"m2","message":{"role":"user","content":"Add --verbose flag"}}