gstack/lib
benjamin beres 24eb07e477 Fix: resolveSlug silently returns "unknown" on Windows
`gstack-slug` is an extension-less bash script. On Windows, spawnSync
without a shell goes through CreateProcess, which does not honour shebangs:
the call fails with ENOENT, stdout is null, the SLUG regex does not match,
and resolveSlug returns the literal "unknown".

Repro (Windows, git-bash, bun):

    bun -e 'const {spawnSync}=require("child_process");
      const r=spawnSync("C:/Users/<u>/.claude/skills/gstack/bin/gstack-slug",
                        {encoding:"utf-8"});
      console.log(r.error&&r.error.code, JSON.stringify(r.stdout))'
    # ENOENT null

The impact is worse than a wrong path. Both consumers — gstack-decision-log
and gstack-decision-search — then read and write ~/.gstack/projects/unknown/.
On the write side that is a single anonymous bucket shared by every repo on
the machine. On the read side the directory does not exist, so the search
returns an empty list and exits 0: the session is told there are no prior
decisions and re-litigates settled calls in good faith. The `?? "unknown"`
fallback turned an exec failure into a plausible value, which is why this
went unnoticed for so long.

`shell: true` does not fix it — cmd.exe has no association for an
extension-less file and yields "unknown" too. Naming the interpreter does.

Two changes:

- Retry via `bash <script>` when the direct call yields no slug. The direct
  call is still attempted first, so POSIX behaviour is unchanged. The retry
  is keyed on "no slug parsed" rather than on ENOENT specifically, so a shim
  that exits non-zero without output is covered as well. bash and not sh:
  gstack-slug uses `[[ ]]` and `set -o pipefail`.

- Warn on stderr before falling back. The fallback value is kept for
  compatibility, but a tooling failure is not an anonymous project, and a
  mute fallback is what allowed this to live.

Verified on Windows 11 / git-bash / bun 1.3.14:

- nominal: resolveSlug returns benjaminberes-bp-alfred-agent
- failure: warns `direct: ENOENT; via bash: exited 127` and still returns
  "unknown", so no caller breaks
- end to end: `gstack-decision-search --recent 5` goes from empty to 4
  records against an existing 3930-byte decisions.active.json

gitBranch() in the same file is unaffected: it spawns `git`, which Windows
resolves via PATHEXT. Only extension-less scripts break, so an audit should
look for spawnSync calls targeting bin/gstack-*.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 14:58:09 +02:00
..
diagram-render v1.58.0.0 feat: diagram + multi-format document engine (mermaid, excalidraw, single-file HTML, DOCX) (#1990) 2026-06-12 15:38:53 -07:00
bin-context.ts Fix: resolveSlug silently returns "unknown" on Windows 2026-07-27 14:58:09 +02:00
conductor-env-shim.ts v1.58.1.0 feat: hermetic local E2E + Conductor prose AskUserQuestion (#2004) 2026-06-14 11:40:57 -07:00
gbrain-exec.ts v1.58.4.0 fix: high-priority community bug wave + PTY plan-mode smoke gate (#2077) 2026-06-21 07:15:19 -07:00
gbrain-guards.ts v1.57.5.0 feat: cross-session decision memory + gbrain dream-stage call graph (#1910) 2026-06-08 06:20:58 -07:00
gbrain-local-status.ts fix(gbrain): classify PGLite connect locks 2026-07-10 16:51:16 -07:00
gbrain-sources.ts v1.57.5.0 feat: cross-session decision memory + gbrain dream-stage call graph (#1910) 2026-06-08 06:20:58 -07:00
gstack-decision-semantic.ts v1.57.5.0 feat: cross-session decision memory + gbrain dream-stage call graph (#1910) 2026-06-08 06:20:58 -07:00
gstack-decision.ts v1.57.5.0 feat: cross-session decision memory + gbrain dream-stage call graph (#1910) 2026-06-08 06:20:58 -07:00
gstack-memory-helpers.ts fix(gbrain): canonicalize remotes with trailing slashes 2026-07-14 12:56:09 -07:00
is-conductor.ts v1.58.1.0 feat: hermetic local E2E + Conductor prose AskUserQuestion (#2004) 2026-06-14 11:40:57 -07:00
jsonl-store.ts v1.57.5.0 feat: cross-session decision memory + gbrain dream-stage call graph (#1910) 2026-06-08 06:20:58 -07:00
redact-audit-log.ts v1.53.0.0 feat: smarter redaction — PII/secrets/legal guard across /spec, /ship, /cso, /document-* (#1797) 2026-05-30 08:54:46 -07:00
redact-engine.ts v1.58.4.0 fix: high-priority community bug wave + PTY plan-mode smoke gate (#2077) 2026-06-21 07:15:19 -07:00
redact-patterns.ts v1.58.4.0 fix: high-priority community bug wave + PTY plan-mode smoke gate (#2077) 2026-06-21 07:15:19 -07:00
staging-guard.ts v1.56.1.0 fix(sync): staging-dir ownership guard + resume-correctness fixes (#1802) (#1856) 2026-06-07 06:51:10 -07:00
worktree.ts feat: content security — 4-layer prompt injection defense for pair-agent (#815) 2026-04-06 14:41:06 -07:00