Diego asked the right question: what if someone edits the Codex side directly? Until now the sync was unidirectional (root → .agents/) but the hook only fired when root SKILL.md was staged — isolated .agents/ edits sneaked past the hook locally and only got caught by CI's --check. A future ``make sync-skill`` would then silently destroy them. Hardened both layers to reject the asymmetric case at the moment of the commit/PR: - Pre-commit hook now classifies the staged set and REJECTS commits that stage .agents/skills/.../SKILL.md in isolation, with a step-by-step message: move the edit into root SKILL.md and re-stage. The hook still auto-syncs when root SKILL.md is staged. - CI gate adds a second step (PR-only) that diffs the PR's base against head; if .agents/ moved but root SKILL.md didn't, fails with an actionable error before the build proceeds. - README documents the direction explicitly: edits land in root SKILL.md; .agents/ is auto-generated; Codex-specific instructions belong in AGENTS.md, not in the SKILL. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
68 lines
2.3 KiB
YAML
68 lines
2.3 KiB
YAML
# SKILL.md sync gate.
|
|
#
|
|
# Why: the root SKILL.md (read by Claude Code) and
|
|
# .agents/skills/oci-deal-accelerator/SKILL.md (read by Codex) MUST
|
|
# stay byte-aligned modulo the auto-generated banner. If they drift,
|
|
# the two agents give the user different instructions — exactly the
|
|
# class of regression that produced the workload-driven-mode incident
|
|
# Diego flagged on 2026-04-25.
|
|
#
|
|
# This workflow blocks the merge if a PR/push leaves them out of
|
|
# sync. Locally, ``make install-hooks`` enables a pre-commit hook
|
|
# that auto-syncs before the commit lands; this CI step is the
|
|
# safety net for anyone who skipped the hook.
|
|
|
|
name: SKILL.md sync
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'SKILL.md'
|
|
- '.agents/skills/oci-deal-accelerator/SKILL.md'
|
|
- 'scripts/sync-skill.py'
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- 'SKILL.md'
|
|
- '.agents/skills/oci-deal-accelerator/SKILL.md'
|
|
- 'scripts/sync-skill.py'
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
check-sync:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 2
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Python
|
|
run: |
|
|
apt-get update -qq
|
|
apt-get install -y --no-install-recommends python3
|
|
python3 --version
|
|
|
|
- name: Verify SKILL.md is in sync with the Codex copy
|
|
run: |
|
|
python3 scripts/sync-skill.py --check
|
|
|
|
- name: Reject .agents/ edits without a matching root SKILL.md change
|
|
if: github.event_name == 'pull_request'
|
|
run: |
|
|
# If only .agents/skills/.../SKILL.md is in the diff (no
|
|
# change to root SKILL.md), someone edited the auto-generated
|
|
# copy directly. That edit will be silently destroyed by the
|
|
# next sync. Block the PR with a clear message.
|
|
base="${{ github.event.pull_request.base.sha }}"
|
|
head="${{ github.event.pull_request.head.sha }}"
|
|
changed=$(git diff --name-only "$base" "$head" || true)
|
|
if echo "$changed" | grep -qx ".agents/skills/oci-deal-accelerator/SKILL.md"; then
|
|
if ! echo "$changed" | grep -qx "SKILL.md"; then
|
|
echo "::error::PR edits .agents/skills/.../SKILL.md without touching root SKILL.md."
|
|
echo "::error::That file is auto-generated. Move the edit to SKILL.md and run 'make sync-skill'."
|
|
exit 1
|
|
fi
|
|
fi
|