test(plugin): pin both halves of the local prior-art arm
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 28s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 54s
CI & Build / Build & push image (push) Successful in 21s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 28s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 54s
CI & Build / Build & push image (push) Successful in 21s
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 <name>(` 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) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
This commit is contained in:
+56
-1
@@ -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 <name>(` 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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user