Files
PaperClipAI/packages/paperclip-runner/test/capability-contract.test.mjs
DottaandPaperclip ed42cb2be3 Use one declared skill source list for both capability inventories
Include the issue-document reference in the normative inventory and calibrate coverage of every declared source without changing each inventory’s existing heading parsing scope.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-02 16:48:49 -05:00

67 lines
3.4 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import { resolve } from "node:path";
import { decodeInventory } from "../scripts/lib/capability-inventory.mjs";
import { validateRows } from "../scripts/generate-capability-contract.mjs";
const phaseDirectory = resolve(import.meta.dirname, "../generated/capability");
async function readRows(file) {
return JSON.parse(await readFile(resolve(phaseDirectory, file), "utf8")).rows;
}
test("generated Capability inventory has full source coverage", async () => {
const [capabilities, tools, evals] = await Promise.all([
readRows("capabilities.yaml"),
readRows("mcp-tool-map.yaml"),
readRows("eval-traceability.yaml"),
]);
assert.equal(capabilities.length, 161);
assert.equal(capabilities.filter(row => row.sourceAnchor.startsWith("skills/paperclip/references/issue-documents.md#")).length, 3);
assert.equal(tools.length, 42);
assert.equal(evals.length, 106);
assert.equal(new Set(evals.map((row) => row.group)).size, 16);
for (const row of [...capabilities, ...tools, ...evals]) {
assert.match(row.sourceAnchor, /\S/);
assert.match(row.semanticOperation, /\S/);
assert.match(row.expectedMockState, /\S/);
}
});
test("contract validation rejects missing, duplicate, and unclassified entries", () => {
const row = {
id: "example:1",
sourceAnchor: "source.md#L1:example",
primaryDisposition: "control_plane_owned",
};
assert.throws(() => validateRows([{ ...row, sourceAnchor: "" }], "fixture"), /source anchor/);
assert.throws(() => validateRows([row, { ...row, id: "example:2" }], "fixture"), /duplicate source anchor/);
assert.throws(() => validateRows([{ ...row, primaryDisposition: "unclassified" }], "fixture"), /valid primary disposition/);
});
test("conversational answer guidance has the same agent-operation classification in both inventories", async () => {
const generated = (await readRows("capabilities.yaml")).filter(row => row.heading === "Conversational confirmation answers");
const spec = decodeInventory(await readFile(resolve(import.meta.dirname, "../spec/capability/capabilities.yaml"), "utf8")).rows
.filter(row => row.title === "Conversational confirmation answers");
assert.equal(generated.length, 2);
assert.equal(spec.length, 2);
for (const row of [...generated, ...spec]) assert.equal(row.primaryDisposition, "always_agent_tool");
for (const row of generated) assert.equal(row.semanticOperation, "call_api");
});
test("both capability inventories cover every declared skill source", async () => {
const contract = JSON.parse(await readFile(resolve(import.meta.dirname, "../spec/capability/source-contract.json"), "utf8"));
const generated = await readRows("capabilities.yaml");
const normative = decodeInventory(await readFile(resolve(import.meta.dirname, "../spec/capability/capabilities.yaml"), "utf8"));
assert.deepEqual(normative.generatedFrom, contract.skillSources);
for (const source of contract.skillSources) {
assert.ok(generated.some(row => row.sourceAnchor.startsWith(`${source}#`)), `Missing generated source ${source}`);
assert.ok(normative.rows.some(row => row.sourceAnchor.startsWith(`${source}:`)), `Missing normative source ${source}`);
}
assert.equal(normative.rows.filter(row => row.sourceAnchor.startsWith("skills/paperclip/references/issue-documents.md:")).length, 3);
});