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.
Files changed (212) hide show
  1. package/.agents/agents/AGENTS.md +0 -31
  2. package/.agents/agents/clean-coder.md +1 -1
  3. package/.agents/agents/test_agent_frontmatter.py +3 -1
  4. package/.agents/skills/AGENTS.md +0 -70
  5. package/.agents/skills/_shared/AGENTS.md +0 -44
  6. package/.agents/skills/_shared/advisor/AGENTS.md +0 -8
  7. package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -57
  8. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -8
  9. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -34
  10. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -23
  11. package/.agents/skills/autoconverge/AGENTS.md +0 -35
  12. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -15
  13. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -25
  14. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -15
  15. package/.agents/skills/everything-search/AGENTS.md +0 -16
  16. package/.agents/skills/fresh-branch/AGENTS.md +0 -13
  17. package/.agents/skills/grok-spawn/AGENTS.md +0 -27
  18. package/.agents/skills/orchestrator/.claude/CLAUDE.md +1 -0
  19. package/.agents/skills/orchestrator/AGENTS.md +1 -0
  20. package/.agents/skills/orchestrator/SKILL.md +44 -53
  21. package/.agents/skills/orchestrator/reference/.claude/CLAUDE.md +1 -0
  22. package/.agents/skills/orchestrator/reference/AGENTS.md +1 -0
  23. package/.agents/skills/orchestrator/reference/consult-the-orchestrator.md +70 -0
  24. package/.agents/skills/orchestrator/reference/executor-consult-block.md +62 -0
  25. package/.agents/skills/orchestrator/reference/host-detect.md +15 -0
  26. package/.agents/skills/orchestrator/test_orchestrator_skill_contract.py +62 -0
  27. package/.agents/skills/orchestrator-refresh/SKILL.md +16 -34
  28. package/.agents/skills/rebase/AGENTS.md +0 -31
  29. package/.agents/skills/session-log/AGENTS.md +0 -31
  30. package/.agents/skills/session-tidy/AGENTS.md +0 -35
  31. package/.agents/skills/skill-builder/AGENTS.md +0 -48
  32. package/.agents/skills/skill-builder/references/AGENTS.md +0 -24
  33. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -13
  34. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -18
  35. package/.agents/skills/task-build/AGENTS.md +0 -28
  36. package/.agents/skills/update/AGENTS.md +0 -37
  37. package/.agents/skills-archived/AGENTS.md +0 -44
  38. package/.agents/skills-archived/anthropic-plan/AGENTS.md +0 -33
  39. package/.agents/skills-archived/anthropic-plan/scripts/AGENTS.md +0 -10
  40. package/.agents/skills-archived/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +0 -15
  41. package/.agents/skills-archived/anthropic-plan/templates/AGENTS.md +0 -12
  42. package/.agents/skills-archived/anthropic-plan/workflow/AGENTS.md +0 -13
  43. package/.agents/skills-archived/auditing-claude-config/AGENTS.md +0 -20
  44. package/.agents/skills-archived/bugteam/AGENTS.md +0 -29
  45. package/.agents/skills-archived/bugteam/reference/AGENTS.md +0 -19
  46. package/.agents/skills-archived/bugteam/reference/obstacles/AGENTS.md +0 -23
  47. package/.agents/skills-archived/bugteam/scripts/AGENTS.md +0 -29
  48. package/.agents/skills-archived/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +0 -17
  49. package/.agents/skills-archived/codex-review/AGENTS.md +0 -45
  50. package/.agents/skills-archived/codex-review/reference/AGENTS.md +0 -14
  51. package/.agents/skills-archived/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +0 -17
  52. package/.agents/skills-archived/codex-review/test_skill_scaffold.py +13 -3
  53. package/.agents/skills-archived/copilot-review/AGENTS.md +0 -17
  54. package/.agents/skills-archived/pr-converge/AGENTS.md +0 -31
  55. package/.agents/skills-archived/pr-converge/pr_converge_skill_constants/AGENTS.md +0 -25
  56. package/.agents/skills-archived/pr-converge/reference/AGENTS.md +0 -27
  57. package/.agents/skills-archived/pr-converge/reference/obstacles/AGENTS.md +0 -22
  58. package/.agents/skills-archived/pr-converge/scripts/AGENTS.md +0 -45
  59. package/.agents/skills-archived/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +0 -17
  60. package/.agents/skills-archived/pr-converge/workflows/AGENTS.md +0 -15
  61. package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
  62. package/.agents/skills-archived/recall/AGENTS.md +0 -29
  63. package/.agents/skills-archived/remember/AGENTS.md +0 -30
  64. package/AGENTS.md +0 -112
  65. package/_shared/AGENTS.md +0 -16
  66. package/_shared/advisor/AGENTS.md +0 -21
  67. package/_shared/advisor/advisor-protocol.md +4 -1
  68. package/_shared/advisor/reference/consult-format.md +4 -2
  69. package/_shared/pr-loop/AGENTS.md +0 -27
  70. package/_shared/pr-loop/scripts/AGENTS.md +0 -49
  71. package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +0 -41
  72. package/_shared/pr-loop/scripts/codex_review_scripts_constants/AGENTS.md +0 -17
  73. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/AGENTS.md +0 -17
  74. package/_shared/pr-loop/scripts/pr_converge_skill_constants/AGENTS.md +0 -25
  75. package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +0 -25
  76. package/_shared/pr-loop/scripts/tests/AGENTS.md +0 -43
  77. package/_shared/process-tree/AGENTS.md +0 -40
  78. package/audit-rubrics/AGENTS.md +0 -42
  79. package/audit-rubrics/category_rubrics/AGENTS.md +0 -36
  80. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +16 -29
  81. package/audit-rubrics/prompts/AGENTS.md +0 -36
  82. package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
  83. package/bin/AGENTS.md +0 -118
  84. package/bin/install.test.mjs +1 -1
  85. package/commands/AGENTS.md +0 -14
  86. package/docs/AGENTS.md +0 -31
  87. package/docs/CODE_RULES.md +3 -3
  88. package/docs/agent-spawn-protocol.md +1 -1
  89. package/docs/references/AGENTS.md +0 -16
  90. package/docs/references/prose-style-enforcement.md +5 -9
  91. package/hooks/AGENTS.md +0 -27
  92. package/hooks/advisory/AGENTS.md +0 -15
  93. package/hooks/blocking/AGENTS.md +0 -122
  94. package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +0 -27
  95. package/hooks/blocking/code_rules_docstrings.py +10 -2138
  96. package/hooks/blocking/code_rules_enforcer.py +0 -121
  97. package/hooks/blocking/code_rules_imports_logging.py +1 -236
  98. package/hooks/blocking/code_rules_shared.py +23 -0
  99. package/hooks/blocking/code_rules_test_layout.py +8 -8
  100. package/hooks/blocking/config/AGENTS.md +0 -9
  101. package/hooks/blocking/config/prose_style_enforcement_constants.py +4 -2
  102. package/hooks/blocking/config/test_prose_style_enforcement_constants.py +5 -1
  103. package/hooks/blocking/inventory_intent_records/AGENTS.md +0 -25
  104. package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +0 -25
  105. package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +0 -23
  106. package/hooks/blocking/precommit_code_rules_gate.py +8 -43
  107. package/hooks/blocking/state_description_blocker.py +1 -7
  108. package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +0 -29
  109. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
  110. package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +0 -8
  111. package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +11 -112
  112. package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +0 -1
  113. package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +1 -18
  114. package/hooks/blocking/test_code_rules_shared.py +12 -0
  115. package/hooks/blocking/test_precommit_code_rules_gate.py +32 -179
  116. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -1
  117. package/hooks/blocking/test_state_description_blocker.py +6 -4
  118. package/hooks/blocking/test_stop_dispatcher.py +5 -29
  119. package/hooks/git-hooks/AGENTS.md +0 -31
  120. package/hooks/git-hooks/git_hooks_constants/AGENTS.md +0 -20
  121. package/hooks/hooks_constants/AGENTS.md +0 -104
  122. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -1
  123. package/hooks/hooks_constants/code_rules_enforcer_constants.py +3 -0
  124. package/hooks/hooks_constants/messages.py +0 -2
  125. package/hooks/hooks_constants/precommit_code_rules_gate_constants.py +3 -17
  126. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -2
  127. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -1
  128. package/hooks/hooks_constants/test_code_rules_enforcer_constants.py +7 -0
  129. package/hooks/hooks_constants/test_messages.py +5 -3
  130. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -2
  131. package/hooks/lifecycle/AGENTS.md +0 -17
  132. package/hooks/observability/AGENTS.md +0 -19
  133. package/hooks/session/AGENTS.md +0 -37
  134. package/hooks/validation/AGENTS.md +0 -19
  135. package/hooks/validators/AGENTS.md +0 -51
  136. package/hooks/workflow/AGENTS.md +0 -15
  137. package/output-styles/AGENTS.md +0 -14
  138. package/package.json +1 -1
  139. package/rules/AGENTS.md +0 -58
  140. package/rules/claims-as-quotes.md +0 -10
  141. package/rules/code-standards.md +6 -6
  142. package/rules/explore-thoroughly.md +0 -1
  143. package/rules/failure-blast-radius.md +0 -8
  144. package/rules/falsify-before-green.md +0 -8
  145. package/rules/file-global-constants.md +2 -2
  146. package/rules/filesystem-search.md +1 -1
  147. package/rules/git-workflow.md +1 -9
  148. package/rules/hedging-claims.md +2 -6
  149. package/rules/long-horizon-autonomy.md +1 -1
  150. package/rules/measurement-denominators.md +0 -9
  151. package/rules/research-mode.md +0 -6
  152. package/rules/verify-before-asking.md +0 -5
  153. package/rules/verify-runtime-state.md +0 -5
  154. package/scripts/AGENTS.md +0 -55
  155. package/scripts/codex_compat_materializer.py +0 -12
  156. package/scripts/dev_env_scripts_constants/AGENTS.md +0 -20
  157. package/scripts/sync_to_cursor/AGENTS.md +0 -22
  158. package/scripts/sync_to_cursor/rules.py +0 -10
  159. package/scripts/tests/AGENTS.md +0 -34
  160. package/scripts/tests/test_engine.py +0 -1
  161. package/scripts/tests/test_rules.py +0 -1
  162. package/scripts/tests/test_sync_to_cursor.py +0 -1
  163. package/system-prompts/AGENTS.md +0 -24
  164. package/system-prompts/software-engineer.xml +3 -3
  165. package/hooks/blocking/code_rules_dead_argparse_argument.py +0 -554
  166. package/hooks/blocking/code_rules_dead_config_field.py +0 -568
  167. package/hooks/blocking/code_rules_dead_dataclass_field.py +0 -348
  168. package/hooks/blocking/code_rules_dead_module_constant.py +0 -757
  169. package/hooks/blocking/code_rules_dead_split_branch.py +0 -225
  170. package/hooks/blocking/code_rules_mock_completeness.py +0 -295
  171. package/hooks/blocking/code_rules_scope_binding.py +0 -151
  172. package/hooks/blocking/code_rules_unused_imports.py +0 -197
  173. package/hooks/blocking/hedging_language_blocker.py +0 -221
  174. package/hooks/blocking/intent_only_ending_blocker.py +0 -148
  175. package/hooks/blocking/test_code_rules_enforcer_dead_argparse_argument.py +0 -534
  176. package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +0 -846
  177. package/hooks/blocking/test_code_rules_enforcer_dead_dataclass_field.py +0 -507
  178. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +0 -679
  179. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_alias.py +0 -133
  180. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +0 -103
  181. package/hooks/blocking/test_code_rules_enforcer_dead_split_branch.py +0 -105
  182. package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +0 -425
  183. package/hooks/blocking/test_code_rules_enforcer_docstring_cardinal_family.py +0 -176
  184. package/hooks/blocking/test_code_rules_enforcer_docstring_delegation_summary.py +0 -385
  185. package/hooks/blocking/test_code_rules_enforcer_docstring_fallback_branch.py +0 -398
  186. package/hooks/blocking/test_code_rules_enforcer_docstring_field_runmode_outcome.py +0 -129
  187. package/hooks/blocking/test_code_rules_enforcer_docstring_inline_literal_claim.py +0 -93
  188. package/hooks/blocking/test_code_rules_enforcer_docstring_length_constant_superlative.py +0 -198
  189. package/hooks/blocking/test_code_rules_enforcer_docstring_mark_glyph_enumeration.py +0 -262
  190. package/hooks/blocking/test_code_rules_enforcer_docstring_no_consumer.py +0 -93
  191. package/hooks/blocking/test_code_rules_enforcer_docstring_no_network.py +0 -115
  192. package/hooks/blocking/test_code_rules_enforcer_docstring_raises_largezipfile.py +0 -226
  193. package/hooks/blocking/test_code_rules_enforcer_docstring_returns_plural_cardinality.py +0 -207
  194. package/hooks/blocking/test_code_rules_enforcer_docstring_step_dispatch.py +0 -262
  195. package/hooks/blocking/test_code_rules_enforcer_docstring_type_checking_gate.py +0 -164
  196. package/hooks/blocking/test_code_rules_enforcer_docstring_unguarded_payload.py +0 -188
  197. package/hooks/blocking/test_code_rules_enforcer_import_block_sort.py +0 -157
  198. package/hooks/blocking/test_code_rules_enforcer_split_mocks_1.py +0 -303
  199. package/hooks/blocking/test_code_rules_enforcer_split_mocks_2.py +0 -111
  200. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +0 -656
  201. package/hooks/blocking/test_hedging_language_blocker.py +0 -261
  202. package/hooks/blocking/test_intent_only_ending_blocker.py +0 -209
  203. package/hooks/hooks_constants/dead_argparse_argument_constants.py +0 -28
  204. package/hooks/hooks_constants/dead_config_field_constants.py +0 -39
  205. package/hooks/hooks_constants/dead_dataclass_field_constants.py +0 -25
  206. package/hooks/hooks_constants/dead_module_constant_constants.py +0 -30
  207. package/hooks/hooks_constants/hedging_uncertainty_constants.py +0 -42
  208. package/hooks/hooks_constants/test_dispatcher_constants_docstrings.py +0 -44
  209. package/rules/conservative-action.md +0 -17
  210. package/rules/context7.md +0 -8
  211. package/rules/few-words.md +0 -3
  212. package/rules/parallel-tools.md +0 -23
