#!/bin/bash

set -u

ROOT=$(git rev-parse --show-toplevel) || exit 1
SUBJECT="$ROOT/.dev-config/bin/ipz-remote-php-gate"
HOOK="$ROOT/.dev-config/hooks/pre-commit"
REFERENCE_HOOK="$ROOT/.dev-config/hooks/reference-transaction"
REAL_ID=$(command -v id) || exit 1
export REAL_ID
TMP=$(mktemp -d) || exit 1
export TEST_LOG="$TMP/test.log"
PASS=0
FAIL=0

cleanup() {
    local rc=${1:-$?}
    trap - EXIT HUP INT TERM
    rm -rf "$TMP"
    exit "$rc"
}
trap 'cleanup $?' EXIT
trap 'cleanup 129' HUP
trap 'cleanup 130' INT
trap 'cleanup 143' TERM

ok() {
    PASS=$((PASS + 1))
    printf 'ok %d - %s\n' "$PASS" "$1"
}

not_ok() {
    FAIL=$((FAIL + 1))
    printf 'not ok - %s\n' "$1" >&2
}

assert_success() {
    local name=$1
    shift
    "$@" >"$TMP/assert.out" 2>&1
    local rc=$?
    if [ "$rc" -eq 0 ]; then ok "$name"; else not_ok "$name (exit $rc)"; while IFS= read -r line; do printf '%s\n' "$line" >&2; done <"$TMP/assert.out"; fi
}

assert_failure() {
    local name=$1 evidence=$2
    shift 2
    "$@" >"$TMP/assert.out" 2>&1
    local rc=$?
    if [ "$rc" -ne 0 ] && grep -Fq -- "$evidence" "$TMP/assert.out"; then
        ok "$name"
    else
        not_ok "$name (exit $rc; missing evidence: $evidence)"
        while IFS= read -r line; do printf '%s\n' "$line" >&2; done <"$TMP/assert.out"
    fi
}

make_fixture() {
    trap - EXIT HUP INT TERM
    local name=$1 repo="$TMP/$1"
    mkdir -p "$repo/plugins/international-press-zone/includes" \
        "$repo/plugins/international-press-zone/examples" \
        "$repo/plugins/international-press-zone/tools" \
        "$repo/.dev-config/bin" "$repo/.dev-config/hooks" "$repo/fake-bin" "$repo/no-slop-bin" "$repo/.slopgate/rules"
    git -C "$repo" init -q
    git -C "$repo" config user.name 'Remote Gate Test'
    git -C "$repo" config user.email remote-gate@example.invalid
    cp "$SUBJECT" "$repo/.dev-config/bin/ipz-remote-php-gate"
    cp "$HOOK" "$repo/.dev-config/hooks/pre-commit"
    chmod 700 "$repo/.dev-config/hooks/pre-commit"
    chmod 700 "$repo/.dev-config/bin/ipz-remote-php-gate"
    cp "$REFERENCE_HOOK" "$repo/.dev-config/hooks/reference-transaction"
    cp "$ROOT/.dev-config/bin/approve-gate-change" "$repo/.dev-config/bin/approve-gate-change"
    chmod 700 "$repo/.dev-config/hooks/reference-transaction" "$repo/.dev-config/bin/approve-gate-change"
    cp "$ROOT/.dev-config/package.json" "$repo/.dev-config/package.json"
    cp "$ROOT/.dev-config/package-lock.json" "$repo/.dev-config/package-lock.json"

    local reference_snapshot_count=0
    while IFS= read -r line || [ -n "$line" ]; do
        case "$line" in
            LOCAL_GATE=*) printf 'LOCAL_GATE="%s/mock-local-gate"\n' "$repo" ;;
            *'capture_trusted_install_snapshot "$repo_root" "$git_dir" || {'*)
                reference_snapshot_count=$((reference_snapshot_count + 1))
                if [ "$reference_snapshot_count" -eq 3 ]; then
                    printf '%s\n' \
                        '    if [ "${TEST_BEHAVIOR-}" = post-claim-reference-mutate ]; then' \
                        '        chmod 700 "$TEST_TRUSTED_INSTALL/reference-transaction"' \
                        '        printf "\\n# post-claim mutation\\n" >>"$TEST_TRUSTED_INSTALL/reference-transaction"' \
                        '        chmod 500 "$TEST_TRUSTED_INSTALL/reference-transaction"' \
                        '    fi'
                fi
                printf '%s\n' "$line"
                ;;
            *'echo "IPZ remote PHP gate passed for staged tree'*)
                printf '%s\n' \
                    '    if [ "${TEST_BEHAVIOR-}" = post-check-index-mutate ]; then' \
                    '        printf "%s\n" "<?php // post-check mutation" >"$TEST_ORIGINAL_REPO/plugins/international-press-zone/includes/post-check-mutation.php"' \
                    '        git -C "$TEST_ORIGINAL_REPO" add plugins/international-press-zone/includes/post-check-mutation.php >/dev/null 2>&1' \
                    '        seam_rc=$?' \
                    '        seam_tree=$(git -C "$TEST_ORIGINAL_REPO" write-tree 2>/dev/null || true)' \
                    '        printf "post-check-add-rc=%s\npost-check-tree=%s\n" "$seam_rc" "$seam_tree" >>"$TEST_LOG"' \
                    '    fi' \
                    '    if [ "${TEST_BEHAVIOR-}" = post-claim-interrupt ]; then' \
                    '        printf "post-claim-interrupt\n" >>"$TEST_LOG"' \
                    '        kill -TERM "$$"' \
                    '    fi'
                printf '%s\n' "$line"
                ;;
            *) printf '%s\n' "$line" ;;
        esac
    done <"$SUBJECT" >"$repo/.dev-config/bin/ipz-remote-php-gate.tmp"
    mv "$repo/.dev-config/bin/ipz-remote-php-gate.tmp" "$repo/.dev-config/bin/ipz-remote-php-gate"
    chmod 700 "$repo/.dev-config/bin/ipz-remote-php-gate"

    printf '%s\n' '{"name":"test/ipz","require-dev":{}}' >"$repo/plugins/international-press-zone/composer.json"
    printf '%s\n' '{"packages":[],"packages-dev":[]}' >"$repo/plugins/international-press-zone/composer.lock"
    printf '%s\n' '<ruleset><file>includes/</file><file>examples/</file><file>international-press-zone.php</file><file>uninstall.php</file></ruleset>' >"$repo/plugins/international-press-zone/phpcs.xml.dist"
    printf '%s\n' '{}' >"$repo/plugins/international-press-zone/phpcs-baseline.json"
    printf '%s\n' 'parameters:' '    paths:' '        - includes' '        - international-press-zone.php' '        - uninstall.php' >"$repo/plugins/international-press-zone/phpstan.neon.dist"
    printf '%s\n' 'parameters:' '    ignoreErrors: []' >"$repo/plugins/international-press-zone/phpstan-baseline.neon"
    printf '%s\n' 'roots = ["plugins/international-press-zone/includes", "plugins/international-press-zone/admin/src"]' 'exts = [".php", ".js", ".jsx", ".scss"]' 'rules = ["./rules/project.json"]' 'suppressions = "./suppressions.json"' >"$repo/.slopgate/config.toml"
    printf '%s\n' '{}' >"$repo/.slopgate/rules/project.json"
    printf '%s\n' '{"entries":[],"version":1}' >"$repo/.slopgate/suppressions.json"
    printf '%s\n' '<?php' '$args = array_slice($argv, 2);' 'file_put_contents(getenv("TEST_LOG"), "phpcs-args=" . var_export($args, true) . "\n", FILE_APPEND);' '$behavior = getenv("TEST_BEHAVIOR");' 'if ($behavior === "phpcs-tracked-mutate") { file_put_contents("includes/base.php", "<?php // phpcs tracked mutation\n"); }' 'if ($behavior === "phpcs-index-mutate") { file_put_contents("includes/base.php", "<?php // phpcs index mutation\n"); exec("git add includes/base.php"); }' 'if ($behavior === "phpcs-untracked-mutate") { file_put_contents("../../phpcs-rogue.txt", "rogue\n"); }' 'if ($behavior === "phpcs-fail") { exit(11); }' 'foreach ($args as $path) { if (str_contains((string) file_get_contents($path), "BAD")) { exit(12); } }' >"$repo/plugins/international-press-zone/tools/phpcs-baseline.php"
    printf '%s\n' '<?php // base' >"$repo/plugins/international-press-zone/includes/base.php"
    printf '%s\n' '<?php // delete' >"$repo/plugins/international-press-zone/includes/delete.php"
    printf '%s\n' '<?php // rename' >"$repo/plugins/international-press-zone/includes/rename.php"
    printf '%s\n' '*.ignored.php' >"$repo/.gitignore"
    printf '%s\n' '{"packages":[],"packages-dev":[]}' >"$repo/composer.lock"
    cat >"$repo/.dev-config/bin/lint" <<'LINT'
#!/bin/bash
printf 'unrelated-eslint=%s\n' "$*" >>"$TEST_LOG"
if [ "${TEST_BEHAVIOR-}" = async-stage-ipz ]; then
    (
        printf '%s\n' '<?php // asynchronously staged by candidate check' >plugins/international-press-zone/includes/async.php
        git add plugins/international-press-zone/includes/async.php
        printf 'async-stage-ipz=done\n' >>"$TEST_LOG"
    ) &
    child=$!
    wait "$child" || exit 32
fi
[ "${TEST_BEHAVIOR-}" != unrelated-eslint-fail ]
LINT
    chmod 700 "$repo/.dev-config/bin/lint"

    cat >"$repo/fake-bin/composer" <<'COMPOSER'
#!/bin/bash
printf 'composer-cwd=%s composer-args=%q\n' "$PWD" "$*" >>"$TEST_LOG"
if [ "${1-}" = "--version" ]; then echo 'Composer test 1.0'; exit 0; fi
if [ "${1-}" = audit ]; then [ "${TEST_BEHAVIOR-}" != unrelated-composer-fail ]; exit; fi
if [ "${TEST_BEHAVIOR-}" = "setup-fail" ]; then exit 23; fi
mkdir -p vendor/bin
if [ "${TEST_BEHAVIOR-}" != missing-phpcs-standard-directory ]; then
    mkdir -p vendor/wp-coding-standards/wpcs vendor/phpcsstandards/phpcsextra vendor/phpcsstandards/phpcsutils
fi
case "${TEST_BEHAVIOR-}" in
    missing-phpstan-wordpress-extension) ;;
    symlinked-phpstan-wordpress-extension)
        mkdir -p vendor/szepeviktor/phpstan-wordpress
        ln -s /etc/passwd vendor/szepeviktor/phpstan-wordpress/extension.neon
        ;;
    escaped-phpstan-wordpress-extension)
        mkdir -p vendor/szepeviktor escaped-phpstan
        printf '%s\n' 'parameters:' >escaped-phpstan/extension.neon
        ln -s ../../escaped-phpstan vendor/szepeviktor/phpstan-wordpress
        ;;
    *)
        mkdir -p vendor/szepeviktor/phpstan-wordpress
        printf '%s\n' 'parameters:' '    wordpress:' >vendor/szepeviktor/phpstan-wordpress/extension.neon
        ;;
esac
if [ "${TEST_BEHAVIOR-}" = phpstan-config-internal-symlink ]; then
    ln -s szepeviktor/phpstan-wordpress/extension.neon vendor/ipz-phpstan.neon
fi
case "${TEST_BEHAVIOR-}" in
    setup-tracked-mutate) printf '%s\n' '<?php // setup tracked mutation' >includes/base.php ;;
    setup-index-mutate) printf '%s\n' '<?php // setup index mutation' >includes/base.php; git add includes/base.php ;;
    setup-untracked-mutate) printf '%s\n' rogue >../../setup-rogue.txt ;;
    setup-runner-metadata-mutate) printf '%s' '1723456789012346' >../../.rb-epoch ;;
    setup-eslint-toolchain-mutate) printf '%s\n' changed >../../.dev-config/node_modules/@ast-grep/cli-linux-x64-gnu/ast-grep ;;
