CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/coding-policy

General-purpose coding policy for Baruch's AI agents

73

Quality

91%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

test_check_leftover_worktrees.shhooks/tests/

#!/usr/bin/env bash
# Outcome-based tests for hooks/check-leftover-worktrees.sh.
#
# Each case builds its own bare "origin", a shared clone, its own worktree
# root and a fake `gh` first on PATH that answers from files in the case's
# directory, so no case sees another's state and they run in any order
# (rules/testing-standards.md Independence). Every commit and reflog entry is
# dated FIXTURE_DATE, every aged file is touched to a fixed mtime, and the
# owner scripts judge at PRUNE_NOW, never the wall clock (Determinism).
#
# The owner scripts' decisions have their own suites
# (skills/herdr-foreman/tests/test_prune_worktrees.sh,
# test_prune_remote_branches.sh); these cases cover what the hook adds: it
# runs them live for this repository, lists only what the operator must
# decide, never acts in a worker session or in portable mode, and turns a
# failure into one "could not check" line.
#
# Covers:
#   1. Removable            -> an idle clean worktree origin holds, a merged
#                              local branch and a merged branch on origin are
#                              removed; nothing is printed.
#   2. Questionable         -> an idle dirty worktree, an idle unpushed
#                              worktree, an unpushed local branch and a stale
#                              unmerged branch on origin are listed and left.
#                              An open pull request's branch and another
#                              repository's dirty worktree are not listed.
#   3. Not yet idle         -> a fresh dirty worktree is not listed.
#   4. In use               -> a process inside keeps a removable worktree,
#                              unlisted.
#   5. gh failing           -> nothing on origin is deleted; one could-not-check
#                              line, never the URL gh printed.
#   6. Worker session       -> HERDR_ENV in a linked worktree: nothing deleted,
#                              nothing printed.
#   7. Portable mode        -> nothing deleted; the questionable list still
#                              printed.
#   8. No origin, no repo   -> silent.
#  13. Portable, linked     -> under tessl a linked worktree runs neither owner
#                              script and changes no ref.
#  12. Malformed result     -> an owner result missing documented fields is a
#                              could-not-check status.
#  11. Newline path         -> a shared checkout whose name ends in a newline
#                              is cleaned, never truncated.
#  10. Missing tool         -> a missing git or python3 still prints the
#                              could-not-check status, as fixed JSON.
#   9. Out of time          -> a could-not-check line naming the budget (a
#                              stand-in runner reports the timeout; no case
#                              waits on a clock, and every other run gets an
#                              hour).
#  14. Inventory failure    -> a git worktree list that fails after printing a
#                              valid prefix is a could-not-check status naming
#                              git's own exit, and runs neither owner script.
#  15. SSH BatchMode        -> appended to a user-set GIT_SSH_COMMAND, and the
#                              default when none is set.
#
# The harness drops `set -e` to aggregate results; every fixture command is
# checked explicitly (rules/error-handling.md aggregate-reporting carve-out).
#
# Run: bash hooks/tests/test_check_leftover_worktrees.sh
set -uo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HOOK="${HERE}/../check-leftover-worktrees.sh"
PASS=0
FAIL=0
TMP=""
SLEEPER=""

#: Every fixture commit and reflog entry carries this date.
FIXTURE_DATE="2020-01-01T00:00:00Z"
AGED_MTIME=202001010000
#: A mtime inside the idle window of PRUNE_NOW in any time zone.
FRESH_MTIME=202001092000
#: 2020-01-10T00:00:00Z: nine days after FIXTURE_DATE.
PRUNE_NOW=1578614400

die() { echo "fatal: $*" >&2; exit 2; }
pass() { PASS=$((PASS+1)); }
fail() { FAIL=$((FAIL+1)); echo "  ✗ FAIL: $1" >&2; }
cleanup() {
  stop_sleeper
  if [[ -n "$TMP" ]] && ! rm -rf "$TMP"; then echo "warn: could not remove $TMP" >&2; fi
  return 0
}

# Run a fixture command with stderr captured; a failure stops the harness
# with the command's own words.
quiet() { # <what> <command...>
  local what="$1" rc=0; shift
  "$@" 2>"$TMP/quiet.err" || rc=$?
  if (( rc != 0 )); then
    die "${what} (exit ${rc}): $(tr '\n' ' ' < "$TMP/quiet.err") — rerun \`$*\` by hand"
  fi
}

