Green because it could not be red

A passing test proves nothing until you have seen it fail. This PR converts 26 security assertions that reported success unconditionally, and fixes two more checks with the same shape. Flip the mutation probe: break the thing under test on purpose and watch which assertions notice.

Mutation probe

Assertions
26
Report pass
26
Would catch a regression
0

What each repair was hiding

CheckBeforeAfterThe lie

The blocker's real contract

The path-traversal suite asserted a two-tier world, allow or deny. The hook has three tiers. rm -rf ./node_modules returns ask, not allow: destructive but plausibly intentional, so it prompts. The test asserted allow and passed anyway, because the helper mapped every non-deny answer to allow.

The test was wrong, not the hook. That distinction is the whole point. Rewriting the hook to match a mistaken test would have removed a real prompt from a destructive command.

The placeholder repair has the same shape at a different layer. ${CLAUDE_SKILL_DIR} was never a Claude Code substitution; OrchestKit invented it. An undefined placeholder does not error, it expands to the empty string, so python3 ${CLAUDE_SKILL_DIR}/scripts/x.py ran as python3 /scripts/x.py and the prose form handed the model a path that does not exist. 218 files carried it. The new gate now fails the build on any undocumented ${CLAUDE_*}, on any ${CLAUDE_PLUGIN_ROOT}/skills/... target that does not resolve on disk, and on any built artifact still carrying the invented name.

PR #3288· security suite 17/17· placeholder gate 3/3 rules· rebased onto 9.7.0