claude-dev-env 2.28.0 → 2.29.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/agents/AGENTS.md +0 -31
- package/.agents/agents/clean-coder.md +1 -1
- package/.agents/agents/test_agent_frontmatter.py +3 -1
- package/.agents/skills/AGENTS.md +0 -70
- package/.agents/skills/_shared/AGENTS.md +0 -44
- package/.agents/skills/_shared/advisor/AGENTS.md +0 -8
- package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -57
- package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -8
- package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -34
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -23
- package/.agents/skills/autoconverge/AGENTS.md +0 -35
- package/.agents/skills/autoconverge/reference/AGENTS.md +0 -15
- package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -25
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -15
- package/.agents/skills/everything-search/AGENTS.md +0 -16
- package/.agents/skills/fresh-branch/AGENTS.md +0 -13
- package/.agents/skills/grok-spawn/AGENTS.md +0 -27
- package/.agents/skills/orchestrator/.claude/CLAUDE.md +1 -0
- package/.agents/skills/orchestrator/AGENTS.md +1 -0
- package/.agents/skills/orchestrator/SKILL.md +44 -53
- package/.agents/skills/orchestrator/reference/.claude/CLAUDE.md +1 -0
- package/.agents/skills/orchestrator/reference/AGENTS.md +1 -0
- package/.agents/skills/orchestrator/reference/consult-the-orchestrator.md +70 -0
- package/.agents/skills/orchestrator/reference/executor-consult-block.md +62 -0
- package/.agents/skills/orchestrator/reference/host-detect.md +15 -0
- package/.agents/skills/orchestrator/test_orchestrator_skill_contract.py +62 -0
- package/.agents/skills/orchestrator-refresh/SKILL.md +16 -34
- package/.agents/skills/rebase/AGENTS.md +0 -31
- package/.agents/skills/session-log/AGENTS.md +0 -31
- package/.agents/skills/session-tidy/AGENTS.md +0 -35
- package/.agents/skills/skill-builder/AGENTS.md +0 -48
- package/.agents/skills/skill-builder/references/AGENTS.md +0 -24
- package/.agents/skills/skill-builder/templates/AGENTS.md +0 -13
- package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -18
- package/.agents/skills/task-build/AGENTS.md +0 -28
- package/.agents/skills/update/AGENTS.md +0 -37
- package/.agents/skills-archived/AGENTS.md +0 -44
- package/.agents/skills-archived/anthropic-plan/AGENTS.md +0 -33
- package/.agents/skills-archived/anthropic-plan/scripts/AGENTS.md +0 -10
- package/.agents/skills-archived/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +0 -15
- package/.agents/skills-archived/anthropic-plan/templates/AGENTS.md +0 -12
- package/.agents/skills-archived/anthropic-plan/workflow/AGENTS.md +0 -13
- package/.agents/skills-archived/auditing-claude-config/AGENTS.md +0 -20
- package/.agents/skills-archived/bugteam/AGENTS.md +0 -29
- package/.agents/skills-archived/bugteam/reference/AGENTS.md +0 -19
- package/.agents/skills-archived/bugteam/reference/obstacles/AGENTS.md +0 -23
- package/.agents/skills-archived/bugteam/scripts/AGENTS.md +0 -29
- package/.agents/skills-archived/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +0 -17
- package/.agents/skills-archived/codex-review/AGENTS.md +0 -45
- package/.agents/skills-archived/codex-review/reference/AGENTS.md +0 -14
- package/.agents/skills-archived/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +0 -17
- package/.agents/skills-archived/codex-review/test_skill_scaffold.py +13 -3
- package/.agents/skills-archived/copilot-review/AGENTS.md +0 -17
- package/.agents/skills-archived/pr-converge/AGENTS.md +0 -31
- package/.agents/skills-archived/pr-converge/pr_converge_skill_constants/AGENTS.md +0 -25
- package/.agents/skills-archived/pr-converge/reference/AGENTS.md +0 -27
- package/.agents/skills-archived/pr-converge/reference/obstacles/AGENTS.md +0 -22
- package/.agents/skills-archived/pr-converge/scripts/AGENTS.md +0 -45
- package/.agents/skills-archived/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +0 -17
- package/.agents/skills-archived/pr-converge/workflows/AGENTS.md +0 -15
- package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
- package/.agents/skills-archived/recall/AGENTS.md +0 -29
- package/.agents/skills-archived/remember/AGENTS.md +0 -30
- package/AGENTS.md +0 -112
- package/_shared/AGENTS.md +0 -16
- package/_shared/advisor/AGENTS.md +0 -21
- package/_shared/advisor/advisor-protocol.md +4 -1
- package/_shared/advisor/reference/consult-format.md +4 -2
- package/_shared/pr-loop/AGENTS.md +0 -27
- package/_shared/pr-loop/scripts/AGENTS.md +0 -49
- package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +0 -41
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/AGENTS.md +0 -17
- package/_shared/pr-loop/scripts/pr_converge_scripts_constants/AGENTS.md +0 -17
- package/_shared/pr-loop/scripts/pr_converge_skill_constants/AGENTS.md +0 -25
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +0 -25
- package/_shared/pr-loop/scripts/tests/AGENTS.md +0 -43
- package/_shared/process-tree/AGENTS.md +0 -40
- package/audit-rubrics/AGENTS.md +0 -42
- package/audit-rubrics/category_rubrics/AGENTS.md +0 -36
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +16 -29
- package/audit-rubrics/prompts/AGENTS.md +0 -36
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
- package/bin/AGENTS.md +0 -118
- package/bin/install.test.mjs +1 -1
- package/commands/AGENTS.md +0 -14
- package/docs/AGENTS.md +0 -31
- package/docs/CODE_RULES.md +3 -3
- package/docs/agent-spawn-protocol.md +1 -1
- package/docs/references/AGENTS.md +0 -16
- package/docs/references/prose-style-enforcement.md +5 -9
- package/hooks/AGENTS.md +0 -27
- package/hooks/advisory/AGENTS.md +0 -15
- package/hooks/blocking/AGENTS.md +0 -122
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +0 -27
- package/hooks/blocking/code_rules_docstrings.py +10 -2138
- package/hooks/blocking/code_rules_enforcer.py +0 -121
- package/hooks/blocking/code_rules_imports_logging.py +1 -236
- package/hooks/blocking/code_rules_shared.py +23 -0
- package/hooks/blocking/code_rules_test_layout.py +8 -8
- package/hooks/blocking/config/AGENTS.md +0 -9
- package/hooks/blocking/config/prose_style_enforcement_constants.py +4 -2
- package/hooks/blocking/config/test_prose_style_enforcement_constants.py +5 -1
- package/hooks/blocking/inventory_intent_records/AGENTS.md +0 -25
- package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +0 -25
- package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +0 -23
- package/hooks/blocking/precommit_code_rules_gate.py +8 -43
- package/hooks/blocking/state_description_blocker.py +1 -7
- package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +0 -29
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
- package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +0 -8
- package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +11 -112
- package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +0 -1
- package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +1 -18
- package/hooks/blocking/test_code_rules_shared.py +12 -0
- package/hooks/blocking/test_precommit_code_rules_gate.py +32 -179
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -1
- package/hooks/blocking/test_state_description_blocker.py +6 -4
- package/hooks/blocking/test_stop_dispatcher.py +5 -29
- package/hooks/git-hooks/AGENTS.md +0 -31
- package/hooks/git-hooks/git_hooks_constants/AGENTS.md +0 -20
- package/hooks/hooks_constants/AGENTS.md +0 -104
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -1
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +3 -0
- package/hooks/hooks_constants/messages.py +0 -2
- package/hooks/hooks_constants/precommit_code_rules_gate_constants.py +3 -17
- package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -2
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -1
- package/hooks/hooks_constants/test_code_rules_enforcer_constants.py +7 -0
- package/hooks/hooks_constants/test_messages.py +5 -3
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -2
- package/hooks/lifecycle/AGENTS.md +0 -17
- package/hooks/observability/AGENTS.md +0 -19
- package/hooks/session/AGENTS.md +0 -37
- package/hooks/validation/AGENTS.md +0 -19
- package/hooks/validators/AGENTS.md +0 -51
- package/hooks/workflow/AGENTS.md +0 -15
- package/output-styles/AGENTS.md +0 -14
- package/package.json +1 -1
- package/rules/AGENTS.md +0 -58
- package/rules/claims-as-quotes.md +0 -10
- package/rules/code-standards.md +6 -6
- package/rules/explore-thoroughly.md +0 -1
- package/rules/failure-blast-radius.md +0 -8
- package/rules/falsify-before-green.md +0 -8
- package/rules/file-global-constants.md +2 -2
- package/rules/filesystem-search.md +1 -1
- package/rules/git-workflow.md +1 -9
- package/rules/hedging-claims.md +2 -6
- package/rules/long-horizon-autonomy.md +1 -1
- package/rules/measurement-denominators.md +0 -9
- package/rules/research-mode.md +0 -6
- package/rules/verify-before-asking.md +0 -5
- package/rules/verify-runtime-state.md +0 -5
- package/scripts/AGENTS.md +0 -55
- package/scripts/codex_compat_materializer.py +0 -12
- package/scripts/dev_env_scripts_constants/AGENTS.md +0 -20
- package/scripts/sync_to_cursor/AGENTS.md +0 -22
- package/scripts/sync_to_cursor/rules.py +0 -10
- package/scripts/tests/AGENTS.md +0 -34
- package/scripts/tests/test_engine.py +0 -1
- package/scripts/tests/test_rules.py +0 -1
- package/scripts/tests/test_sync_to_cursor.py +0 -1
- package/system-prompts/AGENTS.md +0 -24
- package/system-prompts/software-engineer.xml +3 -3
- package/hooks/blocking/code_rules_dead_argparse_argument.py +0 -554
- package/hooks/blocking/code_rules_dead_config_field.py +0 -568
- package/hooks/blocking/code_rules_dead_dataclass_field.py +0 -348
- package/hooks/blocking/code_rules_dead_module_constant.py +0 -757
- package/hooks/blocking/code_rules_dead_split_branch.py +0 -225
- package/hooks/blocking/code_rules_mock_completeness.py +0 -295
- package/hooks/blocking/code_rules_scope_binding.py +0 -151
- package/hooks/blocking/code_rules_unused_imports.py +0 -197
- package/hooks/blocking/hedging_language_blocker.py +0 -221
- package/hooks/blocking/intent_only_ending_blocker.py +0 -148
- package/hooks/blocking/test_code_rules_enforcer_dead_argparse_argument.py +0 -534
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +0 -846
- package/hooks/blocking/test_code_rules_enforcer_dead_dataclass_field.py +0 -507
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +0 -679
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_alias.py +0 -133
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +0 -103
- package/hooks/blocking/test_code_rules_enforcer_dead_split_branch.py +0 -105
- package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +0 -425
- package/hooks/blocking/test_code_rules_enforcer_docstring_cardinal_family.py +0 -176
- package/hooks/blocking/test_code_rules_enforcer_docstring_delegation_summary.py +0 -385
- package/hooks/blocking/test_code_rules_enforcer_docstring_fallback_branch.py +0 -398
- package/hooks/blocking/test_code_rules_enforcer_docstring_field_runmode_outcome.py +0 -129
- package/hooks/blocking/test_code_rules_enforcer_docstring_inline_literal_claim.py +0 -93
- package/hooks/blocking/test_code_rules_enforcer_docstring_length_constant_superlative.py +0 -198
- package/hooks/blocking/test_code_rules_enforcer_docstring_mark_glyph_enumeration.py +0 -262
- package/hooks/blocking/test_code_rules_enforcer_docstring_no_consumer.py +0 -93
- package/hooks/blocking/test_code_rules_enforcer_docstring_no_network.py +0 -115
- package/hooks/blocking/test_code_rules_enforcer_docstring_raises_largezipfile.py +0 -226
- package/hooks/blocking/test_code_rules_enforcer_docstring_returns_plural_cardinality.py +0 -207
- package/hooks/blocking/test_code_rules_enforcer_docstring_step_dispatch.py +0 -262
- package/hooks/blocking/test_code_rules_enforcer_docstring_type_checking_gate.py +0 -164
- package/hooks/blocking/test_code_rules_enforcer_docstring_unguarded_payload.py +0 -188
- package/hooks/blocking/test_code_rules_enforcer_import_block_sort.py +0 -157
- package/hooks/blocking/test_code_rules_enforcer_split_mocks_1.py +0 -303
- package/hooks/blocking/test_code_rules_enforcer_split_mocks_2.py +0 -111
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +0 -656
- package/hooks/blocking/test_hedging_language_blocker.py +0 -261
- package/hooks/blocking/test_intent_only_ending_blocker.py +0 -209
- package/hooks/hooks_constants/dead_argparse_argument_constants.py +0 -28
- package/hooks/hooks_constants/dead_config_field_constants.py +0 -39
- package/hooks/hooks_constants/dead_dataclass_field_constants.py +0 -25
- package/hooks/hooks_constants/dead_module_constant_constants.py +0 -30
- package/hooks/hooks_constants/hedging_uncertainty_constants.py +0 -42
- package/hooks/hooks_constants/test_dispatcher_constants_docstrings.py +0 -44
- package/rules/conservative-action.md +0 -17
- package/rules/context7.md +0 -8
- package/rules/few-words.md +0 -3
- package/rules/parallel-tools.md +0 -23
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
"""Tests for check_docstring_fallback_branch_coverage — O6 fallback-branch drift.
|
|
2
|
-
|
|
3
|
-
A function whose summary scopes a fallback to one condition while the body
|
|
4
|
-
routes to that same fallback call from two or more distinct early-return guards
|
|
5
|
-
hides the second condition from the reader. This is the deterministic slice of
|
|
6
|
-
Category O6 (docstring prose vs implementation drift).
|
|
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_fallback_branch_coverage(content: str, file_path: str) -> list[str]:
|
|
30
|
-
return code_rules_enforcer.check_docstring_fallback_branch_coverage(content, file_path)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def validate_content(content: str, file_path: str, old_content: str) -> list[str]:
|
|
34
|
-
return code_rules_enforcer.validate_content(content, file_path, old_content)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
PRODUCTION_FILE_PATH = "/project/src/human_actions.py"
|
|
38
|
-
TEST_FILE_PATH = "/project/src/test_human_actions.py"
|
|
39
|
-
HOOK_INFRASTRUCTURE_PATH = "/home/user/.claude/hooks/blocking/example.py"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _drifted_scroll_method() -> str:
|
|
43
|
-
return (
|
|
44
|
-
"class HumanActions:\n"
|
|
45
|
-
" async def _scroll_once_toward_target(\n"
|
|
46
|
-
" self, all_container_screen_bounds: object\n"
|
|
47
|
-
" ) -> None:\n"
|
|
48
|
-
' """Drive one scrollbar pass, falling back to the keyboard when'
|
|
49
|
-
' the bar has no geometry."""\n'
|
|
50
|
-
" if all_container_screen_bounds is None:\n"
|
|
51
|
-
" await self._activate_then_press_right_arrow(None)\n"
|
|
52
|
-
" return\n"
|
|
53
|
-
" if random.random() < wheel_scroll_config.keyboard_scroll_fallback_probability:\n"
|
|
54
|
-
" await self._activate_then_press_right_arrow(all_container_screen_bounds)\n"
|
|
55
|
-
" return\n"
|
|
56
|
-
" await self._drive_scrollbar_gesture(all_container_screen_bounds)\n"
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _enumerated_scroll_method() -> str:
|
|
61
|
-
return (
|
|
62
|
-
"class HumanActions:\n"
|
|
63
|
-
" async def _scroll_once_toward_target(\n"
|
|
64
|
-
" self, all_container_screen_bounds: object\n"
|
|
65
|
-
" ) -> None:\n"
|
|
66
|
-
' """Drive one scrollbar pass.\n'
|
|
67
|
-
"\n"
|
|
68
|
-
" Route to the Right-Arrow keyboard burst either when the bar has\n"
|
|
69
|
-
" no geometry or, on a random keyboard_scroll_fallback_probability\n"
|
|
70
|
-
" fraction of passes, when geometry is available.\n"
|
|
71
|
-
' """\n'
|
|
72
|
-
" if all_container_screen_bounds is None:\n"
|
|
73
|
-
" await self._activate_then_press_right_arrow(None)\n"
|
|
74
|
-
" return\n"
|
|
75
|
-
" if random.random() < wheel_scroll_config.keyboard_scroll_fallback_probability:\n"
|
|
76
|
-
" await self._activate_then_press_right_arrow(all_container_screen_bounds)\n"
|
|
77
|
-
" return\n"
|
|
78
|
-
" await self._drive_scrollbar_gesture(all_container_screen_bounds)\n"
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def test_should_flag_two_branches_routing_to_one_scoped_fallback() -> None:
|
|
83
|
-
issues = check_docstring_fallback_branch_coverage(
|
|
84
|
-
_drifted_scroll_method(), PRODUCTION_FILE_PATH
|
|
85
|
-
)
|
|
86
|
-
assert any("_activate_then_press_right_arrow" in each for each in issues), (
|
|
87
|
-
f"Expected the second fallback route to be flagged, got: {issues!r}"
|
|
88
|
-
)
|
|
89
|
-
assert len(issues) == 1
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def test_should_report_the_route_count_in_the_message() -> None:
|
|
93
|
-
issues = check_docstring_fallback_branch_coverage(
|
|
94
|
-
_drifted_scroll_method(), PRODUCTION_FILE_PATH
|
|
95
|
-
)
|
|
96
|
-
assert any("2 distinct branches" in each for each in issues), (
|
|
97
|
-
f"Expected the branch count in the message, got: {issues!r}"
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def test_should_not_flag_when_both_conditions_are_enumerated() -> None:
|
|
102
|
-
issues = check_docstring_fallback_branch_coverage(
|
|
103
|
-
_enumerated_scroll_method(), PRODUCTION_FILE_PATH
|
|
104
|
-
)
|
|
105
|
-
assert issues == [], (
|
|
106
|
-
f"A docstring that enumerates both routes must not be flagged, got: {issues!r}"
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def test_should_not_flag_single_branch_fallback() -> None:
|
|
111
|
-
source = (
|
|
112
|
-
"def render(view: object) -> str:\n"
|
|
113
|
-
' """Render the view, falling back to the empty string when absent."""\n'
|
|
114
|
-
" if view is None:\n"
|
|
115
|
-
" return ''\n"
|
|
116
|
-
" return view.body\n"
|
|
117
|
-
)
|
|
118
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
119
|
-
assert issues == [], f"One fallback route under one named condition is correct, got: {issues!r}"
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def test_should_not_flag_two_branches_to_different_callees() -> None:
|
|
123
|
-
source = (
|
|
124
|
-
"def dispatch(event: object) -> None:\n"
|
|
125
|
-
' """Dispatch the event, falling back to the logger when unroutable."""\n'
|
|
126
|
-
" if event is None:\n"
|
|
127
|
-
" log_warning('empty')\n"
|
|
128
|
-
" return\n"
|
|
129
|
-
" if event.is_stale:\n"
|
|
130
|
-
" drop_event(event)\n"
|
|
131
|
-
" return\n"
|
|
132
|
-
" route_event(event)\n"
|
|
133
|
-
)
|
|
134
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
135
|
-
assert issues == [], (
|
|
136
|
-
f"Distinct callees per branch are not a shared-fallback drift, got: {issues!r}"
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def test_should_not_flag_when_docstring_has_no_scope_phrase() -> None:
|
|
141
|
-
source = (
|
|
142
|
-
"def select(target: object) -> None:\n"
|
|
143
|
-
' """Pick the first matching candidate from the registry."""\n'
|
|
144
|
-
" if target is None:\n"
|
|
145
|
-
" await _press(None)\n"
|
|
146
|
-
" return\n"
|
|
147
|
-
" if target.is_idle:\n"
|
|
148
|
-
" await _press(target)\n"
|
|
149
|
-
" return\n"
|
|
150
|
-
" await _drive(target)\n"
|
|
151
|
-
)
|
|
152
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
153
|
-
assert issues == [], (
|
|
154
|
-
f"No exclusive-scope phrase means no fallback claim to check, got: {issues!r}"
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def test_should_not_flag_when_scope_phrase_is_a_substring_of_another_word() -> None:
|
|
159
|
-
source = (
|
|
160
|
-
"def refresh(cache: object) -> None:\n"
|
|
161
|
-
' """Rebuild the cache; commonly when idle it reuses the warm copy."""\n'
|
|
162
|
-
" if cache is None:\n"
|
|
163
|
-
" rebuild_cache(None)\n"
|
|
164
|
-
" return\n"
|
|
165
|
-
" if cache.is_cold:\n"
|
|
166
|
-
" rebuild_cache(cache)\n"
|
|
167
|
-
" return\n"
|
|
168
|
-
" serve_cache(cache)\n"
|
|
169
|
-
)
|
|
170
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
171
|
-
assert issues == [], (
|
|
172
|
-
"'commonly when' must not match the 'only when' scope phrase as a bare "
|
|
173
|
-
f"substring, got: {issues!r}"
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def test_should_still_flag_word_boundary_scope_phrase() -> None:
|
|
178
|
-
source = (
|
|
179
|
-
"def refresh(cache: object) -> None:\n"
|
|
180
|
-
' """Rebuild the cache only when it is invalid."""\n'
|
|
181
|
-
" if cache is None:\n"
|
|
182
|
-
" rebuild_cache(None)\n"
|
|
183
|
-
" return\n"
|
|
184
|
-
" if cache.is_cold:\n"
|
|
185
|
-
" rebuild_cache(cache)\n"
|
|
186
|
-
" return\n"
|
|
187
|
-
" serve_cache(cache)\n"
|
|
188
|
-
)
|
|
189
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
190
|
-
assert any("rebuild_cache" in each for each in issues), (
|
|
191
|
-
f"A genuine 'only when' scope phrase must still be flagged, got: {issues!r}"
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def test_should_not_flag_when_summary_enumerates_both_conditions_inline() -> None:
|
|
196
|
-
source = (
|
|
197
|
-
"def scroll(bar: object) -> None:\n"
|
|
198
|
-
' """Drive a scrollbar pass, falling back to the keyboard when the bar'
|
|
199
|
-
' lacks geometry or on a random fraction of passes."""\n'
|
|
200
|
-
" if bar is None:\n"
|
|
201
|
-
" _keyboard(None)\n"
|
|
202
|
-
" return\n"
|
|
203
|
-
" if bar.is_random:\n"
|
|
204
|
-
" _keyboard(bar)\n"
|
|
205
|
-
" return\n"
|
|
206
|
-
" _drive(bar)\n"
|
|
207
|
-
)
|
|
208
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
209
|
-
assert issues == [], (
|
|
210
|
-
"A summary that enumerates both fallback conditions inline with 'or' is not "
|
|
211
|
-
f"a single-condition scope and must not be flagged, got: {issues!r}"
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def test_should_still_flag_single_condition_fallback_with_two_routes() -> None:
|
|
216
|
-
source = (
|
|
217
|
-
"def scroll(bar: object) -> None:\n"
|
|
218
|
-
' """Drive a scrollbar pass, falling back to the keyboard when the bar'
|
|
219
|
-
' lacks geometry."""\n'
|
|
220
|
-
" if bar is None:\n"
|
|
221
|
-
" _keyboard(None)\n"
|
|
222
|
-
" return\n"
|
|
223
|
-
" if bar.is_random:\n"
|
|
224
|
-
" _keyboard(bar)\n"
|
|
225
|
-
" return\n"
|
|
226
|
-
" _drive(bar)\n"
|
|
227
|
-
)
|
|
228
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
229
|
-
assert any("_keyboard" in each for each in issues), (
|
|
230
|
-
"A summary scoping the fallback to one named condition while two routes reach "
|
|
231
|
-
f"it must still be flagged, got: {issues!r}"
|
|
232
|
-
)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
def test_should_not_flag_when_scope_phrase_is_a_left_anchored_prefix() -> None:
|
|
236
|
-
source = (
|
|
237
|
-
"def forward(packet: object) -> None:\n"
|
|
238
|
-
' """Forward the packet; falls back toward the default sink when both'
|
|
239
|
-
' checks miss."""\n'
|
|
240
|
-
" if packet is None:\n"
|
|
241
|
-
" send_to_sink(None)\n"
|
|
242
|
-
" return\n"
|
|
243
|
-
" if packet.is_stale:\n"
|
|
244
|
-
" send_to_sink(packet)\n"
|
|
245
|
-
" return\n"
|
|
246
|
-
" deliver(packet)\n"
|
|
247
|
-
)
|
|
248
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
249
|
-
assert issues == [], (
|
|
250
|
-
"'falls back toward' must not match the 'falls back to' scope phrase as a "
|
|
251
|
-
f"left-anchored prefix, got: {issues!r}"
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
def test_should_not_flag_only_whenever_left_anchored_prefix() -> None:
|
|
256
|
-
source = (
|
|
257
|
-
"def refresh(cache: object) -> None:\n"
|
|
258
|
-
' """Rebuild the cache only whenever it is invalid or stale."""\n'
|
|
259
|
-
" if cache is None:\n"
|
|
260
|
-
" rebuild_cache(None)\n"
|
|
261
|
-
" return\n"
|
|
262
|
-
" if cache.is_cold:\n"
|
|
263
|
-
" rebuild_cache(cache)\n"
|
|
264
|
-
" return\n"
|
|
265
|
-
" serve_cache(cache)\n"
|
|
266
|
-
)
|
|
267
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
268
|
-
assert issues == [], (
|
|
269
|
-
"'only whenever' must not match the 'only when' scope phrase as a "
|
|
270
|
-
f"left-anchored prefix, got: {issues!r}"
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def test_should_not_flag_two_branches_to_same_method_on_distinct_indexed_receivers() -> None:
|
|
275
|
-
source = (
|
|
276
|
-
"class Pool:\n"
|
|
277
|
-
" def shutdown(self, signal: object) -> None:\n"
|
|
278
|
-
' """Close resources only when a shutdown signal arrives."""\n'
|
|
279
|
-
" if signal is None:\n"
|
|
280
|
-
" self.pool[0].close(signal)\n"
|
|
281
|
-
" return\n"
|
|
282
|
-
" if signal.is_secondary:\n"
|
|
283
|
-
" self.pool[1].close(signal)\n"
|
|
284
|
-
" return\n"
|
|
285
|
-
" self.pool[2].drain(signal)\n"
|
|
286
|
-
)
|
|
287
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
288
|
-
assert issues == [], (
|
|
289
|
-
"Distinct indexed receivers calling the same method name are different "
|
|
290
|
-
f"fallbacks, got: {issues!r}"
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
def test_should_not_flag_two_branches_to_same_named_method_on_distinct_receivers() -> None:
|
|
295
|
-
source = (
|
|
296
|
-
"class Closer:\n"
|
|
297
|
-
" def shutdown(self, signal: object) -> None:\n"
|
|
298
|
-
' """Close resources only when a shutdown signal arrives."""\n'
|
|
299
|
-
" if signal is None:\n"
|
|
300
|
-
" self.primary.close(signal)\n"
|
|
301
|
-
" return\n"
|
|
302
|
-
" if signal.is_secondary:\n"
|
|
303
|
-
" self.secondary.close(signal)\n"
|
|
304
|
-
" return\n"
|
|
305
|
-
" self.tertiary.close(signal)\n"
|
|
306
|
-
)
|
|
307
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
308
|
-
assert issues == [], (
|
|
309
|
-
"Distinct receivers calling the same method name are different fallbacks, "
|
|
310
|
-
f"got: {issues!r}"
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
def test_should_flag_two_branches_to_same_method_on_one_receiver() -> None:
|
|
315
|
-
source = (
|
|
316
|
-
"class Closer:\n"
|
|
317
|
-
" def shutdown(self, signal: object) -> None:\n"
|
|
318
|
-
' """Close resources only when a shutdown signal arrives."""\n'
|
|
319
|
-
" if signal is None:\n"
|
|
320
|
-
" self.primary.close(signal)\n"
|
|
321
|
-
" return\n"
|
|
322
|
-
" if signal.is_secondary:\n"
|
|
323
|
-
" self.primary.close(signal)\n"
|
|
324
|
-
" return\n"
|
|
325
|
-
" self.primary.drain(signal)\n"
|
|
326
|
-
)
|
|
327
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
328
|
-
assert any("self.primary.close" in each for each in issues), (
|
|
329
|
-
f"Two routes to the same receiver.method must be flagged, got: {issues!r}"
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
def test_should_flag_multi_statement_guard_with_one_call_before_return() -> None:
|
|
334
|
-
source = (
|
|
335
|
-
"def select(target: object) -> None:\n"
|
|
336
|
-
' """Pick a candidate, falling back to the press action when idle."""\n'
|
|
337
|
-
" if target is None:\n"
|
|
338
|
-
" attempt = 1\n"
|
|
339
|
-
" _press(None)\n"
|
|
340
|
-
" return\n"
|
|
341
|
-
" if target.is_idle:\n"
|
|
342
|
-
" attempt = 1\n"
|
|
343
|
-
" _press(target)\n"
|
|
344
|
-
" return\n"
|
|
345
|
-
" _drive(target)\n"
|
|
346
|
-
)
|
|
347
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
348
|
-
assert any("_press" in each for each in issues), (
|
|
349
|
-
"A guard with a non-call statement before its single call still routes "
|
|
350
|
-
f"to that call, got: {issues!r}"
|
|
351
|
-
)
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
def test_should_not_flag_guard_with_a_second_call_expression() -> None:
|
|
355
|
-
source = (
|
|
356
|
-
"def select(target: object) -> None:\n"
|
|
357
|
-
' """Pick a candidate, falling back to the press action when idle."""\n'
|
|
358
|
-
" if target is None:\n"
|
|
359
|
-
" _press(None)\n"
|
|
360
|
-
" _press(None)\n"
|
|
361
|
-
" return\n"
|
|
362
|
-
" if target.is_idle:\n"
|
|
363
|
-
" _press(target)\n"
|
|
364
|
-
" _press(target)\n"
|
|
365
|
-
" return\n"
|
|
366
|
-
" _drive(target)\n"
|
|
367
|
-
)
|
|
368
|
-
issues = check_docstring_fallback_branch_coverage(source, PRODUCTION_FILE_PATH)
|
|
369
|
-
assert issues == [], (
|
|
370
|
-
f"A second call expression disqualifies the block as a route, got: {issues!r}"
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
def test_should_skip_test_file() -> None:
|
|
375
|
-
issues = check_docstring_fallback_branch_coverage(_drifted_scroll_method(), TEST_FILE_PATH)
|
|
376
|
-
assert issues == [], f"Test files exempt, got: {issues!r}"
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
def test_should_skip_hook_infrastructure() -> None:
|
|
380
|
-
issues = check_docstring_fallback_branch_coverage(
|
|
381
|
-
_drifted_scroll_method(), HOOK_INFRASTRUCTURE_PATH
|
|
382
|
-
)
|
|
383
|
-
assert issues == [], f"Hook infrastructure exempt, got: {issues!r}"
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
def test_should_handle_syntax_error_gracefully() -> None:
|
|
387
|
-
issues = check_docstring_fallback_branch_coverage("def fetch(\n", PRODUCTION_FILE_PATH)
|
|
388
|
-
assert issues == [], f"Syntax error must yield no issues, got: {issues!r}"
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
def test_validate_content_surfaces_fallback_branch_drift() -> None:
|
|
392
|
-
issues = validate_content(_drifted_scroll_method(), PRODUCTION_FILE_PATH, old_content="")
|
|
393
|
-
matching_issues = [
|
|
394
|
-
each for each in issues if "_activate_then_press_right_arrow" in each and "O6" in each
|
|
395
|
-
]
|
|
396
|
-
assert matching_issues, (
|
|
397
|
-
f"Expected validate_content to surface the O6 fallback-branch drift, got: {issues!r}"
|
|
398
|
-
)
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"""Tests for check_docstring_field_runmode_outcome — O6 run-mode-vs-per-record drift.
|
|
2
|
-
|
|
3
|
-
A dataclass field whose name marks a run-mode flag (``is_dry_run``) is documented
|
|
4
|
-
in the class Attributes block with per-record write-outcome prose ("True when no
|
|
5
|
-
STP was written"), while the value is set the same way for every record from the
|
|
6
|
-
run mode (``is_dry_run=not is_execute``). An already-OK record in an execute run
|
|
7
|
-
writes no file yet still stores ``False``, so the per-record prose misleads every
|
|
8
|
-
reader. This is the deterministic single-file slice of Category O6 drift.
|
|
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_docstring_field_runmode_outcome(content: str, file_path: str) -> list[str]:
|
|
32
|
-
return code_rules_enforcer.check_docstring_field_runmode_outcome(content, file_path)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
PRODUCTION_FILE_PATH = "/project/src/jsonl_writer.py"
|
|
36
|
-
TEST_FILE_PATH = "/project/src/test_jsonl_writer.py"
|
|
37
|
-
HOOK_INFRASTRUCTURE_PATH = "/home/user/.claude/hooks/blocking/example.py"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _drifted_record() -> str:
|
|
41
|
-
return (
|
|
42
|
-
"@dataclass(frozen=True)\n"
|
|
43
|
-
"class PerThemeJsonlRecord:\n"
|
|
44
|
-
' """Exact schema for the per-theme JSONL line.\n'
|
|
45
|
-
"\n"
|
|
46
|
-
" Attributes:\n"
|
|
47
|
-
" theme_name: Theme directory basename.\n"
|
|
48
|
-
" is_dry_run: True when no STP was written.\n"
|
|
49
|
-
' """\n'
|
|
50
|
-
"\n"
|
|
51
|
-
" theme_name: str\n"
|
|
52
|
-
" is_dry_run: bool\n"
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def _run_mode_record() -> str:
|
|
57
|
-
return (
|
|
58
|
-
"@dataclass(frozen=True)\n"
|
|
59
|
-
"class PerThemeJsonlRecord:\n"
|
|
60
|
-
' """Exact schema for the per-theme JSONL line.\n'
|
|
61
|
-
"\n"
|
|
62
|
-
" Attributes:\n"
|
|
63
|
-
" theme_name: Theme directory basename.\n"
|
|
64
|
-
" is_dry_run: True for a dry run; False for an execute run.\n"
|
|
65
|
-
' """\n'
|
|
66
|
-
"\n"
|
|
67
|
-
" theme_name: str\n"
|
|
68
|
-
" is_dry_run: bool\n"
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_flags_run_mode_field_documented_as_per_record_write_outcome() -> None:
|
|
73
|
-
issues = check_docstring_field_runmode_outcome(_drifted_record(), PRODUCTION_FILE_PATH)
|
|
74
|
-
assert len(issues) == 1
|
|
75
|
-
assert "is_dry_run" in issues[0]
|
|
76
|
-
assert "run-mode" in issues[0]
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def test_run_mode_phrasing_is_left_alone() -> None:
|
|
80
|
-
issues = check_docstring_field_runmode_outcome(_run_mode_record(), PRODUCTION_FILE_PATH)
|
|
81
|
-
assert issues == []
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def test_multiline_description_continuation_still_flags() -> None:
|
|
85
|
-
source = (
|
|
86
|
-
"@dataclass\n"
|
|
87
|
-
"class Record:\n"
|
|
88
|
-
' """Schema.\n'
|
|
89
|
-
"\n"
|
|
90
|
-
" Attributes:\n"
|
|
91
|
-
" is_dry_run: True for the record when\n"
|
|
92
|
-
" no STP was written.\n"
|
|
93
|
-
' """\n'
|
|
94
|
-
"\n"
|
|
95
|
-
" is_dry_run: bool\n"
|
|
96
|
-
)
|
|
97
|
-
issues = check_docstring_field_runmode_outcome(source, PRODUCTION_FILE_PATH)
|
|
98
|
-
assert len(issues) == 1
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def test_non_run_mode_field_with_write_outcome_is_left_alone() -> None:
|
|
102
|
-
source = (
|
|
103
|
-
"@dataclass\n"
|
|
104
|
-
"class Record:\n"
|
|
105
|
-
' """Schema.\n'
|
|
106
|
-
"\n"
|
|
107
|
-
" Attributes:\n"
|
|
108
|
-
" stp_path: Path the STP was written to disk.\n"
|
|
109
|
-
' """\n'
|
|
110
|
-
"\n"
|
|
111
|
-
" stp_path: str\n"
|
|
112
|
-
)
|
|
113
|
-
issues = check_docstring_field_runmode_outcome(source, PRODUCTION_FILE_PATH)
|
|
114
|
-
assert issues == []
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def test_test_file_is_exempt() -> None:
|
|
118
|
-
issues = check_docstring_field_runmode_outcome(_drifted_record(), TEST_FILE_PATH)
|
|
119
|
-
assert issues == []
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def test_hook_infrastructure_is_exempt() -> None:
|
|
123
|
-
issues = check_docstring_field_runmode_outcome(_drifted_record(), HOOK_INFRASTRUCTURE_PATH)
|
|
124
|
-
assert issues == []
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def test_syntax_error_returns_no_issues() -> None:
|
|
128
|
-
issues = check_docstring_field_runmode_outcome("class Record(\n", PRODUCTION_FILE_PATH)
|
|
129
|
-
assert issues == []
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"""Tests for check_docstring_no_inline_literal_claim — Category O6 completeness drift.
|
|
2
|
-
|
|
3
|
-
A constants-module docstring asserting "no literals appear inline in the
|
|
4
|
-
dispatcher" makes an unverifiable completeness claim about a companion file. The
|
|
5
|
-
claim drifts the moment a literal lands inline in that companion — a deny or
|
|
6
|
-
block reason left inline contradicts the docstring even though the file under
|
|
7
|
-
edit never changed. This is the deterministic slice of Category O6 (docstring
|
|
8
|
-
prose vs implementation drift) and a no-transitional-language violation in its
|
|
9
|
-
own right.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import importlib.util
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
from types import ModuleType
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _load_enforcer_module() -> ModuleType:
|
|
20
|
-
module_path = Path(__file__).parent / "code_rules_enforcer.py"
|
|
21
|
-
spec = importlib.util.spec_from_file_location("code_rules_enforcer", module_path)
|
|
22
|
-
assert spec is not None
|
|
23
|
-
assert spec.loader is not None
|
|
24
|
-
module = importlib.util.module_from_spec(spec)
|
|
25
|
-
spec.loader.exec_module(module)
|
|
26
|
-
return module
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
code_rules_enforcer = _load_enforcer_module()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def check_docstring_no_inline_literal_claim(content: str, file_path: str) -> list[str]:
|
|
33
|
-
return code_rules_enforcer.check_docstring_no_inline_literal_claim(content, file_path)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
CONSTANTS_FILE_PATH = "/project/hooks/hooks_constants/example_dispatcher_constants.py"
|
|
37
|
-
TEST_FILE_PATH = "/project/hooks/hooks_constants/test_example_dispatcher_constants.py"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def test_flags_no_literals_appear_inline_in_the_dispatcher_claim() -> None:
|
|
41
|
-
content = (
|
|
42
|
-
'"""Constants for the dispatcher.\n'
|
|
43
|
-
"\n"
|
|
44
|
-
"The dispatcher imports these; no literals appear inline in the dispatcher\n"
|
|
45
|
-
"script.\n"
|
|
46
|
-
'"""\n'
|
|
47
|
-
"\n"
|
|
48
|
-
'DENY_DECISION = "deny"\n'
|
|
49
|
-
)
|
|
50
|
-
issues = check_docstring_no_inline_literal_claim(content, CONSTANTS_FILE_PATH)
|
|
51
|
-
assert len(issues) == 1
|
|
52
|
-
assert "no literals appear inline" in issues[0]
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def test_flags_no_literals_appear_inline_short_form() -> None:
|
|
56
|
-
content = (
|
|
57
|
-
'"""Constants module. No literals appear inline in the script."""\n'
|
|
58
|
-
"\n"
|
|
59
|
-
'BLOCK_DECISION = "block"\n'
|
|
60
|
-
)
|
|
61
|
-
assert len(check_docstring_no_inline_literal_claim(content, CONSTANTS_FILE_PATH)) == 1
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def test_passes_when_docstring_states_what_is_centralized() -> None:
|
|
65
|
-
content = (
|
|
66
|
-
'"""Constants for the dispatcher.\n'
|
|
67
|
-
"\n"
|
|
68
|
-
"Holds the deny decision string and the crash deny reason. The dispatcher\n"
|
|
69
|
-
"imports each of these by name.\n"
|
|
70
|
-
'"""\n'
|
|
71
|
-
"\n"
|
|
72
|
-
'DENY_DECISION = "deny"\n'
|
|
73
|
-
)
|
|
74
|
-
assert check_docstring_no_inline_literal_claim(content, CONSTANTS_FILE_PATH) == []
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def test_test_files_are_exempt() -> None:
|
|
78
|
-
content = (
|
|
79
|
-
'"""Constants module. No literals appear inline in the dispatcher script."""\n'
|
|
80
|
-
"\n"
|
|
81
|
-
'DENY_DECISION = "deny"\n'
|
|
82
|
-
)
|
|
83
|
-
assert check_docstring_no_inline_literal_claim(content, TEST_FILE_PATH) == []
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def test_hook_infrastructure_is_in_scope() -> None:
|
|
87
|
-
hook_constants_path = "/home/user/.claude/hooks/hooks_constants/foo_constants.py"
|
|
88
|
-
content = (
|
|
89
|
-
'"""Constants module. No literals appear inline in the dispatcher script."""\n'
|
|
90
|
-
"\n"
|
|
91
|
-
'DENY_DECISION = "deny"\n'
|
|
92
|
-
)
|
|
93
|
-
assert len(check_docstring_no_inline_literal_claim(content, hook_constants_path)) == 1
|