claude-dev-env 2.27.0 → 2.28.1

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.
Files changed (109) hide show
  1. package/.agents/skills/hitl/SKILL.md +45 -0
  2. package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
  3. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +16 -29
  4. package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
  5. package/bin/ever-shipped-skills.mjs +1 -0
  6. package/docs/CODE_RULES.md +3 -3
  7. package/docs/agent-spawn-protocol.md +1 -1
  8. package/docs/references/prose-style-enforcement.md +5 -9
  9. package/hooks/blocking/AGENTS.md +3 -13
  10. package/hooks/blocking/code_rules_docstrings.py +10 -2138
  11. package/hooks/blocking/code_rules_enforcer.py +0 -121
  12. package/hooks/blocking/code_rules_imports_logging.py +1 -236
  13. package/hooks/blocking/code_rules_shared.py +23 -0
  14. package/hooks/blocking/code_rules_test_layout.py +8 -8
  15. package/hooks/blocking/config/prose_style_enforcement_constants.py +4 -2
  16. package/hooks/blocking/config/test_prose_style_enforcement_constants.py +5 -1
  17. package/hooks/blocking/precommit_code_rules_gate.py +8 -43
  18. package/hooks/blocking/state_description_blocker.py +1 -7
  19. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
  20. package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +0 -8
  21. package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +11 -112
  22. package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +0 -1
  23. package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +1 -18
  24. package/hooks/blocking/test_code_rules_shared.py +12 -0
  25. package/hooks/blocking/test_precommit_code_rules_gate.py +32 -179
  26. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -1
  27. package/hooks/blocking/test_state_description_blocker.py +6 -4
  28. package/hooks/blocking/test_stop_dispatcher.py +5 -29
  29. package/hooks/hooks_constants/AGENTS.md +1 -6
  30. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -1
  31. package/hooks/hooks_constants/code_rules_enforcer_constants.py +3 -0
  32. package/hooks/hooks_constants/messages.py +0 -2
  33. package/hooks/hooks_constants/precommit_code_rules_gate_constants.py +3 -17
  34. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -2
  35. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -1
  36. package/hooks/hooks_constants/test_code_rules_enforcer_constants.py +7 -0
  37. package/hooks/hooks_constants/test_messages.py +5 -3
  38. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -2
  39. package/package.json +1 -1
  40. package/rules/AGENTS.md +3 -6
  41. package/rules/claims-as-quotes.md +0 -10
  42. package/rules/code-standards.md +6 -6
  43. package/rules/explore-thoroughly.md +0 -1
  44. package/rules/failure-blast-radius.md +0 -8
  45. package/rules/falsify-before-green.md +0 -8
  46. package/rules/file-global-constants.md +2 -2
  47. package/rules/filesystem-search.md +1 -1
  48. package/rules/git-workflow.md +1 -9
  49. package/rules/hedging-claims.md +2 -6
  50. package/rules/long-horizon-autonomy.md +1 -1
  51. package/rules/measurement-denominators.md +0 -9
  52. package/rules/research-mode.md +0 -6
  53. package/rules/verify-before-asking.md +0 -5
  54. package/rules/verify-runtime-state.md +0 -5
  55. package/scripts/codex_compat_materializer.py +99 -24
  56. package/scripts/sync_to_cursor/rules.py +0 -10
  57. package/scripts/tests/test_codex_compat_materializer.py +70 -19
  58. package/scripts/tests/test_engine.py +0 -1
  59. package/scripts/tests/test_rules.py +0 -1
  60. package/scripts/tests/test_sync_to_cursor.py +0 -1
  61. package/system-prompts/software-engineer.xml +3 -3
  62. package/hooks/blocking/code_rules_dead_argparse_argument.py +0 -554
  63. package/hooks/blocking/code_rules_dead_config_field.py +0 -568
  64. package/hooks/blocking/code_rules_dead_dataclass_field.py +0 -348
  65. package/hooks/blocking/code_rules_dead_module_constant.py +0 -757
  66. package/hooks/blocking/code_rules_dead_split_branch.py +0 -225
  67. package/hooks/blocking/code_rules_mock_completeness.py +0 -295
  68. package/hooks/blocking/code_rules_scope_binding.py +0 -151
  69. package/hooks/blocking/code_rules_unused_imports.py +0 -197
  70. package/hooks/blocking/hedging_language_blocker.py +0 -221
  71. package/hooks/blocking/intent_only_ending_blocker.py +0 -148
  72. package/hooks/blocking/test_code_rules_enforcer_dead_argparse_argument.py +0 -534
  73. package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +0 -846
  74. package/hooks/blocking/test_code_rules_enforcer_dead_dataclass_field.py +0 -507
  75. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +0 -679
  76. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_alias.py +0 -133
  77. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +0 -103
  78. package/hooks/blocking/test_code_rules_enforcer_dead_split_branch.py +0 -105
  79. package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +0 -425
  80. package/hooks/blocking/test_code_rules_enforcer_docstring_cardinal_family.py +0 -176
  81. package/hooks/blocking/test_code_rules_enforcer_docstring_delegation_summary.py +0 -385
  82. package/hooks/blocking/test_code_rules_enforcer_docstring_fallback_branch.py +0 -398
  83. package/hooks/blocking/test_code_rules_enforcer_docstring_field_runmode_outcome.py +0 -129
  84. package/hooks/blocking/test_code_rules_enforcer_docstring_inline_literal_claim.py +0 -93
  85. package/hooks/blocking/test_code_rules_enforcer_docstring_length_constant_superlative.py +0 -198
  86. package/hooks/blocking/test_code_rules_enforcer_docstring_mark_glyph_enumeration.py +0 -262
  87. package/hooks/blocking/test_code_rules_enforcer_docstring_no_consumer.py +0 -93
  88. package/hooks/blocking/test_code_rules_enforcer_docstring_no_network.py +0 -115
  89. package/hooks/blocking/test_code_rules_enforcer_docstring_raises_largezipfile.py +0 -226
  90. package/hooks/blocking/test_code_rules_enforcer_docstring_returns_plural_cardinality.py +0 -207
  91. package/hooks/blocking/test_code_rules_enforcer_docstring_step_dispatch.py +0 -262
  92. package/hooks/blocking/test_code_rules_enforcer_docstring_type_checking_gate.py +0 -164
  93. package/hooks/blocking/test_code_rules_enforcer_docstring_unguarded_payload.py +0 -188
  94. package/hooks/blocking/test_code_rules_enforcer_import_block_sort.py +0 -157
  95. package/hooks/blocking/test_code_rules_enforcer_split_mocks_1.py +0 -303
  96. package/hooks/blocking/test_code_rules_enforcer_split_mocks_2.py +0 -111
  97. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +0 -656
  98. package/hooks/blocking/test_hedging_language_blocker.py +0 -261
  99. package/hooks/blocking/test_intent_only_ending_blocker.py +0 -209
  100. package/hooks/hooks_constants/dead_argparse_argument_constants.py +0 -28
  101. package/hooks/hooks_constants/dead_config_field_constants.py +0 -39
  102. package/hooks/hooks_constants/dead_dataclass_field_constants.py +0 -25
  103. package/hooks/hooks_constants/dead_module_constant_constants.py +0 -30
  104. package/hooks/hooks_constants/hedging_uncertainty_constants.py +0 -42
  105. package/hooks/hooks_constants/test_dispatcher_constants_docstrings.py +0 -44
  106. package/rules/conservative-action.md +0 -17
  107. package/rules/context7.md +0 -8
  108. package/rules/few-words.md +0 -3
  109. package/rules/parallel-tools.md +0 -23
