diff --git a/skills-manifest.json b/skills-manifest.json index 4b844c1240..caf80df57b 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -58,7 +58,7 @@ "files": 132 }, "pr-to-video": { - "hash": "922c2714750d5a54", + "hash": "41171bbed1c5d8f4", "files": 30 }, "product-launch-video": { diff --git a/skills/pr-to-video/scripts/workflow-guardrails.test.mjs b/skills/pr-to-video/scripts/workflow-guardrails.test.mjs index 61a591fcd0..e73dc74f64 100644 --- a/skills/pr-to-video/scripts/workflow-guardrails.test.mjs +++ b/skills/pr-to-video/scripts/workflow-guardrails.test.mjs @@ -141,6 +141,54 @@ test("code frames without an upstream-selected excerpt fail before dispatch", () ); }); +// The other half of this skill's frame-packets delta. The excerpt guard above is +// pinned; the code-vocabulary injection was not — deleting `codeVocabularySection` +// outright left all 455 skills tests green, so the section a code worker reads to +// pick its registry block could have been dropped silently. +test("a code frame carries the vocabulary excerpt for the block it names", () => { + const project = mkdtempSync(join(tmpdir(), "p2v-packets-vocab-")); + write(join(project, "frame.md"), "# frame\n"); + write( + join(project, "STORYBOARD.md"), + `---\nformat: 1920x1080\n---\n\n## Frame 1 — Diff\n\n- duration: 4s\n- src: compositions/frames/01-diff.html\n- focal: code-diff\n\n### Source excerpt\n\n\`\`\`diff\n-oldCall()\n+newCall()\n\`\`\`\n`, + ); + + const [packet] = buildFramePackets({ projectDir: project }); + const contents = readFileSync(packet.path, "utf8"); + + assert.match(contents, /## Code block excerpt \(code-diff\)/); + assert.match(contents, /`code-diff`/); +}); + +test("a code block the vocabulary does not describe still names itself for install", () => { + const project = mkdtempSync(join(tmpdir(), "p2v-packets-vocab-miss-")); + write(join(project, "frame.md"), "# frame\n"); + write( + join(project, "STORYBOARD.md"), + `---\nformat: 1920x1080\n---\n\n## Frame 1 — Diff\n\n- duration: 4s\n- src: compositions/frames/01-diff.html\n- focal: code-not-in-the-vocabulary\n\n### Source excerpt\n\n\`\`\`diff\n-oldCall()\n+newCall()\n\`\`\`\n`, + ); + + const [packet] = buildFramePackets({ projectDir: project }); + + assert.match( + readFileSync(packet.path, "utf8"), + /## Code block\n\nUse registry block `code-not-in-the-vocabulary`\./, + ); +}); + +test("a mechanism frame gets no code-block section at all", () => { + const project = mkdtempSync(join(tmpdir(), "p2v-packets-mechanism-")); + write(join(project, "frame.md"), "# frame\n"); + write( + join(project, "STORYBOARD.md"), + `---\nformat: 1920x1080\n---\n\n## Frame 1 — Mechanism\n\n- duration: 4s\n- src: compositions/frames/01-mechanism.html\n- focal: the request-lifecycle flow\n`, + ); + + const [packet] = buildFramePackets({ projectDir: project }); + + assert.doesNotMatch(readFileSync(packet.path, "utf8"), /## Code block/); +}); + test("CLI capability detection rejects skills newer than the available command surface", () => { const stableHelp = `Project:\n lint Validate a composition\n snapshot Capture frames\n\nUnknown command check`; const currentHelp = `Project:\n lint Validate a composition\n check Run the full project validation gate\n snapshot Capture frames`;