claude-dev-env 2.25.0 → 2.27.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 (45) hide show
  1. package/.agents/agents/clean-coder.md +95 -33
  2. package/.agents/agents/code-quality-agent.md +85 -24
  3. package/.agents/agents/pr-description-writer.md +2 -2
  4. package/.agents/agents/test_agent_frontmatter.py +626 -0
  5. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +47 -1
  6. package/.agents/skills/pr-cleanup/SKILL.md +1 -0
  7. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +93 -0
  8. package/.agents/skills/pr-title-description/SKILL.md +92 -0
  9. package/.agents/skills/source-command-sr-loop/SKILL.md +12 -3
  10. package/bin/ever-shipped-skills.mjs +2 -0
  11. package/bin/install.agents-home.test.mjs +199 -8
  12. package/bin/install.mjs +15 -8
  13. package/bin/install.test.mjs +82 -2
  14. package/commands/sr-loop.md +10 -1
  15. package/docs/codex-compatibility.md +19 -0
  16. package/hooks/blocking/luna_fast_mode_gate.py +160 -0
  17. package/hooks/blocking/test_luna_fast_mode_gate.py +211 -0
  18. package/hooks/hooks.json +10 -5
  19. package/hooks/hooks_constants/AGENTS.md +1 -1
  20. package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +45 -0
  21. package/hooks/hooks_constants/mypy_integration_constants.py +14 -3
  22. package/hooks/hooks_constants/session_start_injector.py +11 -4
  23. package/hooks/hooks_constants/test_session_start_injector.py +4 -2
  24. package/hooks/session/AGENTS.md +2 -2
  25. package/hooks/session/issue_tracker_session_starter.py +3 -2
  26. package/hooks/session/orchestrator_auto_starter.py +3 -2
  27. package/hooks/session/task_list_loop_starter.py +10 -1
  28. package/hooks/session/test_issue_tracker_session_starter.py +3 -1
  29. package/hooks/session/test_orchestrator_auto_starter.py +5 -3
  30. package/hooks/session/test_task_list_loop_starter.py +10 -8
  31. package/hooks/session/test_untracked_repo_detector.py +7 -5
  32. package/hooks/session/test_working_style_prompt.py +5 -3
  33. package/hooks/session/untracked_repo_detector.py +10 -1
  34. package/hooks/session/working_style_prompt.py +10 -1
  35. package/hooks/validators/AGENTS.md +2 -1
  36. package/hooks/validators/conftest.py +21 -4
  37. package/hooks/validators/mypy_integration.py +77 -16
  38. package/hooks/validators/run_all_validators.py +2 -35
  39. package/hooks/validators/system_temporary_roots.py +90 -0
  40. package/hooks/validators/test_directory_exemption_constants.py +1 -1
  41. package/hooks/validators/test_mypy_integration.py +169 -0
  42. package/hooks/validators/test_system_temporary_roots.py +93 -0
  43. package/package.json +1 -1
  44. package/scripts/codex_compat_materializer.py +4 -2
  45. package/scripts/tests/test_codex_compat_materializer.py +2 -2
@@ -79,6 +79,8 @@ import pytest
79
79
  import yaml
80
80
 
81
81
  ACCEPTED_FRONTMATTER_KEYS = frozenset({"name", "description", "tools", "color"})
82
+ CODE_QUALITY_AGENT_FILENAME = "code-quality-agent.md"
83
+ CODE_QUALITY_READ_ONLY_TOOLS = ("Read", "Grep", "Glob")
82
84
  INSTRUCTION_ALIAS_FILENAMES = frozenset({"AGENTS.md", "CLAUDE.md"})
83
85
  FRONTMATTER_FENCE_LINE = "---"
84
86
  MATERIALIZER_MODULE_NAME = "codex_compat_materializer"
@@ -420,6 +422,301 @@ def _clean_coder_body() -> str:
420
422
  return (Path(__file__).parent / "clean-coder.md").read_text(encoding="utf-8")
421
423
 
422
424
 
