fix(specs): add frontmatter to the shipped vault-user skill (#115)

* fix(specs): give the vault-user skill the frontmatter every shipped skill needs (OPR.0.6.1.6)

vault-user/SKILL.md was the only shipped SKILL.md without a name/description
frontmatter block, so it failed the daemon's own parseSkillFrontmatter
contract and external skill syncers rejected it. Adds name + description and
leaves the body byte-identical.

Adds a regression check that runs parseSkillFrontmatter over every SKILL.md
under packages/daemon/specs and packages/daemon/assets, the two skill roots
scripts/build-package.sh copies into the npm artifact.

* test(daemon): correct the shipped-skill check's context-packs scope note (OPR.0.6.1.6)

The projection's default source is packages/daemon/specs/agents/shared/skills,
already inside the checked specs root. Manifest validation is not SKILL.md
frontmatter validation. Comment only; test behavior unchanged.

---------

Co-authored-by: v-openrig-build <v-openrig-build@users.noreply.github.com>
This commit is contained in:
Mike Schwarz
2026-09-28 19:21:34 -07:00
committed by GitHub
co-authored by v-openrig-build
parent 57380a250e
commit ea52fa8f83
2 changed files with 56 additions and 0 deletions
@@ -1,3 +1,8 @@
---
name: vault-user
description: Use when checking the health of this rig's HashiCorp Vault or writing, reading, listing, deleting or explaining its secrets.
---
# Vault User
You have access to a HashiCorp Vault instance managed by this rig's environment.
@@ -0,0 +1,51 @@
// OPR.0.6.1.6 — every SKILL.md the package ships must pass the daemon's own
// frontmatter contract (parseSkillFrontmatter: `---` block with name +
// description, non-empty body). scripts/build-package.sh copies
// packages/daemon/specs and packages/daemon/assets wholesale into the npm
// artifact, so those are the roots checked here. The package-time
// context-packs projection is generated from packages/daemon/specs/agents/
// shared/skills (generate-context-packs.mjs), so its source skills are
// covered by the specs root.
import { describe, it, expect } from "vitest";
import { readFileSync, readdirSync, existsSync } from "node:fs";
import { join, relative, resolve } from "node:path";
import { parseSkillFrontmatter } from "../src/domain/skill-discovery.js";
const PACKAGE_ROOT = resolve(import.meta.dirname, "..");
const SHIPPED_ROOTS = ["specs", "assets"].map((r) => join(PACKAGE_ROOT, r));
function findSkillFiles(dir: string): string[] {
const out: string[] = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = join(dir, entry.name);
if (entry.isDirectory()) out.push(...findSkillFiles(full));
else if (entry.isFile() && entry.name === "SKILL.md") out.push(full);
}
return out;
}
describe("shipped SKILL.md frontmatter", () => {
const skillFiles = SHIPPED_ROOTS.filter((r) => existsSync(r)).flatMap(findSkillFiles);
it("finds shipped skills under every shipped root", () => {
for (const root of SHIPPED_ROOTS) {
expect(skillFiles.some((f) => f.startsWith(root + "/")), `no SKILL.md under ${root}`).toBe(true);
}
});
it("every shipped SKILL.md passes parseSkillFrontmatter", () => {
const failures = skillFiles.flatMap((file) => {
const result = parseSkillFrontmatter(readFileSync(file, "utf8"));
return result.ok ? [] : [`${relative(PACKAGE_ROOT, file)}: ${result.reason}`];
});
expect(failures).toEqual([]);
});
it("rejects a shipped skill whose frontmatter is removed", () => {
const vault = join(PACKAGE_ROOT, "specs/agents/apps/vault-specialist/skills/vault-user/SKILL.md");
const stripped = readFileSync(vault, "utf8").replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, "");
const result = parseSkillFrontmatter(stripped);
expect(result.ok).toBe(false);
});
});