From 3a3949b80b17ff03815f0e39cb90551b17c61d75 Mon Sep 17 00:00:00 2001 From: mergetest Date: Wed, 23 Sep 2026 21:53:20 -0700 Subject: [PATCH] fix(tests): keep __pycache__ out of a clean checkout A green tests/run-tests.sh run left agentkit/hooks/lib/__pycache__/ behind because test-rewrite-runtime.sh's --prefix-fixture reinvocation launches python3 with -I (which implies -E and so ignores PYTHONDONTWRITEBYTECODE) but not -B, so it re-imports rewrite_runtime.py without bytecode-write protection. .gitignore also didn't exclude __pycache__/, so agent-run.sh --cmd test then reported the checkout dirty and finding-ledger.sh evidence refused. Ignore __pycache__/ and *.pyc, export PYTHONDONTWRITEBYTECODE=1 from run-tests.sh as defense in depth, add -B to the reinvocation that was the actual write path, and pin all of it with a new suite that fails on the unfixed fixture and passes after. Closes #897 Co-Authored-By: Claude Fable 5.1 Co-Authored-By: Claude Sonnet 5 --- .gitignore | 6 ++++++ tests/fixtures/test_rewrite_runtime.py | 6 +++++- tests/run-tests.sh | 6 ++++++ tests/test-clean-tree-after-probe.sh | 29 ++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100755 tests/test-clean-tree-after-probe.sh diff --git a/.gitignore b/.gitignore index be6351ec..820816f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,12 @@ tests/tmp/ plugin/ +# CPython writes these when a suite imports the hook's Python modules +# (e.g. the activation probe); untracked but unignored, they dirty the +# checkout after a green run (issue #897). +__pycache__/ +*.pyc + # opencode: @opencode-ai/plugin is a devDependency for typings only (issue # #319); node_modules/ is never imported at runtime and never ships (see # tests/build-plugin.sh, which copies only opencode/{index.js,package.json}). diff --git a/tests/fixtures/test_rewrite_runtime.py b/tests/fixtures/test_rewrite_runtime.py index 6fa564ae..1be7ecd5 100644 --- a/tests/fixtures/test_rewrite_runtime.py +++ b/tests/fixtures/test_rewrite_runtime.py @@ -205,7 +205,11 @@ def prefix_fixture(self, body): spec = self.root / "prefix-fixture.json" spec.write_text(json.dumps({"profile": profile, "records": str(records.root)})) native = self.native.replace("'printf fixture'", shlex.quote(issued["command"])) - return [sys.executable, "-I", str(Path(__file__).resolve()), "--prefix-fixture", str(spec), native] + # -I ignores PYTHONDONTWRITEBYTECODE (it implies -E), and this + # reinvocation re-imports RUNTIME at module load before any guard in + # __main__ runs, so -B is required here to keep it from writing + # agentkit/hooks/lib/__pycache__ into the shipped tree (issue #897). + return [sys.executable, "-I", "-B", str(Path(__file__).resolve()), "--prefix-fixture", str(spec), native] def test_public_prefix_executes_once_with_native_result_and_refuses_replay(self): command = self.prefix_fixture('printf "%s|%s|%s\\n" "$PWD" "$1" "$2"\nprintf stderr >&2\nexit 37\n') diff --git a/tests/run-tests.sh b/tests/run-tests.sh index bb86e004..09924d4f 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -27,6 +27,12 @@ pin_locale() { } pin_locale +# Suites that import Python modules (e.g. the activation probe) write +# __pycache__/ into the shipped tree unless bytecode writes are disabled. +# Export it here so it reaches every suite subprocess and the probe stays +# read-only (issue #897). +export PYTHONDONTWRITEBYTECODE=1 + here=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) root=$(dirname -- "$here") # The plugin root holds both the skills and the hook dispatchers. The hooks are diff --git a/tests/test-clean-tree-after-probe.sh b/tests/test-clean-tree-after-probe.sh new file mode 100755 index 00000000..8e0518db --- /dev/null +++ b/tests/test-clean-tree-after-probe.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Pins issue #897: a suite that imports the hook's Python modules must never +# leave __pycache__/ behind in the shipped tree. test-rewrite-runtime.sh's +# --prefix-fixture reinvocation runs with `-I`, which implies `-E` and so +# ignores PYTHONDONTWRITEBYTECODE; without an explicit -B on that reinvocation +# it re-imports agentkit/hooks/lib/rewrite_runtime.py and writes +# agentkit/hooks/lib/__pycache__/, dirtying an otherwise-clean checkout. +set -uo pipefail + +TEST_NAME='clean tree after probe' +here=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +root=$(dirname -- "$here") +# shellcheck source=lib/assert.sh +source "$here/lib/assert.sh" + +plugin="$root/agentkit" + +before=$(find "$plugin" -name '__pycache__' 2>/dev/null | LC_ALL=C sort) +assert_eq '' "$before" 'no __pycache__ under agentkit/ before the suite runs' + +unset PYTHONDONTWRITEBYTECODE +(cd -- "$here" && python3 -B fixtures/test_rewrite_runtime.py) > /dev/null 2>&1 +rc=$? +assert_eq 0 "$rc" 'test_rewrite_runtime.py exits clean' + +after=$(find "$plugin" -name '__pycache__' 2>/dev/null | LC_ALL=C sort) +assert_eq '' "$after" 'no __pycache__ under agentkit/ after running the rewrite-runtime fixture' + +finish