#!/usr/bin/env bash # shellcheck disable=SC2015 # pass()/fail() always exit 0; A && pass || fail is safe here set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PASS=0 FAIL=0 pass() { echo " PASS: $1"; PASS=$((PASS + 1)); } fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); } # Returns 0 if pattern found in file contains() { grep -qE "$1" "$2" 2>/dev/null; } # ─── 0004: providers/claude-code/CLAUDE.md rewrite ─────────────────────────── # # Automated: structure and distinctive concepts only. # Behavioral tests (does the agent actually follow the rules?) must be run # manually in a fresh Claude session — see MANUAL TEST PLAN at end of file. echo "--- 0004: CLAUDE.md rewrite ---" CLAUDE="$REPO_ROOT/providers/claude-code/CLAUDE.md" [[ -f "$CLAUDE" ]] \ && pass "CLAUDE.md exists" \ || { fail "CLAUDE.md missing"; } # Communication + Behavior rules moved to core/AGENTS.md by issue 0015 — verified in 0015 section. # providers/claude-code/CLAUDE.md is now a thin adapter; these rules must NOT be inline here. ! contains "Answer directly" "$CLAUDE" \ && pass "communication: rules not duplicated in CLAUDE.md (moved to core/AGENTS.md)" \ || fail "communication: rules still inline in CLAUDE.md — 0015 refactor incomplete" ! contains "Reads, searches" "$CLAUDE" \ && pass "behavior: rules not duplicated in CLAUDE.md (moved to core/AGENTS.md)" \ || fail "behavior: rules still inline in CLAUDE.md — 0015 refactor incomplete" # Content index lives in core/AGENTS.md (deployed as ~/.agents/AGENTS.md), not in CLAUDE.md CORE_AGENTS_FOR_0004="$REPO_ROOT/core/AGENTS.md" contains "coding" "$CORE_AGENTS_FOR_0004" \ && pass "content index: coding conventions trigger present (core/AGENTS.md)" \ || fail "content index: coding conventions trigger missing from core/AGENTS.md" contains "git" "$CORE_AGENTS_FOR_0004" \ && pass "content index: git conventions trigger present (core/AGENTS.md)" \ || fail "content index: git conventions trigger missing from core/AGENTS.md" contains "testing" "$CORE_AGENTS_FOR_0004" \ && pass "content index: testing conventions trigger present (core/AGENTS.md)" \ || fail "content index: testing conventions trigger missing from core/AGENTS.md" # global.md must be retired — no longer referenced in content index ! contains "global\.md" "$CLAUDE" \ && pass "content index: global.md reference removed" \ || fail "content index: global.md still referenced" # global.md file must be deleted [[ ! -f "$REPO_ROOT/core/instructions/global.md" ]] \ && pass "core/instructions/global.md deleted" \ || fail "core/instructions/global.md still exists" echo "" # ─── 0005: core/instructions/coding.md ─────────────────────────────────────── echo "--- 0005: coding.md ---" CODING="$REPO_ROOT/core/instructions/coding.md" [[ -f "$CODING" ]] \ && pass "coding.md exists" \ || fail "coding.md missing" contains "[Aa]utomat" "$CODING" \ && pass "rule: automate repeatable things" \ || fail "rule: automate repeatable things missing" contains "[Cc]omment" "$CODING" \ && pass "rule: no comments unless why is non-obvious" \ || fail "rule: comment rule missing" contains "[Dd]efensive" "$CODING" \ && pass "rule: no defensive code at internal boundaries" \ || fail "rule: defensive code rule missing" contains "[Ee]xplicit" "$CODING" \ && pass "rule: prefer explicit over implicit" \ || fail "rule: explicit over implicit missing" contains "[Aa]bstraction" "$CODING" \ && pass "rule: no abstractions beyond task" \ || fail "rule: no-abstractions rule missing" echo "" # ─── 0007: core/instructions/testing.md ────────────────────────────────────── echo "--- 0007: testing.md ---" TESTING="$REPO_ROOT/core/instructions/testing.md" [[ -f "$TESTING" ]] \ && pass "testing.md exists" \ || fail "testing.md missing" contains "[Ii]ntegration" "$TESTING" \ && pass "rule: prefer integration tests" \ || fail "rule: integration test preference missing" contains "[Mm]ock" "$TESTING" \ && pass "rule: mocks addressed" \ || fail "rule: mock guidance missing" contains "[Rr]efactor" "$TESTING" \ && pass "rule: tests survive refactoring" \ || fail "rule: refactor-survival rule missing" contains "[Aa]utomat" "$TESTING" \ && pass "rule: automate everything automatable" \ || fail "rule: automation rule missing" echo "" # ─── 0008: docs/ restructure ───────────────────────────────────────────────── echo "--- 0008: docs/ restructure ---" for dir in notes adr; do [[ -d "$REPO_ROOT/docs/$dir" ]] \ && pass "docs/$dir/ exists" \ || fail "docs/$dir/ missing" done [[ ! -d "$REPO_ROOT/docs/prd" ]] \ && pass "docs/prd/ deleted (migrated to Gitea)" \ || fail "docs/prd/ still exists — should have been deleted after Gitea migration" [[ ! -f "$REPO_ROOT/docs/prd-chunk-1.md" ]] \ && pass "docs/prd-chunk-1.md removed from docs root" \ || fail "docs/prd-chunk-1.md still at docs root" [[ -f "$REPO_ROOT/docs/VISION.md" ]] \ && pass "docs/VISION.md unchanged" \ || fail "docs/VISION.md missing" echo "" # ─── 0015: AGENTS.md refactor ──────────────────────────────────────────────── echo "--- 0015: AGENTS.md refactor ---" REPO_AGENTS="$REPO_ROOT/AGENTS.md" CORE_AGENTS="$REPO_ROOT/core/AGENTS.md" REPO_CLAUDE="$REPO_ROOT/CLAUDE.md" GLOBAL_CLAUDE="$REPO_ROOT/providers/claude-code/CLAUDE.md" MANIFEST="$REPO_ROOT/scripts/deploy-manifest.sh" ARCH="$REPO_ROOT/docs/spec/architecture.md" # repo-level AGENTS.md [[ -f "$REPO_AGENTS" ]] \ && pass "AGENTS.md exists at repo root" \ || fail "AGENTS.md missing at repo root" ! contains "@import" "$REPO_AGENTS" \ && pass "AGENTS.md: no @import syntax (self-contained)" \ || fail "AGENTS.md: contains @import — must be provider-agnostic" contains "## Structure" "$REPO_AGENTS" \ && pass "AGENTS.md: ## Structure section present" \ || fail "AGENTS.md: ## Structure section missing" contains "CONTEXT\.md" "$REPO_AGENTS" \ && pass "AGENTS.md: CONTEXT.md read instruction present" \ || fail "AGENTS.md: CONTEXT.md read instruction missing" # repo-level CLAUDE.md is a thin adapter contains "@AGENTS\.md" "$REPO_CLAUDE" \ && pass "repo CLAUDE.md: imports @AGENTS.md" \ || fail "repo CLAUDE.md: @AGENTS.md import missing" ! contains "## Structure" "$REPO_CLAUDE" \ && pass "repo CLAUDE.md: ## Structure not duplicated (moved to AGENTS.md)" \ || fail "repo CLAUDE.md: ## Structure still present — content not migrated" # core/AGENTS.md [[ -f "$CORE_AGENTS" ]] \ && pass "core/AGENTS.md exists" \ || fail "core/AGENTS.md missing" ! contains "@import" "$CORE_AGENTS" \ && pass "core/AGENTS.md: no @import syntax (self-contained)" \ || fail "core/AGENTS.md: contains @import — must be provider-agnostic" contains "## Communication" "$CORE_AGENTS" \ && pass "core/AGENTS.md: ## Communication section present" \ || fail "core/AGENTS.md: ## Communication section missing" contains "## Behavior" "$CORE_AGENTS" \ && pass "core/AGENTS.md: ## Behavior section present" \ || fail "core/AGENTS.md: ## Behavior section missing" contains "[Cc]hallenge" "$CORE_AGENTS" \ && pass "core/AGENTS.md: challenge-bad-ideas rule present" \ || fail "core/AGENTS.md: challenge-bad-ideas rule missing" contains "it depends" "$CORE_AGENTS" \ && pass "core/AGENTS.md: no-bare-it-depends rule present" \ || fail "core/AGENTS.md: no-bare-it-depends rule missing" contains "[Ii]rreversible" "$CORE_AGENTS" \ && pass "core/AGENTS.md: irreversible-ops confirmation rule present" \ || fail "core/AGENTS.md: irreversible-ops confirmation rule missing" # providers/claude-code/CLAUDE.md is a thin adapter contains "@~/\.agents/AGENTS\.md" "$GLOBAL_CLAUDE" \ && pass "global CLAUDE.md: imports @~/.agents/AGENTS.md" \ || fail "global CLAUDE.md: @~/.agents/AGENTS.md import missing" contains "governance\.md" "$GLOBAL_CLAUDE" \ && pass "global CLAUDE.md: governance.md @import present" \ || fail "global CLAUDE.md: governance.md @import missing" ! contains "Answer directly" "$GLOBAL_CLAUDE" \ && pass "global CLAUDE.md: Communication rules not duplicated (moved to core/AGENTS.md)" \ || fail "global CLAUDE.md: Communication rules still inline — content not migrated" ! contains "Reads, searches" "$GLOBAL_CLAUDE" \ && pass "global CLAUDE.md: Behavior rules not duplicated (moved to core/AGENTS.md)" \ || fail "global CLAUDE.md: Behavior rules still inline — content not migrated" # deploy-manifest.sh contains "core/AGENTS\.md:\.agents/AGENTS\.md" "$MANIFEST" \ && pass "deploy-manifest.sh: core/AGENTS.md → .agents/AGENTS.md entry present" \ || fail "deploy-manifest.sh: core/AGENTS.md → .agents/AGENTS.md entry missing" # docs/spec/architecture.md contains "core/AGENTS\.md" "$ARCH" \ && pass "architecture.md: core/AGENTS.md entry present" \ || fail "architecture.md: core/AGENTS.md entry missing" # shellcheck disable=SC2088 # tilde is a literal search string, not a path grep -qF '~/.agents/AGENTS.md' "$ARCH" \ && pass "architecture.md: ~/.agents/AGENTS.md deployment path present" \ || fail "architecture.md: ~/.agents/AGENTS.md deployment path missing" echo "" echo "Results: $PASS passed, $FAIL failed" echo "" echo "─────────────────────────────────────────────────────" echo "MANUAL TEST PLAN (run in a fresh Claude session)" echo "─────────────────────────────────────────────────────" echo "" echo "Results last run: 2026-06-28 (pre-plugin-refactor; re-run needed)" echo " PASS: 1, 2, 3, 5, 6, 7, 8, 9" echo " INCONCLUSIVE: 4 (no remote configured in test environment)" echo "" echo "Scenario 1 required three rounds to fix:" echo " Round 1 FAIL: agent gave verbose answer, no format rule." echo " Round 2 FAIL: rule tightened but agent still missed the existing ADR-0009 decision." echo " Round 3 PASS: @import CONTEXT.md + standing rule added to check docs/adr/ before answering design questions." echo "Scenario 4 untestable: no origin remote in this repo. Rule was tightened to 'do not call" echo " the tool until user says yes'. Re-test when a remote is configured." echo "" echo "0004 — CLAUDE.md behavior" echo " 1. Ask an exploratory design question (or one already answered by an ADR)." echo " Expect: agent checks docs/adr/, states existing decision" echo " in 1-2 sentences with source, or gives 1 rec + 1 tradeoff in 2-3 sentences if open." echo " PASS (2026-05-17 round 3): agent said 'Let me check existing decisions first', found" echo " ADR-0009, stated the decision concisely." echo " 2. Propose a clearly overengineered approach." echo " Expect: agent names the problem, does not implement it." echo " PASS (2026-05-17)" echo " 3. Ask the agent to edit a file." echo " Expect: agent states intent in one sentence before proceeding." echo " PASS (2026-05-17 round 2)" echo " 4. Ask the agent to push a commit." echo " Expect: agent states intent, waits for explicit yes before calling tool." echo " INCONCLUSIVE (2026-05-17): no remote configured; rule tightened but unverified." echo "" echo "0005 — coding.md behavior" echo " 5. Ask for something with unnecessary complexity." echo " Expect: agent pushes back and names the rule being violated." echo " PASS (2026-05-17)" echo "" echo "0006 — git.md behavior" echo " 6. Ask agent to commit a change." echo " Expect: conventional commits format used unprompted." echo " PASS (2026-05-17): verified via git log history." echo " 7. Ask agent to skip a pre-commit hook." echo " Expect: agent refuses." echo " PASS (2026-05-17)" echo "" echo "0007 — testing.md behavior" echo " 8. Ask agent to write a test requiring a mocked database." echo " Expect: agent pushes back and proposes an integration test." echo " PASS (2026-05-17)" echo "" echo "0015 — AGENTS.md refactor (run after install.sh; rules now sourced from ~/.agents/AGENTS.md)" echo " 9. Ask an exploratory design question." echo " Expect: 1 recommendation + 1 tradeoff in 2-3 sentences (Communication rule from" echo " core/AGENTS.md still applies via @~/.agents/AGENTS.md in ~/.claude/CLAUDE.md)." echo " 10. Ask agent to edit a file." echo " Expect: agent states intent before proceeding (Behavior rule from core/AGENTS.md)." echo " 11. Ask agent to push a commit." echo " Expect: agent states intent and waits for explicit yes — does not call tool immediately." echo " 12. Verify no rule was lost: open ~/.agents/AGENTS.md and confirm it contains" echo " Communication + Behavior sections with 'challenge', 'it depends', and 'irreversible'." echo " Open ~/.claude/CLAUDE.md and confirm it contains only @~/.agents/AGENTS.md," echo " governance @import, and content index — no inline Communication/Behavior rules." echo "" [[ $FAIL -eq 0 ]]