FN-7194: verify Compound Engineering workflow skills

Add regression coverage proving the built-in Compound Engineering workflow resolves and installs every referenced skill.

- Collect skill-bearing nodes from the built-in Compound Engineering workflow, including optional-group template children.\n- Assert ce-plan, ce-work, ce-code-review, and follow-on workflow skills map to bundled SKILL.md files.\n- Verify bundled CE skill installation places each workflow-named skill on the discovery root used by sessions.\n\nFiles changed:\n .../__tests__/ce-workflow-skill-alignment.test.ts  | 106 +++++++++++++++++++++\n 1 file changed, 106 insertions(+)

Fusion-Task-Id: FN-7194

Fusion-Task-Lineage: 8461ebae-1484-4e77-a9ae-e18531e617b5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-06-28 09:12:13 -07:00
parent 8d3c15eb10
commit 68d2d41f37

View File

@@ -0,0 +1,106 @@
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { BUILTIN_WORKFLOWS, type WorkflowIrNode } from "@fusion/core";
import { installBundledCeSkills, resolveBundledSkillsRoot } from "../skill-installation.js";
/*
FNXC:CompoundEngineering 2026-06-28-08:58:
FN-7145 requires workflow-named Compound Engineering skills to load as real resources, not only appear in prompt text. This regression test ties every `builtin:compound-engineering` skill node, including optional-group children, to a bundled and installable `SKILL.md` so ce-plan, ce-work, ce-code-review, and follow-on CE skills remain discoverable by the step session.
*/
type CeWorkflowSkillNode = {
nodePath: string;
skillName: string;
bareSkillId: string;
};
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function bareSkillId(skillName: string): string {
return skillName.includes(":") ? skillName.slice(skillName.lastIndexOf(":") + 1) : skillName;
}
function templateNodes(node: WorkflowIrNode): WorkflowIrNode[] {
const template = isRecord(node.config?.template) ? node.config.template : undefined;
const nodes = isRecord(template) ? template.nodes : undefined;
return Array.isArray(nodes) ? nodes.filter((candidate): candidate is WorkflowIrNode => isRecord(candidate) && typeof candidate.id === "string" && typeof candidate.kind === "string") : [];
}
function collectSkillNodes(nodes: readonly WorkflowIrNode[], parentPath: readonly string[] = []): CeWorkflowSkillNode[] {
return nodes.flatMap((node) => {
const nodePathParts = [...parentPath, node.id];
const skillName = typeof node.config?.skillName === "string" ? node.config.skillName.trim() : "";
const direct = skillName
? [
{
nodePath: nodePathParts.join(" > "),
skillName,
bareSkillId: bareSkillId(skillName),
},
]
: [];
return [...direct, ...collectSkillNodes(templateNodes(node), nodePathParts)];
});
}
function compoundEngineeringWorkflowSkillNodes(): CeWorkflowSkillNode[] {
const workflow = BUILTIN_WORKFLOWS.find((candidate) => candidate.id === "builtin:compound-engineering");
if (!workflow) throw new Error("builtin:compound-engineering workflow not found");
return collectSkillNodes(workflow.ir.nodes);
}
function frontmatterName(content: string): string | undefined {
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/m)?.[1] ?? "";
const rawName = frontmatter.match(/^name:\s*(.+?)\s*$/m)?.[1];
return rawName?.replace(/^['\"]|['\"]$/g, "");
}
const workflowSkillNodes = compoundEngineeringWorkflowSkillNodes();
const workflowBareSkillIds = workflowSkillNodes.map((node) => node.bareSkillId);
describe("built-in Compound Engineering workflow skill alignment", () => {
const tmpTargets: string[] = [];
afterEach(() => {
for (const target of tmpTargets) rmSync(target, { recursive: true, force: true });
tmpTargets.length = 0;
});
it("derives every skill-bearing node, including optional-group template children", () => {
expect(workflowSkillNodes.map((node) => [node.nodePath, node.skillName])).toEqual([
["plan", "compound-engineering:ce-plan"],
["ce-doc-review > ce-doc-review-step", "compound-engineering:ce-doc-review"],
["execute", "compound-engineering:ce-work"],
["code-review", "compound-engineering:ce-code-review"],
["commit-pr", "compound-engineering:ce-commit-push-pr"],
["resolve-feedback", "compound-engineering:ce-resolve-pr-feedback"],
["document", "compound-engineering:ce-compound"],
]);
expect(workflowBareSkillIds).toEqual(expect.arrayContaining(["ce-plan", "ce-work", "ce-code-review"]));
});
it.each(workflowSkillNodes)("$nodePath resolves $bareSkillId to a bundled SKILL.md source", ({ bareSkillId }) => {
const skillMd = join(resolveBundledSkillsRoot(), bareSkillId, "SKILL.md");
expect(existsSync(skillMd)).toBe(true);
expect(frontmatterName(readFileSync(skillMd, "utf-8"))).toBe(bareSkillId);
});
it("installs every workflow-named skill onto the discovery root scanned by CE sessions", () => {
const targetRoot = mkdtempSync(join(tmpdir(), "ce-workflow-skill-alignment-"));
tmpTargets.push(targetRoot);
const { results } = installBundledCeSkills({ targetRoot });
for (const { bareSkillId } of workflowSkillNodes) {
const result = results.find((candidate) => candidate.skillId === bareSkillId);
expect(result).toMatchObject({ skillId: bareSkillId, outcome: "installed" });
const installedSkillMd = join(targetRoot, bareSkillId, "SKILL.md");
expect(existsSync(installedSkillMd)).toBe(true);
expect(frontmatterName(readFileSync(installedSkillMd, "utf-8"))).toBe(bareSkillId);
}
});
});