mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-06 13:45:35 +02:00
fix: resolve merge conflicts with origin/main (v0.4.3 + v0.4.4)
Merge origin/main which added: - v0.4.3: /document-release skill, simplified AskUserQuestion (always ELI16), runtime branch detection via _BRANCH preamble variable - v0.4.4: 60-min update check TTL, --force flag for gstack-update-check Resolved conflicts: - VERSION: keep 0.6.0 (our version, above 0.4.4) - CHANGELOG: all entries preserved (0.6.0 > 0.4.4 > 0.4.3 > 0.4.2) - TODOS.md: keep both sections (Brainstorm/Design + Document-Release) - test/skill-validation.test.ts: include all three new skills in arrays (brainstorm, debug, document-release) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -144,7 +144,18 @@ describe('gen-skill-docs', () => {
|
||||
const content = fs.readFileSync(path.join(ROOT, 'SKILL.md'), 'utf-8');
|
||||
expect(content).toContain('_SESSIONS');
|
||||
expect(content).toContain('RECOMMENDATION');
|
||||
expect(content).toContain('ELI16');
|
||||
});
|
||||
|
||||
test('generated SKILL.md contains branch detection', () => {
|
||||
const content = fs.readFileSync(path.join(ROOT, 'SKILL.md'), 'utf-8');
|
||||
expect(content).toContain('_BRANCH');
|
||||
expect(content).toContain('git branch --show-current');
|
||||
});
|
||||
|
||||
test('generated SKILL.md contains ELI16 simplification rules', () => {
|
||||
const content = fs.readFileSync(path.join(ROOT, 'SKILL.md'), 'utf-8');
|
||||
expect(content).toContain('No raw function names');
|
||||
expect(content).toContain('plain English');
|
||||
});
|
||||
|
||||
test('qa and qa-only templates use QA_METHODOLOGY placeholder', () => {
|
||||
|
||||
@@ -1546,6 +1546,110 @@ Write your retrospective to ${dir}/retro-output.md`,
|
||||
}, 300_000);
|
||||
});
|
||||
|
||||
// --- Document-Release skill E2E ---
|
||||
|
||||
describeE2E('Document-Release skill E2E', () => {
|
||||
let docReleaseDir: string;
|
||||
|
||||
beforeAll(() => {
|
||||
docReleaseDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-doc-release-'));
|
||||
|
||||
// Copy document-release skill files
|
||||
copyDirSync(path.join(ROOT, 'document-release'), path.join(docReleaseDir, 'document-release'));
|
||||
|
||||
// Init git repo with initial docs
|
||||
const run = (cmd: string, args: string[]) =>
|
||||
spawnSync(cmd, args, { cwd: docReleaseDir, stdio: 'pipe', timeout: 5000 });
|
||||
|
||||
run('git', ['init']);
|
||||
run('git', ['config', 'user.email', 'test@test.com']);
|
||||
run('git', ['config', 'user.name', 'Test']);
|
||||
|
||||
// Create initial README with a features list
|
||||
fs.writeFileSync(path.join(docReleaseDir, 'README.md'),
|
||||
'# Test Project\n\n## Features\n\n- Feature A\n- Feature B\n\n## Install\n\n```bash\nnpm install\n```\n');
|
||||
|
||||
// Create initial CHANGELOG that must NOT be clobbered
|
||||
fs.writeFileSync(path.join(docReleaseDir, 'CHANGELOG.md'),
|
||||
'# Changelog\n\n## 1.0.0 — 2026-03-01\n\n- Initial release with Feature A and Feature B\n- Setup CI pipeline\n');
|
||||
|
||||
// Create VERSION file (already bumped)
|
||||
fs.writeFileSync(path.join(docReleaseDir, 'VERSION'), '1.1.0\n');
|
||||
|
||||
run('git', ['add', '.']);
|
||||
run('git', ['commit', '-m', 'initial']);
|
||||
|
||||
// Create feature branch with a code change
|
||||
run('git', ['checkout', '-b', 'feat/add-feature-c']);
|
||||
fs.writeFileSync(path.join(docReleaseDir, 'feature-c.ts'), 'export function featureC() { return "C"; }\n');
|
||||
fs.writeFileSync(path.join(docReleaseDir, 'VERSION'), '1.1.1\n');
|
||||
fs.writeFileSync(path.join(docReleaseDir, 'CHANGELOG.md'),
|
||||
'# Changelog\n\n## 1.1.1 — 2026-03-16\n\n- Added Feature C\n\n## 1.0.0 — 2026-03-01\n\n- Initial release with Feature A and Feature B\n- Setup CI pipeline\n');
|
||||
run('git', ['add', '.']);
|
||||
run('git', ['commit', '-m', 'feat: add feature C']);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
try { fs.rmSync(docReleaseDir, { recursive: true, force: true }); } catch {}
|
||||
});
|
||||
|
||||
test('/document-release updates docs without clobbering CHANGELOG', async () => {
|
||||
const result = await runSkillTest({
|
||||
prompt: `Read the file document-release/SKILL.md for the document-release workflow instructions.
|
||||
|
||||
Run the /document-release workflow on this repo. The base branch is "main".
|
||||
|
||||
IMPORTANT:
|
||||
- Do NOT use AskUserQuestion — auto-approve everything or skip if unsure.
|
||||
- Do NOT push or create PRs (there is no remote).
|
||||
- Do NOT run gh commands (no remote).
|
||||
- Focus on updating README.md to reflect the new Feature C.
|
||||
- Do NOT overwrite or regenerate CHANGELOG entries.
|
||||
- Skip VERSION bump (it's already bumped).
|
||||
- After editing, just commit the changes locally.`,
|
||||
workingDirectory: docReleaseDir,
|
||||
maxTurns: 30,
|
||||
allowedTools: ['Bash', 'Read', 'Write', 'Edit', 'Grep', 'Glob'],
|
||||
timeout: 180_000,
|
||||
testName: 'document-release',
|
||||
runId,
|
||||
});
|
||||
|
||||
logCost('/document-release', result);
|
||||
|
||||
// Read CHANGELOG to verify it was NOT clobbered
|
||||
const changelog = fs.readFileSync(path.join(docReleaseDir, 'CHANGELOG.md'), 'utf-8');
|
||||
const hasOriginalEntries = changelog.includes('Initial release with Feature A and Feature B')
|
||||
&& changelog.includes('Setup CI pipeline')
|
||||
&& changelog.includes('1.0.0');
|
||||
if (!hasOriginalEntries) {
|
||||
console.warn('CHANGELOG CLOBBERED — original entries missing!');
|
||||
}
|
||||
|
||||
// Check if README was updated
|
||||
const readme = fs.readFileSync(path.join(docReleaseDir, 'README.md'), 'utf-8');
|
||||
const readmeUpdated = readme.includes('Feature C') || readme.includes('feature-c') || readme.includes('feature C');
|
||||
|
||||
const exitOk = ['success', 'error_max_turns'].includes(result.exitReason);
|
||||
recordE2E('/document-release', 'Document-Release skill E2E', result, {
|
||||
passed: exitOk && hasOriginalEntries,
|
||||
});
|
||||
|
||||
// Critical guardrail: CHANGELOG must not be clobbered
|
||||
expect(hasOriginalEntries).toBe(true);
|
||||
|
||||
// Accept error_max_turns — thorough doc review is not a failure
|
||||
expect(['success', 'error_max_turns']).toContain(result.exitReason);
|
||||
|
||||
// Informational: did it update README?
|
||||
if (readmeUpdated) {
|
||||
console.log('README updated to include Feature C');
|
||||
} else {
|
||||
console.warn('README was NOT updated — agent may not have found the feature');
|
||||
}
|
||||
}, 240_000);
|
||||
});
|
||||
|
||||
// --- Deferred skill E2E tests (destructive or require interactive UI) ---
|
||||
|
||||
describeE2E('Deferred skill E2E', () => {
|
||||
|
||||
@@ -177,6 +177,7 @@ describe('Update check preamble', () => {
|
||||
'plan-ceo-review/SKILL.md', 'plan-eng-review/SKILL.md',
|
||||
'retro/SKILL.md',
|
||||
'brainstorm/SKILL.md', 'debug/SKILL.md',
|
||||
'document-release/SKILL.md',
|
||||
];
|
||||
|
||||
for (const skill of skillsWithUpdateCheck) {
|
||||
@@ -398,6 +399,7 @@ describe('No hardcoded branch names in SKILL templates', () => {
|
||||
'qa/SKILL.md.tmpl',
|
||||
'plan-ceo-review/SKILL.md.tmpl',
|
||||
'retro/SKILL.md.tmpl',
|
||||
'document-release/SKILL.md.tmpl',
|
||||
];
|
||||
|
||||
// Patterns that indicate hardcoded 'main' in git commands
|
||||
@@ -481,6 +483,7 @@ describe('v0.4.1 preamble features', () => {
|
||||
'plan-ceo-review/SKILL.md', 'plan-eng-review/SKILL.md',
|
||||
'retro/SKILL.md',
|
||||
'brainstorm/SKILL.md', 'debug/SKILL.md',
|
||||
'document-release/SKILL.md',
|
||||
];
|
||||
|
||||
for (const skill of skillsWithPreamble) {
|
||||
@@ -493,7 +496,7 @@ describe('v0.4.1 preamble features', () => {
|
||||
test(`${skill} contains session awareness`, () => {
|
||||
const content = fs.readFileSync(path.join(ROOT, skill), 'utf-8');
|
||||
expect(content).toContain('_SESSIONS');
|
||||
expect(content).toContain('ELI16');
|
||||
expect(content).toContain('RECOMMENDATION');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user