gstack/test/bug-report-skill.test.ts

85 lines
3.9 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
const ROOT = path.resolve(import.meta.dir, '..');
function read(relativePath: string): string {
return fs.readFileSync(path.join(ROOT, relativePath), 'utf8');
}
describe('/bug-report skill', () => {
test('is report-only and requires explicit approval before publication', () => {
const template = read('bug-report/SKILL.md.tmpl');
expect(template).toContain('REPORT, DO NOT FIX');
expect(template).toContain('NO SURPRISE PUBLICATION');
expect(template).toContain('does not fix code or publish the report without explicit approval');
expect(template).not.toMatch(/allowed-tools:[\s\S]*?\n - Edit\n/);
});
test('requires reproducible evidence and distinguishes fact from hypothesis', () => {
const template = read('bug-report/SKILL.md.tmpl');
expect(template).toContain('## Steps to reproduce');
expect(template).toContain('## Evidence');
expect(template).toContain('### Confirmed facts');
expect(template).toContain('### Root-cause hypothesis');
expect(template).toContain('Not reproduced');
});
test('protects sensitive histories and scans the report at the sink', () => {
const template = read('bug-report/SKILL.md.tmpl');
expect(template).toMatch(/shell history as\s+sensitive activity records/);
expect(template).toContain('{{REDACT_INVOCATION_BLOCK:pre-issue:retain}}');
expect(template).toContain('{{REDACT_INVOCATION_BLOCK:pre-pr-body:brief:retain}}');
expect(template).toContain('Never run bare `env`, `printenv`, `set`');
expect(template).toContain('Never copy an entire tool home');
expect(template).toContain('SQLite state');
expect(template).toContain('Treat every evidence file as its own persistence sink');
expect(template).toContain('separate sinks');
expect(template).toContain('finally-style cleanup step');
expect(template).toContain('Rerun the PR-body scan');
expect(template).toContain('Screenshots and other binary evidence require a separate visual privacy gate');
expect(template).toContain('Never upload raw captures');
});
test('generated workflow retains scanned bytes until persistence succeeds', () => {
const generated = read('bug-report/SKILL.md');
expect(generated).toContain('Retain `$REDACT_FILE` for the immediate destination write');
expect(generated).toContain('Never delete or reconstruct it before the write');
expect(generated).toContain('Retain `$REDACT_FILE` until the destination write succeeds');
});
test('drafts honest maintainer guidance without pretending a fix exists', () => {
const template = read('bug-report/SKILL.md.tmpl');
expect(template).toContain('{YYYY-MM-DD}-{slug}-pr-body.md');
expect(template).toContain('Not implemented in this report-only run');
expect(template).toContain('Verify generator idempotency by snapshotting');
});
test('is included in the router and documentation inventories', () => {
const router = read('SKILL.md.tmpl');
expect(router).toContain('"this doesn\'t work"');
expect(router).toMatch(/"this doesn't work"[^\n]+invoke `\/investigate`/);
expect(router).toMatch(/Explicit precedence exception:[^\n]+invoke `\/bug-report`/);
expect(read('AGENTS.md')).toContain('`/bug-report`');
expect(read('docs/skills.md')).toContain('## `/bug-report`');
});
test('produces PR guidance without manufacturing an empty PR', () => {
const template = read('bug-report/SKILL.md.tmpl');
expect(template).toContain('-pr-body.md');
expect(template).toContain('How maintainers can verify');
expect(template).toContain('Do not manufacture an');
expect(template).toContain('Open a remote draft PR only when');
});
test('generated catalogs include the skill', () => {
expect(read('gstack/llms.txt')).toContain('[/bug-report](bug-report/SKILL.md)');
expect(read('scripts/proactive-suggestions.json')).toContain('"bug-report"');
});
});