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.
Files changed (68) hide show
  1. package/CLAUDE.md +8 -0
  2. package/_shared/pr-loop/scripts/code_rules_gate.py +5 -3
  3. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +39 -0
  4. package/agents/clean-coder.md +1 -0
  5. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +2 -2
  6. package/bin/install.mjs +73 -5
  7. package/bin/install.test.mjs +360 -4
  8. package/docs/CODE_RULES.md +1 -1
  9. package/hooks/blocking/CLAUDE.md +3 -1
  10. package/hooks/blocking/claude_md_orphan_file_blocker.py +5 -6
  11. package/hooks/blocking/code_rules_dead_config_field.py +69 -56
  12. package/hooks/blocking/code_rules_docstrings.py +676 -0
  13. package/hooks/blocking/code_rules_enforcer.py +26 -0
  14. package/hooks/blocking/code_rules_shared.py +19 -0
  15. package/hooks/blocking/code_rules_test_assertions.py +152 -1
  16. package/hooks/blocking/code_rules_type_escape.py +447 -2
  17. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +420 -0
  18. package/hooks/blocking/md_to_html_blocker.py +7 -8
  19. package/hooks/blocking/open_questions_in_plans_blocker.py +5 -6
  20. package/hooks/blocking/plain_language_blocker.py +51 -16
  21. package/hooks/blocking/pr_converge_bugteam_enforcer.py +5 -5
  22. package/hooks/blocking/pre_tool_use_dispatcher.py +545 -0
  23. package/hooks/blocking/pytest_testpaths_orphan_blocker.py +358 -0
  24. package/hooks/blocking/state_description_blocker.py +75 -36
  25. package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +81 -0
  26. package/hooks/blocking/test_code_rules_enforcer_docstring_inline_literal_claim.py +93 -0
  27. package/hooks/blocking/test_code_rules_enforcer_docstring_no_consumer.py +93 -0
  28. package/hooks/blocking/test_code_rules_enforcer_docstring_step_dispatch.py +262 -0
  29. package/hooks/blocking/test_code_rules_enforcer_docstring_undefined_constant.py +253 -0
  30. package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +279 -0
  31. package/hooks/blocking/test_code_rules_enforcer_object_parameter.py +499 -0
  32. package/hooks/blocking/test_code_rules_enforcer_stale_test_name.py +103 -0
  33. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +456 -0
  34. package/hooks/blocking/test_pre_tool_use_dispatcher.py +816 -0
  35. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +341 -0
  36. package/hooks/blocking/test_pytest_testpaths_orphan_blocker.py +247 -0
  37. package/hooks/blocking/test_shared_stdin_adoption.py +166 -0
  38. package/hooks/blocking/verdict_directory_write_blocker.py +12 -7
  39. package/hooks/hooks.json +9 -79
  40. package/hooks/hooks_constants/CLAUDE.md +3 -1
  41. package/hooks/hooks_constants/blocking_check_limits.py +75 -0
  42. package/hooks/hooks_constants/code_rules_enforcer_constants.py +6 -0
  43. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +45 -0
  44. package/hooks/hooks_constants/dead_config_field_constants.py +5 -5
  45. package/hooks/hooks_constants/mypy_validator_cache_constants.py +36 -0
  46. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +69 -0
  47. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +135 -0
  48. package/hooks/hooks_constants/precommit_code_rules_gate_constants.py +1 -1
  49. package/hooks/hooks_constants/pytest_testpaths_orphan_blocker_constants.py +79 -0
  50. package/hooks/validation/mypy_validator.py +215 -17
  51. package/hooks/validation/post_tool_use_dispatcher.py +344 -0
  52. package/hooks/validation/test_mypy_validator.py +184 -1
  53. package/hooks/validation/test_post_tool_use_dispatcher.py +610 -0
  54. package/hooks/workflow/test_auto_formatter.py +10 -9
  55. package/package.json +1 -1
  56. package/rules/docstring-prose-matches-implementation.md +3 -2
  57. package/scripts/CLAUDE.md +1 -0
  58. package/scripts/Show-Asset.ps1 +106 -0
  59. package/skills/autoconverge/SKILL.md +123 -3
  60. package/skills/autoconverge/reference/convergence.md +41 -1
  61. package/skills/autoconverge/workflow/converge.contract.test.mjs +90 -0
  62. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +98 -0
  63. package/skills/autoconverge/workflow/converge.mjs +203 -8
  64. package/skills/autoconverge/workflow/converge.path-aware.test.mjs +47 -0
  65. package/skills/autoconverge/workflow/converge_multi.mjs +161 -0
  66. package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +100 -0
  67. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +47 -3
  68. 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. Returns
643
- an empty list when the file is not Python or has a syntax error.
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