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)