esac
cat >vendor/bin/phpcs <<'PHPCS'
#!/bin/bash
{
    printf 'phpcs-args='
    printf '%s|' "$@"
    printf '\n'
} >>"$TEST_LOG"
if [ "${1-}" = --config-set ]; then
    [ "${TEST_BEHAVIOR-}" != phpcs-config-fail ] || exit 19
    [ "${2-}" = installed_paths ] || exit 20
    [ "${3-}" = ../../wp-coding-standards/wpcs,../../phpcsstandards/phpcsextra,../../phpcsstandards/phpcsutils ] || exit 21
    exit 0
fi
if [ "${1-}" = --standard=WordPress ] && [ "${2-}" = -e ]; then
    [ "${TEST_BEHAVIOR-}" != phpcs-standard-unavailable ] || exit 22
    exit 0
fi
echo 'PHPCS test 1.0'
PHPCS
cat >vendor/bin/phpstan <<'PHPSTAN'
#!/bin/bash
if [ "${1-}" = "--version" ]; then echo 'PHPStan test 1.0'; exit 0; fi
printf 'phpstan-arg=%q\n' "$@" >>"$TEST_LOG"
[ "${1-}" = analyse ] || exit 24
[ "${2-}" = --configuration=vendor/ipz-phpstan.neon ] || exit 25
grep -Fxq '    - ../phpstan.neon.dist' vendor/ipz-phpstan.neon || exit 26
grep -Fxq '    - szepeviktor/phpstan-wordpress/extension.neon' vendor/ipz-phpstan.neon || exit 27
case "${TEST_BEHAVIOR-}" in
    phpstan-tracked-mutate) printf '%s\n' '<?php // phpstan tracked mutation' >includes/base.php ;;
    phpstan-index-mutate) printf '%s\n' '<?php // phpstan index mutation' >includes/base.php; git add includes/base.php ;;
    phpstan-untracked-mutate) printf '%s\n' rogue >../../phpstan-rogue.txt ;;
esac
if [ "${TEST_BEHAVIOR-}" = "phpstan-fail" ]; then exit 13; fi
for arg in "$@"; do
    [ -f "$arg" ] || continue
    if grep -q BAD "$arg"; then exit 14; fi
done
exit 0
PHPSTAN
chmod 700 vendor/bin/phpcs vendor/bin/phpstan
if [ "${TEST_BEHAVIOR-}" = "missing-tools" ]; then rm -f vendor/bin/phpstan; fi
exit 0
COMPOSER
    chmod 700 "$repo/fake-bin/composer"
    cat >"$repo/fake-bin/npm" <<'NPM'
#!/bin/bash
if [ "${1-}" = --version ]; then echo 'npm test 1.0'; exit 0; fi
printf 'npm-args=%s\n' "$*" >>"$TEST_LOG"
[ "${TEST_BEHAVIOR-}" != npm-install-fail ] || exit 28
[ "${1-}" = ci ] || exit 29
prefix=""
while [ $# -gt 0 ]; do
    case "$1" in
        --prefix) prefix=${2-}; shift 2 ;;
        *) shift ;;
    esac
done
[ -n "$prefix" ] || exit 30
rm -rf "$prefix/node_modules"
mkdir -p "$prefix/node_modules/.bin" "$prefix/node_modules/@ast-grep/cli" "$prefix/node_modules/@ast-grep/cli-linux-x64-gnu"
cat >"$prefix/node_modules/@ast-grep/cli/ast-grep" <<'AST_GREP_WRAPPER'
#!/bin/bash
printf 'ast-grep-wrapper-invoked=%s\n' "$*" >>"$TEST_LOG"
printf '%s\n' '[warn] wrapper must not run'
exit 35
AST_GREP_WRAPPER
chmod 700 "$prefix/node_modules/@ast-grep/cli/ast-grep"
ln -s ../@ast-grep/cli/ast-grep "$prefix/node_modules/.bin/ast-grep"
case "${TEST_BEHAVIOR-}" in
    missing-native-ast-grep) ;;
    escaping-native-ast-grep) ln -s /bin/true "$prefix/node_modules/@ast-grep/cli-linux-x64-gnu/ast-grep" ;;
    *)
        cat >"$prefix/node_modules/@ast-grep/cli-linux-x64-gnu/ast-grep" <<'AST_GREP_NATIVE'
#!/bin/bash
printf 'ast-grep-native-args=%s\n' "$*" >>"$TEST_LOG"
[ "${TEST_BEHAVIOR-}" != native-ast-grep-version-fail ] || exit 31
printf '%s\n' 'ast-grep test 1.0'
AST_GREP_NATIVE
        chmod 700 "$prefix/node_modules/@ast-grep/cli-linux-x64-gnu/ast-grep"
        ;;
esac
case "${TEST_BEHAVIOR-}" in
    npm-tracked-mutate) printf '%s\n' '<?php // npm tracked mutation' >"$prefix/../plugins/international-press-zone/includes/base.php" ;;
    npm-untracked-mutate) printf '%s\n' rogue >"$prefix/../npm-rogue.txt" ;;
    npm-runner-metadata-mutate) printf '%s' '1723456789012346' >"$prefix/../.rb-epoch" ;;
esac
exit 0
NPM
    chmod 700 "$repo/fake-bin/npm"
    cat >"$repo/fake-bin/slopgate" <<'SLOPGATE'
#!/bin/bash
if [ "${1-}" = "--version" ]; then echo 'slopgate-rs 0.1.0'; exit 0; fi
ast_grep="$PWD/node_modules/.bin/ast-grep"
[ -L "$ast_grep" ] || exit 32
case "$(readlink "$ast_grep")" in ../../.dev-config/node_modules/@ast-grep/cli-linux-x64-gnu/ast-grep) ;; *) exit 33 ;; esac
"$ast_grep" --version >/dev/null || exit 34
file=""
while [ $# -gt 0 ]; do
    case "$1" in
        --file) file=${2-}; shift 2 ;;
        --tier|--config) shift 2 ;;
        *) exit 31 ;;
    esac
done
printf 'slopgate-file=%q\n' "$file" >>"$TEST_LOG"
case "${TEST_BEHAVIOR-}" in
    slopgate-tracked-mutate) printf '%s\n' '<?php // slopgate tracked mutation' >plugins/international-press-zone/includes/base.php ;;
    slopgate-index-mutate) printf '%s\n' '<?php // slopgate index mutation' >plugins/international-press-zone/includes/base.php; git add plugins/international-press-zone/includes/base.php ;;
    slopgate-untracked-mutate) printf '%s\n' rogue >slopgate-rogue.txt ;;
    slopgate-compat-link-mutate) rm -f node_modules/.bin/ast-grep; ln -s /bin/true node_modules/.bin/ast-grep ;;
    slopgate-native-mutate) printf '%s\n' changed >.dev-config/node_modules/@ast-grep/cli-linux-x64-gnu/ast-grep ;;
esac
[ "${TEST_BEHAVIOR-}" != slopgate-fail ] || exit 17
[ -f "$file" ] || exit 18
exit 0
SLOPGATE
    chmod 700 "$repo/fake-bin/slopgate"
    cat >"$repo/fake-bin/trivy" <<'TRIVY'
#!/bin/bash
printf 'unrelated-trivy=%s\n' "$*" >>"$TEST_LOG"
[ "${TEST_BEHAVIOR-}" != unrelated-trivy-fail ]
TRIVY
    chmod 700 "$repo/fake-bin/trivy"
    cat >"$repo/fake-bin/git" <<'GIT'
#!/bin/bash
if [ "${1-}" = diff ]; then
    has_cached=0 has_zero=0 has_name_only=0 has_php=0 has_js=0 has_ipz=0 has_acmrt=0
    for arg in "$@"; do
        case "$arg" in
            --cached) has_cached=1 ;;
            -z) has_zero=1 ;;
            --name-only) has_name_only=1 ;;
            '*.php') has_php=1 ;;
            '*.js') has_js=1 ;;
            --diff-filter=ACMRT) has_acmrt=1 ;;
            plugins/international-press-zone/includes) has_ipz=1 ;;
        esac
    done
    if [ "$has_cached$has_zero$has_name_only" = 111 ]; then
        case "${TEST_BEHAVIOR-}" in
            discovery-js-fail) [ "$has_js" -eq 0 ] || exit 71 ;;
            discovery-php-fail) [ "$has_php$has_acmrt" != 11 ] || exit 72 ;;
            discovery-ipz-fail) [ "$has_ipz" -eq 0 ] || exit 73 ;;
        esac
    fi
fi
exec "$REAL_GIT" "$@"
GIT
    chmod 700 "$repo/fake-bin/git"
    cat >"$repo/fake-bin/id" <<'ID'
#!/bin/bash
if [ "${TEST_BEHAVIOR-}" = runner-metadata-group-mismatch ] && [ "${1-}" = -g ]; then
    group=$("$REAL_ID" -g) || exit 1
    printf '%s\n' "$((group + 1))"
    exit 0
fi
exec "$REAL_ID" "$@"
ID
    chmod 700 "$repo/fake-bin/id"
    cat >"$repo/fake-bin/uname" <<'UNAME'
#!/bin/bash
case "${1-}" in
    -s) if [ "${TEST_BEHAVIOR-}" = unsupported-native-platform ]; then printf '%s\n' FreeBSD; else printf '%s\n' Linux; fi ;;
    -m) printf '%s\n' x86_64 ;;
    *) exit 36 ;;
esac
UNAME
    chmod 700 "$repo/fake-bin/uname"
    local tool target
    for tool in bash git sha256sum mktemp chmod rm realpath stat cmp readlink cp id mkdir cat ln uname; do
        target=$(PATH="$ORIGINAL_PATH" command -v "$tool") || return 1
        ln -s "$target" "$repo/no-slop-bin/$tool"
    done
    ln -s "$repo/fake-bin/npm" "$repo/no-slop-bin/npm"

    cat >"$repo/mock-local-gate" <<'RUNNER'
#!/bin/bash
key=""
remote_only=0
while [ $# -gt 0 ]; do
    case "$1" in
        --remote-only) remote_only=1; shift ;;
        --key) key=${2-}; shift 2 ;;
        --) shift; break ;;
        --hosts) echo 'mock-local-gate: forbidden --hosts' >&2; exit 88 ;;
        *) echo "mock-local-gate: unexpected option $1" >&2; exit 89 ;;
    esac
done
[ "$remote_only" -eq 1 ] || exit 92
printf 'key=%s\ncwd=%s\n' "$key" "$PWD" >>"$TEST_LOG"
case "$PWD" in */plugins/international-press-zone) ;; *) exit 90 ;; esac
[ -f composer.lock ] || exit 91
export GATE0_REMOTE_ACTIVE=1
runner_root=$(git rev-parse --show-toplevel) || exit 93
printf '%s' '1723456789012345' >"$runner_root/.rb-epoch"
case "${TEST_BEHAVIOR-}" in
    runner-metadata-missing-origin) ;;
    runner-metadata-symlink) ln -s /etc/passwd "$runner_root/.rb-origin" ;;
    *) printf '%s' "$TEST_ORIGINAL_REPO" >"$runner_root/.rb-origin" ;;
