claude-dev-env 1.82.0 → 1.84.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 (217) hide show
  1. package/CLAUDE.md +16 -13
  2. package/_shared/pr-loop/audit-contract.md +24 -12
  3. package/_shared/pr-loop/scripts/CLAUDE.md +1 -0
  4. package/_shared/pr-loop/scripts/README.md +1 -0
  5. package/_shared/pr-loop/scripts/_claude_permissions_common.py +16 -5
  6. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  7. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -0
  8. package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviewer_availability_constants.py +12 -0
  9. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +0 -2
  10. package/_shared/pr-loop/scripts/reviewer_availability.py +182 -0
  11. package/_shared/pr-loop/scripts/reviews_disabled.py +2 -0
  12. package/_shared/pr-loop/scripts/terminology_sweep.py +9 -33
  13. package/_shared/pr-loop/scripts/tests/CLAUDE.md +2 -0
  14. package/_shared/pr-loop/scripts/tests/test__claude_permissions_common.py +212 -0
  15. package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +18 -0
  16. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +18 -0
  17. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +37 -0
  18. package/_shared/pr-loop/scripts/tests/test_reviewer_availability.py +159 -0
  19. package/_shared/pr-loop/scripts/tests/test_reviewer_availability_constants.py +36 -0
  20. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +14 -0
  21. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +14 -4
  22. package/agents/clean-coder.md +3 -0
  23. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +1 -1
  24. package/bin/install.mjs +16 -8
  25. package/bin/install.test.mjs +17 -6
  26. package/commands/CLAUDE.md +0 -1
  27. package/docs/CODE_RULES.md +1 -1
  28. package/hooks/blocking/CLAUDE.md +2 -4
  29. package/hooks/blocking/code_rules_constants_config.py +164 -1
  30. package/hooks/blocking/code_rules_docstrings.py +428 -15
  31. package/hooks/blocking/code_rules_enforcer.py +33 -0
  32. package/hooks/blocking/code_rules_imports_logging.py +867 -1
  33. package/hooks/blocking/code_rules_magic_values.py +5 -0
  34. package/hooks/blocking/code_rules_naming_collection.py +152 -6
  35. package/hooks/blocking/code_rules_shared.py +34 -0
  36. package/hooks/blocking/code_rules_string_magic.py +68 -0
  37. package/hooks/blocking/duplicate_rmtree_helper_blocker.py +4 -4
  38. package/hooks/blocking/pre_tool_use_dispatcher.py +3 -3
  39. package/hooks/blocking/reviewer_spawn_gate.py +182 -0
  40. package/hooks/blocking/stale_comment_reference_blocker.py +267 -0
  41. package/hooks/blocking/state_description_blocker.py +96 -5
  42. package/hooks/blocking/test_code_rules_config_duplicate_path_anchor.py +132 -0
  43. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +2 -0
  44. package/hooks/blocking/test_code_rules_enforcer_docstring_delegation_summary.py +385 -0
  45. package/hooks/blocking/test_code_rules_enforcer_docstring_prose_wall_illustration.py +197 -0
  46. package/hooks/blocking/test_code_rules_enforcer_docstring_runon_sentence.py +27 -0
  47. package/hooks/blocking/test_code_rules_enforcer_join_separator_magic.py +67 -0
  48. package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +40 -0
  49. package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +213 -0
  50. package/hooks/blocking/test_code_rules_enforcer_referenced_underscore_loop.py +169 -0
  51. package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +33 -0
  52. package/hooks/blocking/test_code_rules_enforcer_split_shared.py +23 -0
  53. package/hooks/blocking/test_code_rules_js_bare_flag_return_directive.py +266 -0
  54. package/hooks/blocking/test_code_rules_js_sibling_return_object_key_drift.py +490 -0
  55. package/hooks/blocking/test_code_rules_logging_adjacent_literals.py +171 -0
  56. package/hooks/blocking/test_code_rules_magic_values.py +54 -0
  57. package/hooks/blocking/test_duplicate_rmtree_helper_blocker.py +0 -6
  58. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +184 -0
  59. package/hooks/blocking/test_pre_tool_use_dispatcher.py +9 -3
  60. package/hooks/blocking/test_reviewer_spawn_gate.py +230 -0
  61. package/hooks/blocking/test_shared_stdin_adoption.py +5 -30
  62. package/hooks/blocking/test_stale_comment_reference_blocker.py +236 -0
  63. package/hooks/blocking/test_state_description_blocker.py +135 -0
  64. package/hooks/hooks.json +5 -0
  65. package/hooks/hooks_constants/CLAUDE.md +3 -4
  66. package/hooks/hooks_constants/blocking_check_limits.py +46 -0
  67. package/hooks/hooks_constants/code_rules_enforcer_constants.py +41 -0
  68. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
  69. package/hooks/hooks_constants/duplicate_rmtree_helper_blocker_constants.py +0 -1
  70. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -5
  71. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
  72. package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +41 -0
  73. package/hooks/hooks_constants/stale_comment_reference_blocker_constants.py +76 -0
  74. package/hooks/hooks_constants/state_description_blocker_constants.py +8 -0
  75. package/hooks/validation/post_tool_use_dispatcher.py +1 -1
  76. package/hooks/validation/test_post_tool_use_dispatcher.py +51 -35
  77. package/hooks/workflow/CLAUDE.md +2 -8
  78. package/package.json +1 -1
  79. package/rules/CLAUDE.md +5 -2
  80. package/rules/claude-md-orphan-file.md +5 -0
  81. package/rules/docstring-prose-matches-implementation.md +10 -1
  82. package/rules/env-var-table-code-drift.md +5 -0
  83. package/rules/es-exe-file-search.md +17 -0
  84. package/rules/no-historical-clutter.md +12 -1
  85. package/rules/orphan-css-class.md +5 -0
  86. package/rules/package-inventory-stale-entry.md +10 -0
  87. package/rules/paired-test-coverage.md +5 -0
  88. package/rules/plain-illustrative-docstrings.md +40 -1
  89. package/rules/verify-before-asking.md +7 -0
  90. package/rules/verify-runtime-state.md +40 -0
  91. package/rules/windows-filesystem-safe.md +8 -0
  92. package/rules/workers-done-before-complete.md +33 -0
  93. package/rules/workflow-substitution-slots.md +5 -0
  94. package/skills/CLAUDE.md +7 -2
  95. package/skills/autoconverge/SKILL.md +48 -61
  96. package/skills/autoconverge/reference/closing-report.md +6 -6
  97. package/skills/autoconverge/reference/convergence.md +17 -15
  98. package/skills/autoconverge/reference/gotchas.md +6 -3
  99. package/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +2 -9
  100. package/skills/autoconverge/workflow/converge.contract.test.mjs +153 -10
  101. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +55 -19
  102. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +16 -9
  103. package/skills/autoconverge/workflow/converge.mjs +167 -106
  104. package/skills/autoconverge/workflow/render_report.py +7 -11
  105. package/skills/bugteam/CLAUDE.md +1 -1
  106. package/skills/bugteam/PROMPTS.md +7 -6
  107. package/skills/bugteam/SKILL.md +25 -72
  108. package/skills/bugteam/reference/CLAUDE.md +1 -3
  109. package/skills/bugteam/reference/README.md +1 -1
  110. package/skills/bugteam/reference/audit-and-teammates.md +1 -1
  111. package/skills/bugteam/reference/obstacles/fix-publish-summary.md +1 -1
  112. package/skills/bugteam/reference/team-setup.md +8 -7
  113. package/skills/bugteam/scripts/CLAUDE.md +0 -6
  114. package/skills/bugteam/scripts/README.md +0 -4
  115. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +0 -1
  116. package/skills/code/SKILL.md +2 -0
  117. package/skills/copilot-review/CLAUDE.md +1 -1
  118. package/skills/copilot-review/SKILL.md +25 -23
  119. package/skills/everything-search/SKILL.md +5 -0
  120. package/skills/findbugs/CLAUDE.md +2 -2
  121. package/skills/findbugs/SKILL.md +22 -83
  122. package/skills/fixbugs/SKILL.md +2 -4
  123. package/skills/log-audit/CLAUDE.md +20 -0
  124. package/skills/log-audit/SKILL.md +68 -0
  125. package/skills/log-audit/reference/CLAUDE.md +9 -0
  126. package/skills/log-audit/reference/charter.md +52 -0
  127. package/skills/log-audit/scripts/CLAUDE.md +27 -0
  128. package/skills/log-audit/scripts/cluster_recurrences.py +261 -0
  129. package/skills/log-audit/scripts/collect_log_window.py +199 -0
  130. package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +12 -0
  131. package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +23 -0
  132. package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +24 -0
  133. package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +49 -0
  134. package/skills/log-audit/scripts/mine_copilot_findings.py +302 -0
  135. package/skills/log-audit/scripts/test_cluster_recurrences.py +160 -0
  136. package/skills/log-audit/scripts/test_collect_log_window.py +111 -0
  137. package/skills/log-audit/scripts/test_mine_copilot_findings.py +126 -0
  138. package/skills/monitor-open-prs/SKILL.md +2 -2
  139. package/skills/post-audit-findings/SKILL.md +84 -0
  140. package/skills/pr-converge/CLAUDE.md +2 -0
  141. package/skills/pr-converge/SKILL.md +72 -59
  142. package/skills/pr-converge/reference/CLAUDE.md +1 -1
  143. package/skills/pr-converge/reference/convergence-gates.md +16 -19
  144. package/skills/pr-converge/reference/examples.md +5 -5
  145. package/skills/pr-converge/reference/fix-protocol.md +16 -43
  146. package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +1 -1
  147. package/skills/pr-converge/reference/per-tick.md +24 -45
  148. package/skills/pr-converge/reference/state-schema.md +15 -0
  149. package/skills/pr-converge/scripts/README.md +3 -5
  150. package/skills/pr-fix-protocol/SKILL.md +70 -0
  151. package/skills/pr-loop-lifecycle/SKILL.md +73 -0
  152. package/skills/{bugteam → pr-loop-lifecycle}/reference/teardown-publish-permissions.md +22 -24
  153. package/skills/pr-scope-resolve/SKILL.md +48 -0
  154. package/skills/qbug/CLAUDE.md +4 -4
  155. package/skills/qbug/SKILL.md +46 -144
  156. package/skills/qbug/test_qbug_skill_audit_schema.py +2 -2
  157. package/skills/qbug/test_qbug_skill_post_fix_audit.py +1 -1
  158. package/skills/reviewer-gates/SKILL.md +96 -0
  159. package/skills/session-log/CLAUDE.md +7 -7
  160. package/skills/session-log/SKILL.md +27 -44
  161. package/skills/test_markdown_link_integrity.py +103 -0
  162. package/commands/doc-gist.md +0 -16
  163. package/hooks/blocking/_md_to_html_blocker_test_support.py +0 -65
  164. package/hooks/blocking/conftest.py +0 -30
  165. package/hooks/blocking/md_path_exemptions.py +0 -224
  166. package/hooks/blocking/md_to_html_blocker.py +0 -155
  167. package/hooks/blocking/test_md_to_html_blocker_exemptions.py +0 -434
  168. package/hooks/blocking/test_md_to_html_blocker_extensions.py +0 -157
  169. package/hooks/blocking/test_md_to_html_blocker_path_resolution.py +0 -336
  170. package/hooks/hooks_constants/doc_gist_auto_publish_constants.py +0 -18
  171. package/hooks/hooks_constants/html_companion_constants.py +0 -20
  172. package/hooks/hooks_constants/md_to_html_blocker_constants.py +0 -76
  173. package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +0 -125
  174. package/hooks/workflow/doc_gist_auto_publish.py +0 -144
  175. package/hooks/workflow/md_to_html_companion.py +0 -358
  176. package/hooks/workflow/test_doc_gist_auto_publish.py +0 -117
  177. package/hooks/workflow/test_md_to_html_companion.py +0 -613
  178. package/skills/bugteam/reference/audit-contract.md +0 -163
  179. package/skills/bugteam/scripts/_bugteam_permissions_common.py +0 -455
  180. package/skills/bugteam/scripts/bugteam_scripts_constants/claude_permissions_common_constants.py +0 -69
  181. package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -280
  182. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -266
  183. package/skills/bugteam/scripts/test__bugteam_permissions_common.py +0 -160
  184. package/skills/bugteam/scripts/test_agent_config_carveout.py +0 -356
  185. package/skills/bugteam/scripts/test_bugteam_permissions_common.py +0 -140
  186. package/skills/doc-gist/CLAUDE.md +0 -25
  187. package/skills/doc-gist/SKILL.md +0 -97
  188. package/skills/doc-gist/references/CLAUDE.md +0 -9
  189. package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +0 -453
  190. package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +0 -515
  191. package/skills/doc-gist/references/examples/03-code-review-pr.html +0 -638
  192. package/skills/doc-gist/references/examples/04-code-understanding.html +0 -491
  193. package/skills/doc-gist/references/examples/05-design-system.html +0 -629
  194. package/skills/doc-gist/references/examples/06-component-variants.html +0 -605
  195. package/skills/doc-gist/references/examples/07-prototype-animation.html +0 -455
  196. package/skills/doc-gist/references/examples/08-prototype-interaction.html +0 -396
  197. package/skills/doc-gist/references/examples/09-slide-deck.html +0 -592
  198. package/skills/doc-gist/references/examples/10-svg-illustrations.html +0 -492
  199. package/skills/doc-gist/references/examples/11-status-report.html +0 -528
  200. package/skills/doc-gist/references/examples/12-incident-report.html +0 -596
  201. package/skills/doc-gist/references/examples/13-flowchart-diagram.html +0 -395
  202. package/skills/doc-gist/references/examples/14-research-feature-explainer.html +0 -381
  203. package/skills/doc-gist/references/examples/15-research-concept-explainer.html +0 -368
  204. package/skills/doc-gist/references/examples/16-implementation-plan.html +0 -702
  205. package/skills/doc-gist/references/examples/17-pr-writeup.html +0 -595
  206. package/skills/doc-gist/references/examples/18-editor-triage-board.html +0 -573
  207. package/skills/doc-gist/references/examples/19-editor-feature-flags.html +0 -663
  208. package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +0 -722
  209. package/skills/doc-gist/references/examples/21-decision-signoff.html +0 -546
  210. package/skills/doc-gist/references/examples/CLAUDE.md +0 -25
  211. package/skills/doc-gist/references/examples/README.md +0 -5
  212. package/skills/doc-gist/scripts/CLAUDE.md +0 -27
  213. package/skills/doc-gist/scripts/doc_gist_scripts_constants/CLAUDE.md +0 -10
  214. package/skills/doc-gist/scripts/doc_gist_scripts_constants/gist_upload_constants.py +0 -16
  215. package/skills/doc-gist/scripts/gist_upload.py +0 -177
  216. package/skills/doc-gist/scripts/test_gist_upload.py +0 -51
  217. /package/skills/{doc-gist/scripts/doc_gist_scripts_constants → log-audit/scripts/log_audit_constants}/__init__.py +0 -0
