#!/usr/bin/env bash # Verify the Git semantics that SKILL.md asserts, by experiment. # # Scope: this script proves the GIT behaviours exercised below. It does NOT prove # GitHub CLI behaviour (gh pr merge, run selection, merge queues) — those are # verified procedurally in SKILL.md §6, not here. Do not read a green run as # "every claim in the skill is verified"; read it as "these specific git claims # hold". Adding a claim to the skill without adding a check here leaves it # unverified, which is the failure the skill itself warns about. set -euo pipefail pass=0; fail=0 chk() { # chk "description" expected actual if [ "$2" = "$3" ]; then printf ' PASS %s\n' "$1"; pass=$((pass+1)); else printf ' FAIL %s\n expected=[%s] actual=[%s]\n' "$1" "$2" "$3"; fail=$((fail+1)); fi } newrepo() { T=$(mktemp -d); cd "$T" git init -q -b master .; git config user.email t@t; git config user.name t echo base > base.txt; git add .; git commit -qm base } cleanup() { cd /; rm -rf "$T"; } echo "--- §7: two-tree diff vs the actual merge result (the D trap) ---" newrepo git checkout -qb feature git checkout -q master; echo m > master-only.txt; git add .; git commit -qm "master adds file" chk "two-tree diff reports D for a file the branch merely predates" \ "D master-only.txt" "$(git diff --name-status master feature)" TREE=$(git merge-tree --write-tree master feature | head -1) chk "merge-tree PRESERVES it -> a D does NOT mean the merge deletes it" \ "1" "$(git ls-tree -r --name-only "$TREE" | grep -c '^master-only.txt$')" chk "three-dot diff is empty for a stale-only branch" "" "$(git diff --name-status master...feature)" if git merge-base --is-ancestor master feature; then rc=0; else rc=1; fi chk "is-ancestor exits 1 when the branch predates master" "1" "$rc" cleanup echo "--- §7: a genuine branch-side deletion IS visible in the three-dot diff ---" newrepo git checkout -q master; echo shared > shared.txt; git add .; git commit -qm "add shared" git checkout -qb feature; git merge -q master; git rm -q shared.txt; git commit -qm "branch deletes shared" chk "three-dot diff shows the real branch-side deletion" \ "D shared.txt" "$(git diff --name-status master...feature)" cleanup echo "--- §6: --no-merges excludes the merge commit from a commit-message gate ---" newrepo git checkout -qb f; echo x > x.txt; git add .; git commit -qm x git checkout -q master; git merge -q --no-ff -m "a merge commit" f MERGE=$(git rev-parse master) chk "the merge commit exists and is found by --merges" "$MERGE" "$(git rev-list --merges master)" # The real assertion: the merge sha is ABSENT from the --no-merges enumeration. # (An earlier version of this script tested `--no-merges --merges`, which is a # contradiction: it returns nothing for ANY repo and so proved nothing.) chk "the merge sha is absent from the --no-merges list" \ "0" "$(git rev-list --no-merges master | grep -c "^$MERGE$" || true)" chk "non-merge commits are still listed" \ "2" "$(git rev-list --no-merges master | wc -l | tr -d ' ')" cleanup echo "--- §7: squash captures the head AT MERGE TIME ---" newrepo git checkout -qb work; echo a > a.txt; git add .; git commit -qm work EARLY=$(git rev-parse work) echo b > b.txt; git add .; git commit -qm "later commit, pushed after the head was selected" git checkout -q master; git merge -q --squash "$EARLY" >/dev/null; git commit -qm "squash work (#1)" chk "the selected head is in the merge" "1" "$(git ls-tree -r --name-only HEAD | grep -c '^a.txt$')" chk "the later commit is NOT in the merge" "0" "$(git ls-tree -r --name-only HEAD | grep -c '^b.txt$')" cleanup echo "--- §6: stacked-branch recovery after a base squash-merge ---" newrepo git checkout -qb feat-base; echo b1 > b1.txt; git add .; git commit -qm "base: work" B=$(git rev-parse feat-base) # base tip recorded BEFORE the merge git checkout -qb feat-stack; echo s1 > s1.txt; git add .; git commit -qm "stack: work" git checkout -q master; git merge -q --squash feat-base >/dev/null; git commit -qm "base: work (#1)" M=$(git rev-parse master) chk "log --no-merges M..branch does NOT isolate the real work (lists base too)" \ "2" "$(git log --oneline --no-merges $M..feat-stack | wc -l | tr -d ' ')" chk "diff M...branch shows the base's file too (M is not an ancestor)" \ "2" "$(git diff --name-status $M...feat-stack | wc -l | tr -d ' ')" git switch -q feat-stack git rebase -q --onto $M $B feat-stack chk "after rebase --onto M B, the branch's diff is ONLY its own work" \ "A s1.txt" "$(git diff --name-status $M...feat-stack)" cleanup echo "--- §7: merge-tree reports conflicts instead of silently succeeding ---" newrepo git checkout -qb c1; echo one > c.txt; git add .; git commit -qm one git checkout -q master; echo two > c.txt; git add .; git commit -qm two if git merge-tree --write-tree master c1 >/dev/null 2>&1; then rc=0; else rc=1; fi chk "a conflicting merge makes merge-tree exit non-zero" "1" "$rc" cleanup echo "--- §6: the merge guards fail closed (not just warn) ---" # These are the forms that DO NOT fail closed, so the skill's assertions matter. set +e out=$(echo '{"a":1}' | jq 'select(.b=="x")' 2>/dev/null); rc=$? set -e chk "jq select with no match emits nothing and exits 0 (the trap)" "0" "$rc" chk "...and emits no output, so an assertion on its output is required" "" "$out" set +e out=$(echo '[]' | jq 'sort_by(.x) | last | .y' 2>/dev/null); rc=$? set -e chk "last on an empty array yields the literal null" "null" "$out" set +e; bash -c '[ 1 = 2 ] || { echo warn; }' >/dev/null 2>&1; rc=$?; set -e chk "a bare '|| echo' warning still exits 0 (why guards must exit 1)" "0" "$rc" guard() { # guard ; emulate the skill's guard shape if "$@" >/dev/null 2>&1; then return 0; else return 1; fi } chk "empty RUN is rejected" "1" "$(guard bash -c '[ -n "" ] && [ "" != null ]' ; echo $?)" chk "null RUN is rejected" "1" "$(guard bash -c '[ -n null ] && [ null != null ]' ; echo $?)" chk "valid RUN is accepted" "0" "$(guard bash -c '[ -n 123 ] && [ 123 != null ]' ; echo $?)" chk "empty MATCH is rejected" "1" "$(guard bash -c '[ -n "" ]' ; echo $?)" chk "skipped step is rejected" "1" "$(guard bash -c 'echo skipped | grep -qx success' ; echo $?)" chk "success step is accepted" "0" "$(guard bash -c 'echo success | grep -qx success' ; echo $?)" chk "missing step is rejected" "1" "$(guard bash -c 'echo "" | grep -qx success' ; echo $?)" printf '\n%d passed, %d failed\n' "$pass" "$fail" [ "$fail" -eq 0 ]