garrytan / garrytan/gstack

gstack-slug: sticky project identity is cached per-directory, so linked worktrees get a different slug and split the session store

Open Beginner friendly
#2,767 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.