mirror of
https://github.com/boshu2/agentops.git
synced 2026-09-14 15:08:13 +08:00
9aff5df0db
One canonical root for skill create/heal/audit. Mode routing table absorbs all heal-skill triggers; every live consumer (gates seed, quality checks, release scripts, bats, docs, catalogs, parity twins) updated to relocated paths; all projections regenerated via owning commands. Validated fresh: verdict PASS (e9b6cdb8), intent 26a4f2be, all five acceptance scenarios re-executed by an independent context. Co-authored-by: Cursor <cursoragent@cursor.com>
331 lines
9.9 KiB
Bash
Executable File
331 lines
9.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# regen-changed-scope.sh - changed-scope derived-artifact repair.
|
|
#
|
|
# This is the narrow default for ordinary slices. It maps changed files to the
|
|
# smallest known generator/checker set and leaves release-wide regeneration to
|
|
# scripts/regen-all.sh.
|
|
#
|
|
# Usage:
|
|
# bash scripts/regen-changed-scope.sh
|
|
# bash scripts/regen-changed-scope.sh --check --scope head
|
|
# bash scripts/regen-changed-scope.sh --list --file skills/plan/SKILL.md
|
|
# bash scripts/regen-changed-scope.sh --files skills/foo/SKILL.md,docs/contracts/x.md
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
MODE="regen"
|
|
SCOPE="worktree"
|
|
LIST_ONLY=false
|
|
DRY_RUN=false
|
|
FILES=()
|
|
|
|
usage() {
|
|
sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'
|
|
}
|
|
|
|
add_file_arg() {
|
|
local raw="$1"
|
|
local part
|
|
local old_ifs="$IFS"
|
|
IFS=,
|
|
for part in $raw; do
|
|
[[ -n "$part" ]] && FILES+=("$part")
|
|
done
|
|
IFS="$old_ifs"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--check) MODE="check" ;;
|
|
--scope)
|
|
shift
|
|
[[ $# -gt 0 ]] || { echo "--scope requires a value" >&2; exit 2; }
|
|
SCOPE="$1"
|
|
;;
|
|
--scope=*) SCOPE="${1#--scope=}" ;;
|
|
--file)
|
|
shift
|
|
[[ $# -gt 0 ]] || { echo "--file requires a path" >&2; exit 2; }
|
|
FILES+=("$1")
|
|
;;
|
|
--files)
|
|
shift
|
|
[[ $# -gt 0 ]] || { echo "--files requires a comma-separated path list" >&2; exit 2; }
|
|
add_file_arg "$1"
|
|
;;
|
|
--files=*) add_file_arg "${1#--files=}" ;;
|
|
--list|--dry-run) LIST_ONLY=true; DRY_RUN=true ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "Unknown arg: $1" >&2; usage >&2; exit 2 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
case "$SCOPE" in
|
|
head|staged|worktree|upstream|auto) ;;
|
|
*) echo "Invalid --scope: $SCOPE" >&2; exit 2 ;;
|
|
esac
|
|
|
|
collect_changed_files() {
|
|
case "$SCOPE" in
|
|
head)
|
|
git diff-tree --no-commit-id --name-only -r HEAD
|
|
;;
|
|
staged)
|
|
git diff --cached --name-only
|
|
;;
|
|
worktree)
|
|
git diff --name-only
|
|
git ls-files --others --exclude-standard
|
|
;;
|
|
upstream)
|
|
local upstream_ref base
|
|
upstream_ref="$(git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null || true)"
|
|
if [[ -n "$upstream_ref" ]]; then
|
|
base="$(git merge-base HEAD "$upstream_ref")"
|
|
git diff --name-only "$base"...HEAD
|
|
else
|
|
git diff-tree --no-commit-id --name-only -r HEAD
|
|
fi
|
|
;;
|
|
auto)
|
|
if [[ -n "$(git diff --cached --name-only)" ]]; then
|
|
git diff --cached --name-only
|
|
elif [[ -n "$(git diff --name-only)" ]]; then
|
|
git diff --name-only
|
|
git ls-files --others --exclude-standard
|
|
else
|
|
git diff-tree --no-commit-id --name-only -r HEAD
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
if [[ "${#FILES[@]}" -eq 0 ]]; then
|
|
while IFS= read -r file; do
|
|
[[ -n "$file" ]] && FILES+=("$file")
|
|
done < <(collect_changed_files | sort -u)
|
|
fi
|
|
|
|
if [[ "${#FILES[@]}" -eq 0 ]]; then
|
|
echo "changed-scope: no changed files; release-wide regen-all not needed"
|
|
exit 0
|
|
fi
|
|
|
|
NEED_CODEX=false
|
|
NEED_CODEX_ALL=false
|
|
NEED_SKILL_MESH=false
|
|
NEED_CLI_REFERENCE=false
|
|
NEED_COMMAND_SURFACES=false
|
|
NEED_CONTRACT_COMPAT=false
|
|
CODEX_SKILLS=()
|
|
SOURCE_SKILLS=()
|
|
STEPS=()
|
|
|
|
add_unique_codex_skill() {
|
|
local skill="$1"
|
|
local existing
|
|
[[ -n "$skill" ]] || return 0
|
|
for existing in "${CODEX_SKILLS[@]}"; do
|
|
[[ "$existing" == "$skill" ]] && return 0
|
|
done
|
|
CODEX_SKILLS+=("$skill")
|
|
}
|
|
|
|
add_unique_source_skill() {
|
|
local skill="$1"
|
|
local existing skill_md="skills/$1/SKILL.md"
|
|
[[ -n "$skill" ]] || return 0
|
|
# Redirect-only runtime packages are compatibility aliases, not independent
|
|
# implementations. They still route Codex/registry/context projections below,
|
|
# but the deep implementation audit would manufacture false output-contract,
|
|
# rubric, and trigger failures for their intentionally tiny pointer bodies.
|
|
if [[ -f "$skill_md" ]] \
|
|
&& grep -Eq '^implementation:[[:space:]]+false([[:space:]]|$)' "$skill_md"; then
|
|
return 0
|
|
fi
|
|
for existing in "${SOURCE_SKILLS[@]}"; do
|
|
[[ "$existing" == "$skill" ]] && return 0
|
|
done
|
|
SOURCE_SKILLS+=("$skill")
|
|
}
|
|
|
|
skill_from_path() {
|
|
local path="$1"
|
|
local rest
|
|
case "$path" in
|
|
skills/*/*)
|
|
rest="${path#skills/}"
|
|
printf '%s\n' "${rest%%/*}"
|
|
;;
|
|
skills-codex/*/*)
|
|
rest="${path#skills-codex/}"
|
|
printf '%s\n' "${rest%%/*}"
|
|
;;
|
|
skills-codex-overrides/*/*)
|
|
rest="${path#skills-codex-overrides/}"
|
|
printf '%s\n' "${rest%%/*}"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
for file in "${FILES[@]}"; do
|
|
case "$file" in
|
|
skills/*)
|
|
NEED_CODEX=true
|
|
source_skill="$(skill_from_path "$file")"
|
|
add_unique_codex_skill "$source_skill"
|
|
[[ -f "skills/$source_skill/SKILL.md" ]] && add_unique_source_skill "$source_skill"
|
|
NEED_SKILL_MESH=true
|
|
;;
|
|
skills-codex/*)
|
|
NEED_CODEX=true
|
|
add_unique_codex_skill "$(skill_from_path "$file")"
|
|
;;
|
|
skills-codex-overrides/*)
|
|
NEED_CODEX=true
|
|
NEED_CODEX_ALL=true
|
|
add_unique_codex_skill "$(skill_from_path "$file")"
|
|
;;
|
|
docs/contracts/context-map.md|docs/reference/agentops-skill-domain-map.md|docs/reference/agentops-skill-graph.md|docs/SKILL-ROUTER.md|docs/SKILLS.md|skills/SKILL-TIERS.md|skills/catalog.json|registry.json)
|
|
NEED_SKILL_MESH=true
|
|
;;
|
|
docs/contracts/bounded-contexts.yaml)
|
|
NEED_CONTRACT_COMPAT=true
|
|
;;
|
|
docs/contracts/*)
|
|
NEED_CONTRACT_COMPAT=true
|
|
;;
|
|
cli/cmd/ao/*)
|
|
NEED_CLI_REFERENCE=true
|
|
NEED_COMMAND_SURFACES=true
|
|
;;
|
|
cli/docs/COMMANDS.md)
|
|
NEED_CLI_REFERENCE=true
|
|
NEED_COMMAND_SURFACES=true
|
|
;;
|
|
docs/cli-surface.json|docs/cli-surface.md|evals/agentops-core/cli-command-surface-matrix.json|evals/agentops-core/fixtures/cli-command-surface-smoke.sh)
|
|
NEED_COMMAND_SURFACES=true
|
|
;;
|
|
esac
|
|
done
|
|
|
|
join_codex_skills() {
|
|
local joined=""
|
|
local skill
|
|
for skill in "${CODEX_SKILLS[@]}"; do
|
|
if [[ -z "$joined" ]]; then
|
|
joined="$skill"
|
|
else
|
|
joined="$joined,$skill"
|
|
fi
|
|
done
|
|
printf '%s\n' "$joined"
|
|
}
|
|
|
|
add_step() {
|
|
STEPS+=("$1")
|
|
}
|
|
|
|
if [[ "${#SOURCE_SKILLS[@]}" -gt 0 ]]; then
|
|
integrity_cmd=""
|
|
for source_skill in "${SOURCE_SKILLS[@]}"; do
|
|
printf -v source_target '%q' "skills/$source_skill"
|
|
if [[ -n "$integrity_cmd" ]]; then
|
|
integrity_cmd+=" && "
|
|
fi
|
|
integrity_cmd+="bash skills/skill-builder/scripts/heal.sh --check --strict $source_target"
|
|
done
|
|
# Changed-scope release proof owns structural package integrity only. Content
|
|
# quality and trigger usefulness are semantic evidence for the independent
|
|
# validator; they must not become a blocking keyword or prose-score gate.
|
|
add_step "changed skill structural integrity|$integrity_cmd|$integrity_cmd"
|
|
fi
|
|
|
|
if $NEED_SKILL_MESH; then
|
|
if [[ "$MODE" == "check" ]]; then
|
|
add_step "skill mesh drift|python3 scripts/generate-skill-mesh.py --check|python3 scripts/generate-skill-mesh.py"
|
|
else
|
|
add_step "skill mesh|python3 scripts/generate-skill-mesh.py|"
|
|
fi
|
|
fi
|
|
|
|
if $NEED_CLI_REFERENCE; then
|
|
if [[ "$MODE" == "check" ]]; then
|
|
add_step "cli reference drift|bash scripts/generate-cli-reference.sh --check|bash scripts/generate-cli-reference.sh"
|
|
else
|
|
add_step "cli reference|bash scripts/generate-cli-reference.sh|"
|
|
fi
|
|
fi
|
|
|
|
if $NEED_COMMAND_SURFACES; then
|
|
if [[ "$MODE" == "check" ]]; then
|
|
add_step "command surfaces drift|bash scripts/regen-command-surfaces.sh --check && bash scripts/check-cmdao-surface-parity.sh|bash scripts/generate-cli-reference.sh && bash scripts/regen-command-surfaces.sh && bash scripts/check-cmdao-surface-parity.sh --write-surface"
|
|
else
|
|
add_step "command surfaces|bash scripts/regen-command-surfaces.sh && bash scripts/check-cmdao-surface-parity.sh --write-surface|"
|
|
fi
|
|
fi
|
|
|
|
if $NEED_CODEX; then
|
|
codex_only="$(join_codex_skills)"
|
|
codex_arg=""
|
|
if [[ -n "$codex_only" && "$NEED_CODEX_ALL" == false ]]; then
|
|
codex_arg=" --only $codex_only"
|
|
fi
|
|
if [[ "$MODE" == "check" ]]; then
|
|
add_step "codex artifact drift|bash scripts/codex-sync.sh --check$codex_arg && bash scripts/regen-codex-hashes.sh --check$codex_arg && bash scripts/validate-codex-generated-artifacts.sh --scope $SCOPE && bash scripts/audit-codex-parity.sh|bash scripts/codex-sync.sh$codex_arg && bash scripts/regen-codex-hashes.sh$codex_arg && bash scripts/validate-codex-generated-artifacts.sh --scope $SCOPE"
|
|
else
|
|
add_step "codex artifacts|bash scripts/codex-sync.sh$codex_arg && bash scripts/regen-codex-hashes.sh$codex_arg && bash scripts/validate-codex-generated-artifacts.sh --scope $SCOPE|"
|
|
fi
|
|
fi
|
|
|
|
if $NEED_CONTRACT_COMPAT; then
|
|
if [[ "$MODE" == "check" ]]; then
|
|
add_step "contract indexes and structural floor|bash scripts/check-contracts-structural-floor.sh && bash scripts/check-contract-compatibility.sh|update docs/contracts/index.md + docs/documentation-index.md, then rerun this changed-scope check"
|
|
else
|
|
add_step "contract indexes and structural floor|bash scripts/check-contracts-structural-floor.sh && bash scripts/check-contract-compatibility.sh|"
|
|
fi
|
|
fi
|
|
|
|
if [[ "${#STEPS[@]}" -eq 0 ]]; then
|
|
echo "changed-scope: no derived-artifact generator selected for ${#FILES[@]} changed file(s)"
|
|
echo "changed-scope: release-wide regen-all not needed"
|
|
exit 0
|
|
fi
|
|
|
|
echo "== changed-scope derived artifact ${MODE} =="
|
|
echo "scope: $SCOPE"
|
|
echo "files: ${#FILES[@]}"
|
|
echo "release-wide regen-all: not selected"
|
|
|
|
fail=0
|
|
for step in "${STEPS[@]}"; do
|
|
IFS='|' read -r label cmd repair_hint <<< "$step"
|
|
if $LIST_ONLY; then
|
|
echo "changed-scope: $label -> $cmd"
|
|
[[ -n "${repair_hint:-}" ]] && echo " repair: $repair_hint"
|
|
continue
|
|
fi
|
|
echo "changed-scope: $label"
|
|
if $DRY_RUN; then
|
|
echo " $cmd"
|
|
continue
|
|
fi
|
|
if bash -lc "$cmd"; then
|
|
echo " ok"
|
|
else
|
|
echo " fail"
|
|
if [[ -n "${repair_hint:-}" ]]; then
|
|
echo " repair: $repair_hint"
|
|
elif [[ "$MODE" == "check" ]]; then
|
|
echo " repair: run 'bash scripts/regen-changed-scope.sh --scope $SCOPE' for the changed-scope repair, not release-wide regen-all"
|
|
fi
|
|
fail=1
|
|
fi
|
|
done
|
|
|
|
exit "$fail"
|