From 47388eda36a2d2bbadb55cb9aa32f82c3af08393 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 16 Sep 2026 08:13:56 -0400 Subject: [PATCH] fix(tests): the no-block guard reads the shell, not the comment explaining it (#3680) The hook's header quotes `{"decision":"block"}` while explaining why this hook must never emit one, and the instructions it prints use "decision" in a sentence. Grepping the whole file caught both and failed the guard on the file doing its job. Strips comments and the heredoc first, and guards the stripper: `_code()` returning nothing would make all three static checks pass against an empty string, which is the circularity rule 167 is about. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy --- tests/test_precompact_hook.py | 41 +++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/tests/test_precompact_hook.py b/tests/test_precompact_hook.py index cc91028..f07f77d 100644 --- a/tests/test_precompact_hook.py +++ b/tests/test_precompact_hook.py @@ -51,6 +51,39 @@ def _run(event: dict) -> subprocess.CompletedProcess: capture_output=True, text=True, timeout=30) +def _code() -> str: + """The script with its comments and its heredoc removed. + + The static checks below are about what the shell DOES. Run over the whole + file they also read the header — which quotes `{"decision":"block"}` in the + course of explaining why this hook must never emit one — and the emitted + instructions, which use the word "decision" in a sentence. Both are the + file doing its job, and neither is code. + """ + lines, in_heredoc = [], False + for line in HOOK.read_text().splitlines(): + if in_heredoc: + in_heredoc = line.strip() != "EOF" + continue + if line.lstrip().startswith("cat <<'EOF'"): + in_heredoc = True + continue + if not line.lstrip().startswith("#"): + lines.append(line) + return "\n".join(lines) + + +def test_the_comment_stripper_still_leaves_the_shell_behind(): + """Guard on the three checks below it. `_code()` returning nothing would + make every one of them pass while checking an empty string — the same + circularity the plugin version check has to defend against.""" + code = _code() + assert "set -uo pipefail" in code and "exit 0" in code + # It really did strip: both of the words the checks look for are present + # in the file, and neither is in the code. + assert "decision" in HOOK.read_text() + + @pytest.mark.parametrize("trigger", ["manual", "auto"]) def test_it_exits_zero_with_instructions_on_stdout(trigger): """Exit 0 plus non-empty stdout is the entire contract for reaching the @@ -78,9 +111,9 @@ def test_it_emits_text_and_not_a_json_envelope(): def test_it_never_blocks_the_compaction(): """A block skips compaction silently from the model's side. Nothing in the script may produce one — not an exit code, not a decision.""" - body = HOOK.read_text() - assert '"decision"' not in body and "'decision'" not in body - assert "exit 2" not in body + code = _code() + assert "decision" not in code, "a block decision would skip the compaction" + assert "exit 2" not in code # A truncated or absent event must not turn into a non-zero exit either. for event in ("", "not json", "{}"): out = subprocess.run(["bash", str(HOOK)], input=event, @@ -92,7 +125,7 @@ def test_additional_context_is_not_how_this_event_works(): """SessionStart's channel, which does not exist on PreCompact. A rewrite that reaches for it would read as consistent with the other hooks and inject nothing at all.""" - assert "additionalContext" not in HOOK.read_text().split("set -uo pipefail")[-1] + assert "additionalContext" not in _code() def test_the_plugin_registers_it_on_precompact():