Files
gstack/test/dx-error-contract-grader.test.ts
Garry Tanandcapy-ai[bot] bc2ed0cee7 v1.91.114.0 feat(dx): score every error for an AI agent reader (agent-ready error contract + gstack-error-capture) (#3226)
* 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>

* feat(dx): gstack-error-capture runs a CLI with no terminal and prints a redacted record

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(dx): CI-clean fences, runtime-root-safe contract location, path-agnostic launch-check test

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(dx): preregistered behavior case for the agent-ready error contract; README skill rows

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(dx): grader items come from the contract; paid-file count includes the new case

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(dx): outside DX voice uses the agent-ready contract; DX_CAPTURE_* codes join RESULT_CODES with troubleshooting anchors

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(dx): review fixes for gstack-error-capture and the paid grader

- every helper diagnostic passes the record's redaction, fail-closed
- forwarded values are masked before and after sanitizing, plus JSON-escaped forms
- the final secret check reads the record's string fields with byte-based scan limits
- mise/asdf config, trust and env-loading variables need --pass-env; only data locations pass by default
- launch reports whether the target started; --launch-check's probe result is exit_code
- Step 3 captures --help first; help states the containment lifetime limit
- the paid grader validates the bound result with parseReviewResult

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(dx): mask forwarded values before argument validation; protocol strings never match literals; Step 3 captures stay read-only

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* v1.91.114.0 feat(dx): score every error for an AI agent reader (agent-ready error contract + gstack-error-capture)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-17 02:10:12 +00:00

66 lines
4.2 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { DX_ERROR_COMPACT, DX_ERROR_ITEMS } from '../lib/dx-error-contract';
import { DX_ERROR_CONTRACT_PLAN, capFindings, gradeDxContractReview } from './fixtures/dx-error-contract-case';
const SHA = 'a'.repeat(64);
const fence = (rows: Array<Record<string, unknown>>) => [`INPUT: dx ${SHA}`, 'Review prose.', '```gstack-findings', ...rows.map(row => JSON.stringify(row)), '```'].join('\n');
const PASSING = [
{ id: 'F1', severity: 'Critical', title: 'CAP4:P1:redaction --debug prints the bearer token' },
{ id: 'F2', severity: 'Critical', title: 'CAP4:P1:channel push failure exits 0 and blocks on a y/N prompt' },
{ id: 'F3', severity: 'Critical', title: 'CAP4:P1:fix --force skips the overwrite confirmation' },
{ id: 'F4', severity: 'High', title: 'CAP6:P1:context network error is swallowed' },
{ id: 'F5', severity: 'High', title: 'CAP6:P5:fix migrate has no error handling' },
{ id: 'F6', severity: 'Medium', title: 'Getting started takes four steps' },
];
describe('dx-error-contract-review grader (free controls for the paid case)', () => {
test('the fixture plan labels five paths and keeps the decoy complete', () => {
for (const path of ['P1', 'P2', 'P3', 'P4', 'P5']) expect(DX_ERROR_CONTRACT_PLAN).toContain(`### Error path ${path} `);
expect(DX_ERROR_CONTRACT_PLAN).toContain('## Implementation plan');
expect(DX_ERROR_CONTRACT_PLAN).toContain('(PROJECT_NOT_FOUND); fix: run');
});
test('the grader keys on the title and severity shapes the product prompt asks for', () => {
expect(DX_ERROR_COMPACT).toContain('as a Critical finding titled "CAP4:<path>:<item>"');
expect(DX_ERROR_COMPACT).toContain('as a High finding titled "CAP6:<path>:<item>"');
const rows = DX_ERROR_ITEMS.map((item, index) => ({ id: `F${index}`, severity: 'High', title: `CAP6:P1:${item} gap` }));
expect(capFindings(fence(rows)).map(cap => cap.item)).toEqual([...DX_ERROR_ITEMS]);
});
test('a review meeting every bar passes', () => {
const grade = gradeDxContractReview(fence(PASSING), SHA);
expect(grade.reasons).toEqual([]);
expect(grade.pass).toBe(true);
});
test('command-named paths map to their labels', () => {
const rows = PASSING.map(row => ({ ...row, title: String(row.title).replace(':P1:', ':acmesync push:').replace(':P5:', ':acmesync migrate:') }));
expect(gradeDxContractReview(fence(rows), SHA).pass).toBe(true);
});
test.each([
['decoy capped', [...PASSING, { id: 'F7', severity: 'High', title: 'CAP6:P2:trace no trace' }], 'P2 should not be capped'],
['remote API penalized for trace', [...PASSING, { id: 'F7', severity: 'High', title: 'CAP6:P3:trace no stack trace' }], 'P3 should not be capped'],
['usage error capped', [...PASSING, { id: 'F7', severity: 'High', title: 'CAP6:P4:context no versions' }], 'P4 should not be capped'],
['secret leak missed', PASSING.filter(row => row.id !== 'F1'), 'P1 missing CAP4:redaction'],
['unsafe fix missed', PASSING.filter(row => row.id !== 'F3'), 'P1 missing CAP4:fix'],
['context gap missed', PASSING.filter(row => row.id !== 'F4'), 'P1 missing a CAP finding on context'],
['silent path missed', PASSING.filter(row => row.id !== 'F5'), 'P5 (unspecified error handling) not capped'],
['malformed severity on a decoy row', [...PASSING, { id: 'F7', severity: 'Banana', title: 'Looks fine' }], 'result:'],
['CAP4 under-rated', PASSING.map(row => row.id === 'F2' ? { ...row, severity: 'High' } : row), 'CAP4 labelled High, want Critical'],
])('%s fails', (_name, rows, reason) => {
const grade = gradeDxContractReview(fence(rows as Array<Record<string, unknown>>), SHA);
expect(grade.pass).toBe(false);
expect(grade.reasons.join('\n')).toContain(reason);
});
test('a review with no findings fence, or bound to another snapshot, fails', () => {
const unfenced = gradeDxContractReview(`INPUT: dx ${SHA}\nLooks fine to me.`, SHA);
expect(unfenced.pass).toBe(false);
expect(unfenced.reasons.join('\n')).toContain('gstack-findings');
const rebound = gradeDxContractReview(fence(PASSING), 'b'.repeat(64));
expect(rebound.pass).toBe(false);
expect(rebound.reasons[0]).toContain('result: receipt names');
});
});