mirror of https://github.com/garrytan/gstack.git
`slugFromCwd()` in the AskUserQuestion PreToolUse hook returned
`path.basename(cwd)`. Every writer of project-local state uses the slug from
`bin/gstack-slug`, which derives it from the git remote (owner-repo) and only
falls back to basename when the repo has NO remote configured:
plan-tune/SKILL.md: _PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG}"
hook: <stateRoot>/projects/<basename>/question-preferences.json
In any repo with a remote the two disagree, so the hook read a directory that
does not exist. Observed on a real machine:
gstack-slug → Shmulyitz-tristatelvs (what /plan-tune writes)
basename → tristatelvs (what the hook read)
~/.gstack/projects/ → Shmulyitz-tristatelvs, Shmulyitz-Tristate-pim,
Shmulyitz-tristate-vendure, millwoods-registry
Every bucket is remote-derived; no basename-named directory existed at all.
Impact: project-local preferences were silently inert, and the documented
project > global precedence (D8) collapsed to global-only. A user setting a
per-project never-ask preference got no enforcement and no error.
Fix: resolve the same slug gstack-slug does by reading its on-disk cache
(`<root>/slug-cache/<abs-path-with-slashes-as-underscores>`), falling back to
basename on a cache miss — which mirrors gstack-slug's own no-remote
fallback. This is a single file read, so the hot-path constraint that
motivated the basename shortcut still holds; we do not shell out to git. The
cached value is re-sanitized to the [a-zA-Z0-9._-] invariant on read because
it becomes a path segment.
Why it survived: every fixture in question-preference-hook.test.ts used a cwd
whose basename happened to equal the slug, so the mismatch could not appear.
The two added tests pin the real contract — the first FAILS on the old code
(expected "deny", received "defer"), the second covers the no-remote fallback.
Verified: 29 pass / 0 fail across question-preference-hook and
memory-cache-injection. Also confirmed end-to-end against a real slug cache.
|
||
|---|---|---|
| .. | ||
| claude/hooks | ||
| claude.ts | ||
| codex.ts | ||
| cursor.ts | ||
| factory.ts | ||
| gbrain.ts | ||
| hermes.ts | ||
| index.ts | ||
| kiro.ts | ||
| openclaw.ts | ||
| opencode.ts | ||
| slate.ts | ||