C9: Step 2 falls back to the install registry's source checkout (DX-7)

Codex users who installed from the README's ~/gstack clone got 'ERROR:
gstack not found' from /gstack-upgrade: Step 2 only looked under host skill
directories. It now falls back to the source checkout the install registry
recorded for the current host.
This commit is contained in:
garrytan committed 2026-10-03 21:44:22 +00:00
1 parent dc7a95a4b4
commit b94288db0b
2 files changed
+31

No files matched your search

+4
View File
@@ -102,6 +102,10 @@ elif [ -d ".claude/skills/gstack" ]; then
elif [ -d "$HOME/.claude/skills/gstack" ]; then
INSTALL_TYPE="vendored-global"
INSTALL_DIR="$HOME/.claude/skills/gstack"
elif _SRC=$({{BIN_DIR}}/gstack-paths --get GSTACK_STATE_ROOT 2>/dev/null) && _SRC=$(awk -F '\t' '$(1) == "{{HOST_ID}}" && $(6) != "-" { print $(6); exit }' "$_SRC/installs.tsv" 2>/dev/null) && [ -d "$_SRC/.git" ]; then
# The install registry names the checkout setup activated for this host (e.g. a ~/gstack clone).
INSTALL_TYPE="global-git"
INSTALL_DIR="$_SRC"
else
echo "ERROR: gstack not found"
exit 1
+27
View File
@@ -236,3 +236,30 @@ describe.skipIf(IS_WINDOWS)('C9: destructive upgrade/spec fences refuse bad path
expect(problems).toEqual([]);
}, 60_000);
});
describe.skipIf(IS_WINDOWS)('DX-7: Step 2 finds the registered source checkout for this host', () => {
test('codex: the README ~/gstack clone recorded in the install registry is the install dir', () => {
const step2 = fence(hostRenders.find(r => r.host === 'codex')!.upgrade, 'Install type: $INSTALL_TYPE at $INSTALL_DIR');
const w = mkdtempSync(join(root, 'dx7-'));
const home = join(w, 'home');
const state = join(w, 'state');
const clone = join(home, 'gstack');
makeGstackLike(clone, true);
mkdirSync(state, { recursive: true });
// Setup's Codex runtime root carries bin/ and lib/ linked from the clone; it has no .git.
const runtime = join(home, '.codex', 'skills', 'gstack');
mkdirSync(join(runtime, 'bin'), { recursive: true });
mkdirSync(join(runtime, 'lib'), { recursive: true });
writeFileSync(join(runtime, 'bin', 'gstack-paths'), `#!/bin/sh\necho "${state}"\n`, { mode: 0o755 });
writeFileSync(join(state, 'installs.tsv'), [
['claude', 'global', '-', '/x', '/x', '/elsewhere', '1.0', '-', 'committed', '0'].join('\t'),
['codex', 'global', '-', join(home, '.codex', 'skills'), runtime, clone, '1.0.0.0', 'false', 'committed', '0'].join('\t'),
].join('\n') + '\n');
const cwd = mkdtempSync(join(w, 'cwd-'));
const run = () => spawnSync('env', ['-i', `HOME=${home}`, `PATH=${process.env.PATH}`, 'bash', '-c', step2], { cwd, encoding: 'utf8', timeout: 20_000 });
const r = run();
expect(r.stdout.trim(), r.stderr).toBe(`Install type: global-git at ${clone}`);
rmSync(join(state, 'installs.tsv'));
expect(run().stdout).toContain('ERROR: gstack not found');
});
});