claude-dev-env 2.12.0 → 2.14.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.md +61 -0
- package/CLAUDE.md +1 -57
- package/_shared/AGENTS.md +15 -0
- package/_shared/CLAUDE.md +1 -15
- package/_shared/advisor/AGENTS.md +22 -0
- package/_shared/advisor/CLAUDE.md +1 -22
- package/_shared/pr-loop/AGENTS.md +27 -0
- package/_shared/pr-loop/CLAUDE.md +1 -27
- package/_shared/pr-loop/scripts/AGENTS.md +35 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -35
- package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
- package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +42 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +1 -42
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +26 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -26
- package/_shared/pr-loop/scripts/test_code_rules_gate.py +0 -26
- package/_shared/pr-loop/scripts/tests/AGENTS.md +44 -0
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -44
- package/_shared/process-tree/AGENTS.md +41 -0
- package/_shared/process-tree/CLAUDE.md +1 -41
- package/agents/AGENTS.md +31 -0
- package/agents/CLAUDE.md +1 -32
- package/agents/clean-coder.md +6 -6
- package/agents/pr-description-writer.md +11 -81
- package/agents/test_agent_frontmatter.py +4 -4
- package/audit-rubrics/AGENTS.md +43 -0
- package/audit-rubrics/CLAUDE.md +1 -43
- package/audit-rubrics/category_rubrics/AGENTS.md +37 -0
- package/audit-rubrics/category_rubrics/CLAUDE.md +1 -37
- package/audit-rubrics/prompts/AGENTS.md +37 -0
- package/audit-rubrics/prompts/CLAUDE.md +1 -37
- package/bin/AGENTS.md +112 -0
- package/bin/CLAUDE.md +1 -112
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-constants.mjs +6 -7
- package/bin/install.mjs +71 -13
- package/bin/install.prune.test.mjs +26 -0
- package/bin/install.settings-defaults.test.mjs +60 -33
- package/bin/install.test.mjs +36 -4
- package/commands/AGENTS.md +23 -0
- package/commands/CLAUDE.md +1 -23
- package/commands/hook-log-extract.md +3 -3
- package/commands/sr-loop.md +9 -5
- package/docs/AGENTS.md +32 -0
- package/docs/CLAUDE.md +1 -33
- package/docs/CODE_RULES.md +1 -1
- package/docs/imagegen.md +22 -0
- package/docs/references/AGENTS.md +17 -0
- package/docs/references/CLAUDE.md +1 -18
- package/hooks/AGENTS.md +31 -0
- package/hooks/CLAUDE.md +1 -31
- package/hooks/_gh_pr_author_swap_utils.py +14 -0
- package/hooks/advisory/AGENTS.md +16 -0
- package/hooks/advisory/CLAUDE.md +1 -16
- package/hooks/blocking/AGENTS.md +123 -0
- package/hooks/blocking/CLAUDE.md +1 -148
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +28 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +1 -28
- package/hooks/blocking/code_rules_banned_identifiers.py +1 -1
- package/hooks/blocking/code_rules_dead_module_constant.py +149 -37
- package/hooks/blocking/code_rules_probe_chains.py +6 -2
- package/hooks/blocking/config/AGENTS.md +10 -0
- package/hooks/blocking/config/CLAUDE.md +1 -22
- package/hooks/blocking/conventional_pr_title_gate.py +1 -2
- package/hooks/blocking/gh_pr_author_restore.py +7 -2
- package/hooks/blocking/intent_only_ending_blocker.py +11 -16
- package/hooks/blocking/inventory_intent_records/AGENTS.md +26 -0
- package/hooks/blocking/inventory_intent_records/CLAUDE.md +1 -26
- package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +26 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +1 -26
- package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +24 -0
- package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +1 -24
- package/hooks/blocking/send_user_file_open_locally_blocker.py +1 -1
- package/hooks/blocking/stop_dispatcher.py +4 -4
- package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +30 -0
- package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +1 -30
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -3
- package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +171 -0
- package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
- package/hooks/blocking/test_gh_pr_author_restore.py +2 -1
- package/hooks/blocking/test_intent_only_ending_blocker.py +4 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +6 -7
- package/hooks/blocking/test_send_user_file_open_locally_blocker.py +4 -3
- package/hooks/blocking/test_shared_stdin_adoption.py +0 -2
- package/hooks/diagnostic/AGENTS.md +43 -0
- package/hooks/diagnostic/CLAUDE.md +1 -43
- package/hooks/diagnostic/hook_log_extractor.py +4 -38
- package/hooks/diagnostic/hook_log_stop_wrapper.py +6 -155
- package/hooks/diagnostic/migrations/AGENTS.md +16 -0
- package/hooks/diagnostic/migrations/CLAUDE.md +1 -16
- package/hooks/diagnostic/queries/AGENTS.md +19 -0
- package/hooks/diagnostic/queries/CLAUDE.md +1 -19
- package/hooks/diagnostic/test_hook_log_extractor.py +10 -23
- package/hooks/diagnostic/test_hook_log_stop_wrapper.py +3 -331
- package/hooks/git-hooks/AGENTS.md +31 -0
- package/hooks/git-hooks/CLAUDE.md +1 -31
- package/hooks/git-hooks/git_hooks_constants/AGENTS.md +21 -0
- package/hooks/git-hooks/git_hooks_constants/CLAUDE.md +1 -21
- package/hooks/git-hooks/git_hooks_constants/__init__.py +4 -12
- package/hooks/git-hooks/pre_push.py +49 -118
- package/hooks/git-hooks/test_pre_push.py +111 -137
- package/hooks/hooks.json +0 -35
- package/hooks/hooks_constants/AGENTS.md +104 -0
- package/hooks/hooks_constants/CLAUDE.md +1 -113
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -11
- package/hooks/hooks_constants/convergence_branch_constants.py +0 -1
- package/hooks/hooks_constants/fable_spawn_gate_constants.py +3 -4
- package/hooks/hooks_constants/hook_log_extractor_constants.py +1 -2
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -8
- package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +1 -6
- package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
- package/hooks/hooks_constants/task_list_loop_starter_constants.py +4 -3
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -26
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +0 -5
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
- package/hooks/hooks_constants/working_style_prompt_constants.py +29 -20
- package/hooks/lifecycle/AGENTS.md +18 -0
- package/hooks/lifecycle/CLAUDE.md +1 -20
- package/hooks/observability/AGENTS.md +20 -0
- package/hooks/observability/CLAUDE.md +1 -20
- package/hooks/session/AGENTS.md +33 -0
- package/hooks/session/CLAUDE.md +1 -33
- package/hooks/session/gh_pr_author_session_cleanup.py +7 -2
- package/hooks/session/task_list_loop_starter.py +3 -3
- package/hooks/session/test_gh_pr_author_session_cleanup.py +2 -1
- package/hooks/session/test_task_list_loop_starter.py +10 -2
- package/hooks/session/test_working_style_prompt.py +9 -4
- package/hooks/validation/AGENTS.md +20 -0
- package/hooks/validation/CLAUDE.md +1 -20
- package/hooks/validators/AGENTS.md +51 -0
- package/hooks/validators/CLAUDE.md +1 -51
- package/hooks/validators/health_check.py +52 -19
- package/hooks/validators/python_style_checks.py +1 -1
- package/hooks/validators/test_health_check.py +112 -27
- package/hooks/workflow/AGENTS.md +16 -0
- package/hooks/workflow/CLAUDE.md +1 -16
- package/installable-surfaces.manifest.json +1 -0
- package/output-styles/AGENTS.md +15 -0
- package/output-styles/CLAUDE.md +1 -15
- package/package.json +2 -1
- package/rules/AGENTS.md +61 -0
- package/rules/CLAUDE.md +1 -63
- package/rules/code-standards.md +5 -9
- package/rules/doc-prose-cuts.md +1 -1
- package/rules/gh-cli-conventions.md +0 -1
- package/rules/git-workflow.md +1 -3
- package/scripts/AGENTS.md +62 -0
- package/scripts/CLAUDE.md +1 -63
- package/scripts/_code_review_test_support.py +0 -95
- package/scripts/codex_compat_materializer.py +4 -0
- package/scripts/dev_env_scripts_constants/AGENTS.md +21 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -21
- package/scripts/dev_env_scripts_constants/code_review_constants.py +9 -98
- package/scripts/dev_env_scripts_constants/test_code_review_constants.py +1 -44
- package/scripts/invoke_code_review.py +8 -451
- package/scripts/sync_to_cursor/AGENTS.md +23 -0
- package/scripts/sync_to_cursor/CLAUDE.md +1 -23
- package/scripts/test_invoke_code_review.py +5 -61
- package/scripts/test_invoke_code_review_cli.py +1 -45
- package/scripts/test_invoke_code_review_contract.py +1 -118
- package/scripts/test_validate_instruction_pairs.py +90 -0
- package/scripts/test_verify_installable_package.py +5 -1
- package/scripts/tests/AGENTS.md +33 -0
- package/scripts/tests/CLAUDE.md +1 -33
- package/scripts/tests/test_codex_compat_materializer.py +36 -1
- package/scripts/validate_instruction_pairs.py +183 -0
- package/settings.json +1 -10
- package/skills/AGENTS.md +72 -0
- package/skills/CLAUDE.md +1 -64
- package/skills/_shared/AGENTS.md +44 -0
- package/skills/_shared/CLAUDE.md +1 -44
- package/skills/_shared/advisor/AGENTS.md +9 -0
- package/skills/_shared/advisor/CLAUDE.md +1 -9
- package/skills/_shared/pr-loop/AGENTS.md +50 -0
- package/skills/_shared/pr-loop/CLAUDE.md +1 -50
- package/skills/_shared/pr-loop/prompts/AGENTS.md +9 -0
- package/skills/_shared/pr-loop/prompts/CLAUDE.md +1 -9
- package/skills/_shared/pr-loop/scripts/AGENTS.md +35 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -35
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +24 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -24
- package/skills/anthropic-plan/AGENTS.md +34 -0
- package/skills/anthropic-plan/CLAUDE.md +1 -34
- package/skills/anthropic-plan/scripts/AGENTS.md +11 -0
- package/skills/anthropic-plan/scripts/CLAUDE.md +1 -11
- package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +16 -0
- package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/CLAUDE.md +1 -16
- package/skills/anthropic-plan/templates/AGENTS.md +13 -0
- package/skills/anthropic-plan/templates/CLAUDE.md +1 -13
- package/skills/anthropic-plan/workflow/AGENTS.md +14 -0
- package/skills/anthropic-plan/workflow/CLAUDE.md +1 -14
- package/skills/auditing-claude-config/AGENTS.md +21 -0
- package/skills/auditing-claude-config/CLAUDE.md +1 -21
- package/skills/autoconverge/AGENTS.md +36 -0
- package/skills/autoconverge/CLAUDE.md +1 -36
- package/skills/autoconverge/reference/AGENTS.md +16 -0
- package/skills/autoconverge/reference/CLAUDE.md +1 -16
- package/skills/autoconverge/reference/convergence.md +7 -7
- package/skills/autoconverge/reference/gotchas.md +3 -10
- package/skills/autoconverge/workflow/AGENTS.md +26 -0
- package/skills/autoconverge/workflow/CLAUDE.md +1 -26
- package/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +16 -0
- package/skills/autoconverge/workflow/autoconverge_report_constants/CLAUDE.md +1 -16
- package/skills/autoconverge/workflow/converge.contract.test.mjs +53 -1388
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +4 -3
- package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +33 -493
- package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +15 -97
- package/skills/autoconverge/workflow/converge.mjs +201 -238
- package/skills/autoconverge/workflow/converge.precatch.test.mjs +1 -1
- package/skills/bugteam/AGENTS.md +30 -0
- package/skills/bugteam/CLAUDE.md +1 -30
- package/skills/bugteam/CONSTRAINTS.md +1 -1
- package/skills/bugteam/SKILL.md +9 -0
- package/skills/bugteam/reference/AGENTS.md +20 -0
- package/skills/bugteam/reference/CLAUDE.md +1 -20
- package/skills/bugteam/reference/audit-and-teammates.md +10 -6
- package/skills/bugteam/reference/obstacles/AGENTS.md +24 -0
- package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -24
- package/skills/bugteam/scripts/AGENTS.md +30 -0
- package/skills/bugteam/scripts/CLAUDE.md +1 -30
- package/skills/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +18 -0
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +1 -18
- package/skills/bugteam/test_skill_additions.py +9 -0
- package/skills/closeout/reference/handoff-prompt-template.md +1 -1
- package/skills/codex-review/AGENTS.md +46 -0
- package/skills/codex-review/CLAUDE.md +1 -46
- package/skills/codex-review/reference/AGENTS.md +15 -0
- package/skills/codex-review/reference/CLAUDE.md +1 -15
- package/skills/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +1 -18
- package/skills/codex-review/test_skill_scaffold.py +3 -3
- package/skills/comments/SKILL.md +65 -0
- package/skills/copilot-review/AGENTS.md +18 -0
- package/skills/copilot-review/CLAUDE.md +1 -18
- package/skills/descriptions/SKILL.md +51 -0
- package/skills/emergencies/SKILL.md +42 -0
- package/skills/everything-search/AGENTS.md +17 -0
- package/skills/everything-search/CLAUDE.md +1 -17
- package/skills/fresh-branch/AGENTS.md +14 -0
- package/skills/fresh-branch/CLAUDE.md +1 -14
- package/skills/grok-spawn/AGENTS.md +28 -0
- package/skills/grok-spawn/CLAUDE.md +1 -28
- package/skills/orchestrator/SKILL.md +2 -4
- package/skills/plan-to-pr/SKILL.md +6 -7
- package/skills/plan-to-pr/reference/final-validation-tasks.md +1 -1
- package/skills/plan-to-pr/reference/process-inventory.md +2 -2
- package/skills/plan-to-pr/reference/review-loop.md +2 -2
- package/skills/plan-to-pr/reference/run-record.schema.json +1 -1
- package/skills/plan-to-pr/reference/task-seeds.md +1 -1
- package/skills/plan-to-pr/reference/task-ticket.md +3 -4
- package/skills/plan-to-pr/scripts/config/constants.py +1 -2
- package/skills/plan-to-pr/scripts/test_validate_protocol.py +1 -1
- package/skills/plan-to-pr/test_skill_contract.py +2 -2
- package/skills/pr-converge/AGENTS.md +32 -0
- package/skills/pr-converge/CLAUDE.md +1 -32
- package/skills/pr-converge/SKILL.md +13 -16
- package/skills/pr-converge/pr_converge_skill_constants/AGENTS.md +26 -0
- package/skills/pr-converge/pr_converge_skill_constants/CLAUDE.md +1 -26
- package/skills/pr-converge/reference/AGENTS.md +28 -0
- package/skills/pr-converge/reference/CLAUDE.md +1 -28
- package/skills/pr-converge/reference/obstacles/AGENTS.md +23 -0
- package/skills/pr-converge/reference/obstacles/CLAUDE.md +1 -23
- package/skills/pr-converge/reference/progress-checklist.md +6 -5
- package/skills/pr-converge/reference/state-schema.md +1 -27
- package/skills/pr-converge/scripts/AGENTS.md +46 -0
- package/skills/pr-converge/scripts/CLAUDE.md +1 -46
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -18
- package/skills/pr-converge/workflows/AGENTS.md +16 -0
- package/skills/pr-converge/workflows/CLAUDE.md +1 -16
- package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
- package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +3 -3
- package/skills/pr-loop-lifecycle/SKILL.md +1 -1
- package/skills/pr-loop-lifecycle/reference/teardown-publish-permissions.md +1 -1
- package/skills/prototype/SKILL.md +4 -4
- package/skills/prototype/reference/honest-limitations.md +5 -5
- package/skills/prototype/reference/promotion-tasks.md +2 -2
- package/skills/prototype/workflows/promotion.md +4 -6
- package/skills/rebase/AGENTS.md +32 -0
- package/skills/rebase/CLAUDE.md +1 -32
- package/skills/recall/AGENTS.md +30 -0
- package/skills/recall/CLAUDE.md +1 -30
- package/skills/remember/AGENTS.md +31 -0
- package/skills/remember/CLAUDE.md +1 -31
- package/skills/reviews/SKILL.md +85 -0
- package/skills/session-log/AGENTS.md +32 -0
- package/skills/session-log/CLAUDE.md +1 -32
- package/skills/session-tidy/AGENTS.md +36 -0
- package/skills/session-tidy/CLAUDE.md +1 -36
- package/skills/skill-builder/AGENTS.md +49 -0
- package/skills/skill-builder/CLAUDE.md +1 -49
- package/skills/skill-builder/references/AGENTS.md +25 -0
- package/skills/skill-builder/references/CLAUDE.md +1 -25
- package/skills/skill-builder/references/deterministic-elements.md +1 -1
- package/skills/skill-builder/templates/AGENTS.md +14 -0
- package/skills/skill-builder/templates/CLAUDE.md +1 -14
- package/skills/skill-builder/workflows/AGENTS.md +19 -0
- package/skills/skill-builder/workflows/CLAUDE.md +1 -19
- package/skills/small-cl/SKILL.md +52 -0
- package/skills/task-build/AGENTS.md +29 -0
- package/skills/task-build/CLAUDE.md +1 -29
- package/skills/update/AGENTS.md +38 -0
- package/skills/update/CLAUDE.md +1 -38
- package/system-prompts/AGENTS.md +17 -0
- package/system-prompts/CLAUDE.md +1 -17
- package/system-prompts/software-engineer.xml +3 -3
- package/agents/code-verifier.md +0 -74
- package/docs/PR_DESCRIPTION_GUIDE.md +0 -157
- package/docs/references/code-review-enforcement.md +0 -132
- package/hooks/blocking/code_review_enforcement_config_bootstrap.py +0 -53
- package/hooks/blocking/code_review_gate_deny.py +0 -74
- package/hooks/blocking/code_review_pr_create_gate.py +0 -198
- package/hooks/blocking/code_review_push_gate.py +0 -145
- package/hooks/blocking/code_review_stamp_directory_write_blocker.py +0 -348
- package/hooks/blocking/code_review_stamp_store.py +0 -233
- package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +0 -7
- package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +0 -15
- package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +0 -212
- package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +0 -138
- package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +0 -49
- package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +0 -38
- package/hooks/blocking/code_verifier_spawn_preflight_gate.py +0 -483
- package/hooks/blocking/config/code_review_enforcement_constants.py +0 -143
- package/hooks/blocking/config/test_code_review_enforcement_constants.py +0 -166
- package/hooks/blocking/config/verified_commit_constants.py +0 -160
- package/hooks/blocking/config/verified_commit_context_constants.py +0 -21
- package/hooks/blocking/config/verified_commit_gate_output_constants.py +0 -14
- package/hooks/blocking/conftest.py +0 -123
- package/hooks/blocking/convergence_gate_blocker.py +0 -310
- package/hooks/blocking/pr_converge_bugteam_enforcer.py +0 -170
- package/hooks/blocking/pr_description_body_audit.py +0 -148
- package/hooks/blocking/pr_description_command_parser.py +0 -237
- package/hooks/blocking/pr_description_enforcer.py +0 -227
- package/hooks/blocking/pr_description_pr_number.py +0 -155
- package/hooks/blocking/pr_description_proof_of_work.py +0 -385
- package/hooks/blocking/pr_description_readability.py +0 -364
- package/hooks/blocking/reviewer_spawn_gate.py +0 -182
- package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +0 -62
- package/hooks/blocking/test_code_review_gate_deny.py +0 -54
- package/hooks/blocking/test_code_review_pr_create_gate.py +0 -199
- package/hooks/blocking/test_code_review_push_gate.py +0 -205
- package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +0 -199
- package/hooks/blocking/test_code_review_stamp_store.py +0 -205
- package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +0 -710
- package/hooks/blocking/test_code_verifier_tools_contract.py +0 -28
- package/hooks/blocking/test_convergence_gate_blocker.py +0 -428
- package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +0 -311
- package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +0 -184
- package/hooks/blocking/test_pr_description_enforcer.py +0 -221
- package/hooks/blocking/test_pr_description_enforcer_body_audit.py +0 -247
- package/hooks/blocking/test_pr_description_enforcer_body_rules.py +0 -493
- package/hooks/blocking/test_pr_description_enforcer_command_parser.py +0 -366
- package/hooks/blocking/test_pr_description_enforcer_pr_number.py +0 -159
- package/hooks/blocking/test_pr_description_enforcer_proof_gate.py +0 -175
- package/hooks/blocking/test_pr_description_enforcer_readability.py +0 -443
- package/hooks/blocking/test_pr_description_proof_of_work.py +0 -313
- package/hooks/blocking/test_reviewer_spawn_gate.py +0 -230
- package/hooks/blocking/test_verdict_directory_write_blocker.py +0 -804
- package/hooks/blocking/test_verification_verdict_store.py +0 -974
- package/hooks/blocking/test_verified_commit_config_bootstrap.py +0 -67
- package/hooks/blocking/test_verified_commit_docs_delta.py +0 -176
- package/hooks/blocking/test_verified_commit_gate.py +0 -581
- package/hooks/blocking/test_verified_commit_gate_additional_context.py +0 -134
- package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +0 -131
- package/hooks/blocking/test_verifier_verdict_minter.py +0 -299
- package/hooks/blocking/tests/test_verified_commit_gate.py +0 -41
- package/hooks/blocking/verdict_directory_write_blocker.py +0 -687
- package/hooks/blocking/verification_verdict_store.py +0 -1039
- package/hooks/blocking/verified_commit_config_bootstrap.py +0 -63
- package/hooks/blocking/verified_commit_gate.py +0 -113
- package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +0 -28
- package/hooks/blocking/verified_commit_gate_parts/__init__.py +0 -1
- package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +0 -174
- package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +0 -53
- package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +0 -80
- package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +0 -170
- package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +0 -217
- package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +0 -10
- package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +0 -94
- package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +0 -17
- package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +0 -38
- package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +0 -71
- package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +0 -96
- package/hooks/blocking/verified_commit_message_accuracy_blocker.py +0 -167
- package/hooks/blocking/verifier_verdict_minter.py +0 -280
- package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +0 -64
- package/hooks/hooks_constants/convergence_gate_blocker_constants.py +0 -53
- package/hooks/hooks_constants/pr_converge_bugteam_enforcer_constants.py +0 -55
- package/hooks/hooks_constants/pr_converge_bugteam_enforcer_state.py +0 -67
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +0 -167
- package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -107
- package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +0 -41
- package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +0 -292
- package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +0 -198
- package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +0 -283
- package/rules/proof-of-work-pr-comments.md +0 -22
- package/rules/verified-commit-gate-skip.md +0 -4
- package/scripts/Show-Asset.ps1 +0 -106
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""Attribute-chain and os.environ alias-resolution primitives
|
|
1
|
+
"""Attribute-chain and os.environ alias-resolution primitives.
|
|
2
|
+
|
|
3
|
+
Serves the test-isolation check, and lends ``_dotted_attribute_chain`` to the
|
|
4
|
+
dead-module-constant check's widened attribute-read scan.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
import ast
|
|
4
8
|
import sys
|
|
@@ -143,7 +147,7 @@ def _attribute_chain_resolves_to_os_environ(
|
|
|
143
147
|
return canonical_chain == OS_ENVIRON_DOTTED_NAME
|
|
144
148
|
|
|
145
149
|
|
|
146
|
-
def _dotted_attribute_chain(attribute_node: ast.
|
|
150
|
+
def _dotted_attribute_chain(attribute_node: ast.expr) -> str | None:
|
|
147
151
|
chain_parts: list[str] = []
|
|
148
152
|
walker: ast.expr = attribute_node
|
|
149
153
|
while isinstance(walker, ast.Attribute):
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# hooks/blocking/config
|
|
2
|
+
|
|
3
|
+
A Python package that holds shared constants for blocking hooks. Opinionated prose-style gates import from here.
|
|
4
|
+
|
|
5
|
+
## Key files
|
|
6
|
+
|
|
7
|
+
| File | Contents |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `__init__.py` | Declares this as a regular package (not a namespace package) so it resolves first on `sys.path` |
|
|
10
|
+
| `prose_style_enforcement_constants.py` | `CLAUDE_PROSE_STYLE_ENFORCEMENT` opt-in (default off) for opinionated prose gates |
|
|
@@ -1,22 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
A Python package that holds shared constants for blocking hooks. Verified-commit gate modules and opinionated prose-style gates import from here.
|
|
4
|
-
|
|
5
|
-
## Key files
|
|
6
|
-
|
|
7
|
-
| File | Contents |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `__init__.py` | Declares this as a regular package (not a namespace package) so it resolves first on `sys.path` |
|
|
10
|
-
| `code_review_enforcement_constants.py` | Stamp and effort tunables for the code-review enforcement gates |
|
|
11
|
-
| `prose_style_enforcement_constants.py` | `CLAUDE_PROSE_STYLE_ENFORCEMENT` opt-in (default off) for opinionated prose gates |
|
|
12
|
-
| `verified_commit_constants.py` | All tunables for the gate: directory names, regex patterns for detecting verdict paths and obfuscation attempts, timeout values, git subcommand sets, bypass marker, and corrective messages |
|
|
13
|
-
| `verified_commit_context_constants.py` | `VERIFY_SKIP_ADDITIONAL_CONTEXT`, the ``# verify-skip`` usage rule the gate attaches to a deny payload's `additionalContext` |
|
|
14
|
-
| `verified_commit_gate_output_constants.py` | `PRE_TOOL_USE_HOOK_EVENT_NAME` and `DENY_PERMISSION_DECISION` (the deny payload's event name and decision string), `GATE_HOOK_MODULE_NAME` (the gate's own module name for block logging), and `REGEX_ALTERNATION_SEPARATOR` (the `\|` join for the directory-change verb alternation in `gated_invocations.py`) |
|
|
15
|
-
|
|
16
|
-
## Key constants in `verified_commit_constants.py`
|
|
17
|
-
|
|
18
|
-
- `VERIFICATION_BYPASS_MARKER` — the `# verify-skip` comment that exempts a single commit/push from the gate
|
|
19
|
-
- `MINTING_AGENT_TYPE` — `"code-verifier"`, the agent type whose SubagentStop hook mints verdicts
|
|
20
|
-
- `VERDICT_DIRECTORY_NAME` — `"verification"`, the directory under `~/.claude/` that holds verdict JSON files
|
|
21
|
-
- `DOCS_ONLY_EXTENSIONS` — extensions (`.md`, `.txt`, images) whose changes are mechanically exempt from the gate
|
|
22
|
-
- `CORRECTIVE_MESSAGE` / `VERDICT_DIRECTORY_GUARD_MESSAGE` — user-facing block messages
|
|
1
|
+
@AGENTS.md
|
|
@@ -36,8 +36,6 @@ _hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
|
36
36
|
if _hooks_dir not in sys.path:
|
|
37
37
|
sys.path.insert(0, _hooks_dir)
|
|
38
38
|
|
|
39
|
-
from verification_verdict_store import resolve_repo_root # noqa: E402
|
|
40
|
-
|
|
41
39
|
from blocking._gh_body_arg_utils import ( # noqa: E402
|
|
42
40
|
all_value_flags,
|
|
43
41
|
count_extra_tokens_to_skip_for_split_quoted_value,
|
|
@@ -68,6 +66,7 @@ from hooks_constants.conventional_pr_title_gate_constants import ( # noqa: E402
|
|
|
68
66
|
YAML_LIST_ITEM_PREFIX,
|
|
69
67
|
)
|
|
70
68
|
from hooks_constants.hook_block_logger import log_hook_block # noqa: E402
|
|
69
|
+
from hooks_constants.project_paths_reader import find_git_root as resolve_repo_root # noqa: E402
|
|
71
70
|
|
|
72
71
|
|
|
73
72
|
def _matches_gh_pr_title_subcommand(command: str) -> bool:
|
|
@@ -36,6 +36,7 @@ if hooks_parent_directory not in sys.path:
|
|
|
36
36
|
sys.path.insert(0, hooks_parent_directory)
|
|
37
37
|
|
|
38
38
|
from _gh_pr_author_swap_utils import ( # noqa: E402
|
|
39
|
+
_build_restore_failure_message,
|
|
39
40
|
_command_invokes_gh_pr_create_in_stripped,
|
|
40
41
|
_delete_state_file,
|
|
41
42
|
_preprocess_command_for_matching,
|
|
@@ -89,8 +90,12 @@ def main() -> None:
|
|
|
89
90
|
_delete_state_file(state_file)
|
|
90
91
|
else:
|
|
91
92
|
_write_line(
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
_build_restore_failure_message(
|
|
94
|
+
"gh-pr-author-restore",
|
|
95
|
+
original_account,
|
|
96
|
+
state_file,
|
|
97
|
+
"state file",
|
|
98
|
+
),
|
|
94
99
|
sys.stderr,
|
|
95
100
|
)
|
|
96
101
|
sys.exit(0)
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Stop hook that blocks Claude responses ending on a promise about undone work.
|
|
2
|
+
"""Stop hook that blocks a turn ending with a promise of future work.
|
|
4
3
|
|
|
5
|
-
When a turn ends
|
|
6
|
-
tests", "Let me implement the fix")
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
cleanly. The rule name is long-horizon-autonomy.
|
|
4
|
+
When a turn ends with a forward-looking statement of intent ("I'll now run the
|
|
5
|
+
tests", "Let me implement the fix"), complete the work with tool calls during
|
|
6
|
+
the turn. Route work requiring user-only input through AskUserQuestion. The
|
|
7
|
+
rule name is long-horizon-autonomy.
|
|
10
8
|
"""
|
|
11
9
|
|
|
12
10
|
import json
|
|
@@ -104,7 +102,7 @@ def find_intent_only_ending(text: str) -> bool:
|
|
|
104
102
|
|
|
105
103
|
|
|
106
104
|
def main() -> None:
|
|
107
|
-
"""Read the stop-hook payload and block turns that
|
|
105
|
+
"""Read the stop-hook payload and block turns that promise future work."""
|
|
108
106
|
try:
|
|
109
107
|
hook_input = json.load(sys.stdin)
|
|
110
108
|
except json.JSONDecodeError:
|
|
@@ -125,14 +123,11 @@ def main() -> None:
|
|
|
125
123
|
sys.exit(0)
|
|
126
124
|
|
|
127
125
|
block_reason = (
|
|
128
|
-
"LONG-HORIZON-AUTONOMY GUARDRAIL:
|
|
129
|
-
"that
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"carry out the stated action this turn.\n\n"
|
|
134
|
-
"You MUST re-output the complete response with the work actually performed, "
|
|
135
|
-
"per the long-horizon-autonomy rule."
|
|
126
|
+
"LONG-HORIZON-AUTONOMY GUARDRAIL: This turn ends with a promise of future "
|
|
127
|
+
"work. Complete that work with tool calls during this turn. Route questions "
|
|
128
|
+
"requiring user-only input through AskUserQuestion, then end the turn cleanly.\n\n"
|
|
129
|
+
"Re-output the complete response with the work performed, per the "
|
|
130
|
+
"long-horizon-autonomy rule."
|
|
136
131
|
)
|
|
137
132
|
block_response = {
|
|
138
133
|
"decision": "block",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# inventory_intent_records
|
|
2
|
+
|
|
3
|
+
The shared pending-intent store that lets `package_inventory_stale_blocker.py`
|
|
4
|
+
and `claude_md_orphan_file_blocker.py` add a new file and its inventory row in
|
|
5
|
+
one change, in either order. When one blocker denies, it records a note; the
|
|
6
|
+
sibling blocker reads that note to allow the matching second write.
|
|
7
|
+
|
|
8
|
+
## Modules
|
|
9
|
+
|
|
10
|
+
| File | Purpose |
|
|
11
|
+
|---|---|
|
|
12
|
+
| `records.py` | Read, record, peek, and consume the per-session file and row intents; a missing or corrupt store reads as no notes |
|
|
13
|
+
| `__init__.py` | Package marker |
|
|
14
|
+
|
|
15
|
+
## Subdirectories
|
|
16
|
+
|
|
17
|
+
| Entry | Description |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `config/` | The records-file name shape, the freshness window, the list keys, and the note field names (`intent_records_constants.py`) |
|
|
20
|
+
| `tests/` | pytest suite for the records store |
|
|
21
|
+
|
|
22
|
+
## Running tests
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
python -m pytest packages/claude-dev-env/hooks/blocking/inventory_intent_records/tests/
|
|
26
|
+
```
|
|
@@ -1,26 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
The shared pending-intent store that lets `package_inventory_stale_blocker.py`
|
|
4
|
-
and `claude_md_orphan_file_blocker.py` add a new file and its inventory row in
|
|
5
|
-
one change, in either order. When one blocker denies, it records a note; the
|
|
6
|
-
sibling blocker reads that note to allow the matching second write.
|
|
7
|
-
|
|
8
|
-
## Modules
|
|
9
|
-
|
|
10
|
-
| File | Purpose |
|
|
11
|
-
|---|---|
|
|
12
|
-
| `records.py` | Read, record, peek, and consume the per-session file and row intents; a missing or corrupt store reads as no notes |
|
|
13
|
-
| `__init__.py` | Package marker |
|
|
14
|
-
|
|
15
|
-
## Subdirectories
|
|
16
|
-
|
|
17
|
-
| Entry | Description |
|
|
18
|
-
|---|---|
|
|
19
|
-
| `config/` | The records-file name shape, the freshness window, the list keys, and the note field names (`intent_records_constants.py`) |
|
|
20
|
-
| `tests/` | pytest suite for the records store |
|
|
21
|
-
|
|
22
|
-
## Running tests
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
python -m pytest packages/claude-dev-env/hooks/blocking/inventory_intent_records/tests/
|
|
26
|
-
```
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# package_inventory_stale_blocker_parts
|
|
2
|
+
|
|
3
|
+
The concern modules `package_inventory_stale_blocker.py` wires together to block a
|
|
4
|
+
new production file its package inventory omits. The entry hook imports them and
|
|
5
|
+
re-exports their surface for the test suite.
|
|
6
|
+
|
|
7
|
+
## Modules
|
|
8
|
+
|
|
9
|
+
| File | Purpose |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `inventory_detection.py` | Surveys a directory's `README.md`/`CLAUDE.md`/`SKILL.md`, collects the filenames they name, and reports whether a maintained inventory omits the file being written |
|
|
12
|
+
| `decision.py` | Builds the deny payload naming the omitted file and closing with the retry hint, and emits the decision JSON |
|
|
13
|
+
| `__init__.py` | Package marker |
|
|
14
|
+
|
|
15
|
+
## Subdirectories
|
|
16
|
+
|
|
17
|
+
| Entry | Description |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `config/` | The retry hint the deny reason closes with and the inventory-name join separator (`inventory_blocker_constants.py`) |
|
|
20
|
+
| `tests/` | pytest suite with one test module per module above |
|
|
21
|
+
|
|
22
|
+
## Running tests
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
python -m pytest packages/claude-dev-env/hooks/blocking/package_inventory_stale_blocker_parts/tests/
|
|
26
|
+
```
|
|
@@ -1,26 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
The concern modules `package_inventory_stale_blocker.py` wires together to block a
|
|
4
|
-
new production file its package inventory omits. The entry hook imports them and
|
|
5
|
-
re-exports their surface for the test suite.
|
|
6
|
-
|
|
7
|
-
## Modules
|
|
8
|
-
|
|
9
|
-
| File | Purpose |
|
|
10
|
-
|---|---|
|
|
11
|
-
| `inventory_detection.py` | Surveys a directory's `README.md`/`CLAUDE.md`/`SKILL.md`, collects the filenames they name, and reports whether a maintained inventory omits the file being written |
|
|
12
|
-
| `decision.py` | Builds the deny payload naming the omitted file and closing with the retry hint, and emits the decision JSON |
|
|
13
|
-
| `__init__.py` | Package marker |
|
|
14
|
-
|
|
15
|
-
## Subdirectories
|
|
16
|
-
|
|
17
|
-
| Entry | Description |
|
|
18
|
-
|---|---|
|
|
19
|
-
| `config/` | The retry hint the deny reason closes with and the inventory-name join separator (`inventory_blocker_constants.py`) |
|
|
20
|
-
| `tests/` | pytest suite with one test module per module above |
|
|
21
|
-
|
|
22
|
-
## Running tests
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
python -m pytest packages/claude-dev-env/hooks/blocking/package_inventory_stale_blocker_parts/tests/
|
|
26
|
-
```
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# pii_prevention_blocker_parts
|
|
2
|
+
|
|
3
|
+
The concern modules `pii_prevention_blocker.py` wires together. The entry hook
|
|
4
|
+
imports them and re-exports their surface for the test suite.
|
|
5
|
+
|
|
6
|
+
## Modules
|
|
7
|
+
|
|
8
|
+
| File | Purpose |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `repository_exemption.py` | Resolves a commit's origin owner/repo slug, decides whether the repository skips the staged PII scan, and resolves the exact values that repository allows past the scan |
|
|
11
|
+
| `repository_resolution.py` | Reads the repository a commit command targets — composing multiple `-C` values and a leading `cd`/`pushd` — and builds the deny reason naming the path when the repository root does not resolve |
|
|
12
|
+
| `__init__.py` | Package marker |
|
|
13
|
+
|
|
14
|
+
## Subdirectories
|
|
15
|
+
|
|
16
|
+
| Entry | Description |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `config/` | The repository-resolution deny-message template and the session-cwd label (`repository_resolution_constants.py`) |
|
|
19
|
+
|
|
20
|
+
## Running tests
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m pytest packages/claude-dev-env/hooks/blocking/tests/
|
|
24
|
+
```
|
|
@@ -1,24 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
The concern modules `pii_prevention_blocker.py` wires together. The entry hook
|
|
4
|
-
imports them and re-exports their surface for the test suite.
|
|
5
|
-
|
|
6
|
-
## Modules
|
|
7
|
-
|
|
8
|
-
| File | Purpose |
|
|
9
|
-
|---|---|
|
|
10
|
-
| `repository_exemption.py` | Resolves a commit's origin owner/repo slug, decides whether the repository skips the staged PII scan, and resolves the exact values that repository allows past the scan |
|
|
11
|
-
| `repository_resolution.py` | Reads the repository a commit command targets — composing multiple `-C` values and a leading `cd`/`pushd` — and builds the deny reason naming the path when the repository root does not resolve |
|
|
12
|
-
| `__init__.py` | Package marker |
|
|
13
|
-
|
|
14
|
-
## Subdirectories
|
|
15
|
-
|
|
16
|
-
| Entry | Description |
|
|
17
|
-
|---|---|
|
|
18
|
-
| `config/` | The repository-resolution deny-message template and the session-cwd label (`repository_resolution_constants.py`) |
|
|
19
|
-
|
|
20
|
-
## Running tests
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
python -m pytest packages/claude-dev-env/hooks/blocking/tests/
|
|
24
|
-
```
|
|
1
|
+
@AGENTS.md
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
SendUserFile attaches a file to the session. While the user is at the terminal
|
|
5
5
|
(status "normal" or unset) an attach does not let them see the file — it must
|
|
6
|
-
open on screen in its
|
|
6
|
+
open on screen in its native Windows app. The one attach allowed
|
|
7
7
|
through is an away-from-desk phone push (status "proactive").
|
|
8
8
|
"""
|
|
9
9
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Stop-hook dispatcher that hosts the
|
|
2
|
+
"""Stop-hook dispatcher that hosts the Stop-chain blockers in one process.
|
|
3
3
|
|
|
4
4
|
Reads the Stop payload from stdin once, runs each hosted hook in registration
|
|
5
5
|
order via the shared hosted-hook runner, and emits the first block decision the
|
|
6
|
-
chain produced. Later hooks still run
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
chain produced. Later hooks still run after an earlier block so every hosted
|
|
7
|
+
hook gets a chance to run. A single hosted hook crash fails open and does not
|
|
8
|
+
suppress the remaining hooks.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# tdd_enforcer_parts
|
|
2
|
+
|
|
3
|
+
The concern modules `tdd_enforcer.py` wires together to run the TDD gate. Each
|
|
4
|
+
module owns one concern; the entry hook imports them and re-exports their
|
|
5
|
+
surface for the test suite.
|
|
6
|
+
|
|
7
|
+
## Modules
|
|
8
|
+
|
|
9
|
+
| File | Purpose |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `path_classification.py` | Classifies a write target (docs, tests, `.claude` trees) the gate skips, and extracts the written text from a Write, Edit, or MultiEdit payload |
|
|
12
|
+
| `content_analysis.py` | Decides whether a payload is constants-only, and whether an Edit or MultiEdit merely removes or reorders imports |
|
|
13
|
+
| `candidate_paths.py` | Resolves the candidate test files whose freshness can satisfy the gate for a production file |
|
|
14
|
+
| `freshness.py` | Checks whether a candidate test was modified within the window and holds a real test function |
|
|
15
|
+
| `git_tracking.py` | Detects a write that restores a git-tracked file absent on disk, so a remove-then-Write rewrite is exempt from the gate |
|
|
16
|
+
| `decisions.py` | Builds the deny reason and writes the allow or deny decision JSON to stdout |
|
|
17
|
+
| `__init__.py` | Package marker |
|
|
18
|
+
|
|
19
|
+
## Subdirectories
|
|
20
|
+
|
|
21
|
+
| Entry | Description |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `config/` | The freshness window, the ancestor-walk limit, the git-tracking command tokens, the source-file extension sets, and the join separator (`tdd_enforcer_constants.py`) |
|
|
24
|
+
| `tests/` | pytest suite with one test module per module above |
|
|
25
|
+
|
|
26
|
+
## Running tests
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python -m pytest packages/claude-dev-env/hooks/blocking/tdd_enforcer_parts/tests/
|
|
30
|
+
```
|
|
@@ -1,30 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
The concern modules `tdd_enforcer.py` wires together to run the TDD gate. Each
|
|
4
|
-
module owns one concern; the entry hook imports them and re-exports their
|
|
5
|
-
surface for the test suite.
|
|
6
|
-
|
|
7
|
-
## Modules
|
|
8
|
-
|
|
9
|
-
| File | Purpose |
|
|
10
|
-
|---|---|
|
|
11
|
-
| `path_classification.py` | Classifies a write target (docs, tests, `.claude` trees) the gate skips, and extracts the written text from a Write, Edit, or MultiEdit payload |
|
|
12
|
-
| `content_analysis.py` | Decides whether a payload is constants-only, and whether an Edit or MultiEdit merely removes or reorders imports |
|
|
13
|
-
| `candidate_paths.py` | Resolves the candidate test files whose freshness can satisfy the gate for a production file |
|
|
14
|
-
| `freshness.py` | Checks whether a candidate test was modified within the window and holds a real test function |
|
|
15
|
-
| `git_tracking.py` | Detects a write that restores a git-tracked file absent on disk, so a remove-then-Write rewrite is exempt from the gate |
|
|
16
|
-
| `decisions.py` | Builds the deny reason and writes the allow or deny decision JSON to stdout |
|
|
17
|
-
| `__init__.py` | Package marker |
|
|
18
|
-
|
|
19
|
-
## Subdirectories
|
|
20
|
-
|
|
21
|
-
| Entry | Description |
|
|
22
|
-
|---|---|
|
|
23
|
-
| `config/` | The freshness window, the ancestor-walk limit, the git-tracking command tokens, the source-file extension sets, and the join separator (`tdd_enforcer_constants.py`) |
|
|
24
|
-
| `tests/` | pytest suite with one test module per module above |
|
|
25
|
-
|
|
26
|
-
## Running tests
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
python -m pytest packages/claude-dev-env/hooks/blocking/tdd_enforcer_parts/tests/
|
|
30
|
-
```
|
|
1
|
+
@AGENTS.md
|
|
@@ -221,9 +221,6 @@ def test_powershell_selects_the_shared_hooks_in_registration_order() -> None:
|
|
|
221
221
|
assert powershell_paths == [
|
|
222
222
|
"blocking/unscoped_search_blocker.py",
|
|
223
223
|
"blocking/pii_prevention_blocker.py",
|
|
224
|
-
"blocking/code_review_push_gate.py",
|
|
225
|
-
"blocking/code_review_pr_create_gate.py",
|
|
226
|
-
"blocking/code_review_stamp_directory_write_blocker.py",
|
|
227
224
|
]
|
|
228
225
|
|
|
229
226
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Tests for check_banned_prefixes — flags function names with generic prefixes.
|
|
2
2
|
|
|
3
|
-
CODE_RULES.md §5
|
|
3
|
+
CODE_RULES.md §5 "Naming → Function names use specific verbs:
|
|
4
4
|
parse_invoice, dispatch_event, migrate_schema. Generic prefixes to replace:
|
|
5
5
|
handle_, process_, manage_, do_."
|
|
6
6
|
|
|
@@ -506,3 +506,174 @@ def test_dead_constant_stays_denied_on_every_attempt(neutral_root: Path) -> None
|
|
|
506
506
|
"re-issue this write" in each_issue for each_issue in each_attempt
|
|
507
507
|
), f"A dead constant stays denied with the retry escape, got: {each_attempt}"
|
|
508
508
|
|
|
509
|
+
|
|
510
|
+
ALIAS_CONSTANTS_BODY = 'BASE_PROMPTS_DIRECTORY_NAME = "prompts"\nSTYLE_SHEET_FILENAME = "style.css"\n'
|
|
511
|
+
|
|
512
|
+
ALIASED_IMPORT_CONSUMER_BODY = (
|
|
513
|
+
"from style_guide_gen_constants.config import constants as sg\n"
|
|
514
|
+
"\n"
|
|
515
|
+
"def prompts_directory() -> str:\n"
|
|
516
|
+
" return sg.BASE_PROMPTS_DIRECTORY_NAME\n"
|
|
517
|
+
"\n"
|
|
518
|
+
"def style_sheet() -> str:\n"
|
|
519
|
+
" return sg.STYLE_SHEET_FILENAME\n"
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _build_alias_repository(
|
|
524
|
+
package_parent: Path,
|
|
525
|
+
consumer_directory: Path,
|
|
526
|
+
consumer_body: str,
|
|
527
|
+
) -> Path:
|
|
528
|
+
package_directory = package_parent / "style_guide_gen_constants"
|
|
529
|
+
config_directory = package_directory / "config"
|
|
530
|
+
config_directory.mkdir(parents=True)
|
|
531
|
+
(package_directory / "__init__.py").write_text("", encoding="utf-8")
|
|
532
|
+
(config_directory / "__init__.py").write_text("", encoding="utf-8")
|
|
533
|
+
constants_path = config_directory / "constants.py"
|
|
534
|
+
constants_path.write_text(ALIAS_CONSTANTS_BODY, encoding="utf-8")
|
|
535
|
+
consumer_directory.mkdir(parents=True, exist_ok=True)
|
|
536
|
+
(consumer_directory / "style_guide_gen.py").write_text(consumer_body, encoding="utf-8")
|
|
537
|
+
return constants_path
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def test_does_not_flag_constant_read_through_an_aliased_module_import(
|
|
541
|
+
neutral_root: Path,
|
|
542
|
+
) -> None:
|
|
543
|
+
constants_path = _build_alias_repository(
|
|
544
|
+
neutral_root,
|
|
545
|
+
neutral_root / "style_guide_gen_constants",
|
|
546
|
+
ALIASED_IMPORT_CONSUMER_BODY,
|
|
547
|
+
)
|
|
548
|
+
issues = _check(ALIAS_CONSTANTS_BODY, str(constants_path))
|
|
549
|
+
assert issues == [], (
|
|
550
|
+
f"A constant read as an attribute on an aliased module import stays live, got: {issues}"
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def test_does_not_flag_constant_read_through_an_alias_outside_the_package_tree(
|
|
555
|
+
neutral_root: Path,
|
|
556
|
+
) -> None:
|
|
557
|
+
constants_path = _build_alias_repository(
|
|
558
|
+
neutral_root / "scripts",
|
|
559
|
+
neutral_root / "scripts",
|
|
560
|
+
ALIASED_IMPORT_CONSUMER_BODY,
|
|
561
|
+
)
|
|
562
|
+
issues = _check(ALIAS_CONSTANTS_BODY, str(constants_path))
|
|
563
|
+
assert issues == [], (
|
|
564
|
+
"A constant an aliased module import reads from outside the package tree "
|
|
565
|
+
f"stays live through the widened scan, got: {issues}"
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def test_does_not_flag_constant_read_through_an_unaliased_module_import_outside_the_tree(
|
|
570
|
+
neutral_root: Path,
|
|
571
|
+
) -> None:
|
|
572
|
+
consumer_body = (
|
|
573
|
+
"from style_guide_gen_constants.config import constants\n"
|
|
574
|
+
"\n"
|
|
575
|
+
"def prompts_directory() -> str:\n"
|
|
576
|
+
" return constants.BASE_PROMPTS_DIRECTORY_NAME\n"
|
|
577
|
+
"\n"
|
|
578
|
+
"def style_sheet() -> str:\n"
|
|
579
|
+
" return constants.STYLE_SHEET_FILENAME\n"
|
|
580
|
+
)
|
|
581
|
+
constants_path = _build_alias_repository(
|
|
582
|
+
neutral_root / "scripts",
|
|
583
|
+
neutral_root / "scripts",
|
|
584
|
+
consumer_body,
|
|
585
|
+
)
|
|
586
|
+
issues = _check(ALIAS_CONSTANTS_BODY, str(constants_path))
|
|
587
|
+
assert issues == [], (
|
|
588
|
+
f"A constant read on an unaliased imported module object stays live, got: {issues}"
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def test_does_not_flag_constant_read_through_a_dotted_module_path_outside_the_tree(
|
|
593
|
+
neutral_root: Path,
|
|
594
|
+
) -> None:
|
|
595
|
+
consumer_body = (
|
|
596
|
+
"import style_guide_gen_constants.config.constants\n"
|
|
597
|
+
"\n"
|
|
598
|
+
"def prompts_directory() -> str:\n"
|
|
599
|
+
" return style_guide_gen_constants.config.constants.BASE_PROMPTS_DIRECTORY_NAME\n"
|
|
600
|
+
"\n"
|
|
601
|
+
"def style_sheet() -> str:\n"
|
|
602
|
+
" return style_guide_gen_constants.config.constants.STYLE_SHEET_FILENAME\n"
|
|
603
|
+
)
|
|
604
|
+
constants_path = _build_alias_repository(
|
|
605
|
+
neutral_root / "scripts",
|
|
606
|
+
neutral_root / "scripts",
|
|
607
|
+
consumer_body,
|
|
608
|
+
)
|
|
609
|
+
issues = _check(ALIAS_CONSTANTS_BODY, str(constants_path))
|
|
610
|
+
assert issues == [], (
|
|
611
|
+
f"A constant read through the full dotted module path stays live, got: {issues}"
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def test_does_not_flag_constant_read_through_an_alias_in_a_sibling_tree(
|
|
616
|
+
neutral_root: Path,
|
|
617
|
+
) -> None:
|
|
618
|
+
constants_body = 'CROSS_TREE_CONSTANT = "cross"\nLOCALLY_DEAD_CONSTANT = "dead"\n'
|
|
619
|
+
sibling_consumer_body = (
|
|
620
|
+
"from shared.theme_db.config import constants as theme_constants\n"
|
|
621
|
+
"\n"
|
|
622
|
+
"def tally() -> str:\n"
|
|
623
|
+
" return theme_constants.CROSS_TREE_CONSTANT\n"
|
|
624
|
+
)
|
|
625
|
+
constants_path = _build_cross_tree_repository(
|
|
626
|
+
neutral_root, constants_body, sibling_consumer_body
|
|
627
|
+
)
|
|
628
|
+
issues = _check(constants_body, str(constants_path))
|
|
629
|
+
assert not any("CROSS_TREE_CONSTANT" in each_issue for each_issue in issues), (
|
|
630
|
+
"A constant a sibling tree reads as an attribute on an aliased module import "
|
|
631
|
+
f"must not be flagged, got: {issues}"
|
|
632
|
+
)
|
|
633
|
+
assert any("LOCALLY_DEAD_CONSTANT" in each_issue for each_issue in issues), (
|
|
634
|
+
f"A constant referenced nowhere in the repository stays flagged, got: {issues}"
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def _build_alias_collision_repository(
|
|
639
|
+
repository_root: Path,
|
|
640
|
+
promoter_constants_body: str,
|
|
641
|
+
harness_constants_body: str,
|
|
642
|
+
harness_consumer_body: str,
|
|
643
|
+
) -> Path:
|
|
644
|
+
promoter_config = repository_root / "promoter" / "config"
|
|
645
|
+
promoter_config.mkdir(parents=True)
|
|
646
|
+
promoter_constants_path = promoter_config / "constants.py"
|
|
647
|
+
promoter_constants_path.write_text(promoter_constants_body, encoding="utf-8")
|
|
648
|
+
harness_config = repository_root / "harness" / "config"
|
|
649
|
+
harness_config.mkdir(parents=True)
|
|
650
|
+
(harness_config / "other_constants.py").write_text(harness_constants_body, encoding="utf-8")
|
|
651
|
+
(repository_root / "harness" / "lockfile.py").write_text(
|
|
652
|
+
harness_consumer_body, encoding="utf-8"
|
|
653
|
+
)
|
|
654
|
+
return promoter_constants_path
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def test_alias_attribute_read_on_an_unrelated_module_does_not_mask_a_dead_constant(
|
|
658
|
+
neutral_root: Path,
|
|
659
|
+
) -> None:
|
|
660
|
+
promoter_constants_body = 'NEVER_READ_CONSTANT = "dead"\n'
|
|
661
|
+
harness_constants_body = 'NEVER_READ_CONSTANT = "twin"\n'
|
|
662
|
+
harness_consumer_body = (
|
|
663
|
+
"from harness.config import other_constants as oc\n"
|
|
664
|
+
"\n"
|
|
665
|
+
"def acquire() -> str:\n"
|
|
666
|
+
" return oc.NEVER_READ_CONSTANT\n"
|
|
667
|
+
)
|
|
668
|
+
promoter_constants_path = _build_alias_collision_repository(
|
|
669
|
+
neutral_root,
|
|
670
|
+
promoter_constants_body,
|
|
671
|
+
harness_constants_body,
|
|
672
|
+
harness_consumer_body,
|
|
673
|
+
)
|
|
674
|
+
issues = _check(promoter_constants_body, str(promoter_constants_path))
|
|
675
|
+
assert any("NEVER_READ_CONSTANT" in each_issue for each_issue in issues), (
|
|
676
|
+
"A constant whose only same-named twin is read as an attribute on an "
|
|
677
|
+
f"unrelated aliased module must still be flagged dead, got: {issues}"
|
|
678
|
+
)
|
|
679
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Tests for magic-value allowlist alignment with CODE_RULES §HOOK-ENFORCED.
|
|
2
2
|
|
|
3
|
-
CODE_RULES.md
|
|
3
|
+
CODE_RULES.md states that only
|
|
4
4
|
0, 1, and -1 (plus their float forms 0.0, 1.0) are exempt from the
|
|
5
5
|
magic-value check. Prior to this change, the hook silently allowed 2
|
|
6
6
|
and 100 as well, making the hook more permissive than the written rule.
|
|
@@ -422,7 +422,8 @@ def test_main_logs_high_level_failure_when_restore_switch_fails(
|
|
|
422
422
|
|
|
423
423
|
assert exit_code == 0
|
|
424
424
|
assert state_file.exists()
|
|
425
|
-
assert "[gh-pr-author-restore]
|
|
425
|
+
assert "[gh-pr-author-restore] Restore the gh account" in captured_streams.err
|
|
426
|
+
assert "gh account switch requires attention" in captured_streams.err
|
|
426
427
|
assert "'jl-cmd'" in captured_streams.err
|
|
427
428
|
assert str(state_file) in captured_streams.err
|
|
428
429
|
|
|
@@ -111,6 +111,10 @@ def test_intent_ending_message_emits_block_with_short_user_notice():
|
|
|
111
111
|
assert parsed_response["systemMessage"] == USER_FACING_INTENT_ENDING_NOTICE
|
|
112
112
|
assert parsed_response["suppressOutput"] is True
|
|
113
113
|
assert "long-horizon-autonomy" in parsed_response["reason"]
|
|
114
|
+
assert "This turn ends with a promise of future work." in parsed_response["reason"]
|
|
115
|
+
assert "Complete that work with tool calls during this turn." in parsed_response[
|
|
116
|
+
"reason"
|
|
117
|
+
]
|
|
114
118
|
|
|
115
119
|
|
|
116
120
|
def test_next_steps_lead_in_emits_block():
|