esac
case "${TEST_BEHAVIOR-}" in
    runner-metadata-valid-lockhash) printf '%064d' 0 >"$runner_root/.rb-lockhash" ;;
    runner-metadata-invalid-lockhash) printf '%s' invalid >"$runner_root/.rb-lockhash" ;;
    runner-metadata-permissive) chmod 666 "$runner_root/.rb-epoch" ;;
    runner-metadata-group-valid-660) chmod 660 "$runner_root/.rb-epoch" "$runner_root/.rb-origin" ;;
    runner-metadata-group-valid-664|runner-metadata-group-mismatch) chmod 664 "$runner_root/.rb-epoch" "$runner_root/.rb-origin" ;;
esac
case "${TEST_BEHAVIOR-}" in
    eslint-toolchain-valid|setup-eslint-toolchain-mutate)
        mkdir -p "$runner_root/.dev-config/node_modules/eslint" "$runner_root/.dev-config/node_modules/.bin"
        printf '%s\n' clean >"$runner_root/.dev-config/node_modules/eslint/index.js"
        ln -s ../eslint/index.js "$runner_root/.dev-config/node_modules/.bin/eslint"
        ;;
    eslint-toolchain-symlink)
        mkdir -p "$runner_root/.dev-config/node_modules"
        ln -s /etc/passwd "$runner_root/.dev-config/node_modules/escape"
        ;;
esac
case "${TEST_BEHAVIOR-}" in
    exit-254) exit 254 ;;
    exit-97) exit 97 ;;
    exit-42) "$@"; exit 42 ;;
    missing-sentinel) exit 0 ;;
    wrong-sentinel) printf 'IPZ_REMOTE_PHP_GATE_OK nonce=wrong tree=wrong contract=wrong\n'; exit 0 ;;
    duplicate-sentinel) "$@"; rc=$?; [ "$rc" -eq 0 ] || exit "$rc"; printf 'IPZ_REMOTE_PHP_GATE_OK nonce=%s tree=%s contract=%s\n' "$3" "$4" "$5"; exit 0 ;;
    conflicting-sentinel) "$@"; rc=$?; [ "$rc" -eq 0 ] || exit "$rc"; printf 'IPZ_REMOTE_PHP_GATE_OK conflicting\n'; exit 0 ;;
    missing-slopgate) export PATH="$TEST_ORIGINAL_REPO/no-slop-bin" ;;
    parent-vector-mutate) git_dir=$(git -C "$TEST_ORIGINAL_REPO" rev-parse --absolute-git-dir); merge_head="$git_dir/MERGE_HEAD"; git -C "$TEST_ORIGINAL_REPO" rev-parse HEAD >>"$merge_head" ;;
    index-mutate) printf '%s\n' '<?php // mutation' >"$TEST_ORIGINAL_REPO/plugins/international-press-zone/includes/index-mutation.php"; git -C "$TEST_ORIGINAL_REPO" add plugins/international-press-zone/includes/index-mutation.php ;;
    config-mutate) printf '%s\n' '<ruleset><file>includes/</file></ruleset>' >"$TEST_ORIGINAL_REPO/plugins/international-press-zone/phpcs.xml.dist"; git -C "$TEST_ORIGINAL_REPO" add plugins/international-press-zone/phpcs.xml.dist ;;
    slop-config-mutate) printf '%s\n' 'roots = []' >"$TEST_ORIGINAL_REPO/.slopgate/config.toml"; git -C "$TEST_ORIGINAL_REPO" add .slopgate/config.toml ;;
    head-mutate) head=$(git -C "$TEST_ORIGINAL_REPO" rev-parse HEAD); tree=$(git -C "$TEST_ORIGINAL_REPO" rev-parse 'HEAD^{tree}'); new=$(printf '%s\n' mutation | git -C "$TEST_ORIGINAL_REPO" commit-tree "$tree" -p "$head"); ref=$(git -C "$TEST_ORIGINAL_REPO" symbolic-ref HEAD); git -C "$TEST_ORIGINAL_REPO" update-ref "$ref" "$new" "$head" ;;
    index-attempt) printf '%s\n' '<?php // mutation attempt' >"$TEST_ORIGINAL_REPO/plugins/international-press-zone/includes/index-attempt.php"; git -C "$TEST_ORIGINAL_REPO" add plugins/international-press-zone/includes/index-attempt.php >/dev/null 2>&1; printf 'index-attempt-rc=%s\n' "$?" >>"$TEST_LOG" ;;
    delay) sleep 2 ;;
    interrupt) trap 'exit 143' TERM INT; printf 'runner-ready\n' >>"$TEST_LOG"; sleep 2 ;;
esac
"$@"
rc=$?
[ "$rc" -eq 0 ] || exit "$rc"
case "${TEST_BEHAVIOR-}" in
    hooks-path-mutate) git -C "$TEST_ORIGINAL_REPO" config core.hooksPath .changed-hooks ;;
    reference-hook-mutate) chmod 700 "$TEST_TRUSTED_INSTALL/reference-transaction"; printf '\n# dispatch mutation\n' >>"$TEST_TRUSTED_INSTALL/reference-transaction"; chmod 500 "$TEST_TRUSTED_INSTALL/reference-transaction" ;;
    trusted-helper-mutate) chmod 700 "$TEST_TRUSTED_INSTALL/ipz-remote-php-gate"; printf '\n# dispatch mutation\n' >>"$TEST_TRUSTED_INSTALL/ipz-remote-php-gate"; chmod 500 "$TEST_TRUSTED_INSTALL/ipz-remote-php-gate" ;;
    trusted-precommit-mutate) chmod 700 "$TEST_TRUSTED_INSTALL/pre-commit"; printf '\n# dispatch mutation\n' >>"$TEST_TRUSTED_INSTALL/pre-commit"; chmod 500 "$TEST_TRUSTED_INSTALL/pre-commit" ;;
esac
exit 0
RUNNER
    chmod 700 "$repo/mock-local-gate"
    git -C "$repo" add . || return 1
    git -C "$repo" commit -q -m base || return 1
    git -C "$repo" update-ref refs/remotes/origin/master HEAD || return 1
    install_fixture_hooks "$repo" || return 1
    printf '%s\n' "$repo"
}

trusted_install_path() {
    local repo=$1 common
    common=$(git -C "$repo" rev-parse --git-common-dir) || return 1
    common=$(realpath "$repo/$common" 2>/dev/null || realpath "$common" 2>/dev/null) || return 1
    printf '%s/ipz-remote-gate-hooks\n' "$common"
}

install_fixture_hooks() {
    local repo=$1 install_dir
    install_dir=$(trusted_install_path "$repo") || return 1
    mkdir -p "$install_dir" || return 1
    chmod 700 "$install_dir" || return 1
    rm -f "$install_dir/pre-commit" "$install_dir/reference-transaction" "$install_dir/ipz-remote-php-gate" || return 1
    cp "$repo/.dev-config/hooks/pre-commit" "$install_dir/pre-commit" || return 1
    cp "$repo/.dev-config/hooks/reference-transaction" "$install_dir/reference-transaction" || return 1
    cp "$repo/.dev-config/bin/ipz-remote-php-gate" "$install_dir/ipz-remote-php-gate" || return 1
    chmod 500 "$install_dir/pre-commit" "$install_dir/reference-transaction" "$install_dir/ipz-remote-php-gate" || return 1
    git -C "$repo" config core.hooksPath "$install_dir"
}

stage_candidate() {
    local repo=$1 content=${2:-'<?php // candidate'}
    printf '%s\n' "$content" >"$repo/plugins/international-press-zone/includes/base.php"
    git -C "$repo" add plugins/international-press-zone/includes/base.php
}

run_gate() {
    local repo=$1 behavior=${2-} install_dir
    install_dir=$(trusted_install_path "$repo") || return 1
    : >"$TEST_LOG"
    (
        cd "$repo" || exit 125
        TEST_BEHAVIOR=$behavior TEST_ORIGINAL_REPO=$repo TEST_TRUSTED_INSTALL=$install_dir PATH="$repo/fake-bin:$ORIGINAL_PATH" \
            "$install_dir/ipz-remote-php-gate"
    )
}

run_claim_only() {
    local repo=$1 behavior=${2-} install_dir
    install_dir=$(trusted_install_path "$repo") || return 1
    : >"$TEST_LOG"
    (
        cd "$repo" || exit 125
        TEST_BEHAVIOR=$behavior TEST_ORIGINAL_REPO=$repo TEST_TRUSTED_INSTALL=$install_dir PATH="$repo/fake-bin:$ORIGINAL_PATH" \
            "$install_dir/ipz-remote-php-gate" --claim-only
    )
}

claim_path() {
    local repo=$1 git_dir
    git_dir=$(git -C "$repo" rev-parse --absolute-git-dir) || return 1
    printf '%s/ipz-remote-gate.claim\n' "$git_dir"
}

inflight_path() {
    local repo=$1 git_dir
    git_dir=$(git -C "$repo" rev-parse --absolute-git-dir) || return 1
    printf '%s/ipz-remote-gate.inflight\n' "$git_dir"
}

write_update() {
    local output=$1 old=$2 new=$3 ref=$4
    printf '%s %s %s\n' "$old" "$new" "$ref" >"$output"
}

run_reference() {
    local repo=$1 phase=$2 input=$3 install_dir
    install_dir=$(trusted_install_path "$repo") || return 1
    (
        cd "$repo" || exit 125
        "$install_dir/reference-transaction" "$phase" <"$input"
    )
}

abort_pending_claim() {
    local repo=$1 output=$2 head tree ref new update
    head=$(git -C "$repo" rev-parse HEAD) || return 1
    tree=$(git -C "$repo" write-tree) || return 1
    if ! ref=$(git -C "$repo" symbolic-ref -q HEAD); then ref=HEAD; fi
    new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m aborted-claim) || return 1
    update="$TMP/abort-pending.update"
    write_update "$update" "$head" "$new" "$ref"
    run_reference "$repo" aborted "$update" >"$output" 2>&1
}

run_remote_without_context() {
    local repo=$1 nonce contract tree
    nonce=$(printf 'a%.0s' {1..64})
    contract=$(printf 'b%.0s' {1..64})
    tree=$(git -C "$repo" rev-parse 'HEAD^{tree}') || return 1
    (
        cd "$repo/plugins/international-press-zone" || exit 125
        env -u GATE0_REMOTE_ACTIVE bash ../../.dev-config/bin/ipz-remote-php-gate --remote "$nonce" "$tree" "$contract"
    )
}

ORIGINAL_PATH=$PATH
REAL_GIT=$(PATH="$ORIGINAL_PATH" command -v git) || exit 1
export REAL_GIT

repo=$(make_fixture missing-remote-context)
assert_failure 'direct noncanonical remote mode blocks' 'canonical remote execution context is missing' run_remote_without_context "$repo"

repo=$(make_fixture trusted-install-contract)
install_dir=$(trusted_install_path "$repo")
configured_hooks=$(git -C "$repo" config --path --get core.hooksPath)
install_ok=1
[ "$configured_hooks" = "$install_dir" ] && [ "$(stat -c %a "$install_dir")" = 700 ] || install_ok=0
for contract_pair in \
    '.dev-config/hooks/pre-commit:pre-commit' \
    '.dev-config/bin/ipz-remote-php-gate:ipz-remote-php-gate' \
    '.dev-config/hooks/reference-transaction:reference-transaction'; do
    IFS=: read -r tracked installed <<<"$contract_pair"
    [ "$(stat -c %a "$install_dir/$installed")" = 500 ] \
        && [ "$(stat -c %u "$install_dir/$installed")" = "$(id -u)" ] \
        && cmp -s "$repo/$tracked" "$install_dir/$installed" || install_ok=0
done
if [ "$install_ok" -eq 1 ]; then ok 'trusted installation is external, hash-identical, owner-only, and canonically configured'; else not_ok 'trusted installation contract is not restrictive or exact'; fi

