claude-dev-env 1.75.0 → 1.76.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/_shared/pr-loop/scripts/code_rules_gate.py +60 -5
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/inline_duplicate_body_span_constants.py +22 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +147 -3
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/CLAUDE.md +2 -2
- package/hooks/blocking/claude_md_orphan_file_blocker.py +170 -20
- package/hooks/blocking/code_rules_duplicate_body.py +378 -26
- package/hooks/blocking/code_rules_enforcer.py +36 -5
- package/hooks/blocking/code_rules_imports_logging.py +679 -1
- package/hooks/blocking/code_rules_shared.py +8 -5
- package/hooks/blocking/code_rules_test_assertions.py +6 -7
- package/hooks/blocking/test_claude_md_orphan_file_blocker.py +484 -0
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +1 -0
- package/hooks/blocking/test_code_rules_enforcer_import_block_sort.py +157 -0
- package/hooks/blocking/test_code_rules_enforcer_same_file_inline_duplicate.py +466 -0
- package/hooks/blocking/test_code_rules_enforcer_split_test_assertions.py +11 -9
- package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +758 -0
- package/hooks/blocking/test_code_rules_logging_printf_tokens.py +134 -0
- package/hooks/blocking/test_verification_verdict_store.py +66 -1
- package/hooks/blocking/test_verifier_verdict_minter.py +64 -5
- package/hooks/blocking/verification_verdict_store.py +19 -5
- package/hooks/blocking/verifier_verdict_minter.py +18 -15
- package/hooks/hooks_constants/blocking_check_limits.py +30 -1
- package/hooks/hooks_constants/claude_md_orphan_file_blocker_constants.py +52 -24
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +41 -1
- package/hooks/hooks_constants/duplicate_function_body_constants.py +21 -5
- package/package.json +1 -1
- package/rules/claude-md-orphan-file.md +7 -8
- package/rules/docstring-prose-matches-implementation.md +1 -0
- package/rules/package-inventory-stale-entry.md +8 -0
- package/skills/anthropic-plan/CLAUDE.md +1 -1
- package/skills/anthropic-plan/SKILL.md +15 -2
- package/skills/autoconverge/workflow/converge.contract.test.mjs +12 -19
- package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +71 -0
- package/skills/autoconverge/workflow/converge.mjs +86 -110
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +58 -4
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +9 -0
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +42 -0
|
@@ -23,6 +23,11 @@ from bugteam_scripts_constants.bugteam_code_rules_gate_constants import (
|
|
|
23
23
|
BANNED_NOUN_DEFINITION_LINE_GROUP_INDEX,
|
|
24
24
|
BANNED_NOUN_SPAN_GROUP_INDEX,
|
|
25
25
|
BANNED_NOUN_VIOLATION_PATTERN,
|
|
26
|
+
INLINE_DUPLICATE_BODY_ENCLOSING_LINE_GROUP_INDEX,
|
|
27
|
+
INLINE_DUPLICATE_BODY_ENCLOSING_SPAN_GROUP_INDEX,
|
|
28
|
+
INLINE_DUPLICATE_BODY_HELPER_LINE_GROUP_INDEX,
|
|
29
|
+
INLINE_DUPLICATE_BODY_HELPER_SPAN_GROUP_INDEX,
|
|
30
|
+
INLINE_DUPLICATE_BODY_VIOLATION_PATTERN,
|
|
26
31
|
HUNK_HEADER_RAW_PATTERN,
|
|
27
32
|
ISOLATION_DEFINITION_LINE_GROUP_INDEX,
|
|
28
33
|
ISOLATION_SPAN_GROUP_INDEX,
|
|
@@ -958,6 +963,43 @@ def banned_noun_span_range(violation_text: str) -> range | None:
|
|
|
958
963
|
return range(definition_line, definition_line + line_span)
|
|
959
964
|
|
|
960
965
|
|
|
966
|
+
def inline_duplicate_body_span_lines(violation_text: str) -> frozenset[int] | None:
|
|
967
|
+
"""Return the union of both spans of a same-file inline-duplicate issue, or None.
|
|
968
|
+
|
|
969
|
+
The same-file inline-duplicate message names two functions that share a body —
|
|
970
|
+
the helper and the enclosing function carrying the inline copy — and the live
|
|
971
|
+
Write/Edit hook scopes the violation by the UNION of both spans, blocking when
|
|
972
|
+
an edit touches either function. So the message carries both spans: ``(inline
|
|
973
|
+
duplicate body spans: helper at line H spanning P lines, enclosing at line E
|
|
974
|
+
spanning Q lines)``. The two spans can be disjoint (an unrelated function may
|
|
975
|
+
sit between the helper and its inline copy), so this returns the union as a
|
|
976
|
+
line-number set rather than a single contiguous range — a range covering the
|
|
977
|
+
gap would wrongly block an edit confined to that intervening function, which
|
|
978
|
+
the PreToolUse path leaves unflagged.
|
|
979
|
+
|
|
980
|
+
Args:
|
|
981
|
+
violation_text: A single violation string emitted by the enforcer.
|
|
982
|
+
|
|
983
|
+
Returns:
|
|
984
|
+
The frozenset of every line in the helper span and the enclosing span, or
|
|
985
|
+
None when the text is not a same-file inline-duplicate violation.
|
|
986
|
+
"""
|
|
987
|
+
span_match = INLINE_DUPLICATE_BODY_VIOLATION_PATTERN.search(violation_text)
|
|
988
|
+
if span_match is None:
|
|
989
|
+
return None
|
|
990
|
+
helper_line = int(span_match.group(INLINE_DUPLICATE_BODY_HELPER_LINE_GROUP_INDEX))
|
|
991
|
+
helper_span = int(span_match.group(INLINE_DUPLICATE_BODY_HELPER_SPAN_GROUP_INDEX))
|
|
992
|
+
enclosing_line = int(
|
|
993
|
+
span_match.group(INLINE_DUPLICATE_BODY_ENCLOSING_LINE_GROUP_INDEX)
|
|
994
|
+
)
|
|
995
|
+
enclosing_span = int(
|
|
996
|
+
span_match.group(INLINE_DUPLICATE_BODY_ENCLOSING_SPAN_GROUP_INDEX)
|
|
997
|
+
)
|
|
998
|
+
helper_lines = range(helper_line, helper_line + helper_span)
|
|
999
|
+
enclosing_lines = range(enclosing_line, enclosing_line + enclosing_span)
|
|
1000
|
+
return frozenset(helper_lines) | frozenset(enclosing_lines)
|
|
1001
|
+
|
|
1002
|
+
|
|
961
1003
|
def _all_span_range_extractors() -> tuple[Callable[[str], range | None], ...]:
|
|
962
1004
|
return (
|
|
963
1005
|
function_length_span_range,
|
|
@@ -1003,10 +1045,15 @@ def split_violations_by_scope(
|
|
|
1003
1045
|
|
|
1004
1046
|
Returns:
|
|
1005
1047
|
Tuple ``(blocking, advisory)``. When *all_added_line_numbers* is
|
|
1006
|
-
None, every issue is blocking.
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1048
|
+
None, every issue is blocking. A same-file inline-duplicate violation
|
|
1049
|
+
carries both the helper span and the enclosing span;
|
|
1050
|
+
``inline_duplicate_body_span_lines`` reconstructs their union as a
|
|
1051
|
+
line-number set, and the violation is blocking when an added line falls
|
|
1052
|
+
in either span — matching the live Write/Edit hook's union scoping. Every
|
|
1053
|
+
other diff-scoped violation (function-length, HOME/TMP isolation,
|
|
1054
|
+
banned-noun) carries one enclosing-unit span fragment that
|
|
1055
|
+
``enclosing_span_range`` reconstructs through one shared extractor
|
|
1056
|
+
registry; such a violation is blocking
|
|
1010
1057
|
when its declared span intersects the added lines (the unit grew or its
|
|
1011
1058
|
signature changed in this diff) and advisory otherwise (a pre-existing
|
|
1012
1059
|
untouched unit). Every other issue is blocking when its ``Line N:``
|
|
@@ -1017,6 +1064,13 @@ def split_violations_by_scope(
|
|
|
1017
1064
|
blocking: list[str] = []
|
|
1018
1065
|
advisory: list[str] = []
|
|
1019
1066
|
for each_issue in all_issues:
|
|
1067
|
+
inline_duplicate_lines = inline_duplicate_body_span_lines(each_issue)
|
|
1068
|
+
if inline_duplicate_lines is not None:
|
|
1069
|
+
if inline_duplicate_lines & all_added_line_numbers:
|
|
1070
|
+
blocking.append(each_issue)
|
|
1071
|
+
else:
|
|
1072
|
+
advisory.append(each_issue)
|
|
1073
|
+
continue
|
|
1020
1074
|
span_range = enclosing_span_range(each_issue)
|
|
1021
1075
|
if span_range is not None:
|
|
1022
1076
|
if any(each_line in all_added_line_numbers for each_line in span_range):
|
package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py
CHANGED
|
@@ -31,6 +31,15 @@ BANNED_NOUN_VIOLATION_PATTERN: re.Pattern[str] = re.compile(
|
|
|
31
31
|
BANNED_NOUN_DEFINITION_LINE_GROUP_INDEX: int = 1
|
|
32
32
|
BANNED_NOUN_SPAN_GROUP_INDEX: int = 2
|
|
33
33
|
|
|
34
|
+
INLINE_DUPLICATE_BODY_VIOLATION_PATTERN: re.Pattern[str] = re.compile(
|
|
35
|
+
r"\(inline duplicate body spans: helper at line (\d+) spanning (\d+) lines, "
|
|
36
|
+
r"enclosing at line (\d+) spanning (\d+) lines\)"
|
|
37
|
+
)
|
|
38
|
+
INLINE_DUPLICATE_BODY_HELPER_LINE_GROUP_INDEX: int = 1
|
|
39
|
+
INLINE_DUPLICATE_BODY_HELPER_SPAN_GROUP_INDEX: int = 2
|
|
40
|
+
INLINE_DUPLICATE_BODY_ENCLOSING_LINE_GROUP_INDEX: int = 3
|
|
41
|
+
INLINE_DUPLICATE_BODY_ENCLOSING_SPAN_GROUP_INDEX: int = 4
|
|
42
|
+
|
|
34
43
|
ALL_CODE_FILE_EXTENSIONS: frozenset[str] = frozenset(
|
|
35
44
|
{".py", ".js", ".ts", ".tsx", ".jsx"}
|
|
36
45
|
)
|
|
@@ -1022,3 +1022,45 @@ def test_main_staged_mode_passes_on_staged_deletion_of_clean_file(
|
|
|
1022
1022
|
"a staged deletion has no staged blob; the gate must skip it cleanly "
|
|
1023
1023
|
"rather than fail closed as if the file were unreadable"
|
|
1024
1024
|
)
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
_INLINE_DUPLICATE_MESSAGE = (
|
|
1028
|
+
"Function '_wait_for_render' duplicates an inline block in '_navigate_then_wait'"
|
|
1029
|
+
" — this function body is also present inline (Reuse before create / DRY) "
|
|
1030
|
+
"(inline duplicate body spans: helper at line 4 spanning 10 lines, "
|
|
1031
|
+
"enclosing at line 16 spanning 11 lines)"
|
|
1032
|
+
)
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
def test_inline_duplicate_body_span_lines_unions_helper_and_enclosing_spans() -> None:
|
|
1036
|
+
"""The same-file inline-duplicate message carries both spans, and the gate
|
|
1037
|
+
recovers their union as a line-number set so a touch of either function blocks —
|
|
1038
|
+
mirroring the live Write/Edit hook's union scoping."""
|
|
1039
|
+
span_lines = gate_module.inline_duplicate_body_span_lines(_INLINE_DUPLICATE_MESSAGE)
|
|
1040
|
+
assert span_lines == frozenset(range(4, 14)) | frozenset(range(16, 27))
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
def test_inline_duplicate_blocks_when_only_enclosing_copy_added() -> None:
|
|
1044
|
+
"""An added line in the enclosing span alone blocks, because the live hook scopes
|
|
1045
|
+
by the union of both spans and blocks the same edit — the common shape where a
|
|
1046
|
+
block is copied INTO a growing enclosing function, leaving the helper untouched."""
|
|
1047
|
+
added_line_in_enclosing_only = 18
|
|
1048
|
+
blocking, advisory = gate_module.split_violations_by_scope(
|
|
1049
|
+
[_INLINE_DUPLICATE_MESSAGE],
|
|
1050
|
+
all_added_line_numbers={added_line_in_enclosing_only},
|
|
1051
|
+
)
|
|
1052
|
+
assert blocking == [_INLINE_DUPLICATE_MESSAGE]
|
|
1053
|
+
assert advisory == []
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
def test_inline_duplicate_advises_when_gap_line_added() -> None:
|
|
1057
|
+
"""An edit confined to the gap between the helper span (4-13) and the enclosing
|
|
1058
|
+
span (16-26) must not block, matching the live hook. The union set keeps the gap
|
|
1059
|
+
out of scope where a single contiguous range would wrongly block it."""
|
|
1060
|
+
gap_line_between_spans = 14
|
|
1061
|
+
blocking, advisory = gate_module.split_violations_by_scope(
|
|
1062
|
+
[_INLINE_DUPLICATE_MESSAGE],
|
|
1063
|
+
all_added_line_numbers={gap_line_between_spans},
|
|
1064
|
+
)
|
|
1065
|
+
assert advisory == [_INLINE_DUPLICATE_MESSAGE]
|
|
1066
|
+
assert blocking == []
|