425
+ SOURCE_LINK_PATTERN = re.compile(
426
+ r"`(?P<installed><(?:managed-root|agents-home)>/[^`]+)` "
427
+ r"\(source fallback: `(?P<source>packages/claude-dev-env/[^`]+)`\)"
428
+ )
429
+
430
+ EXPECTED_SOURCE_LINK_PAIRS = {
431
+ "clean-coder.md": frozenset({
432
+ (
433
+ "<managed-root>/docs/CODE_RULES.md#5-no-abbreviations",
434
+ "packages/claude-dev-env/docs/CODE_RULES.md#5-no-abbreviations",
435
+ ),
436
+ (
437
+ "<managed-root>/docs/CODE_RULES.md",
438
+ "packages/claude-dev-env/docs/CODE_RULES.md",
439
+ ),
440
+ (
441
+ "<managed-root>/hooks/blocking/code_rules_enforcer.py",
442
+ "packages/claude-dev-env/hooks/blocking/code_rules_enforcer.py",
443
+ ),
444
+ (
445
+ "<managed-root>/rules/code-standards.md",
446
+ "packages/claude-dev-env/rules/code-standards.md",
447
+ ),
448
+ (
449
+ "<managed-root>/rules/file-global-constants.md",
450
+ "packages/claude-dev-env/rules/file-global-constants.md",
451
+ ),
452
+ (
453
+ "<managed-root>/rules/windows-filesystem-safe.md",
454
+ "packages/claude-dev-env/rules/windows-filesystem-safe.md",
455
+ ),
456
+ (
457
+ "<managed-root>/rules/gh-cli-conventions.md",
458
+ "packages/claude-dev-env/rules/gh-cli-conventions.md",
459
+ ),
460
+ (
461
+ "<managed-root>/rules/plain-illustrative-docstrings.md",
462
+ "packages/claude-dev-env/rules/plain-illustrative-docstrings.md",
463
+ ),
464
+ (
465
+ "<managed-root>/rules/testing.md",
466
+ "packages/claude-dev-env/rules/testing.md",
467
+ ),
468
+ (
469
+ "<managed-root>/rules/anti-corollary-tests.md",
470
+ "packages/claude-dev-env/rules/anti-corollary-tests.md",
471
+ ),
472
+ (
473
+ "<managed-root>/rules/paired-test-coverage.md",
474
+ "packages/claude-dev-env/rules/paired-test-coverage.md",
475
+ ),
476
+ (
477
+ "<managed-root>/rules/bdd.md",
478
+ "packages/claude-dev-env/rules/bdd.md",
479
+ ),
480
+ (
481
+ "<managed-root>/rules/ask-user-question-required.md",
482
+ "packages/claude-dev-env/rules/ask-user-question-required.md",
483
+ ),
484
+ (
485
+ "<managed-root>/rules/verify-before-asking.md",
486
+ "packages/claude-dev-env/rules/verify-before-asking.md",
487
+ ),
488
+ (
489
+ "<managed-root>/rules/filesystem-search.md",
490
+ "packages/claude-dev-env/rules/filesystem-search.md",
491
+ ),
492
+ (
493
+ "<managed-root>/rules/shell-invocation.md",
494
+ "packages/claude-dev-env/rules/shell-invocation.md",
495
+ ),
496
+ (
497
+ "<managed-root>/rules/verify-runtime-state.md",
498
+ "packages/claude-dev-env/rules/verify-runtime-state.md",
499
+ ),
500
+ (
501
+ "<managed-root>/rules/doc-inventory-integrity.md",
502
+ "packages/claude-dev-env/rules/doc-inventory-integrity.md",
503
+ ),
504
+ (
505
+ "<managed-root>/rules/docstring-prose-matches-implementation.md",
506
+ "packages/claude-dev-env/rules/docstring-prose-matches-implementation.md",
507
+ ),
508
+ (
509
+ "<managed-root>/rules/durable-post-artifacts.md",
510
+ "packages/claude-dev-env/rules/durable-post-artifacts.md",
511
+ ),
512
+ (
513
+ "<managed-root>/rules/failure-blast-radius.md",
514
+ "packages/claude-dev-env/rules/failure-blast-radius.md",
515
+ ),
516
+ (
517
+ "<managed-root>/rules/git-workflow.md",
518
+ "packages/claude-dev-env/rules/git-workflow.md",
519
+ ),
520
+ (
521
+ "<managed-root>/rules/re-stage-before-commit.md",
522
+ "packages/claude-dev-env/rules/re-stage-before-commit.md",
523
+ ),
524
+ (
525
+ "<managed-root>/rules/agent-spawn-protocol.md",
526
+ "packages/claude-dev-env/rules/agent-spawn-protocol.md",
527
+ ),
528
+ (
529
+ "<managed-root>/rules/workers-done-before-complete.md",
530
+ "packages/claude-dev-env/rules/workers-done-before-complete.md",
531
+ ),
532
+ }),
533
+ "code-quality-agent.md": frozenset({
534
+ (
535
+ "<managed-root>/audit-rubrics/category_rubrics/category-a-api-contracts.md",
536
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-a-api-contracts.md",
537
+ ),
538
+ (
539
+ "<managed-root>/audit-rubrics/category_rubrics/category-b-selector-engine-compat.md",
540
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-b-selector-engine-compat.md",
541
+ ),
542
+ (
543
+ "<managed-root>/audit-rubrics/category_rubrics/category-c-resource-cleanup.md",
544
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-c-resource-cleanup.md",
545
+ ),
546
+ (
547
+ "<managed-root>/audit-rubrics/category_rubrics/category-d-scoping-and-ordering.md",
548
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-d-scoping-and-ordering.md",
549
+ ),
550
+ (
551
+ "<managed-root>/audit-rubrics/category_rubrics/category-e-dead-code.md",
552
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-e-dead-code.md",
553
+ ),
554
+ (
555
+ "<managed-root>/audit-rubrics/category_rubrics/category-f-silent-failures.md",
556
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-f-silent-failures.md",
557
+ ),
558
+ (
559
+ "<managed-root>/audit-rubrics/category_rubrics/category-g-bounds-and-overflow.md",
560
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-g-bounds-and-overflow.md",
561
+ ),
562
+ (
563
+ "<managed-root>/audit-rubrics/category_rubrics/category-h-security-boundaries.md",
564
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-h-security-boundaries.md",
565
+ ),
566
+ (
567
+ "<managed-root>/audit-rubrics/category_rubrics/category-i-concurrency.md",
568
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-i-concurrency.md",
569
+ ),
570
+ (
571
+ "<managed-root>/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md",
572
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md",
573
+ ),
574
+ (
575
+ "<managed-root>/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md",
576
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md",
577
+ ),
578
+ (
579
+ "<managed-root>/audit-rubrics/category_rubrics/category-l-behavior-equivalence.md",
580
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-l-behavior-equivalence.md",
581
+ ),
582
+ (
583
+ "<managed-root>/audit-rubrics/category_rubrics/category-m-producer-consumer-cardinality.md",
584
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-m-producer-consumer-cardinality.md",
585
+ ),
586
+ (
587
+ "<managed-root>/audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md",
588
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md",
589
+ ),
590
+ (
591
+ "<managed-root>/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md",
592
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md",
593
+ ),
594
+ (
595
+ "<managed-root>/audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md",
596
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md",
597
+ ),
598
+ (
599
+ "<managed-root>/audit-rubrics/category_rubrics/category-q-cross-surface-claims.md",
600
+ "packages/claude-dev-env/audit-rubrics/category_rubrics/category-q-cross-surface-claims.md",
601
+ ),
602
+ }),
603
+ "pr-description-writer.md": frozenset({
604
+ (
605
+ "<agents-home>/skills/pr-title-description/SKILL.md",
606
+ "packages/claude-dev-env/.agents/skills/pr-title-description/SKILL.md",
607
+ ),
608
+ (
609
+ "<managed-root>/rules/gh-cli-conventions.md#body-content-goes-in-a-file",
610
+ "packages/claude-dev-env/rules/gh-cli-conventions.md#body-content-goes-in-a-file",
611
+ ),
612
+ }),
613
+ }
614
+
615
+
616
+ def _assert_source_link_contract(agent_file_name: str, agent_text: str) -> None:
617
+ agents_directory = Path(__file__).parent
618
+ repository_root = agents_directory.parents[3]
619
+ actual_source_links = frozenset(SOURCE_LINK_PATTERN.findall(agent_text))
620
+ expected_source_links = EXPECTED_SOURCE_LINK_PAIRS[agent_file_name]
621
+ assert actual_source_links == expected_source_links, (
622
+ f"{agent_file_name} source links must match the complete expected set"
623
+ )
624
+ for each_installed_path, each_source_path in actual_source_links:
625
+ installed_suffix = each_installed_path.split(">/", 1)[1]
626
+ source_suffix = each_source_path.removeprefix("packages/claude-dev-env/")
627
+ source_suffix = source_suffix.removeprefix(".agents/")
628
+ assert installed_suffix == source_suffix, (
629
+ f"installed/source suffix mismatch: {each_installed_path} -> "
630
+ f"{each_source_path}"
631
+ )
632
+ source_file_path = repository_root / each_source_path.split("#", 1)[0]
633
+ assert source_file_path.is_file(), (
634
+ f"source fallback must be a file: {each_source_path}"
635
+ )
636
+
637
+
638
+ def test_named_agents_document_installed_paths_and_source_fallbacks() -> None:
639
+ agents_directory = Path(__file__).parent
640
+ for each_agent_file_name in (
641
+ "clean-coder.md",
642
+ "code-quality-agent.md",
643
+ "pr-description-writer.md",
644
+ ):
645
+ agent_text = (agents_directory / each_agent_file_name).read_text(
646
+ encoding="utf-8"
647
+ )
648
+ _assert_source_link_contract(each_agent_file_name, agent_text)
649
+ for each_stale_prefix in (
650
+ "../docs/",
651
+ "../hooks/",
652
+ "../rules/",
653
+ "../audit-rubrics/",
654
+ "../skills/descriptions/",
655
+ "../skills/comments/",
656
+ ):
657
+ assert each_stale_prefix not in agent_text, (
658
+ f"{each_agent_file_name} must not use stale relative guide "
659
+ f"path {each_stale_prefix}"
660
+ )
661
+
662
+
663
+ def test_source_link_contract_rejects_missing_pair() -> None:
664
+ body = _clean_coder_body()
665
+ missing_pair_text = (
666
+ "`<managed-root>/hooks/blocking/code_rules_enforcer.py` "
667
+ "(source fallback: `packages/claude-dev-env/hooks/blocking/"
668
+ "code_rules_enforcer.py`)"
669
+ )
670
+ mutated_body = body.replace(missing_pair_text, "", 1)
671
+ with pytest.raises(AssertionError):
672
+ _assert_source_link_contract("clean-coder.md", mutated_body)
673
+
674
+
675
+ def test_source_link_contract_rejects_altered_valid_file() -> None:
676
+ body = _clean_coder_body()
677
+ mutated_body = body.replace(
678
+ "`<managed-root>/docs/CODE_RULES.md` (source fallback: "
679
+ "`packages/claude-dev-env/docs/CODE_RULES.md`)",
680
+ "`<managed-root>/docs/CODE_RULES.md` (source fallback: "
681
+ "`packages/claude-dev-env/rules/code-standards.md`)",
682
+ 1,
683
+ )
684
+ with pytest.raises(AssertionError):
685
+ _assert_source_link_contract("clean-coder.md", mutated_body)
686
+
687
+
688
+ def test_source_link_contract_rejects_directory_fallback() -> None:
689
+ body = _clean_coder_body()
690
+ mutated_body = body.replace(
691
+ "`<managed-root>/docs/CODE_RULES.md` (source fallback: "
692
+ "`packages/claude-dev-env/docs/CODE_RULES.md`)",
693
+ "`<managed-root>/docs/CODE_RULES.md` (source fallback: "
694
+ "`packages/claude-dev-env/docs`)",
695
+ 1,
696
+ )
697
+ with pytest.raises(AssertionError):
698
+ _assert_source_link_contract("clean-coder.md", mutated_body)
699
+
700
+
701
+ def test_named_agents_resolve_active_managed_root_and_agents_home() -> None:
702
+ agents_directory = Path(__file__).parent
703
+ for each_agent_file_name in (
704
+ "clean-coder.md",
705
+ "code-quality-agent.md",
706
+ "pr-description-writer.md",
707
+ ):
708
+ agent_text = (agents_directory / each_agent_file_name).read_text(
709
+ encoding="utf-8"
710
+ )
711
+ assert "active managed root" in agent_text.lower()
712
+ assert "active agents home" in agent_text.lower()
713
+ assert "CLAUDE_CONFIG_DIR" in agent_text
714
+ assert "--target" in agent_text
715
+ assert "<managed-root>/" in agent_text
716
+ assert "<agents-home>/" in agent_text
717
+ assert "do not assume" in agent_text.lower()
718
+
719
+
423
720
  def test_clean_coder_never_globs_or_reads_dotenv_files() -> None:
