mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
feat(dx): agent-ready error contract for the DevEx review skills
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
a889ef9393
commit
2a17a595bf
20 files changed
+467
-81
No files matched your search
@@ -0,0 +1,84 @@
|
||||
import { afterEach, describe, expect, test } from 'bun:test';
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { createSnapshot, prepareMethodology } from '../bin/gstack-autoplan-snapshot';
|
||||
import { DX_ERROR_COMPACT, DX_ERROR_CONTRACT, DX_ERROR_ITEMS, DX_ERROR_RUBRIC } from '../lib/dx-error-contract';
|
||||
|
||||
const ROOT = resolve(import.meta.dir, '..');
|
||||
const read = (path: string) => readFileSync(join(ROOT, path), 'utf8');
|
||||
const owned: string[] = [];
|
||||
afterEach(() => { for (const dir of owned.splice(0)) rmSync(dir, { recursive: true, force: true }); });
|
||||
|
||||
describe('agent-ready error contract reaches every DX reviewer', () => {
|
||||
test('the contract names all eight items, both caps and the failure-class rule', () => {
|
||||
for (const item of DX_ERROR_ITEMS) {
|
||||
expect(DX_ERROR_CONTRACT).toContain(`**${item}**`);
|
||||
expect(DX_ERROR_COMPACT).toContain(`${item} (`);
|
||||
}
|
||||
for (const text of [DX_ERROR_CONTRACT, DX_ERROR_COMPACT]) {
|
||||
expect(text).toContain('CAP4:<path>:<item>');
|
||||
expect(text).toContain('CAP6:<path>:<item>');
|
||||
expect(text).toContain('4/10');
|
||||
expect(text).toContain('6/10');
|
||||
expect(text).toMatch(/[Uu]sage (and|or) validation error/);
|
||||
expect(text).toMatch(/unexpected failures only/i);
|
||||
}
|
||||
});
|
||||
|
||||
test('the snapshot literal equals the compact form, and the built native DX prompt carries it', () => {
|
||||
const snapshotSource = read('bin/gstack-autoplan-snapshot.ts');
|
||||
expect(snapshotSource).toContain(`3. Error handling: ${DX_ERROR_COMPACT}`);
|
||||
const dir = mkdtempSync(join(tmpdir(), 'gstack-dx-contract-')); owned.push(dir);
|
||||
const active = join(dir, 'active.md');
|
||||
const restore = join(dir, 'restore.md');
|
||||
writeFileSync(active, '# Plan\n\n## Implementation plan\nShip a CLI.\n## Review record\n');
|
||||
writeFileSync(restore, 'Ship a CLI.\n');
|
||||
const method = prepareMethodology('dx', join(ROOT, 'plan-devex-review/SKILL.md'), restore);
|
||||
const methodology = readFileSync(method.methodologyPath, 'utf8');
|
||||
expect(methodology).toContain(DX_ERROR_COMPACT);
|
||||
const snapshot = createSnapshot('dx', active, restore, method.methodologyPath);
|
||||
expect(snapshot.nativePrompt).toContain(DX_ERROR_COMPACT);
|
||||
});
|
||||
|
||||
test('rendered skills carry the contract where each consumer reads it', () => {
|
||||
expect(read('plan-devex-review/sections/review-sections.md').includes(DX_ERROR_COMPACT)).toBe(true);
|
||||
expect(read('plan-devex-review/sections/review-sections.md')).toContain('docs/agent-ready-errors.md');
|
||||
expect(read('autoplan/sections/dx-phase.md')).toContain('2. Errors, agent-ready (what, fix, recovery, code, context, trace, redaction, channel)');
|
||||
const devex = read('devex-review/SKILL.md');
|
||||
expect(devex).toContain(DX_ERROR_ITEMS.join(', '));
|
||||
expect(devex).toContain('plan-devex-review/dx-hall-of-fame.md');
|
||||
expect(devex).toContain('gstack-error-capture');
|
||||
expect(devex).toContain('secret_findings');
|
||||
expect(read('plan-devex-review/sections/checklist.md')).toContain('CAP4');
|
||||
});
|
||||
|
||||
test('the public doc is generated from the contract', () => {
|
||||
const body = DX_ERROR_CONTRACT.slice(DX_ERROR_CONTRACT.indexOf('| Item |'));
|
||||
expect(read('docs/agent-ready-errors.md')).toContain(body);
|
||||
});
|
||||
|
||||
test('review-log rows record the rubric version', () => {
|
||||
for (const path of ['plan-devex-review/sections/review-sections.md', 'devex-review/SKILL.md']) {
|
||||
const text = read(path);
|
||||
expect(text.includes(`"dx_rubric":"${DX_ERROR_RUBRIC}"`), path).toBe(true);
|
||||
expect(text.includes('"dimension_scores":{"getting_started":N'), path).toBe(true);
|
||||
expect(text.includes('rubric changed, not comparable'), path).toBe(true);
|
||||
}
|
||||
expect(read('autoplan/SKILL.md').includes(`"unresolved":N,"dx_rubric":"${DX_ERROR_RUBRIC}","via":"autoplan"`)).toBe(true);
|
||||
});
|
||||
|
||||
test('no DX consumer keeps the old error formula', () => {
|
||||
const consumers = [
|
||||
'scripts/resolvers/dx.ts', 'bin/gstack-autoplan-snapshot.ts', 'autoplan/sections/dx-phase.md',
|
||||
'plan-devex-review/SKILL.md', 'plan-devex-review/sections/review-sections.md',
|
||||
'plan-devex-review/sections/checklist.md', 'plan-devex-review/dx-hall-of-fame.md', 'devex-review/SKILL.md',
|
||||
];
|
||||
for (const path of consumers) {
|
||||
const text = read(path);
|
||||
for (const old of ['problem + cause + fix', 'What happened + Why + How to fix', 'three-tier model', 'explain the cause, show the fix']) {
|
||||
expect(text.includes(old), `${path} still says "${old}"`).toBe(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user