mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
* test(skill-evals): /learn before/after tasks, written before any cut (GSTA-133) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(learnings): --help, unknown-flag errors and --stats on the learnings bins; cross-project search no longer drops every project when the state root path contains the slug Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * refactor(learn): diet /learn onto the levels: tool flags replace the inline stats program, one mode table, prune rules kept Skill-Diet: learn Skill-Size: 5797→2586 bytes (SKILL.md.tmpl) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(write-skill): /write-skill skill, gstack-skill-bill, static skill-to-bin drift ratchet, shared skill eval contract Skill-Size: 0→3245 bytes (write-skill/SKILL.md.tmpl) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(learn): guard the state-root lookup in Prune; re-measure the learn ceiling Skill-Size: 2586→2707 bytes (learn/SKILL.md.tmpl) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (drift scanner, learnings flags, skill-bill baselines, eval fixtures) - skill-bin-drift: a redirect no longer ends a command, so `bin/x 2>/dev/null --bogus` is checked - gstack-learnings-search: a missing value or a non-positive --limit exits 2 like an unknown flag - gstack-skill-bill: the gstack root skill is a checkout skill; Skill-Diet trailers match the exact name; the baseline counts the committed bytes untrimmed - skill-evals task files get a free contract test and are registered in FREE_FIXTURES, with the drift allowlist - learn eval: the no-code-change safety task no longer asks the model to fix code (its check penalised obeying the user) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(learn,write-skill): full bin paths in the mode table; prune asks per entry unless the request names the action Skill-Diet: learn Skill-Size: 2707→2955 bytes (learn/SKILL.md.tmpl) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): /write-skill, gstack-skill-bill, drift ratchet, eval contract, /learn diet Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: drift ratchet in CONTRIBUTING's static tests; /learn prune example asks per entry Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(skill-bill): report a checkout skill's eval task file and check it against the contract The skill-evals task-file test now exercises lib/skill-bill.ts's checkEvalTasks, with a negative control, instead of a test-only validator (test-of-test ratchet). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(skill-bill): EVAL TASKS path uses forward slashes on every platform; a non-object task entry is a problem, not a crash Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(learn): BASH_COMPAT guard on in-window heredocs; count skill-e2e-write-skill in the paid census 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>
63 lines
2.7 KiB
TypeScript
63 lines
2.7 KiB
TypeScript
import { describe, test, expect, afterAll } from 'bun:test';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import * as os from 'os';
|
|
import { buildSkillBill, checkEvalTasks, renderSkillBill } from '../lib/skill-bill';
|
|
|
|
/**
|
|
* Task files for the shared skill eval (docs/designs/SKILL_EVAL_CONTRACT.md) live at
|
|
* test/fixtures/skill-evals/<skill>/tasks.json. gstack-skill-bill reports them for a checkout
|
|
* skill through checkEvalTasks; this test holds every committed file to the contract's format.
|
|
*/
|
|
const ROOT = path.resolve(import.meta.dir, '..');
|
|
const DIR = path.join(ROOT, 'test', 'fixtures', 'skill-evals');
|
|
const skills = fs.readdirSync(DIR).filter(d => fs.statSync(path.join(DIR, d)).isDirectory()).sort();
|
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-eval-tasks-'));
|
|
afterAll(() => fs.rmSync(tmp, { recursive: true, force: true }));
|
|
|
|
describe('skill-evals task files follow the shared eval contract', () => {
|
|
test('there is at least one task file', () => {
|
|
expect(skills.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
for (const skill of skills) {
|
|
test(`${skill}/tasks.json`, () => {
|
|
const r = checkEvalTasks(path.join(DIR, skill, 'tasks.json'));
|
|
expect(r.problems).toEqual([]);
|
|
expect(r.count).toBeGreaterThan(0);
|
|
});
|
|
}
|
|
|
|
test('a malformed task file is reported, one problem per defect', () => {
|
|
const file = path.join(tmp, 'tasks.json');
|
|
fs.writeFileSync(file, JSON.stringify([
|
|
{ id: 'Bad Id', prompt: '' },
|
|
{ id: 'dup', prompt: 'x', must: ['('], extra: 1 },
|
|
{ id: 'dup', prompt: 'y', check: 'true', setup: 'sh "$SKILL_EVAL_FIXTURES/seed.sh"', safety: 'yes' },
|
|
1,
|
|
['nested'],
|
|
]));
|
|
expect(checkEvalTasks(file).problems).toEqual([
|
|
'Bad Id: id must be kebab-case',
|
|
'Bad Id: prompt must be a non-empty string',
|
|
'Bad Id: needs must or check, or nothing can fail it',
|
|
'dup: unknown key extra',
|
|
'dup: must pattern /(/ does not compile',
|
|
'dup: duplicate id',
|
|
'dup: safety must be a boolean',
|
|
'dup: setup runs seed.sh but it is missing',
|
|
'?: task must be an object, got 1',
|
|
'?: task must be an object, got ["nested"]',
|
|
]);
|
|
fs.writeFileSync(file, '{');
|
|
expect(checkEvalTasks(file).problems[0]).toStartWith('not valid JSON');
|
|
});
|
|
|
|
test('gstack-skill-bill shows a checkout skill its eval tasks', () => {
|
|
const report = buildSkillBill(path.join(ROOT, 'learn'));
|
|
const count = JSON.parse(fs.readFileSync(path.join(DIR, 'learn', 'tasks.json'), 'utf8')).length;
|
|
expect(report.evalTasks).toEqual({ path: 'test/fixtures/skill-evals/learn/tasks.json', count, problems: [] });
|
|
expect(renderSkillBill(report)).toContain(`EVAL TASKS: test/fixtures/skill-evals/learn/tasks.json (${count} tasks)`);
|
|
});
|
|
});
|