#!/bin/bash
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLUGIN_ROOT="$(dirname "$SCRIPT_DIR")"
echo "=========================================="
echo "Claude Harness v4 — プラグイン検証テスト"
echo "=========================================="
echo ""
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
PASS_COUNT=0
FAIL_COUNT=0
WARN_COUNT=0
pass_test() { echo -e "${GREEN}✓${NC} $1"; PASS_COUNT=$((PASS_COUNT + 1)); }
fail_test() { echo -e "${RED}✗${NC} $1"; FAIL_COUNT=$((FAIL_COUNT + 1)); }
warn_test() { echo -e "${YELLOW}⚠${NC} $1"; WARN_COUNT=$((WARN_COUNT + 1)); }
echo "📁 [1/7] v4 Go コア構造チェック..."
V4_REQUIRED_FILES=(
"go/go.mod"
"go/cmd/harness/main.go"
"go/cmd/harness/doctor.go"
"go/cmd/harness/sync.go"
"go/cmd/harness/validate.go"
"go/internal/policy/rules.go"
"go/internal/guardrail/pre_tool.go"
"go/internal/policy/post_tool.go"
"go/internal/policy/permission.go"
"go/internal/policy/tampering.go"
"go/internal/hookhandler/setup_hook.go"
"go/internal/hookhandler/stop_session_evaluator.go"
)
for f in "${V4_REQUIRED_FILES[@]}"; do
if [ -f "$PLUGIN_ROOT/$f" ]; then
pass_test "$f"
else
fail_test "$f (存在しない)"
fi
done
echo ""
echo "🎯 [2/7] 5動詞スキルチェック..."
V3_SKILLS=(harness-plan harness-work harness-review harness-release harness-setup)
AUX_V3_SKILLS=(harness-sync)
for skill in "${V3_SKILLS[@]}"; do
skill_dir="$PLUGIN_ROOT/skills/$skill"
skill_md="$skill_dir/SKILL.md"
if [ ! -d "$skill_dir" ]; then
fail_test "skills/$skill/ (ディレクトリなし)"
continue
fi
if [ ! -f "$skill_md" ]; then
fail_test "skills/$skill/SKILL.md (なし)"
continue
fi
if grep -q "^name: $skill$" "$skill_md"; then
pass_test "skills/$skill/SKILL.md (name: $skill)"
else
fail_test "skills/$skill/SKILL.md (name: フィールドが '$skill' でない)"
fi
done
echo ""
echo "🧭 [2.5/7] 補助 workflow surface チェック..."
for skill in "${AUX_V3_SKILLS[@]}"; do
skill_dir="$PLUGIN_ROOT/skills/$skill"
skill_md="$skill_dir/SKILL.md"
if [ ! -d "$skill_dir" ]; then
fail_test "skills/$skill/ (ディレクトリなし)"
continue
fi
if [ ! -f "$skill_md" ]; then
fail_test "skills/$skill/SKILL.md (なし)"
continue
fi
if grep -q "^name: $skill$" "$skill_md"; then
pass_test "skills/$skill/SKILL.md (name: $skill)"
else
fail_test "skills/$skill/SKILL.md (name: フィールドが '$skill' でない)"
fi
done
echo ""
echo "📦 [3/7] Public mirror bundle チェック..."
MIRRORS=(
"codex/.codex/skills"
"opencode/skills"
)
for mirror_dir in "${MIRRORS[@]}"; do
if [ ! -d "$PLUGIN_ROOT/$mirror_dir" ]; then
warn_test "$mirror_dir (存在しない、スキップ)"
continue
fi
for skill in "${V3_SKILLS[@]}"; do
mirror_path="$PLUGIN_ROOT/$mirror_dir/$skill"
if [ ! -d "$mirror_path" ]; then
fail_test "$mirror_dir/$skill (ディレクトリなし)"
continue
fi
if [ -L "$mirror_path" ]; then
fail_test "$mirror_dir/$skill (symlink のまま)"
continue
fi
pass_test "$mirror_dir/$skill (実体ディレクトリ)"
done
for skill in "${AUX_V3_SKILLS[@]}"; do
mirror_path="$PLUGIN_ROOT/$mirror_dir/$skill"
if [ ! -d "$mirror_path" ]; then
fail_test "$mirror_dir/$skill (ディレクトリなし)"
continue
fi
if [ -L "$mirror_path" ]; then
fail_test "$mirror_dir/$skill (symlink のまま)"
continue
fi
pass_test "$mirror_dir/$skill (実体ディレクトリ)"
done
done
if bash "$PLUGIN_ROOT/scripts/sync-skill-mirrors.sh" --check >/dev/null 2>&1; then
pass_test "sync-skill-mirrors.sh --check"
else
fail_test "sync-skill-mirrors.sh --check"
fi
echo ""
echo "🤖 [4/7] エージェントチェック..."
V3_AGENTS=(worker reviewer advisor)
for agent in "${V3_AGENTS[@]}"; do
agent_file="$PLUGIN_ROOT/agents/$agent.md"
if [ -f "$agent_file" ]; then
if grep -q "^name: $agent$" "$agent_file"; then
pass_test "agents/$agent.md (name: $agent)"
else
fail_test "agents/$agent.md (name: フィールドが '$agent' でない)"
fi
else
fail_test "agents/$agent.md (存在しない)"
fi
done
if [ -f "$PLUGIN_ROOT/docs/team-composition.md" ]; then
pass_test "docs/team-composition.md"
else
warn_test "docs/team-composition.md (なし)"
fi
echo ""
echo "🔷 [5/7] Go build / guardrail test..."
GO_DIR="$PLUGIN_ROOT/go"
if [ ! -d "$GO_DIR" ]; then
fail_test "go/ (存在しない)"
else
if (cd "$GO_DIR" && go build ./cmd/harness >/dev/null 2>&1); then
pass_test "go build ./cmd/harness"
else
fail_test "go build ./cmd/harness"
fi
if (cd "$GO_DIR" && go test ./internal/guardrail >/dev/null 2>&1); then
pass_test "go test ./internal/guardrail"
else
fail_test "go test ./internal/guardrail"
fi
fi
echo ""
echo "🪝 [6/7] hooks / runtime チェック..."
HOOK_FILES=(
"hooks/hooks.json"
"bin/harness"
)
for f in "${HOOK_FILES[@]}"; do
if [ -f "$PLUGIN_ROOT/$f" ]; then
pass_test "$f"
else
fail_test "$f (存在しない)"
fi
done
for f in \
"scripts/lib/harness-mem-bridge.sh" \
"scripts/hook-handlers/memory-bridge.sh" \
"scripts/hook-handlers/memory-session-start.sh" \
"scripts/hook-handlers/memory-user-prompt.sh" \
"scripts/hook-handlers/memory-post-tool-use.sh" \
"scripts/hook-handlers/memory-stop.sh" \
"scripts/hook-handlers/memory-codex-notify.sh"
do
if [ -f "$PLUGIN_ROOT/$f" ]; then
pass_test "$f"
else
fail_test "$f (存在しない)"
fi
done
echo ""
echo "🛡️ [7/7] Hardening parity チェック..."
if [ -f "$PLUGIN_ROOT/docs/hardening-parity.md" ]; then
pass_test "docs/hardening-parity.md"
else
fail_test "docs/hardening-parity.md (存在しない)"
fi
if [ -f "$PLUGIN_ROOT/scripts/lib/codex-hardening-contract.txt" ] && grep -q 'HARNESS_HARDENING_CONTRACT_V1' "$PLUGIN_ROOT/scripts/lib/codex-hardening-contract.txt"; then
pass_test "scripts/lib/codex-hardening-contract.txt"
else
fail_test "scripts/lib/codex-hardening-contract.txt (存在しない)"
fi
if grep -q 'docs/hardening-parity.md' "$PLUGIN_ROOT/README.md"; then
pass_test "README.md → hardening parity リンク"
else
fail_test "README.md に hardening parity リンクがない"
fi
for rule_id in \
"R10:no-git-bypass-flags" \
"R11:no-reset-hard-protected-branch" \
"R12:confirm-direct-push-protected-branch" \
"R13:warn-protected-review-paths"
do
if grep -q "$rule_id" "$PLUGIN_ROOT/go/internal/policy/rules.go"; then
pass_test "go/internal/policy/rules.go ($rule_id)"
else
fail_test "go/internal/policy/rules.go ($rule_id がない)"
fi
done
if grep -q 'codex-hardening-contract.txt' "$PLUGIN_ROOT/scripts/codex/codex-exec-wrapper.sh"; then
pass_test "codex-exec-wrapper.sh hardening contract template"
else
fail_test "codex-exec-wrapper.sh が hardening contract template を参照していない"
fi
if grep -q 'codex-hardening-contract.txt' "$PLUGIN_ROOT/scripts/codex-worker-engine.sh"; then
pass_test "codex-worker-engine.sh hardening contract template"
else
fail_test "codex-worker-engine.sh が hardening contract template を参照していない"
fi
if grep -q 'gate_hardening()' "$PLUGIN_ROOT/scripts/codex-worker-quality-gate.sh"; then
pass_test "codex-worker-quality-gate.sh hardening gate"
else
fail_test "codex-worker-quality-gate.sh に hardening gate がない"
fi
echo ""
echo "=========================================="
echo "結果サマリー"
echo "=========================================="
echo -e "${GREEN}✓ 通過${NC}: $PASS_COUNT"
echo -e "${RED}✗ 失敗${NC}: $FAIL_COUNT"
echo -e "${YELLOW}⚠ 警告${NC}: $WARN_COUNT"
echo ""
if [ "$FAIL_COUNT" -gt 0 ]; then
echo -e "${RED}❌ バリデーション失敗: $FAIL_COUNT 件のエラーがあります${NC}"
exit 1
else
echo -e "${GREEN}✅ バリデーション通過${NC}"
exit 0
fi