# A fake gh answering from $CASE: `prs` lists open pull-request heads; `fail`
# makes every call exit 1 with a credential-bearing message.
write_fake_gh() {
  mkdir -p "$CASE/bin" || die "mkdir $CASE/bin failed"
  cat > "$CASE/bin/gh" <<'SH' || die "cannot write the fake gh"
#!/usr/bin/env bash
set -euo pipefail
d="${FAKE_GH_DIR:?}"
if [[ -e "$d/fail" ]]; then echo "gh: HTTP 502 from https://s3cr3t@example.invalid" >&2; exit 1; fi
case "$1" in
  api)
    if [[ "$2" == *'?protected'* ]]; then
      if [[ -f "$d/protected" ]]; then cat "$d/protected"; fi
    else
      # GitHub routes a raw slash as another path segment: 404, as here.
      seg="${2#*/branches/}"
      if [[ "$seg" == */* ]]; then echo "gh: HTTP 404: Not Found (branches/${seg})" >&2; exit 1; fi
      printf '%s\n' "$seg" >> "$d/api-branches"
      b="$(python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))' "$seg")"
      if [[ -f "$d/protected" ]] && grep -qxF -- "$b" "$d/protected"; then echo true; else echo false; fi
    fi
    exit 0 ;;
  pr)
    head=""
    while (( $# )); do
      if [[ "$1" == --head ]]; then head="$2"; shift; fi
      shift
    done
    [[ -f "$d/prs" ]] || exit 0
    if [[ -n "$head" ]]; then grep -xF -- "$head" "$d/prs" || [[ $? -eq 1 ]]; else cat "$d/prs"; fi
    exit 0 ;;
esac
echo "fake gh: unexpected call $*" >&2
exit 3
SH
  chmod +x "$CASE/bin/gh" || die "chmod the fake gh failed"
}

# A git first on PATH that answers `remote get-url` with a GitHub URL, so the
# bare origin can pose as a GitHub repository for prune-remote-branches.sh;
# every other call is the real git's.
write_fake_git() {
  local real
  real="$(command -v git)" || die "git is required"
  printf '#!/usr/bin/env bash\nREAL_GIT=%q\n' "$real" > "$CASE/bin/git" || die "cannot write the fake git"
  cat >> "$CASE/bin/git" <<'SH' || die "cannot write the fake git"
set -euo pipefail
args=("$@")
i=0
if [[ "${args[0]:-}" == -C ]]; then i=2; fi
if [[ "${args[i]:-}" == remote && "${args[i+1]:-}" == get-url ]]; then
  # A missing origin still fails as git's own does.
  "$REAL_GIT" "$@" >/dev/null
  echo https://github.com/acme/widgets.git; exit 0
fi
exec "$REAL_GIT" "$@"
SH
  chmod +x "$CASE/bin/git" || die "chmod the fake git failed"
}

# Per case: origin, a shared clone, a worktree root and the fake gh. Sets
# CASE, BARE, SHARED, ROOT.
mk_case() { # <name>
  CASE="$TMP/$1"; BARE="$CASE/origin.git"; SHARED="$CASE/shared"; ROOT="$CASE/worktrees"
  mkdir -p "$CASE" "$ROOT" || die "mkdir $CASE failed"
  quiet "init origin" git init -q --bare -b main "$BARE"
  quiet "clone" git clone -q "$BARE" "$SHARED"
  quiet "switch main" git -C "$SHARED" switch -q -C main
  printf 'base\n' > "$SHARED/f" || die "write f failed"
  quiet "add f" git -C "$SHARED" add f
  quiet "commit base" git -C "$SHARED" commit -q -m base
  quiet "push main" git -C "$SHARED" push -q -u origin main
  quiet "set-head" git -C "$SHARED" remote set-head origin --auto >/dev/null
  write_fake_gh
  write_fake_git
}

commit_file() { # <checkout> <file>
  printf '%s\n' "$2" > "$1/$2" || die "write $2 failed"
  quiet "add $2" git -C "$1" add "$2"
  quiet "commit $2" git -C "$1" commit -q -m "$2"
}

# Touch a worktree's files and its gitdir's activity files to <mtime>.
age_wt() { # <worktree> <mtime>
  local gitdir f
  gitdir="$(git -C "$1" rev-parse --absolute-git-dir)" || die "rev-parse --absolute-git-dir failed in $1"
  for f in "$gitdir/HEAD" "$gitdir/index" "$gitdir/logs/HEAD"; do
    if [[ -e "$f" ]]; then touch -t "$2" "$f" || die "touch $f failed"; fi
  done
  find "$1" -path "$1/.git" -prune -o -exec touch -h -t "$2" {} + || die "touch the files of $1 failed"
}

# A branch on origin cut from main with one commit; merged into main or not.
push_branch() { # <branch> <merged 0|1>
  quiet "branch $1" git -C "$SHARED" switch -q -c "$1" main
  commit_file "$SHARED" "file-${1//\//-}"
  quiet "push $1" git -C "$SHARED" push -q origin "$1"
  quiet "switch main" git -C "$SHARED" switch -q main
  if (( $2 )); then
    quiet "merge $1" git -C "$SHARED" merge -q --ff-only "$1"
    quiet "push main" git -C "$SHARED" push -q origin main
  fi
  quiet "drop local $1" git -C "$SHARED" branch -q -D "$1"
}

on_origin() { git -C "$BARE" show-ref --verify --quiet "refs/heads/$1"; }
has_branch() { git -C "$SHARED" show-ref --verify --quiet "refs/heads/$1"; }

# Run the hook from <dir> with the case's environment; sets OUT, RC, ERR.
run_hook() { # <dir> [VAR=value...]
  local dir="$1"; shift
  RC=0
  OUT="$(cd "$dir" && env PATH="$CASE/bin:$PATH" FAKE_GH_DIR="$CASE" WORKTREE_ROOT="$ROOT" \
    PRUNE_NOW="$PRUNE_NOW" PRUNE_IDLE_HOURS=24 PRUNE_REMOTE_IDLE_HOURS=24 LEFTOVER_BUDGET_SEC=3600 "$@" \
    bash "$HOOK" </dev/null 2>"$CASE/hook.err")" || RC=$?
  ERR="$(cat "$CASE/hook.err")"
}

# The additionalContext text of OUT, or empty when OUT is empty.
context() {
  [[ -n "$OUT" ]] || return 0
  printf '%s' "$OUT" | python3 -c 'import json,sys; print(json.load(sys.stdin)["additionalContext"])'
}

# A process with its cwd inside <dir>, registered with the fake lsof;
# returns once it is there. The FIFO read returns when the child writes after
# its cd: no polling, no deadline.
start_sleeper() { # <dir>
  local ready="$CASE/sleeper.ready" said
  mkfifo "$ready" || die "mkfifo $ready failed"
  (
    if cd "$1"; then
      printf 'in' > "$ready"
      exec sleep 3600
    fi
    printf 'cd-failed' > "$ready"
  ) &
  SLEEPER=$!
  said="$(cat "$ready")" || die "cannot read the sleeper's FIFO $ready"
  [[ "$said" == in ]] || die "the sleeper could not enter $1"
  printf '%s %s\n' "$SLEEPER" "$1" >> "$FAKE_LSOF_CWDS" || die "cannot register the sleeper with the fake lsof"
}
stop_sleeper() {
  if [[ -n "$SLEEPER" ]]; then
    if ! kill "$SLEEPER"; then echo "warn: could not stop sleeper $SLEEPER" >&2; fi
    # A killed sleeper exits non-zero: that is the expected outcome here.
    wait "$SLEEPER"
  fi
  SLEEPER=""
  if [[ -n "${FAKE_LSOF_CWDS:-}" && -e "${FAKE_LSOF_CWDS:-}" ]] && ! : > "$FAKE_LSOF_CWDS"; then
    echo "warn: could not clear the fake lsof's records" >&2
  fi
  return 0
}

# The process probe every run uses: a stand-in for lsof that prints one
# NUL-framed cwd record per "<pid> <cwd>" line of $FAKE_LSOF_CWDS and nothing
# else, so the host's own process table never reaches a test. A case that
# needs a process inside a worktree registers it there; a case exercising the
# probe's failure modes names its own stand-in through PRUNE_LSOF.
write_fake_lsof() { # <path>
  mkdir -p "$(dirname "$1")" || die "mkdir for the fake lsof failed"
  cat > "$1" <<'SH' || die "write the fake lsof failed"
#!/usr/bin/env bash
set -euo pipefail
list="${FAKE_LSOF_CWDS:-}"
if [[ -z "$list" || ! -s "$list" ]]; then exit 0; fi
while IFS=' ' read -r pid cwd; do
  printf 'p%s\0\nfcwd\0n%s\0\n' "$pid" "$cwd"
done < "$list"
SH
  chmod +x "$1" || die "chmod the fake lsof failed"
}

# A staged plugin: the hook and the result-check module it imports, laid out
# as the plugin ships them. Each case adds its own owner scripts.
stage_plugin() { # <dir> [hooks-dir-name]
  local hooks="${2:-hooks}"
  mkdir -p "$1/$hooks" "$1/skills/herdr-foreman/foreman" || die "mkdir stage $1 failed"
  cp "$HOOK" "$1/$hooks/" || die "stage the hook failed"
  cp "${HERE}/../../skills/herdr-foreman/foreman/__init__.py" "${HERE}/../../skills/herdr-foreman/foreman/prune_result.py" \
    "$1/skills/herdr-foreman/foreman/" || die "stage the result checks failed"
}

# A staged plugin with the real runner and owner scripts that record their
# name and GIT_SSH_COMMAND to <calls>, then fail.
stage_recording_plugin() { # <dir> <calls>
  stage_plugin "$1"
  cp "${HERE}/../../skills/herdr-foreman/bounded-run.sh" "$1/skills/herdr-foreman/" || die "stage the runner failed"
  local owner
  for owner in prune-worktrees.sh prune-remote-branches.sh; do
    # shellcheck disable=SC2016  # GIT_SSH_COMMAND expands in the stand-in, not here.
    printf '#!/usr/bin/env bash\nset -euo pipefail\nprintf "%%s %%s\\n" "%s" "${GIT_SSH_COMMAND:-}" >> %q\nexit 1\n' "$owner" "$2" \
      > "$1/skills/herdr-foreman/$owner" || die "write the recording $owner failed"
  done
}

main() {
  command -v python3 >/dev/null || die "python3 is required"
  TMP="$(mktemp -d)" || die "mktemp failed"
  TMP="$(cd "$TMP" && pwd -P)" || die "cannot resolve $TMP"
  trap cleanup EXIT
  write_fake_lsof "$TMP/fake-lsof/lsof"
  export PRUNE_LSOF="$TMP/fake-lsof/lsof" FAKE_LSOF_CWDS="$TMP/fake-lsof/cwds"
  export HOME="$TMP/home" GIT_CONFIG_NOSYSTEM=1
  mkdir -p "$HOME" || die "mkdir HOME failed"
  export GIT_AUTHOR_NAME=Ada GIT_AUTHOR_EMAIL=ada@example.invalid
  export GIT_COMMITTER_NAME=Ada GIT_COMMITTER_EMAIL=ada@example.invalid
  export GIT_AUTHOR_DATE="$FIXTURE_DATE" GIT_COMMITTER_DATE="$FIXTURE_DATE"
  unset HERDR_ENV SESSION_START_MODE GIT_SSH_COMMAND
  local ctx

  echo "1. removable worktrees and branches go, and nothing is printed"
  mk_case c1
  quiet "wt add" git -C "$SHARED" worktree add -q --detach "$ROOT/spent" origin/main
  age_wt "$ROOT/spent" "$AGED_MTIME"
  quiet "local branch" git -C "$SHARED" branch -q --no-track feat/local-merged main
  push_branch feat/remote-merged 1
  run_hook "$SHARED"
  if [[ $RC -eq 0 && -z "$OUT" && ! -e "$ROOT/spent" ]] && ! has_branch feat/local-merged \
     && ! on_origin feat/remote-merged && on_origin main; then pass
  else fail "c1: RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "2. only work the operator must decide on is listed, and none of it is touched"
  mk_case c2
  quiet "wt dirty" git -C "$SHARED" worktree add -q -b review/dirty "$ROOT/dirty" origin/main
  printf 'notes\n' > "$ROOT/dirty/notes.txt" || die "write notes failed"
  age_wt "$ROOT/dirty" "$AGED_MTIME"
  quiet "wt unpushed" git -C "$SHARED" worktree add -q -b feat/unpushed "$ROOT/unpushed" origin/main
  commit_file "$ROOT/unpushed" u.txt
  age_wt "$ROOT/unpushed" "$AGED_MTIME"
  quiet "local unpushed" git -C "$SHARED" switch -q -c feat/local-only main
  commit_file "$SHARED" l.txt
  quiet "switch main" git -C "$SHARED" switch -q main
  push_branch feat/stale 0
  push_branch feat/open-pr 0
  printf 'feat/open-pr\n' > "$CASE/prs" || die "write prs failed"
  local other="$CASE/other"
  quiet "clone other" git clone -q "$BARE" "$other"
  quiet "wt other" git -C "$other" worktree add -q -b review/other "$ROOT/other-dirty" origin/main
  printf 'x\n' > "$ROOT/other-dirty/x.txt" || die "write other failed"
  age_wt "$ROOT/other-dirty" "$AGED_MTIME"
  run_hook "$SHARED"
  ctx="$(context)"
  if [[ $RC -eq 0 ]] && [[ "$ctx" == "Session-start status — 4 item(s)"* ]] \
     && [[ "$ctx" == *"$ROOT/dirty (review/dirty): 1 uncommitted file(s)"* ]] \
     && [[ "$ctx" == *"$ROOT/unpushed (feat/unpushed): 1 commit(s) origin does not hold"* ]] \
     && [[ "$ctx" == *"local branch feat/local-only: 1 unpushed commit(s)"* ]] \
     && [[ "$ctx" == *"origin branch feat/stale: 1 commit(s) not in main"*"by Ada"* ]] \
     && [[ "$ctx" == *"one at a time"* ]] \
     && [[ "$ctx" != *feat/open-pr* && "$ctx" != *other-dirty* ]] \
     && [[ -e "$ROOT/dirty/notes.txt" && -e "$ROOT/unpushed" && -e "$ROOT/other-dirty" ]] \
     && has_branch feat/local-only && on_origin feat/stale && on_origin feat/open-pr; then pass
  else fail "c2: RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "3. a dirty worktree inside the idle window is not listed"
  mk_case c3
  quiet "wt fresh" git -C "$SHARED" worktree add -q -b review/fresh "$ROOT/fresh" origin/main
  printf 'wip\n' > "$ROOT/fresh/wip.txt" || die "write wip failed"
  age_wt "$ROOT/fresh" "$FRESH_MTIME"
  run_hook "$SHARED"
  if [[ $RC -eq 0 && -z "$OUT" && -e "$ROOT/fresh/wip.txt" ]]; then pass
  else fail "c3: RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "4. a process inside keeps a removable worktree, and it is not listed"
  mk_case c4
  quiet "wt busy" git -C "$SHARED" worktree add -q --detach "$ROOT/busy" origin/main
  age_wt "$ROOT/busy" "$AGED_MTIME"
  start_sleeper "$ROOT/busy"
  run_hook "$SHARED"
  stop_sleeper
  if [[ $RC -eq 0 && -z "$OUT" && -e "$ROOT/busy" ]]; then pass
  else fail "c4: RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "5. a failing gh deletes nothing on origin and says so in one line"
  mk_case c5
  push_branch feat/remote-merged 1
  : > "$CASE/fail" || die "write fail flag failed"
  run_hook "$SHARED"
  ctx="$(context)"
  if [[ $RC -eq 0 ]] && on_origin feat/remote-merged \
     && [[ "$ctx" == "Session-start status — could not check this repository"*"prune-remote-branches.sh"* ]] \
     && [[ "$ctx" != *$'\n'* && "$OUT$ERR" != *s3cr3t* ]]; then pass
  else fail "c5: RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "6. a Herdr worker session deletes nothing and prints nothing"
  mk_case c6
  quiet "wt worker" git -C "$SHARED" worktree add -q -b review/worker "$ROOT/worker" origin/main
  quiet "wt spent" git -C "$SHARED" worktree add -q --detach "$ROOT/spent" origin/main
  age_wt "$ROOT/spent" "$AGED_MTIME"
  push_branch feat/remote-merged 1
  run_hook "$ROOT/worker" HERDR_ENV=1
  local rc_set=$RC out_set=$OUT
  # Set but empty is still Herdr: the role is unknown, so it acts as a worker.
  run_hook "$ROOT/worker" HERDR_ENV=
  if [[ $rc_set -eq 0 && -z "$out_set" && $RC -eq 0 && -z "$OUT" && -e "$ROOT/spent" ]] && on_origin feat/remote-merged; then pass
  else fail "c6: set RC=$rc_set OUT=$out_set; empty RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "7. portable mode deletes nothing and still lists what awaits the operator"
  mk_case c7
  quiet "wt spent" git -C "$SHARED" worktree add -q --detach "$ROOT/spent" origin/main
  age_wt "$ROOT/spent" "$AGED_MTIME"
  quiet "wt dirty" git -C "$SHARED" worktree add -q -b review/dirty "$ROOT/dirty" origin/main
  printf 'notes\n' > "$ROOT/dirty/notes.txt" || die "write notes failed"
  age_wt "$ROOT/dirty" "$AGED_MTIME"
  push_branch feat/remote-merged 1
  run_hook "$SHARED" SESSION_START_MODE=portable
  ctx="$(context)"
  if [[ $RC -eq 0 && -e "$ROOT/spent" ]] && on_origin feat/remote-merged \
     && [[ "$ctx" == "Session-start status — 1 item(s)"*"$ROOT/dirty"* ]]; then pass
  else fail "c7: RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "8. a repository without origin, and a directory outside any, are silent"
  mk_case c8
  quiet "remove origin" git -C "$SHARED" remote remove origin
  run_hook "$SHARED"
  local rc_a=$RC out_a=$OUT err_a=$ERR
  mkdir -p "$CASE/plain" || die "mkdir plain failed"
  run_hook "$CASE/plain" GIT_CEILING_DIRECTORIES="$CASE"
  if [[ $rc_a -eq 0 && -z "$out_a" && -z "$err_a" && $RC -eq 0 && -z "$OUT" && -z "$ERR" ]]; then pass
  else fail "c8: no-origin RC=$rc_a OUT=$out_a ERR=$err_a; plain RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "9. a run past the budget is a could-not-check line, never silence"
  mk_case c9
  quiet "wt spent" git -C "$SHARED" worktree add -q --detach "$ROOT/spent" origin/main
  age_wt "$ROOT/spent" "$AGED_MTIME"
  # A staged plugin whose bounded runner reports every command out of time:
  # the budget ends by the runner's own verdict, never by waiting on a clock.
  local stage="$CASE/stage"
  stage_plugin "$stage"
  cp "${HERE}/../../skills/herdr-foreman/prune-worktrees.sh" "${HERE}/../../skills/herdr-foreman/prune-remote-branches.sh" \
    "$stage/skills/herdr-foreman/" || die "stage the owner scripts failed"
  printf '#!/usr/bin/env bash\nset -euo pipefail\necho "bounded-run: stand-in budget spent" >&2\nexit 124\n' > "$stage/skills/herdr-foreman/bounded-run.sh" \
    || die "write the stand-in runner failed"
  local real_hook="$HOOK"
  HOOK="$stage/hooks/$(basename "$real_hook")"
  run_hook "$SHARED"
  HOOK="$real_hook"
  ctx="$(context)"
  if [[ $RC -eq 0 && -e "$ROOT/spent" ]] && [[ "$ctx" == "Session-start status — could not check"*"time budget"* ]]; then pass
  else fail "c9: RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "10. a missing git or python3 is a could-not-check status, never stderr alone"
  mk_case c10
  local bare_path="$CASE/path-nogit" real_bash
  real_bash="$(command -v bash)" || die "bash not found"
  mkdir -p "$bare_path" "$CASE/path-nopy" || die "mkdir the PATH dirs failed"
  ln -s "$(command -v git)" "$CASE/path-nopy/git" || die "link git failed"
  local ctx_git ctx_py
  RC=0; OUT="$(cd "$SHARED" && PATH="$bare_path" "$real_bash" "$HOOK" </dev/null 2>"$CASE/hook.err")" || RC=$?
  ctx_git="$(context)"
  local rc_git=$RC
  RC=0; OUT="$(cd "$SHARED" && PATH="$CASE/path-nopy" "$real_bash" "$HOOK" </dev/null 2>"$CASE/hook.err")" || RC=$?
  ctx_py="$(context)"
  if [[ $rc_git -eq 0 && $RC -eq 0 ]] \
     && [[ "$ctx_git" == "Session-start status — could not check this repository"*"git is not on PATH"* ]] \
     && [[ "$ctx_py" == "Session-start status — could not check this repository"*"python3 is not on PATH"* ]]; then pass
  else fail "c10: git=$ctx_git python3=$ctx_py"; fi

  echo "13. under tessl a linked worktree runs neither owner script and changes no ref"
  mk_case c13
  quiet "wt linked" git -C "$SHARED" worktree add -q -b review/linked13 "$ROOT/linked" origin/main
  push_branch feat/remote-merged 1
  local stage13="$CASE/stage13" calls13="$CASE/calls13"
  stage_plugin "$stage13"
  cp "${HERE}/../../skills/herdr-foreman/bounded-run.sh" "$stage13/skills/herdr-foreman/" || die "stage the runner failed"
  local owner
  for owner in prune-worktrees.sh prune-remote-branches.sh; do
    printf '#!/usr/bin/env bash\nset -euo pipefail\nprintf "%%s\\n" "%s" >> %q\nexit 1\n' "$owner" "$calls13" > "$stage13/skills/herdr-foreman/$owner" \
      || die "write the recording $owner failed"
  done
  local refs_before refs_after
  refs_before="$(git -C "$SHARED" for-each-ref)" || die "for-each-ref failed"
  local real_hook13="$HOOK"
  HOOK="$stage13/hooks/$(basename "$real_hook13")"
  run_hook "$ROOT/linked" SESSION_START_MODE=portable
  HOOK="$real_hook13"
  refs_after="$(git -C "$SHARED" for-each-ref)" || die "for-each-ref failed"
  if [[ $RC -eq 0 && -z "$OUT" && ! -e "$calls13" && "$refs_before" == "$refs_after" ]] && on_origin feat/remote-merged; then pass
  else fail "c13: RC=$RC OUT=$OUT ERR=$ERR calls=$(cat "$calls13" 2>&1)"; fi

  echo "12. an owner result missing its documented fields is a could-not-check status, never a clean one"
  mk_case c12
  local stage12="$CASE/stage"
  stage_plugin "$stage12"
  cp "${HERE}/../../skills/herdr-foreman/bounded-run.sh" "$stage12/skills/herdr-foreman/" || die "stage the runner failed"
  printf '#!/usr/bin/env bash\nset -euo pipefail\nprintf "{}\\n"\n' > "$stage12/skills/herdr-foreman/prune-worktrees.sh" || die "write the stand-in prune failed"
  printf '#!/usr/bin/env bash\nset -euo pipefail\nprintf "{\\"deleted\\": [], \\"questionable\\": [{\\"branch\\": 7}], \\"kept\\": [], \\"failed\\": [], \\"could_not_check\\": null, \\"default_branch\\": \\"main\\"}\\n"\n' \
    > "$stage12/skills/herdr-foreman/prune-remote-branches.sh" || die "write the stand-in remote pass failed"
  local real_hook12="$HOOK"
  HOOK="$stage12/hooks/$(basename "$real_hook12")"
  run_hook "$SHARED"
  HOOK="$real_hook12"
  ctx="$(context)"
  if [[ $RC -eq 0 ]] && [[ "$ctx" == "Session-start status — could not check this repository"* ]] \
     && [[ "$ctx" == *"prune-worktrees.sh exited 0 with a result holding no worktrees_removed list"* ]] \
     && [[ "$ctx" == *"prune-remote-branches.sh exited 0 with a result holding a malformed questionable entry"* ]]; then pass
  else fail "c12: RC=$RC OUT=$OUT ERR=$ERR"; fi

  echo "11. a shared checkout whose name ends in a newline is cleaned, never truncated"
  local nl=$'\n'
  if mkdir "$TMP/nl-probe${nl}" 2>"$TMP/nl.err"; then
    rmdir "$TMP/nl-probe${nl}" || die "rmdir the newline probe failed"
    mk_case c11
    local nl_shared="$CASE/shared-nl${nl}"
    quiet "clone newline" git clone -q "$BARE" "$nl_shared"
    quiet "set-head newline" git -C "$nl_shared" remote set-head origin --auto
    quiet "wt spent" git -C "$nl_shared" worktree add -q --detach "$ROOT/spent" origin/main
    age_wt "$ROOT/spent" "$AGED_MTIME"
    push_branch feat/remote-merged 1
    run_hook "$nl_shared"
    if [[ $RC -eq 0 && -z "$OUT" && ! -e "$ROOT/spent" ]] && ! on_origin feat/remote-merged; then pass
    else fail "c11: RC=$RC OUT=$OUT ERR=$ERR"; fi
  else
    echo "11. skipped: this filesystem refuses a name ending in a newline ($(cat "$TMP/nl.err"))"
  fi

  echo "14. a git worktree list that fails after a valid prefix runs neither owner script"
  mk_case c14
  local stage14="$CASE/stage14" calls14="$CASE/calls14" real_git
  stage_recording_plugin "$stage14" "$calls14"
  real_git="$(command -v git)" || die "git not found"
  mkdir -p "$CASE/failing-git" || die "mkdir failing-git failed"
  # Prints the real inventory, then fails: the prefix alone parses cleanly.
  # shellcheck disable=SC2016  # The $@ belongs to the stand-in git.
  printf '#!/usr/bin/env bash\nset -euo pipefail\nif [[ "${1:-}" == worktree && "${2:-}" == list ]]; then\n  %q "$@"\n  echo "fatal: stand-in failure after the listing" >&2\n  exit 1\nfi\nexec %q "$@"\n' \
    "$real_git" "$real_git" > "$CASE/failing-git/git" || die "write the failing git failed"
  chmod +x "$CASE/failing-git/git" || die "chmod the failing git failed"
  local real_hook14="$HOOK"
  HOOK="$stage14/hooks/$(basename "$real_hook14")"
  RC=0
  OUT="$(cd "$SHARED" && env PATH="$CASE/failing-git:$CASE/bin:$PATH" FAKE_GH_DIR="$CASE" LEFTOVER_BUDGET_SEC=3600 \
    bash "$HOOK" </dev/null 2>"$CASE/hook.err")" || RC=$?
  HOOK="$real_hook14"
  ctx="$(context)"
  if [[ $RC -eq 0 && ! -e "$calls14" ]] \
     && [[ "$ctx" == "Session-start status — could not check this repository"*"git worktree list --porcelain -z"*"exited 1"* ]]; then pass
  else fail "c14: RC=$RC OUT=$OUT ERR=$(cat "$CASE/hook.err") calls=$(if [[ -e "$calls14" ]]; then cat "$calls14"; fi)"; fi

  echo "15. BatchMode reaches the owner scripts whether or not GIT_SSH_COMMAND is already set"
  mk_case c15
  local stage15="$CASE/stage15" calls15="$CASE/calls15" set_line unset_line
  stage_recording_plugin "$stage15" "$calls15"
  local real_hook15="$HOOK"
  HOOK="$stage15/hooks/$(basename "$real_hook15")"
  run_hook "$SHARED" GIT_SSH_COMMAND="ssh -i /keys/case15"
  set_line="$(head -n 1 "$calls15")"
  : > "$calls15" || die "clear calls15 failed"
  run_hook "$SHARED"
  unset_line="$(head -n 1 "$calls15")"
  HOOK="$real_hook15"
  if [[ "$set_line" == "prune-worktrees.sh ssh -i /keys/case15 -o BatchMode=yes" \
     && "$unset_line" == "prune-worktrees.sh ssh -o BatchMode=yes" ]]; then pass
  else fail "c15: set=$set_line unset=$unset_line ERR=$ERR"; fi

  echo "16. a hooks directory whose name ends in a newline still reaches its owner scripts"
  # A `$(dirname ...)` capture drops the newline, and the hook then looks for
  # its owner scripts beside a directory that does not exist (#487).
  if mkdir "$TMP/nl-probe16${nl}" 2>"$TMP/nl16.err"; then
    rmdir "$TMP/nl-probe16${nl}" || die "rmdir the newline probe failed"
    mk_case c16
    quiet "wt add" git -C "$SHARED" worktree add -q --detach "$ROOT/spent" origin/main
    age_wt "$ROOT/spent" "$AGED_MTIME"
    local stage16="$CASE/stage16"
    stage_plugin "$stage16" "hooks${nl}"
    cp "${HERE}/../../skills/herdr-foreman/bounded-run.sh" "${HERE}/../../skills/herdr-foreman/prune-worktrees.sh" \
      "${HERE}/../../skills/herdr-foreman/prune-remote-branches.sh" "$stage16/skills/herdr-foreman/" \
      || die "stage the owner scripts failed"
    mkdir -p "$stage16/skills/release" || die "mkdir the staged release skill failed"
    cp "${HERE}/../../skills/release/origin-repo.py" "$stage16/skills/release/" || die "stage origin-repo.py failed"
    local real_hook16="$HOOK"
    HOOK="$stage16/hooks${nl}/$(basename "$real_hook16")"
    run_hook "$SHARED"
    HOOK="$real_hook16"
    if [[ $RC -eq 0 && -z "$OUT" && ! -e "$ROOT/spent" ]]; then pass
    else fail "c16: RC=$RC OUT=$OUT ERR=$ERR"; fi
  else
    echo "16. skipped: this filesystem refuses a name ending in a newline ($(cat "$TMP/nl16.err"))"
  fi

  echo
  echo "passed=${PASS} failed=${FAIL}"
  (( FAIL == 0 ))
}

if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
  main "$@"
fi

README.md

tile.json