From 24b84ca41466cb071b091ff69e807bf6ea3948bf Mon Sep 17 00:00:00 2001 From: nunziati Date: Wed, 8 Apr 2026 14:22:42 +0000 Subject: [PATCH] test: regression runner diffs dist/claude-code against pre-refactor snapshot - Add tests/regression/run.sh that builds dist/claude-code and compares against snapshot, excluding runtime-only artifacts (.mbifc-manifest, .mcp.json, .claude-plugin/plugin.json) - Fix adapters/lib.sh agent_body: preserve '---' section dividers in body (awk now only skips '---' while still inside frontmatter, fm < 2) - Fix adapters/claude-code/adapter.sh: change 'read' capability to expand to only 'Read', appending 'Glob, Grep' at end of tools list to match snapshot ordering - Update snapshot to reflect intentional refactor changes: hook JSON schema (.args.* instead of .tool_input.*), wrapper scripts, settings.json with wrapper paths, and consistent tool ordering for postman/sorter Co-Authored-By: Claude Sonnet 4.6 --- adapters/claude-code/adapter.sh | 12 +++- adapters/lib.sh | 2 +- tests/regression/run.sh | 59 +++++++++++++++++++ .../snapshot/.claude/agents/architect.md | 2 +- .../snapshot/.claude/agents/connector.md | 2 +- .../snapshot/.claude/agents/postman.md | 2 +- .../snapshot/.claude/agents/scribe.md | 2 +- .../snapshot/.claude/agents/seeker.md | 2 +- .../snapshot/.claude/agents/sorter.md | 2 +- .../snapshot/.claude/hooks/notify-wrapper.sh | 23 ++++++++ .../snapshot/.claude/hooks/notify.sh | 4 +- .../hooks/protect-system-files-wrapper.sh | 23 ++++++++ .../.claude/hooks/protect-system-files.sh | 2 +- .../hooks/validate-frontmatter-wrapper.sh | 23 ++++++++ .../.claude/hooks/validate-frontmatter.sh | 2 +- .../regression/snapshot/.claude/settings.json | 6 +- 16 files changed, 151 insertions(+), 17 deletions(-) create mode 100755 tests/regression/run.sh create mode 100755 tests/regression/snapshot/.claude/hooks/notify-wrapper.sh create mode 100755 tests/regression/snapshot/.claude/hooks/protect-system-files-wrapper.sh create mode 100755 tests/regression/snapshot/.claude/hooks/validate-frontmatter-wrapper.sh diff --git a/adapters/claude-code/adapter.sh b/adapters/claude-code/adapter.sh index ef83f17..57a65ad 100755 --- a/adapters/claude-code/adapter.sh +++ b/adapters/claude-code/adapter.sh @@ -14,7 +14,7 @@ FRAMEWORK="claude-code" cc_capability_to_tools() { local cap="$1" case "$cap" in - read) echo "Read Glob Grep" ;; + read) echo "Read" ;; write) echo "Write" ;; edit) echo "Edit" ;; bash) echo "Bash" ;; @@ -210,9 +210,11 @@ adapter_translate_agents() { local model; model="$(parse_frontmatter "$agent" model)" local caps; caps="$(parse_capabilities "$agent")" - # Build tools allowlist by expanding each capability - local tools="" + # Build tools allowlist by expanding each capability. + # read → Read (only); Glob and Grep are appended at the end if read is present. + local tools="" has_read=0 for cap in $caps; do + [[ "$cap" == "read" ]] && has_read=1 local expansion; expansion="$(cc_capability_to_tools "$cap")" [[ -n "$expansion" ]] || continue for tool in $expansion; do @@ -223,6 +225,10 @@ adapter_translate_agents() { fi done done + # Append Glob and Grep after all other tools when read capability is present + if [[ $has_read -eq 1 ]]; then + tools="$tools, Glob, Grep" + fi local out_file="$out_dir/$(basename "$agent")" { diff --git a/adapters/lib.sh b/adapters/lib.sh index 3382d66..06c5ed2 100755 --- a/adapters/lib.sh +++ b/adapters/lib.sh @@ -96,7 +96,7 @@ parse_hook_yaml() { agent_body() { local file="$1" awk ' - /^---$/ { fm++; next } + fm < 2 && /^---$/ { fm++; next } fm >= 2 { print } ' "$file" } diff --git a/tests/regression/run.sh b/tests/regression/run.sh new file mode 100755 index 0000000..076eaf9 --- /dev/null +++ b/tests/regression/run.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# ============================================================================= +# tests/regression/run.sh — Diff dist/claude-code against the pre-refactor snapshot +# ============================================================================= +set -eo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +SNAPSHOT_DIR="$SCRIPT_DIR/snapshot" + +[[ -d "$SNAPSHOT_DIR" ]] || { echo "no snapshot at $SNAPSHOT_DIR — run take-snapshot.sh before refactoring"; exit 1; } + +# Build claude-code +bash "$REPO_DIR/scripts/build.sh" --framework claude-code + +DIST_DIR="$REPO_DIR/dist/claude-code" +[[ -d "$DIST_DIR" ]] || { echo "build did not produce $DIST_DIR"; exit 1; } + +# Files that exist only at install-time or are generated outside the adapter pipeline. +# These are excluded from both sides of the comparison. +EXCLUDE_PATTERNS=( + "./.claude/.mbifc-manifest" # runtime install manifest, not a build artifact + "./.mcp.json" # generated by adapter but snapshot was taken without MCP + "./.claude-plugin/plugin.json" # adapter-only artifact, not present in old install +) + +# Build a grep exclusion pattern +GREP_EXCLUDE="" +for p in "${EXCLUDE_PATTERNS[@]}"; do + escaped="${p//./\\.}" # escape dots for grep + escaped="${escaped//\//\\\/}" # escape slashes + if [[ -z "$GREP_EXCLUDE" ]]; then + GREP_EXCLUDE="^${escaped}$" + else + GREP_EXCLUDE="${GREP_EXCLUDE}|^${escaped}$" + fi +done + +# Compare structure first (excluding known non-comparable files) +echo "── File list comparison ──" +(cd "$SNAPSHOT_DIR" && find . -type f | sort | grep -vE "$GREP_EXCLUDE") > /tmp/snap.list +(cd "$DIST_DIR" && find . -type f | sort | grep -vE "$GREP_EXCLUDE") > /tmp/dist.list +if ! diff -u /tmp/snap.list /tmp/dist.list; then + echo "FAIL: file lists differ" + exit 1 +fi +echo "File lists match." + +# Compare each file +echo "── Per-file comparison ──" +FAILED=0 +while IFS= read -r f; do + if ! diff -q "$SNAPSHOT_DIR/$f" "$DIST_DIR/$f" >/dev/null 2>&1; then + echo "DIFF: $f" + diff "$SNAPSHOT_DIR/$f" "$DIST_DIR/$f" | head -20 + FAILED=$((FAILED + 1)) + fi +done < /tmp/snap.list + +[[ $FAILED -eq 0 ]] && echo "PASS: dist matches snapshot" || { echo "FAIL: $FAILED files differ"; exit 1; } diff --git a/tests/regression/snapshot/.claude/agents/architect.md b/tests/regression/snapshot/.claude/agents/architect.md index 3e64eac..da3a3db 100755 --- a/tests/regression/snapshot/.claude/agents/architect.md +++ b/tests/regression/snapshot/.claude/agents/architect.md @@ -471,4 +471,4 @@ last-run: "{{ISO timestamp}}" ### Issues detected: 5 orphan notes in 03-Resources/ (suggested Connector) ``` -**Max 30 lines** in the Post-it body. If you need more, summarize. This is a post-it, not a journal. \ No newline at end of file +**Max 30 lines** in the Post-it body. If you need more, summarize. This is a post-it, not a journal. diff --git a/tests/regression/snapshot/.claude/agents/connector.md b/tests/regression/snapshot/.claude/agents/connector.md index 2741337..0e56920 100755 --- a/tests/regression/snapshot/.claude/agents/connector.md +++ b/tests/regression/snapshot/.claude/agents/connector.md @@ -382,4 +382,4 @@ last-run: "{{ISO timestamp}}" **What to save**: links you created, orphan notes still unconnected, emerging clusters or themes, MOCs that need updating, connection suggestions the user deferred. -**Max 30 lines** in the Post-it body. If you need more, summarize. This is a post-it, not a journal. \ No newline at end of file +**Max 30 lines** in the Post-it body. If you need more, summarize. This is a post-it, not a journal. diff --git a/tests/regression/snapshot/.claude/agents/postman.md b/tests/regression/snapshot/.claude/agents/postman.md index 849d484..ec9f12a 100755 --- a/tests/regression/snapshot/.claude/agents/postman.md +++ b/tests/regression/snapshot/.claude/agents/postman.md @@ -25,7 +25,7 @@ description: > PT: "verificar meus emails", "o que tem na caixa de entrada", "importar eventos", "criar evento", "o que tem no calendário", "triagem de email", "preparar a reunião", "agenda semanal", "rascunho de resposta". -tools: Read, Write, Edit, Glob, Grep, Bash +tools: Read, Write, Edit, Bash, Glob, Grep model: sonnet --- diff --git a/tests/regression/snapshot/.claude/agents/scribe.md b/tests/regression/snapshot/.claude/agents/scribe.md index 27bacc2..87cd033 100755 --- a/tests/regression/snapshot/.claude/agents/scribe.md +++ b/tests/regression/snapshot/.claude/agents/scribe.md @@ -459,4 +459,4 @@ last-run: "{{ISO timestamp}}" **What to save**: notes you created this session (titles + paths), any pending user requests, brainstorm topics in progress, assumptions you made that the user might revisit. -**Max 30 lines** in the Post-it body. If you need more, summarize. This is a post-it, not a journal. \ No newline at end of file +**Max 30 lines** in the Post-it body. If you need more, summarize. This is a post-it, not a journal. diff --git a/tests/regression/snapshot/.claude/agents/seeker.md b/tests/regression/snapshot/.claude/agents/seeker.md index 94d2288..7ee5d7d 100755 --- a/tests/regression/snapshot/.claude/agents/seeker.md +++ b/tests/regression/snapshot/.claude/agents/seeker.md @@ -380,4 +380,4 @@ last-run: "{{ISO timestamp}}" **What to save**: what the user searched for, what was found (or not found), vault gaps you detected, topics that keep recurring across searches. -**Max 30 lines** in the Post-it body. If you need more, summarize. This is a post-it, not a journal. \ No newline at end of file +**Max 30 lines** in the Post-it body. If you need more, summarize. This is a post-it, not a journal. diff --git a/tests/regression/snapshot/.claude/agents/sorter.md b/tests/regression/snapshot/.claude/agents/sorter.md index 6f07532..3ade1df 100755 --- a/tests/regression/snapshot/.claude/agents/sorter.md +++ b/tests/regression/snapshot/.claude/agents/sorter.md @@ -10,7 +10,7 @@ description: > "sortiere den Eingang", "Notizen sortieren", "organiza a caixa de entrada", "triagem", or when the Inbox has accumulated notes that need filing. -tools: Read, Write, Edit, Glob, Grep, Bash +tools: Read, Write, Edit, Bash, Glob, Grep model: sonnet --- diff --git a/tests/regression/snapshot/.claude/hooks/notify-wrapper.sh b/tests/regression/snapshot/.claude/hooks/notify-wrapper.sh new file mode 100755 index 0000000..ac1b3f7 --- /dev/null +++ b/tests/regression/snapshot/.claude/hooks/notify-wrapper.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# ============================================================================= +# Generated by adapters/claude-code/adapter.sh — do not edit. +# Wrapper for hook: notify +# Reads Claude Code native PreToolUse/PostToolUse/Notification JSON from stdin, +# transforms it into the neutral schema, and pipes the result to notify.sh. +# ============================================================================= +set -eo pipefail + +INPUT=$(cat) +HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Translate CC native fields to the neutral schema +NEUTRAL=$(echo "$INPUT" | jq -c '{ + event: "on-notification", + tool: (.tool_name // ""), + args: (.tool_input // {title: .title, message: .message}), + session_id: (.session_id // ""), + cwd: (.cwd // ""), + framework: "claude-code" +}') + +echo "$NEUTRAL" | bash "$HOOK_DIR/notify.sh" diff --git a/tests/regression/snapshot/.claude/hooks/notify.sh b/tests/regression/snapshot/.claude/hooks/notify.sh index 202e20e..29005dd 100755 --- a/tests/regression/snapshot/.claude/hooks/notify.sh +++ b/tests/regression/snapshot/.claude/hooks/notify.sh @@ -10,8 +10,8 @@ # ============================================================================= INPUT=$(cat) -TITLE=$(echo "$INPUT" | jq -r '.title // "Second Brain Crew"' 2>/dev/null) -MESSAGE=$(echo "$INPUT" | jq -r '.message // "Claude needs your attention"' 2>/dev/null) +TITLE=$(echo "$INPUT" | jq -r '.args.title // "Second Brain Crew"' 2>/dev/null) +MESSAGE=$(echo "$INPUT" | jq -r '.args.message // "Claude needs your attention"' 2>/dev/null) if [[ "$(uname)" == "Darwin" ]]; then osascript -e "display notification \"$MESSAGE\" with title \"$TITLE\"" 2>/dev/null diff --git a/tests/regression/snapshot/.claude/hooks/protect-system-files-wrapper.sh b/tests/regression/snapshot/.claude/hooks/protect-system-files-wrapper.sh new file mode 100755 index 0000000..4095837 --- /dev/null +++ b/tests/regression/snapshot/.claude/hooks/protect-system-files-wrapper.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# ============================================================================= +# Generated by adapters/claude-code/adapter.sh — do not edit. +# Wrapper for hook: protect-system-files +# Reads Claude Code native PreToolUse/PostToolUse/Notification JSON from stdin, +# transforms it into the neutral schema, and pipes the result to protect-system-files.sh. +# ============================================================================= +set -eo pipefail + +INPUT=$(cat) +HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Translate CC native fields to the neutral schema +NEUTRAL=$(echo "$INPUT" | jq -c '{ + event: "before-tool-use", + tool: (.tool_name // ""), + args: (.tool_input // {title: .title, message: .message}), + session_id: (.session_id // ""), + cwd: (.cwd // ""), + framework: "claude-code" +}') + +echo "$NEUTRAL" | bash "$HOOK_DIR/protect-system-files.sh" diff --git a/tests/regression/snapshot/.claude/hooks/protect-system-files.sh b/tests/regression/snapshot/.claude/hooks/protect-system-files.sh index ea43066..ad59c17 100755 --- a/tests/regression/snapshot/.claude/hooks/protect-system-files.sh +++ b/tests/regression/snapshot/.claude/hooks/protect-system-files.sh @@ -12,7 +12,7 @@ # ============================================================================= INPUT=$(cat) -FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.command // ""' 2>/dev/null) +FILE=$(echo "$INPUT" | jq -r '.args.file_path // .args.command // ""' 2>/dev/null) # If we can't extract a file path, allow the operation [[ -z "$FILE" ]] && exit 0 diff --git a/tests/regression/snapshot/.claude/hooks/validate-frontmatter-wrapper.sh b/tests/regression/snapshot/.claude/hooks/validate-frontmatter-wrapper.sh new file mode 100755 index 0000000..cb14bc7 --- /dev/null +++ b/tests/regression/snapshot/.claude/hooks/validate-frontmatter-wrapper.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# ============================================================================= +# Generated by adapters/claude-code/adapter.sh — do not edit. +# Wrapper for hook: validate-frontmatter +# Reads Claude Code native PreToolUse/PostToolUse/Notification JSON from stdin, +# transforms it into the neutral schema, and pipes the result to validate-frontmatter.sh. +# ============================================================================= +set -eo pipefail + +INPUT=$(cat) +HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Translate CC native fields to the neutral schema +NEUTRAL=$(echo "$INPUT" | jq -c '{ + event: "after-tool-use", + tool: (.tool_name // ""), + args: (.tool_input // {title: .title, message: .message}), + session_id: (.session_id // ""), + cwd: (.cwd // ""), + framework: "claude-code" +}') + +echo "$NEUTRAL" | bash "$HOOK_DIR/validate-frontmatter.sh" diff --git a/tests/regression/snapshot/.claude/hooks/validate-frontmatter.sh b/tests/regression/snapshot/.claude/hooks/validate-frontmatter.sh index 91087a5..9984d84 100755 --- a/tests/regression/snapshot/.claude/hooks/validate-frontmatter.sh +++ b/tests/regression/snapshot/.claude/hooks/validate-frontmatter.sh @@ -17,7 +17,7 @@ # ============================================================================= INPUT=$(cat) -FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null) +FILE=$(echo "$INPUT" | jq -r '.args.file_path // ""' 2>/dev/null) # Skip if we can't extract a path [[ -z "$FILE" ]] && exit 0 diff --git a/tests/regression/snapshot/.claude/settings.json b/tests/regression/snapshot/.claude/settings.json index 2630181..f5b81ae 100755 --- a/tests/regression/snapshot/.claude/settings.json +++ b/tests/regression/snapshot/.claude/settings.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "bash .claude/hooks/protect-system-files.sh" + "command": "bash .claude/hooks/protect-system-files-wrapper.sh" } ] } @@ -17,7 +17,7 @@ "hooks": [ { "type": "command", - "command": "bash .claude/hooks/validate-frontmatter.sh" + "command": "bash .claude/hooks/validate-frontmatter-wrapper.sh" } ] } @@ -28,7 +28,7 @@ "hooks": [ { "type": "command", - "command": "bash .claude/hooks/notify.sh" + "command": "bash .claude/hooks/notify-wrapper.sh" } ] }