Skip to content

fix(hooks): parse Stryker commands instead of scanning for the tool name - #2194

Merged
bdfinst merged 2 commits into
mainfrom
fix/2185-stryker-command-parsing
Sep 22, 2026
Merged

bdfinst merged 2 commits into
mainfrom
fix/2185-stryker-command-parsing

Conversation

@bdfinst

@bdfinst bdfinst commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Both C# mutation PreToolUse gates (mutation_testing_smoke_gate.py, stryker_xunit_shim_guard.py) decided "is this a Stryker run?" with a bare re.search over the whole command string — any command merely mentioning the tool name in prose (a --body, a grep, an echo, a Python comment) was treated as an invocation and blocked.
  • Extracted a shared hooks/lib/stryker_invocation.py::is_stryker_invocation() predicate: tokenize with shlex (punctuation_chars mode, so &&/||/;/| are recognised as their own tokens while quoted text stays one token), split into operator-separated segments, and match the tool only in program position of a segment. Heredoc bodies are stripped before tokenizing. Falls back to the old permissive regex only when shlex can't tokenize the command at all, so a malformed command still fails closed.
  • Both gates now delegate to the shared predicate instead of keeping their own drifting copy, with the old regex kept only as each hook's degraded-import fallback (hooks/lib unreachable).

Test Plan

  • TDD-first per the issue: added the four false-positive repro cases as tests against today's implementation first, confirmed 3/4 failed outright and the 4th silently passed for the wrong reason (coincidental -t mtp match) — proving the bug — before writing the fix.
  • python3 -m pytest plugins/dev-team/tests/hooks/test_stryker_invocation.py plugins/dev-team/tests/hooks/test_mutation_testing_smoke_gate.py plugins/dev-team/tests/hooks/test_stryker_xunit_shim_guard.py -q — 106 passed, including all pre-existing trigger cases (cd <dir> && dotnet stryker ..., dotnet-stryker, the wrapper script) unchanged
  • python3 plugins/dev-team/skills/code-review/scripts/repo_invariants.py and internal_double_detector.py static pre-passes — clean
  • python3 scripts/check_md_references.py — clean
  • Full pre-PR pytest gate (plugins/dev-team/tests tests/repo tests/agents tests/commands tests/docs tests/knowledge tests/stack_aware tests/skills tests/scripts tests/hooks) — 10786 passed, 48 skipped
  • pre-push hook's own local CI — all checks passed

Closes #2185

🤖 Generated with Claude Code

https://claude.ai/code/session_016uBnw1i52qEik2k9LSHa4n


Generated by Claude Code

Both C# mutation PreToolUse gates decided "is this a Stryker run?" with a
bare regex search over the whole command string, with no notion of
argument position or quoting — any command merely mentioning the tool
name in prose (a --body, a grep, an echo, a Python comment) was treated
as an invocation and blocked.

Extract a shared hooks/lib/stryker_invocation.py predicate: tokenize with
shlex (punctuation_chars mode, so &&/||/;/| are recognised as their own
tokens while quoted text stays one token), split into operator-separated
segments, and match the tool only in program position of a segment.
Heredoc bodies are stripped before tokenizing. Falls back to the old
permissive regex only when shlex can't tokenize the command at all, so a
malformed command still fails closed.

Both mutation_testing_smoke_gate.py and stryker_xunit_shim_guard.py now
delegate to the shared predicate instead of keeping their own drifting
copy, with the old regex kept only as each hook's degraded-import
fallback.

Closes #2185

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uBnw1i52qEik2k9LSHa4n
@bdfinst
bdfinst merged commit 302c319 into main Sep 22, 2026
15 checks passed
@bdfinst
bdfinst deleted the fix/2185-stryker-command-parsing branch September 22, 2026 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(hooks): Stryker gates trigger on the tool name anywhere in a command, blocking greps and prose

2 participants