mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +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> * 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>
66 lines
4.2 KiB
TypeScript
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');
|
|
});
|
|
});
|