#2879·gstack

./setup run inside a project-vendored gstack silently captures the machine-global Codex skill namespace

Author: michaeladair44Created Sep 15, 2026Updated Sep 15, 2026

Summary

setup writes the Codex host registration to a machine-global destination, but sources it from whichever checkout ./setup happened to run in. Running setup once inside a project that carries its own vendored gstack therefore repoints every Codex session on the machine at that project's copy, including in completely unrelated repos.

Combined with #1925 (upgrades only refresh the default Claude host), the capture never heals: the machine-owned Claude checkout keeps upgrading while the Codex lane stays pinned to the project copy indefinitely.

Mechanism (read at v1.87.0.0)

setup:64    SOURCE_GSTACK_DIR="$(cd "$(dirname "$0")" && pwd -P)"     # wherever setup was invoked
setup:67    CODEX_SKILLS="${CODEX_HOME:-$HOME/.codex}/skills"         # machine-global
setup:2398  link_codex_skill_dirs "$SOURCE_GSTACK_DIR" "$CODEX_SKILLS"

link_codex_skill_dirs symlinks $SOURCE_GSTACK_DIR/.agents/skills/gstack-* into the global $CODEX_SKILLS. Nothing checks that SOURCE_GSTACK_DIR is a machine-owned checkout rather than a project-local one.

CODEX_REPO_LOCAL does not cover this: it is set by inspecting the destination (INSTALL_SKILLS_DIR basename skills with parent .agents), so a global install whose source is project-local takes the ordinary global path.

Reproduction

  1. Create a project from a template that vendors gstack into the project. Conductor's "new project with gstack" flow does this; any repo with a committed .claude/skills/gstack has the same shape.
  2. Run ./setup --host codex from that project's vendored gstack.
  3. Open Codex in any other, unrelated repo.
  4. Every gstack-* skill resolves into the first project.
readlink -f ~/.codex/skills/gstack-investigate/SKILL.md
<vendored-project>/.claude/skills/gstack/.agents/skills/gstack-investigate/SKILL.md

On my machine this persisted for five weeks. The machine-owned Claude checkout moved from v1.60.1.0 to v1.87.0.0 while all 52 Codex skill symlinks stayed pinned to the project copy at v1.60.1.0 (plus the ~/.codex/skills/gstack runtime root, whose bin, lib, ETHOS.md and SKILL.md entries also pointed into the project). For gstack-investigate alone the two generated SKILL.md files differ by 666 lines.

Two properties make it hard to notice:

  • The captured skills work. They are real gstack skills, just old, so nothing errors.
  • It is invisible from inside the project that caused it, because from there the path looks correct. It is only visible from an unrelated repo, and only if you happen to look at a resolved skill path.

What finally surfaced it was a Codex skill reference rendering its absolute path in an unrelated project, which is not something a user would normally read.

Suggested direction

Either of these would have prevented it:

  1. Refuse to write a machine-global host lane from a project-local source without an explicit opt-in. If SOURCE_GSTACK_DIR is not the machine-owned install root, require a flag before writing $HOME/.codex/skills, and print which directory is about to own the global namespace. A one-line confirmation at install time would have made this impossible to do by accident.
  2. Persist installed hosts and their source roots, then have any later setup or upgrade re-assert the recorded source, which repairs a capture automatically. This is the Fix 2 direction #1925 already converged on.

(1) prevents the capture, (2) recovers from it. (2) is tracked in #1925, so (1) looks like the unowned half. Filing this separately rather than commenting on #1925 because the entry vector is a different defect from the upgrade-refresh gap, though the two compose into the silent five-week version.

Workaround

Re-run from the machine-owned checkout:

cd ~/.claude/skills/gstack && ./setup --host codex

This is self-healing: create_codex_runtime_root removes the stale runtime root and link_codex_skill_dirs overwrites each existing symlink. Verify with readlink -f ~/.codex/skills/gstack-<skill>/SKILL.md.

Note that active Codex sessions cache their skill catalog, so verification needs a fresh session.

Environment

  • macOS (Apple Silicon)
  • gstack v1.87.0.0, machine-owned checkout at ~/.claude/skills/gstack
  • Codex CLI, global host lane at ~/.codex/skills

Related

  • #1925 — upgrades only re-register Claude, so a capture like this never self-corrects
  • #2347 — explicit --host selection not being exclusive (adjacent host-boundary shape)
  • #1235 — repo-local install option that keeps state inside the project