From 40d4c13c4da49df801651c9424abcaf8a43f03d9 Mon Sep 17 00:00:00 2001 From: Yigtwxx Date: Fri, 17 Jul 2026 23:39:23 +0300 Subject: [PATCH] fix(skills): load ray-data and ray-train by quoting YAML extras Both SKILL.md files declared dependencies as [ray[data], ...] and [ray[train], ...]. In a YAML flow sequence the inner [ opens a nested sequence, so gray-matter/js-yaml cannot parse the frontmatter. The skill loader catches the parse failure and drops both skills, so they never appear in `skill list`, the / picker, or the Skills panel, with no user-visible error. Quote the extras so each entry is a plain scalar. Add a test that parses every bundled SKILL.md and validates its skill frontmatter, so a malformed block fails CI instead of silently dropping a skill. Fixes #187 --- .../skills/data-engineering/ray-data/SKILL.md | 2 +- .../cli/skills/ml-training/ray-train/SKILL.md | 2 +- backend/cli/test/skill/bundled-skills.test.ts | 40 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 backend/cli/test/skill/bundled-skills.test.ts diff --git a/backend/cli/skills/data-engineering/ray-data/SKILL.md b/backend/cli/skills/data-engineering/ray-data/SKILL.md index 6365e87d..3098a515 100644 --- a/backend/cli/skills/data-engineering/ray-data/SKILL.md +++ b/backend/cli/skills/data-engineering/ray-data/SKILL.md @@ -6,7 +6,7 @@ version: 1.0.0 author: Synthetic Sciences license: MIT tags: [Data Processing, Ray Data, Distributed Computing, ML Pipelines, Batch Inference, ETL, Scalable, Ray, PyTorch, TensorFlow] -dependencies: [ray[data], pyarrow, pandas] +dependencies: ["ray[data]", pyarrow, pandas] --- # Ray Data - Scalable ML Data Processing diff --git a/backend/cli/skills/ml-training/ray-train/SKILL.md b/backend/cli/skills/ml-training/ray-train/SKILL.md index 2770e49e..d2a1590d 100644 --- a/backend/cli/skills/ml-training/ray-train/SKILL.md +++ b/backend/cli/skills/ml-training/ray-train/SKILL.md @@ -6,7 +6,7 @@ version: 1.0.0 author: Synthetic Sciences license: MIT tags: [Ray Train, Distributed Training, Synthetic Sciencestion, Ray, Hyperparameter Tuning, Fault Tolerance, Elastic Scaling, Multi-Node, PyTorch, TensorFlow] -dependencies: [ray[train], torch, transformers] +dependencies: ["ray[train]", torch, transformers] --- # Ray Train - Distributed Training Synthetic Sciencestion diff --git a/backend/cli/test/skill/bundled-skills.test.ts b/backend/cli/test/skill/bundled-skills.test.ts new file mode 100644 index 00000000..21a36847 --- /dev/null +++ b/backend/cli/test/skill/bundled-skills.test.ts @@ -0,0 +1,40 @@ +import { test, expect } from "bun:test" +import { ConfigMarkdown } from "../../src/config/markdown" +import { Skill } from "../../src/skill" +import path from "path" + +// Same fields the loader validates in Skill.compute() before accepting a skill. +const Frontmatter = Skill.Info.pick({ name: true, description: true, category: true, tags: true, entry: true }) + +const root = path.join(import.meta.dir, "..", "..", "skills") +const files = await Array.fromAsync(new Bun.Glob("**/SKILL.md").scan({ cwd: root, absolute: true })) + +// Every bundled SKILL.md that declares a frontmatter block must produce a +// loadable skill. Malformed YAML (e.g. the unquoted `ray[train]` extras in +// issue #187) leaves the parser with empty data, so the loader silently drops +// the skill and it never appears in `skill list`. Category READMEs carry no +// frontmatter block and are intentionally skipped. +test("every bundled skill with frontmatter loads", async () => { + expect(files.length).toBeGreaterThan(0) + const broken = await Promise.all( + files.map(async (file) => { + const raw = await Bun.file(file).text() + if (!raw.startsWith("---")) return undefined + const parsed = await ConfigMarkdown.parse(file) + return Frontmatter.safeParse(parsed.data).success ? undefined : path.relative(root, file) + }), + ) + expect(broken.filter(Boolean)).toEqual([]) +}) + +test("ray-train frontmatter parses with quoted extras", async () => { + const parsed = await ConfigMarkdown.parse(path.join(root, "ml-training", "ray-train", "SKILL.md")) + expect(parsed.data.name).toBe("ray-train") + expect(parsed.data.dependencies[0]).toBe("ray[train]") +}) + +test("ray-data frontmatter parses with quoted extras", async () => { + const parsed = await ConfigMarkdown.parse(path.join(root, "data-engineering", "ray-data", "SKILL.md")) + expect(parsed.data.name).toBe("ray-data") + expect(parsed.data.dependencies[0]).toBe("ray[data]") +})