mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-06 05:35:46 +02:00
docs+test: SKILL authoring guidance + regression tests
Adds "Writing SKILL templates" section to CLAUDE.md explaining that templates are prompts, not scripts. Adds validation test catching hardcoded 'main' in git commands, and resolver content test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -131,6 +131,27 @@ describe('gen-skill-docs', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('BASE_BRANCH_DETECT resolver', () => {
|
||||
// Find a generated SKILL.md that uses the placeholder (ship is guaranteed to)
|
||||
const shipContent = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
|
||||
|
||||
test('resolver output contains PR base detection command', () => {
|
||||
expect(shipContent).toContain('gh pr view --json baseRefName');
|
||||
});
|
||||
|
||||
test('resolver output contains repo default branch detection command', () => {
|
||||
expect(shipContent).toContain('gh repo view --json defaultBranchRef');
|
||||
});
|
||||
|
||||
test('resolver output contains fallback to main', () => {
|
||||
expect(shipContent).toMatch(/fall\s*back\s+to\s+`main`/i);
|
||||
});
|
||||
|
||||
test('resolver output uses "the base branch" phrasing', () => {
|
||||
expect(shipContent).toContain('the base branch');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Quality evals — catch description regressions.
|
||||
*
|
||||
|
||||
@@ -361,6 +361,64 @@ describe('Greptile history format consistency', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// --- Hardcoded branch name detection in templates ---
|
||||
|
||||
describe('No hardcoded branch names in SKILL templates', () => {
|
||||
const tmplFiles = [
|
||||
'ship/SKILL.md.tmpl',
|
||||
'review/SKILL.md.tmpl',
|
||||
'qa/SKILL.md.tmpl',
|
||||
'plan-ceo-review/SKILL.md.tmpl',
|
||||
'retro/SKILL.md.tmpl',
|
||||
];
|
||||
|
||||
// Patterns that indicate hardcoded 'main' in git commands
|
||||
const gitMainPatterns = [
|
||||
/\bgit\s+diff\s+(?:origin\/)?main\b/,
|
||||
/\bgit\s+log\s+(?:origin\/)?main\b/,
|
||||
/\bgit\s+fetch\s+origin\s+main\b/,
|
||||
/\bgit\s+merge\s+origin\/main\b/,
|
||||
/\borigin\/main\b/,
|
||||
];
|
||||
|
||||
// Lines that are allowed to mention 'main' (fallback logic, prose)
|
||||
const allowlist = [
|
||||
/fall\s*back\s+to\s+`main`/i,
|
||||
/fall\s*back\s+to\s+`?main`?/i,
|
||||
/typically\s+`?main`?/i,
|
||||
/If\s+on\s+`main`/i, // old pattern — should not exist
|
||||
];
|
||||
|
||||
for (const tmplFile of tmplFiles) {
|
||||
test(`${tmplFile} has no hardcoded 'main' in git commands`, () => {
|
||||
const filePath = path.join(ROOT, tmplFile);
|
||||
if (!fs.existsSync(filePath)) return;
|
||||
const lines = fs.readFileSync(filePath, 'utf-8').split('\n');
|
||||
const violations: string[] = [];
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const isAllowlisted = allowlist.some(p => p.test(line));
|
||||
if (isAllowlisted) continue;
|
||||
|
||||
for (const pattern of gitMainPatterns) {
|
||||
if (pattern.test(line)) {
|
||||
violations.push(`Line ${i + 1}: ${line.trim()}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (violations.length > 0) {
|
||||
throw new Error(
|
||||
`${tmplFile} has hardcoded 'main' in git commands:\n` +
|
||||
violations.map(v => ` ${v}`).join('\n')
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// --- Part 7b: TODOS-format.md reference consistency ---
|
||||
|
||||
describe('TODOS-format.md reference consistency', () => {
|
||||
|
||||
Reference in New Issue
Block a user