mirror of
https://github.com/garrytan/gstack.git
synced 2026-07-07 08:37:52 +02:00
feat: QA restructure, browser ref staleness, eval efficiency metrics (v0.4.0) (#83)
* feat: browser ref staleness detection via async count() validation resolveRef() now checks element count to detect stale refs after page mutations (e.g. SPA navigation). RefEntry stores role+name metadata for better diagnostics. 3 new snapshot tests for staleness detection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: qa-only skill, qa fix loop, plan-to-QA artifact flow Add /qa-only (report-only, Edit tool blocked), restructure /qa with find-fix-verify cycle, add {{QA_METHODOLOGY}} DRY placeholder for shared methodology. /plan-eng-review now writes test-plan artifacts to ~/.gstack/projects/<slug>/ for QA consumption. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: eval efficiency metrics — turns, duration, commentary across all surfaces Add generateCommentary() for natural-language delta interpretation, per-test turns/duration in comparison and summary output, judgePassed unit tests, 3 new E2E tests (qa-only, qa fix loop, plan artifact). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump version and changelog (v0.4.0) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update ARCHITECTURE, BROWSER, CONTRIBUTING, README for v0.4.0 - ARCHITECTURE: add ref staleness detection section, update RefEntry type - BROWSER: add ref staleness paragraph to snapshot system docs - CONTRIBUTING: update eval tool descriptions with commentary feature - README: fix missing qa-only in project-local uninstall command Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add user-facing benefit descriptions to v0.4.0 changelog Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,7 @@ describe('gen-skill-docs', () => {
|
||||
{ dir: '.', name: 'root gstack' },
|
||||
{ dir: 'browse', name: 'browse' },
|
||||
{ dir: 'qa', name: 'qa' },
|
||||
{ dir: 'qa-only', name: 'qa-only' },
|
||||
{ dir: 'review', name: 'review' },
|
||||
{ dir: 'ship', name: 'ship' },
|
||||
{ dir: 'plan-ceo-review', name: 'plan-ceo-review' },
|
||||
@@ -129,6 +130,61 @@ describe('gen-skill-docs', () => {
|
||||
expect(browseTmpl).toContain('{{COMMAND_REFERENCE}}');
|
||||
expect(browseTmpl).toContain('{{SNAPSHOT_FLAGS}}');
|
||||
});
|
||||
|
||||
test('qa and qa-only templates use QA_METHODOLOGY placeholder', () => {
|
||||
const qaTmpl = fs.readFileSync(path.join(ROOT, 'qa', 'SKILL.md.tmpl'), 'utf-8');
|
||||
expect(qaTmpl).toContain('{{QA_METHODOLOGY}}');
|
||||
|
||||
const qaOnlyTmpl = fs.readFileSync(path.join(ROOT, 'qa-only', 'SKILL.md.tmpl'), 'utf-8');
|
||||
expect(qaOnlyTmpl).toContain('{{QA_METHODOLOGY}}');
|
||||
});
|
||||
|
||||
test('QA_METHODOLOGY appears expanded in both qa and qa-only generated files', () => {
|
||||
const qaContent = fs.readFileSync(path.join(ROOT, 'qa', 'SKILL.md'), 'utf-8');
|
||||
const qaOnlyContent = fs.readFileSync(path.join(ROOT, 'qa-only', 'SKILL.md'), 'utf-8');
|
||||
|
||||
// Both should contain the health score rubric
|
||||
expect(qaContent).toContain('Health Score Rubric');
|
||||
expect(qaOnlyContent).toContain('Health Score Rubric');
|
||||
|
||||
// Both should contain framework guidance
|
||||
expect(qaContent).toContain('Framework-Specific Guidance');
|
||||
expect(qaOnlyContent).toContain('Framework-Specific Guidance');
|
||||
|
||||
// Both should contain the important rules
|
||||
expect(qaContent).toContain('Important Rules');
|
||||
expect(qaOnlyContent).toContain('Important Rules');
|
||||
|
||||
// Both should contain the 6 phases
|
||||
expect(qaContent).toContain('Phase 1');
|
||||
expect(qaOnlyContent).toContain('Phase 1');
|
||||
expect(qaContent).toContain('Phase 6');
|
||||
expect(qaOnlyContent).toContain('Phase 6');
|
||||
});
|
||||
|
||||
test('qa-only has no-fix guardrails', () => {
|
||||
const qaOnlyContent = fs.readFileSync(path.join(ROOT, 'qa-only', 'SKILL.md'), 'utf-8');
|
||||
expect(qaOnlyContent).toContain('Never fix bugs');
|
||||
expect(qaOnlyContent).toContain('NEVER fix anything');
|
||||
// Should not have Edit, Glob, or Grep in allowed-tools
|
||||
expect(qaOnlyContent).not.toMatch(/allowed-tools:[\s\S]*?Edit/);
|
||||
expect(qaOnlyContent).not.toMatch(/allowed-tools:[\s\S]*?Glob/);
|
||||
expect(qaOnlyContent).not.toMatch(/allowed-tools:[\s\S]*?Grep/);
|
||||
});
|
||||
|
||||
test('qa has fix-loop tools and phases', () => {
|
||||
const qaContent = fs.readFileSync(path.join(ROOT, 'qa', 'SKILL.md'), 'utf-8');
|
||||
// Should have Edit, Glob, Grep in allowed-tools
|
||||
expect(qaContent).toContain('Edit');
|
||||
expect(qaContent).toContain('Glob');
|
||||
expect(qaContent).toContain('Grep');
|
||||
// Should have fix-loop phases
|
||||
expect(qaContent).toContain('Phase 7');
|
||||
expect(qaContent).toContain('Phase 8');
|
||||
expect(qaContent).toContain('Fix Loop');
|
||||
expect(qaContent).toContain('Triage');
|
||||
expect(qaContent).toContain('WTF');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user