From 431c7e51e92a3f566500c740b3419e3592ff60df Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sun, 15 Feb 2026 20:05:26 +0100 Subject: [PATCH 1/3] test: add trigger-check coverage for private debug skills --- tests/installer/adopted-debug-skills.test.ts | 81 +++++++++++++++++++ .../private-skills-legacy-trigger.test.ts | 38 +++++++++ 2 files changed, 119 insertions(+) create mode 100644 tests/installer/adopted-debug-skills.test.ts create mode 100644 tests/installer/private-skills-legacy-trigger.test.ts diff --git a/tests/installer/adopted-debug-skills.test.ts b/tests/installer/adopted-debug-skills.test.ts new file mode 100644 index 0000000..237470d --- /dev/null +++ b/tests/installer/adopted-debug-skills.test.ts @@ -0,0 +1,81 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; + +const repoRoot = process.cwd(); +const checkerScript = path.join(repoRoot, "scripts", "skill-trigger-check.mjs"); + +function runChecker(skillPath: string): { ok: true; stdout: string } | { ok: false; stdout: string; stderr: string } { + try { + const stdout = execFileSync(process.execPath, [checkerScript, "--skill", skillPath], { + cwd: repoRoot, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }); + return { ok: true, stdout }; + } catch (error) { + const err = error as { stdout?: string | Buffer; stderr?: string | Buffer }; + return { + ok: false, + stdout: typeof err.stdout === "string" ? err.stdout : (err.stdout?.toString("utf8") ?? ""), + stderr: typeof err.stderr === "string" ? err.stderr : (err.stderr?.toString("utf8") ?? ""), + }; + } +} + +const adoptedSkills = [ + { + name: "systematic-debugging", + path: path.join(repoRoot, "private-skills", "skills", "systematic-debugging", "SKILL.md"), + expectedPhrases: [ + "NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST", + "## Acceptance Tests", + "Positive trigger", + "Negative trigger", + "Behavior", + ], + }, + { + name: "parallel-debugging", + path: path.join(repoRoot, "private-skills", "skills", "parallel-debugging", "SKILL.md"), + expectedPhrases: [ + "Analysis of Competing Hypotheses", + "## Acceptance Tests", + "Positive trigger", + "Negative trigger", + "Behavior", + ], + }, + { + name: "gh-fix-ci", + path: path.join(repoRoot, "private-skills", "skills", "gh-fix-ci", "SKILL.md"), + expectedPhrases: [ + "GitHub Actions", + "gh pr checks", + "## Acceptance Tests", + "Positive trigger", + "Negative trigger", + "Behavior", + ], + }, +]; + +for (const skill of adoptedSkills) { + test(`adopted skill exists: ${skill.name}`, () => { + assert.equal(fs.existsSync(skill.path), true, `${skill.name} should exist at ${skill.path}`); + }); + + test(`adopted skill passes trigger checks: ${skill.name}`, () => { + const result = runChecker(skill.path); + assert.equal(result.ok, true, `${skill.name} should pass skill-trigger-check`); + }); + + test(`adopted skill includes TDD acceptance coverage: ${skill.name}`, () => { + const text = fs.readFileSync(skill.path, "utf8"); + for (const phrase of skill.expectedPhrases) { + assert.match(text, new RegExp(phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i")); + } + }); +} diff --git a/tests/installer/private-skills-legacy-trigger.test.ts b/tests/installer/private-skills-legacy-trigger.test.ts new file mode 100644 index 0000000..2aed3a2 --- /dev/null +++ b/tests/installer/private-skills-legacy-trigger.test.ts @@ -0,0 +1,38 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; + +const repoRoot = process.cwd(); +const checkerScript = path.join(repoRoot, "scripts", "skill-trigger-check.mjs"); + +function runChecker(skillPath: string): { ok: true; stdout: string } | { ok: false; stdout: string; stderr: string } { + try { + const stdout = execFileSync(process.execPath, [checkerScript, "--skill", skillPath], { + cwd: repoRoot, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }); + return { ok: true, stdout }; + } catch (error) { + const err = error as { stdout?: string | Buffer; stderr?: string | Buffer }; + return { + ok: false, + stdout: typeof err.stdout === "string" ? err.stdout : (err.stdout?.toString("utf8") ?? ""), + stderr: typeof err.stderr === "string" ? err.stderr : (err.stderr?.toString("utf8") ?? ""), + }; + } +} + +const legacySkills = [ + path.join(repoRoot, "private-skills", "skills", "audit-pr-skills", "SKILL.md"), + path.join(repoRoot, "private-skills", "skills", "rebuild-skill-index", "SKILL.md"), +]; + +for (const skillPath of legacySkills) { + const skillName = path.basename(path.dirname(skillPath)); + test(`legacy private skill passes trigger check: ${skillName}`, () => { + const result = runChecker(skillPath); + assert.equal(result.ok, true, `${skillName} should pass skill-trigger-check`); + }); +} From ec06d8ff38259cf73f03ba2e7884ab020bd4e042 Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sun, 15 Feb 2026 20:05:52 +0100 Subject: [PATCH 2/3] chore: bump version to 12.3.1 --- CHANGELOG.md | 9 +++++++++ VERSION | 2 +- src/VERSION | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8893082..aa6c943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [12.3.1] - 2026-02-15 + +### Added +- Installer trigger-check regression tests for adopted private debug skills (`systematic-debugging`, `parallel-debugging`, `gh-fix-ci`). +- Installer trigger-check regression tests for legacy private skills (`audit-pr-skills`, `rebuild-skill-index`) after schema modernization. + +### Changed +- Aligned `src/VERSION` with root `VERSION` for release consistency. + ## [12.3.0] - 2026-02-15 ### Added diff --git a/VERSION b/VERSION index 4d23cb8..9c028e2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -12.3.0 +12.3.1 diff --git a/src/VERSION b/src/VERSION index 6853326..9c028e2 100644 --- a/src/VERSION +++ b/src/VERSION @@ -1 +1 @@ -12.2.0 +12.3.1 From 900e33399c3bc27abc968e2e205b3da5cba47e86 Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sun, 15 Feb 2026 20:07:41 +0100 Subject: [PATCH 3/3] test: skip private-skill checks when source is absent --- tests/installer/adopted-debug-skills.test.ts | 8 +++++--- tests/installer/private-skills-legacy-trigger.test.ts | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/installer/adopted-debug-skills.test.ts b/tests/installer/adopted-debug-skills.test.ts index 237470d..7864818 100644 --- a/tests/installer/adopted-debug-skills.test.ts +++ b/tests/installer/adopted-debug-skills.test.ts @@ -6,6 +6,8 @@ import { execFileSync } from "node:child_process"; const repoRoot = process.cwd(); const checkerScript = path.join(repoRoot, "scripts", "skill-trigger-check.mjs"); +const privateSkillsRoot = path.join(repoRoot, "private-skills", "skills"); +const hasPrivateSkills = fs.existsSync(privateSkillsRoot); function runChecker(skillPath: string): { ok: true; stdout: string } | { ok: false; stdout: string; stderr: string } { try { @@ -63,16 +65,16 @@ const adoptedSkills = [ ]; for (const skill of adoptedSkills) { - test(`adopted skill exists: ${skill.name}`, () => { + test(`adopted skill exists: ${skill.name}`, { skip: !hasPrivateSkills }, () => { assert.equal(fs.existsSync(skill.path), true, `${skill.name} should exist at ${skill.path}`); }); - test(`adopted skill passes trigger checks: ${skill.name}`, () => { + test(`adopted skill passes trigger checks: ${skill.name}`, { skip: !hasPrivateSkills }, () => { const result = runChecker(skill.path); assert.equal(result.ok, true, `${skill.name} should pass skill-trigger-check`); }); - test(`adopted skill includes TDD acceptance coverage: ${skill.name}`, () => { + test(`adopted skill includes TDD acceptance coverage: ${skill.name}`, { skip: !hasPrivateSkills }, () => { const text = fs.readFileSync(skill.path, "utf8"); for (const phrase of skill.expectedPhrases) { assert.match(text, new RegExp(phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i")); diff --git a/tests/installer/private-skills-legacy-trigger.test.ts b/tests/installer/private-skills-legacy-trigger.test.ts index 2aed3a2..5a5b4ff 100644 --- a/tests/installer/private-skills-legacy-trigger.test.ts +++ b/tests/installer/private-skills-legacy-trigger.test.ts @@ -1,10 +1,13 @@ import test from "node:test"; import assert from "node:assert/strict"; +import fs from "node:fs"; import path from "node:path"; import { execFileSync } from "node:child_process"; const repoRoot = process.cwd(); const checkerScript = path.join(repoRoot, "scripts", "skill-trigger-check.mjs"); +const privateSkillsRoot = path.join(repoRoot, "private-skills", "skills"); +const hasPrivateSkills = process.env.ICA_REQUIRE_PRIVATE_SKILLS === "1" || fs.existsSync(privateSkillsRoot); function runChecker(skillPath: string): { ok: true; stdout: string } | { ok: false; stdout: string; stderr: string } { try { @@ -31,7 +34,7 @@ const legacySkills = [ for (const skillPath of legacySkills) { const skillName = path.basename(path.dirname(skillPath)); - test(`legacy private skill passes trigger check: ${skillName}`, () => { + test(`legacy private skill passes trigger check: ${skillName}`, { skip: !hasPrivateSkills }, () => { const result = runChecker(skillPath); assert.equal(result.ok, true, `${skillName} should pass skill-trigger-check`); });