mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 06:28:59 +02:00
fix(relink): skill_prefix patches the gbrain render too — the file the host actually serves (#2738)
gstack-relink linked SKILL.md from RENDER_DIR when a gbrain render was active but ran gstack-patch-names only on INSTALL_DIR, so the served frontmatter kept the unprefixed name and skill_prefix=true silently no-oped for every brain-aware skill. The render tree (user-owned, untracked) is now patched too; gstack-patch-names is idempotent so repeat relinks never double-prefix. The gen-skill-docs note that pointed users at relink now describes what relink actually covers. Receipt: the new test fails on a v1.77.0.0 scratch worktree (served render keeps 'name: qa'). Fixes #2738 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f7e378cebe
commit
e2d6cb570f
+7
-1
@@ -110,8 +110,14 @@ for skill_dir in "$INSTALL_DIR"/*/; do
|
||||
SKILL_COUNT=$((SKILL_COUNT + 1))
|
||||
done
|
||||
|
||||
# Patch SKILL.md name: fields to match prefix setting
|
||||
# Patch SKILL.md name: fields to match prefix setting. When a gbrain render
|
||||
# is active the loop above links SKILL.md from RENDER_DIR — the file the host
|
||||
# actually serves — so patch THAT tree too or skill_prefix=true is a no-op
|
||||
# for every brain-aware skill (#2738). gstack-patch-names takes an arbitrary
|
||||
# root, skips already-prefixed names (idempotent), and the render dir is
|
||||
# user-owned and untracked, so patching it never dirties a checkout.
|
||||
"$INSTALL_DIR/bin/gstack-patch-names" "$INSTALL_DIR" "$PREFIX"
|
||||
[ -d "$RENDER_DIR" ] && "$INSTALL_DIR/bin/gstack-patch-names" "$RENDER_DIR" "$PREFIX"
|
||||
|
||||
if [ "$PREFIX" = "true" ]; then
|
||||
echo "Relinked $SKILL_COUNT skills as gstack-*"
|
||||
|
||||
@@ -1146,7 +1146,7 @@ if (!DRY_RUN) {
|
||||
if (fs.existsSync(configPath)) {
|
||||
const config = fs.readFileSync(configPath, 'utf-8');
|
||||
if (/^skill_prefix:\s*true/m.test(config)) {
|
||||
console.log('\nNote: skill_prefix is true. Run gstack-relink to re-apply name: patches.');
|
||||
console.log('\nNote: skill_prefix is true. Run gstack-relink to re-apply name: patches (it patches both the install and any active gbrain render).');
|
||||
}
|
||||
}
|
||||
} catch { /* non-fatal */ }
|
||||
|
||||
@@ -298,6 +298,44 @@ describe('gstack-relink (#578)', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// #2738: with skill_prefix=true AND an active gbrain render, the symlink
|
||||
// target is the RENDER copy — so the render's `name:` must get the gstack-
|
||||
// prefix too, or the served frontmatter stays unprefixed and skill_prefix
|
||||
// silently no-ops for every brain-aware skill.
|
||||
test('skill_prefix=true patches the rendered SKILL.md name too (#2738)', () => {
|
||||
setupMockInstall(['qa']);
|
||||
const renderDir = path.join(tmpDir, 'render', 'claude', 'qa');
|
||||
fs.mkdirSync(renderDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(renderDir, 'SKILL.md'),
|
||||
'---\nname: qa\ndescription: test\n---\nrendered brain-aware qa',
|
||||
);
|
||||
|
||||
run(`${path.join(installDir, 'bin', 'gstack-config')} set skill_prefix true`, {
|
||||
GSTACK_INSTALL_DIR: installDir,
|
||||
GSTACK_SKILLS_DIR: skillsDir,
|
||||
GSTACK_HOME: tmpDir,
|
||||
});
|
||||
run(`${path.join(installDir, 'bin', 'gstack-relink')}`, {
|
||||
GSTACK_INSTALL_DIR: installDir,
|
||||
GSTACK_SKILLS_DIR: skillsDir,
|
||||
GSTACK_HOME: tmpDir,
|
||||
});
|
||||
|
||||
const served = path.join(skillsDir, 'gstack-qa', 'SKILL.md');
|
||||
expect(fs.readlinkSync(served)).toBe(path.join(renderDir, 'SKILL.md'));
|
||||
// The SERVED file (the render) carries the prefixed name.
|
||||
expect(fs.readFileSync(served, 'utf-8')).toContain('name: gstack-qa');
|
||||
// Idempotent: a second relink must not double-prefix.
|
||||
run(`${path.join(installDir, 'bin', 'gstack-relink')}`, {
|
||||
GSTACK_INSTALL_DIR: installDir,
|
||||
GSTACK_SKILLS_DIR: skillsDir,
|
||||
GSTACK_HOME: tmpDir,
|
||||
});
|
||||
expect(fs.readFileSync(served, 'utf-8')).toContain('name: gstack-qa');
|
||||
expect(fs.readFileSync(served, 'utf-8')).not.toContain('gstack-gstack-');
|
||||
});
|
||||
|
||||
// FIRST INSTALL: --no-prefix must create ONLY flat names, zero gstack-* pollution
|
||||
test('first install --no-prefix: only flat names exist, zero gstack-* entries', () => {
|
||||
setupMockInstall(['qa', 'ship', 'review', 'plan-ceo-review', 'gstack-upgrade']);
|
||||
|
||||
Reference in New Issue
Block a user