From afa9dd792368b1e4dc422bac1c46d69a6efc0147 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Tue, 20 Oct 2026 13:41:14 -0400 Subject: [PATCH] fix(source-control): extend the merge floor to git hooks and every plugin scripts directory HIGH_FLOOR gains plugins/*/git-hooks/** (installed as git hooks in consumer repos), plugins/**/scripts/** (scripts directories at any depth), and the two skill-run scripts outside one: net-probe.sh and preflight.mjs. Tests cover each as HIGH with near-miss neighbors staying LOW. The declared-executables hook fixture moves its command out of scripts/ so that test still proves the hooks.json declaration, not a floor glob, floors the file. Co-Authored-By: Claude Opus 5.5 --- .../fix-8526-g4-floor-remainder-db682273.md | 7 ++++++ .../babysit-prs/scripts/babysit_risk.py | 5 ++++ .../scripts/tests/test_babysit_risk.py | 25 +++++++++++++++++-- .../test_babysit_risk_declared_executables.py | 5 ++-- 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 .changes/source-control/fix-8526-g4-floor-remainder-db682273.md diff --git a/.changes/source-control/fix-8526-g4-floor-remainder-db682273.md b/.changes/source-control/fix-8526-g4-floor-remainder-db682273.md new file mode 100644 index 00000000000..543f583ca4e --- /dev/null +++ b/.changes/source-control/fix-8526-g4-floor-remainder-db682273.md @@ -0,0 +1,7 @@ +--- +bump: patch +--- + +### Changed + +- The babysit merge gate's high-path floor now also holds plugin git hooks (`plugins/*/git-hooks/**`), any `scripts/` directory at any depth under `plugins/`, and two skill-run scripts outside one (`net-probe.sh`, `preflight.mjs`) out of unattended merge. diff --git a/plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py b/plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py index 3728f34d19b..726076fbb89 100755 --- a/plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py +++ b/plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py @@ -179,6 +179,11 @@ class RiskResult: # Scripts skills run through `!` lines, and agents and workflows by path. "plugins/*/scripts/**", "plugins/*/skills/*/scripts/**", + "plugins/**/scripts/**", # any deeper scripts/ directory + "plugins/*/git-hooks/**", # installed as git hooks in consumer repos + # Run by a skill, not under a scripts/ directory. + "plugins/harness-ops/skills/observability/otel/net-probe.sh", + "plugins/knowledge/skills/video-digest/extraction/preflight.mjs", # Hook-run scripts outside hooks/. "plugins/disk-hygiene/skills/clean/scripts/destructive_guard.py", "plugins/disk-hygiene/skills/clean/scripts/engine_context.py", diff --git a/plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk.py b/plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk.py index e87544188bd..962a332334f 100644 --- a/plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk.py +++ b/plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk.py @@ -339,6 +339,24 @@ def test_plugin_and_skill_scripts_are_high(self): "plugins/foo/skills/bar/scripts/run.py", "high-path:plugins/*/skills/*/scripts/**", ), + ( + "plugins/foo/skills/bar/reference/scripts/x.md", + "high-path:plugins/**/scripts/**", + ), + ("plugins/foo/docs/scripts/x.sh", "high-path:plugins/**/scripts/**"), + ( + "plugins/source-control/git-hooks/pre-push", + "high-path:plugins/*/git-hooks/**", + ), + ( + "plugins/harness-ops/skills/observability/otel/net-probe.sh", + "high-path:plugins/harness-ops/skills/observability/otel/net-probe.sh", + ), + ( + "plugins/knowledge/skills/video-digest/extraction/preflight.mjs", + "high-path:plugins/knowledge/skills/video-digest/extraction/" + "preflight.mjs", + ), ] for path, code in cases: with self.subTest(path): @@ -354,8 +372,11 @@ def test_skill_prose_edit_stays_low(self): def test_near_miss_script_directories_are_not_high(self): for path in ( "plugins/foo/reference/scripts.md", - "plugins/foo/skills/bar/reference/scripts/x.md", - "plugins/foo/docs/scripts/x.sh", + "plugins/foo/skills/bar/scriptsx/run.sh", + "plugins/foo/docs/git-hooks.md", + "plugins/foo/skills/bar/git-hooks/x.md", + "plugins/harness-ops/skills/observability/otel/net-probe.md", + "plugins/knowledge/skills/video-digest/extraction/preflight.md", ): with self.subTest(path): self.assertEqual(score(_input(files=(_file(path),))).score, Risk.LOW) diff --git a/plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk_declared_executables.py b/plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk_declared_executables.py index f15685efaf0..b0715e5e72a 100644 --- a/plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk_declared_executables.py +++ b/plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk_declared_executables.py @@ -69,7 +69,7 @@ { "type": "command", "command": 'python3 "${CLAUDE_PLUGIN_ROOT}' - '/skills/clean/scripts/guard.py" --strict', + '/skills/clean/tools/guard.py" --strict', } ], } @@ -171,7 +171,8 @@ def test_declared_executable_mcp_server_sibling_scores_high(self): self.assertIs(result.score, Risk.HIGH) def test_declared_executable_hook_command_script_scores_high(self): - result = _risk("plugins/example-hooks/skills/clean/scripts/guard.py") + # Outside every floor glob, so only the hooks.json declaration floors it. + result = _risk("plugins/example-hooks/skills/clean/tools/guard.py") self.assertIs(result.score, Risk.HIGH) def test_declared_executable_inline_plugin_json_server_scores_high(self):