From b469d1392c6a1f342a6d585b6fe0d93169401fc1 Mon Sep 17 00:00:00 2001 From: mignot Date: Sat, 31 Oct 2026 20:38:33 +0100 Subject: [PATCH 1/2] feat: add reusable agent drift validation --- .github/tests/agent-drift.sh | 17 +++++++++++++++++ .github/workflows/agent-drift.yml | 25 +++++++++++++++++++++++++ .github/workflows/test-actions.yml | 14 ++++++++++++++ AGENTS.md | 5 +++++ CHANGELOG.md | 4 ++++ README.md | 17 +++++++++++++++++ 6 files changed, 82 insertions(+) create mode 100644 .github/tests/agent-drift.sh create mode 100644 .github/workflows/agent-drift.yml create mode 100644 AGENTS.md diff --git a/.github/tests/agent-drift.sh b/.github/tests/agent-drift.sh new file mode 100644 index 0000000..d3ca62b --- /dev/null +++ b/.github/tests/agent-drift.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +engine="${1:?provide the path to check-project-agents.py}" +fixture="$(mktemp -d "${TMPDIR:-/tmp}/agent drift.XXXXXX")" +trap 'rm -rf "$fixture"' EXIT +printf '# Instructions\n' > "$fixture/AGENTS.md" +printf '@AGENTS.md\n' > "$fixture/CLAUDE.md" +mkdir -p "$fixture/.agents/skills/example" "$fixture/.claude/skills" +printf '%s\n' 'Example skill' > "$fixture/.agents/skills/example/SKILL.md" +ln -s ../../.agents/skills/example "$fixture/.claude/skills/example" +python3 "$engine" "$fixture" +printf '%s\n' 'extra instruction' >> "$fixture/CLAUDE.md" +if python3 "$engine" "$fixture"; then + echo 'Drift was not detected' >&2 + exit 1 +fi diff --git a/.github/workflows/agent-drift.yml b/.github/workflows/agent-drift.yml new file mode 100644 index 0000000..6173067 --- /dev/null +++ b/.github/workflows/agent-drift.yml @@ -0,0 +1,25 @@ +name: Agent drift + +on: + workflow_call: + +permissions: + contents: read + +jobs: + agent-drift: + runs-on: ubuntu-latest + steps: + - name: Check out caller + uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Check out public agent-config v1.1.0 + uses: actions/checkout@v5 + with: + repository: BehindTheMusicTree/agent-config + ref: 1e199e26e8c5d8778d94547b96d42d6c075bc7e7 + path: .agent-config-engine + persist-credentials: false + - name: Check project adapters + run: python3 .agent-config-engine/scripts/check-project-agents.py "$GITHUB_WORKSPACE" diff --git a/.github/workflows/test-actions.yml b/.github/workflows/test-actions.yml index fa24cd0..e0f51ba 100644 --- a/.github/workflows/test-actions.yml +++ b/.github/workflows/test-actions.yml @@ -26,3 +26,17 @@ jobs: - name: Run check-api-version tests run: bash .github/actions/check-api-version/tests/check_api_version.sh + + agent-drift-tests: + name: agent drift fixture + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/checkout@v5 + with: + repository: BehindTheMusicTree/agent-config + ref: 1e199e26e8c5d8778d94547b96d42d6c075bc7e7 + path: .agent-config-engine + persist-credentials: false + - name: Verify compliant and divergent fixtures + run: bash .github/tests/agent-drift.sh "$GITHUB_WORKSPACE/.agent-config-engine/scripts/check-project-agents.py" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f1776a3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,5 @@ +# GitHub Workflows + +This repository maintains reusable GitHub Actions workflows and composite actions. Follow [CONTRIBUTING.md](CONTRIBUTING.md) for GitHub Flow, testing, and changelog requirements. Changes to reusable workflows can affect all callers. + +Run `actionlint` for workflow changes and the relevant tests in `.github/actions/*/tests/`. For a release, read `.claude/rules/releases-use-script.md` and follow the documented `.github/scripts/release.sh` procedure. diff --git a/CHANGELOG.md b/CHANGELOG.md index 07f5d0d..f4e3429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,10 @@ All contributors (including maintainers) should update `CHANGELOG.md` when creat ## [Unreleased] +### Added + +- Validate portable agent instructions and skill adapters in caller repositories with a pinned public engine. + ### Changed - Agent rules: migrated `.cursor/rules/*.mdc` to `.claude/rules/*.md` (`globs` → `paths`) and removed the remaining Cursor-specific files and references. diff --git a/README.md b/README.md index a2274e0..589426f 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines ## Table of Contents - [Available Workflows](#available-workflows) + - [Agent drift](#agent-drift) - [Actionlint](#actionlint) - [Call Redeployment Webhook](#call-redeployment-webhook) - [Set Image Tags On Server](#set-image-tags-on-server) @@ -36,6 +37,22 @@ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines ## Available Workflows +### Agent drift + +Checks a caller project that has adopted the portable `AGENTS.md` / `CLAUDE.md` and skill-link conventions. It uses the public agent-config v1.1.0 engine pinned to commit `1e199e26e8c5d8778d94547b96d42d6c075bc7e7`, with read-only repository permissions and no private extension. It checks structure and configured scoped rules; it does not assess the accuracy of instructions or agent runtime loading. + +Call from a workflow on pull requests and pin the reusable workflow to a released version of this repository: + +```yaml +jobs: + agent-drift: + uses: BehindTheMusicTree/github-workflows/.github/workflows/agent-drift.yml@ + permissions: + contents: read +``` + +The caller checkout must contain `AGENTS.md` and a `CLAUDE.md` importing it. The check is read-only. See the [agent-config documentation](https://github.com/BehindTheMusicTree/agent-config) for the adapter contract. + ### Actionlint Runs [actionlint](https://github.com/rhysd/actionlint) on the caller repo. Uses `.github/actionlint.yaml` from the caller if present, so repo-specific ignore rules (e.g. for reusable workflow calls) apply. From c5bbdf8cb7294bfdff315b050118964ccd1f9497 Mon Sep 17 00:00:00 2001 From: mignot Date: Sat, 31 Oct 2026 20:40:18 +0100 Subject: [PATCH 2/2] test: cover drift in scoped project rules --- .github/tests/agent-drift.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/tests/agent-drift.sh b/.github/tests/agent-drift.sh index d3ca62b..5ce9bf9 100644 --- a/.github/tests/agent-drift.sh +++ b/.github/tests/agent-drift.sh @@ -15,3 +15,15 @@ if python3 "$engine" "$fixture"; then echo 'Drift was not detected' >&2 exit 1 fi +printf '@AGENTS.md\n' > "$fixture/CLAUDE.md" +mkdir -p "$fixture/.agents/rules" "$fixture/.claude/rules" +printf '%s\n' 'Python rule' > "$fixture/.agents/rules/python.md" +printf '%s\n' '{"python":{"source":".agents/rules/python.md","paths":["**/*.py"]}}' > "$fixture/.agents/scopes.json" +printf '%s\n' 'Read .agents/rules/python.md for **/*.py' >> "$fixture/AGENTS.md" +printf '%s\n' '---' 'paths:' ' - "**/*.py"' '---' '' 'Python rule' > "$fixture/.claude/rules/python.md" +python3 "$engine" "$fixture" +printf '%s\n' 'modified rule' > "$fixture/.claude/rules/python.md" +if python3 "$engine" "$fixture"; then + echo 'Scoped rule drift was not detected' >&2 + exit 1 +fi