Files
boshu2__agentops/tests/release-smoke-test.sh
T
Bo 092b99dad9 feat(skills): add skill-eval.sh ms-backed quality gate (ag-yzoz #skill-eval) (#726)
## What

Adds `scripts/skill-eval.sh` — a thin gate that runs one skill's
`SKILL.md` through Jeff Emanuel's `ms` (meta_skill) `lint` + `validate`.
The wrapper decides the gate purely on whether a **blocking** `rule_id`
appears (ms's own severity/exit is not trusted):

- Blocking: `no-secrets`, `no-injection`, `safe-paths`,
`required-metadata`, `no-cycle`, `valid-version` → non-zero exit.
- Everything else → annotated only (`::warning::`), never fails.
- `ms` not on PATH (or `MS_BIN` missing) → **loud `::error::` hard-fail
(exit 3)**, never skip-and-pass — a silent skip would recreate the
no-evaluation gap this gate closes.

Planted `skills/_fixtures/{bad,good}-skill/SKILL.md` fixtures +
`tests/scripts/skill-eval.bats` (10 tests) exercise the
bad/good/ms-absent contracts.

## Fixture scoping

`skills/_fixtures/` is non-skill scaffolding (planted fixtures). The
skill-enumerating gates/generators are taught to skip `skills/_*` so the
fixtures never inflate the skill count (stays 82) or fail registry /
schema / wiring / runtime / count gates. Touched enumerators:
`generate-registry`, `validate-skill-schema`, `sync-skill-counts`,
`validate-skill-count`, `generate-skill-catalog`,
`generate-context-map`, `validate-manifests`, `check-wiring-closure`,
`validate-headless-runtime-skills`, `lib/sku_catalog.py`,
`check-alias-collisions`, `run-all`, `test-runtime-opencode-smoke`,
`release-smoke-test`, `smoke-test`, `validate-skill`.

Does **not** touch `validate.yml` — wiring skill-eval.sh into CI is a
separate bead.

Epic: ag-czzf

## Verification

- `bats tests/scripts/skill-eval.bats` → 10/10 pass (ms 0.1.2 on PATH)
- `shellcheck scripts/skill-eval.sh` → clean
- `check-registry-drift`, `validate-skill-schema`, `sync-skill-counts`,
`run-all`, opencode-smoke → all PASS with fixtures present, skill count
= 82 (fixtures excluded)

Closes-scenario: ag-yzoz#skill-eval
Bounded-context: BC2-Validation
Evidence: scripts/skill-eval.sh

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-06-04 16:38:57 -04:00

127 lines
4.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Release smoke test - verify all skills are loadable
# Usage: ./tests/release-smoke-test.sh [--full]
#
# Default: Fast verification (~30s) - checks components are registered
# --full: Slow verification (~10min) - invokes each skill individually
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
source "$SCRIPT_DIR/claude-code/test-helpers.sh"
# Logging (redefine to avoid conflict with macOS log command)
log() { echo -e "${BLUE}[TEST]${NC} $1"; }
pass() { echo -e "${GREEN} ✓${NC} $1"; }
fail() { echo -e "${RED} ✗${NC} $1"; }
# Expected counts — computed dynamically from skill directories
EXPECTED_SKILLS=$(find "$REPO_ROOT/skills" -maxdepth 2 -name SKILL.md -type f | wc -l | tr -d ' ')
# Parse args
FULL_TEST=false
[[ "${1:-}" == "--full" ]] && FULL_TEST=true
[[ "${1:-}" == "--help" ]] && { echo "Usage: $0 [--full]"; echo " --full Run slow individual tests (~10min)"; exit 0; }
echo ""
echo "═══════════════════════════════════════════"
echo " AgentOps Release Smoke Test"
echo "═══════════════════════════════════════════"
echo ""
if $FULL_TEST; then
# =========================================================================
# FULL TEST: Individual invocation of each skill
# =========================================================================
log "Running FULL test (individual invocations)..."
# Build skills array dynamically from skill directories
SKILLS=()
for skill_dir in "$REPO_ROOT"/skills/*/; do
# Skip leading-underscore scaffolding (e.g. skills/_fixtures/) — planted
# test fixtures, not invokable skills.
case "$(basename "$skill_dir")" in _*) continue ;; esac
[[ -f "${skill_dir}SKILL.md" ]] && SKILLS+=("$(basename "$skill_dir")")
done
passed=0
failed=0
for skill in "${SKILLS[@]}"; do
# Skills need unrestricted tool access — they may use Write, Edit,
# WebFetch, etc. internally. --dangerously-skip-permissions is correct
# here; scoped --allowedTools would cause false failures.
if timeout 45 claude -p "Invoke agentops:$skill skill" \
--plugin-dir "$REPO_ROOT" \
--dangerously-skip-permissions \
--max-turns 3 \
--no-session-persistence \
--max-budget-usd 0.50 \
>/dev/null 2>&1; then
pass "$skill"
((passed++))
else
fail "$skill"
((failed++))
fi
done
print_summary "$passed" "$failed" 0
exit $((failed > 0))
fi
# =============================================================================
# FAST TEST: Single prompt to verify all components are registered
# =============================================================================
log "Running FAST test (registration check)..."
echo ""
log "Checking Claude can load the plugin..."
load_output=$(timeout 30 claude --plugin-dir "$REPO_ROOT" --help 2>&1) || {
fail "Claude plugin load failed"
printf '%s\n' "$load_output" | sed -n '1,40p'
exit 1
}
if printf '%s\n' "$load_output" | grep -qiE "invalid manifest|validation error|failed to load"; then
fail "Claude reported plugin load errors"
printf '%s\n' "$load_output" | grep -iE "invalid|failed|error" | head -5
exit 1
fi
skill_count="$EXPECTED_SKILLS"
echo ""
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
echo "Release Smoke Test Results"
echo -e "${BLUE}───────────────────────────────────────────${NC}"
passed=0
failed=0
# Check skills
if [[ "$skill_count" -ge "$EXPECTED_SKILLS" ]]; then
pass "Skills: $skill_count found (expected $EXPECTED_SKILLS)"
((passed++)) || true
else
fail "Skills: $skill_count found (expected $EXPECTED_SKILLS)"
((failed++)) || true
fi
echo -e "${BLUE}───────────────────────────────────────────${NC}"
echo -e " Total: ${GREEN}$passed passed${NC}, ${RED}$failed failed${NC}"
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
if [[ $failed -gt 0 ]]; then
echo ""
echo -e "${RED}RELEASE BLOCKED${NC}"
exit 1
fi
echo ""
echo -e "${GREEN}RELEASE READY: All components registered${NC}"
exit 0