424
721
  body = _clean_coder_body()
425
722
  assert "`**/.env`" not in body
@@ -436,7 +733,336 @@ def test_clean_coder_uses_task_local_config_discovery() -> None:
436
733
  assert "Issue all seven Glob calls" not in body
437
734
 
438
735
 
736
+ def test_clean_coder_loads_scoped_agents_files_before_editing() -> None:
737
+ body = _clean_coder_body()
738
+ assert "scoped AGENTS.md" in body
739
+ assert body.count("Load scoped AGENTS.md files first.") == 1
740
+ assert "repository root" in body.lower()
741
+ assert "target directory" in body.lower()
742
+ assert "unrelated" in body.lower()
743
+
744
+
745
+ def test_clean_coder_explains_red_green_refactor_steps() -> None:
746
+ body = _clean_coder_body()
747
+ assert "behavior change" in body.lower()
748
+ assert "failing test" in body.lower()
749
+ assert "RED" in body
750
+ assert "GREEN" in body
751
+ assert "REFACTOR" in body
752
+ assert "write production behavior before red" in body.lower()
753
+
754
+
755
+ def test_clean_coder_distinguishes_code_rules_precheck_from_full_quality_gates() -> None:
756
+ body = _clean_coder_body()
757
+ assert "mechanical CODE_RULES" in body
758
+ assert "does not run tests" in body
759
+ assert "full quality gate" in body.lower()
760
+ assert "ruff" in body.lower()
761
+ assert "mypy" in body.lower()
762
+
763
+
764
+ def test_clean_coder_has_hook_specific_workflow() -> None:
765
+ body = _clean_coder_body()
766
+ assert "Hook-specific workflow" in body
767
+ assert "production entry point" in body.lower()
768
+ assert "JSON" in body
769
+ assert "registered" in body.lower()
770
+
771
+
772
+ def test_clean_coder_keeps_warm_session_advisor_triggers() -> None:
773
+ body = _clean_coder_body().lower()
774
+ assert "warm `session-advisor`" in body
775
+ assert "before substantive work" in body
776
+ assert "when you believe the task is complete" in body
777
+ assert "when stuck" in body
778
+ assert "change of approach" in body
779
+ assert "before any commit" in body
780
+
781
+
782
+
783
+ def test_clean_coder_separates_constants_and_caller_search() -> None:
784
+ body = _clean_coder_body()
785
+ assert "task-local constants search" in body
786
+ assert "caller boundary" in body
787
+
788
+
789
+ def test_clean_coder_scopes_task_artifact_guidance() -> None:
790
+ body = _clean_coder_body()
791
+ assert "Follow the target repo's policy for scratch, planning, and image files" in body
792
+ assert "No scratch/planning artifacts" not in body
793
+
794
+
795
+ def test_clean_coder_links_canonical_policy_areas() -> None:
796
+ body = _clean_coder_body()
797
+ required_links = (
798
+ "<managed-root>/docs/CODE_RULES.md#5-no-abbreviations",
799
+ "<managed-root>/rules/testing.md",
800
+ "<managed-root>/rules/ask-user-question-required.md",
801
+ "<managed-root>/rules/verify-runtime-state.md",
802
+ "<managed-root>/rules/doc-inventory-integrity.md",
803
+ "<managed-root>/rules/failure-blast-radius.md",
804
+ "<managed-root>/rules/git-workflow.md",
805
+ "<managed-root>/rules/workers-done-before-complete.md",
806
+ )
807
+ assert all(each_link in body for each_link in required_links)
808
+
809
+
810
+ def test_clean_coder_preserves_existing_comment_instruction() -> None:
811
+ body = _clean_coder_body()
812
+ assert (
813
+ "Note every existing comment so you can leave each one untouched on lines "
814
+ "that remain otherwise unchanged."
815
+ ) in body
816
+
439
817
  def test_clean_coder_examples_import_constants_from_config() -> None:
