claude-dev-env 2.28.1 → 2.30.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/pr-description-writer.md +21 -6
- package/.agents/agents/reference/README.md +6 -0
- package/.agents/agents/reference/pr-description-illustrative-voice.md +45 -0
- package/.agents/agents/reference/pr-description-verification.md +19 -0
- package/.agents/agents/test_agent_frontmatter.py +3 -1
- package/.agents/agents/test_pr_description_writer_style.py +47 -0
- 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/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/code-rules-gate.md +1 -1
- package/_shared/pr-loop/scripts/AGENTS.md +0 -49
- package/_shared/pr-loop/scripts/code_rules_gate.py +2 -2
- package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +0 -41
- package/_shared/pr-loop/scripts/code_rules_gate_parts/enforcer_loading.py +8 -8
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_enforcer_loading.py +2 -2
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +1 -1
- 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/pr-loop/scripts/tests/test_code_rules_gate.py +15 -15
- 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/prompts/AGENTS.md +0 -36
- 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 +10 -0
- package/docs/references/AGENTS.md +0 -16
- package/hooks/AGENTS.md +0 -27
- package/hooks/advisory/AGENTS.md +0 -15
- package/hooks/blocking/AGENTS.md +0 -112
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +0 -27
- package/hooks/blocking/code_rules_enforcer.py +433 -313
- package/hooks/blocking/config/AGENTS.md +0 -9
- 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/tdd_enforcer_parts/AGENTS.md +0 -29
- package/hooks/blocking/test_code_rules_enforcer_banned_noun_word.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_class_docstring_methods.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +117 -22
- package/hooks/blocking/test_code_rules_enforcer_docstring_args_signature.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_docstring_prose_wall_illustration.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_docstring_runon_sentence.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_docstring_unreferenced_param.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_fstring_scan.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_ignored_must_check_return.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_inline_tuple_string_magic.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_join_separator_magic.py +2 -2
- package/hooks/blocking/test_code_rules_enforcer_js_returns_object.py +3 -3
- package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_naming_pattern.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +117 -38
- package/hooks/blocking/test_code_rules_enforcer_output_snapshot.py +121 -0
- package/hooks/blocking/test_code_rules_enforcer_precheck_forecast.py +17 -15
- package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +4 -2
- package/hooks/blocking/test_code_rules_enforcer_split_entry_2.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_stage.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_string_magic.py +4 -4
- package/hooks/blocking/test_code_rules_enforcer_thin_wrapper_files.py +2 -2
- package/hooks/blocking/test_code_rules_enforcer_zero_payload_alias.py +2 -2
- package/hooks/blocking/test_code_rules_enforcer_zero_payload_alias_hook_routing.py +2 -2
- package/hooks/blocking/test_code_rules_validation_phase.py +281 -0
- package/hooks/blocking/test_fable_spawn_gate.py +1 -1
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +26 -11
- 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 -99
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +8 -0
- package/hooks/hooks_constants/test_code_rules_enforcer_constants.py +21 -0
- package/hooks/hooks_constants/validation_phase_constants.py +34 -0
- 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/validators/README.md +1 -1
- package/hooks/validators/config/hook_timing_harness_constants.py +16 -0
- package/hooks/validators/hook_timing_harness.py +287 -0
- package/hooks/validators/test_hook_timing_harness.py +251 -0
- package/hooks/workflow/AGENTS.md +0 -15
- package/output-styles/AGENTS.md +0 -14
- package/package.json +1 -1
- package/rules/AGENTS.md +0 -55
- package/scripts/AGENTS.md +0 -55
- package/scripts/dev_env_scripts_constants/AGENTS.md +0 -20
- package/scripts/sync_to_cursor/AGENTS.md +0 -22
- package/scripts/tests/AGENTS.md +0 -34
- package/system-prompts/AGENTS.md +0 -24
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
This entry module reads a PreToolUse JSON payload on stdin, reconstructs the
|
|
5
5
|
post-edit content, and runs every applicable check. The individual checks live
|
|
6
6
|
in focused ``code_rules_<concern>.py`` sibling modules; this module imports the
|
|
7
|
-
ones ``
|
|
7
|
+
ones ``validate_content_for_phase`` calls and orchestrates them.
|
|
8
8
|
|
|
9
9
|
Advisory only (non-blocking):
|
|
10
10
|
- File line count: stderr warning at 400 lines (soft) and 1000 lines (hard)
|
|
@@ -21,7 +21,7 @@ import sys
|
|
|
21
21
|
from collections import Counter
|
|
22
22
|
from collections.abc import Callable
|
|
23
23
|
from pathlib import Path
|
|
24
|
-
from typing import TextIO
|
|
24
|
+
from typing import NamedTuple, TextIO
|
|
25
25
|
|
|
26
26
|
_BLOCKING_DIRECTORY = str(Path(__file__).resolve().parent)
|
|
27
27
|
_HOOKS_DIRECTORY = str(Path(__file__).resolve().parent.parent)
|
|
@@ -178,9 +178,13 @@ from hooks_constants.code_rules_enforcer_constants import ( # noqa: E402
|
|
|
178
178
|
ALL_CODE_EXTENSIONS,
|
|
179
179
|
ALL_JAVASCRIPT_EXTENSIONS,
|
|
180
180
|
ALL_PYTHON_EXTENSIONS,
|
|
181
|
+
ALL_VALIDATION_PHASES,
|
|
181
182
|
DENY_REASON_ISSUE_PREVIEW_COUNT,
|
|
183
|
+
EDIT_LANE_PHASE,
|
|
184
|
+
FULL_GATE_PHASE,
|
|
182
185
|
PRECHECK_USAGE_EXIT_CODE,
|
|
183
186
|
PRECHECK_USAGE_MESSAGE,
|
|
187
|
+
UNKNOWN_VALIDATION_PHASE_MESSAGE_TEMPLATE,
|
|
184
188
|
VIOLATION_SEPARATOR,
|
|
185
189
|
)
|
|
186
190
|
from hooks_constants.hook_block_logger import log_hook_block # noqa: E402
|
|
@@ -189,6 +193,30 @@ from hooks_constants.setup_project_paths_constants import ( # noqa: E402
|
|
|
189
193
|
)
|
|
190
194
|
|
|
191
195
|
|
|
196
|
+
class _ValidationContext(NamedTuple):
|
|
197
|
+
"""Bundle the parameters every extension-issue check function reads."""
|
|
198
|
+
|
|
199
|
+
content: str
|
|
200
|
+
old_content: str
|
|
201
|
+
effective_content: str
|
|
202
|
+
file_path: str
|
|
203
|
+
all_changed_lines: set[int] | None
|
|
204
|
+
defer_scope_to_caller: bool
|
|
205
|
+
sibling_directory: Path | None
|
|
206
|
+
phase: str
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _validated_phase(phase: str) -> str:
|
|
210
|
+
"""Return *phase* unchanged, after rejecting a phase outside ALL_VALIDATION_PHASES."""
|
|
211
|
+
if phase not in ALL_VALIDATION_PHASES:
|
|
212
|
+
raise ValueError(
|
|
213
|
+
UNKNOWN_VALIDATION_PHASE_MESSAGE_TEMPLATE.format(
|
|
214
|
+
phase=phase, all_phases=sorted(ALL_VALIDATION_PHASES)
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
return phase
|
|
218
|
+
|
|
219
|
+
|
|
192
220
|
def _codex_patch_issues(each_patch_file: CodexPatchFile) -> list[str]:
|
|
193
221
|
"""Run the existing code-rules verdict over one Codex patch view."""
|
|
194
222
|
if not each_patch_file.post_content and each_patch_file.operation == "delete":
|
|
@@ -199,9 +227,10 @@ def _codex_patch_issues(each_patch_file: CodexPatchFile) -> list[str]:
|
|
|
199
227
|
each_patch_file.file_path,
|
|
200
228
|
each_patch_file.post_content,
|
|
201
229
|
each_patch_file.prior_content,
|
|
230
|
+
phase=EDIT_LANE_PHASE,
|
|
202
231
|
)
|
|
203
232
|
elif _is_validated_target(each_patch_file.file_path):
|
|
204
|
-
all_issues =
|
|
233
|
+
all_issues = validate_content_for_edit_lane(
|
|
205
234
|
each_patch_file.post_content,
|
|
206
235
|
each_patch_file.file_path,
|
|
207
236
|
each_patch_file.prior_content,
|
|
@@ -259,314 +288,396 @@ def _report_codex_patch_payload(
|
|
|
259
288
|
)
|
|
260
289
|
|
|
261
290
|
|
|
262
|
-
def
|
|
263
|
-
|
|
264
|
-
file_path
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
defer_scope_to_caller: bool = False,
|
|
269
|
-
sibling_directory: Path | None = None,
|
|
270
|
-
) -> list[str]:
|
|
271
|
-
"""Run all applicable validators on content.
|
|
272
|
-
|
|
273
|
-
Args:
|
|
274
|
-
content: The new content being written. For Edit, this is the
|
|
275
|
-
``new_string`` fragment; for Write, the entire new file body.
|
|
276
|
-
file_path: Path to the file.
|
|
277
|
-
old_content: Previous content (old_string for Edit, existing file for Write).
|
|
278
|
-
Used to detect comment additions/removals instead of flagging all comments.
|
|
279
|
-
full_file_content: For Edit operations, the reconstructed post-edit
|
|
280
|
-
content of the entire file (existing file with ``old_string`` replaced
|
|
281
|
-
by ``new_string``). Whole-file checks such as the unused-import
|
|
282
|
-
scanner use this to evaluate references across the file rather than
|
|
283
|
-
just within the inserted fragment.
|
|
284
|
-
prior_full_file_content: For Edit operations, the entire file content as
|
|
285
|
-
it existed before the edit applied. Whole-file span checks
|
|
286
|
-
(function length, test isolation) diff this against
|
|
287
|
-
``full_file_content`` to recover the lines the edit touched, then
|
|
288
|
-
block only on violations whose source span intersects those lines —
|
|
289
|
-
mirroring the gate's span-intersection scoping. Defaults to the
|
|
290
|
-
empty string for Write and for gate invocations, which leaves those
|
|
291
|
-
checks scanning the whole file with no diff scoping.
|
|
292
|
-
defer_scope_to_caller: The explicit signal that a downstream scoper will
|
|
293
|
-
run, used to disambiguate the two callers that supply no changed-line
|
|
294
|
-
set. The commit/push gate passes True: it owns
|
|
295
|
-
``split_violations_by_scope`` and classifies blocking vs advisory by
|
|
296
|
-
added line, so the function-length, test-isolation, and banned-noun
|
|
297
|
-
checks return their violations unscoped for the gate to classify.
|
|
298
|
-
PreToolUse new-file or full-file writes leave this False: this
|
|
299
|
-
enforcer is terminal, so it marks every violation in scope.
|
|
300
|
-
sibling_directory: The absolute directory the cross-file duplicate-body
|
|
301
|
-
check scans for sibling modules. The commit/push gate passes the
|
|
302
|
-
resolved file's parent so the on-disk sibling scan stays anchored to
|
|
303
|
-
the repository regardless of the gate process's working directory.
|
|
304
|
-
None (the PreToolUse default) derives the directory from
|
|
305
|
-
``file_path``'s parent, which is already absolute on that path.
|
|
306
|
-
"""
|
|
307
|
-
extension = get_file_extension(file_path)
|
|
308
|
-
all_issues = []
|
|
309
|
-
effective_content = content if full_file_content is None else full_file_content
|
|
310
|
-
all_changed_lines = (
|
|
311
|
-
changed_line_numbers(prior_full_file_content, full_file_content)
|
|
312
|
-
if full_file_content is not None
|
|
313
|
-
else None
|
|
291
|
+
def _python_comment_and_logging_issues(context: _ValidationContext) -> list[str]:
|
|
292
|
+
"""Return the comment-diff and logging-hygiene checks for a Python file."""
|
|
293
|
+
content, old_content, file_path = (
|
|
294
|
+
context.content,
|
|
295
|
+
context.old_content,
|
|
296
|
+
context.file_path,
|
|
314
297
|
)
|
|
298
|
+
all_issues: list[str] = []
|
|
299
|
+
if not is_test_file(file_path):
|
|
300
|
+
all_issues.extend(check_comment_changes(old_content, content, file_path))
|
|
301
|
+
all_issues.extend(check_imports_at_top(content))
|
|
302
|
+
all_issues.extend(check_logging_fstrings(content))
|
|
303
|
+
all_issues.extend(check_logging_printf_tokens(content, file_path))
|
|
304
|
+
all_issues.extend(check_logging_adjacent_string_literals(content, file_path))
|
|
305
|
+
all_issues.extend(check_windows_api_none(content))
|
|
306
|
+
all_issues.extend(check_naive_datetime_construction(content, file_path))
|
|
307
|
+
return all_issues
|
|
315
308
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
defer_scope_to_caller,
|
|
332
|
-
)
|
|
333
|
-
)
|
|
334
|
-
all_issues.extend(
|
|
335
|
-
_fragment_or_deferred_check(
|
|
336
|
-
check_blast_radius_declared,
|
|
337
|
-
old_content,
|
|
338
|
-
content,
|
|
339
|
-
file_path,
|
|
340
|
-
defer_scope_to_caller,
|
|
341
|
-
)
|
|
342
|
-
)
|
|
343
|
-
all_issues.extend(check_fstring_structural_literals(content, file_path))
|
|
344
|
-
all_issues.extend(check_constants_outside_config(content, file_path))
|
|
345
|
-
all_issues.extend(check_config_duplicate_path_anchor(content, file_path))
|
|
346
|
-
all_issues.extend(check_constants_outside_config_advisory(content, file_path))
|
|
347
|
-
all_issues.extend(check_file_global_constants_use_count(content, file_path))
|
|
348
|
-
all_issues.extend(
|
|
349
|
-
check_duplicate_function_body_across_files(
|
|
350
|
-
effective_content,
|
|
351
|
-
file_path,
|
|
352
|
-
all_changed_lines,
|
|
353
|
-
defer_scope_to_caller,
|
|
354
|
-
sibling_directory,
|
|
355
|
-
)
|
|
356
|
-
)
|
|
357
|
-
all_issues.extend(
|
|
358
|
-
check_same_file_inline_duplicate_body(
|
|
359
|
-
effective_content,
|
|
360
|
-
file_path,
|
|
361
|
-
all_changed_lines,
|
|
362
|
-
defer_scope_to_caller,
|
|
363
|
-
)
|
|
364
|
-
)
|
|
365
|
-
all_issues.extend(check_type_escape_hatches(effective_content, file_path))
|
|
366
|
-
all_issues.extend(
|
|
367
|
-
check_banned_identifiers(
|
|
368
|
-
effective_content,
|
|
369
|
-
file_path,
|
|
370
|
-
all_changed_lines,
|
|
371
|
-
defer_scope_to_caller,
|
|
372
|
-
)
|
|
373
|
-
)
|
|
374
|
-
all_issues.extend(
|
|
375
|
-
check_banned_noun_word_boundary(
|
|
376
|
-
effective_content,
|
|
377
|
-
file_path,
|
|
378
|
-
all_changed_lines,
|
|
379
|
-
defer_scope_to_caller,
|
|
380
|
-
)
|
|
381
|
-
)
|
|
382
|
-
all_issues.extend(check_banned_prefixes(effective_content, file_path))
|
|
383
|
-
all_issues.extend(check_stub_implementations(effective_content, file_path))
|
|
384
|
-
all_issues.extend(check_typed_dict_encode_decode(effective_content, file_path))
|
|
385
|
-
all_issues.extend(check_test_branching_in_production(effective_content, file_path))
|
|
386
|
-
all_issues.extend(check_dead_test_module_constant(effective_content, file_path))
|
|
387
|
-
all_issues.extend(check_unused_test_helper_parameter(effective_content, file_path))
|
|
388
|
-
all_issues.extend(check_bare_except(effective_content, file_path))
|
|
389
|
-
all_issues.extend(check_thin_wrapper_files(effective_content, file_path))
|
|
390
|
-
all_issues.extend(check_zero_payload_function_alias(effective_content, file_path))
|
|
391
|
-
all_issues.extend(check_boundary_types(effective_content, file_path))
|
|
392
|
-
all_issues.extend(check_docstring_format(effective_content, file_path))
|
|
393
|
-
all_issues.extend(check_docstring_args_match_signature(effective_content, file_path))
|
|
394
|
-
all_issues.extend(
|
|
395
|
-
check_docstring_documents_unreferenced_parameter(effective_content, file_path)
|
|
396
|
-
)
|
|
397
|
-
all_issues.extend(
|
|
398
|
-
check_class_docstring_names_public_methods(effective_content, file_path)
|
|
399
|
-
)
|
|
400
|
-
all_issues.extend(
|
|
401
|
-
check_docstring_runon_sentence(
|
|
402
|
-
effective_content,
|
|
403
|
-
file_path,
|
|
404
|
-
all_changed_lines,
|
|
405
|
-
defer_scope_to_caller,
|
|
406
|
-
)
|
|
407
|
-
)
|
|
408
|
-
all_issues.extend(
|
|
409
|
-
check_docstring_prose_wall_without_illustration(
|
|
410
|
-
effective_content,
|
|
411
|
-
file_path,
|
|
412
|
-
all_changed_lines,
|
|
413
|
-
defer_scope_to_caller,
|
|
414
|
-
)
|
|
415
|
-
)
|
|
416
|
-
all_issues.extend(
|
|
417
|
-
check_module_docstring_names_public_checks(effective_content, file_path)
|
|
418
|
-
)
|
|
419
|
-
all_issues.extend(
|
|
420
|
-
check_module_docstring_scope_omits_data_schema_constants(
|
|
421
|
-
effective_content, file_path
|
|
422
|
-
)
|
|
423
|
-
)
|
|
424
|
-
all_issues.extend(
|
|
425
|
-
check_docstring_names_undefined_constant(effective_content, file_path)
|
|
426
|
-
)
|
|
427
|
-
all_issues.extend(
|
|
428
|
-
check_boolean_naming(
|
|
429
|
-
effective_content,
|
|
430
|
-
file_path,
|
|
431
|
-
all_changed_lines,
|
|
432
|
-
defer_scope_to_caller,
|
|
433
|
-
)
|
|
434
|
-
)
|
|
435
|
-
all_issues.extend(
|
|
436
|
-
check_ignored_must_check_return(
|
|
437
|
-
effective_content,
|
|
438
|
-
file_path,
|
|
439
|
-
all_changed_lines,
|
|
440
|
-
defer_scope_to_caller,
|
|
441
|
-
)
|
|
442
|
-
)
|
|
443
|
-
all_issues.extend(check_skip_decorators_in_tests(content, file_path))
|
|
444
|
-
all_issues.extend(
|
|
445
|
-
check_tests_use_isolated_filesystem_paths(
|
|
446
|
-
effective_content,
|
|
447
|
-
file_path,
|
|
448
|
-
all_changed_lines,
|
|
449
|
-
defer_scope_to_caller,
|
|
450
|
-
)
|
|
451
|
-
)
|
|
452
|
-
all_issues.extend(check_existence_check_tests(content, file_path))
|
|
453
|
-
all_issues.extend(check_constant_equality_tests(content, file_path))
|
|
454
|
-
all_issues.extend(check_vacuous_cleanup_assertion_tests(content, file_path))
|
|
455
|
-
all_issues.extend(check_stale_test_name_target(content, file_path))
|
|
456
|
-
check_flag_gated_scenario_test_naming(content, file_path)
|
|
457
|
-
all_issues.extend(check_unused_optional_parameters(content, file_path))
|
|
458
|
-
all_issues.extend(check_collection_prefix(content, file_path))
|
|
459
|
-
all_issues.extend(check_stuttering_collection_prefix(content, file_path))
|
|
460
|
-
all_issues.extend(check_hardcoded_user_paths(content, file_path))
|
|
461
|
-
all_issues.extend(check_sys_path_insert_deduplication_guard(content, file_path))
|
|
462
|
-
all_issues.extend(check_library_print(content, file_path))
|
|
463
|
-
all_issues.extend(check_parameter_annotations(content, file_path))
|
|
464
|
-
all_issues.extend(check_known_pytest_fixture_annotations(content, file_path))
|
|
465
|
-
all_issues.extend(
|
|
466
|
-
check_unused_known_pytest_fixture_parameters(content, file_path)
|
|
467
|
-
)
|
|
468
|
-
all_issues.extend(
|
|
469
|
-
check_return_annotations(
|
|
470
|
-
effective_content,
|
|
471
|
-
file_path,
|
|
472
|
-
all_changed_lines,
|
|
473
|
-
defer_scope_to_caller,
|
|
474
|
-
)
|
|
475
|
-
)
|
|
476
|
-
all_issues.extend(
|
|
477
|
-
check_function_length(
|
|
478
|
-
effective_content,
|
|
479
|
-
file_path,
|
|
480
|
-
all_changed_lines,
|
|
481
|
-
defer_scope_to_caller,
|
|
482
|
-
)
|
|
309
|
+
|
|
310
|
+
def _python_magic_value_and_constant_issues(context: _ValidationContext) -> list[str]:
|
|
311
|
+
"""Return the magic-value and constants-location checks for a Python file."""
|
|
312
|
+
old_content, content, file_path = (
|
|
313
|
+
context.old_content,
|
|
314
|
+
context.content,
|
|
315
|
+
context.file_path,
|
|
316
|
+
)
|
|
317
|
+
defer = context.defer_scope_to_caller
|
|
318
|
+
all_issues = _fragment_or_deferred_check(
|
|
319
|
+
check_magic_values, old_content, content, file_path, defer
|
|
320
|
+
)
|
|
321
|
+
all_issues.extend(
|
|
322
|
+
_fragment_or_deferred_check(
|
|
323
|
+
check_blast_radius_declared, old_content, content, file_path, defer
|
|
483
324
|
)
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
325
|
+
)
|
|
326
|
+
all_issues.extend(check_fstring_structural_literals(content, file_path))
|
|
327
|
+
all_issues.extend(check_constants_outside_config(content, file_path))
|
|
328
|
+
all_issues.extend(check_constants_outside_config_advisory(content, file_path))
|
|
329
|
+
all_issues.extend(check_file_global_constants_use_count(content, file_path))
|
|
330
|
+
return all_issues
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _python_duplicate_body_and_banned_issues(context: _ValidationContext) -> list[str]:
|
|
334
|
+
"""Return the same-file duplicate-body and banned-name checks for Python."""
|
|
335
|
+
effective_content, file_path = context.effective_content, context.file_path
|
|
336
|
+
changed, defer = context.all_changed_lines, context.defer_scope_to_caller
|
|
337
|
+
all_issues = check_same_file_inline_duplicate_body(
|
|
338
|
+
effective_content, file_path, changed, defer
|
|
339
|
+
)
|
|
340
|
+
all_issues.extend(check_type_escape_hatches(effective_content, file_path))
|
|
341
|
+
all_issues.extend(
|
|
342
|
+
check_banned_identifiers(effective_content, file_path, changed, defer)
|
|
343
|
+
)
|
|
344
|
+
all_issues.extend(
|
|
345
|
+
check_banned_noun_word_boundary(effective_content, file_path, changed, defer)
|
|
346
|
+
)
|
|
347
|
+
all_issues.extend(check_banned_prefixes(effective_content, file_path))
|
|
348
|
+
return all_issues
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _python_structure_and_stub_issues(context: _ValidationContext) -> list[str]:
|
|
352
|
+
"""Return the stub, wrapper, and structural checks for a Python file."""
|
|
353
|
+
effective_content, file_path = context.effective_content, context.file_path
|
|
354
|
+
all_issues = check_stub_implementations(effective_content, file_path)
|
|
355
|
+
all_issues.extend(check_typed_dict_encode_decode(effective_content, file_path))
|
|
356
|
+
all_issues.extend(check_test_branching_in_production(effective_content, file_path))
|
|
357
|
+
all_issues.extend(check_dead_test_module_constant(effective_content, file_path))
|
|
358
|
+
all_issues.extend(check_unused_test_helper_parameter(effective_content, file_path))
|
|
359
|
+
all_issues.extend(check_bare_except(effective_content, file_path))
|
|
360
|
+
all_issues.extend(check_thin_wrapper_files(effective_content, file_path))
|
|
361
|
+
all_issues.extend(check_zero_payload_function_alias(effective_content, file_path))
|
|
362
|
+
all_issues.extend(check_boundary_types(effective_content, file_path))
|
|
363
|
+
return all_issues
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def _python_docstring_format_issues(context: _ValidationContext) -> list[str]:
|
|
367
|
+
"""Return the docstring-format-vs-signature checks for a Python file."""
|
|
368
|
+
effective_content, file_path = context.effective_content, context.file_path
|
|
369
|
+
all_issues = check_docstring_format(effective_content, file_path)
|
|
370
|
+
all_issues.extend(
|
|
371
|
+
check_docstring_args_match_signature(effective_content, file_path)
|
|
372
|
+
)
|
|
373
|
+
all_issues.extend(
|
|
374
|
+
check_docstring_documents_unreferenced_parameter(effective_content, file_path)
|
|
375
|
+
)
|
|
376
|
+
all_issues.extend(
|
|
377
|
+
check_class_docstring_names_public_methods(effective_content, file_path)
|
|
378
|
+
)
|
|
379
|
+
return all_issues
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _python_docstring_narrative_issues(context: _ValidationContext) -> list[str]:
|
|
383
|
+
"""Return the docstring narrative-prose checks for a Python file."""
|
|
384
|
+
effective_content, file_path = context.effective_content, context.file_path
|
|
385
|
+
changed, defer = context.all_changed_lines, context.defer_scope_to_caller
|
|
386
|
+
all_issues = check_docstring_runon_sentence(
|
|
387
|
+
effective_content, file_path, changed, defer
|
|
388
|
+
)
|
|
389
|
+
all_issues.extend(
|
|
390
|
+
check_docstring_prose_wall_without_illustration(
|
|
391
|
+
effective_content, file_path, changed, defer
|
|
491
392
|
)
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
393
|
+
)
|
|
394
|
+
all_issues.extend(
|
|
395
|
+
check_module_docstring_names_public_checks(effective_content, file_path)
|
|
396
|
+
)
|
|
397
|
+
all_issues.extend(
|
|
398
|
+
check_module_docstring_scope_omits_data_schema_constants(
|
|
399
|
+
effective_content, file_path
|
|
497
400
|
)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
401
|
+
)
|
|
402
|
+
all_issues.extend(
|
|
403
|
+
check_docstring_names_undefined_constant(effective_content, file_path)
|
|
404
|
+
)
|
|
405
|
+
return all_issues
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def _python_boolean_and_test_assertion_issues(context: _ValidationContext) -> list[str]:
|
|
409
|
+
"""Return the boolean-naming and test-assertion-quality checks for Python."""
|
|
410
|
+
content, effective_content, file_path = (
|
|
411
|
+
context.content,
|
|
412
|
+
context.effective_content,
|
|
413
|
+
context.file_path,
|
|
414
|
+
)
|
|
415
|
+
changed, defer = context.all_changed_lines, context.defer_scope_to_caller
|
|
416
|
+
all_issues = check_boolean_naming(effective_content, file_path, changed, defer)
|
|
417
|
+
all_issues.extend(
|
|
418
|
+
check_ignored_must_check_return(effective_content, file_path, changed, defer)
|
|
419
|
+
)
|
|
420
|
+
all_issues.extend(check_skip_decorators_in_tests(content, file_path))
|
|
421
|
+
all_issues.extend(
|
|
422
|
+
check_tests_use_isolated_filesystem_paths(
|
|
423
|
+
effective_content, file_path, changed, defer
|
|
511
424
|
)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
425
|
+
)
|
|
426
|
+
all_issues.extend(check_existence_check_tests(content, file_path))
|
|
427
|
+
all_issues.extend(check_constant_equality_tests(content, file_path))
|
|
428
|
+
all_issues.extend(check_vacuous_cleanup_assertion_tests(content, file_path))
|
|
429
|
+
all_issues.extend(check_stale_test_name_target(content, file_path))
|
|
430
|
+
check_flag_gated_scenario_test_naming(content, file_path)
|
|
431
|
+
return all_issues
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def _python_naming_and_annotation_issues(context: _ValidationContext) -> list[str]:
|
|
435
|
+
"""Return the naming-convention and annotation checks for a Python file."""
|
|
436
|
+
content, file_path = context.content, context.file_path
|
|
437
|
+
all_issues = check_unused_optional_parameters(content, file_path)
|
|
438
|
+
all_issues.extend(check_collection_prefix(content, file_path))
|
|
439
|
+
all_issues.extend(check_stuttering_collection_prefix(content, file_path))
|
|
440
|
+
all_issues.extend(check_hardcoded_user_paths(content, file_path))
|
|
441
|
+
all_issues.extend(check_sys_path_insert_deduplication_guard(content, file_path))
|
|
442
|
+
all_issues.extend(check_library_print(content, file_path))
|
|
443
|
+
all_issues.extend(check_parameter_annotations(content, file_path))
|
|
444
|
+
all_issues.extend(check_known_pytest_fixture_annotations(content, file_path))
|
|
445
|
+
all_issues.extend(check_unused_known_pytest_fixture_parameters(content, file_path))
|
|
446
|
+
all_issues.extend(
|
|
447
|
+
check_return_annotations(
|
|
448
|
+
context.effective_content,
|
|
449
|
+
file_path,
|
|
450
|
+
context.all_changed_lines,
|
|
451
|
+
context.defer_scope_to_caller,
|
|
520
452
|
)
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
check_duplicated_format_patterns(content, file_path)
|
|
524
|
-
advise_cross_skill_duplicate_helper(effective_content, file_path)
|
|
453
|
+
)
|
|
454
|
+
return all_issues
|
|
525
455
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
456
|
+
|
|
457
|
+
def _python_function_length_and_naming_issues(context: _ValidationContext) -> list[str]:
|
|
458
|
+
"""Return the function-length and loop/name-contradiction checks for Python."""
|
|
459
|
+
content, file_path = context.content, context.file_path
|
|
460
|
+
all_issues = check_function_length(
|
|
461
|
+
context.effective_content,
|
|
462
|
+
file_path,
|
|
463
|
+
context.all_changed_lines,
|
|
464
|
+
context.defer_scope_to_caller,
|
|
465
|
+
)
|
|
466
|
+
all_issues.extend(check_loop_variable_naming(content, file_path))
|
|
467
|
+
all_issues.extend(check_referenced_underscore_loop_variable(content, file_path))
|
|
468
|
+
all_issues.extend(check_polarity_name_contradiction(content, file_path))
|
|
469
|
+
return all_issues
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _python_string_magic_issues(context: _ValidationContext) -> list[str]:
|
|
473
|
+
"""Return the inline-literal and string-magic checks for a Python file."""
|
|
474
|
+
content, old_content, file_path = (
|
|
475
|
+
context.content,
|
|
476
|
+
context.old_content,
|
|
477
|
+
context.file_path,
|
|
478
|
+
)
|
|
479
|
+
defer = context.defer_scope_to_caller
|
|
480
|
+
all_issues = [*check_inline_literal_collections(content, file_path)]
|
|
481
|
+
all_issues.extend(check_inline_tuple_string_magic(content, file_path))
|
|
482
|
+
all_issues.extend(
|
|
483
|
+
_fragment_or_deferred_check(
|
|
484
|
+
check_join_separator_string_magic, old_content, content, file_path, defer
|
|
537
485
|
)
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
all_changed_lines,
|
|
543
|
-
defer_scope_to_caller,
|
|
544
|
-
)
|
|
486
|
+
)
|
|
487
|
+
all_issues.extend(
|
|
488
|
+
_fragment_or_deferred_check(
|
|
489
|
+
check_string_literal_magic, old_content, content, file_path, defer
|
|
545
490
|
)
|
|
546
|
-
|
|
547
|
-
|
|
491
|
+
)
|
|
492
|
+
all_issues.extend(check_whitespace_indentation_magic(content, file_path))
|
|
493
|
+
check_duplicated_format_patterns(content, file_path)
|
|
494
|
+
return all_issues
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def _python_full_gate_only_issues(context: _ValidationContext) -> list[str]:
|
|
498
|
+
"""Return the six cross-file checks that read sibling modules, full-gate only."""
|
|
499
|
+
if context.phase != FULL_GATE_PHASE:
|
|
500
|
+
return []
|
|
501
|
+
content, effective_content, file_path = (
|
|
502
|
+
context.content,
|
|
503
|
+
context.effective_content,
|
|
504
|
+
context.file_path,
|
|
505
|
+
)
|
|
506
|
+
changed, defer = context.all_changed_lines, context.defer_scope_to_caller
|
|
507
|
+
all_issues = check_config_duplicate_path_anchor(content, file_path)
|
|
508
|
+
all_issues.extend(
|
|
509
|
+
check_duplicate_function_body_across_files(
|
|
510
|
+
effective_content, file_path, changed, defer, context.sibling_directory
|
|
548
511
|
)
|
|
549
|
-
|
|
550
|
-
|
|
512
|
+
)
|
|
513
|
+
all_issues.extend(
|
|
514
|
+
check_public_function_missing_paired_test(
|
|
515
|
+
effective_content, file_path, changed, defer
|
|
551
516
|
)
|
|
552
|
-
|
|
553
|
-
|
|
517
|
+
)
|
|
518
|
+
all_issues.extend(
|
|
519
|
+
check_test_file_omits_module_public_function(effective_content, file_path)
|
|
520
|
+
)
|
|
521
|
+
all_issues.extend(check_orphan_css_classes(effective_content, file_path))
|
|
522
|
+
advise_cross_skill_duplicate_helper(effective_content, file_path)
|
|
523
|
+
return all_issues
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _python_extension_issues(context: _ValidationContext) -> list[str]:
|
|
527
|
+
"""Return every issue a Python-extension target can raise."""
|
|
528
|
+
all_issues = _python_comment_and_logging_issues(context)
|
|
529
|
+
all_issues.extend(_python_magic_value_and_constant_issues(context))
|
|
530
|
+
all_issues.extend(_python_duplicate_body_and_banned_issues(context))
|
|
531
|
+
all_issues.extend(_python_structure_and_stub_issues(context))
|
|
532
|
+
all_issues.extend(_python_docstring_format_issues(context))
|
|
533
|
+
all_issues.extend(_python_docstring_narrative_issues(context))
|
|
534
|
+
all_issues.extend(_python_boolean_and_test_assertion_issues(context))
|
|
535
|
+
all_issues.extend(_python_naming_and_annotation_issues(context))
|
|
536
|
+
all_issues.extend(_python_function_length_and_naming_issues(context))
|
|
537
|
+
all_issues.extend(_python_string_magic_issues(context))
|
|
538
|
+
all_issues.extend(_python_full_gate_only_issues(context))
|
|
539
|
+
return all_issues
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _javascript_comment_and_naming_issues(context: _ValidationContext) -> list[str]:
|
|
543
|
+
"""Return the comment-diff, e2e-naming, and boolean-naming checks for JavaScript."""
|
|
544
|
+
content, old_content, file_path = (
|
|
545
|
+
context.content,
|
|
546
|
+
context.old_content,
|
|
547
|
+
context.file_path,
|
|
548
|
+
)
|
|
549
|
+
all_issues: list[str] = []
|
|
550
|
+
if not is_test_file(file_path):
|
|
551
|
+
all_issues.extend(check_comment_changes(old_content, content, file_path))
|
|
552
|
+
all_issues.extend(check_e2e_test_naming(content, file_path))
|
|
553
|
+
all_issues.extend(
|
|
554
|
+
check_js_boolean_naming(
|
|
555
|
+
context.effective_content,
|
|
556
|
+
file_path,
|
|
557
|
+
context.all_changed_lines,
|
|
558
|
+
context.defer_scope_to_caller,
|
|
554
559
|
)
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
560
|
+
)
|
|
561
|
+
return all_issues
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def _javascript_structure_issues(context: _ValidationContext) -> list[str]:
|
|
565
|
+
"""Return the banned-identifier and object-shape checks for JavaScript."""
|
|
566
|
+
content, effective_content, file_path = (
|
|
567
|
+
context.content,
|
|
568
|
+
context.effective_content,
|
|
569
|
+
context.file_path,
|
|
570
|
+
)
|
|
571
|
+
changed, defer = context.all_changed_lines, context.defer_scope_to_caller
|
|
572
|
+
all_issues = check_js_banned_identifiers(
|
|
573
|
+
effective_content, file_path, changed, defer
|
|
574
|
+
)
|
|
575
|
+
all_issues.extend(check_js_resume_task_enumeration_coverage(content, file_path))
|
|
576
|
+
all_issues.extend(check_js_returns_object_schemaless_branch(content, file_path))
|
|
577
|
+
all_issues.extend(check_js_sibling_return_object_key_drift(content, file_path))
|
|
578
|
+
all_issues.extend(
|
|
579
|
+
check_js_bare_flag_return_directive(
|
|
580
|
+
effective_content, file_path, changed, defer
|
|
562
581
|
)
|
|
582
|
+
)
|
|
583
|
+
return all_issues
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def _javascript_extension_issues(context: _ValidationContext) -> list[str]:
|
|
587
|
+
"""Return every issue a JavaScript-extension target can raise."""
|
|
588
|
+
all_issues = _javascript_comment_and_naming_issues(context)
|
|
589
|
+
all_issues.extend(_javascript_structure_issues(context))
|
|
590
|
+
return all_issues
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def _effective_content_and_changed_lines(
|
|
594
|
+
content: str, full_file_content: str | None, prior_full_file_content: str
|
|
595
|
+
) -> tuple[str, set[int] | None]:
|
|
596
|
+
"""Return the effective content and changed-line set an Edit reconstructs."""
|
|
597
|
+
effective_content = content if full_file_content is None else full_file_content
|
|
598
|
+
all_changed_lines = (
|
|
599
|
+
changed_line_numbers(prior_full_file_content, full_file_content)
|
|
600
|
+
if full_file_content is not None
|
|
601
|
+
else None
|
|
602
|
+
)
|
|
603
|
+
return effective_content, all_changed_lines
|
|
563
604
|
|
|
605
|
+
|
|
606
|
+
def validate_content_for_phase(
|
|
607
|
+
content: str,
|
|
608
|
+
file_path: str,
|
|
609
|
+
old_content: str = "",
|
|
610
|
+
full_file_content: str | None = None,
|
|
611
|
+
prior_full_file_content: str = "",
|
|
612
|
+
defer_scope_to_caller: bool = False,
|
|
613
|
+
sibling_directory: Path | None = None,
|
|
614
|
+
*,
|
|
615
|
+
phase: str,
|
|
616
|
+
) -> list[str]:
|
|
617
|
+
"""Run all applicable validators on content for one named validation phase."""
|
|
618
|
+
validated_phase = _validated_phase(phase)
|
|
619
|
+
extension = get_file_extension(file_path)
|
|
620
|
+
effective_content, all_changed_lines = _effective_content_and_changed_lines(
|
|
621
|
+
content, full_file_content, prior_full_file_content
|
|
622
|
+
)
|
|
623
|
+
context = _ValidationContext(
|
|
624
|
+
content, old_content, effective_content, file_path, all_changed_lines,
|
|
625
|
+
defer_scope_to_caller, sibling_directory, validated_phase,
|
|
626
|
+
)
|
|
627
|
+
all_issues: list[str] = []
|
|
628
|
+
if extension in ALL_PYTHON_EXTENSIONS:
|
|
629
|
+
all_issues = _python_extension_issues(context)
|
|
630
|
+
elif extension in ALL_JAVASCRIPT_EXTENSIONS:
|
|
631
|
+
all_issues = _javascript_extension_issues(context)
|
|
564
632
|
if extension in ALL_CODE_EXTENSIONS:
|
|
565
633
|
advise_file_line_count(content, file_path)
|
|
566
|
-
|
|
567
634
|
return all_issues
|
|
568
635
|
|
|
569
636
|
|
|
637
|
+
def validate_content_for_edit_lane(
|
|
638
|
+
content: str,
|
|
639
|
+
file_path: str,
|
|
640
|
+
old_content: str = "",
|
|
641
|
+
full_file_content: str | None = None,
|
|
642
|
+
prior_full_file_content: str = "",
|
|
643
|
+
defer_scope_to_caller: bool = False,
|
|
644
|
+
sibling_directory: Path | None = None,
|
|
645
|
+
) -> list[str]:
|
|
646
|
+
"""Run the edit-lane phase, skipping the six checks that read sibling files."""
|
|
647
|
+
return validate_content_for_phase(
|
|
648
|
+
content,
|
|
649
|
+
file_path,
|
|
650
|
+
old_content,
|
|
651
|
+
full_file_content,
|
|
652
|
+
prior_full_file_content,
|
|
653
|
+
defer_scope_to_caller,
|
|
654
|
+
sibling_directory,
|
|
655
|
+
phase=EDIT_LANE_PHASE,
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def validate_content_for_full_gate(
|
|
660
|
+
content: str,
|
|
661
|
+
file_path: str,
|
|
662
|
+
old_content: str = "",
|
|
663
|
+
full_file_content: str | None = None,
|
|
664
|
+
prior_full_file_content: str = "",
|
|
665
|
+
defer_scope_to_caller: bool = False,
|
|
666
|
+
sibling_directory: Path | None = None,
|
|
667
|
+
) -> list[str]:
|
|
668
|
+
"""Run the full-gate phase, including the six checks that read sibling files."""
|
|
669
|
+
return validate_content_for_phase(
|
|
670
|
+
content,
|
|
671
|
+
file_path,
|
|
672
|
+
old_content,
|
|
673
|
+
full_file_content,
|
|
674
|
+
prior_full_file_content,
|
|
675
|
+
defer_scope_to_caller,
|
|
676
|
+
sibling_directory,
|
|
677
|
+
phase=FULL_GATE_PHASE,
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
|
|
570
681
|
def _read_existing_file_content(file_path: str) -> str | None:
|
|
571
682
|
"""Return the on-disk content of *file_path*, or None when it cannot be read."""
|
|
572
683
|
try:
|
|
@@ -660,17 +771,20 @@ def _hook_infrastructure_blocking_issues(
|
|
|
660
771
|
file_path: str,
|
|
661
772
|
full_file_content: str | None = None,
|
|
662
773
|
prior_full_file_content: str = "",
|
|
774
|
+
*,
|
|
775
|
+
phase: str,
|
|
663
776
|
) -> list[str]:
|
|
664
|
-
"""Run the checks that still guard a hook Python target.
|
|
777
|
+
"""Run the checks that still guard a hook Python target for one phase.
|
|
665
778
|
|
|
666
779
|
The whole code-rules verdict stays off hook-infrastructure files, so this
|
|
667
|
-
runs the checks that must still guard them: the
|
|
668
|
-
check and the
|
|
669
|
-
lines an edit touched exactly as
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
780
|
+
runs the checks that must still guard them in every phase: the same-file
|
|
781
|
+
inline-duplicate-body check and the unanchored command-dispatch check,
|
|
782
|
+
each span-scoped to the lines an edit touched exactly as
|
|
783
|
+
``validate_content_for_phase`` scopes it for production code; and the
|
|
784
|
+
zero-payload alias check, whose docstring names hook modules as its
|
|
785
|
+
motivating case, run over the whole post-edit file. The cross-file
|
|
786
|
+
duplicate-body check joins that roster only when ``phase`` is
|
|
787
|
+
``FULL_GATE_PHASE``.
|
|
674
788
|
|
|
675
789
|
Args:
|
|
676
790
|
content: The fragment or whole-file body under validation.
|
|
@@ -679,10 +793,11 @@ def _hook_infrastructure_blocking_issues(
|
|
|
679
793
|
None for a whole-file Write.
|
|
680
794
|
prior_full_file_content: The file content before the edit applied, used to
|
|
681
795
|
recover the changed lines on an Edit.
|
|
796
|
+
phase: ``EDIT_LANE_PHASE`` or ``FULL_GATE_PHASE``; selects whether the
|
|
797
|
+
cross-file duplicate-body check runs.
|
|
682
798
|
|
|
683
799
|
Returns:
|
|
684
|
-
The in-scope
|
|
685
|
-
violations for the target.
|
|
800
|
+
The in-scope violations for the target at the given phase.
|
|
686
801
|
"""
|
|
687
802
|
effective_content = content if full_file_content is None else full_file_content
|
|
688
803
|
all_changed_lines = (
|
|
@@ -690,11 +805,15 @@ def _hook_infrastructure_blocking_issues(
|
|
|
690
805
|
if full_file_content is not None
|
|
691
806
|
else None
|
|
692
807
|
)
|
|
693
|
-
all_issues =
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
808
|
+
all_issues: list[str] = []
|
|
809
|
+
if phase == FULL_GATE_PHASE:
|
|
810
|
+
all_issues.extend(
|
|
811
|
+
check_duplicate_function_body_across_files(
|
|
812
|
+
effective_content,
|
|
813
|
+
file_path,
|
|
814
|
+
all_changed_lines,
|
|
815
|
+
)
|
|
816
|
+
)
|
|
698
817
|
all_issues.extend(
|
|
699
818
|
check_same_file_inline_duplicate_body(
|
|
700
819
|
effective_content,
|
|
@@ -885,7 +1004,7 @@ def _forecast_full_file_violations(
|
|
|
885
1004
|
Returns:
|
|
886
1005
|
The full-file violations not already in ``all_blocking_issues``.
|
|
887
1006
|
"""
|
|
888
|
-
all_full_file_issues =
|
|
1007
|
+
all_full_file_issues = validate_content_for_edit_lane(
|
|
889
1008
|
full_file_content_after_edit, file_path, prior_full_file_content
|
|
890
1009
|
)
|
|
891
1010
|
return _issues_absent_from_prior_bodies(all_full_file_issues, all_blocking_issues)
|
|
@@ -939,10 +1058,10 @@ def _run_precheck(
|
|
|
939
1058
|
candidate_content = candidate_content.lstrip(UTF8_BYTE_ORDER_MARK)
|
|
940
1059
|
if runs_full_verdict:
|
|
941
1060
|
old_content = _read_existing_file_content(target_path) or ""
|
|
942
|
-
all_issues =
|
|
1061
|
+
all_issues = validate_content_for_full_gate(candidate_content, target_path, old_content)
|
|
943
1062
|
else:
|
|
944
1063
|
all_issues = _hook_infrastructure_blocking_issues(
|
|
945
|
-
candidate_content, target_path
|
|
1064
|
+
candidate_content, target_path, phase=FULL_GATE_PHASE
|
|
946
1065
|
)
|
|
947
1066
|
for each_issue in all_issues:
|
|
948
1067
|
violation_stream.write(f"{each_issue}\n")
|
|
@@ -1140,7 +1259,7 @@ def _report_blocking_violations(
|
|
|
1140
1259
|
prior_full_file_content: The on-disk content before the edit.
|
|
1141
1260
|
deny_stream: The stream the JSON deny payload is written to.
|
|
1142
1261
|
"""
|
|
1143
|
-
all_blocking_issues =
|
|
1262
|
+
all_blocking_issues = validate_content_for_edit_lane(
|
|
1144
1263
|
content,
|
|
1145
1264
|
file_path,
|
|
1146
1265
|
old_content,
|
|
@@ -1170,9 +1289,9 @@ def _report_hook_blocking_issues(
|
|
|
1170
1289
|
) -> None:
|
|
1171
1290
|
"""Write a deny payload when a hook target trips a check that still guards it.
|
|
1172
1291
|
|
|
1173
|
-
The full code-rules verdict stays off hook-infrastructure files; this runs
|
|
1174
|
-
|
|
1175
|
-
|
|
1292
|
+
The full code-rules verdict stays off hook-infrastructure files; this runs
|
|
1293
|
+
the edit-lane hook-infrastructure checks and emits the deny payload when
|
|
1294
|
+
one fires.
|
|
1176
1295
|
|
|
1177
1296
|
Args:
|
|
1178
1297
|
content: The fragment or whole-file body under validation.
|
|
@@ -1187,6 +1306,7 @@ def _report_hook_blocking_issues(
|
|
|
1187
1306
|
file_path,
|
|
1188
1307
|
full_file_content_after_edit,
|
|
1189
1308
|
prior_full_file_content,
|
|
1309
|
+
phase=EDIT_LANE_PHASE,
|
|
1190
1310
|
)
|
|
1191
1311
|
if not all_blocking_issues:
|
|
1192
1312
|
return
|