Files
msd-core/src/eval.cts
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

75 lines
3.2 KiB
TypeScript

/**
* Deterministic eval scoring verb (#10).
* Moves coverage/infra/overall arithmetic out of the msd-eval-auditor prompt
* into code, per the framework's code-delegation discipline.
*/
interface EvalScoreResult {
coverage_score: number;
infra_score: number;
overall_score: number;
verdict: string;
}
function parseFlag(args: string[], flag: string): string | undefined {
const i = args.indexOf(flag);
return i >= 0 && i + 1 < args.length ? args[i + 1] : undefined;
}
const INFRA_VALUE: Record<string, number> = { ok: 1, partial: 0.5, missing: 0 };
const INFRA_TOKENS = new Set(Object.keys(INFRA_VALUE));
function computeEvalScore(covered: number, total: number, infra: string[]): EvalScoreResult {
const coverage = total > 0 ? (covered / total) * 100 : 0;
// unknown/typo tokens are treated as `missing` (score 0) by design — upstream agent only passes ok|partial|missing
const infraSum = infra.reduce((acc, s) => acc + (INFRA_VALUE[s.trim().toLowerCase()] ?? 0), 0);
const infraScore = (infraSum / 5) * 100;
const overall = coverage * 0.6 + infraScore * 0.4;
const round = (n: number) => Math.round(n * 100) / 100;
const o = round(overall);
const verdict =
o >= 80 ? 'PRODUCTION READY' :
o >= 60 ? 'NEEDS WORK' :
o >= 40 ? 'SIGNIFICANT GAPS' : 'NOT IMPLEMENTED';
return { coverage_score: round(coverage), infra_score: round(infraScore), overall_score: o, verdict };
}
function cmdEvalScore(_cwd: string, args: string[], raw: boolean): void {
const coveredRaw = parseFlag(args, '--covered');
const totalRaw = parseFlag(args, '--total');
const infraRaw = parseFlag(args, '--infra') || '';
const infra = infraRaw ? infraRaw.split(',').map((s) => s.trim().toLowerCase()) : [];
const covered = Number(coveredRaw);
const total = Number(totalRaw);
if (
coveredRaw === undefined || coveredRaw.trim() === '' ||
totalRaw === undefined || totalRaw.trim() === '' ||
!Number.isFinite(covered) || !Number.isFinite(total) ||
infra.length !== 5
) {
process.stderr.write('Usage: msd-tools query eval.score --covered N --total N --infra a,b,c,d,e (each ok|partial|missing)\n');
process.exitCode = 1;
return;
}
// Domain validation: this is a public CLI verb, so reject out-of-domain inputs
// rather than emit nonsense (covered>total -> coverage_score>100; negatives ->
// negative scores). Counts must be non-negative integers and covered cannot
// exceed total; infra tokens must match the documented ok|partial|missing set.
if (!Number.isInteger(covered) || !Number.isInteger(total) || covered < 0 || total < 0 || covered > total) {
process.stderr.write('Invalid eval.score domain: require integer counts with 0 <= covered <= total.\n');
process.exitCode = 1;
return;
}
const invalidInfra = infra.find((s) => !INFRA_TOKENS.has(s));
if (invalidInfra !== undefined) {
process.stderr.write(`Invalid eval.score infra token: ${invalidInfra || '<empty>'}. Expected ok|partial|missing.\n`);
process.exitCode = 1;
return;
}
const result = computeEvalScore(covered, total, infra);
process.stdout.write(raw ? JSON.stringify(result) : JSON.stringify(result, null, 2));
process.stdout.write('\n');
}
export = { cmdEvalScore, computeEvalScore };