From 6d01788326a8434793e6d6b6b0d8dccbf1ee3ec8 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 31 Jul 2026 23:49:10 -0400 Subject: [PATCH] test(plugin): pin both halves of the local prior-art arm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI caught the previous commit: the fail-open contract asserts a hook stays SILENT with no working instance, and the new local arm deliberately speaks there. The invariant is right and the hook is right — the smoke event was wrong. It used `def f`, which this repo really does define, so the hook found something and "silent" was asserting the wrong thing. Fixed by asserting the two properties separately: - SILENT for a symbol that genuinely does not exist. - SPEAKING, with NO credentials, for one that does. That is the point of the arm — the other arms ask Scribe what was RECORDED; this one asks the repo what EXISTS, which needs no instance. Were it to start depending on configuration it would stop covering the case it was built for, and only this assertion would notice. Second trap, hit while fixing the first: spelling the absent symbol out in full wrote `def (` into check_plugin.py, so the smoke event DEFINED the very symbol it claimed was missing, and the hook found it again. The name is now assembled from fragments so the contiguous string never appears in the source. scribe_prior_art.sh joins scribe_session_context.sh as a hook that legitimately produces output without credentials — for the same reason, that it carries something needing neither network nor config. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs --- scripts/check_plugin.py | 57 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/scripts/check_plugin.py b/scripts/check_plugin.py index 9fc42e2..22c706b 100755 --- a/scripts/check_plugin.py +++ b/scripts/check_plugin.py @@ -181,13 +181,25 @@ def check_shellcheck() -> None: # pinning: the bug and the healthy no-results case look identical from outside. # Pinning it does NOT make the failure visible; it makes sure the fail-open # behaviour is deliberate rather than accidental. +# A symbol that exists nowhere, ASSEMBLED rather than written literally. +# The prior-art hook's local arm (#2280) fires with no credentials, so the +# silence assertion below needs a name the repo genuinely lacks. Two traps, +# both hit while writing this: +# - `def f` matched real code, so the hook spoke and "silent" was asserting +# the wrong thing; +# - spelling the replacement out in full put `def (` INTO this file, +# so the smoke event defined the very symbol it claimed was absent. +# Concatenating keeps the contiguous string out of the source. +_ABSENT_SYM = "zz" + "_absent_" + "9f3a2b" + SMOKE_EVENTS: dict[str, str] = { "scribe_autoinject.sh": json.dumps( {"session_id": "smoke", "cwd": ".", "prompt": "a multi-line\nprompt\nhere"} ), "scribe_prior_art.sh": json.dumps( {"session_id": "smoke", "cwd": ".", "tool_name": "Edit", - "tool_input": {"file_path": "src/x.py", "new_string": "def f():\n pass\n"}} + "tool_input": {"file_path": "src/x.py", + "new_string": f"def {_ABSENT_SYM}():\n pass\n"}} ), "scribe_sync_processes.sh": json.dumps({"source": "startup"}), "scribe_session_context.sh": json.dumps({"source": "startup"}), @@ -253,6 +265,48 @@ def check_fail_open() -> None: ok(f"{rel} [{label}]: exit 0, silent") +def check_local_prior_art_needs_no_instance() -> None: + """The prior-art hook's local arm must answer with no credentials (#2280). + + The other arms ask Scribe what was RECORDED. This one asks the repo what + EXISTS, which needs no instance — and that is the whole reason it catches + the case the recorded arms structurally cannot: a helper nobody thought to + record. If it ever silently starts depending on configuration, it stops + covering that case and nothing else would notice. + + Paired with the silence assertion in check_fail_open, which uses a symbol + that cannot exist. Together they pin both halves: silent when there is + nothing to say, and speaking when there is — both with no instance at all. + """ + script = HOOKS_DIR / "scribe_prior_art.sh" + if not script.is_file() or not shutil.which("jq"): + skip("prior-art local arm: hook or jq missing") + return + + # A definition this repo really does contain, written into a DIFFERENT file + # so the self-match exclusion doesn't suppress it. + event = json.dumps({ + "session_id": "smoke", "cwd": ".", "tool_name": "Write", + "tool_input": { + "file_path": "scripts/_probe_not_real.py", + "content": "def check_local_prior_art_needs_no_instance():\n pass\n", + }, + }) + try: + proc = _run_hook(script, event, {}) # NO credentials, on purpose + except subprocess.TimeoutExpired: + fail("prior-art local arm: hung") + return + if proc.returncode != 0: + fail(f"prior-art local arm: exited {proc.returncode}, must be 0") + elif "already defined" not in proc.stdout: + fail("prior-art local arm: found nothing for a symbol this repo " + "defines, with no credentials — the arm that needs no instance " + "has stopped working, and the recorded arms cannot cover for it") + else: + ok("prior-art local arm: answers with no instance configured") + + def _git(*args: str) -> tuple[int, str]: proc = subprocess.run( ["git", *args], capture_output=True, text=True, cwd=ROOT @@ -344,6 +398,7 @@ def main() -> int: check_patterns() check_shellcheck() check_fail_open() + check_local_prior_art_needs_no_instance() if not args.no_version: check_version_bump(args.base)