SKILL.md sync: pre-commit hook + CI gate + make install-hooks
Three-layer defense to keep root SKILL.md (Claude Code) and .agents/skills/oci-deal-accelerator/SKILL.md (Codex) byte-aligned. Without this, Claude Code and Codex can drift and give the user contradictory instructions — exactly the failure mode that produced the workload-driven-mode incident on 2026-04-25. Layers (least → most enforcement): 1. Local pre-commit hook (.githooks/pre-commit, opt-in via ``make install-hooks`` which sets core.hooksPath). When SKILL.md is staged, auto-runs scripts/sync-skill.py and stages the regenerated .agents/ copy in the same commit. Idempotent. 2. ``make install-hooks`` target + a tip line in ``make venv``'s output so any new clone discovers the hook setup. 3. CI gate (.gitea/workflows/skill-sync.yaml) on every push/PR touching SKILL.md or the .agents/ copy. Runs ``scripts/sync-skill.py --check`` and fails the build on drift. Catches anyone who skipped the hook. README documents the rationale and the three layers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -567,3 +567,5 @@ KB lives under `kb/`. See [kb/README.md](kb/README.md) for the directory map, fr
|
|||||||
- You do NOT claim features exist if you're unsure. Check the KB first.
|
- You do NOT claim features exist if you're unsure. Check the KB first.
|
||||||
- You do NOT do detailed project management. DELIVER artifacts are lightweight handover aids.
|
- You do NOT do detailed project management. DELIVER artifacts are lightweight handover aids.
|
||||||
- You do NOT add services or components the user did not request.
|
- You do NOT add services or components the user did not request.
|
||||||
|
|
||||||
|
<!-- test marker for sync hook -->
|
||||||
|
|||||||
49
.gitea/workflows/skill-sync.yaml
Normal file
49
.gitea/workflows/skill-sync.yaml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# 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
|
||||||
54
.githooks/pre-commit
Normal file
54
.githooks/pre-commit
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Pre-commit hook: keep .agents/skills/oci-deal-accelerator/SKILL.md
|
||||||
|
# byte-aligned with the root SKILL.md (the source of truth that
|
||||||
|
# CLAUDE.md / Claude Code reads). The Codex copy under .agents/
|
||||||
|
# follows the same content with an auto-generated banner — they MUST
|
||||||
|
# stay in lock-step or Claude Code and Codex will give the user
|
||||||
|
# different instructions.
|
||||||
|
#
|
||||||
|
# Behavior:
|
||||||
|
# • If SKILL.md is staged but the .agents/ copy is not synced,
|
||||||
|
# auto-run scripts/sync-skill.py and stage the result so the
|
||||||
|
# commit ships both files together.
|
||||||
|
# • If the script can't run (no python, etc.), abort with a clear
|
||||||
|
# message rather than committing a desync silently.
|
||||||
|
#
|
||||||
|
# Install once per clone: make install-hooks (sets core.hooksPath)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(git rev-parse --show-toplevel)"
|
||||||
|
SOURCE="SKILL.md"
|
||||||
|
TARGET=".agents/skills/oci-deal-accelerator/SKILL.md"
|
||||||
|
SCRIPT="scripts/sync-skill.py"
|
||||||
|
|
||||||
|
# Only act when SKILL.md is part of the staged change set.
|
||||||
|
if ! git diff --cached --name-only | grep -qx "$SOURCE"; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$ROOT/$SCRIPT" ]; then
|
||||||
|
echo "pre-commit: $SCRIPT missing — cannot verify SKILL.md sync. Aborting." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PYTHON="$(command -v python3 || command -v python || true)"
|
||||||
|
if [ -z "$PYTHON" ]; then
|
||||||
|
echo "pre-commit: no python found in PATH — cannot run $SCRIPT. Aborting." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the full sync, then stage the regenerated target so it travels
|
||||||
|
# in the same commit. (Idempotent — if it was already in sync, the
|
||||||
|
# file write is a no-op.)
|
||||||
|
"$PYTHON" "$ROOT/$SCRIPT" >&2
|
||||||
|
git add "$ROOT/$TARGET"
|
||||||
|
|
||||||
|
# Final check — if for any reason the result still drifts, fail loud.
|
||||||
|
if ! "$PYTHON" "$ROOT/$SCRIPT" --check >/dev/null 2>&1; then
|
||||||
|
echo "pre-commit: SKILL.md and $TARGET are still out of sync after auto-run." >&2
|
||||||
|
echo " Investigate scripts/sync-skill.py before committing." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
11
Makefile
11
Makefile
@@ -1,6 +1,6 @@
|
|||||||
# OCI Deal Accelerator — Build Automation
|
# OCI Deal Accelerator — Build Automation
|
||||||
|
|
||||||
.PHONY: help install test validate example diagram deck full clean lint codex-package update-icons freshness freshness-refresh sync-skill sku-discover pptx-icons-refresh archcenter-benchmark-20 diagram-lookup diagram-validate-spec archcenter-descriptions-refresh diagram-spec-audit archcenter-smoke
|
.PHONY: help install test validate example diagram deck full clean lint codex-package update-icons freshness freshness-refresh sync-skill sku-discover pptx-icons-refresh archcenter-benchmark-20 diagram-lookup diagram-validate-spec archcenter-descriptions-refresh diagram-spec-audit archcenter-smoke install-hooks
|
||||||
|
|
||||||
# Use venv if present, otherwise find best available python3
|
# Use venv if present, otherwise find best available python3
|
||||||
ifneq (,$(wildcard .venv/bin/python))
|
ifneq (,$(wildcard .venv/bin/python))
|
||||||
@@ -25,6 +25,15 @@ venv: ## Create virtual environment and install dependencies
|
|||||||
.venv/bin/pip install --upgrade pip
|
.venv/bin/pip install --upgrade pip
|
||||||
.venv/bin/pip install -r requirements.txt
|
.venv/bin/pip install -r requirements.txt
|
||||||
@echo "Virtual environment ready. Run: source .venv/bin/activate"
|
@echo "Virtual environment ready. Run: source .venv/bin/activate"
|
||||||
|
@echo "Tip: run 'make install-hooks' once to enable the pre-commit"
|
||||||
|
@echo " hook that keeps SKILL.md in sync with the Codex copy."
|
||||||
|
|
||||||
|
install-hooks: ## Install repo-versioned git hooks (pre-commit auto-syncs SKILL.md)
|
||||||
|
@if [ ! -d .git ]; then echo "install-hooks: not a git repo"; exit 1; fi
|
||||||
|
git config core.hooksPath .githooks
|
||||||
|
@echo "install-hooks: core.hooksPath set to .githooks"
|
||||||
|
@echo " → pre-commit now auto-syncs .agents/skills/.../SKILL.md"
|
||||||
|
@echo " whenever SKILL.md is part of the staged change set."
|
||||||
|
|
||||||
install: ## Install Python dependencies (system-wide)
|
install: ## Install Python dependencies (system-wide)
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -452,6 +452,26 @@ For temporary overrides (e.g., focusing on a specific customer), create `AGENTS.
|
|||||||
|
|
||||||
Full setup details: [`codex/README.md`](codex/README.md)
|
Full setup details: [`codex/README.md`](codex/README.md)
|
||||||
|
|
||||||
|
#### Keeping the Claude and Codex skills in sync
|
||||||
|
|
||||||
|
The root `SKILL.md` (read by Claude Code) and `.agents/skills/oci-deal-accelerator/SKILL.md` (read by Codex) MUST stay byte-aligned modulo an auto-generated banner. If they drift, the two agents give the user different instructions.
|
||||||
|
|
||||||
|
Three layers of defense, run from least to most enforcement:
|
||||||
|
|
||||||
|
1. **Local pre-commit hook (recommended once per clone):**
|
||||||
|
```bash
|
||||||
|
make install-hooks
|
||||||
|
```
|
||||||
|
Sets `core.hooksPath = .githooks`. From then on, any commit that touches `SKILL.md` auto-runs `scripts/sync-skill.py` and stages the regenerated `.agents/` copy in the same commit. No manual step required.
|
||||||
|
|
||||||
|
2. **Manual sync (anytime):**
|
||||||
|
```bash
|
||||||
|
make sync-skill # write the .agents/ copy from SKILL.md
|
||||||
|
make lint # check (also runs sync --check)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **CI gate (`.gitea/workflows/skill-sync.yaml`):** every push/PR runs `python scripts/sync-skill.py --check` and fails the build if the two files have drifted. Catches anyone who skipped the hook.
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
### ECAL Completeness (see `docs/ecal-gaps-backlog.md` for full list)
|
### ECAL Completeness (see `docs/ecal-gaps-backlog.md` for full list)
|
||||||
|
|||||||
Reference in New Issue
Block a user