* fix(db): remove unique constraint on folder to support multi-channel agents * ci: implement automated skill drift detection and self-healing PRs * fix: align registration logic with Gavriel's feedback and fix build/test issues from Daniel Mi * style: conform to prettier standards for CI validation * test: fix branch naming inconsistency in CI (master vs main) * fix(ci): robust module resolution by removing file extensions in scripts * refactor(ci): simplify skill validation by removing redundant combination tests * style: conform skills-engine to prettier, unify logging in index.ts and cleanup unused imports * refactor: extract multi-channel DB changes to separate branch Move channel column, folder suffix logic, and related migrations to feat/multi-channel-db-v2 for independent review. This PR now contains only CI/CD optimizations, Prettier formatting, and logging improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
152 lines
5.2 KiB
YAML
152 lines
5.2 KiB
YAML
name: Skill PR Validation
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- '.claude/skills/**'
|
|
- 'skills-engine/**'
|
|
|
|
jobs:
|
|
# ── Job 1: Policy gate ────────────────────────────────────────────────
|
|
# Block PRs that add NEW skill files while also modifying source code.
|
|
# Skill PRs should contain instructions for Claude, not raw source edits.
|
|
policy-check:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Check for mixed skill + source changes
|
|
run: |
|
|
ADDED_SKILLS=$(git diff --name-only --diff-filter=A origin/main...HEAD \
|
|
| grep '^\\.claude/skills/' || true)
|
|
CHANGED=$(git diff --name-only origin/main...HEAD)
|
|
SOURCE=$(echo "$CHANGED" \
|
|
| grep -E '^src/|^container/|^package\.json|^package-lock\.json' || true)
|
|
|
|
if [ -n "$ADDED_SKILLS" ] && [ -n "$SOURCE" ]; then
|
|
echo "::error::PRs that add new skills should not modify source files."
|
|
echo ""
|
|
echo "New skill files:"
|
|
echo "$ADDED_SKILLS"
|
|
echo ""
|
|
echo "Source files:"
|
|
echo "$SOURCE"
|
|
echo ""
|
|
echo "Please split into separate PRs. See CONTRIBUTING.md."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Comment on failure
|
|
if: failure()
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: context.issue.number,
|
|
body: `This PR adds a skill while also modifying source code. A skill PR should not change source files—the skill should contain **instructions** for Claude to follow.
|
|
|
|
If you're fixing a bug or simplifying code, please submit that as a separate PR.
|
|
|
|
See [CONTRIBUTING.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/CONTRIBUTING.md) for details.`
|
|
})
|
|
|
|
# ── Job 2: Detect which skills changed ────────────────────────────────
|
|
detect-changed:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
skills: ${{ steps.detect.outputs.skills }}
|
|
has_skills: ${{ steps.detect.outputs.has_skills }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Detect changed skills
|
|
id: detect
|
|
run: |
|
|
CHANGED_SKILLS=$(git diff --name-only origin/main...HEAD \
|
|
| grep '^\\.claude/skills/' \
|
|
| sed 's|^\.claude/skills/||' \
|
|
| cut -d/ -f1 \
|
|
| sort -u \
|
|
| jq -R . | jq -s .)
|
|
echo "skills=$CHANGED_SKILLS" >> "$GITHUB_OUTPUT"
|
|
if [ "$CHANGED_SKILLS" = "[]" ]; then
|
|
echo "has_skills=false" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "has_skills=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
echo "Changed skills: $CHANGED_SKILLS"
|
|
|
|
# ── Job 3: Validate each changed skill in isolation ───────────────────
|
|
validate-skills:
|
|
needs: detect-changed
|
|
if: needs.detect-changed.outputs.has_skills == 'true'
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
skill: ${{ fromJson(needs.detect-changed.outputs.skills) }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
- run: npm ci
|
|
|
|
- name: Initialize skills system
|
|
run: >-
|
|
npx tsx -e
|
|
"import { initNanoclawDir } from './skills-engine/index'; initNanoclawDir();"
|
|
|
|
- name: Apply skill
|
|
run: npx tsx scripts/apply-skill.ts ".claude/skills/${{ matrix.skill }}"
|
|
|
|
- name: Typecheck after apply
|
|
run: npx tsc --noEmit
|
|
|
|
- name: Run skill tests
|
|
run: |
|
|
TEST_CMD=$(npx tsx -e "
|
|
import { parse } from 'yaml';
|
|
import fs from 'fs';
|
|
const m = parse(fs.readFileSync('.claude/skills/${{ matrix.skill }}/manifest.yaml', 'utf-8'));
|
|
if (m.test) console.log(m.test);
|
|
")
|
|
if [ -n "$TEST_CMD" ]; then
|
|
echo "Running: $TEST_CMD"
|
|
eval "$TEST_CMD"
|
|
else
|
|
echo "No test command defined, skipping"
|
|
fi
|
|
|
|
# ── Summary gate for branch protection ────────────────────────────────
|
|
skill-validation-summary:
|
|
needs:
|
|
- policy-check
|
|
- detect-changed
|
|
- validate-skills
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Check results
|
|
run: |
|
|
echo "policy-check: ${{ needs.policy-check.result }}"
|
|
echo "validate-skills: ${{ needs.validate-skills.result }}"
|
|
|
|
if [ "${{ needs.policy-check.result }}" = "failure" ]; then
|
|
echo "::error::Policy check failed"
|
|
exit 1
|
|
fi
|
|
if [ "${{ needs.validate-skills.result }}" = "failure" ]; then
|
|
echo "::error::Skill validation failed"
|
|
exit 1
|
|
fi
|
|
echo "All skill checks passed"
|