repo=$(make_fixture isolation)
stage_candidate "$repo" '<?php // GOOD staged'
printf '%s\n' '<?php // BAD unstaged' >"$repo/plugins/international-press-zone/includes/base.php"
printf '%s\n' '<?php // BAD untracked' >"$repo/plugins/international-press-zone/includes/untracked.php"
printf '%s\n' '<?php // BAD ignored' >"$repo/plugins/international-press-zone/bad.ignored.php"
assert_success 'exact staged tree excludes unstaged, untracked, and ignored files' run_gate "$repo"

repo=$(make_fixture paths)
stage_candidate "$repo"
printf '%s\n' '<?php // added' >"$repo/plugins/international-press-zone/includes/- added file.php"
printf '%s\n' '<?php // newline' >"$repo/plugins/international-press-zone/includes/line
break.php"
git -C "$repo" add plugins/international-press-zone/includes
git -C "$repo" rm -q plugins/international-press-zone/includes/delete.php
git -C "$repo" mv plugins/international-press-zone/includes/rename.php "plugins/international-press-zone/includes/renamed file.php"
assert_success 'add, delete, rename, leading-dash, space, and newline paths are NUL-safe' run_gate "$repo"
if grep -Fq -- '- added file.php' "$TEST_LOG" \
    && grep -Fq -- 'renamed file.php' "$TEST_LOG" \
    && grep -Fq -- 'line\nbreak.php' "$TEST_LOG" \
    && ! grep -Fq -- 'delete.php' "$TEST_LOG" \
    && ! grep -Fq -- 'rename.php' "$TEST_LOG"; then
    ok 'PHPCS and PHPStan receive only exact staged destination paths'
else
    not_ok 'analysis path set did not match staged destinations'
fi

repo=$(make_fixture runner-metadata-valid-lockhash)
stage_candidate "$repo"
assert_success 'canonical runner metadata and a valid lock hash are accepted and monitored' run_gate "$repo" runner-metadata-valid-lockhash
if grep -Fq 'phpcs-args=--config-set|installed_paths|../../wp-coding-standards/wpcs,../../phpcsstandards/phpcsextra,../../phpcsstandards/phpcsutils|' "$TEST_LOG" \
    && grep -Fq 'phpcs-args=--standard=WordPress|-e|' "$TEST_LOG"; then
    ok 'hardened Composer setup registers and verifies exact lock-owned PHPCS standards'
else
    not_ok 'hardened Composer setup did not register and verify exact PHPCS standards'
fi
if grep -Fq 'phpstan-arg=--configuration=vendor/ipz-phpstan.neon' "$TEST_LOG"; then
    ok 'hardened Composer setup explicitly loads the lock-owned PHPStan WordPress extension'
else
    not_ok 'hardened Composer setup did not load the PHPStan WordPress extension'
fi
if grep -Fq 'npm-args=ci --prefix ' "$TEST_LOG" \
    && grep -Fq -- '--ignore-scripts --no-audit --no-fund' "$TEST_LOG" \
    && grep -Fq 'ast-grep-native-args=--version' "$TEST_LOG" \
    && ! grep -Fq 'ast-grep-wrapper-invoked=' "$TEST_LOG"; then
    ok 'slopgate uses the hardened lock-owned native AST toolchain without the warning wrapper'
else
    not_ok 'slopgate did not use the hardened lock-owned native AST toolchain directly'
fi

repo=$(make_fixture phpstan-config-internal-symlink)
stage_candidate "$repo"
assert_success 'PHPStan registration replaces an internal dependency symlink without following it' run_gate "$repo" phpstan-config-internal-symlink

for behavior in runner-metadata-group-valid-660 runner-metadata-group-valid-664; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    assert_success "$behavior is accepted for the effective runner group" run_gate "$repo" "$behavior"
done

repo=$(make_fixture eslint-toolchain-valid)
stage_candidate "$repo"
assert_success 'canonical ESLint toolchain cache and confined internal symlinks are accepted and monitored' run_gate "$repo" eslint-toolchain-valid

repo=$(make_fixture eslint-toolchain-symlink)
stage_candidate "$repo"
assert_failure 'ESLint toolchain symlinks escaping the cache are rejected before analyzer setup' 'remote repository state is not pristine before setup' run_gate "$repo" eslint-toolchain-symlink

for behavior in runner-metadata-missing-origin runner-metadata-symlink runner-metadata-invalid-lockhash runner-metadata-permissive runner-metadata-group-mismatch; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    assert_failure "$behavior blocks before analyzer setup" 'remote repository state is not pristine before setup' run_gate "$repo" "$behavior"
done

for behavior in phpcs-fail phpstan-fail setup-fail missing-tools missing-phpcs-standard-directory phpcs-config-fail phpcs-standard-unavailable missing-phpstan-wordpress-extension symlinked-phpstan-wordpress-extension escaped-phpstan-wordpress-extension npm-install-fail missing-native-ast-grep escaping-native-ast-grep unsupported-native-platform native-ast-grep-version-fail slopgate-fail exit-254 exit-97 exit-42 missing-sentinel wrong-sentinel duplicate-sentinel conflicting-sentinel; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    case "$behavior" in
        phpcs-fail) evidence='PHPCS failed (exit 11)' ;;
        phpstan-fail) evidence='PHPStan failed (exit 13)' ;;
        setup-fail) evidence='Composer install failed (exit 23)' ;;
        missing-tools) evidence='installed PHPStan executable is missing' ;;
        missing-phpcs-standard-directory) evidence='installed PHPCS standard directory is missing or symlinked: vendor/wp-coding-standards/wpcs' ;;
        phpcs-config-fail) evidence='PHPCS standard registration failed (exit 19)' ;;
        phpcs-standard-unavailable) evidence='WordPress PHPCS standard is unavailable after registration (exit 22)' ;;
        missing-phpstan-wordpress-extension|symlinked-phpstan-wordpress-extension) evidence='installed PHPStan WordPress extension is missing or symlinked' ;;
        escaped-phpstan-wordpress-extension) evidence='installed PHPStan WordPress extension escapes its lock-owned path' ;;
        npm-install-fail) evidence='AST toolchain installation failed (exit 28)' ;;
        missing-native-ast-grep|escaping-native-ast-grep) evidence='lock-owned native ast-grep executable is missing, symlinked, or not executable' ;;
        unsupported-native-platform) evidence='AST toolchain platform is unsupported: FreeBSD/x86_64' ;;
        native-ast-grep-version-fail) evidence='native ast-grep version check failed (exit 31)' ;;
        slopgate-fail) evidence='slopgate failed for plugins/international-press-zone/includes/base.php (exit 17)' ;;
        exit-254) evidence='canonical remote runner failed (exit 254)' ;;
        exit-97) evidence='canonical remote runner failed (exit 97)' ;;
        exit-42) evidence='canonical remote runner failed (exit 42)' ;;
        missing-sentinel) evidence='remote completion sentinel count is 0, expected 1' ;;
        wrong-sentinel) evidence='remote completion sentinel is missing, malformed, or conflicting' ;;
        duplicate-sentinel|conflicting-sentinel) evidence='remote completion sentinel count is 2, expected 1' ;;
    esac
    assert_failure "$behavior blocks the gate" "$evidence" run_gate "$repo" "$behavior"
    if [ "$behavior" = exit-254 ]; then
        if grep -Fq 'canonical remote runner failed (exit 254)' "$TMP/assert.out"; then ok 'exit 254 propagates directly from the package-owner runner'; else not_ok 'exit 254 was not observed directly'; fi
    fi
    if [ "$behavior" = exit-97 ]; then
        if grep -Fq 'canonical remote runner failed (exit 97)' "$TMP/assert.out" \
            && ! grep -Fq 'Composer test' "$TMP/assert.out"; then ok 'remote refusal has no local analysis fallback'; else not_ok 'remote refusal attempted a fallback'; fi
    fi
    if [ "$behavior" = exit-42 ]; then
        if grep -Fq 'canonical remote runner failed (exit 42)' "$TMP/assert.out"; then ok 'nonzero remote status wins over a valid-looking sentinel'; else not_ok 'nonzero remote status was masked'; fi
    fi
done

for behavior in npm-tracked-mutate npm-untracked-mutate npm-runner-metadata-mutate; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    assert_failure "$behavior is detected remotely" 'remote repository state changed outside slopgate dependency setup boundaries' run_gate "$repo" "$behavior"
done
for behavior in setup-tracked-mutate setup-index-mutate setup-untracked-mutate setup-runner-metadata-mutate setup-eslint-toolchain-mutate; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    assert_failure "$behavior is detected remotely" 'remote repository state changed outside dependency setup boundaries' run_gate "$repo" "$behavior"
done
for analyzer in phpcs phpstan slopgate; do
    case "$analyzer" in phpcs) analyzer_label=PHPCS ;; phpstan) analyzer_label=PHPStan ;; *) analyzer_label=slopgate ;; esac
    for mutation in tracked index untracked; do
        behavior="$analyzer-$mutation-mutate"
        repo=$(make_fixture "$behavior")
        stage_candidate "$repo"
        assert_failure "$behavior is detected remotely" "remote repository state changed during $analyzer_label" run_gate "$repo" "$behavior"
    done
done

for behavior in slopgate-compat-link-mutate slopgate-native-mutate; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    assert_failure "$behavior is detected remotely" 'remote repository state changed during slopgate' run_gate "$repo" "$behavior"
done

repo=$(make_fixture composer-hardening)
stage_candidate "$repo"
assert_success 'Composer hardening fixture completes' run_gate "$repo"
if grep -Fq -- '--no-scripts' "$TEST_LOG" && grep -Fq -- '--no-plugins' "$TEST_LOG"; then
    ok 'remote Composer install disables scripts and plugins'
else
    not_ok 'remote Composer install did not disable scripts and plugins'
fi

repo=$(make_fixture slop-only)
mkdir -p "$repo/plugins/international-press-zone/admin/src/components"
printf '%s\n' 'const clean = true;' >"$repo/plugins/international-press-zone/admin/src/components/clean file.js"
git -C "$repo" add plugins/international-press-zone/admin/src/components/clean\ file.js
assert_success 'slopgate-only staged path runs remotely without Composer setup' run_gate "$repo"
if grep -Fq 'slopgate-file=plugins/international-press-zone/admin/src/components/clean\ file.js' "$TEST_LOG" \
    && ! grep -Fq 'Composer test' "$TMP/assert.out"; then ok 'slopgate receives the exact staged config-root destination'; else not_ok 'slopgate-only path or setup was incorrect'; fi

repo=$(make_fixture missing-slopgate)
mkdir -p "$repo/plugins/international-press-zone/admin/src"
printf '%s\n' 'const clean = true;' >"$repo/plugins/international-press-zone/admin/src/clean.js"
git -C "$repo" add plugins/international-press-zone/admin/src/clean.js
assert_failure 'missing remote slopgate tool blocks' 'slopgate is unavailable' run_gate "$repo" missing-slopgate

for behavior in index-mutate config-mutate slop-config-mutate; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    assert_failure "$behavior is detected after remote success" 'staged tree changed while the remote gate ran' run_gate "$repo" "$behavior"
done

repo=$(make_fixture parent-vector-mutate)
stage_candidate "$repo"
head=$(git -C "$repo" rev-parse HEAD)
base_tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
merge_parent=$(git -C "$repo" commit-tree "$base_tree" -p "$head" -m parent-vector-mutation-base)
merge_head_path="$(git -C "$repo" rev-parse --absolute-git-dir)/MERGE_HEAD"
printf '%s\n' "$merge_parent" >"$merge_head_path"
assert_failure 'merge parent mutation is detected after remote success' 'commit parent vector changed while the remote gate ran' \
    run_gate "$repo" parent-vector-mutate
rm -f "$merge_head_path"