@@ -25,6 +25,7 @@ from hooks_constants.blocking_check_limits import ( # noqa: E402
25
25
  ALL_ABSENT_TYPE_CHECKING_GATE_DOCSTRING_PHRASES,
26
26
  ALL_DATA_SCHEMA_CONSTANT_NAME_MARKERS,
27
27
  ALL_DATA_SCHEMA_DOCSTRING_ACKNOWLEDGEMENT_PHRASES,
28
+ ALL_DELEGATION_ENUMERATION_STOP_TOKENS,
28
29
  ALL_DOCSTRING_EXCLUSIVE_SCOPE_PHRASES,
29
30
  ALL_DOCSTRING_EXEMPT_DECORATOR_NAMES,
30
31
  ALL_DOCSTRING_FILE_REFERENCE_SUFFIXES,
@@ -52,6 +53,8 @@ from hooks_constants.blocking_check_limits import ( # noqa: E402
52
53
  ALL_ZIPFILE_WRITE_MODE_VALUES,
53
54
  DOCSTRING_FALLBACK_BRANCH_MINIMUM_ROUTE_COUNT,
54
55
  DOCSTRING_LARGE_ZIP_FILE_EXCEPTION_NAME,
56
+ DOCSTRING_NARRATIVE_LINE_JOIN_SEPARATOR,
57
+ DOCSTRING_NARRATIVE_PROSE_LINE_LIMIT,
55
58
  DOCSTRING_REFERENCE_MARKER_WINDOW,
56
59
  DOCSTRING_RUNON_SENTENCE_BOUNDARY_PATTERN,
57
60
  DOCSTRING_RUNON_SENTENCE_WORD_LIMIT,
@@ -63,6 +66,7 @@ from hooks_constants.blocking_check_limits import ( # noqa: E402
63
66
  MAX_DOCSTRING_ARGS_SIGNATURE_ISSUES,
64
67
  MAX_DOCSTRING_ARGS_SPAN_SCOPE_ISSUES,
65
68
  MAX_DOCSTRING_CARDINAL_FAMILY_ISSUES,
69
+ MAX_DOCSTRING_DELEGATION_ENUMERATION_ISSUES,
66
70
  MAX_DOCSTRING_FALLBACK_BRANCH_ISSUES,
67
71
  MAX_DOCSTRING_FIELD_RUNMODE_OUTCOME_ISSUES,
68
72
  MAX_DOCSTRING_FORMAT_ISSUES,
@@ -70,6 +74,7 @@ from hooks_constants.blocking_check_limits import ( # noqa: E402
70
74
  MAX_DOCSTRING_MARK_GLYPH_ENUMERATION_ISSUES,
71
75
  MAX_DOCSTRING_NO_CONSUMER_CLAIM_ISSUES,
72
76
  MAX_DOCSTRING_NO_NETWORK_CLAIM_ISSUES,
77
+ MAX_DOCSTRING_PROSE_WALL_ISSUES,
73
78
  MAX_DOCSTRING_RAISES_LARGEZIPFILE_ISSUES,
74
79
  MAX_DOCSTRING_RETURNS_PLURAL_CARDINALITY_ISSUES,
75
80
  MAX_DOCSTRING_RUNON_SENTENCE_ISSUES,
@@ -82,16 +87,21 @@ from hooks_constants.blocking_check_limits import ( # noqa: E402
82
87
  MAX_MODULE_DOCSTRING_CHECK_ROSTER_ISSUES,
83
88
  MAX_MODULE_DOCSTRING_DATA_SCHEMA_SCOPE_ISSUES,
84
89
  MINIMUM_CONSTANT_FAMILY_MEMBERS_FOR_CARDINAL_CHECK,
90
+ MINIMUM_DELEGATION_ENUMERATION_ITEMS,
85
91
  MINIMUM_DOCSTRING_FAMILY_OVERLAP_FOR_CARDINAL_CHECK,
86
92
  MINIMUM_NAMED_LINEAR_STEPS_FOR_DISPATCH_CHECK,
87
93
  MINIMUM_NAMED_MARKS_FOR_PROSE_ENUMERATION,
88
94
  MINIMUM_PUBLIC_CHECKS_FOR_MODULE_DOCSTRING_ROSTER,
89
95
  MINIMUM_PUBLIC_METHODS_FOR_CLASS_DOCSTRING_BREADTH,
96
+ MINIMUM_SIBLING_OCCURRENCES_FOR_SHARED_TOKEN,
90
97
  MINIMUM_TOKENS_FOR_DISPATCH_CALLEE,
91
98
  MINIMUM_TUPLE_MEMBERS_FOR_DOCSTRING_ENUMERATION,
92
99
  MODULE_DOCSTRING_DATA_SCHEMA_CONSTANT_SAMPLE_LIMIT,
100
+ NEIGHBOR_SCAN_FILE_LIMIT,
101
+ POINTER_TO_DELEGATE_PATTERN,
93
102
  PYTHON_MODULE_FILE_SUFFIX,
94
103
  SINGLE_DICT_KEY_COUNT_FOR_PLURAL_CARDINALITY_DRIFT,
104
+ TOKEN_WORD_PATTERN,
95
105
  TYPE_CHECKING_IDENTIFIER_MARKER,
96
106
  WORD_BOUNDARY_REGEX,
97
107
  ZIPFILE_ALLOW_ZIP64_KEYWORD,
@@ -1128,14 +1138,48 @@ def _distinctive_name_tokens(check_name: str) -> list[str]:
1128
1138
  ]
1129
1139
 
1130
1140
 
1131
- def _docstring_mentions_check(docstring_text: str, check_name: str) -> bool:
1141
+ def _shared_sibling_name_tokens(all_check_names: list[str]) -> frozenset[str]:
1142
+ token_occurrence_count: dict[str, int] = {}
1143
+ for each_check_name in all_check_names:
1144
+ for each_token in set(each_token.lower() for each_token in _name_tokens(each_check_name)):
1145
+ token_occurrence_count[each_token] = token_occurrence_count.get(each_token, 0) + 1
1146
+ return frozenset(
1147
+ each_token
1148
+ for each_token, each_count in token_occurrence_count.items()
1149
+ if each_count >= MINIMUM_SIBLING_OCCURRENCES_FOR_SHARED_TOKEN
1150
+ )
1151
+
1152
+
1153
+ def _module_distinctive_name_tokens(
1154
+ check_name: str, all_shared_sibling_tokens: frozenset[str]
1155
+ ) -> list[str]:
1156
+ return [
1157
+ each_token
1158
+ for each_token in _distinctive_name_tokens(check_name)
1159
+ if each_token.lower() not in all_shared_sibling_tokens
1160
+ ]
1161
+
1162
+
1163
+ def _token_present_in_text(token: str, lowered_text: str) -> bool:
1164
+ lowered_token = token.lower()
1165
+ if lowered_token in lowered_text:
1166
+ return True
1167
+ return lowered_token.endswith("s") and lowered_token[:-1] in lowered_text
1168
+
1169
+
1170
+ def _docstring_mentions_check(
1171
+ docstring_text: str, check_name: str, all_shared_sibling_tokens: frozenset[str]
1172
+ ) -> bool:
1132
1173
  lowered_docstring = docstring_text.lower()
1133
1174
  if check_name.lower() in lowered_docstring:
1134
1175
  return True
1135
- distinctive_tokens = _distinctive_name_tokens(check_name)
1136
- if not distinctive_tokens:
1176
+ distinguishing_tokens = _module_distinctive_name_tokens(check_name, all_shared_sibling_tokens)
1177
+ if not distinguishing_tokens:
1137
1178
  return True
1138
- return any(each_token.lower() in lowered_docstring for each_token in distinctive_tokens)
1179
+ return any(
1180
+ _token_present_in_text(each_token, lowered_docstring)
1181
+ for each_token in distinguishing_tokens
1182
+ )
1139
1183
 
1140
1184
 
1141
1185
  def check_module_docstring_names_public_checks(content: str, file_path: str) -> list[str]:
@@ -1146,10 +1190,17 @@ def check_module_docstring_names_public_checks(content: str, file_path: str) ->
1146
1190
  roster. When the module grows a public ``check_*`` entry point the summary
1147
1191
  never names, the enumeration under-describes the module — the
1148
1192
  docstring-prose-vs-implementation drift the repo flags as Category O6/O8.
1149
- A check counts as named when the full ``check_*`` name, or any distinctive
1150
- (non-generic) underscore-separated token of it, appears in the summary;
1151
- generic tokens (``check``, ``test``, ``tests``) do not count. A module with
1152
- two or more public checks and any check the summary never names is reported
1193
+ A check counts as named when the full ``check_*`` name, or a
1194
+ module-distinctive underscore-separated token of it, appears in the summary
1195
+ (matched allowing a trailing-``s`` plural). A token is module-distinctive
1196
+ when it is non-generic (generic tokens ``check``, ``test``, ``tests`` never
1197
+ count) and it is not shared by two or more of the module's check names — a
1198
+ token such as ``string`` or ``magic`` that recurs across sibling checks is
1199
+ no evidence that any one check is named, so a check whose only
1200
+ summary-present tokens are shared ones is reported. A check with no
1201
+ module-distinctive token is treated as named, since the summary cannot single
1202
+ it out. A module with two or more public checks and any check the summary
1203
+ never names is reported
1153
1204
  so the summary names the full roster. Modules with a multi-paragraph
1154
1205
  docstring body are left to the audit lane, since their prose can carry the
1155
1206
  roster without naming each check by name. This check covers hook
@@ -1175,9 +1226,10 @@ def check_module_docstring_names_public_checks(content: str, file_path: str) ->
1175
1226
  public_check_names = _module_public_check_names(parsed_tree)
1176
1227
  if len(public_check_names) < MINIMUM_PUBLIC_CHECKS_FOR_MODULE_DOCSTRING_ROSTER:
1177
1228
  return []
1229
+ all_shared_sibling_tokens = _shared_sibling_name_tokens(public_check_names)
1178
1230
  issues: list[str] = []
1179
1231
  for each_name in public_check_names:
1180
- if _docstring_mentions_check(module_docstring, each_name):
1232
+ if _docstring_mentions_check(module_docstring, each_name, all_shared_sibling_tokens):
1181
1233
  continue
1182
1234
  issues.append(
1183
1235
  f"Line 1: module docstring omits public check {each_name}() — name every "
@@ -2513,14 +2565,71 @@ def _is_narrative_cut_header(stripped_line: str) -> bool:
2513
2565
  )
2514
2566
 
2515
2567
 
2516
- def _docstring_narrative_text(docstring_text: str) -> str:
2517
- narrative_lines: list[str] = []
2518
- for each_line in docstring_text.splitlines():
2519
- stripped_line = each_line.strip()
2568
+ def _docstring_narrative_partition(docstring_text: str) -> tuple[list[str], bool]:
2569
+ """Split a docstring narrative into its prose lines and whether it shows an example.
2570
+
2571
+ Walk the narrative up to the first ``Args:``/``Returns:`` cut header and sort
2572
+ every line into one of two buckets::
2573
+
2574
+ Render the report so the reader sees how it ended. <- prose line
2575
+ A finished run versus an interrupted one:: <- prose line (opener)
2576
+ theme_42 -- interrupted -> marked in-flight -- indented example body
2577
+ OK: a clean run reads differently in the log -- indented example body
2578
+ The reader reads the final outcome at a glance. <- prose line
2579
+
2580
+ A ``>>>`` doctest region and a ``::`` block with an indented body both count as
2581
+ an illustration, so their lines never join the prose bucket. The caller reads
2582
+ the prose count to size the wall and the flag to know whether an example is present.
2583
+ """
2584
+ prose_lines: list[str] = []
2585
+ has_illustration = False
2586
+ all_lines = docstring_text.splitlines()
2587
+ total_lines = len(all_lines)
2588
+ line_index = 0
2589
+ while line_index < total_lines:
2590
+ raw_line = all_lines[line_index]
2591
+ stripped_line = raw_line.strip()
2520
2592
  if _is_narrative_cut_header(stripped_line):
2521
2593
  break
2522
- narrative_lines.append(stripped_line)
2523
- return " ".join(narrative_lines)
2594
+ if stripped_line.startswith(">>>"):
2595
+ has_illustration = True
2596
+ line_index += 1
2597
+ while line_index < total_lines:
2598
+ inner_stripped = all_lines[line_index].strip()
2599
+ if not inner_stripped or _is_narrative_cut_header(inner_stripped):
2600
+ break
2601
+ line_index += 1
2602
+ continue
2603
+ if stripped_line.endswith("::"):
2604
+ opener_indent = len(raw_line) - len(raw_line.lstrip())
2605
+ probe_index = line_index + 1
2606
+ has_indented_body = False
2607
+ while probe_index < total_lines:
2608
+ probe_raw = all_lines[probe_index]
2609
+ probe_stripped = probe_raw.strip()
2610
+ if not probe_stripped:
2611
+ probe_index += 1
2612
+ continue
2613
+ probe_indent = len(probe_raw) - len(probe_raw.lstrip())
2614
+ if probe_indent > opener_indent:
2615
+ has_indented_body = True
2616
+ probe_index += 1
2617
+ continue
2618
+ break
2619
+ if has_indented_body:
2620
+ has_illustration = True
2621
+ prose_lines.append(stripped_line)
2622
+ line_index = probe_index
2623
+ continue
2624
+ if stripped_line:
2625
+ prose_lines.append(stripped_line)
2626
+ line_index += 1
2627
+ return prose_lines, has_illustration
2628
+
2629
+
2630
+ def _docstring_narrative_text(docstring_text: str) -> str:
2631
+ prose_lines, _has_illustration = _docstring_narrative_partition(docstring_text)
2632
+ return DOCSTRING_NARRATIVE_LINE_JOIN_SEPARATOR.join(prose_lines)
2524
2633
 
2525
2634
 
2526
2635
  def _sentence_word_count(sentence_text: str) -> int:
@@ -2626,6 +2735,56 @@ def check_docstring_runon_sentence(content: str, file_path: str) -> list[str]:
2626
2735
  return issues[:MAX_DOCSTRING_RUNON_SENTENCE_ISSUES]
2627
2736
 
2628
2737
 
2738
+ def check_docstring_prose_wall_without_illustration(content: str, file_path: str) -> list[str]:
2739
+ """Flag a summary that tells for many sentences and shows nothing.
2740
+
2741
+ A reader trusts the opening to paint a scene. A run of short sentences with
2742
+ no worked example leaves the reader piecing that scene together alone::
2743
+
2744
+ A calm voyage ends well for every vessel. <- one more line
2745
+ A halted voyage marks the vessel it neared. <- and another
2746
+ ... more like these, no worked example ... flag: a wall, no scene
2747
+ A calm voyage versus a halted one:: ok: a worked example
2748
+ a lone vessel -- halted -> marked mid-voyage
2749
+
2750
+ Past the prose-line limit with no ``::`` listing and no ``>>>`` doctest, this
2751
+ fires. A narrative that shows a worked example, or one at the limit, passes.
2752
+
2753
+ Args:
2754
+ content: The source text to inspect.
2755
+ file_path: The path the source will be written to, used for exemptions.
2756
+
2757
+ Returns:
2758
+ One issue per summary that runs a wall of sentences with no worked
2759
+ example, capped at the issue limit for the rule.
2760
+ """
2761
+ if is_test_file(file_path) or is_hook_infrastructure(file_path):
2762
+ return []
2763
+ try:
2764
+ parsed_tree = ast.parse(content)
2765
+ except SyntaxError:
2766
+ return []
2767
+ issues: list[str] = []
2768
+ for each_line_number, each_label, each_docstring in _documentable_docstring_targets(
2769
+ parsed_tree
2770
+ ):
2771
+ prose_lines, has_illustration = _docstring_narrative_partition(each_docstring)
2772
+ if has_illustration:
2773
+ continue
2774
+ prose_line_count = len(prose_lines)
2775
+ if prose_line_count <= DOCSTRING_NARRATIVE_PROSE_LINE_LIMIT:
2776
+ continue
2777
+ issues.append(
2778
+ f"Line {each_line_number}: {each_label} summary runs {prose_line_count} "
2779
+ "narrative lines with no worked example - show, don't tell: swap the wall for a "
2780
+ "'::' listing (a sample input, an annotated outcome, ok/flag contrast rows) and "
2781
+ "keep the narrative to a few short lines (plain-illustrative-docstrings)"
2782
+ )
2783
+ if len(issues) >= MAX_DOCSTRING_PROSE_WALL_ISSUES:
2784
+ break
2785
+ return issues[:MAX_DOCSTRING_PROSE_WALL_ISSUES]
2786
+
2787
+
2629
2788
  def _raises_section_text(docstring_text: str) -> str:
2630
2789
  docstring_lines = docstring_text.splitlines()
2631
2790
  raises_section_lines: list[str] = []
@@ -2874,3 +3033,257 @@ def check_docstring_raises_unraisable_largezipfile(
2874
3033
  if len(issues) >= MAX_DOCSTRING_RAISES_LARGEZIPFILE_ISSUES:
2875
3034
  break
2876
3035
  return issues[:MAX_DOCSTRING_RAISES_LARGEZIPFILE_ISSUES]
3036
+
3037
+
3038
+ def _leading_summary_line(raw_docstring: str) -> str:
3039
+ summary_paragraph = raw_docstring.strip().split("\n\n", maxsplit=1)[0]
3040
+ return " ".join(summary_paragraph.split())
3041
+
3042
+
3043
+ def _pointer_target_stem(raw_docstring: str) -> str:
3044
+ marker_match = POINTER_TO_DELEGATE_PATTERN.search(raw_docstring)
3045
+ return marker_match.group(1) if marker_match is not None else ""
3046
+
3047
+
3048
+ def _listed_entries(wrapper_summary: str) -> list[str]:
3049
+ marker_match = POINTER_TO_DELEGATE_PATTERN.search(wrapper_summary)
3050
+ if marker_match is None:
3051
+ return []
3052
+ listed_text = wrapper_summary[: marker_match.start()].strip().rstrip(";,").strip()
3053
+ normalized_text = re.sub(r"\s+and\s+", ", ", listed_text, flags=re.IGNORECASE)
3054
+ return [
3055
+ each_entry.strip()
3056
+ for each_entry in normalized_text.split(",")
3057
+ if each_entry.strip()
3058
+ ]
3059
+
3060
+
3061
+ def _words_of_entry(raw_entry: str) -> list[str]:
3062
+ return [
3063
+ each_token.lower()
3064
+ for each_token in TOKEN_WORD_PATTERN.findall(raw_entry)
3065
+ if each_token.lower() not in ALL_DELEGATION_ENUMERATION_STOP_TOKENS
3066
+ ]
3067
+
3068
+
3069
+ def _matches_any_word(delegate_summary: str, all_distinct_words: list[str]) -> bool:
3070
+ lowered_summary = delegate_summary.lower()
3071
+ return any(
3072
+ re.search(
3073
+ WORD_BOUNDARY_REGEX + re.escape(each_token) + WORD_BOUNDARY_REGEX,
3074
+ lowered_summary,
3075
+ )
3076
+ is not None
3077
+ for each_token in all_distinct_words
3078
+ )
3079
+
3080
+
3081
+ def _absent_entries(wrapper_summary: str, delegate_summary: str) -> list[str]:
3082
+ all_entries = _listed_entries(wrapper_summary)
3083
+ if len(all_entries) < MINIMUM_DELEGATION_ENUMERATION_ITEMS:
3084
+ return []
3085
+ if "," not in delegate_summary:
3086
+ return []
3087
+ absent_entry_texts: list[str] = []
3088
+ for each_entry in all_entries:
3089
+ distinct_words = _words_of_entry(each_entry)
3090
+ if not distinct_words:
3091
+ continue
3092
+ if _matches_any_word(delegate_summary, distinct_words):
3093
+ continue
3094
+ absent_entry_texts.append(each_entry)
3095
+ return absent_entry_texts
3096
+
3097
+
3098
+ def _top_level_definitions_and_methods(
3099
+ parsed_tree: ast.Module,
3100
+ ) -> list[ast.FunctionDef | ast.AsyncFunctionDef]:
3101
+ all_definitions_and_methods: list[ast.FunctionDef | ast.AsyncFunctionDef] = []
3102
+ for each_node in parsed_tree.body:
3103
+ if isinstance(each_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
3104
+ all_definitions_and_methods.append(each_node)
3105
+ elif isinstance(each_node, ast.ClassDef):
3106
+ all_definitions_and_methods.extend(
3107
+ each_member
3108
+ for each_member in each_node.body
3109
+ if isinstance(each_member, (ast.FunctionDef, ast.AsyncFunctionDef))
3110
+ )
3111
+ return all_definitions_and_methods
3112
+
3113
+
3114
+ def _entries_by_function_name(parsed_tree: ast.Module) -> dict[str, tuple[int, str]]:
3115
+ entries_by_name: dict[str, tuple[int, str]] = {}
3116
+ ambiguous_names: set[str] = set()
3117
+ for each_definition in _top_level_definitions_and_methods(parsed_tree):
3118
+ if each_definition.name in entries_by_name:
3119
+ ambiguous_names.add(each_definition.name)
3120
+ continue
3121
+ entries_by_name[each_definition.name] = (
3122
+ each_definition.lineno,
3123
+ ast.get_docstring(each_definition) or "",
3124
+ )
3125
+ for each_ambiguous_name in ambiguous_names:
3126
+ del entries_by_name[each_ambiguous_name]
3127
+ return entries_by_name
3128
+
3129
+
3130
+ def _source_text_or_empty(source_path: Path) -> str:
3131
+ try:
3132
+ return source_path.read_text(encoding="utf-8")
3133
+ except (OSError, UnicodeDecodeError):
3134
+ return ""
3135
+
3136
+
3137
+ def _parsed_module_or_none(source_text: str) -> ast.Module | None:
3138
+ if not source_text:
3139
+ return None
3140
+ try:
3141
+ return ast.parse(source_text)
3142
+ except SyntaxError:
3143
+ return None
3144
+
3145
+
3146
+ def _outbound_pointer_issues(parsed_tree: ast.Module, file_path: str) -> list[str]:
3147
+ parent_directory = Path(file_path).parent
3148
+ issues: list[str] = []
3149
+ delegate_entries_by_stem: dict[str, dict[str, tuple[int, str]]] = {}
3150
+ for each_node in _top_level_definitions_and_methods(parsed_tree):
3151
+ wrapper_docstring = ast.get_docstring(each_node) or ""
3152
+ wrapper_summary = _leading_summary_line(wrapper_docstring)
3153
+ target_stem = _pointer_target_stem(wrapper_summary)
3154
+ if not target_stem:
3155
+ continue
3156
+ if target_stem not in delegate_entries_by_stem:
3157
+ delegate_path = parent_directory / (target_stem + PYTHON_MODULE_FILE_SUFFIX)
3158
+ delegate_tree = _parsed_module_or_none(_source_text_or_empty(delegate_path))
3159
+ delegate_entries_by_stem[target_stem] = (
3160
+ _entries_by_function_name(delegate_tree)
3161
+ if delegate_tree is not None
3162
+ else {}
3163
+ )
3164
+ delegate_entry = delegate_entries_by_stem[target_stem].get(each_node.name)
3165
+ if delegate_entry is None:
3166
+ continue
3167
+ delegate_summary = _leading_summary_line(delegate_entry[1])
3168
+ for each_entry in _absent_entries(wrapper_summary, delegate_summary):
3169
+ issues.append(
3170
+ f"Line {each_node.lineno}: {each_node.name}() names "
3171
+ f"'{each_entry}' in its summary, but the summary of the "
3172
+ f"same-named function in {target_stem} omits it - align the "
3173
+ "two summaries (Category O6 docstring-vs-implementation drift)"
3174
+ )
3175
+ return issues
3176
+
3177
+
3178
+ def _scannable_neighbor_paths(written_path: Path) -> list[Path]:
3179
+ try:
3180
+ all_neighbor_paths = sorted(
3181
+ written_path.parent.glob("*" + PYTHON_MODULE_FILE_SUFFIX)
3182
+ )
3183
+ except OSError:
3184
+ return []
3185
+ scannable_paths: list[Path] = []
3186
+ for each_path in all_neighbor_paths:
3187
+ if each_path.name == written_path.name:
3188
+ continue
3189
+ if is_strict_test_file(str(each_path)):
3190
+ continue
3191
+ scannable_paths.append(each_path)
3192
+ if len(scannable_paths) >= NEIGHBOR_SCAN_FILE_LIMIT:
3193
+ break
3194
+ return scannable_paths
3195
+
3196
+
3197
+ def _inbound_pointer_issues(
3198
+ neighbor_tree: ast.Module,
3199
+ neighbor_name: str,
3200
+ delegate_stem: str,
3201
+ all_entries_by_name: dict[str, tuple[int, str]],
3202
+ ) -> list[str]:
3203
+ issues: list[str] = []
3204
+ for each_node in _top_level_definitions_and_methods(neighbor_tree):
3205
+ wrapper_docstring = ast.get_docstring(each_node) or ""
3206
+ wrapper_summary = _leading_summary_line(wrapper_docstring)
3207
+ if _pointer_target_stem(wrapper_summary) != delegate_stem:
3208
+ continue
3209
+ delegate_entry = all_entries_by_name.get(each_node.name)
3210
+ if delegate_entry is None:
3211
+ continue
3212
+ delegate_summary = _leading_summary_line(delegate_entry[1])
3213
+ for each_entry in _absent_entries(wrapper_summary, delegate_summary):
3214
+ issues.append(
3215
+ f"Line {delegate_entry[0]}: {each_node.name}() summary omits "
3216
+ f"'{each_entry}', named by the pointing wrapper docstring in "
3217
+ f"{neighbor_name} - reword that wrapper docstring in the same "
3218
+ "change (Category O6 docstring-vs-implementation drift)"
3219
+ )
3220
+ return issues
3221
+
3222
+
3223
+ def _neighbor_scan_issues(parsed_tree: ast.Module, file_path: str) -> list[str]:
3224
+ written_path = Path(file_path)
3225
+ all_entries_by_name = _entries_by_function_name(parsed_tree)
3226
+ if not all_entries_by_name:
3227
+ return []
3228
+ issues: list[str] = []
3229
+ for each_neighbor in _scannable_neighbor_paths(written_path):
3230
+ neighbor_source = _source_text_or_empty(each_neighbor)
3231
+ if POINTER_TO_DELEGATE_PATTERN.search(neighbor_source) is None:
3232
+ continue
3233
+ neighbor_tree = _parsed_module_or_none(neighbor_source)
3234
+ if neighbor_tree is None:
3235
+ continue
3236
+ issues.extend(
3237
+ _inbound_pointer_issues(
3238
+ neighbor_tree,
3239
+ each_neighbor.name,
3240
+ written_path.stem,
3241
+ all_entries_by_name,
3242
+ )
3243
+ )
3244
+ return issues
3245
+
3246
+
3247
+ def check_docstring_delegation_summary_enumeration_drift(
3248
+ content: str, file_path: str
3249
+ ) -> list[str]:
3250
+ """Flag a delegating wrapper summary that its delegation target contradicts.
3251
+
3252
+ The drift this catches: a thin wrapper method whose docstring summary
3253
+ enumerates the actions it stands for and points at the home of the real
3254
+ body ("Apply App Info, Russia, review note, publication edits; full doc
3255
+ on `listing_edit_flow`"), while the same-named function in that named
3256
+ sibling file carries a summary enumeration that omits one of those
3257
+ actions. An edit that moves one action out of the delegated body and
3258
+ rewords only the delegated docstring leaves the wrapper claiming work the
3259
+ body skips. The gate fires from both sides. On a wrapper save, it resolves
3260
+ the named sibling file beside the saved one, finds the same-named
3261
+ function, and compares the two summary enumerations. On a delegated-body
3262
+ save, it scans neighboring .py files for a wrapper docstring pointing at
3263
+ the saved stem and runs the same comparison, so the save that removes the
3264
+ action hears about the stranded wrapper docstring. A listed action counts
3265
+ as named when any of its distinctive words appears at a word boundary in
3266
+ the delegated summary. The comparison binds when the wrapper enumerates
3267
+ two or more actions and the delegated summary itself holds a comma, so a
3268
+ plain one-purpose delegated summary never gets compared. This covers the
3269
+ deterministic delegating-wrapper slice of Category O6
3270
+ docstring-prose-vs-implementation drift.
3271
+
3272
+ Args:
3273
+ content: The source text to inspect.
3274
+ file_path: The path the source will be written to, used for exemptions
3275
+ and to resolve the delegated or wrapping file beside it.
3276
+
3277
+ Returns:
3278
+ One issue per listed wrapper action the paired delegated summary
3279
+ omits, capped at the module limit.
3280
+ """
3281
+ if is_strict_test_file(file_path) or is_hook_infrastructure(file_path):
3282
+ return []
3283
+ try:
3284
+ parsed_tree = ast.parse(content)
3285
+ except SyntaxError:
3286
+ return []
3287
+ issues = _outbound_pointer_issues(parsed_tree, file_path)
3288
+ issues.extend(_neighbor_scan_issues(parsed_tree, file_path))
3289
+ return issues[:MAX_DOCSTRING_DELEGATION_ENUMERATION_ISSUES]
@@ -51,6 +51,7 @@ from code_rules_comments import ( # noqa: E402
51
51
  check_comment_changes,
52
52
  )
53
53
  from code_rules_constants_config import ( # noqa: E402
54
+ check_config_duplicate_path_anchor,
54
55
  check_constants_outside_config,
55
56
  check_constants_outside_config_advisory,
56
57
  check_file_global_constants_use_count,
@@ -75,6 +76,7 @@ from code_rules_docstrings import ( # noqa: E402
75
76
  check_docstring_args_match_signature,
76
77
  check_docstring_args_single_line_scope_vs_span,
77
78
  check_docstring_cardinal_count_matches_constant_family,
79
+ check_docstring_delegation_summary_enumeration_drift,
78
80
  check_docstring_documents_unreferenced_parameter,
79
81
  check_docstring_fallback_branch_coverage,
80
82
  check_docstring_field_runmode_outcome,
@@ -85,6 +87,7 @@ from code_rules_docstrings import ( # noqa: E402
85
87
  check_docstring_no_consumer_claim,
86
88
  check_docstring_no_inline_literal_claim,
87
89
  check_docstring_no_network_claim_with_metadata_access,
90
+ check_docstring_prose_wall_without_illustration,
88
91
  check_docstring_punctuation_mark_enumeration_coverage,
89
92
  check_docstring_raises_unraisable_largezipfile,
90
93
  check_docstring_returns_plural_cardinality,
@@ -105,11 +108,15 @@ from code_rules_imports_logging import ( # noqa: E402
105
108
  check_e2e_test_naming,
106
109
  check_import_block_sorted,
107
110
  check_imports_at_top,
111
+ check_js_bare_flag_return_directive,
108
112
  check_js_resume_task_enumeration_coverage,
109
113
  check_js_returns_object_schemaless_branch,
114
+ check_js_sibling_return_object_key_drift,
110
115
  check_library_print,
116
+ check_logging_adjacent_string_literals,
111
117
  check_logging_fstrings,
112
118
  check_logging_printf_tokens,
119
+ check_naive_datetime_construction,
113
120
  check_windows_api_none,
114
121
  )
115
122
  from code_rules_js_conventions import ( # noqa: E402
@@ -127,6 +134,7 @@ from code_rules_naming_collection import ( # noqa: E402
127
134
  check_collection_prefix,
128
135
  check_loop_variable_naming,
129
136
  check_polarity_name_contradiction,
137
+ check_referenced_underscore_loop_variable,
130
138
  check_stuttering_collection_prefix,
131
139
  )
132
140
  from code_rules_optional_params import ( # noqa: E402
@@ -154,6 +162,7 @@ from code_rules_shared import ( # noqa: E402
154
162
  from code_rules_string_magic import ( # noqa: E402
155
163
  check_inline_literal_collections,
156
164
  check_inline_tuple_string_magic,
165
+ check_join_separator_string_magic,
157
166
  check_string_literal_magic,
158
167
  check_whitespace_indentation_magic,
159
168
  )
@@ -268,10 +277,13 @@ def validate_content(
268
277
  )
269
278
  all_issues.extend(check_logging_fstrings(content))
270
279
  all_issues.extend(check_logging_printf_tokens(content, file_path))
280
+ all_issues.extend(check_logging_adjacent_string_literals(content, file_path))
271
281
  all_issues.extend(check_windows_api_none(content))
282
+ all_issues.extend(check_naive_datetime_construction(content, file_path))
272
283
  all_issues.extend(check_magic_values(content, file_path))
273
284
  all_issues.extend(check_fstring_structural_literals(content, file_path))
274
285
  all_issues.extend(check_constants_outside_config(content, file_path))
286
+ all_issues.extend(check_config_duplicate_path_anchor(content, file_path))
275
287
  all_issues.extend(check_constants_outside_config_advisory(content, file_path))
276
288
  all_issues.extend(check_file_global_constants_use_count(content, file_path))
277
289
  all_issues.extend(
@@ -340,6 +352,9 @@ def validate_content(
340
352
  check_class_docstring_names_public_methods(effective_content, file_path)
341
353
  )
342
354
  all_issues.extend(check_docstring_runon_sentence(effective_content, file_path))
355
+ all_issues.extend(
356
+ check_docstring_prose_wall_without_illustration(effective_content, file_path)
357
+ )
343
358
  all_issues.extend(
344
359
  check_module_docstring_names_public_checks(effective_content, file_path)
345
360
  )
@@ -389,6 +404,11 @@ def validate_content(
389
404
  all_issues.extend(
390
405
  check_docstring_args_single_line_scope_vs_span(effective_content, file_path)
391
406
  )
407
+ all_issues.extend(
408
+ check_docstring_delegation_summary_enumeration_drift(
409
+ effective_content, file_path
410
+ )
411
+ )
392
412
  all_issues.extend(
393
413
  check_boolean_naming(
394
414
  effective_content,
@@ -477,9 +497,11 @@ def validate_content(
477
497
  )
478
498
  )
479
499
  all_issues.extend(check_loop_variable_naming(content, file_path))
500
+ all_issues.extend(check_referenced_underscore_loop_variable(content, file_path))
480
501
  all_issues.extend(check_polarity_name_contradiction(content, file_path))
481
502
  all_issues.extend(check_inline_literal_collections(content, file_path))
482
503
  all_issues.extend(check_inline_tuple_string_magic(content, file_path))
504
+ all_issues.extend(check_join_separator_string_magic(content, file_path))
483
505
  all_issues.extend(check_string_literal_magic(content, file_path))
484
506
  all_issues.extend(check_whitespace_indentation_magic(content, file_path))
485
507
  all_issues.extend(check_orphan_css_classes(effective_content, file_path))
@@ -513,6 +535,17 @@ def validate_content(
513
535
  all_issues.extend(
514
536
  check_js_returns_object_schemaless_branch(content, file_path)
515
537
  )
538
+ all_issues.extend(
539
+ check_js_sibling_return_object_key_drift(content, file_path)
540
+ )
541
+ all_issues.extend(
542
+ check_js_bare_flag_return_directive(
543
+ effective_content,
544
+ file_path,
545
+ all_changed_lines,
546
+ defer_scope_to_caller,
547
+ )
548
+ )
516
549
 
517
550
  if extension in ALL_CODE_EXTENSIONS:
518
551
  advise_file_line_count(content, file_path)