Conversation
Extract the slug field from Claude Code JSONL records and store it per-session in the database. Sessions sharing the same (project, slug) are grouped in the sidebar as a single row with a continuation badge (e.g., "x3"), and clicking selects the most recent session. Changes across the stack: - Parser: extract first non-empty slug from any JSONL record - Database: add slug column with project+slug index, wire through all Session column lists and UpsertSession - Sync: pass parsed slug through to database - Frontend: add SessionGroup type with grouping logic, update SessionList virtual list to iterate groups, add continuation badge to SessionItem Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Select primary session by ended_at (most recent) instead of started_at, consistent with sidebar sort order - Mark sidebar row active when activeSessionId matches any session in the group, not just the primary - Handle slug index creation error instead of silently ignoring it Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Primary session in a group is now selected using ended_at ?? started_at ?? created_at, matching the backend's COALESCE ordering. Handles null ended_at (in-progress sessions) and equal ended_at ties deterministically. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The virtualizer preserved the previous session's scroll offset via initialOffset and postUpdate. When measureCacheKey changes (session switch), reset initialOffset to 0 and set scrollTop = 0 on the DOM element instead of restoring the stale position. Also use COALESCE-style recency key (ended_at ?? started_at ?? created_at) for primary session selection, matching backend ordering. Added tests for null ended_at, null both, and equal ended_at ties. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The slug-based grouping didn't work because original sessions have slug: null -- only continuations carry a slug, so the original session was never grouped with its continuations. Replace with sessionId-based chaining: extract sessionId from the first user/assistant record in Claude JSONL files. If sessionId differs from the file's own UUID, store it as parent_session_id. The frontend walks parent_session_id chains to find the root and groups all sessions sharing the same root. Migration clears file_hash for existing Claude sessions missing parent_session_id so they get re-parsed on next sync. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The hash-clearing migration ran on every startup because original sessions always have parent_session_id IS NULL, forcing a full re-parse of all Claude sessions each time. Remove the migration entirely -- normal sync populates parent_session_id for fresh databases. Existing users should recreate their database. Also exclude sessions that have children from prune candidates to avoid breaking continuity chains. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Detect missing parent_session_id column on startup and delete the database file so it gets recreated from scratch. Session data is re-synced from source files on the next sync cycle. Move parent_session_id and its index into schema.sql and remove the incremental ensureColumn migrations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
roborev: Combined Review (
|
The test was creating an old schema and racing two concurrent Opens. With the drop-and-rebuild approach, both callers race to delete the file, causing file-not-found errors. Update the test to use a current schema since the interesting concurrent behavior is now in init(), not incremental migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
needsRebuild now returns false on any transient error (can't open, query failure) instead of treating it as a schema mismatch. Only deletes the database when the schema check positively confirms the parent_session_id column is missing. dropDatabase now returns an error if file removal fails (ignoring ENOENT), failing fast instead of proceeding with inconsistent state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
roborev: Combined Review (
|
roborev: Combined Review (
|
Summary
parent_session_idchaining, extracted from Claude JSONLsessionIdfieldsparent_session_idchains to find root sessions and groups all sessions sharing the same rootparent_session_idcolumn) are dropped and rebuilt from scratch on startupHow it works
Claude JSONL files have a
sessionIdfield on user/assistant records. For original sessions,sessionIdmatches the file's UUID. For continuations, it carries the parent file's UUID, forming a linked list (A -> B -> C). The parser extracts this and stores it asparent_session_id. The frontend walks the chain to find the root and groups all sessions sharing the same root.Test plan
go vet ./... && go test -tags fts5 ./...-- all passcd frontend && npx vitest run-- all 318 tests pass