repo=$(make_fixture head-mutate)
stage_candidate "$repo"
head_before=$(git -C "$repo" rev-parse HEAD)
run_gate "$repo" head-mutate >"$TMP/head-mutate.out" 2>&1
rc=$?
head_after=$(git -C "$repo" rev-parse HEAD)
if [ "$rc" -eq 0 ] && [ "$head_after" = "$head_before" ] \
    && grep -Fq 'normal checked-out-ref commit is missing its required claim' "$TMP/head-mutate.out"; then
    ok 'unclaimed checked-out-ref mutation is rejected during remote dispatch'
else
    not_ok 'unclaimed checked-out-ref mutation escaped during remote dispatch'
fi
run_reference "$repo" aborted /dev/null >"$TMP/head-mutate-aborted.out" 2>&1

for behavior in hooks-path-mutate reference-hook-mutate trusted-helper-mutate trusted-precommit-mutate; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    assert_failure "$behavior is detected before claim creation" 'trusted gate installation' run_gate "$repo" "$behavior"
done

repo=$(make_fixture missing-contract)
stage_candidate "$repo"
git -C "$repo" rm -q plugins/international-press-zone/phpstan-baseline.neon
assert_failure 'missing staged contract file blocks before dispatch' 'required staged contract file is missing or unsafe: plugins/international-press-zone/phpstan-baseline.neon' run_gate "$repo"

repo=$(make_fixture missing-slop-config)
stage_candidate "$repo"
git -C "$repo" rm -q .slopgate/config.toml
assert_failure 'missing staged slopgate config blocks before dispatch' 'required staged contract file is missing or unsafe: .slopgate/config.toml' run_gate "$repo"

repo=$(make_fixture contract-symlink)
stage_candidate "$repo"
rm -f "$repo/plugins/international-press-zone/phpstan-baseline.neon"
ln -s /etc/passwd "$repo/plugins/international-press-zone/phpstan-baseline.neon"
git -C "$repo" add plugins/international-press-zone/phpstan-baseline.neon
assert_failure 'symlinked staged contract blocks before dispatch' 'required staged contract file is missing or unsafe: plugins/international-press-zone/phpstan-baseline.neon' run_gate "$repo"

policy_files=(
    .dev-config/package.json
    .dev-config/package-lock.json
    plugins/international-press-zone/composer.json
    plugins/international-press-zone/composer.lock
    plugins/international-press-zone/phpcs.xml.dist
    plugins/international-press-zone/phpcs-baseline.json
    plugins/international-press-zone/tools/phpcs-baseline.php
    plugins/international-press-zone/phpstan.neon.dist
    plugins/international-press-zone/phpstan-baseline.neon
    .slopgate/config.toml
    .slopgate/rules/project.json
    .slopgate/suppressions.json
)
for policy_file in "${policy_files[@]}"; do
    policy_name=${policy_file//\//-}
    repo=$(make_fixture "immutable-$policy_name")
    stage_candidate "$repo"
    printf '\nfixture policy mutation\n' >>"$repo/$policy_file"
    git -C "$repo" add "$policy_file"
    case "$policy_file" in
        .dev-config/package.json|.dev-config/package-lock.json) evidence='staged AST toolchain policy does not match the trusted gate contract' ;;
        *) evidence="staged analyzer policy changes are not allowed: $policy_file" ;;
    esac
    assert_failure "staged analyzer policy change blocks: $policy_file" "$evidence" run_gate "$repo"
done

for source_case in pre-commit ipz-remote-php-gate reference-transaction; do
    repo=$(make_fixture "mismatched-source-$source_case")
    stage_candidate "$repo"
    case "$source_case" in
        pre-commit) source_path=.dev-config/hooks/pre-commit ;;
        ipz-remote-php-gate) source_path=.dev-config/bin/ipz-remote-php-gate ;;
        reference-transaction) source_path=.dev-config/hooks/reference-transaction ;;
    esac
    printf '%s\n' '#!/bin/bash' 'exit 0' >"$repo/$source_path"
    chmod 700 "$repo/$source_path"
    git -C "$repo" add "$source_path"
    if [ "$source_case" = ipz-remote-php-gate ]; then
        assert_failure "candidate helper source cannot forge remote completion" "remote completion sentinel count is 0, expected 1" run_gate "$repo"
    else
        assert_success "candidate $source_case source preserves landed installation trust" run_gate "$repo"
    fi
done

repo=$(make_fixture missing-trusted-reference)
stage_candidate "$repo"
install_dir=$(trusted_install_path "$repo")
rm -f "$install_dir/reference-transaction"
assert_failure 'missing trusted reference hook blocks before dispatch' 'cannot capture the trusted gate installation' run_gate "$repo"

repo=$(make_fixture unsafe-trusted-permissions)
stage_candidate "$repo"
install_dir=$(trusted_install_path "$repo")
chmod 700 "$install_dir/ipz-remote-php-gate"
assert_failure 'writable trusted helper blocks before dispatch' 'cannot capture the trusted gate installation' run_gate "$repo"

repo=$(make_fixture path-escape)
stage_candidate "$repo"
ln -s /etc/passwd "$repo/plugins/international-press-zone/includes/escape.php"
git -C "$repo" add plugins/international-press-zone/includes/escape.php
assert_failure 'symlink path escape blocks' 'staged gate candidate is not a regular file: includes/escape.php' run_gate "$repo"

repo=$(make_fixture cleanup-success)
stage_candidate "$repo"
assert_success 'success path completes' run_gate "$repo"
last_cwd=$(while IFS= read -r line; do case "$line" in cwd=*) printf '%s\n' "${line#cwd=}" ;; esac; done <"$TEST_LOG")
if [ -n "$last_cwd" ] && [ ! -e "${last_cwd%/plugins/international-press-zone}" ]; then ok 'success cleanup removes the detached worktree'; else not_ok 'success cleanup left the detached worktree'; fi

repo=$(make_fixture claim-only-success)
printf '%s\n' 'const claimOnly = true;' >"$repo/claim-only.js"
git -C "$repo" add claim-only.js
assert_success 'claim-only path binds a non-applicable staged tree' run_claim_only "$repo"
claim=$(claim_path "$repo")
if [ -f "$claim" ] && ! grep -Fq 'key=' "$TEST_LOG"; then ok 'claim-only path creates a claim without remote dispatch'; else not_ok 'claim-only path dispatched remotely or omitted its claim'; fi
abort_pending_claim "$repo" "$TMP/claim-only-aborted.out"

repo=$(make_fixture claim-only-applicable)
stage_candidate "$repo"
assert_failure 'claim-only path rejects applicable IPZ candidates' 'claim-only mode found applicable staged IPZ candidates' run_claim_only "$repo"

repo=$(make_fixture cleanup-failure)
stage_candidate "$repo"
run_gate "$repo" phpcs-fail >"$TMP/cleanup-failure.out" 2>&1
rc=$?
last_cwd=$(while IFS= read -r line; do case "$line" in cwd=*) printf '%s\n' "${line#cwd=}" ;; esac; done <"$TEST_LOG")
if [ "$rc" -ne 0 ] && [ -n "$last_cwd" ] && [ ! -e "${last_cwd%/plugins/international-press-zone}" ]; then ok 'failure cleanup removes the detached worktree'; else not_ok 'failure cleanup left the detached worktree'; fi

repo=$(make_fixture interruption)
stage_candidate "$repo"
install_dir=$(trusted_install_path "$repo")
export TEST_BEHAVIOR=interrupt TEST_ORIGINAL_REPO=$repo TEST_TRUSTED_INSTALL=$install_dir PATH="$repo/fake-bin:$ORIGINAL_PATH"
: >"$TEST_LOG"
setsid bash -c 'cd "$1" || exit 125; exec "$2/ipz-remote-php-gate"' bash "$repo" "$install_dir" >"$TMP/interruption.out" 2>&1 &
gate_pid=$!
ready=0
for unused in 1 2 3 4 5 6 7 8 9 10; do
    while IFS= read -r line; do [ "$line" = runner-ready ] && ready=1; done <"$TEST_LOG"
    [ "$ready" -eq 1 ] && break
    sleep 1
done
kill -TERM -- "-$gate_pid" 2>/dev/null || true
wait "$gate_pid"
rc=$?
last_cwd=$(while IFS= read -r line; do case "$line" in cwd=*) printf '%s\n' "${line#cwd=}" ;; esac; done <"$TEST_LOG")
if [ "$ready" -eq 1 ] && [ "$rc" -ne 0 ] && [ -n "$last_cwd" ] && [ ! -e "${last_cwd%/plugins/international-press-zone}" ]; then ok 'interruption cleanup removes the detached worktree'; else not_ok 'interruption cleanup failed'; fi

repo=$(make_fixture concurrency)
stage_candidate "$repo"
install_dir=$(trusted_install_path "$repo")
export TEST_BEHAVIOR=delay TEST_ORIGINAL_REPO=$repo TEST_TRUSTED_INSTALL=$install_dir PATH="$repo/fake-bin:$ORIGINAL_PATH"
: >"$TEST_LOG"
(cd "$repo" && "$install_dir/ipz-remote-php-gate") >"$TMP/concurrent-1.out" 2>&1 & p1=$!
(cd "$repo" && "$install_dir/ipz-remote-php-gate") >"$TMP/concurrent-2.out" 2>&1 & p2=$!
wait "$p1"; rc1=$?
wait "$p2"; rc2=$?
keys=()
while IFS= read -r line; do case "$line" in key=*) keys+=("${line#key=}") ;; esac; done <"$TEST_LOG"
if { [ "$rc1" -eq 0 ] && [ "$rc2" -ne 0 ]; } || { [ "$rc1" -ne 0 ] && [ "$rc2" -eq 0 ]; }; then
    if [ ${#keys[@]} -eq 1 ] \
        && { grep -Fq 'existing live or unknown reference claim blocks this commit' "$TMP/concurrent-1.out" \
            || grep -Fq 'existing live or unknown reference claim blocks this commit' "$TMP/concurrent-2.out"; }; then
        ok 'concurrent runs serialize, dispatch once, and issue one live claim'
    else
        not_ok 'concurrent claim or dispatch behavior was incorrect'
    fi
else
    not_ok 'concurrent runs did not allow exactly one live claim'
fi

repo=$(make_fixture unique-keys)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/unique-key-first.out" 2>&1
key_first=$(while IFS= read -r line; do case "$line" in key=*) printf '%s\n' "${line#key=}" ;; esac; done <"$TEST_LOG")
abort_pending_claim "$repo" "$TMP/unique-key-aborted.out"
run_gate "$repo" >"$TMP/unique-key-second.out" 2>&1
key_second=$(while IFS= read -r line; do case "$line" in key=*) printf '%s\n' "${line#key=}" ;; esac; done <"$TEST_LOG")
if [ -n "$key_first" ] && [ -n "$key_second" ] && [ "$key_first" != "$key_second" ]; then ok 'sequential dispatches use unique nonce keys'; else not_ok 'sequential dispatch keys were missing or reused'; fi
abort_pending_claim "$repo" "$TMP/unique-key-final-aborted.out"

repo=$(make_fixture reference-lifecycle)
stage_candidate "$repo"
assert_success 'reference lifecycle gate creates a claim' run_gate "$repo"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m exact-reference-test)
update="$TMP/reference-lifecycle.update"
write_update "$update" "$head" "$new" "$ref"
claim=$(claim_path "$repo")
inflight=$(inflight_path "$repo")
if run_reference "$repo" prepared "$update" >"$TMP/reference-prepared.out" 2>&1 \
    && [ ! -e "$claim" ] && [ -f "$inflight" ]; then
    ok 'prepared phase consumes the exact claim once'
else
    not_ok 'prepared phase did not consume the exact claim'
