C4: external hosts point at carved sections relative to the installed skill

Publishes the pointer form setup matches: on every non-Claude host a carved
section is `sections/<file>` relative to the installed gstack-<skill> SKILL.md
directory (QA's existing form), never $GSTACK_ROOT/<skill>/sections/, which
does not exist in external runtime roots. No render changes yet: only QA is
carved on external hosts today.
This commit is contained in:
garrytan committed 2026-10-03 20:46:43 +00:00
1 parent a0c0668b3b
commit b141b101e9
2 files changed
+36 -2

No files matched your search

+8 -2
View File
@@ -54,11 +54,17 @@ function findSection(skill: string, id: string): SectionEntry {
return entry;
}
/**
* Pointer to a carved section file. Claude keeps its global-root path; QA and
* every external host point relative to the installed skill directory, because
* external runtime roots (`$GSTACK_ROOT`) carry no `<skill>/sections/` tree
* while setup links or copies whole skill directories.
*/
export function sectionPath(ctx: TemplateContext, skill: string, id: string): string {
const entry = findSection(skill, id);
if (skill === 'qa' || skill === 'qa-only') {
if (ctx.host !== 'claude' || skill === 'qa' || skill === 'qa-only') {
fs.accessSync(path.join(ROOT, skill, 'sections', `${entry.file}.tmpl`), fs.constants.R_OK);
const installedName = ctx.host === 'claude' ? `\`${skill}\`/\`gstack-${skill}\`` : `\`gstack-${skill}\``;
const installedName = ctx.host === 'claude' ? `\`${skill}\`/\`gstack-${skill}\`` : `\`${skill.startsWith('gstack-') ? skill : `gstack-${skill}`}\``;
return `\`sections/${entry.file}\` relative to the installed ${installedName} SKILL.md directory`;
}
return `\`${ctx.paths.skillRoot}/${skill}/sections/${entry.file}\``;
+28
View File
@@ -0,0 +1,28 @@
/**
* C4: carved skills on external hosts point at sections relative to the
* installed skill directory. `$GSTACK_ROOT/<skill>/sections/...` does not exist
* in external runtime roots (Codex's holds bin, lib, browse, review only), so a
* root-relative pointer would 404 and the model would work from memory.
*/
import { describe, expect, test } from 'bun:test';
import { ALL_HOST_CONFIGS } from '../hosts';
import { sectionPath } from '../scripts/resolvers/sections';
import { HOST_PATHS, type TemplateContext } from '../scripts/resolvers/types';
const context = (host: string, skillName: string): TemplateContext => ({ host, skillName, tmplPath: '', paths: HOST_PATHS[host] });
describe('C4: external section pointers', () => {
for (const config of ALL_HOST_CONFIGS.filter(c => c.name !== 'claude')) {
test(`${config.name}: ship and plan-ceo-review point relative to the installed skill directory`, () => {
expect(sectionPath(context(config.name, 'ship'), 'ship', 'tests'))
.toBe('`sections/tests.md` relative to the installed `gstack-ship` SKILL.md directory');
expect(sectionPath(context(config.name, 'plan-ceo-review'), 'plan-ceo-review', 'review-sections'))
.toBe('`sections/review-sections.md` relative to the installed `gstack-plan-ceo-review` SKILL.md directory');
expect(sectionPath(context(config.name, 'ship'), 'ship', 'tests')).not.toContain('$GSTACK_ROOT');
});
}
test('claude keeps its global-root pointer (bytes unchanged)', () => {
expect(sectionPath(context('claude', 'ship'), 'ship', 'tests')).toBe('`~/.claude/skills/gstack/ship/sections/tests.md`');
});
});