mirror of
https://github.com/garrytan/gstack.git
synced 2026-08-22 05:57:18 +02:00
test: dedupe coverage gates; route both walks through skill-census
skill-coverage-floor duplicated two matrix assertions (registry completeness, gate-tier floor) with a DIFFERENT hand-rolled directory walk — matrix's skipped nothing, floor's skipped node_modules/docs/test. Two 'same' gates disagreeing on the census is the bug class test/helpers/skill-census.ts was written to kill. Registry assertions now live in matrix only (with floor's better error message), both files walk via skillCensus().authoredSkills, and floor keeps the per-skill structural checks it owns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
da30a3cb5a
commit
3e12d8a26a
@@ -19,6 +19,7 @@ import { describe, test, expect } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { SKILL_COVERAGE } from './skill-coverage-matrix';
|
||||
import { skillCensus } from './helpers/skill-census';
|
||||
|
||||
const REPO_ROOT = path.resolve(import.meta.dir, '..');
|
||||
|
||||
@@ -31,46 +32,14 @@ function readSkillMd(skill: string): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
function listSkillDirs(): string[] {
|
||||
const entries = fs.readdirSync(REPO_ROOT, { withFileTypes: true });
|
||||
return entries
|
||||
.filter(e => e.isDirectory() && !e.name.startsWith('.'))
|
||||
.filter(e => e.name !== 'node_modules' && e.name !== 'docs' && e.name !== 'test')
|
||||
.filter(e => fs.existsSync(path.join(REPO_ROOT, e.name, 'SKILL.md')))
|
||||
.map(e => e.name)
|
||||
.sort();
|
||||
}
|
||||
// Registry-completeness assertions ("every skill on disk is registered",
|
||||
// "every entry has a gate test") live in test/skill-coverage-matrix.test.ts —
|
||||
// they were duplicated here with a DIFFERENT hand-rolled directory walk, which
|
||||
// is the divergence class test/helpers/skill-census.ts exists to kill. This
|
||||
// file owns the per-skill structural compliance checks only.
|
||||
|
||||
describe('skill-coverage-floor: every skill passes structural compliance', () => {
|
||||
const skills = listSkillDirs();
|
||||
|
||||
test('skill registry mentions every skill on disk', () => {
|
||||
const onDisk = new Set(skills);
|
||||
const inRegistry = new Set(Object.keys(SKILL_COVERAGE));
|
||||
const missingFromRegistry: string[] = [];
|
||||
for (const s of onDisk) {
|
||||
if (!inRegistry.has(s)) missingFromRegistry.push(s);
|
||||
}
|
||||
if (missingFromRegistry.length > 0) {
|
||||
throw new Error(
|
||||
`Skills on disk missing from test/skill-coverage-matrix.ts: ${missingFromRegistry.join(', ')}. ` +
|
||||
`Add an entry to SKILL_COVERAGE with at least 'test/skill-coverage-floor.test.ts' in gate[].`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('every registry entry has at least one gate-tier test', () => {
|
||||
const missingGate: string[] = [];
|
||||
for (const [skill, coverage] of Object.entries(SKILL_COVERAGE)) {
|
||||
if (!coverage.gate || coverage.gate.length === 0) missingGate.push(skill);
|
||||
}
|
||||
if (missingGate.length > 0) {
|
||||
throw new Error(
|
||||
`Skills with no gate-tier eval: ${missingGate.join(', ')}. ` +
|
||||
`Eval-first foundation requires at least one CI-blocking check per skill.`,
|
||||
);
|
||||
}
|
||||
});
|
||||
const skills = skillCensus(REPO_ROOT).authoredSkills;
|
||||
|
||||
test('every gate-tier test path referenced in registry exists on disk', () => {
|
||||
const missing: string[] = [];
|
||||
|
||||
@@ -8,18 +8,17 @@
|
||||
*/
|
||||
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { SKILL_COVERAGE, type SkillCoverage } from './skill-coverage-matrix';
|
||||
import { skillCensus } from './helpers/skill-census';
|
||||
|
||||
const REPO_ROOT = path.resolve(import.meta.dir, '..');
|
||||
|
||||
// Canonical walk (skill-census.ts). This file and skill-coverage-floor
|
||||
// previously hand-rolled two DIFFERENT walks (one skipped node_modules/docs/
|
||||
// test, one didn't) — exactly the divergence class the census exists to kill.
|
||||
function discoverSkills(): string[] {
|
||||
return fs.readdirSync(REPO_ROOT, { withFileTypes: true })
|
||||
.filter(e => e.isDirectory() && !e.name.startsWith('.'))
|
||||
.filter(e => fs.existsSync(path.join(REPO_ROOT, e.name, 'SKILL.md')))
|
||||
.map(e => e.name)
|
||||
.sort();
|
||||
return skillCensus(REPO_ROOT).authoredSkills;
|
||||
}
|
||||
|
||||
describe('skill coverage matrix', () => {
|
||||
@@ -29,16 +28,23 @@ describe('skill coverage matrix', () => {
|
||||
});
|
||||
|
||||
test('every entry has the right shape', () => {
|
||||
const missingGate: string[] = [];
|
||||
for (const [skill, coverage] of Object.entries(SKILL_COVERAGE)) {
|
||||
expect(Array.isArray(coverage.gate)).toBe(true);
|
||||
expect(Array.isArray(coverage.periodic)).toBe(true);
|
||||
expect(coverage.gate.length).toBeGreaterThan(0);
|
||||
if (!coverage.gate || coverage.gate.length === 0) missingGate.push(skill);
|
||||
for (const p of [...coverage.gate, ...coverage.periodic]) {
|
||||
expect(typeof p).toBe('string');
|
||||
expect(p.startsWith('test/')).toBe(true);
|
||||
expect(p.endsWith('.test.ts')).toBe(true);
|
||||
}
|
||||
}
|
||||
if (missingGate.length > 0) {
|
||||
throw new Error(
|
||||
`Skills with no gate-tier eval: ${missingGate.join(', ')}. ` +
|
||||
`Eval-first foundation requires at least one CI-blocking check per skill.`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('every skill on disk has a registry entry', () => {
|
||||
|
||||
Reference in New Issue
Block a user