@@ -1,262 +0,0 @@
1
- """Tests for check_docstring_step_enumeration_dispatch_coverage — O4 step drift.
2
-
3
- A function whose docstring enumerates a linear step sequence matching the body's
4
- top-level calls, while the body also routes to a corrective action inside an
5
- ``if``/``elif`` branch the prose never names, hides that conditional path from the
6
- reader. This is the deterministic slice of Category O4 (step-ordering narrative).
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import importlib.util
12
- from pathlib import Path
13
- from types import ModuleType
14
-
15
-
16
- def _load_enforcer_module() -> ModuleType:
17
- module_path = Path(__file__).parent / "code_rules_enforcer.py"
18
- spec = importlib.util.spec_from_file_location("code_rules_enforcer", module_path)
19
- assert spec is not None
20
- assert spec.loader is not None
21
- module = importlib.util.module_from_spec(spec)
22
- spec.loader.exec_module(module)
23
- return module
24
-
25
-
26
- code_rules_enforcer = _load_enforcer_module()
27
-
28
-
29
- def check_docstring_step_enumeration_dispatch_coverage(content: str, file_path: str) -> list[str]:
30
- return code_rules_enforcer.check_docstring_step_enumeration_dispatch_coverage(
31
- content, file_path
32
- )
33
-
34
-
35
- def validate_content(content: str, file_path: str, old_content: str) -> list[str]:
36
- return code_rules_enforcer.validate_content(content, file_path, old_content)
37
-
38
-
39
- PRODUCTION_FILE_PATH = "/project/src/theme_update_listing_edits.py"
40
- TEST_FILE_PATH = "/project/src/test_theme_update_listing_edits.py"
41
- HOOK_INFRASTRUCTURE_PATH = "/home/user/.claude/hooks/blocking/example.py"
42
-
43
-
44
- def _drifted_update_binary_function() -> str:
45
- return (
46
- "async def _open_update_and_upload_binary(\n"
47
- " self, content_id: str, apk_path: Path\n"
48
- ") -> ClickedUpdateButton:\n"
49
- ' """Open the theme\'s update form, upload the new binary, and exclude'
50
- " Fold devices.\n"
51
- "\n"
52
- " Navigates to the content list, searches for the content ID, clicks\n"
53
- " whichever update-action button is present, navigates to the Binary tab,\n"
54
- " and uploads the APK.\n"
55
- ' """\n'
56
- " if not await self.navigate_to_content_list_start():\n"
57
- " return ClickedUpdateButton.NONE\n"
58
- " if not await self.search_for_content_id(content_id):\n"
59
- " return ClickedUpdateButton.NONE\n"
60
- " clicked = await self.click_update_button()\n"
61
- " if not await self.navigate_to_binary_tab():\n"
62
- " return ClickedUpdateButton.NONE\n"
63
- " decision = await classify_binary_state(self.automation.cdp, self.oneui_number)\n"
64
- " if decision.path is BinaryUpdatePath.PATH_B_CANCEL_UPDATE:\n"
65
- " if not await cancel_and_reinitiate_update(self, content_id):\n"
66
- " return ClickedUpdateButton.NONE\n"
67
- " elif decision.path is BinaryUpdatePath.PATH_A_REPLACE_BINARY:\n"
68
- " if not await replace_target_binary_row(self.automation, apk_path):\n"
69
- " return ClickedUpdateButton.NONE\n"
70
- " if not await upload_binary_to_open_theme(self.automation, [apk_path]):\n"
71
- " return ClickedUpdateButton.NONE\n"
72
- " return clicked\n"
73
- )
74
-
75
-
76
- def _enumerated_update_binary_function() -> str:
77
- return (
78
- "async def _open_update_and_upload_binary(\n"
79
- " self, content_id: str, apk_path: Path\n"
80
- ") -> ClickedUpdateButton:\n"
81
- ' """Open the theme\'s update form, upload the new binary, and exclude'
82
- " Fold devices.\n"
83
- "\n"
84
- " Navigates to the content list, searches for the content ID, clicks\n"
85
- " whichever update-action button is present, navigates to the Binary tab,\n"
86
- " classifies the existing binary rows and, when needed, cancels and\n"
87
- " reinitiates the update or replaces the target binary row, then uploads\n"
88
- " the APK.\n"
89
- ' """\n'
90
- " if not await self.navigate_to_content_list_start():\n"
91
- " return ClickedUpdateButton.NONE\n"
92
- " if not await self.search_for_content_id(content_id):\n"
93
- " return ClickedUpdateButton.NONE\n"
94
- " clicked = await self.click_update_button()\n"
95
- " if not await self.navigate_to_binary_tab():\n"
96
- " return ClickedUpdateButton.NONE\n"
97
- " decision = await classify_binary_state(self.automation.cdp, self.oneui_number)\n"
98
- " if decision.path is BinaryUpdatePath.PATH_B_CANCEL_UPDATE:\n"
99
- " if not await cancel_and_reinitiate_update(self, content_id):\n"
100
- " return ClickedUpdateButton.NONE\n"
101
- " elif decision.path is BinaryUpdatePath.PATH_A_REPLACE_BINARY:\n"
102
- " if not await replace_target_binary_row(self.automation, apk_path):\n"
103
- " return ClickedUpdateButton.NONE\n"
104
- " if not await upload_binary_to_open_theme(self.automation, [apk_path]):\n"
105
- " return ClickedUpdateButton.NONE\n"
106
- " return clicked\n"
107
- )
108
-
109
-
110
- def test_should_flag_branch_only_dispatch_call_the_prose_omits() -> None:
111
- issues = check_docstring_step_enumeration_dispatch_coverage(
112
- _drifted_update_binary_function(), PRODUCTION_FILE_PATH
113
- )
114
- assert any("cancel_and_reinitiate_update" in each for each in issues), (
115
- f"The Path B cancel-and-reinitiate dispatch must be flagged, got: {issues!r}"
116
- )
117
- assert any("replace_target_binary_row" in each for each in issues), (
118
- f"The Path A replace-target-row dispatch must be flagged, got: {issues!r}"
119
- )
120
-
121
-
122
- def test_should_report_category_o4_in_the_message() -> None:
123
- issues = check_docstring_step_enumeration_dispatch_coverage(
124
- _drifted_update_binary_function(), PRODUCTION_FILE_PATH
125
- )
126
- assert any("O4" in each for each in issues), (
127
- f"Expected the Category O4 label in the message, got: {issues!r}"
128
- )
129
-
130
-
131
- def test_should_not_flag_when_dispatch_steps_are_enumerated() -> None:
132
- issues = check_docstring_step_enumeration_dispatch_coverage(
133
- _enumerated_update_binary_function(), PRODUCTION_FILE_PATH
134
- )
135
- assert issues == [], (
136
- f"A docstring that names the corrective-path steps must not be flagged, got: {issues!r}"
137
- )
138
-
139
-
140
- def test_should_not_flag_when_fewer_than_two_linear_steps_are_named() -> None:
141
- source = (
142
- "def run(target: object) -> None:\n"
143
- ' """Compose the report from the target."""\n'
144
- " compose_report(target)\n"
145
- " flush_pending_writes(target)\n"
146
- " if target.is_stale:\n"
147
- " purge_expired_cache_entries(target)\n"
148
- )
149
- issues = check_docstring_step_enumeration_dispatch_coverage(source, PRODUCTION_FILE_PATH)
150
- assert issues == [], (
151
- "Only one linear step (compose_report) is named in the prose, below the bind "
152
- f"threshold, so no dispatch is flagged, got: {issues!r}"
153
- )
154
-
155
-
156
- def test_should_not_flag_guarded_callee_that_is_also_a_linear_step() -> None:
157
- source = (
158
- "def run(target: object) -> None:\n"
159
- ' """Validate target, then dispatch event to queue."""\n'
160
- " if not validate_target(target):\n"
161
- " return\n"
162
- " if not dispatch_event_to_queue(target):\n"
163
- " return\n"
164
- " if target.is_retry:\n"
165
- " if not dispatch_event_to_queue(target):\n"
166
- " return\n"
167
- )
168
- issues = check_docstring_step_enumeration_dispatch_coverage(source, PRODUCTION_FILE_PATH)
169
- assert issues == [], (
170
- "A guarded callee the body also guards as a linear step is covered by the "
171
- f"enumeration and must not be flagged, got: {issues!r}"
172
- )
173
-
174
-
175
- def test_should_not_flag_plain_branch_logging_call() -> None:
176
- source = (
177
- "def run(target: object) -> None:\n"
178
- ' """Validate target, then dispatch event to queue."""\n'
179
- " if not validate_target(target):\n"
180
- " return\n"
181
- " if not dispatch_event_to_queue(target):\n"
182
- " return\n"
183
- " if target.is_error:\n"
184
- " capture_error_screenshot(target)\n"
185
- " log_error_details(target)\n"
186
- )
187
- issues = check_docstring_step_enumeration_dispatch_coverage(source, PRODUCTION_FILE_PATH)
188
- assert issues == [], (
189
- "Plain (unguarded) branch logging and screenshot calls are not control-flow "
190
- f"dispatch steps and must not be flagged, got: {issues!r}"
191
- )
192
-
193
-
194
- def test_should_not_flag_single_token_guarded_branch_callee() -> None:
195
- source = (
196
- "def run(target: object) -> None:\n"
197
- ' """Open the form, then submit the form."""\n'
198
- " if not open_the_form(target):\n"
199
- " return\n"
200
- " if not submit_the_form(target):\n"
201
- " return\n"
202
- " if target.is_dirty:\n"
203
- " if not rollback(target):\n"
204
- " return\n"
205
- )
206
- issues = check_docstring_step_enumeration_dispatch_coverage(source, PRODUCTION_FILE_PATH)
207
- assert issues == [], (
208
- "A single-token guarded branch callee is below the token threshold and must "
209
- f"not be flagged, got: {issues!r}"
210
- )
211
-
212
-
213
- def test_should_flag_guarded_branch_dispatch_step_the_prose_omits() -> None:
214
- source = (
215
- "def run(target: object) -> None:\n"
216
- ' """Validate target, then dispatch event to queue."""\n'
217
- " if not validate_target(target):\n"
218
- " return\n"
219
- " if not dispatch_event_to_queue(target):\n"
220
- " return\n"
221
- " if target.is_retry:\n"
222
- " if not reissue_pending_credential(target):\n"
223
- " return\n"
224
- )
225
- issues = check_docstring_step_enumeration_dispatch_coverage(source, PRODUCTION_FILE_PATH)
226
- assert any("reissue_pending_credential" in each for each in issues), (
227
- "A guarded multi-token branch dispatch step the prose omits must be flagged, "
228
- f"got: {issues!r}"
229
- )
230
-
231
-
232
- def test_should_skip_test_file() -> None:
233
- issues = check_docstring_step_enumeration_dispatch_coverage(
234
- _drifted_update_binary_function(), TEST_FILE_PATH
235
- )
236
- assert issues == [], f"Test files exempt, got: {issues!r}"
237
-
238
-
239
- def test_should_skip_hook_infrastructure() -> None:
240
- issues = check_docstring_step_enumeration_dispatch_coverage(
241
- _drifted_update_binary_function(), HOOK_INFRASTRUCTURE_PATH
242
- )
243
- assert issues == [], f"Hook infrastructure exempt, got: {issues!r}"
244
-
245
-
246
- def test_should_handle_syntax_error_gracefully() -> None:
247
- issues = check_docstring_step_enumeration_dispatch_coverage(
248
- "def fetch(\n", PRODUCTION_FILE_PATH
249
- )
250
- assert issues == [], f"Syntax error must yield no issues, got: {issues!r}"
251
-
252
-
253
- def test_validate_content_surfaces_step_dispatch_drift() -> None:
254
- issues = validate_content(
255
- _drifted_update_binary_function(), PRODUCTION_FILE_PATH, old_content=""
256
- )
257
- matching_issues = [
258
- each for each in issues if "cancel_and_reinitiate_update" in each and "O4" in each
259
- ]
260
- assert matching_issues, (
261
- f"Expected validate_content to surface the O4 step-dispatch drift, got: {issues!r}"
262
- )
@@ -1,164 +0,0 @@
1
- """Tests for check_docstring_names_absent_type_checking_gate — Category O6.
2
-
3
- A module or function docstring that names a ``TYPE_CHECKING`` gate-detection step,
4
- or a ``type-checking-gate`` helper family, drifts once no identifier in the body
5
- handles TYPE_CHECKING: the prose points a reader at machinery the module does not
6
- hold. This is the deterministic slice of Category O6 (docstring prose versus
7
- implementation drift) for a TYPE_CHECKING gate claim. The check covers hook
8
- infrastructure, where the import-scan gates that carry this drift class live.
9
- """
10
-
11
- from __future__ import annotations
12
-
13
- import importlib.util
14
- from pathlib import Path
15
- from types import ModuleType
16
-
17
-
18
- def _load_enforcer_module() -> ModuleType:
19
- module_path = Path(__file__).parent / "code_rules_enforcer.py"
20
- spec = importlib.util.spec_from_file_location("code_rules_enforcer", module_path)
21
- assert spec is not None
22
- assert spec.loader is not None
23
- module = importlib.util.module_from_spec(spec)
24
- spec.loader.exec_module(module)
25
- return module
26
-
27
-
28
- code_rules_enforcer = _load_enforcer_module()
29
-
30
-
31
- def check_type_checking_gate(content: str, file_path: str) -> list[str]:
32
- return code_rules_enforcer.check_docstring_names_absent_type_checking_gate(content, file_path)
33
-
34
-
35
- PRODUCTION_HOOK_PATH = "/home/user/.claude/hooks/blocking/code_rules_unused_imports.py"
36
- TEST_FILE_PATH = "/home/user/.claude/hooks/blocking/test_code_rules_unused_imports.py"
37
- MYPY_GATE_PRODUCTION_PATH = "/home/user/project/scripts/run_mypy_gate.py"
38
-
39
-
40
- def test_flags_module_docstring_naming_type_checking_gate_helpers() -> None:
41
- content = (
42
- '"""Unused module-level import check and its import-range and '
43
- 'type-checking-gate helpers."""\n'
44
- "\n"
45
- "import ast\n"
46
- "\n"
47
- "\n"
48
- "def check_unused(content: str) -> list[str]:\n"
49
- ' """Flag unused imports."""\n'
50
- " return []\n"
51
- )
52
- issues = check_type_checking_gate(content, PRODUCTION_HOOK_PATH)
53
- assert len(issues) == 1
54
- assert issues[0].startswith("Line 1:")
55
- assert "type-checking-gate" in issues[0]
56
-
57
-
58
- def test_flags_function_docstring_naming_type_checking_gate_detection() -> None:
59
- content = (
60
- "import ast\n"
61
- "\n"
62
- "\n"
63
- "def check_unused_module_level_imports(content: str) -> list[str]:\n"
64
- ' """Flag module-level imports never referenced.\n'
65
- "\n"
66
- " When ``full_file_content`` is provided, the ``__all__`` /\n"
67
- " ``TYPE_CHECKING`` gate detection and reference scanning run against\n"
68
- " ``full_file_content``.\n"
69
- ' """\n'
70
- " return []\n"
71
- )
72
- issues = check_type_checking_gate(content, PRODUCTION_HOOK_PATH)
73
- assert len(issues) == 1
74
- assert "type_checking gate" in issues[0]
75
-
76
-
77
- def test_flags_both_module_and_function_docstrings_on_the_real_drift() -> None:
78
- content = (
79
- '"""Unused module-level import check and its import-range and '
80
- 'type-checking-gate helpers."""\n'
81
- "\n"
82
- "import ast\n"
83
- "\n"
84
- "\n"
85
- "def check_unused_module_level_imports(content: str) -> list[str]:\n"
86
- ' """Flag module-level imports never referenced.\n'
87
- "\n"
88
- " The ``__all__`` / ``TYPE_CHECKING`` gate detection and reference\n"
89
- " scanning run against ``full_file_content``.\n"
90
- ' """\n'
91
- " return []\n"
92
- )
93
- issues = check_type_checking_gate(content, PRODUCTION_HOOK_PATH)
94
- assert len(issues) == 2
95
-
96
-
97
- def test_passes_when_code_declares_a_type_checking_gate_helper() -> None:
98
- content = (
99
- '"""Unused module-level import check and its type-checking-gate helpers."""\n'
100
- "\n"
101
- "import ast\n"
102
- "\n"
103
- "\n"
104
- "def _module_body_declares_type_checking_gate(tree: ast.Module) -> bool:\n"
105
- ' """Return True when the module guards imports behind TYPE_CHECKING."""\n'
106
- " return False\n"
107
- )
108
- assert check_type_checking_gate(content, PRODUCTION_HOOK_PATH) == []
109
-
110
-
111
- def test_passes_when_body_names_type_checking_guard() -> None:
112
- content = (
113
- "import ast\n"
114
- "from typing import TYPE_CHECKING\n"
115
- "\n"
116
- "\n"
117
- "def check_unused(content: str) -> list[str]:\n"
118
- ' """Skip imports the ``TYPE_CHECKING`` gate detection guards."""\n'
119
- " if TYPE_CHECKING:\n"
120
- " return []\n"
121
- " return []\n"
122
- )
123
- assert check_type_checking_gate(content, PRODUCTION_HOOK_PATH) == []
124
-
125
-
126
- def test_passes_when_no_docstring_names_the_gate() -> None:
127
- content = (
128
- '"""Unused module-level import check and its import-range helpers."""\n'
129
- "\n"
130
- "import ast\n"
131
- "\n"
132
- "\n"
133
- "def check_unused(content: str) -> list[str]:\n"
134
- ' """Flag module-level imports never referenced."""\n'
135
- " return []\n"
136
- )
137
- assert check_type_checking_gate(content, PRODUCTION_HOOK_PATH) == []
138
-
139
-
140
- def test_passes_on_mypy_static_type_checker_gate_docstring() -> None:
141
- content = (
142
- "\"\"\"Run the project's type-checking gate and report failures.\"\"\"\n"
143
- "\n"
144
- "import subprocess\n"
145
- "\n"
146
- "\n"
147
- "def run_gate() -> int:\n"
148
- ' return subprocess.run(["mypy", "hooks"]).returncode\n'
149
- )
150
- assert check_type_checking_gate(content, MYPY_GATE_PRODUCTION_PATH) == []
151
-
152
-
153
- def test_test_files_are_exempt() -> None:
154
- content = (
155
- '"""Unused module-level import check and its type-checking-gate helpers."""\n'
156
- "\n"
157
- "import ast\n"
158
- "\n"
159
- "\n"
160
- "def check_unused(content: str) -> list[str]:\n"
161
- ' """Flag unused imports."""\n'
162
- " return []\n"
163
- )
164
- assert check_type_checking_gate(content, TEST_FILE_PATH) == []
@@ -1,188 +0,0 @@
1
- """Tests for check_docstring_unguarded_malformed_payload_claim — Category O6 drift.
2
-
3
- A function docstring that promises "a malformed payload resolves to None" asserts
4
- the body catches a bad payload and turns it into a None return. The claim drifts
5
- when the value construction that dereferences payload fields (``payload["key"]``,
6
- ``float(payload["key"])``) sits OUTSIDE the try/except whose handler returns None:
7
- a present-but-malformed payload raises KeyError or TypeError from that unguarded
8
- dereference and propagates rather than resolving to None. This is the deterministic
9
- slice of Category O6 (docstring prose vs implementation drift) for an
10
- exception-guard claim.
11
- """
12
-
13
- from __future__ import annotations
14
-
15
- import importlib.util
16
- from pathlib import Path
17
- from types import ModuleType
18
-
19
-
20
- def _load_enforcer_module() -> ModuleType:
21
- module_path = Path(__file__).parent / "code_rules_enforcer.py"
22
- spec = importlib.util.spec_from_file_location("code_rules_enforcer", module_path)
23
- assert spec is not None
24
- assert spec.loader is not None
25
- module = importlib.util.module_from_spec(spec)
26
- spec.loader.exec_module(module)
27
- return module
28
-
29
-
30
- code_rules_enforcer = _load_enforcer_module()
31
-
32
-
33
- def check_docstring_unguarded_malformed_payload_claim(content: str, file_path: str) -> list[str]:
34
- return code_rules_enforcer.check_docstring_unguarded_malformed_payload_claim(content, file_path)
35
-
36
-
37
- PRODUCTION_FILE_PATH = "/project/shared/human_actions.py"
38
- TEST_FILE_PATH = "/project/shared/test_human_actions.py"
39
- HOOK_INFRASTRUCTURE_PATH = "/home/user/.claude/hooks/blocking/example.py"
40
-
41
-
42
- _UNGUARDED_DEREFERENCE_BODY = (
43
- "def read_metrics(self, selector: str) -> object:\n"
44
- ' """Read the container geometry over CDP, or None on failure.\n'
45
- "\n"
46
- " A missing element, a CDP error, or a malformed payload resolves to None\n"
47
- " so the caller skips the drag.\n"
48
- ' """\n'
49
- " try:\n"
50
- " evaluate_payload = self.cdp.run(selector)\n"
51
- " parsed_metrics = json.loads(evaluate_payload)\n"
52
- " except (KeyError, TypeError, ValueError):\n"
53
- " return None\n"
54
- ' if not parsed_metrics.get("found"):\n'
55
- " return None\n"
56
- " return Metrics(\n"
57
- ' client_width=float(parsed_metrics["client_width"]),\n'
58
- ' client_height=float(parsed_metrics["client_height"]),\n'
59
- " )\n"
60
- )
61
-
62
-
63
- def test_flags_dereference_outside_the_guarded_block() -> None:
64
- issues = check_docstring_unguarded_malformed_payload_claim(
65
- _UNGUARDED_DEREFERENCE_BODY, PRODUCTION_FILE_PATH
66
- )
67
- assert len(issues) == 1
68
- assert "read_metrics" in issues[0]
69
-
70
-
71
- def test_passes_when_dereference_sits_inside_the_guarded_block() -> None:
72
- content = (
73
- "def read_metrics(self, selector: str) -> object:\n"
74
- ' """Read the container geometry over CDP, or None on failure.\n'
75
- "\n"
76
- " A missing element, a CDP error, or a malformed payload resolves to None\n"
77
- " so the caller skips the drag.\n"
78
- ' """\n'
79
- " try:\n"
80
- " evaluate_payload = self.cdp.run(selector)\n"
81
- " parsed_metrics = json.loads(evaluate_payload)\n"
82
- ' if not parsed_metrics.get("found"):\n'
83
- " return None\n"
84
- " return Metrics(\n"
85
- ' client_width=float(parsed_metrics["client_width"]),\n'
86
- ' client_height=float(parsed_metrics["client_height"]),\n'
87
- " )\n"
88
- " except (KeyError, TypeError, ValueError):\n"
89
- " return None\n"
90
- )
91
- assert check_docstring_unguarded_malformed_payload_claim(content, PRODUCTION_FILE_PATH) == []
92
-
93
-
94
- def test_passes_when_docstring_makes_no_malformed_payload_claim() -> None:
95
- content = (
96
- "def read_metrics(self, selector: str) -> object:\n"
97
- ' """Read the container geometry over CDP, or None on failure."""\n'
98
- " try:\n"
99
- " parsed_metrics = json.loads(self.cdp.run(selector))\n"
100
- " except (KeyError, TypeError, ValueError):\n"
101
- " return None\n"
102
- " return Metrics(\n"
103
- ' client_width=float(parsed_metrics["client_width"]),\n'
104
- " )\n"
105
- )
106
- assert check_docstring_unguarded_malformed_payload_claim(content, PRODUCTION_FILE_PATH) == []
107
-
108
-
109
- def test_passes_when_no_subscript_dereference_follows_the_guard() -> None:
110
- content = (
111
- "def read_metrics(self, selector: str) -> object:\n"
112
- ' """Read the geometry over CDP, or None on failure.\n'
113
- "\n"
114
- " A malformed payload resolves to None so the caller skips the drag.\n"
115
- ' """\n'
116
- " try:\n"
117
- " parsed_metrics = json.loads(self.cdp.run(selector))\n"
118
- " except (KeyError, TypeError, ValueError):\n"
119
- " return None\n"
120
- ' return parsed_metrics.get("found")\n'
121
- )
122
- assert check_docstring_unguarded_malformed_payload_claim(content, PRODUCTION_FILE_PATH) == []
123
-
124
-
125
- def test_passes_when_post_guard_subscript_dereferences_an_unrelated_name() -> None:
126
- content = (
127
- "def read_metrics(self, selector: str) -> object:\n"
128
- ' """Read the geometry over CDP, or None on failure.\n'
129
- "\n"
130
- " A malformed payload resolves to None so the caller skips the drag.\n"
131
- ' """\n'
132
- " try:\n"
133
- " parsed_metrics = json.loads(self.cdp.run(selector))\n"
134
- ' width = float(parsed_metrics["client_width"])\n'
135
- " except (KeyError, TypeError, ValueError):\n"
136
- " return None\n"
137
- " fallback = cache[selector]\n"
138
- " return Metrics(client_width=width, fallback=fallback)\n"
139
- )
140
- assert check_docstring_unguarded_malformed_payload_claim(content, PRODUCTION_FILE_PATH) == []
141
-
142
-
143
- def _unguarded_body_with_claim(claim_sentence: str) -> str:
144
- return (
145
- "def read_metrics(self, selector: str) -> object:\n"
146
- ' """Read the container geometry over CDP, or None on failure.\n'
147
- "\n"
148
- f" {claim_sentence}\n"
149
- ' """\n'
150
- " try:\n"
151
- " parsed_metrics = json.loads(self.cdp.run(selector))\n"
152
- " except (KeyError, TypeError, ValueError):\n"
153
- " return None\n"
154
- ' return float(parsed_metrics["client_width"])\n'
155
- )
156
-
157
-
158
- def test_article_prefixed_claim_phrasings_still_flag_via_articleless_substring() -> None:
159
- all_article_prefixed_claims = (
160
- "A malformed payload resolves to None so the caller skips the drag.",
161
- "A bad payload resolves to None so the caller skips the drag.",
162
- "An invalid payload resolves to None so the caller skips the drag.",
163
- "A malformed payload yields None so the caller skips the drag.",
164
- )
165
- for each_claim in all_article_prefixed_claims:
166
- issues = check_docstring_unguarded_malformed_payload_claim(
167
- _unguarded_body_with_claim(each_claim), PRODUCTION_FILE_PATH
168
- )
169
- assert len(issues) == 1
170
- assert "read_metrics" in issues[0]
171
-
172
-
173
- def test_test_files_are_exempt() -> None:
174
- assert (
175
- check_docstring_unguarded_malformed_payload_claim(
176
- _UNGUARDED_DEREFERENCE_BODY, TEST_FILE_PATH
177
- )
178
- == []
179
- )
180
-
181
-
182
- def test_hook_infrastructure_is_exempt() -> None:
183
- assert (
184
- check_docstring_unguarded_malformed_payload_claim(
185
- _UNGUARDED_DEREFERENCE_BODY, HOOK_INFRASTRUCTURE_PATH
186
- )
187
- == []
188
- )