440
818
  body = _clean_coder_body()
441
819
  assert "from config.timing import MAXIMUM_RETRIES" in body
442
820
  assert re.search(r"(?m)^MAXIMUM_RETRIES\s*=\s*\d+", body) is None
821
+
822
+
823
+ def test_clean_coder_loads_scoped_agents_before_claude() -> None:
824
+ body = _clean_coder_body()
825
+ agents_instruction_at = body.index("Load scoped AGENTS.md files first")
826
+ claude_instruction_at = body.index("Then read the applicable `CLAUDE.md`")
827
+ assert agents_instruction_at < claude_instruction_at
828
+ assert "every applicable `AGENTS.md`" in body
829
+ assert "closest file wins" in body
830
+
831
+
832
+ def test_clean_coder_requires_red_green_refactor_for_behavior_changes() -> None:
833
+ body = _clean_coder_body()
834
+ assert "every behavior change" in body
835
+ assert "red → green → refactor" in body
836
+ assert "run it red" in body
837
+
838
+
839
+ def test_clean_coder_distinguishes_full_gate_from_candidate_check() -> None:
840
+ body = _clean_coder_body()
841
+ assert "pre-check tests CODE_RULES only" in body
842
+ assert "full project gate" in body
843
+ assert "candidate enforcer check" in body
844
+ assert "not the full gate" in body
845
+
846
+
847
+ def test_clean_coder_defines_a_hook_specific_workflow() -> None:
848
+ body = _clean_coder_body()
849
+ assert "Hook-specific workflow" in body
850
+ assert "hooks/AGENTS.md" in body
851
+ assert "stdin JSON" in body
852
+ assert "exit code" in body
853
+ assert "registration" in body
854
+ assert "constants package" in body
855
+ assert "check.ps1" in body
856
+ assert "every applicable test file and suite" in body
857
+
858
+
859
+ def test_clean_coder_uses_target_package_aware_hook_paths() -> None:
860
+ body = _clean_coder_body()
861
+ hook_workflow = body.split("## Hook-specific workflow", 1)[1].split(
862
+ "## Session advisor", 1
863
+ )[0]
864
+ hook_path_pattern = re.compile(
865
+ r"`(?P<managed><managed-root>/(?P<relative>hooks/AGENTS\.md|scripts/check\.ps1))` "
866
+ r"\(default: `(?P<default>~/.claude/(?P=relative))`; "
867
+ r"source fallback: `(?P<source>packages/claude-dev-env/(?P=relative))`\)"
868
+ )
869
+ path_pairs = {
870
+ (
871
+ each_match.group("managed"),
872
+ each_match.group("default"),
873
+ each_match.group("source"),
874
+ )
875
+ for each_match in hook_path_pattern.finditer(hook_workflow)
876
+ }
877
+ expected_pairs = {
878
+ (
879
+ "<managed-root>/hooks/AGENTS.md",
880
+ "~/.claude/hooks/AGENTS.md",
881
+ "packages/claude-dev-env/hooks/AGENTS.md",
882
+ ),
883
+ (
884
+ "<managed-root>/scripts/check.ps1",
885
+ "~/.claude/scripts/check.ps1",
886
+ "packages/claude-dev-env/scripts/check.ps1",
887
+ ),
888
+ }
889
+ assert path_pairs == expected_pairs
890
+ assert len(path_pairs) == len(expected_pairs)
891
+ repository_root = Path(__file__).parent.parents[3]
892
+ for each_source_path in (
893
+ "packages/claude-dev-env/hooks/AGENTS.md",
894
+ "packages/claude-dev-env/scripts/check.ps1",
895
+ ):
896
+ assert (repository_root / each_source_path).is_file()
897
+ assert "active managed root" in hook_workflow
898
+ assert "`~/.claude`" in hook_workflow
899
+ assert "`--target`" in hook_workflow
900
+ assert "`CLAUDE_CONFIG_DIR`" in hook_workflow
901
+
902
+
903
+ def test_clean_coder_uses_the_callers_warm_advisor_at_approved_triggers() -> None:
904
+ body = _clean_coder_body()
905
+ assert "caller's existing warm `session-advisor`" in body
906
+ assert "before first write" in body
907
+ assert "before locking a plan or interpretation" in body
908
+ assert "before a hard-to-reverse action" in body
909
+ assert "after repeated failure or a stall" in body
910
+ assert "changing approach" in body
911
+ assert "before completion" in body
912
+ assert "do not bind or spawn another advisor" in body
913
+
914
+
915
+ def test_clean_coder_policy_targets_exist_in_source_package() -> None:
916
+ _assert_source_link_contract("clean-coder.md", _clean_coder_body())
917
+
918
+
919
+ def test_code_quality_agent_allows_only_read_and_search_tools() -> None:
920
+ agent_definition_path = Path(__file__).parent / CODE_QUALITY_AGENT_FILENAME
921
+ parsed_frontmatter = yaml.safe_load(_frontmatter_block(agent_definition_path))
922
+
923
+ assert parsed_frontmatter["tools"] == list(CODE_QUALITY_READ_ONLY_TOOLS)
924
+
925
+
926
+ def test_code_quality_agent_contract_forbids_mutating_commands() -> None:
927
+ agent_definition_path = Path(__file__).parent / CODE_QUALITY_AGENT_FILENAME
928
+ body = agent_definition_path.read_text(encoding="utf-8")
929
+ assert "Use only `Read`, `Grep`, and `Glob`." in body
930
+ assert "Author zero edits." in body
931
+ assert "Run zero commits or pushes." in body
932
+ assert "run no commands that write files or create PRs." in body
933
+
934
+
935
+ def test_clean_coder_sets_a_small_complexity_budget() -> None:
936
+ body = _clean_coder_body()
937
+ assert "Complexity budget" in body
938
+ assert "1–2 files" in body
939
+ assert "~50–300 lines" in body
940
+ assert "about 40 executable lines" in body
941
+ assert "nesting level of 2" in body
942
+
943
+
944
+ def test_clean_coder_defines_a_liveness_boundary_for_dead_code_cleanup() -> None:
945
+ body = _clean_coder_body()
946
+ assert "orphaned or dead code" in body.lower()
947
+ assert "liveness boundary" in body
948
+ assert "live entry point" in body
949
+ assert "public API, plugin hook, or reflective dispatch" in body
950
+
951
+
952
+ def test_clean_coder_hands_the_full_diff_to_code_quality_agent() -> None:
953
+ body = _clean_coder_body()
954
+ assert "Full Code Quality Agent review handoff" in body
955
+ assert "code-quality-agent" in body
956
+ assert "full diff" in body
957
+ assert "all A–Q categories" in body
958
+
959
+
960
+ assert "Repair each actionable finding" in body
961
+ assert "rerun focused checks and the full project gate on the post-repair diff" in body
962
+ assert "Record both results" in body
963
+
964
+
965
+ def test_clean_coder_uses_evidence_based_completion_language() -> None:
966
+ body = _clean_coder_body()
967
+ assert "Evidence-Based Code Generation" in body
968
+ assert "recorded check results" in body
969
+ assert "Do not claim defect-free code" in body
970
+ assert "Zero-Defect" not in body
971
+ assert "zero-defect" not in body
972
+
973
+
974
+ def test_clean_coder_example_contains_real_code() -> None:
975
+ body = _clean_coder_body()
976
+ example = body.split("```python", 1)[1].split("```", 1)[0]
977
+ assert "..." not in example
978
+ assert "pass" not in example
979
+ assert "NotImplementedError" not in example
980
+
981
+
982
+ def test_clean_coder_requires_evidence_based_completion_wording() -> None:
983
+ body = _clean_coder_body()
984
+ assert "# Clean Coder — Evidence-Based Code Generation" in body
985
+ assert "reviewers find nothing" not in body
986
+ assert "Write clear code. Provide test and review evidence." in body
987
+ assert "candidate check" in body
988
+ assert "focused tests" in body
989
+ assert "full project gate" in body
990
+ assert "recorded check results" in body
991
+ assert "review evidence" in body
992
+ assert "open questions" in body
993
+ assert "Do not claim defect-free code" in body
994
+
995
+
996
+ def test_clean_coder_follows_target_package_constant_layouts() -> None:
997
+ body = _clean_coder_body()
998
+ assert "target package's existing constants layout" in body
999
+ assert "Reuse first" in body
1000
+ assert "shared policy" in body
1001
+ assert "multiple consumers" in body
1002
+ assert "file-global-constants" in body
1003
+ assert "add the constant to the appropriate config file" not in body
1004
+
1005
+
1006
+ def test_clean_coder_allows_requested_plan_packets_and_product_assets() -> None:
1007
+ body = _clean_coder_body()
1008
+ assert "No unasked scratch files" in body
1009
+ assert "valid plan packets" in body
1010
+ assert "uncommitted working files" in body
1011
+ assert "durable artifacts release" in body
1012
+ assert "not the repository tree" in body
1013
+ assert "docs/plans/*.md" not in body
1014
+ assert "or image assets" not in body
1015
+
1016
+
1017
+ def test_clean_coder_links_canonical_naming_guidance() -> None:
1018
+ body = _clean_coder_body()
1019
+ assert "canonical naming guidance" in body
1020
+ assert "CODE_RULES.md §5" in body
1021
+ assert "`each_` loops" not in body
1022
+ assert "`is_`/`has_`/`should_`/`can_`" not in body
1023
+
1024
+
1025
+ def test_clean_coder_groups_session_policy_references() -> None:
1026
+ body = _clean_coder_body()
1027
+ assert "Session policy map" in body
1028
+ for each_policy_group in (
1029
+ "Tests",
1030
+ "Questions",
1031
+ "Search and shell",
1032
+ "Runtime checks",
1033
+ "Documentation",
1034
+ "Batch failures",
1035
+ "Git",
1036
+ "Worker coordination",
1037
+ ):
1038
+ assert each_policy_group in body
1039
+ assert "ask-user-question-required.md" in body
1040
+ assert "workers-done-before-complete.md" in body
1041
+ assert "verify-runtime-state.md" in body
1042
+ assert "Material implementation questions must return to the caller" in body
1043
+ assert "AskUserQuestion" in body
1044
+ assert "do not ask in plain text or guess" in body
1045
+
1046
+ expected_session_policy_files = (
1047
+ "testing.md",
1048
+ "anti-corollary-tests.md",
1049
+ "ask-user-question-required.md",
1050
+ "verify-before-asking.md",
1051
+ "filesystem-search.md",
1052
+ "shell-invocation.md",
1053
+ "verify-runtime-state.md",
1054
+ "doc-inventory-integrity.md",
1055
+ "docstring-prose-matches-implementation.md",
1056
+ "failure-blast-radius.md",
1057
+ "git-workflow.md",
1058
+ "re-stage-before-commit.md",
1059
+ "agent-spawn-protocol.md",
1060
+ "workers-done-before-complete.md",
1061
+ )
1062
+ session_policy_map = body[body.index("## Session policy map") :]
1063
+ session_policy_links = SOURCE_LINK_PATTERN.findall(session_policy_map)
1064
+ for each_policy_file_name in expected_session_policy_files:
1065
+ assert (
1066
+ f"<managed-root>/rules/{each_policy_file_name}",
1067
+ f"packages/claude-dev-env/rules/{each_policy_file_name}",
1068
+ ) in session_policy_links