| 1 | # Session Catalog and Desktop Startup |
| 2 | |
| 3 | Reasonix keeps session transcripts, event logs, metadata sidecars, and |
| 4 | `desktop-projects.json` as the only authoritative session data. The desktop |
| 5 | project tree reads a disposable SQLite projection from |
| 6 | `<cache root>/session-catalog/v8.sqlite`; deleting that database never deletes |
| 7 | or changes a conversation. The earlier `v1.sqlite` through `v5.sqlite` caches |
| 8 | are left in place so a concurrent or downgraded process cannot cross-write the |
| 9 | projection. v6 introduces filesystem-aware path identity and is rebuilt from |
| 10 | authoritative files on first use while the old v5 file remains available for |
| 11 | rollback. A manual rebuild of v6 also leaves a timestamped `.replaced-*` copy |
| 12 | of the previous index. |
| 13 | |
| 14 | ## Invariants |
| 15 | |
| 16 | - Startup and project-tree requests never decode transcript JSONL, run legacy |
| 17 | migration, or wait for a directory scan. |
| 18 | - A successfully saved transcript is committed before its catalog update. The |
| 19 | save observer performs only lexical queue staging and returns without |
| 20 | filesystem probes. Background workers resolve filesystem identity, SQLite |
| 21 | uniqueness is the final deduplication boundary, and reconciliation repairs |
| 22 | updates dropped under queue pressure. |
| 23 | - Original session and workspace-root spellings are retained for file access |
| 24 | and display. Separate identity keys resolve aliases and fold case only where |
| 25 | the governing filesystem directory is case-insensitive; case-distinct files |
| 26 | and projects on case-sensitive volumes remain separate. |
| 27 | - Missing legacy counts are represented as `unknown`. The session is visible |
| 28 | immediately, then a single repair worker decodes it in the background. |
| 29 | - A stale projection (a save interrupted before its listing stamp) is also |
| 30 | `unknown`, but it keeps its last-known preview and turn count as uncertified |
| 31 | hints so the row stays in the sidebar while repair recomputes it. |
| 32 | - A missing file is marked degraded on the first scan. It is removed from the |
| 33 | projection only after a second scan and the missing-file grace period. |
| 34 | - Runtime state (`open`, `running`, and live status) comes only from in-memory |
| 35 | controllers and overlays catalog results. It is never persisted to SQLite. |
| 36 | - Catalog, migration, plugin, and MCP work is cancellable and never participates |
| 37 | in the desktop shutdown lock. Shutdown gives pending catalog writes at most |
| 38 | 250 ms. |
| 39 | - A ready directory is reused only when its authoritative session path set, |
| 40 | scope/workspace assignment, topic projection, and recovery-derived fields |
| 41 | match the current files. Equal row counts are not sufficient. |
| 42 | |
| 43 | ## Storage and migration |
| 44 | |
| 45 | `internal/sessioncatalog` uses a version ledger in `schema_migrations`; database |
| 46 | existence is not a migration signal. Local cache files use WAL, |
| 47 | `synchronous=NORMAL`, and a short busy timeout. An unavailable or obviously |
| 48 | remote cache path falls back to an in-memory catalog so storage failures cannot |
| 49 | block the application. |
| 50 | |
| 51 | At open, Reasonix runs an integrity check. A corrupt or unmigratable database is |
| 52 | renamed with a `.corrupt-<timestamp>` suffix and replaced. The replacement is |
| 53 | rebuilt from sidecars and transcripts in the background. The quarantine and |
| 54 | rebuild paths never remove authoritative files. |
| 55 | |
| 56 | The catalog stores only query projections: |
| 57 | |
| 58 | - directory signatures, scan generations, checkpoints, and errors; |
| 59 | - project ordering, title, color, pin state, and workspace-root identity key; |
| 60 | - topic ordering, aggregate counts, activity, recovery, health state, and |
| 61 | workspace-root identity key; and |
| 62 | - session access path plus path, directory, and workspace-root identity keys, |
| 63 | preview, counts, fingerprints, recovery, and health state; and |
| 64 | - for schema-2 event logs, the log format, the selected head, and one |
| 65 | `catalog_heads` row per head, taken from the kernel's head index sidecar and |
| 66 | the `BranchMeta` mirror rather than from replaying the log. |
| 67 | |
| 68 | Topic pages use a `(pinned, last_activity_at, topic_id)` keyset cursor. The |
| 69 | default page size is 50 and the maximum is 200. Directory reconciliation commits |
| 70 | at most 64 sidecars per batch and persists its checkpoint before yielding. |
| 71 | |
| 72 | ## Desktop API |
| 73 | |
| 74 | - `GetProjectTreeSnapshot` returns project shells, catalog state, progress, and |
| 75 | revision without opening a session or sidecar file. |
| 76 | - `ListProjectTopics` performs cursor-paged search and time filtering. |
| 77 | - `GetTopicSummary` resolves one topic for active-turn UI without rebuilding the |
| 78 | tree. |
| 79 | - `GetSessionCatalogStatus` and `RebuildSessionCatalog` expose safe diagnostics |
| 80 | and replacement. |
| 81 | - `project-tree:changed-v2` carries a monotonic revision, affected workspace |
| 82 | roots, and reason. Clients ignore older revisions and refresh only expanded |
| 83 | affected roots. |
| 84 | |
| 85 | `ListProjectTree` remains as a compatibility wrapper over the catalog. It no |
| 86 | longer has a synchronous filesystem fallback. |
| 87 | |
| 88 | ## Operations |
| 89 | |
| 90 | Inspect the catalog without creating or changing it: |
| 91 | |
| 92 | ```sh |
| 93 | reasonix sessions diagnose |
| 94 | reasonix sessions diagnose --json |
| 95 | ``` |
| 96 | |
| 97 | Replace only the disposable projection and index all saved desktop projects: |
| 98 | |
| 99 | ```sh |
| 100 | reasonix sessions reindex |
| 101 | reasonix sessions reindex --json |
| 102 | ``` |
| 103 | |
| 104 | Use repeated `--dir PATH` flags to rebuild from an explicit set of directories. |
| 105 | Explicit directories are treated as global scope. Reindexing never edits or |
| 106 | deletes transcript, event, metadata, recovery, archive, or project files; the |
| 107 | previous index is retained for rollback. |
| 108 | |
| 109 | ## Plugin isolation |
| 110 | |
| 111 | Manifest validation and plugin handshakes are independent from catalog and |
| 112 | project-tree work. An incompatible plugin is reported as |
| 113 | `disabled_incompatible`; the core controller remains usable. A legacy manifest |
| 114 | under Reasonix's managed plugin directory is atomically upgraded with a backup. |
| 115 | Development directories, absolute external roots, and symlinked sources are |
| 116 | never rewritten automatically and include a manual migration hint instead. |
| 117 | |
| 118 | ## Release gates |
| 119 | |
| 120 | Preview/canary promotion should track catalog repair backlog, rebuild failures, |
| 121 | page latency, queue pressure, and shutdown duration. Required checks include |
| 122 | legacy/ corrupt fixtures, deterministic lifecycle races, `go test -race`, the |
| 123 | React contract tests, and `CGO_ENABLED=0` builds for supported macOS, Windows, |
| 124 | and Linux architectures. |
| 125 |