gstack-slug: sticky project identity is cached per-directory, so linked worktrees get a different slug and split the session store
- Dominant language
- TypeScript
- Stars
- 133k
- Forks
- 19.9k
- Avg merge
- 18h 46m
- Merged PRs (30d)
- 26
Description
## Summary
`bin/gstack-slug` caches project identity keyed by the literal `pwd`. Because the
sticky-identity rule (#2212) only fires on a cache hit, a linked worktree — a fresh
`pwd`, so a fresh cache key — never inherits the project's sticky slug. It recomputes
from the remote and gets a different answer.
Result: session artifacts for the same project split across two
`~/.gstack/projects//` directories depending on which checkout the skill ran from.
gstack 1.78.0.0.
## Repro
```bash
export GSTACK_HOME=$(mktemp -d) # keep the real store clean
SLUG=~/.claude/skills/gstack/bin/gstack-slug
mkdir myproj && cd myproj
git init -q . && git commit -qm init --allow-empty
$SLUG | head -1 # SLUG=myproj <- gstack used before a remote exists
git remote add origin git@github.com:someowner/myproj.git
$SLUG | head -1 # SLUG=myproj <- sticky, correct per #2212
git worktree add -q ../myproj-wt -b feature
(cd ../myproj-wt && $SLUG | head -1)
# SLUG=someowner-myproj <- BUG: same project, different slug
```
Two cache entries are written, one per directory:
```
_path_to_myproj -> myproj
_path_to_myproj-wt -> someowner-myproj
```
## Why this is a bug and not the intended stickiness
**1. It defeats the feature's own stated purpose.** From `bin/gstack-slug`:
> Cached identity is STICKY (#2212): a project that used gstack before it adopted a
> git remote keeps its pre-origin slug — recomputing from the remote here would rename
> the project mid-life and **orphan everything under `~/.gstack/projects//`**
Working from a worktree orphans the store in exactly the same way. The rule prevents
the harm in one configuration and causes it in another.
**2. gstack's own layout says the directory is per-project and the branch belongs in
the filename.** In a real store hit by this:
```
~/.gstack/projects/myproj/
master-reviews.jsonl
feature-branch-evidence.jsonl <- this branch's evidence
~/.gstack/projects/someowner-myproj/
feature-branch-reviews.jsonl <- SAME branch, other directory
```
Same branch, two directories, decided only by which checkout the skill ran from. On the
affected store here that is 34K of `decisions.jsonl` in one directory and 762 bytes in
the other, plus two `timeline.jsonl` and two `learnings.jsonl`.
**3. It breaks in the mode gstack is built around.** `CONTRIBUTING.md`: "Conductor
workspaces are independent. Each workspace is its own git worktree." So sticky project
identity stops working precisely when you go parallel.
**4. The header claims the opposite.** `bin/gstack-slug` says the parse is kept
"byte-identical to `browse/bin/remote-slug` so the two bins can never disagree on a
canonical-remote repo, **worktrees included**". True of the parse — but stickiness
bypasses the parse, so the two bins do disagree, and `browse/bin/remote-slug` has no
cache at all.
**5. gstack already learned this exact lesson elsewhere.** From `ship/SKILL.md`'s
pre-push guard:
> Linked worktrees: `--absolute-git-dir` is `.git/worktrees/` but hooks resolve to
> the COMMON `.git/hooks`, so match against the common dir too or every Conductor
> worktree false-negatives as a "custom hooks path".
Same class of bug, already fixed there, missed in the slug cache.
## Suggested fix
Key the cache on the repository rather than the directory. `git rev-parse
--git-common-dir` is stable across a main checkout and all its linked worktrees, and
still distinguishes genuinely different repos:
```
main checkout common-git-dir = /path/to/myproj/.git
worktree feature common-git-dir = /path/to/myproj/.git
```
Falling back to `pwd` when that command fails preserves today's behaviour for non-git
directories. This also fixes a smaller symptom: subdirectories currently mint their own
cache entries, so a subdir visited during a different era of the repo can stay frozen on
a third, older slug indefinitely.
## Caveat
Changing the key does not merge data already split across two directories. That is
adjacent to the existing P2 "slug store migration" item, though that one is scoped to a
different cause (the degraded marker-basename slug, e.g. `projects/garrytan/`). #2052 is
related but the opposite direction — worktrees *sharing* an origin-derived slug and
colliding, rather than failing to share a sticky one.
Contributor guide
Research direction
Start in bin/gstack-slug and reproduce the linked-worktree example with GSTACK_HOME isolated. Read the existing cache-key logic and the suggested git rev-parse --git-common-dir command; done means a main checkout and linked worktree reuse the same sticky identity, while non-git directories retain the pwd fallback.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, shell
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100