@@ -1,679 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import importlib.util
4
- import os
5
- import shutil
6
- import stat
7
- import tempfile
8
- from collections.abc import Callable, Iterator
9
- from pathlib import Path
10
-
11
- import pytest
12
-
13
- ENFORCER_PATH = Path(__file__).resolve().parent / "code_rules_enforcer.py"
14
- specification = importlib.util.spec_from_file_location("code_rules_enforcer", ENFORCER_PATH)
15
- assert specification is not None and specification.loader is not None
16
- code_rules_enforcer = importlib.util.module_from_spec(specification)
17
- specification.loader.exec_module(code_rules_enforcer)
18
-
19
- CONSTANTS_BODY = 'MEDIUM_TERMINAL = "terminal"\nMEDIUM_CODE = "code"\nMEDIUM_TEXT = "text"\n'
20
-
21
-
22
- def _strip_read_only_and_retry(
23
- removal_function: Callable[[str], object],
24
- target_path: str,
25
- _exc_info: BaseException,
26
- ) -> None:
27
- try:
28
- os.chmod(target_path, stat.S_IWRITE)
29
- removal_function(target_path)
30
- except OSError:
31
- pass
32
-
33
-
34
- @pytest.fixture
35
- def neutral_root() -> Iterator[Path]:
36
- """Yield a temp directory whose path carries no ``test_`` segment.
37
-
38
- The enforcer's ``is_test_file`` keys on the full path string, and pytest's
39
- own ``tmp_path`` directory name embeds the test name, which would make every
40
- synthetic constants path look like a test file. A neutral ``mkdtemp`` root
41
- mirrors how a production constants module path looks.
42
-
43
- A ``.git`` marker is planted at the root so the cross-tree widening resolves
44
- the repository root to this synthetic tree, never an enclosing real
45
- checkout, keeping every test bounded and deterministic.
46
- """
47
- neutral_directory = Path(tempfile.mkdtemp(prefix="deadconst-")).resolve()
48
- (neutral_directory / ".git").mkdir()
49
- try:
50
- yield neutral_directory
51
- finally:
52
- shutil.rmtree(neutral_directory, onexc=_strip_read_only_and_retry)
53
-
54
-
55
- def _check(source: str, file_path: str) -> list[str]:
56
- return code_rules_enforcer.check_dead_module_constants(source, file_path)
57
-
58
-
59
- def _check_edit(fragment: str, full_file_content: str, file_path: str) -> list[str]:
60
- return code_rules_enforcer.check_dead_module_constants(
61
- fragment, file_path, full_file_content
62
- )
63
-
64
-
65
- def _build_constants_package(
66
- workflow_directory: Path,
67
- constants_body: str,
68
- consumer_body: str,
69
- ) -> Path:
70
- constants_package = workflow_directory / "report_constants"
71
- constants_package.mkdir(parents=True)
72
- (constants_package / "__init__.py").write_text("", encoding="utf-8")
73
- constants_path = constants_package / "render_report_constants.py"
74
- constants_path.write_text(constants_body, encoding="utf-8")
75
- (workflow_directory / "render_report.py").write_text(consumer_body, encoding="utf-8")
76
- return constants_path
77
-
78
-
79
- def test_flags_constant_imported_by_no_module_in_the_tree(neutral_root: Path) -> None:
80
- consumer_body = (
81
- "from report_constants.render_report_constants import (\n"
82
- " MEDIUM_CODE,\n"
83
- " MEDIUM_TERMINAL,\n"
84
- ")\n"
85
- "\n"
86
- "def panel_class(medium: str) -> str:\n"
87
- " if medium == MEDIUM_TERMINAL:\n"
88
- " return 'terminal'\n"
89
- " return 'code-panel' if medium == MEDIUM_CODE else 'text-panel'\n"
90
- )
91
- constants_path = _build_constants_package(
92
- neutral_root / "workflow", CONSTANTS_BODY, consumer_body
93
- )
94
- issues = _check(CONSTANTS_BODY, str(constants_path))
95
- assert any("MEDIUM_TEXT" in each_issue for each_issue in issues), (
96
- f"Expected dead MEDIUM_TEXT flagged, got: {issues}"
97
- )
98
- assert not any(
99
- "MEDIUM_TERMINAL" in each_issue or "MEDIUM_CODE" in each_issue for each_issue in issues
100
- ), f"Imported constants must not be flagged, got: {issues}"
101
-
102
-
103
- def test_flags_a_dead_constant_added_by_an_edit_to_an_existing_module(
104
- neutral_root: Path,
105
- ) -> None:
106
- consumer_body = (
107
- "from report_constants.render_report_constants import (\n"
108
- " MEDIUM_CODE,\n"
109
- " MEDIUM_TERMINAL,\n"
110
- ")\n"
111
- "\n"
112
- "def panel_class(medium: str) -> str:\n"
113
- " if medium == MEDIUM_TERMINAL:\n"
114
- " return 'terminal'\n"
115
- " return 'code-panel' if medium == MEDIUM_CODE else 'text-panel'\n"
116
- )
117
- prior_body = 'MEDIUM_TERMINAL = "terminal"\nMEDIUM_CODE = "code"\n'
118
- constants_path = _build_constants_package(
119
- neutral_root / "workflow", prior_body, consumer_body
120
- )
121
- edit_fragment = 'MEDIUM_CODE = "code"\nMEDIUM_TEXT = "text"\n'
122
- post_edit_body = prior_body + 'MEDIUM_TEXT = "text"\n'
123
- issues = _check_edit(edit_fragment, post_edit_body, str(constants_path))
124
- assert any("MEDIUM_TEXT" in each_issue for each_issue in issues), (
125
- f"An Edit that inserts a dead constant must be flagged, got: {issues}"
126
- )
127
- assert not any(
128
- "MEDIUM_TERMINAL" in each_issue or "MEDIUM_CODE" in each_issue
129
- for each_issue in issues
130
- ), f"Imported constants must not be flagged on an edit, got: {issues}"
131
-
132
-
133
- def test_does_not_flag_constant_imported_one_directory_up(neutral_root: Path) -> None:
134
- consumer_uses_text = (
135
- "from report_constants.render_report_constants import (\n"
136
- " MEDIUM_CODE,\n"
137
- " MEDIUM_TERMINAL,\n"
138
- " MEDIUM_TEXT,\n"
139
- ")\n"
140
- "\n"
141
- "def panel_class(medium: str) -> str:\n"
142
- " if medium == MEDIUM_TERMINAL:\n"
143
- " return 'terminal'\n"
144
- " if medium == MEDIUM_TEXT:\n"
145
- " return 'text-panel'\n"
146
- " return 'code-panel' if medium == MEDIUM_CODE else 'text-panel'\n"
147
- )
148
- constants_path = _build_constants_package(
149
- neutral_root / "workflow", CONSTANTS_BODY, consumer_uses_text
150
- )
151
- issues = _check(CONSTANTS_BODY, str(constants_path))
152
- assert issues == [], f"No constant is dead when all are imported, got: {issues}"
153
-
154
-
155
- def test_does_not_flag_dunder_all_member_with_a_live_consumer(neutral_root: Path) -> None:
156
- constants_body = CONSTANTS_BODY + '__all__ = ["MEDIUM_TERMINAL"]\n'
157
- consumer_body = (
158
- "from report_constants.render_report_constants import MEDIUM_TERMINAL\n"
159
- "\n"
160
- "def label() -> str:\n"
161
- " return MEDIUM_TERMINAL\n"
162
- )
163
- constants_path = _build_constants_package(
164
- neutral_root / "workflow", constants_body, consumer_body
165
- )
166
- issues = _check(constants_body, str(constants_path))
167
- assert issues == [], f"An __all__ member a consumer imports stays live, got: {issues}"
168
-
169
-
170
- def test_dunder_all_narrows_check_to_exported_constants(neutral_root: Path) -> None:
171
- constants_body = (
172
- 'JSONL_APPEND_OPEN_MODE = "a"\n'
173
- 'ZIPFILE_READ_OPEN_MODE = "r"\n'
174
- "PRIVATE_BUFFER_BYTES = 1024\n"
175
- '__all__ = ["JSONL_APPEND_OPEN_MODE", "ZIPFILE_READ_OPEN_MODE"]\n'
176
- )
177
- consumer_body = (
178
- "from report_constants.render_report_constants import JSONL_APPEND_OPEN_MODE\n"
179
- "\n"
180
- "def open_mode() -> str:\n"
181
- " return JSONL_APPEND_OPEN_MODE\n"
182
- )
183
- constants_path = _build_constants_package(
184
- neutral_root / "workflow", constants_body, consumer_body
185
- )
186
- issues = _check(constants_body, str(constants_path))
187
- assert any("ZIPFILE_READ_OPEN_MODE" in each_issue for each_issue in issues), (
188
- f"An exported constant no module imports is dead, got: {issues}"
189
- )
190
- assert not any("JSONL_APPEND_OPEN_MODE" in each_issue for each_issue in issues), (
191
- f"An exported constant a consumer imports must not be flagged, got: {issues}"
192
- )
193
- assert not any("PRIVATE_BUFFER_BYTES" in each_issue for each_issue in issues), (
194
- f"A constant __all__ omits is the author's private value and is exempt, got: {issues}"
195
- )
196
-
197
-
198
- def test_does_not_run_on_ordinary_production_module(neutral_root: Path) -> None:
199
- workflow_directory = neutral_root / "workflow"
200
- workflow_directory.mkdir(parents=True)
201
- ordinary_path = workflow_directory / "render_report.py"
202
- body = "WIDGET_LIMIT = 5\n\ndef widgets() -> int:\n return WIDGET_LIMIT\n"
203
- ordinary_path.write_text(body, encoding="utf-8")
204
- issues = _check(body, str(ordinary_path))
205
- assert issues == [], (
206
- f"The dead-constant check runs only on dedicated constants modules, got: {issues}"
207
- )
208
-
209
-
210
- def test_runs_on_config_directory_module(neutral_root: Path) -> None:
211
- package_directory = neutral_root / "app"
212
- config_directory = package_directory / "config"
213
- config_directory.mkdir(parents=True)
214
- constants_body = "TIMEOUT_SECONDS = 30\nUNUSED_THRESHOLD = 99\n"
215
- constants_path = config_directory / "timing.py"
216
- constants_path.write_text(constants_body, encoding="utf-8")
217
- consumer_body = (
218
- "from config.timing import TIMEOUT_SECONDS\n"
219
- "\n"
220
- "def deadline() -> int:\n"
221
- " return TIMEOUT_SECONDS\n"
222
- )
223
- (package_directory / "service.py").write_text(consumer_body, encoding="utf-8")
224
- issues = _check(constants_body, str(constants_path))
225
- assert any("UNUSED_THRESHOLD" in each_issue for each_issue in issues), (
226
- f"Expected dead UNUSED_THRESHOLD flagged in config module, got: {issues}"
227
- )
228
- assert not any("TIMEOUT_SECONDS" in each_issue for each_issue in issues), (
229
- f"Consumed config constant must not be flagged, got: {issues}"
230
- )
231
-
232
-
233
- def test_counts_a_reference_from_a_test_module(neutral_root: Path) -> None:
234
- workflow_directory = neutral_root / "workflow"
235
- constants_body = 'ONLY_TESTS_USE_THIS = "x"\n'
236
- constants_path = workflow_directory / "render_report_constants.py"
237
- workflow_directory.mkdir(parents=True)
238
- constants_path.write_text(constants_body, encoding="utf-8")
239
- test_body = (
240
- "from render_report_constants import ONLY_TESTS_USE_THIS\n"
241
- "\n"
242
- "def test_value() -> None:\n"
243
- " assert ONLY_TESTS_USE_THIS == 'x'\n"
244
- )
245
- (workflow_directory / "test_render_report.py").write_text(test_body, encoding="utf-8")
246
- issues = _check(constants_body, str(constants_path))
247
- assert issues == [], f"A constant used only by a test under the tree stays live, got: {issues}"
248
-
249
-
250
- def test_is_skipped_on_a_constants_test_file(neutral_root: Path) -> None:
251
- workflow_directory = neutral_root / "workflow"
252
- workflow_directory.mkdir(parents=True)
253
- test_constants_path = workflow_directory / "test_render_report_constants.py"
254
- body = 'UNREFERENCED = "y"\n'
255
- test_constants_path.write_text(body, encoding="utf-8")
256
- issues = _check(body, str(test_constants_path))
257
- assert issues == [], f"Test files are exempt, got: {issues}"
258
-
259
-
260
- def _build_cross_tree_repository(
261
- repository_root: Path,
262
- constants_body: str,
263
- sibling_consumer_body: str,
264
- ) -> Path:
265
- config_directory = repository_root / "shared" / "theme_db" / "config"
266
- config_directory.mkdir(parents=True)
267
- constants_path = config_directory / "constants.py"
268
- constants_path.write_text(constants_body, encoding="utf-8")
269
- sibling_directory = repository_root / "cdp"
270
- sibling_directory.mkdir(parents=True)
271
- (sibling_directory / "tally.py").write_text(sibling_consumer_body, encoding="utf-8")
272
- return constants_path
273
-
274
-
275
- def test_does_not_flag_constant_used_only_in_a_sibling_tree(neutral_root: Path) -> None:
276
- constants_body = 'CROSS_TREE_CONSTANT = "cross"\nLOCALLY_DEAD_CONSTANT = "dead"\n'
277
- sibling_consumer_body = (
278
- "from shared.theme_db.config.constants import CROSS_TREE_CONSTANT\n"
279
- "\n"
280
- "def tally() -> str:\n"
281
- " return CROSS_TREE_CONSTANT\n"
282
- )
283
- constants_path = _build_cross_tree_repository(
284
- neutral_root, constants_body, sibling_consumer_body
285
- )
286
- issues = _check(constants_body, str(constants_path))
287
- assert not any("CROSS_TREE_CONSTANT" in each_issue for each_issue in issues), (
288
- f"A constant consumed by a sibling tree in the repository must not be flagged, got: {issues}"
289
- )
290
- assert any("LOCALLY_DEAD_CONSTANT" in each_issue for each_issue in issues), (
291
- f"A constant referenced nowhere in the repository stays flagged, got: {issues}"
292
- )
293
-
294
-
295
- def _build_name_collision_repository(
296
- repository_root: Path,
297
- promoter_timing_body: str,
298
- promoter_consumer_body: str,
299
- harness_constants_body: str,
300
- harness_consumer_body: str,
301
- ) -> Path:
302
- promoter_config = repository_root / "promoter" / "config"
303
- promoter_config.mkdir(parents=True)
304
- timing_path = promoter_config / "timing.py"
305
- timing_path.write_text(promoter_timing_body, encoding="utf-8")
306
- (repository_root / "promoter" / "runtime.py").write_text(
307
- promoter_consumer_body, encoding="utf-8"
308
- )
309
- harness_config = repository_root / "harness" / "config"
310
- harness_config.mkdir(parents=True)
311
- (harness_config / "lockfile_constants.py").write_text(
312
- harness_constants_body, encoding="utf-8"
313
- )
314
- (repository_root / "harness" / "lockfile.py").write_text(
315
- harness_consumer_body, encoding="utf-8"
316
- )
317
- return timing_path
318
-
319
-
320
- def test_same_named_constant_in_an_unrelated_module_does_not_mask_a_dead_one(
321
- neutral_root: Path,
322
- ) -> None:
323
- promoter_timing_body = "LOCKFILE_ACQUIRE_BYTE_LENGTH = 1\nSWEEP_INTERVAL_SECONDS = 30\n"
324
- promoter_consumer_body = (
325
- "from config.timing import SWEEP_INTERVAL_SECONDS\n"
326
- "\n"
327
- "def deadline() -> int:\n"
328
- " return SWEEP_INTERVAL_SECONDS\n"
329
- )
330
- harness_constants_body = "LOCKFILE_ACQUIRE_BYTE_LENGTH = 1\n"
331
- harness_consumer_body = (
332
- "from harness.config.lockfile_constants import LOCKFILE_ACQUIRE_BYTE_LENGTH\n"
333
- "\n"
334
- "def acquire() -> int:\n"
335
- " return LOCKFILE_ACQUIRE_BYTE_LENGTH\n"
336
- )
337
- timing_path = _build_name_collision_repository(
338
- neutral_root,
339
- promoter_timing_body,
340
- promoter_consumer_body,
341
- harness_constants_body,
342
- harness_consumer_body,
343
- )
344
- issues = _check(promoter_timing_body, str(timing_path))
345
- assert any("LOCKFILE_ACQUIRE_BYTE_LENGTH" in each_issue for each_issue in issues), (
346
- "A constant whose only same-named twin lives in an unrelated module's "
347
- f"import must still be flagged dead, got: {issues}"
348
- )
349
- assert not any("SWEEP_INTERVAL_SECONDS" in each_issue for each_issue in issues), (
350
- f"A constant consumed within its own package must not be flagged, got: {issues}"
351
- )
352
-
353
-
354
- def test_returns_empty_list_at_file_cap(
355
- neutral_root: Path, monkeypatch: pytest.MonkeyPatch
356
- ) -> None:
357
- monkeypatch.setattr("code_rules_dead_module_constant.MAX_SCAN_ROOT_FILE_COUNT", 0)
358
- constants_path = _build_constants_package(
359
- neutral_root / "workflow",
360
- CONSTANTS_BODY,
361
- "def noop() -> None:\n pass\n",
362
- )
363
- issues = _check(CONSTANTS_BODY, str(constants_path))
364
- assert issues == [], f"File cap hit must return [] (cannot prove dead), got: {issues}"
365
-
366
-
367
- def _write_noise_modules(directory: Path, count: int) -> None:
368
- directory.mkdir(parents=True)
369
- for each_index in range(count):
370
- (directory / f"mod_{each_index}.py").write_text(
371
- "def noop() -> int:\n return 1\n", encoding="utf-8"
372
- )
373
-
374
-
375
- def test_dead_exported_constant_flagged_despite_many_noncandidate_siblings(
376
- neutral_root: Path, monkeypatch: pytest.MonkeyPatch
377
- ) -> None:
378
- monkeypatch.setattr("code_rules_dead_module_constant.MAX_SCAN_ROOT_FILE_COUNT", 3)
379
- package_directory = neutral_root / "pkg"
380
- package_directory.mkdir(parents=True)
381
- constants_body = 'DEAD_EXPORTED = "x"\n__all__ = ["DEAD_EXPORTED"]\n'
382
- constants_path = package_directory / "settings_constants.py"
383
- constants_path.write_text(constants_body, encoding="utf-8")
384
- _write_noise_modules(neutral_root / "noise", 10)
385
- issues = _check(constants_body, str(constants_path))
386
- assert any("DEAD_EXPORTED" in each_issue for each_issue in issues), (
387
- "A dead exported constant must stay flagged when the widened scan meets "
388
- f"many sibling modules that never import from this module, got: {issues}"
389
- )
390
-
391
-
392
- def test_live_qualified_importer_found_despite_many_noncandidate_siblings(
393
- neutral_root: Path, monkeypatch: pytest.MonkeyPatch
394
- ) -> None:
395
- monkeypatch.setattr("code_rules_dead_module_constant.MAX_SCAN_ROOT_FILE_COUNT", 3)
396
- package_directory = neutral_root / "pkg"
397
- package_directory.mkdir(parents=True)
398
- constants_body = 'LIVE_EXPORTED = "y"\n__all__ = ["LIVE_EXPORTED"]\n'
399
- constants_path = package_directory / "settings_constants.py"
400
- constants_path.write_text(constants_body, encoding="utf-8")
401
- _write_noise_modules(neutral_root / "noise", 10)
402
- consumer_directory = neutral_root / "app"
403
- consumer_directory.mkdir(parents=True)
404
- consumer_body = (
405
- "from pkg.settings_constants import LIVE_EXPORTED\n"
406
- "\n"
407
- "def use() -> str:\n"
408
- " return LIVE_EXPORTED\n"
409
- )
410
- (consumer_directory / "consumer.py").write_text(consumer_body, encoding="utf-8")
411
- issues = _check(constants_body, str(constants_path))
412
- assert issues == [], (
413
- "A constant imported through a qualified from-import must stay live even "
414
- f"when many non-importing siblings surround it, got: {issues}"
415
- )
416
-
417
-
418
- def test_widened_scan_reads_each_file_at_most_once(
419
- neutral_root: Path, monkeypatch: pytest.MonkeyPatch
420
- ) -> None:
421
- constants_body = 'CROSS_TREE_CONSTANT = "cross"\nLOCALLY_DEAD_CONSTANT = "dead"\n'
422
- sibling_consumer_body = (
423
- "from shared.theme_db.config.constants import CROSS_TREE_CONSTANT\n"
424
- "\n"
425
- "def tally() -> str:\n"
426
- " return CROSS_TREE_CONSTANT\n"
427
- )
428
- constants_path = _build_cross_tree_repository(
429
- neutral_root, constants_body, sibling_consumer_body
430
- )
431
- package_tree_neighbor = constants_path.parent.parent / "neighbor.py"
432
- package_tree_neighbor.write_text(
433
- "def neighbor() -> int:\n return 1\n", encoding="utf-8"
434
- )
435
- read_counts: dict[str, int] = {}
436
- original_read_text = Path.read_text
437
-
438
- def counting_read_text(self: Path, *positional: object, **keyword: object) -> str:
439
- normalized_key = os.path.normcase(str(self.resolve()))
440
- read_counts[normalized_key] = read_counts.get(normalized_key, 0) + 1
441
- return original_read_text(self, *positional, **keyword) # type: ignore[arg-type] # forwards args
442
-
443
- monkeypatch.setattr(Path, "read_text", counting_read_text)
444
- _check(constants_body, str(constants_path))
445
- over_read_paths = {
446
- each_path: each_count for each_path, each_count in read_counts.items() if each_count > 1
447
- }
448
- assert not over_read_paths, (
449
- f"Widening must read each .py file at most once, got over-reads: {over_read_paths}"
450
- )
451
-
452
-
453
- def _build_settings_package(workflow_directory: Path, constants_body: str) -> Path:
454
- constants_package = workflow_directory / "app_settings"
455
- constants_package.mkdir(parents=True)
456
- (constants_package / "__init__.py").write_text("", encoding="utf-8")
457
- constants_path = constants_package / "app_settings_constants.py"
458
- constants_path.write_text(constants_body, encoding="utf-8")
459
- return constants_path
460
-
461
-
462
- def test_deny_once_records_intent_then_retry_passes_after_consumer(
463
- neutral_root: Path,
464
- ) -> None:
465
- constants_body = 'PATH_ENV_NAME = "THEME_PATH"\n'
466
- workflow_directory = neutral_root / "workflow"
467
- constants_path = _build_settings_package(workflow_directory, constants_body)
468
- first_issues = _check(constants_body, str(constants_path))
469
- assert any("PATH_ENV_NAME" in each_issue for each_issue in first_issues), first_issues
470
- assert any(
471
- "app_settings_constants.py" in each_issue for each_issue in first_issues
472
- ), f"Deny message must record the target constants module, got: {first_issues}"
473
- assert any(
474
- "re-issue this write" in each_issue for each_issue in first_issues
475
- ), f"Deny message must instruct the retry escape, got: {first_issues}"
476
- consumer_body = (
477
- "from app_settings.app_settings_constants import PATH_ENV_NAME\n"
478
- "\n"
479
- "def resolve() -> str:\n"
480
- " return PATH_ENV_NAME\n"
481
- )
482
- (workflow_directory / "consumer_service.py").write_text(
483
- consumer_body, encoding="utf-8"
484
- )
485
- retry_issues = _check(constants_body, str(constants_path))
486
- assert retry_issues == [], (
487
- f"Once the consumer is on disk the retry passes, got: {retry_issues}"
488
- )
489
-
490
-
491
- def test_dead_constant_stays_denied_on_every_attempt(neutral_root: Path) -> None:
492
- constants_body = 'PATH_ENV_NAME = "THEME_PATH"\n'
493
- workflow_directory = neutral_root / "workflow"
494
- constants_path = _build_settings_package(workflow_directory, constants_body)
495
- (workflow_directory / "unrelated.py").write_text(
496
- "def noop() -> int:\n return 1\n", encoding="utf-8"
497
- )
498
- first_issues = _check(constants_body, str(constants_path))
499
- second_issues = _check(constants_body, str(constants_path))
500
- for each_attempt in (first_issues, second_issues):
501
- assert any("PATH_ENV_NAME" in each_issue for each_issue in each_attempt), each_attempt
502
- assert any(
503
- "app_settings_constants.py" in each_issue for each_issue in each_attempt
504
- ), f"Every deny attempt records the target module, got: {each_attempt}"
505
- assert any(
506
- "re-issue this write" in each_issue for each_issue in each_attempt
507
- ), f"A dead constant stays denied with the retry escape, got: {each_attempt}"
508
-
509
-
510
- ALIAS_CONSTANTS_BODY = 'BASE_PROMPTS_DIRECTORY_NAME = "prompts"\nSTYLE_SHEET_FILENAME = "style.css"\n'
511
-
512
- ALIASED_IMPORT_CONSUMER_BODY = (
513
- "from style_guide_gen_constants.config import constants as sg\n"
514
- "\n"
515
- "def prompts_directory() -> str:\n"
516
- " return sg.BASE_PROMPTS_DIRECTORY_NAME\n"
517
- "\n"
518
- "def style_sheet() -> str:\n"
519
- " return sg.STYLE_SHEET_FILENAME\n"
520
- )
521
-
522
-
523
- def _build_alias_repository(
524
- package_parent: Path,
525
- consumer_directory: Path,
526
- consumer_body: str,
527
- ) -> Path:
528
- package_directory = package_parent / "style_guide_gen_constants"
529
- config_directory = package_directory / "config"
530
- config_directory.mkdir(parents=True)
531
- (package_directory / "__init__.py").write_text("", encoding="utf-8")
532
- (config_directory / "__init__.py").write_text("", encoding="utf-8")
533
- constants_path = config_directory / "constants.py"
534
- constants_path.write_text(ALIAS_CONSTANTS_BODY, encoding="utf-8")
535
- consumer_directory.mkdir(parents=True, exist_ok=True)
536
- (consumer_directory / "style_guide_gen.py").write_text(consumer_body, encoding="utf-8")
537
- return constants_path
538
-
539
-
540
- def test_does_not_flag_constant_read_through_an_aliased_module_import(
541
- neutral_root: Path,
542
- ) -> None:
543
- constants_path = _build_alias_repository(
544
- neutral_root,
545
- neutral_root / "style_guide_gen_constants",
546
- ALIASED_IMPORT_CONSUMER_BODY,
547
- )
548
- issues = _check(ALIAS_CONSTANTS_BODY, str(constants_path))
549
- assert issues == [], (
550
- f"A constant read as an attribute on an aliased module import stays live, got: {issues}"
551
- )
552
-
553
-
554
- def test_does_not_flag_constant_read_through_an_alias_outside_the_package_tree(
555
- neutral_root: Path,
556
- ) -> None:
557
- constants_path = _build_alias_repository(
558
- neutral_root / "scripts",
559
- neutral_root / "scripts",
560
- ALIASED_IMPORT_CONSUMER_BODY,
561
- )
562
- issues = _check(ALIAS_CONSTANTS_BODY, str(constants_path))
563
- assert issues == [], (
564
- "A constant an aliased module import reads from outside the package tree "
565
- f"stays live through the widened scan, got: {issues}"
566
- )
567
-
568
-
569
- def test_does_not_flag_constant_read_through_an_unaliased_module_import_outside_the_tree(
570
- neutral_root: Path,
571
- ) -> None:
572
- consumer_body = (
573
- "from style_guide_gen_constants.config import constants\n"
574
- "\n"
575
- "def prompts_directory() -> str:\n"
576
- " return constants.BASE_PROMPTS_DIRECTORY_NAME\n"
577
- "\n"
578
- "def style_sheet() -> str:\n"
579
- " return constants.STYLE_SHEET_FILENAME\n"
580
- )
581
- constants_path = _build_alias_repository(
582
- neutral_root / "scripts",
583
- neutral_root / "scripts",
584
- consumer_body,
585
- )
586
- issues = _check(ALIAS_CONSTANTS_BODY, str(constants_path))
587
- assert issues == [], (
588
- f"A constant read on an unaliased imported module object stays live, got: {issues}"
589
- )
590
-
591
-
592
- def test_does_not_flag_constant_read_through_a_dotted_module_path_outside_the_tree(
593
- neutral_root: Path,
594
- ) -> None:
595
- consumer_body = (
596
- "import style_guide_gen_constants.config.constants\n"
597
- "\n"
598
- "def prompts_directory() -> str:\n"
599
- " return style_guide_gen_constants.config.constants.BASE_PROMPTS_DIRECTORY_NAME\n"
600
- "\n"
601
- "def style_sheet() -> str:\n"
602
- " return style_guide_gen_constants.config.constants.STYLE_SHEET_FILENAME\n"
603
- )
604
- constants_path = _build_alias_repository(
605
- neutral_root / "scripts",
606
- neutral_root / "scripts",
607
- consumer_body,
608
- )
609
- issues = _check(ALIAS_CONSTANTS_BODY, str(constants_path))
610
- assert issues == [], (
611
- f"A constant read through the full dotted module path stays live, got: {issues}"
612
- )
613
-
614
-
615
- def test_does_not_flag_constant_read_through_an_alias_in_a_sibling_tree(
616
- neutral_root: Path,
617
- ) -> None:
618
- constants_body = 'CROSS_TREE_CONSTANT = "cross"\nLOCALLY_DEAD_CONSTANT = "dead"\n'
619
- sibling_consumer_body = (
620
- "from shared.theme_db.config import constants as theme_constants\n"
621
- "\n"
622
- "def tally() -> str:\n"
623
- " return theme_constants.CROSS_TREE_CONSTANT\n"
624
- )
625
- constants_path = _build_cross_tree_repository(
626
- neutral_root, constants_body, sibling_consumer_body
627
- )
628
- issues = _check(constants_body, str(constants_path))
629
- assert not any("CROSS_TREE_CONSTANT" in each_issue for each_issue in issues), (
630
- "A constant a sibling tree reads as an attribute on an aliased module import "
631
- f"must not be flagged, got: {issues}"
632
- )
633
- assert any("LOCALLY_DEAD_CONSTANT" in each_issue for each_issue in issues), (
634
- f"A constant referenced nowhere in the repository stays flagged, got: {issues}"
635
- )
636
-
637
-
638
- def _build_alias_collision_repository(
639
- repository_root: Path,
640
- promoter_constants_body: str,
641
- harness_constants_body: str,
642
- harness_consumer_body: str,
643
- ) -> Path:
644
- promoter_config = repository_root / "promoter" / "config"
645
- promoter_config.mkdir(parents=True)
646
- promoter_constants_path = promoter_config / "constants.py"
647
- promoter_constants_path.write_text(promoter_constants_body, encoding="utf-8")
648
- harness_config = repository_root / "harness" / "config"
649
- harness_config.mkdir(parents=True)
650
- (harness_config / "other_constants.py").write_text(harness_constants_body, encoding="utf-8")
651
- (repository_root / "harness" / "lockfile.py").write_text(
652
- harness_consumer_body, encoding="utf-8"
653
- )
654
- return promoter_constants_path
655
-
656
-
657
- def test_alias_attribute_read_on_an_unrelated_module_does_not_mask_a_dead_constant(
658
- neutral_root: Path,
659
- ) -> None:
660
- promoter_constants_body = 'NEVER_READ_CONSTANT = "dead"\n'
661
- harness_constants_body = 'NEVER_READ_CONSTANT = "twin"\n'
662
- harness_consumer_body = (
663
- "from harness.config import other_constants as oc\n"
664
- "\n"
665
- "def acquire() -> str:\n"
666
- " return oc.NEVER_READ_CONSTANT\n"
667
- )
668
- promoter_constants_path = _build_alias_collision_repository(
669
- neutral_root,
670
- promoter_constants_body,
671
- harness_constants_body,
672
- harness_consumer_body,
673
- )
674
- issues = _check(promoter_constants_body, str(promoter_constants_path))
675
- assert any("NEVER_READ_CONSTANT" in each_issue for each_issue in issues), (
676
- "A constant whose only same-named twin is read as an attribute on an "
677
- f"unrelated aliased module must still be flagged dead, got: {issues}"
678
- )
679
-