fi
if run_reference "$repo" committed "$update" >"$TMP/reference-committed.out" 2>&1 \
    && [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then
    ok 'committed phase finalizes the exact in-flight claim'
else
    not_ok 'committed phase did not finalize the exact in-flight claim'
fi

repo=$(make_fixture reference-merge-lifecycle)
stage_candidate "$repo"
head=$(git -C "$repo" rev-parse HEAD)
base_tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
merge_parent=$(git -C "$repo" commit-tree "$base_tree" -p "$head" -m merge-parent)
merge_head_path="$(git -C "$repo" rev-parse --absolute-git-dir)/MERGE_HEAD"
printf '%s\n' "$merge_parent" >"$merge_head_path"
assert_success 'merge lifecycle gate creates a complete ordered parent claim' run_gate "$repo"
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -p "$merge_parent" -m exact-merge-reference-test)
update="$TMP/reference-merge-lifecycle.update"
write_update "$update" "$head" "$new" "$ref"
if run_reference "$repo" prepared "$update" >"$TMP/reference-merge-prepared.out" 2>&1 \
    && run_reference "$repo" committed "$update" >"$TMP/reference-merge-committed.out" 2>&1; then
    ok 'matching ordered merge-parent vector completes its claim lifecycle'
else
    not_ok 'matching ordered merge-parent vector failed its claim lifecycle'
fi
rm -f "$merge_head_path"

repo=$(make_fixture reference-merge-reordered)
stage_candidate "$repo"
head=$(git -C "$repo" rev-parse HEAD)
base_tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
merge_parent=$(git -C "$repo" commit-tree "$base_tree" -p "$head" -m reordered-merge-parent)
merge_head_path="$(git -C "$repo" rev-parse --absolute-git-dir)/MERGE_HEAD"
printf '%s\n' "$merge_parent" >"$merge_head_path"
run_gate "$repo" >"$TMP/reference-merge-reordered-gate.out" 2>&1
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
reordered=$(git -C "$repo" commit-tree "$tree" -p "$merge_parent" -p "$head" -m reordered-merge)
update="$TMP/reference-merge-reordered.update"
write_update "$update" "$head" "$reordered" "$ref"
assert_failure 'reordered merge-parent vector blocks' 'proposed reference transaction does not match the tested tree' \
    run_reference "$repo" prepared "$update"
rm -f "$merge_head_path"

repo=$(make_fixture reference-aborted)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-aborted-gate.out" 2>&1
claim=$(claim_path "$repo")
inflight=$(inflight_path "$repo")
if [ -f "$claim" ] && abort_pending_claim "$repo" "$TMP/reference-aborted.out" \
    && [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then
    ok 'aborted phase burns pending claim state'
else
    not_ok 'aborted phase left claim state'
fi

for mismatch in noncommit-new wrong-tree wrong-parent extra-parent multiple-updates; do
    repo=$(make_fixture "reference-$mismatch")
    stage_candidate "$repo"
    run_gate "$repo" >"$TMP/reference-$mismatch-gate.out" 2>&1
    head=$(git -C "$repo" rev-parse HEAD)
    tree=$(git -C "$repo" write-tree)
    ref=$(git -C "$repo" symbolic-ref HEAD)
    new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m "reference-$mismatch")
    update="$TMP/reference-$mismatch.update"
    case "$mismatch" in
        noncommit-new) write_update "$update" "$head" "$tree" "$ref" ;;
        wrong-tree)
            wrong_tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
            wrong_new=$(git -C "$repo" commit-tree "$wrong_tree" -p "$head" -m wrong-tree)
            write_update "$update" "$head" "$wrong_new" "$ref"
            ;;
        wrong-parent)
            wrong_new=$(git -C "$repo" commit-tree "$tree" -m wrong-parent)
            write_update "$update" "$head" "$wrong_new" "$ref"
            ;;
        extra-parent)
            extra_parent=$(git -C "$repo" commit-tree "$(git -C "$repo" rev-parse 'HEAD^{tree}')" -p "$head" -m extra-parent-base)
            wrong_new=$(git -C "$repo" commit-tree "$tree" -p "$head" -p "$extra_parent" -m extra-parent)
            write_update "$update" "$head" "$wrong_new" "$ref"
            ;;
        multiple-updates)
            write_update "$update" "$head" "$new" "$ref"
            printf '%s %s %s\n' "$head" "$new" refs/heads/unrelated >>"$update"
            ;;
    esac
    assert_failure "reference $mismatch transaction blocks" 'proposed reference transaction does not match the tested tree' run_reference "$repo" prepared "$update"
    claim=$(claim_path "$repo")
    inflight=$(inflight_path "$repo")
    if [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then ok "reference $mismatch rejection burns its claim"; else not_ok "reference $mismatch rejection left reusable state"; fi
done

repo=$(make_fixture reference-malformed)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-malformed-gate.out" 2>&1
claim=$(claim_path "$repo")
printf '%s\n' 'version=broken' >"$claim"
chmod 600 "$claim"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m malformed)
update="$TMP/reference-malformed.update"
write_update "$update" "$head" "$new" "$ref"
assert_failure 'malformed claim blocks a reference update' 'malformed or unsafe claim' run_reference "$repo" prepared "$update"
assert_failure 'malformed claim cannot authorize a later gate' 'existing live or unknown reference claim blocks this commit' run_gate "$repo"

repo=$(make_fixture reference-stale)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-stale-gate.out" 2>&1
claim=$(claim_path "$repo")
stale="$claim.stale"
while IFS= read -r line; do case "$line" in created=*) printf 'created=1\n' ;; *) printf '%s\n' "$line" ;; esac; done <"$claim" >"$stale"
chmod 600 "$stale"
mv "$stale" "$claim"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m stale)
update="$TMP/reference-stale.update"
write_update "$update" "$head" "$new" "$ref"
assert_failure 'stale claim blocks a reference update' 'stale or foreign claim' run_reference "$repo" prepared "$update"
if [ ! -e "$claim" ]; then ok 'stale claim is burned'; else not_ok 'stale claim remained reusable'; fi

repo=$(make_fixture reference-contract-mutate)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-contract-mutate-gate.out" 2>&1
claim=$(claim_path "$repo")
install_dir=$(trusted_install_path "$repo")
chmod 700 "$install_dir/ipz-remote-php-gate"
printf '%s\n' 'exit 0' >>"$install_dir/ipz-remote-php-gate"
chmod 500 "$install_dir/ipz-remote-php-gate"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m contract-mutate)
update="$TMP/reference-contract-mutate.update"
write_update "$update" "$head" "$new" "$ref"
assert_failure 'changed active gate contract invalidates its claim' 'stale or foreign claim' run_reference "$repo" prepared "$update"
if [ ! -e "$claim" ]; then ok 'contract-mismatched claim is burned'; else not_ok 'contract-mismatched claim remained reusable'; fi

repo=$(make_fixture reference-symlink)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-symlink-gate.out" 2>&1
claim=$(claim_path "$repo")
mv "$claim" "$claim.target"
ln -s "$claim.target" "$claim"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m symlink)
update="$TMP/reference-symlink.update"
write_update "$update" "$head" "$new" "$ref"
assert_failure 'symlink claim blocks a reference update' 'malformed or unsafe claim' run_reference "$repo" prepared "$update"
assert_failure 'symlink claim cannot authorize a later gate' 'existing live or unknown reference claim blocks this commit' run_gate "$repo"

repo=$(make_fixture reference-replay)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-replay-gate.out" 2>&1
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m replay)
update="$TMP/reference-replay.update"
write_update "$update" "$head" "$new" "$ref"
run_reference "$repo" prepared "$update" >"$TMP/reference-replay-first.out" 2>&1
assert_failure 'replayed prepared phase blocks' 'replayed or unfinished claim' run_reference "$repo" prepared "$update"
run_reference "$repo" aborted "$update" >"$TMP/reference-replay-aborted.out" 2>&1
claim=$(claim_path "$repo")
inflight=$(inflight_path "$repo")
if [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then ok 'replay cleanup burns in-flight state'; else not_ok 'replay cleanup left reusable state'; fi

repo=$(make_fixture reference-duplicate)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-duplicate-gate.out" 2>&1
claim=$(claim_path "$repo")
inflight=$(inflight_path "$repo")
cp "$claim" "$inflight"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m duplicate)
update="$TMP/reference-duplicate.update"
write_update "$update" "$head" "$new" "$ref"
assert_failure 'duplicate pending and in-flight state blocks' 'duplicate claim state' run_reference "$repo" prepared "$update"
run_reference "$repo" aborted "$update" >"$TMP/reference-duplicate-aborted.out" 2>&1
if [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then ok 'aborted phase burns duplicate state'; else not_ok 'duplicate state remained after abort'; fi

repo=$(make_fixture reference-committed-mismatch)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-committed-mismatch-gate.out" 2>&1
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m committed-mismatch)
update="$TMP/reference-committed-mismatch.update"
write_update "$update" "$head" "$new" "$ref"
run_reference "$repo" prepared "$update" >"$TMP/reference-committed-mismatch-prepared.out" 2>&1
wrong_committed=$(git -C "$repo" commit-tree "$tree" -p "$head" -m committed-different)
write_update "$update" "$head" "$wrong_committed" "$ref"
assert_failure 'mismatched committed phase blocks' 'committed transaction does not match the prepared claim' run_reference "$repo" committed "$update"
inflight=$(inflight_path "$repo")
if [ ! -e "$inflight" ]; then ok 'mismatched committed phase burns in-flight state'; else not_ok 'mismatched committed phase left reusable state'; fi

repo=$(make_fixture reference-unrelated)
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
ref=refs/heads/unrelated-probe
zero=0000000000000000000000000000000000000000
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m unrelated)
update="$TMP/reference-unrelated.update"
write_update "$update" "$zero" "$new" "$ref"
if run_reference "$repo" prepared "$update" >"$TMP/reference-unrelated-prepared.out" 2>&1 \
    && run_reference "$repo" committed "$update" >"$TMP/reference-unrelated-committed.out" 2>&1; then
    ok 'genuinely unrelated no-claim reference transactions remain unaffected'
else
    not_ok 'genuinely unrelated no-claim reference transaction was blocked'
fi

repo=$(make_fixture reference-live-claim-unrelated)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-live-claim-unrelated-gate.out" 2>&1
claim=$(claim_path "$repo")
cp "$claim" "$TMP/reference-live-claim.before"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
zero=0000000000000000000000000000000000000000
unrelated_one=$(git -C "$repo" commit-tree "$tree" -p "$head" -m unrelated-one)
unrelated_two=$(git -C "$repo" commit-tree "$tree" -p "$head" -m unrelated-two)
update="$TMP/reference-live-claim-unrelated.update"
printf '%s %s %s\n%s %s %s\n' "$zero" "$unrelated_one" refs/heads/unrelated-one \
    "$zero" "$unrelated_two" refs/heads/unrelated-two >"$update"
live_unrelated_ok=1
for phase_name in prepared committed aborted; do
    run_reference "$repo" "$phase_name" "$update" >"$TMP/reference-live-claim-unrelated-$phase_name.out" 2>&1 \
        && cmp -s "$TMP/reference-live-claim.before" "$claim" || live_unrelated_ok=0
done
if [ "$live_unrelated_ok" -eq 1 ]; then
    ok 'unrelated multi-ref prepared, committed, and aborted phases leave a live pending claim unchanged'
else
    not_ok 'an unrelated reference transaction disturbed a live pending claim'
fi
abort_pending_claim "$repo" "$TMP/reference-live-claim-unrelated-cleanup.out"

repo=$(make_fixture reference-normal-missing-claim)
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m missing-claim)
update="$TMP/reference-normal-missing-claim.update"
write_update "$update" "$head" "$new" "$ref"
assert_failure 'normal checked-out-ref commit without a claim blocks' 'normal checked-out-ref commit is missing its required claim' \
    run_reference "$repo" prepared "$update"

