feat(core): project identity for grouping sessions across sources#122
Merged
feat(core): project identity for grouping sessions across sources#122
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace placeholder identity arg in syncFile with real computeIdentity() call backed by a new realFs adapter (existsSync/readFileSync/spawnSync). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This was referenced Apr 30, 2026
graydawnc
added a commit
that referenced
this pull request
Apr 30, 2026
* docs(design): rewrite for library-first shell Realigns DESIGN.md with the shipped library-first product (PRs #122–#133). Reverses the 2026-03-27 "search box is the product" framing now that the Sidebar + Project View + ⌘K overlay are the home, and adds a decisions-log row that references the reversal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(design): clean up two stale references missed in first pass Spacing's "Search bar padding (home / results bar)" and the First-Person Language Do/Don't row "You starred this" both predated the library-first shell. Updated to "⌘K overlay / results page" and "You pinned this" so the doc reads consistently end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Chen <99816898+donteatfriedrice@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Spool indexes sessions from Claude Code, Codex CLI, and Gemini CLI. Each source records a project's working directory in its own way, and the same physical project (e.g.
~/code/spool) can show up under differentproject_idrows depending on which CLI you used. Without a stable identity, the upcoming library-first UI — Sidebar grouping, single-project page,in:projectsearch scope — has no way to merge them.What this PR does
Introduces a deterministic project identity for every session, computed from canonical signals — git remote URL for repos with origin, git common dir as fallback, manifest path (
package.json/Cargo.toml/pyproject.toml/go.mod/Gemfile/pom.xml/build.gradle) for non-git projects, raw path as last resort. The identity is split into akinddiscriminator and a stablekeythat survives renames, branch checkouts, and home-directory portability (so a workspace cloned to a different machine resolves to the same identity).A backfill step runs once during migration so every pre-v6 row gets an identity immediately. Identity computation is idempotent — re-running on the same project always yields the same key — which is what lets new sessions slot into existing groups without UI churn.
Concrete example: two agents, one project
A user runs
claudein~/code/spooland later runscodexin the same directory. Each CLI writes its session log under its own~/.<agent>/sessions/...tree, so the syncer creates twoprojectsrows withcwd = /Users/chen/code/spoolbut differentsourcevalues. Without identity grouping, the sidebar would show two entries for the same repo.When a new session is inserted,
computeIdentity(cwd, fs)runs:cwdis not the home directory or a known loose dir → notloose.git→ finds it at/Users/chen/code/spool(the gitRoot)git config --get remote.origin.url→git@github.com:spool-lab/spool.gitnormalizeGitRemotestrips thegit@…:SSH prefix, the.gitsuffix, and lower-cases →github.com/spool-lab/spoolpackage.jsonfor the display name →spoolBoth rows persist with the same identity:
Cloning the repo to a second machine at
~/work/spoolproduces the sameidentity_keybecause it's derived from the remote URL, not the path. Switching git branches doesn't change it either. If the project has no remote configured, identity falls back togit_common_dir(a stable absolute path inside.git); for non-git projects it falls back to the manifest directory; only as a last resort does it use the raw cwd.Schema (
user_version6)Adds three columns to
projects:identity_kind TEXT NOT NULLgit_remote/git_common_dir/manifest_path/path/loose)identity_key TEXT NOT NULLdisplay_name TEXT NOT NULLAnd a new view
project_groups_vthat aggregates session counts and last-activity timestamps per identity. Sessions withmessage_count = 0are filtered out of the aggregate because the watcher creates a session row when a CLI process starts, before the first user message is written — those rows would otherwise appear as empty entries in every group.How it connects
This PR is the data contract; everything downstream in the stack consumes it:
project_groups_vdirectlyidentity_keyin:projectsearch scope filters byidentity_keySubmitting it as the first PR keeps the schema migration reviewable on its own.
Test plan
pnpm --filter @spool-lab/core test— identity computation, homedir portability, git common-dir absolutization, remote URL normalization