claude-dev-env 1.71.0 → 1.73.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/CLAUDE.md +8 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +5 -3
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +39 -0
- package/agents/clean-coder.md +1 -0
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +2 -2
- package/bin/install.mjs +73 -5
- package/bin/install.test.mjs +360 -4
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/CLAUDE.md +3 -1
- package/hooks/blocking/claude_md_orphan_file_blocker.py +5 -6
- package/hooks/blocking/code_rules_dead_config_field.py +69 -56
- package/hooks/blocking/code_rules_docstrings.py +676 -0
- package/hooks/blocking/code_rules_enforcer.py +26 -0
- package/hooks/blocking/code_rules_shared.py +19 -0
- package/hooks/blocking/code_rules_test_assertions.py +152 -1
- package/hooks/blocking/code_rules_type_escape.py +447 -2
- package/hooks/blocking/code_verifier_spawn_preflight_gate.py +420 -0
- package/hooks/blocking/md_to_html_blocker.py +7 -8
- package/hooks/blocking/open_questions_in_plans_blocker.py +5 -6
- package/hooks/blocking/plain_language_blocker.py +51 -16
- package/hooks/blocking/pr_converge_bugteam_enforcer.py +5 -5
- package/hooks/blocking/pre_tool_use_dispatcher.py +545 -0
- package/hooks/blocking/pytest_testpaths_orphan_blocker.py +358 -0
- package/hooks/blocking/state_description_blocker.py +75 -36
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +81 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_inline_literal_claim.py +93 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_no_consumer.py +93 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_step_dispatch.py +262 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_undefined_constant.py +253 -0
- package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +279 -0
- package/hooks/blocking/test_code_rules_enforcer_object_parameter.py +499 -0
- package/hooks/blocking/test_code_rules_enforcer_stale_test_name.py +103 -0
- package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +456 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +816 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +341 -0
- package/hooks/blocking/test_pytest_testpaths_orphan_blocker.py +247 -0
- package/hooks/blocking/test_shared_stdin_adoption.py +166 -0
- package/hooks/blocking/verdict_directory_write_blocker.py +12 -7
- package/hooks/hooks.json +9 -79
- package/hooks/hooks_constants/CLAUDE.md +3 -1
- package/hooks/hooks_constants/blocking_check_limits.py +75 -0
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +6 -0
- package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +45 -0
- package/hooks/hooks_constants/dead_config_field_constants.py +5 -5
- package/hooks/hooks_constants/mypy_validator_cache_constants.py +36 -0
- package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +69 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +135 -0
- package/hooks/hooks_constants/precommit_code_rules_gate_constants.py +1 -1
- package/hooks/hooks_constants/pytest_testpaths_orphan_blocker_constants.py +79 -0
- package/hooks/validation/mypy_validator.py +215 -17
- package/hooks/validation/post_tool_use_dispatcher.py +344 -0
- package/hooks/validation/test_mypy_validator.py +184 -1
- package/hooks/validation/test_post_tool_use_dispatcher.py +610 -0
- package/hooks/workflow/test_auto_formatter.py +10 -9
- package/package.json +1 -1
- package/rules/docstring-prose-matches-implementation.md +3 -2
- package/scripts/CLAUDE.md +1 -0
- package/scripts/Show-Asset.ps1 +106 -0
- package/skills/autoconverge/SKILL.md +123 -3
- package/skills/autoconverge/reference/convergence.md +41 -1
- package/skills/autoconverge/workflow/converge.contract.test.mjs +90 -0
- package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +98 -0
- package/skills/autoconverge/workflow/converge.mjs +203 -8
- package/skills/autoconverge/workflow/converge.path-aware.test.mjs +47 -0
- package/skills/autoconverge/workflow/converge_multi.mjs +161 -0
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +100 -0
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +47 -3
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -0
|
@@ -544,6 +544,48 @@ def check_database_column_string_magic(content: str, file_path: str) -> list[str
|
|
|
544
544
|
return issues
|
|
545
545
|
|
|
546
546
|
|
|
547
|
+
def is_test_path(file_path: str) -> bool:
|
|
548
|
+
"""Return True when *file_path* matches CODE_RULES.md test-file detection patterns.
|
|
549
|
+
|
|
550
|
+
Mirrors the test-file detection rule documented in CODE_RULES.md:
|
|
551
|
+
filename matches test_*.py OR *_test.py OR *.test.* OR *.spec.* OR
|
|
552
|
+
conftest.py, OR path contains the segment /tests/.
|
|
553
|
+
|
|
554
|
+
Args:
|
|
555
|
+
file_path: Path string to classify; backslashes are normalized to
|
|
556
|
+
forward slashes before pattern matching.
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
True when the path matches any test-file pattern; False otherwise.
|
|
560
|
+
"""
|
|
561
|
+
tests_path_segment = "/tests/"
|
|
562
|
+
conftest_filename = "conftest.py"
|
|
563
|
+
test_filename_prefix = "test_"
|
|
564
|
+
all_test_filename_suffixes = ("_test.py",)
|
|
565
|
+
all_test_filename_glob_suffixes = (".test.", ".spec.")
|
|
566
|
+
normalized_posix = file_path.replace("\\", "/")
|
|
567
|
+
filename_only = normalized_posix.rsplit("/", maxsplit=1)[-1]
|
|
568
|
+
if tests_path_segment in normalized_posix:
|
|
569
|
+
return True
|
|
570
|
+
if filename_only == conftest_filename:
|
|
571
|
+
return True
|
|
572
|
+
if filename_only.startswith(test_filename_prefix) and filename_only.endswith(
|
|
573
|
+
PYTHON_FILE_EXTENSION
|
|
574
|
+
):
|
|
575
|
+
return True
|
|
576
|
+
if any(
|
|
577
|
+
filename_only.endswith(each_suffix)
|
|
578
|
+
for each_suffix in all_test_filename_suffixes
|
|
579
|
+
):
|
|
580
|
+
return True
|
|
581
|
+
if any(
|
|
582
|
+
each_glob_suffix in filename_only
|
|
583
|
+
for each_glob_suffix in all_test_filename_glob_suffixes
|
|
584
|
+
):
|
|
585
|
+
return True
|
|
586
|
+
return False
|
|
587
|
+
|
|
588
|
+
|
|
547
589
|
def _iter_calls_excluding_nested_functions(node: ast.AST) -> Iterator[ast.Call]:
|
|
548
590
|
for each_child in ast.iter_child_nodes(node):
|
|
549
591
|
if isinstance(each_child, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
@@ -636,11 +678,11 @@ def check_wrapper_plumb_through(content: str, file_path: str) -> list[str]:
|
|
|
636
678
|
Args:
|
|
637
679
|
content: File content as a single string for AST parsing.
|
|
638
680
|
file_path: Repository-relative POSIX path of the file (used to
|
|
639
|
-
skip non-Python code extensions early).
|
|
681
|
+
skip non-Python code extensions and test files early).
|
|
640
682
|
|
|
641
683
|
Returns:
|
|
642
|
-
List of violation strings, one per dropped optional kwarg.
|
|
643
|
-
|
|
684
|
+
List of violation strings, one per dropped optional kwarg. Empty for
|
|
685
|
+
a non-Python file, a test file, or a file with a syntax error.
|
|
644
686
|
"""
|
|
645
687
|
non_python_code_extensions = ALL_CODE_FILE_EXTENSIONS - {PYTHON_FILE_EXTENSION}
|
|
646
688
|
lowercase_file_path = file_path.lower()
|
|
@@ -649,6 +691,8 @@ def check_wrapper_plumb_through(content: str, file_path: str) -> list[str]:
|
|
|
649
691
|
for each_extension in non_python_code_extensions
|
|
650
692
|
):
|
|
651
693
|
return []
|
|
694
|
+
if is_test_path(file_path):
|
|
695
|
+
return []
|
|
652
696
|
try:
|
|
653
697
|
tree = ast.parse(content)
|
|
654
698
|
except SyntaxError:
|
|
@@ -790,6 +790,40 @@ def test_check_wrapper_plumb_through_flags_name_call_dropping_kwarg() -> None:
|
|
|
790
790
|
)
|
|
791
791
|
|
|
792
792
|
|
|
793
|
+
def test_check_wrapper_plumb_through_exempts_test_files() -> None:
|
|
794
|
+
"""A test_* function in a test-file path that calls a module-level helper
|
|
795
|
+
exposing an optional kwarg is an ordinary pytest case, not a wrapper; the
|
|
796
|
+
bugteam gate must exempt test files and emit zero findings."""
|
|
797
|
+
source = (
|
|
798
|
+
"def _helper(name, *, clean_name=None):\n"
|
|
799
|
+
" return (name, clean_name)\n"
|
|
800
|
+
"\n"
|
|
801
|
+
"def test_uses_helper():\n"
|
|
802
|
+
" return _helper('a', clean_name='b')\n"
|
|
803
|
+
)
|
|
804
|
+
issues = gate_module.check_wrapper_plumb_through(source, "pkg/test_thing.py")
|
|
805
|
+
assert issues == [], (
|
|
806
|
+
f"a wrapper shape in a test file must yield no findings; got {issues!r}"
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
def test_check_wrapper_plumb_through_still_flags_non_test_path_with_test_shape() -> None:
|
|
811
|
+
"""The test-file exemption is scoped to test paths only; the same wrapper
|
|
812
|
+
shape on a non-test path must still be flagged."""
|
|
813
|
+
source = (
|
|
814
|
+
"def _helper(name, *, clean_name=None):\n"
|
|
815
|
+
" return (name, clean_name)\n"
|
|
816
|
+
"\n"
|
|
817
|
+
"def test_uses_helper():\n"
|
|
818
|
+
" return _helper('a', clean_name='b')\n"
|
|
819
|
+
)
|
|
820
|
+
issues = gate_module.check_wrapper_plumb_through(source, "pkg/module.py")
|
|
821
|
+
assert any(
|
|
822
|
+
"test_uses_helper" in each_issue and "clean_name" in each_issue
|
|
823
|
+
for each_issue in issues
|
|
824
|
+
), f"the same wrapper shape on a non-test path must still flag; got {issues!r}"
|
|
825
|
+
|
|
826
|
+
|
|
793
827
|
def test_check_wrapper_plumb_through_ignores_calls_nested_inside_delegate_arguments() -> None:
|
|
794
828
|
"""A callee nested as an argument (``delegate(helper(x))``) is not a
|
|
795
829
|
separate call site; only the enclosing call is inspected, matching the
|