repo=$(make_fixture reference-normal-missing-claim-multiple)
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m missing-claim-multiple)
update="$TMP/reference-normal-missing-claim-multiple.update"
printf '%s %s %s\n%s %s %s\n' "$head" "$new" "$ref" \
    0000000000000000000000000000000000000000 "$new" refs/heads/unrelated-companion >"$update"
assert_failure 'multi-ref transaction cannot hide an unclaimed checked-out-ref commit' \
    'normal checked-out-ref commit is missing its required claim' run_reference "$repo" prepared "$update"

repo=$(make_fixture reference-worktrees)
linked="$TMP/reference-worktrees-linked"
git -C "$repo" worktree add -q --detach "$linked" HEAD
stage_candidate "$linked"
assert_success 'linked worktree can create its own claim' run_gate "$linked"
main_claim=$(claim_path "$repo")
linked_claim=$(claim_path "$linked")
if [ "$main_claim" != "$linked_claim" ] && [ ! -e "$main_claim" ] && [ -f "$linked_claim" ]; then
    ok 'claims are isolated by worktree Git directory'
else
    not_ok 'worktree claim state was not isolated'
fi
main_install=$(trusted_install_path "$repo")
linked_install=$(trusted_install_path "$linked")
if [ "$main_install" = "$linked_install" ] && [ "$main_install" = "$(git -C "$linked" config --path --get core.hooksPath)" ]; then
    ok 'parallel worktrees share one common trusted installation'
else
    not_ok 'parallel worktrees did not share the common trusted installation'
fi
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
ref=refs/heads/main-worktree-probe
zero=0000000000000000000000000000000000000000
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m main-worktree-unrelated)
update="$TMP/reference-worktrees.update"
write_update "$update" "$zero" "$new" "$ref"
if run_reference "$repo" prepared "$update" >"$TMP/reference-worktrees-main.out" 2>&1; then ok 'one worktree claim does not block another worktree unrelated ref'; else not_ok 'one worktree claim crossed into another worktree'; fi
abort_pending_claim "$linked" "$TMP/reference-worktrees-aborted.out"

repo=$(make_fixture reference-detached)
git -C "$repo" checkout -q --detach
stage_candidate "$repo"
assert_success 'detached worktree gate creates a HEAD claim' run_gate "$repo"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m detached)
update="$TMP/reference-detached.update"
write_update "$update" "$head" "$new" HEAD
if run_reference "$repo" prepared "$update" >"$TMP/reference-detached-prepared.out" 2>&1 \
    && run_reference "$repo" committed "$update" >"$TMP/reference-detached-committed.out" 2>&1; then
    ok 'detached HEAD reference lifecycle succeeds exactly once'
else
    not_ok 'detached HEAD reference lifecycle failed'
fi

repo=$(make_fixture reference-interruption)
stage_candidate "$repo"
run_gate "$repo" post-claim-interrupt >"$TMP/reference-interruption.out" 2>&1
rc=$?
claim=$(claim_path "$repo")
if [ "$rc" -ne 0 ] && grep -Fq post-claim-interrupt "$TEST_LOG" && [ -f "$claim" ]; then
    ok 'post-claim interruption leaves a blocking one-use claim'
else
    not_ok 'post-claim interruption did not fail closed'
fi
assert_failure 'interrupted claim cannot be reused' 'existing live or unknown reference claim blocks this commit' run_gate "$repo"
if abort_pending_claim "$repo" "$TMP/reference-interruption-aborted.out" && [ ! -e "$claim" ]; then ok 'aborted cleanup burns interrupted claim'; else not_ok 'interrupted claim cleanup failed'; fi

repo=$(make_fixture post-claim-reference-mutate)
stage_candidate "$repo"
assert_failure 'reference-hook mutation immediately after claim creation blocks' 'cannot revalidate the trusted gate installation after claim creation' run_gate "$repo" post-claim-reference-mutate
claim=$(claim_path "$repo")
if [ ! -e "$claim" ]; then ok 'post-claim reference-hook mutation burns its claim'; else not_ok 'post-claim reference-hook mutation left a claim'; fi

repo=$(make_fixture reference-self-hash-mismatch)
stage_candidate "$repo"
run_gate "$repo" >"$TMP/reference-self-hash-gate.out" 2>&1
claim=$(claim_path "$repo")
install_dir=$(trusted_install_path "$repo")
chmod 700 "$install_dir/reference-transaction"
printf '\n# executable self-hash mutation\n' >>"$install_dir/reference-transaction"
chmod 500 "$install_dir/reference-transaction"
head=$(git -C "$repo" rev-parse HEAD)
tree=$(git -C "$repo" write-tree)
ref=$(git -C "$repo" symbolic-ref HEAD)
new=$(git -C "$repo" commit-tree "$tree" -p "$head" -m self-hash-mismatch)
update="$TMP/reference-self-hash.update"
write_update "$update" "$head" "$new" "$ref"
assert_failure 'executing reference hook rejects its own claim hash mismatch' 'stale or foreign claim' run_reference "$repo" prepared "$update"
if [ ! -e "$claim" ]; then ok 'self-hash-mismatched claim is burned'; else not_ok 'self-hash-mismatched claim remained reusable'; fi

if grep -Fq 'LOCAL_GATE="/home/user/.claude/bin/local-gate"' "$SUBJECT" \
    && ! grep -Fq 'e2e-remote' "$SUBJECT" \
    && ! grep -Fq -- '--hosts' "$SUBJECT" \
    && grep -Fq '"$LOCAL_GATE" --remote-only --key "$key" --' "$SUBJECT" \
    && grep -Fq '../../"$GATE_PATH" --remote' "$SUBJECT"; then
    ok 'production gate uses direct remote-only local-gate transport without E2E wrapping or host pinning'
else
    not_ok 'production remote dispatch contract changed'
fi

if grep -Fq '"$LOCAL_GATE" --remote-only --key "$key" --' "$SUBJECT" \
    && grep -Fq 'rc=$?' "$SUBJECT" \
    && ! grep -E '"\$LOCAL_GATE".*[|]' "$SUBJECT" >/dev/null; then
    ok 'remote runner status is captured directly without an output pipeline'
else
    not_ok 'remote runner status discipline changed'
fi

exit_line=""
hook_line_no=0
while IFS= read -r hook_line || [ -n "$hook_line" ]; do
    hook_line_no=$((hook_line_no + 1))
    [ "$hook_line" = 'exit 0' ] && exit_line=$hook_line_no
done <"$HOOK"
if [ -n "$exit_line" ] \
    && grep -Fq 'trivy fs . --scanners vuln,secret,misconfig --exit-code 1' "$HOOK" \
    && grep -Fq 'composer audit || fail' "$HOOK" \
    && grep -Fq '"$LINT_BIN" --max-warnings 9999' "$HOOK" \
    && ! grep -Fq 'slopgate --staged' "$HOOK" \
    && ! grep -Fq 'vendor/bin/phpstan analyse' "$HOOK" \
    && ! grep -Fq 'tools/phpcs-baseline.php check' "$HOOK"; then
    ok 'ESLint, Trivy, Composer audit, and final reporting remain reachable without local PHP or slopgate'
else
    not_ok 'an unrelated pre-commit behavior was removed or a forbidden local gate remains'
fi

if grep -Fq '".slopgate/config.toml"' "$SUBJECT" \
    && grep -Fq 'GATE_PRECOMMIT_SRC=".dev-config/hooks/pre-commit"' "$SUBJECT" \
    && grep -Fq 'GATE_REFERENCE_SRC=".dev-config/hooks/reference-transaction"' "$SUBJECT" \
    && grep -Fq 'installed_blob_is_trusted' "$SUBJECT" \
    && grep -Fq 'slopgate --file "$slopgate_file" --tier commit --config "$repo_root/.slopgate/config.toml"' "$SUBJECT"; then
    ok 'slopgate and final reference enforcement are bound into the staged contract'
else
    not_ok 'remote or reference transaction contract changed'
fi

for behavior in discovery-js-fail discovery-php-fail discovery-ipz-fail; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    printf '%s\n' 'const discovery = true;' >"$repo/discovery.js"
    git -C "$repo" add discovery.js
    old_head=$(git -C "$repo" rev-parse HEAD)
    case "$behavior" in
        discovery-js-fail) evidence='JavaScript staged-path discovery failed' ;;
        discovery-php-fail) evidence='PHP staged-path discovery failed' ;;
        *) evidence='IPZ convention staged-path discovery failed' ;;
    esac
    TEST_BEHAVIOR=$behavior TEST_ORIGINAL_REPO=$repo PATH="$repo/fake-bin:$ORIGINAL_PATH" GIT_EXEC_PATH="$repo/fake-bin" \
        git -C "$repo" commit -q -m "$behavior" >"$TMP/$behavior.out" 2>&1
    rc=$?
    new_head=$(git -C "$repo" rev-parse HEAD)
    if [ "$rc" -ne 0 ] && [ "$new_head" = "$old_head" ] && grep -Fq "$evidence" "$TMP/$behavior.out"; then
        ok "$behavior fails closed in the production pre-commit hook"
    else
        not_ok "$behavior did not fail closed in the production pre-commit hook"
        printf 'discovery diagnostics: behavior=%s rc=%s old=%s new=%s\n' "$behavior" "$rc" "$old_head" "$new_head" >&2
        while IFS= read -r line; do printf 'discovery output: %s\n' "$line" >&2; done <"$TMP/$behavior.out"
    fi
done

for behavior in unrelated-eslint-fail unrelated-trivy-fail unrelated-composer-fail; do
    repo=$(make_fixture "$behavior")
    stage_candidate "$repo"
    printf '%s\n' 'const unrelated = true;' >"$repo/unrelated.js"
    git -C "$repo" add unrelated.js
    old_head=$(git -C "$repo" rev-parse HEAD)
    TEST_BEHAVIOR=$behavior TEST_ORIGINAL_REPO=$repo PATH="$repo/fake-bin:$ORIGINAL_PATH" GIT_EXEC_PATH="$repo/fake-bin" \
        git -C "$repo" commit -q -m "$behavior" >"$TMP/$behavior.out" 2>&1
    rc=$?
    new_head=$(git -C "$repo" rev-parse HEAD)
    if [ "$rc" -ne 0 ] && [ "$new_head" = "$old_head" ]; then
        ok "$behavior remains enforced by production pre-commit"
    else
        not_ok "$behavior was not enforced by production pre-commit"
    fi
done

repo=$(make_fixture real-async-stage-ipz)
printf '%s\n' 'const initiallyJavaScriptOnly = true;' >"$repo/async.js"
git -C "$repo" add async.js
old_head=$(git -C "$repo" rev-parse HEAD)
install_dir=$(trusted_install_path "$repo")
: >"$TEST_LOG"
TEST_BEHAVIOR=async-stage-ipz TEST_ORIGINAL_REPO=$repo TEST_TRUSTED_INSTALL=$install_dir PATH="$repo/fake-bin:$ORIGINAL_PATH" \
    git -C "$repo" commit -q -m 'candidate check stages IPZ PHP' >"$TMP/real-async-stage-ipz.out" 2>&1
