claude-dev-env 7.0.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/pull-request/scripts/pull_request.py +2 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +34 -0
- package/.agents/skills/session-continuity/AGENTS.md +1 -0
- package/.agents/skills/session-continuity/CLAUDE.md +1 -0
- package/.agents/skills/session-continuity/README.md +189 -0
- package/.agents/skills/session-continuity/SKILL.md +114 -0
- package/.agents/skills/session-continuity/continuity.mjs +375 -0
- package/.agents/skills/session-continuity/continuity.test.mjs +303 -0
- package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
- package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
- package/bin/cde.mjs +36 -8
- package/bin/cde.test.mjs +48 -0
- package/bin/ever-shipped-skills.mjs +2 -20
- package/bin/ever-shipped-skills.test.mjs +8 -0
- package/bin/expand_home_directory_tokens.mjs +4 -5
- package/bin/expand_home_directory_tokens.test.mjs +128 -0
- package/bin/git_hooks_installer.mjs +22 -3
- package/bin/git_hooks_installer.test.mjs +159 -5
- package/bin/install-session-continuity.mjs +78 -0
- package/bin/install-session-continuity.test.mjs +42 -0
- package/bin/install.codex-rules.test.mjs +5 -2
- package/bin/install.cursor-rules.test.mjs +5 -2
- package/bin/install.mjs +478 -63
- package/bin/install.policy-lint.test.mjs +56 -4
- package/bin/install.profiles.test.mjs +10 -0
- package/bin/install.prune.test.mjs +227 -17
- package/bin/install.test.mjs +500 -120
- package/bin/install.transaction.test.mjs +3 -3
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
- package/hooks/blocking/precommit_code_rules_gate.py +5 -8
- package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
- package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -244
- package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
- package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
- package/hooks/git-hooks/post_commit.py +33 -0
- package/hooks/git-hooks/pre_commit.py +18 -90
- package/hooks/git-hooks/pre_push.py +45 -541
- package/hooks/git-hooks/test_installed_policy_lint.py +55 -6
- package/hooks/git-hooks/test_native_hook_support.py +129 -0
- package/hooks/git-hooks/test_native_policy_commit.py +39 -6
- package/hooks/git-hooks/test_post_commit.py +43 -0
- package/hooks/git-hooks/test_pre_commit.py +36 -392
- package/hooks/git-hooks/test_pre_push.py +48 -1631
- package/hooks/git-hooks/test_verification_notice.py +60 -0
- package/hooks/git-hooks/test_verification_start.py +173 -0
- package/hooks/git-hooks/verification_notice.py +192 -0
- package/hooks/git-hooks/verification_notice_context.py +253 -0
- package/hooks/git-hooks/verification_notice_state.py +237 -0
- package/hooks/git-hooks/verification_start.py +100 -0
- package/hooks/hooks.json +0 -119
- package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
- package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -50
- package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +42 -185
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
- package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
- package/hooks/validators/run_all_validators.py +5 -1
- package/hooks/validators/security_checks.py +57 -2
- package/hooks/validators/test_hook_timing_harness.py +2 -5
- package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
- package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
- package/hooks/validators/test_security_checks.py +57 -0
- package/package.json +1 -1
- package/rules/confirm-implementation-forks.md +2 -0
- package/rules/git-workflow.md +12 -0
- package/rules/pstack-host-mapping.md +48 -0
- package/rules/vault-context.md +3 -5
- package/rules/verify-runtime-state.md +7 -0
- package/scripts/audit-hooks-cli.mjs +5 -0
- package/scripts/audit-hooks.mjs +72 -16
- package/scripts/automatic_advisory/__init__.py +1 -0
- package/scripts/automatic_advisory/checkout.py +51 -0
- package/scripts/automatic_advisory/cli.py +334 -0
- package/scripts/automatic_advisory/config/__init__.py +1 -0
- package/scripts/automatic_advisory/config/constants.py +93 -0
- package/scripts/automatic_advisory/config/timing.py +4 -0
- package/scripts/automatic_advisory/configuration.py +197 -0
- package/scripts/automatic_advisory/execution.py +350 -0
- package/scripts/automatic_advisory/git.py +69 -0
- package/scripts/automatic_advisory/model.py +107 -0
- package/scripts/automatic_advisory/process_host.py +32 -0
- package/scripts/automatic_advisory/publisher.py +84 -0
- package/scripts/automatic_advisory/runner.py +399 -0
- package/scripts/automatic_advisory/state.py +116 -0
- package/scripts/automatic_advisory/window_flags.py +41 -0
- package/scripts/automatic_advisory/windows_job.py +122 -0
- package/scripts/codex_compat_materializer.py +99 -13
- package/scripts/durable_post_lint.py +30 -2
- package/scripts/durable_post_lint_config/config/constants.py +10 -0
- package/scripts/durable_post_lint_config/config/test_constants.py +13 -0
- package/scripts/local_report_cli.py +184 -0
- package/scripts/local_report_core.py +247 -0
- package/scripts/local_report_publisher.py +12 -0
- package/scripts/local_report_validation.py +357 -0
- package/scripts/local_verification/__init__.py +23 -0
- package/scripts/local_verification/check_runner.py +364 -0
- package/scripts/local_verification/check_support.py +99 -0
- package/scripts/local_verification/cli.py +158 -0
- package/scripts/local_verification/command_runner.py +84 -0
- package/scripts/local_verification/config/__init__.py +1 -0
- package/scripts/local_verification/config/constants.py +89 -0
- package/scripts/local_verification/config/timing.py +1 -0
- package/scripts/local_verification/git_state.py +146 -0
- package/scripts/local_verification/manifest.py +224 -0
- package/scripts/local_verification/model.py +174 -0
- package/scripts/local_verification/runner.py +234 -0
- package/scripts/policy_lint/adapter_pairing.py +58 -3
- package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
- package/scripts/pr_verification/__init__.py +1 -0
- package/scripts/pr_verification/branch_refs.py +114 -0
- package/scripts/pr_verification/config/__init__.py +1 -0
- package/scripts/pr_verification/config/constants.py +84 -0
- package/scripts/pr_verification/config/timing.py +5 -0
- package/scripts/pr_verification/github.py +137 -0
- package/scripts/pr_verification/github_api.py +376 -0
- package/scripts/pr_verification/github_labels.py +88 -0
- package/scripts/pr_verification/github_parsing.py +182 -0
- package/scripts/pr_verification/github_transport.py +91 -0
- package/scripts/pr_verification/lock.py +122 -0
- package/scripts/pr_verification/model.py +43 -0
- package/scripts/pr_verification/requirements.txt +1 -0
- package/scripts/test_durable_post_lint.py +97 -0
- package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
- package/scripts/tests/test-audit-hooks.mjs +94 -2
- package/scripts/tests/test_automatic_advisory.py +376 -0
- package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
- package/scripts/tests/test_automatic_advisory_polling.py +216 -0
- package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
- package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
- package/scripts/tests/test_automatic_advisory_settings.py +48 -0
- package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
- package/scripts/tests/test_closed_pr_label.py +157 -0
- package/scripts/tests/test_codex_compat_materializer.py +189 -6
- package/scripts/tests/test_github_labels.py +101 -0
- package/scripts/tests/test_github_transport.py +125 -0
- package/scripts/tests/test_local_report_publisher.py +396 -0
- package/scripts/tests/test_local_report_publisher_cli.py +85 -0
- package/scripts/tests/test_local_verification.py +348 -0
- package/scripts/tests/test_local_verification_cli.py +98 -0
- package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
- package/scripts/tests/test_pr_verification_github.py +392 -0
- package/scripts/tests/test_pr_verification_lock.py +168 -0
- package/scripts/tests/test_verification_notice.py +347 -0
- package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
- package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
- package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
- package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
- package/hooks/blocking/destructive_command_blocker.py +0 -2073
- package/hooks/blocking/fable_spawn_gate.py +0 -187
- package/hooks/blocking/luna_fast_mode_gate.py +0 -160
- package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
- package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
- package/hooks/blocking/pii_prevention_blocker.py +0 -365
- package/hooks/blocking/piped_pytest_blocker.py +0 -1223
- package/hooks/blocking/question_to_user_enforcer.py +0 -130
- package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
- package/hooks/blocking/sensitive_file_protector.py +0 -167
- package/hooks/blocking/session_edit_stage_gate.py +0 -684
- package/hooks/blocking/session_handoff_blocker.py +0 -182
- package/hooks/blocking/shell_substitution_blocker.py +0 -350
- package/hooks/blocking/test_ask_user_question_shape.py +0 -69
- package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
- package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
- package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
- package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
- package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
- package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
- package/hooks/blocking/test_fable_spawn_gate.py +0 -381
- package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
- package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
- package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
- package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
- package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
- package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
- package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
- package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
- package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
- package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
- package/hooks/blocking/test_sensitive_file_protector.py +0 -365
- package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
- package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
- package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
- package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
- package/hooks/blocking/test_session_handoff_blocker.py +0 -318
- package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
- package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
- package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
- package/hooks/blocking/unscoped_search_blocker.py +0 -391
- package/hooks/blocking/write_existing_file_blocker.py +0 -124
- package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
- package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
- package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
- package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
- package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
- package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
- package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
|
@@ -4,39 +4,24 @@ import importlib
|
|
|
4
4
|
import pathlib
|
|
5
5
|
import sys
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
APPLY_PATCH_TOOL_NAME,
|
|
26
|
-
BLOCKING_CRASH_DENY_REASON,
|
|
27
|
-
EDIT_TOOL_NAME,
|
|
28
|
-
MULTI_EDIT_TOOL_NAME,
|
|
29
|
-
WRITE_TOOL_NAME,
|
|
30
|
-
)
|
|
31
|
-
from pre_tool_use_dispatcher import (
|
|
32
|
-
HostedHookResult,
|
|
33
|
-
aggregate_hosted_hook_results,
|
|
34
|
-
)
|
|
35
|
-
except ImportError as import_error:
|
|
36
|
-
raise ImportError(
|
|
37
|
-
"test_pre_tool_use_dispatcher_constants: cannot import its sibling modules; "
|
|
38
|
-
"ensure the hooks and blocking directories are importable."
|
|
39
|
-
) from import_error
|
|
7
|
+
_HOOKS_ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
8
|
+
if str(_HOOKS_ROOT) not in sys.path:
|
|
9
|
+
sys.path.insert(0, str(_HOOKS_ROOT))
|
|
10
|
+
|
|
11
|
+
_BLOCKING_DIR = _HOOKS_ROOT / "blocking"
|
|
12
|
+
if str(_BLOCKING_DIR) not in sys.path:
|
|
13
|
+
sys.path.insert(0, str(_BLOCKING_DIR))
|
|
14
|
+
|
|
15
|
+
from hooks_constants.pre_tool_use_dispatcher_constants import (
|
|
16
|
+
ALL_HOSTED_HOOK_ENTRIES,
|
|
17
|
+
ALL_WRITE_EDIT_MULTI_EDIT_APPLY_PATCH_TOOL_NAMES,
|
|
18
|
+
ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
|
|
19
|
+
APPLY_PATCH_TOOL_NAME,
|
|
20
|
+
BLOCKING_CRASH_DENY_REASON,
|
|
21
|
+
EDIT_TOOL_NAME,
|
|
22
|
+
MULTI_EDIT_TOOL_NAME,
|
|
23
|
+
)
|
|
24
|
+
from pre_tool_use_dispatcher import HostedHookResult, aggregate_hosted_hook_results
|
|
40
25
|
|
|
41
26
|
|
|
42
27
|
def _entry_for(script_relative_path: str):
|
|
@@ -48,80 +33,31 @@ def _entry_for(script_relative_path: str):
|
|
|
48
33
|
return matching_entries[0] if matching_entries else None
|
|
49
34
|
|
|
50
35
|
|
|
51
|
-
def
|
|
52
|
-
|
|
36
|
+
def test_roster_keeps_only_nonblocking_edit_advisors() -> None:
|
|
37
|
+
all_script_paths = tuple(
|
|
53
38
|
each_entry.script_relative_path for each_entry in ALL_HOSTED_HOOK_ENTRIES
|
|
54
|
-
]
|
|
55
|
-
assert "blocking/duplicate_rmtree_helper_blocker.py" in all_registered_script_paths, (
|
|
56
|
-
"duplicate_rmtree_helper_blocker must be hosted by the dispatcher so a local "
|
|
57
|
-
"re-definition of the Windows-safe rmtree helper trio is blocked at Write time"
|
|
58
39
|
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
entry = _entry_for("blocking/duplicate_rmtree_helper_blocker.py")
|
|
63
|
-
assert entry is not None
|
|
64
|
-
assert WRITE_TOOL_NAME in entry.applicable_tool_names
|
|
65
|
-
assert EDIT_TOOL_NAME in entry.applicable_tool_names
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def test_duplicate_rmtree_helper_blocker_is_blocking() -> None:
|
|
69
|
-
entry = _entry_for("blocking/duplicate_rmtree_helper_blocker.py")
|
|
70
|
-
assert entry is not None
|
|
71
|
-
assert entry.is_blocking is True
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def test_duplicate_rmtree_helper_blocker_runs_via_runpy() -> None:
|
|
75
|
-
entry = _entry_for("blocking/duplicate_rmtree_helper_blocker.py")
|
|
76
|
-
assert entry is not None
|
|
77
|
-
assert entry.native_module_name is None
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def test_windows_rmtree_write_check_is_retired_but_bash_check_remains() -> None:
|
|
81
|
-
assert _entry_for("blocking/windows_rmtree_blocker.py") is None
|
|
82
|
-
all_bash_paths = {
|
|
83
|
-
each_entry.script_relative_path for each_entry in ALL_BASH_HOSTED_HOOK_ENTRIES
|
|
84
|
-
}
|
|
85
|
-
assert "blocking/windows_rmtree_blocker.py" in all_bash_paths
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def test_blocking_hook_crash_deny_reason_surfaces_the_constant() -> None:
|
|
89
|
-
crash_result = HostedHookResult(
|
|
90
|
-
exit_code=0,
|
|
91
|
-
captured_stdout="",
|
|
92
|
-
did_crash=True,
|
|
93
|
-
is_blocking=True,
|
|
94
|
-
)
|
|
95
|
-
decision = aggregate_hosted_hook_results([crash_result])
|
|
96
|
-
assert decision.should_deny
|
|
97
|
-
assert BLOCKING_CRASH_DENY_REASON in decision.all_deny_reasons
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def test_refactor_guard_is_hosted_as_an_edit_and_multi_edit_advisory_hook() -> None:
|
|
101
|
-
entry = _entry_for("advisory/refactor_guard.py")
|
|
102
|
-
assert entry is not None, (
|
|
103
|
-
"refactor_guard must be hosted by the dispatcher rather than spawning its own process"
|
|
40
|
+
assert all_script_paths == (
|
|
41
|
+
"advisory/refactor_guard.py",
|
|
42
|
+
"advisory/migration_safety_advisor.py",
|
|
104
43
|
)
|
|
105
|
-
assert
|
|
106
|
-
assert entry.is_blocking is False
|
|
44
|
+
assert all(not each_entry.is_blocking for each_entry in ALL_HOSTED_HOOK_ENTRIES)
|
|
107
45
|
|
|
108
46
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
"migration_safety_advisor
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
47
|
+
def test_advisors_apply_to_edit_and_multi_edit() -> None:
|
|
48
|
+
for each_script_path in (
|
|
49
|
+
"advisory/refactor_guard.py",
|
|
50
|
+
"advisory/migration_safety_advisor.py",
|
|
51
|
+
):
|
|
52
|
+
entry = _entry_for(each_script_path)
|
|
53
|
+
assert entry is not None
|
|
54
|
+
assert entry.applicable_tool_names == frozenset({EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME})
|
|
117
55
|
|
|
118
56
|
|
|
119
57
|
def test_every_hosted_script_path_exists_under_the_hooks_root() -> None:
|
|
120
58
|
for each_entry in ALL_HOSTED_HOOK_ENTRIES:
|
|
121
59
|
hosted_script_path = _HOOKS_ROOT / each_entry.script_relative_path
|
|
122
|
-
assert hosted_script_path.is_file()
|
|
123
|
-
f"{each_entry.script_relative_path} is on the roster but missing from the hooks tree"
|
|
124
|
-
)
|
|
60
|
+
assert hosted_script_path.is_file()
|
|
125
61
|
|
|
126
62
|
|
|
127
63
|
def test_retired_native_detector_keeps_its_callable_linter_interface() -> None:
|
|
@@ -137,92 +73,13 @@ def test_four_way_set_covers_every_mutation_tool_name() -> None:
|
|
|
137
73
|
)
|
|
138
74
|
|
|
139
75
|
|
|
140
|
-
def
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
f"{each_script_path} must reach apply_patch for mutation-tool parity"
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
_ALL_DEFERRED_LINT_SCRIPT_PATHS = (
|
|
151
|
-
"blocking/claude_md_orphan_file_blocker.py",
|
|
152
|
-
"blocking/package_inventory_stale_blocker.py",
|
|
153
|
-
"blocking/env_var_table_code_drift_blocker.py",
|
|
154
|
-
"blocking/pytest_testpaths_orphan_blocker.py",
|
|
155
|
-
"blocking/open_questions_in_plans_blocker.py",
|
|
156
|
-
"blocking/docstring_rule_gate_count_blocker.py",
|
|
157
|
-
"blocking/duplicate_rmtree_helper_blocker.py",
|
|
158
|
-
"validation/hook_format_validator.py",
|
|
159
|
-
"advisory/refactor_guard.py",
|
|
160
|
-
"advisory/migration_safety_advisor.py",
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def test_lint_and_advisory_hooks_stay_off_the_apply_patch_roster() -> None:
|
|
165
|
-
"""Every hook reaching MultiEdit but not apply_patch fails the same one rule.
|
|
166
|
-
|
|
167
|
-
apply_patch reaches only a gate whose violation is already real and
|
|
168
|
-
unrecoverable the moment the call returns: a leaked secret, an exposed
|
|
169
|
-
sensitive path, or a blind overwrite. Every
|
|
170
|
-
hook named here instead judges the quality of the authored content for a
|
|
171
|
-
consequence the write itself does not cause right now: doc-inventory
|
|
172
|
-
drift, prose or naming style, a future-runtime correctness pattern (an
|
|
173
|
-
unsafe rmtree that only misbehaves on Windows, a subprocess-timeout
|
|
174
|
-
budget the harness reads separately), or an advisory suggestion that
|
|
175
|
-
never denies at all. Each is fixable in a later Edit-tool pass, so none
|
|
176
|
-
needs apply_patch's narrower, deny-only roster.
|
|
177
|
-
"""
|
|
178
|
-
for each_script_path in _ALL_DEFERRED_LINT_SCRIPT_PATHS:
|
|
179
|
-
entry = _entry_for(each_script_path)
|
|
180
|
-
assert entry is not None, f"{each_script_path} must stay on the hosted roster"
|
|
181
|
-
assert MULTI_EDIT_TOOL_NAME in entry.applicable_tool_names, (
|
|
182
|
-
f"{each_script_path} must still reach MultiEdit"
|
|
183
|
-
)
|
|
184
|
-
assert APPLY_PATCH_TOOL_NAME not in entry.applicable_tool_names, (
|
|
185
|
-
f"{each_script_path} judges deferred-fixable quality, not immediate harm, "
|
|
186
|
-
"so it must stay off the apply_patch roster"
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
def test_edit_and_multi_edit_applicable_sets_are_equal() -> None:
|
|
191
|
-
"""The Edit roster and the MultiEdit roster name the same hooks.
|
|
192
|
-
|
|
193
|
-
Every hook applicable to Edit judges Write's content or an Edit's
|
|
194
|
-
old/new string pair with no dependency on which tool delivered it, so
|
|
195
|
-
the two sets must match exactly. A hook that legitimately stays
|
|
196
|
-
Edit-only is an exception written down here with its reason, not a
|
|
197
|
-
silent gap this test lets back in.
|
|
198
|
-
"""
|
|
199
|
-
all_edit_script_paths = {
|
|
200
|
-
each_entry.script_relative_path
|
|
201
|
-
for each_entry in ALL_HOSTED_HOOK_ENTRIES
|
|
202
|
-
if EDIT_TOOL_NAME in each_entry.applicable_tool_names
|
|
203
|
-
}
|
|
204
|
-
all_multi_edit_script_paths = {
|
|
205
|
-
each_entry.script_relative_path
|
|
206
|
-
for each_entry in ALL_HOSTED_HOOK_ENTRIES
|
|
207
|
-
if MULTI_EDIT_TOOL_NAME in each_entry.applicable_tool_names
|
|
208
|
-
}
|
|
209
|
-
assert all_edit_script_paths == all_multi_edit_script_paths, (
|
|
210
|
-
"Edit-only (not MultiEdit): "
|
|
211
|
-
f"{sorted(all_edit_script_paths - all_multi_edit_script_paths)}; "
|
|
212
|
-
"MultiEdit-only (not Edit): "
|
|
213
|
-
f"{sorted(all_multi_edit_script_paths - all_edit_script_paths)}"
|
|
76
|
+
def test_blocking_hook_crash_deny_reason_surfaces_the_constant() -> None:
|
|
77
|
+
crash_result = HostedHookResult(
|
|
78
|
+
exit_code=0,
|
|
79
|
+
captured_stdout="",
|
|
80
|
+
did_crash=True,
|
|
81
|
+
is_blocking=True,
|
|
214
82
|
)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
all_multi_edit_widened_script_paths = (
|
|
219
|
-
"blocking/write_existing_file_blocker.py",
|
|
220
|
-
"blocking/sensitive_file_protector.py",
|
|
221
|
-
"blocking/pii_prevention_blocker.py",
|
|
222
|
-
)
|
|
223
|
-
for each_script_path in all_multi_edit_widened_script_paths:
|
|
224
|
-
entry = _entry_for(each_script_path)
|
|
225
|
-
assert entry is not None, f"{each_script_path} must stay on the hosted roster"
|
|
226
|
-
assert MULTI_EDIT_TOOL_NAME in entry.applicable_tool_names, (
|
|
227
|
-
f"{each_script_path} must reach MultiEdit for mutation-tool parity"
|
|
228
|
-
)
|
|
83
|
+
decision = aggregate_hosted_hook_results([crash_result])
|
|
84
|
+
assert decision.should_deny
|
|
85
|
+
assert BLOCKING_CRASH_DENY_REASON in decision.all_deny_reasons
|
|
@@ -1,29 +1,7 @@
|
|
|
1
1
|
"""Tests for the Stop-hook dispatcher hosted-hook roster."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from hooks_constants.stop_dispatcher_constants import ALL_STOP_HOSTED_HOOK_PATHS
|
|
4
4
|
|
|
5
|
-
import sys
|
|
6
|
-
from pathlib import Path
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
sys.path.insert(0, _HOOKS_DIR)
|
|
11
|
-
|
|
12
|
-
from hooks_constants.stop_dispatcher_constants import ALL_STOP_HOSTED_HOOK_PATHS # noqa: E402
|
|
13
|
-
|
|
14
|
-
_EXPECTED_STOP_ORDER = (
|
|
15
|
-
"blocking/question_to_user_enforcer.py",
|
|
16
|
-
"blocking/session_handoff_blocker.py",
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def test_roster_lists_all_stop_hooks_in_registration_order() -> None:
|
|
21
|
-
"""The roster reproduces the standalone Stop chain order exactly."""
|
|
22
|
-
assert ALL_STOP_HOSTED_HOOK_PATHS == _EXPECTED_STOP_ORDER
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def test_every_roster_path_points_at_an_existing_hook() -> None:
|
|
26
|
-
"""Each roster path names a hook script present on disk, catching a typo."""
|
|
27
|
-
hooks_root = Path(__file__).resolve().parent.parent
|
|
28
|
-
for each_relative_path in ALL_STOP_HOSTED_HOOK_PATHS:
|
|
29
|
-
assert (hooks_root / each_relative_path).is_file()
|
|
6
|
+
def test_roster_has_no_blocking_hooks() -> None:
|
|
7
|
+
assert ALL_STOP_HOSTED_HOOK_PATHS == ()
|