rc=$?
new_head=$(git -C "$repo" rev-parse HEAD)
claim=$(claim_path "$repo")
inflight=$(inflight_path "$repo")
if [ "$rc" -ne 0 ] && [ "$new_head" = "$old_head" ] && [ -f "$repo/plugins/international-press-zone/includes/async.php" ] \
    && grep -Fq 'async-stage-ipz=done' "$TEST_LOG" \
    && grep -Fq 'Staged tree changed while pre-commit checks ran' "$TMP/real-async-stage-ipz.out" \
    && [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then
    ok 'initially JavaScript-only commit rejects asynchronously staged untested IPZ PHP'
else
    not_ok 'post-discovery asynchronous IPZ staging escaped production enforcement'
    while IFS= read -r line; do printf 'async-stage output: %s\n' "$line" >&2; done <"$TMP/real-async-stage-ipz.out"
fi

repo=$(make_fixture real-no-ipz-commit)
printf '%s\n' 'const noIpz = true;' >"$repo/no-ipz.js"
git -C "$repo" add no-ipz.js
expected_tree=$(git -C "$repo" write-tree)
old_head=$(git -C "$repo" rev-parse HEAD)
install_dir=$(trusted_install_path "$repo")
: >"$TEST_LOG"
TEST_BEHAVIOR= TEST_ORIGINAL_REPO=$repo TEST_TRUSTED_INSTALL=$install_dir PATH="$repo/fake-bin:$ORIGINAL_PATH" \
    git -C "$repo" commit -q -m 'clean no-IPZ commit' >"$TMP/real-no-ipz-commit.out" 2>&1
rc=$?
new_head=$(git -C "$repo" rev-parse HEAD)
committed_tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
claim=$(claim_path "$repo")
inflight=$(inflight_path "$repo")
if [ "$rc" -eq 0 ] && [ "$new_head" != "$old_head" ] && [ "$committed_tree" = "$expected_tree" ] \
    && [ ! -e "$claim" ] && [ ! -e "$inflight" ] && ! grep -Fq 'key=' "$TEST_LOG"; then
    ok 'clean no-IPZ normal commit consumes a claim without remote PHP dispatch'
else
    not_ok 'clean no-IPZ normal commit did not complete its claim lifecycle'
    while IFS= read -r line; do printf 'no-IPZ output: %s\n' "$line" >&2; done <"$TMP/real-no-ipz-commit.out"
fi

for source_case in pre-commit ipz-remote-php-gate reference-transaction; do
    repo=$(make_fixture "real-noop-$source_case")
    stage_candidate "$repo"
    case "$source_case" in
        pre-commit) source_path=.dev-config/hooks/pre-commit ;;
        ipz-remote-php-gate) source_path=.dev-config/bin/ipz-remote-php-gate ;;
        reference-transaction) source_path=.dev-config/hooks/reference-transaction ;;
    esac
    printf '%s\n' '#!/bin/bash' 'exit 0' >"$repo/$source_path"
    chmod 700 "$repo/$source_path"
    git -C "$repo" add "$source_path"
    old_head=$(git -C "$repo" rev-parse HEAD)
    (cd "$repo" && .dev-config/bin/approve-gate-change --force) || exit 1
    TEST_BEHAVIOR= TEST_ORIGINAL_REPO=$repo PATH="$repo/fake-bin:$ORIGINAL_PATH" \
        git -C "$repo" commit -q -m "candidate no-op $source_case" >"$TMP/real-noop-$source_case.out" 2>&1
    rc=$?
    new_head=$(git -C "$repo" rev-parse HEAD)
    if [ "$source_case" = ipz-remote-php-gate ]; then
        if [ "$rc" -ne 0 ] && [ "$new_head" = "$old_head" ]; then
            ok "candidate helper source cannot forge a real commit"
        else
            not_ok "candidate helper source forged a real commit"
        fi
    elif [ "$rc" -eq 0 ] && [ "$new_head" != "$old_head" ]; then
        ok "approved candidate source edit retains landed installed enforcement"
    else
        not_ok "approved candidate source edit did not retain installed enforcement"
    fi
done

repo=$(make_fixture install-before-own-commit)
stage_candidate "$repo" '<?php // install-before-own-commit'
printf '\n' >>"$repo/.dev-config/hooks/pre-commit"
printf '\n' >>"$repo/.dev-config/bin/ipz-remote-php-gate"
printf '\n' >>"$repo/.dev-config/hooks/reference-transaction"
git -C "$repo" add .dev-config/hooks/pre-commit .dev-config/bin/ipz-remote-php-gate .dev-config/hooks/reference-transaction
install_fixture_hooks "$repo"
install_dir=$(trusted_install_path "$repo")
old_head=$(git -C "$repo" rev-parse HEAD)
(cd "$repo" && .dev-config/bin/approve-gate-change --force) || exit 1
TEST_BEHAVIOR= TEST_ORIGINAL_REPO=$repo TEST_TRUSTED_INSTALL=$install_dir PATH="$repo/fake-bin:$ORIGINAL_PATH" \
    git -C "$repo" commit -q -m 'install reviewed enforcement before source commit' >"$TMP/install-before-own-commit.out" 2>&1
rc=$?
new_head=$(git -C "$repo" rev-parse HEAD)
if [ "$rc" -ne 0 ] && [ "$new_head" = "$old_head" ]; then ok 'unlanded installed enforcement is rejected even with owner approval'; else not_ok 'unlanded installed enforcement contract failed'; fi

repo=$(make_fixture real-commit)
stage_candidate "$repo" '<?php // real commit candidate'
printf '%s\n' 'const fixture = true;' >"$repo/fixture.js"
git -C "$repo" add fixture.js
install_dir=$(trusted_install_path "$repo")
production_hook_hash=$(sha256sum "$HOOK")
fixture_hook_hash=$(sha256sum "$install_dir/pre-commit")
if [ "${production_hook_hash%% *}" = "${fixture_hook_hash%% *}" ] && [ -x "$install_dir/pre-commit" ] \
    && [ "$install_dir" != "$repo"/.dev-config/hooks ]; then
    ok 'real commits execute an exact trusted external copy of production pre-commit'
else
    not_ok 'real-commit pre-commit copy does not match production'
fi
expected_tree=$(git -C "$repo" write-tree)
old_head=$(git -C "$repo" rev-parse HEAD)
export TEST_BEHAVIOR=post-check-index-mutate TEST_ORIGINAL_REPO=$repo PATH="$repo/fake-bin:$ORIGINAL_PATH"
: >"$TEST_LOG"
git -C "$repo" commit -q -m 'real commit post-check experiment' >"$TMP/real-commit.out" 2>&1
commit_rc=$?
seam_rc=$(while IFS= read -r line; do case "$line" in post-check-add-rc=*) printf '%s\n' "${line#*=}" ;; esac; done <"$TEST_LOG")
seam_tree=$(while IFS= read -r line; do case "$line" in post-check-tree=*) printf '%s\n' "${line#*=}" ;; esac; done <"$TEST_LOG")
actual_head=$(git -C "$repo" rev-parse HEAD)
claim=$(claim_path "$repo")
inflight=$(inflight_path "$repo")
print_commit_diagnostics() {
    printf 'real-commit diagnostics: commit_rc=%s seam_rc=%s old_head=%s actual_head=%s expected_tree=%s seam_tree=%s\n' \
        "$commit_rc" "${seam_rc:-missing}" "$old_head" "$actual_head" "$expected_tree" "${seam_tree:-missing}" >&2
    local output_line output_count=0
    while IFS= read -r output_line || [ -n "$output_line" ]; do
        output_count=$((output_count + 1))
        [ "$output_count" -le 40 ] && printf 'real-commit output: %s\n' "$output_line" >&2
    done <"$TMP/real-commit.out"
    [ "$output_count" -le 40 ] || printf 'real-commit output: ... %d additional line(s) omitted\n' "$((output_count - 40))" >&2
}
if [ -z "$seam_rc" ] || [ -z "$seam_tree" ]; then
    print_commit_diagnostics
    not_ok 'post-check mutation seam did not execute'
elif [ "$seam_rc" = 0 ] && [ "$seam_tree" != "$expected_tree" ] \
    && [ "$commit_rc" -ne 0 ] && [ "$actual_head" = "$old_head" ] \
    && [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then
    ok 'reference transaction rejects the proven post-check mutation without advancing HEAD'
else
    print_commit_diagnostics
    not_ok 'reference transaction did not close the post-check mutation gap'
fi

git -C "$repo" rm -q --cached plugins/international-press-zone/includes/post-check-mutation.php
rm -f "$repo/plugins/international-press-zone/includes/post-check-mutation.php"
clean_tree=$(git -C "$repo" write-tree)
export TEST_BEHAVIOR= TEST_ORIGINAL_REPO=$repo PATH="$repo/fake-bin:$ORIGINAL_PATH"
: >"$TEST_LOG"
git -C "$repo" commit -q -m 'real clean commit' >"$TMP/real-clean-commit.out" 2>&1
clean_rc=$?
clean_head=$(git -C "$repo" rev-parse HEAD)
clean_committed_tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
if [ "$clean_tree" = "$expected_tree" ] && [ "$clean_rc" -eq 0 ] && [ "$clean_head" != "$old_head" ] \
    && [ "$clean_committed_tree" = "$expected_tree" ] && [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then
    ok 'clean real commit advances with exactly the remotely tested tree and consumes its claim'
    if grep -Fq 'unrelated-eslint=' "$TEST_LOG" && grep -Fq 'unrelated-trivy=' "$TEST_LOG" \
        && grep -Fq 'composer-cwd=' "$TEST_LOG"; then
        ok 'clean real commit preserves controlled ESLint, Trivy, and Composer audit checks'
    else
        not_ok 'clean real commit did not execute every controlled unrelated check'
    fi
else
    not_ok 'clean real commit did not preserve tested-tree parity'
    while IFS= read -r line; do printf 'clean-commit output: %s\n' "$line" >&2; done <"$TMP/real-clean-commit.out"
fi

repo=$(make_fixture real-detached-commit)
git -C "$repo" checkout -q --detach
stage_candidate "$repo" '<?php // detached real commit candidate'
detached_tree=$(git -C "$repo" write-tree)
detached_old_head=$(git -C "$repo" rev-parse HEAD)
export TEST_BEHAVIOR= TEST_ORIGINAL_REPO=$repo PATH="$repo/fake-bin:$ORIGINAL_PATH"
: >"$TEST_LOG"
git -C "$repo" commit -q -m 'real detached commit' >"$TMP/real-detached-commit.out" 2>&1
detached_rc=$?
detached_new_head=$(git -C "$repo" rev-parse HEAD)
detached_committed_tree=$(git -C "$repo" rev-parse 'HEAD^{tree}')
claim=$(claim_path "$repo")
inflight=$(inflight_path "$repo")
if [ "$detached_rc" -eq 0 ] && [ "$detached_new_head" != "$detached_old_head" ] \
    && [ "$detached_committed_tree" = "$detached_tree" ] && [ ! -e "$claim" ] && [ ! -e "$inflight" ]; then
    ok 'real detached HEAD commit advances with exactly the remotely tested tree'
else
    not_ok 'real detached HEAD commit did not complete its claim lifecycle'
    while IFS= read -r line; do printf 'detached-commit output: %s\n' "$line" >&2; done <"$TMP/real-detached-commit.out"
fi

repo=$(make_fixture empty-applicability)
printf '%s\n' '<?php // tests only' >"$repo/plugins/international-press-zone/outside.php"
git -C "$repo" add plugins/international-press-zone/outside.php
assert_failure 'empty PHPCS/PHPStan applicability blocks' 'no applicable staged IPZ candidates' run_gate "$repo"

repo=$(make_fixture deletion-only)
git -C "$repo" rm -q plugins/international-press-zone/includes/delete.php
export TEST_BEHAVIOR= TEST_ORIGINAL_REPO=$repo PATH="$repo/fake-bin:$ORIGINAL_PATH"
: >"$TEST_LOG"
mapfile -d '' -t deletion_candidates < <(git -C "$repo" diff --cached --name-only -z --diff-filter=ACMRT -- '*.php')
if [ ${#deletion_candidates[@]} -eq 0 ]; then ok 'deleted-only PHP has no stale candidate to analyse'; else not_ok 'deleted PHP was treated as an analysable candidate'; fi

printf 'IPZ_REMOTE_GATE_TEST_RECEIPT pass=%d fail=%d\n' "$PASS" "$FAIL"
[ "$FAIL" -eq 0 ]
