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
|
@@ -48,6 +48,11 @@ const CONFIG = {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const REVIEWER_GATE_SENTINEL = 'CLAUDE_REVIEWER_GATE=autoconverge '
|
|
51
|
+
const REVIEW_GUIDE_REFERENCE = 'skills/reviews/SKILL.md#review-workflow'
|
|
52
|
+
const REVIEW_RESULT_PASS_MARKER = 'REVIEW_RESULT: PASS'
|
|
53
|
+
const REVIEW_RESULT_FINDINGS_MARKER = 'REVIEW_RESULT: FINDINGS'
|
|
54
|
+
const REVIEW_RESULT_INSTRUCTION =
|
|
55
|
+
`3. State every required finding before the result line. End with exactly one standalone terminal ${REVIEW_RESULT_PASS_MARKER} when no required finding remains, or ${REVIEW_RESULT_FINDINGS_MARKER} when findings remain. Include no other REVIEW_RESULT: text.`
|
|
51
56
|
|
|
52
57
|
const TIERS = {
|
|
53
58
|
opusMedium: { model: 'opus', effort: 'medium' },
|
|
@@ -179,10 +184,9 @@ function runGitTask(task) {
|
|
|
179
184
|
}
|
|
180
185
|
|
|
181
186
|
/**
|
|
182
|
-
* Spawn a fresh fixer clean-coder agent for a commit or recovery edit. The
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* and recovers.
|
|
187
|
+
* Spawn a fresh fixer clean-coder agent for a commit or recovery edit. The
|
|
188
|
+
* review step completes before the commit step, and the fixer preserves that
|
|
189
|
+
* reviewed working tree while it commits or recovers.
|
|
186
190
|
* @param {string} task the short task name
|
|
187
191
|
* @param {object} context task-specific context
|
|
188
192
|
* @returns {Promise<object>} the structured output
|
|
@@ -191,9 +195,9 @@ function runFixerTask(task, context) {
|
|
|
191
195
|
const label = `fixer:${context.sourceLabel}`
|
|
192
196
|
if (task === 'commit') {
|
|
193
197
|
return convergeAgent(
|
|
194
|
-
|
|
198
|
+
`You are the COMMIT step for fixes (${context.sourceLabel}) on ${prCoordinates}, HEAD ${context.head}. The edit step left fixes in the working tree and the review step completed under ${REVIEW_GUIDE_REFERENCE}.\n\n` +
|
|
195
199
|
`Rules:\n` +
|
|
196
|
-
`- Make
|
|
200
|
+
`- Make no further file edits. Preserve the reviewed working tree; only commit and push what is already there.\n` +
|
|
197
201
|
`- Make ONE commit for all the working-tree fixes, then push to the PR branch.\n\n` +
|
|
198
202
|
`Return values:\n` +
|
|
199
203
|
`- On a successful push: newSha=the new HEAD SHA after your push, pushed=true, resolvedWithoutCommit=false, blockedNeedingEdit=false, blockerDetail="", and a one-line summary.\n` +
|
|
@@ -210,7 +214,7 @@ function runFixerTask(task, context) {
|
|
|
210
214
|
`Rules:\n` +
|
|
211
215
|
`- Confirm the working tree is on the PR branch at HEAD ${context.head} with the prior fixes still present.\n` +
|
|
212
216
|
`- Fix ONLY the violation named above, test-first (failing test, then minimum code to pass) per CODE_RULES. Do not re-open the original findings, and do not touch GitHub review threads — the edit step already handled those.\n` +
|
|
213
|
-
`- Leave the corrected fixes in the working tree. Do
|
|
217
|
+
`- Leave the corrected fixes in the working tree. Do not commit or push; the review and commit steps run after you.\n\n` +
|
|
214
218
|
`Return values: edited=true with a one-line summary when you changed code to clear the block; edited=false, resolvedWithoutCommit=false when the block cannot be cleared with a code change.` +
|
|
215
219
|
PRE_COMMIT_GATE_STEP,
|
|
216
220
|
{ label, phase: 'Converge', schema: EDIT_SCHEMA, agentType: 'clean-coder', ...TIERS.sonnetMedium },
|
|
@@ -232,14 +236,10 @@ function runFixerTask(task, context) {
|
|
|
232
236
|
}
|
|
233
237
|
|
|
234
238
|
/**
|
|
235
|
-
* Joined fixer recovery loop: the verify step
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
* separation the repair and conflict paths use. The verify step routes through
|
|
240
|
-
* verifyWithRecovery (verify on the verifier, recover on the fixer); the commit
|
|
241
|
-
* step routes through commitWithRecovery (commit and commit-recover on the fixer,
|
|
242
|
-
* re-verify on the verifier). A failed verdict returns the unchanged HEAD so the
|
|
239
|
+
* Joined fixer recovery loop: the verify step applies the canonical review guide
|
|
240
|
+
* to the working-tree fixes while the commit and recovery steps spawn clean-coder
|
|
241
|
+
* fixer agents. The verify step routes through verifyWithRecovery; the commit
|
|
242
|
+
* step routes through commitWithRecovery. A failed review returns the unchanged HEAD so the
|
|
243
243
|
* round reads as not-progressed.
|
|
244
244
|
* @param {string} head PR HEAD SHA
|
|
245
245
|
* @param {Array<object>} findings the findings to fix
|
|
@@ -248,10 +248,10 @@ function runFixerTask(task, context) {
|
|
|
248
248
|
*/
|
|
249
249
|
async function fixerWithRecovery(head, findings, sourceLabel) {
|
|
250
250
|
const verifyTranscript = await verifyWithRecovery({
|
|
251
|
-
runVerify: () =>
|
|
251
|
+
runVerify: () => runReviewTask('fix-verify', { head, findings, sourceLabel }),
|
|
252
252
|
runRecoverEdit: (objection, attempt) => runFixerTask('verify-recover', { head, findings, sourceLabel, objection, attempt }),
|
|
253
253
|
})
|
|
254
|
-
if (!
|
|
254
|
+
if (!reviewPassed(verifyTranscript)) {
|
|
255
255
|
return {
|
|
256
256
|
newSha: head,
|
|
257
257
|
pushed: false,
|
|
@@ -261,9 +261,15 @@ async function fixerWithRecovery(head, findings, sourceLabel) {
|
|
|
261
261
|
blockerDetail: '',
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
|
+
const captureReviewedSurface = () => captureReviewedSurfaceHash({ sourceLabel, phase: 'Converge' })
|
|
265
|
+
const reviewedSurfaceHash = await captureReviewedSurface()
|
|
266
|
+
if (reviewedSurfaceHash === null) return reviewedSurfaceUnavailableResult(head)
|
|
264
267
|
return commitWithRecovery({
|
|
268
|
+
head,
|
|
269
|
+
reviewedSurfaceHash,
|
|
270
|
+
captureReviewedSurface,
|
|
265
271
|
runCommit: () => runFixerTask('commit', { head, findings, sourceLabel }),
|
|
266
|
-
runVerify: () =>
|
|
272
|
+
runVerify: () => runReviewTask('fix-verify', { head, findings, sourceLabel }),
|
|
267
273
|
runRecoverEdit: (detail, attempt) => runFixerTask('commit-recover', { head, findings, sourceLabel, blockerDetail: detail, attempt }),
|
|
268
274
|
})
|
|
269
275
|
}
|
|
@@ -337,9 +343,9 @@ function runCodeEditorTask(task, context) {
|
|
|
337
343
|
? 'The edit step rebased the branch, so push with git push --force-with-lease.'
|
|
338
344
|
: 'Push to the PR branch with a plain git push.'
|
|
339
345
|
return convergeAgent(
|
|
340
|
-
|
|
346
|
+
`You are the COMMIT step for the convergence repair on ${prCoordinates}, HEAD ${context.head}. The edit step left its repair in the working tree and the review step completed under ${REVIEW_GUIDE_REFERENCE}.\n\n` +
|
|
341
347
|
`Rules:\n` +
|
|
342
|
-
`- Make
|
|
348
|
+
`- Make no further file edits. Preserve the reviewed working tree; only commit and push what is already there.\n` +
|
|
343
349
|
`- Commit any uncommitted bot-thread fix in ONE commit (skip the commit when the working tree carries only already-committed rebase results). ${pushInstruction}\n\n` +
|
|
344
350
|
`Return values:\n` +
|
|
345
351
|
`- On a successful push: newSha=the new HEAD SHA after your push, pushed=true, resolvedWithoutCommit=false, blockedNeedingEdit=false, blockerDetail="", and a one-line summary.\n` +
|
|
@@ -357,7 +363,7 @@ function runCodeEditorTask(task, context) {
|
|
|
357
363
|
`You are the EDIT step deferring a code-standard-only round on ${prCoordinates}, HEAD ${context.head} (${context.sourceLabel}). The round surfaced ONLY code-standard violations (CODE_RULES/style, no behavioral impact); the run treats it as passed and defers the fixes to follow-up work, which you now stage. A separate verify step then a separate commit step open the hardening PR after you. Do NOT commit or push to the PR's own branch.\n\n` +
|
|
358
364
|
`Findings:\n${findingsBlock}\n\n` +
|
|
359
365
|
`1. Follow-up fix issue: file a GitHub issue on ${input.owner}/${input.repo} (gh issue create --body-file with a temp file) titled "Deferred code-standard fixes from PR #${input.prNumber}". The body references the PR and lists each finding with its file:line, severity, and detail. The issue carries the fix work; do not open a fix PR. Capture the issue URL.\n` +
|
|
360
|
-
`2. Stage the environment-hardening change: in the Claude environment config repo (the repo owning ~/.claude hooks and rules — JonEcho/llm-settings for hooks, jl-cmd/claude-dev-env for rules/skills; pick whichever owns the surface that would block these violation classes), find or clone a local checkout, fetch origin, and create a branch off origin/main. Edit the hooks/rules in that checkout's WORKING TREE so each violation class found here is blocked at Write/Edit time, before code is written. Do
|
|
366
|
+
`2. Stage the environment-hardening change: in the Claude environment config repo (the repo owning ~/.claude hooks and rules — JonEcho/llm-settings for hooks, jl-cmd/claude-dev-env for rules/skills; pick whichever owns the surface that would block these violation classes), find or clone a local checkout, fetch origin, and create a branch off origin/main. Edit the hooks/rules in that checkout's WORKING TREE so each violation class found here is blocked at Write/Edit time, before code is written. Do not commit or push; review and commit run after this step. Return the checkout's absolute path in hardeningRepoPath, the branch name in hardeningBranch, and set hardeningEdited=true. When no hardening is feasible for these classes, leave hardeningRepoPath and hardeningBranch empty and hardeningEdited=false; the follow-up issue still stands.\n` +
|
|
361
367
|
`3. For each finding that carries a GitHub review comment id (${threadIds.length ? threadIds.join(', ') : 'none this batch'}): post an inline reply via python "${CONFIG.sharedScripts}/post_fix_reply.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber} --in-reply-to <id> --body "Code-standard-only finding — deferred to follow-up issue <url>." Then resolve the thread by its PRRT_ node id (GraphQL lookup on comment databaseId, then resolveReviewThread or the github MCP pull_request_review_write method=resolve_thread — not the numeric comment id).\n\n` +
|
|
362
368
|
`Return the issue URL in issueUrl (empty string when it could not be filed), the hardening checkout path and branch, hardeningEdited, and a one-line summary.` +
|
|
363
369
|
PRE_COMMIT_GATE_STEP,
|
|
@@ -380,9 +386,9 @@ function runCodeEditorTask(task, context) {
|
|
|
380
386
|
}
|
|
381
387
|
if (task === 'hardening-commit') {
|
|
382
388
|
return convergeAgent(
|
|
383
|
-
|
|
389
|
+
`You are the COMMIT step opening the environment-hardening PR (${context.sourceLabel}) for the change staged in ${context.hardeningRepoPath} on branch ${context.hardeningBranch}. The edit step left the hooks/rules edits in the working tree and the review step completed under ${REVIEW_GUIDE_REFERENCE}. Do not touch the PR's own branch.\n\n` +
|
|
384
390
|
`Rules:\n` +
|
|
385
|
-
`- Make
|
|
391
|
+
`- Make no further file edits. Preserve the reviewed working tree; only commit and push what is already there.\n` +
|
|
386
392
|
`- In ${context.hardeningRepoPath}: make ONE commit of the staged hooks/rules change on branch ${context.hardeningBranch}, push it, then open a DRAFT PR. The PR body references the follow-up issue ${context.issueUrl || '(none)'} and states the PR hardens the environment so the deferred violation classes are blocked at Write/Edit time. Honor the gh-cli-conventions rule: write a BOM-free temp file and pass --body-file.\n` +
|
|
387
393
|
`- Title the PR as a Conventional Commit — a type prefix (feat, fix, chore, docs, refactor, perf, ci, style, test, build, revert), an optional scope in parentheses, then a colon and a short summary, e.g. "feat(hooks): block the deferred violation class". The target repo's CI validates the PR title as a semantic commit and rejects a non-conforming title.\n\n` +
|
|
388
394
|
`Return the full https URL of the DRAFT hardening PR in hardeningPrUrl (empty string when no PR was opened) and a one-line summary.`,
|
|
@@ -420,25 +426,24 @@ function runCodeEditorTask(task, context) {
|
|
|
420
426
|
}
|
|
421
427
|
|
|
422
428
|
/**
|
|
423
|
-
* Spawn a fresh
|
|
424
|
-
*
|
|
425
|
-
* verification only — and ends its message with a fenced verdict block.
|
|
429
|
+
* Spawn a fresh review agent for a specific verify task (fix-verify,
|
|
430
|
+
* repair-verify, hardening-verify). The agent makes no edits.
|
|
426
431
|
* @param {string} task the short task name
|
|
427
432
|
* @param {object} context task-specific context
|
|
428
|
-
* @returns {Promise<string>} the
|
|
433
|
+
* @returns {Promise<string>} the review transcript
|
|
429
434
|
*/
|
|
430
|
-
function
|
|
431
|
-
const label = `
|
|
435
|
+
function runReviewTask(task, context) {
|
|
436
|
+
const label = `review:${task}`
|
|
432
437
|
if (task === 'fix-verify') {
|
|
433
438
|
const findingsBlock = renderFindingsBlock(context.findings)
|
|
434
439
|
return convergeReadOnlyAgent(
|
|
435
|
-
|
|
440
|
+
`You are the VERIFY step for ${context.findings.length} finding(s) (${context.sourceLabel}) on ${prCoordinates}, HEAD ${context.head}. The edit step left fixes in the working tree, uncommitted. Make no edit to the tree under verification. Apply the review guide at ${REVIEW_GUIDE_REFERENCE}.\n\n` +
|
|
436
441
|
`Findings the working-tree fixes must address:\n${findingsBlock}\n\n` +
|
|
437
442
|
`Steps:\n` +
|
|
438
443
|
`1. Resolve the worktree repo root for running tests: REPO=$(git rev-parse --show-toplevel).\n` +
|
|
439
444
|
`2. Verify the uncommitted working-tree changes resolve every finding above: run the relevant tests and the named gates against the working tree. Read the diff (git diff) and confirm each finding is fixed test-first per CODE_RULES.\n` +
|
|
440
|
-
|
|
441
|
-
{ label, phase: 'Converge', agentType: 'code-
|
|
445
|
+
REVIEW_RESULT_INSTRUCTION,
|
|
446
|
+
{ label, phase: 'Converge', agentType: 'code-quality-agent', ...TIERS.sonnetMedium },
|
|
442
447
|
)
|
|
443
448
|
}
|
|
444
449
|
if (task === 'repair-verify') {
|
|
@@ -446,28 +451,34 @@ function runVerifierTask(task, context) {
|
|
|
446
451
|
? context.failures.map((each, position) => `${position + 1}. ${each}`).join('\n')
|
|
447
452
|
: 'none reported'
|
|
448
453
|
return convergeReadOnlyAgent(
|
|
449
|
-
|
|
454
|
+
`You are the VERIFY step for the convergence repair on ${prCoordinates}, HEAD ${context.head}. The edit step left its repair in the working tree (a bot-thread fix uncommitted, and/or a rebase onto origin/main), unpushed. Make no edit to the tree under verification. Apply the review guide at ${REVIEW_GUIDE_REFERENCE}.\n\n` +
|
|
450
455
|
`Concerns the working-tree repair must resolve (the gates the convergence check flagged):\n${failureBlock}\n\n` +
|
|
451
456
|
`Steps:\n` +
|
|
452
457
|
`1. Resolve the worktree repo root for running tests: REPO=$(git rev-parse --show-toplevel).\n` +
|
|
453
458
|
`2. Verify the working tree against origin/main: any bot-thread code fix is correct test-first per CODE_RULES, and a rebase (if any) left a clean, conflict-free tree. Read the diff (git diff origin/main) and run the relevant tests and named gates.\n` +
|
|
454
|
-
|
|
455
|
-
{ label, phase: 'Finalize', agentType: 'code-
|
|
459
|
+
REVIEW_RESULT_INSTRUCTION,
|
|
460
|
+
{ label, phase: 'Finalize', agentType: 'code-quality-agent', ...TIERS.sonnetMedium },
|
|
456
461
|
)
|
|
457
462
|
}
|
|
458
463
|
return convergeReadOnlyAgent(
|
|
459
|
-
|
|
464
|
+
`You are the VERIFY step for an environment-hardening change (${context.sourceLabel}) staged in the working tree of ${context.hardeningRepoPath}. The edit step left the hooks/rules edits uncommitted there. Make no edit to the tree under verification. Apply the review guide at ${REVIEW_GUIDE_REFERENCE}.\n\n` +
|
|
460
465
|
`Concern the working-tree change must resolve: the edited hooks/rules block the code-standard violation classes from the deferred round at Write/Edit time, and a hook change carries a passing test per CODE_RULES.\n\n` +
|
|
461
466
|
`Steps:\n` +
|
|
462
467
|
`1. cd into ${context.hardeningRepoPath}, then resolve its repo root: REPO=$(git rev-parse --show-toplevel).\n` +
|
|
463
468
|
`2. Verify the uncommitted working-tree change in REPO: read the diff (git diff) and run the hook/rule tests in that repo, confirming each violation class is now blocked.\n` +
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
469
|
+
REVIEW_RESULT_INSTRUCTION,
|
|
470
|
+
{ label, phase: 'Converge', agentType: 'code-quality-agent', ...TIERS.sonnetMedium },
|
|
471
|
+
)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function runWorkingTreeSurfaceTask(context) {
|
|
475
|
+
const repositoryInstruction = context.repositoryPath
|
|
476
|
+
? `cd into ${context.repositoryPath}, then resolve its repo root with git rev-parse --show-toplevel.`
|
|
477
|
+
: 'Resolve the current worktree repo root with git rev-parse --show-toplevel.'
|
|
478
|
+
return convergeReadOnlyAgent(
|
|
479
|
+
`Capture the reviewed working-tree surface for ${context.sourceLabel}. Do not edit, stage, commit, push, rebase, or modify any file. ${repositoryInstruction}\n\n` +
|
|
480
|
+
`Use a temporary Python script outside the repository to print one lowercase SHA-256 hash. Hash this exact length-prefixed byte stream: first the literal bytes "tracked", then the exact stdout bytes from git diff --no-ext-diff --binary HEAD; then, for every path from git ls-files --others --exclude-standard -z sorted as raw bytes, the literal bytes "untracked", the raw path bytes, the lstat file-type bits as ASCII, and the payload bytes. The payload is file bytes for a regular file and the raw link target bytes for a symlink. Prefix every element with its eight-byte big-endian length. Return the hash in surfaceHash.`,
|
|
481
|
+
{ label: `working-tree-surface:${context.sourceLabel}`, phase: context.phase, schema: WORKING_TREE_SURFACE_SCHEMA, agentType: 'general-purpose', ...TIERS.haikuLow },
|
|
471
482
|
)
|
|
472
483
|
}
|
|
473
484
|
|
|
@@ -607,7 +618,7 @@ function runConvergenceCheck(context) {
|
|
|
607
618
|
const PRE_COMMIT_GATE_STEP =
|
|
608
619
|
`\n\nFINAL STEP — pre-commit gate check (do NOT commit): before your turn ends, prove your working-tree changes CAN be committed by dry-running the CODE_RULES commit gate that gates git commit (precommit_code_rules_gate). From inside the checkout that holds your changes, resolve its root with git rev-parse --show-toplevel, stage your changes with git add -A, then run exactly:\n` +
|
|
609
620
|
` python "${CONFIG.prLoopScripts}/code_rules_gate.py" --repo-root "<that root>" --staged\n` +
|
|
610
|
-
`Exit 0 means the commit gate would accept the commit. On any non-zero exit, read every violation it prints, fix each one test-first per CODE_RULES, and re-run the gate until it exits 0. Then unstage with git restore --staged . so the
|
|
621
|
+
`Exit 0 means the commit gate would accept the commit. On any non-zero exit, read every violation it prints, fix each one test-first per CODE_RULES, and re-run the gate until it exits 0. Then unstage with git restore --staged . so the review step reads the working-tree diff. Make no git commit or git push here — this is a dry committability check; the separate review and commit steps run after you. Your turn does not end while the commit gate would reject the commit.`
|
|
611
622
|
|
|
612
623
|
const LENS_SCHEMA = {
|
|
613
624
|
type: 'object',
|
|
@@ -789,6 +800,15 @@ const FIX_SCHEMA = {
|
|
|
789
800
|
required: ['newSha', 'pushed', 'resolvedWithoutCommit', 'summary', 'blockedNeedingEdit', 'blockerDetail'],
|
|
790
801
|
}
|
|
791
802
|
|
|
803
|
+
const WORKING_TREE_SURFACE_SCHEMA = {
|
|
804
|
+
type: 'object',
|
|
805
|
+
additionalProperties: false,
|
|
806
|
+
properties: {
|
|
807
|
+
surfaceHash: { type: 'string', description: 'the lowercase SHA-256 fingerprint of the complete tracked and untracked working-tree surface' },
|
|
808
|
+
},
|
|
809
|
+
required: ['surfaceHash'],
|
|
810
|
+
}
|
|
811
|
+
|
|
792
812
|
const EDIT_SCHEMA = {
|
|
793
813
|
type: 'object',
|
|
794
814
|
additionalProperties: false,
|
|
@@ -845,53 +865,6 @@ const HARDENING_COMMIT_SCHEMA = {
|
|
|
845
865
|
required: ['hardeningPrUrl', 'summary'],
|
|
846
866
|
}
|
|
847
867
|
|
|
848
|
-
const VERDICT_FENCE_CONTRACT_SENTENCE =
|
|
849
|
-
'Set all_pass to false when verification fails, and list every code defect you found in findings. When the verdict is incomplete because a check it rests on never showed red, set all_pass to false and name that check in prose directly above the fence rather than in findings. Always include the manifest_sha256. The verdict fence must be the last thing in your message.'
|
|
850
|
-
|
|
851
|
-
/**
|
|
852
|
-
* Build the closing fence recipe every verify prompt ends with: the lead-in
|
|
853
|
-
* sentence, the ```verdict block shape carrying the binding hash, and the
|
|
854
|
-
* contract sentence. Each verify path resolves the binding hash its own way
|
|
855
|
-
* and sits at its own nesting depth, so the caller passes the indent its
|
|
856
|
-
* surrounding prose uses; the fence body itself sits one level deeper.
|
|
857
|
-
* @param {string} indent leading whitespace the caller's prose sits at
|
|
858
|
-
* @returns {string} the fence recipe tail for a verify prompt
|
|
859
|
-
*/
|
|
860
|
-
function buildVerdictFenceTail(indent) {
|
|
861
|
-
const fenceIndent = `${indent} `
|
|
862
|
-
return (
|
|
863
|
-
`${indent}Then END your message with a fenced verdict block exactly in this shape, on its own, carrying that hash:\n` +
|
|
864
|
-
`${fenceIndent}` + "```verdict\n" +
|
|
865
|
-
`${fenceIndent}{"all_pass": true, "findings": [], "manifest_sha256": "<that hash>"}\n` +
|
|
866
|
-
`${fenceIndent}` + "```\n" +
|
|
867
|
-
`${fenceIndent}${VERDICT_FENCE_CONTRACT_SENTENCE}`
|
|
868
|
-
)
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Build the verdict-fence step instructions for a verify agent, binding the
|
|
873
|
-
* surface hash by branch name rather than by a self-resolved cwd. Resolving
|
|
874
|
-
* the branch via `gh pr view` is cwd-immune: it does not matter which worktree
|
|
875
|
-
* the verify agent runs in, so a launcher session whose cwd is a different
|
|
876
|
-
* worktree cannot poison the binding hash.
|
|
877
|
-
* @param {string} prOwner GitHub owner of the repo that holds the branch
|
|
878
|
-
* @param {string} prRepo GitHub repo name
|
|
879
|
-
* @param {number|string} prNumber PR number used to resolve the head branch
|
|
880
|
-
* @returns {string} binding-hash and verdict-fence instructions for a verify prompt
|
|
881
|
-
*/
|
|
882
|
-
function buildVerdictFenceSteps(prOwner, prRepo, prNumber) {
|
|
883
|
-
return (
|
|
884
|
-
`Compute the binding hash for the live surface:\n` +
|
|
885
|
-
` a. Resolve the PR head branch (cwd-immune): run exactly\n` +
|
|
886
|
-
` gh pr view ${prNumber} --repo ${prOwner}/${prRepo} --json headRefName -q .headRefName\n` +
|
|
887
|
-
` Capture the branch name printed on stdout.\n` +
|
|
888
|
-
` b. Run exactly:\n` +
|
|
889
|
-
` "C:\\Python313\\python.exe" "<REPO>/packages/claude-dev-env/hooks/blocking/verification_verdict_store.py" --manifest-hash-for-branch "<that branch>"\n` +
|
|
890
|
-
` (substitute the REPO path you resolved for the script path, and the branch name for <that branch>). That prints a single 64-char hex hash on stdout — capture it.\n` +
|
|
891
|
-
buildVerdictFenceTail('')
|
|
892
|
-
)
|
|
893
|
-
}
|
|
894
|
-
|
|
895
868
|
const FINALIZE_SCHEMA = {
|
|
896
869
|
type: 'object',
|
|
897
870
|
additionalProperties: false,
|
|
@@ -1297,63 +1270,32 @@ function normalizeShaForComparison(sha) {
|
|
|
1297
1270
|
}
|
|
1298
1271
|
|
|
1299
1272
|
/**
|
|
1300
|
-
* Locate the LAST ```verdict ...``` fence in a transcript: the marker, optional
|
|
1301
|
-
* whitespace, a required newline, then a body closed by ```. This is the ONE
|
|
1302
|
-
* definition of "the verdict fence" — the verdict parser and the pre-fence prose
|
|
1303
|
-
* reader both anchor here, so they cannot land on different fences.
|
|
1304
|
-
*
|
|
1305
|
-
* ::
|
|
1306
|
-
*
|
|
1307
|
-
* ok: 'note\n\n```verdict\n{}\n```' -> body '{}\n' starting at index 6
|
|
1308
|
-
* flag: 'note\n\n```verdict {}```' -> null (no newline after the marker)
|
|
1309
|
-
*
|
|
1310
|
-
* A marker with no newline after it is not a fence, so a later one cannot pull
|
|
1311
|
-
* either reader off the fence the other reads.
|
|
1312
|
-
*
|
|
1313
1273
|
* @param {string|null|undefined} transcript the agent transcript text
|
|
1314
|
-
* @returns {
|
|
1274
|
+
* @returns {'PASS'|'FINDINGS'|null} the final review result, or null when absent
|
|
1315
1275
|
*/
|
|
1316
|
-
function
|
|
1276
|
+
function parseReviewResult(transcript) {
|
|
1317
1277
|
if (typeof transcript !== 'string') return null
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
return
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
/**
|
|
1328
|
-
* Parse the LAST ```verdict ...``` fenced JSON block from a transcript.
|
|
1329
|
-
* Guards against non-string input, takes the fence findLastVerdictFence locates,
|
|
1330
|
-
* parses the JSON, and returns the object or null on any failure.
|
|
1331
|
-
* @param {string|null|undefined} transcript the agent transcript text
|
|
1332
|
-
* @returns {object|null} the parsed verdict object, or null when absent or malformed
|
|
1333
|
-
*/
|
|
1334
|
-
function parseLastVerdictFence(transcript) {
|
|
1335
|
-
const lastFence = findLastVerdictFence(transcript)
|
|
1336
|
-
if (lastFence === null) return null
|
|
1337
|
-
try {
|
|
1338
|
-
return JSON.parse(lastFence.bodyText)
|
|
1339
|
-
} catch {
|
|
1340
|
-
return null
|
|
1341
|
-
}
|
|
1278
|
+
const allTranscriptLines = transcript.trimEnd().split(/\r?\n/)
|
|
1279
|
+
const allResultLikeLines = allTranscriptLines.filter((eachTranscriptLine) =>
|
|
1280
|
+
eachTranscriptLine.includes('REVIEW_RESULT:'),
|
|
1281
|
+
)
|
|
1282
|
+
if (allResultLikeLines.length !== 1) return null
|
|
1283
|
+
const terminalReviewLine = allTranscriptLines.at(-1)
|
|
1284
|
+
if (terminalReviewLine === REVIEW_RESULT_PASS_MARKER) return 'PASS'
|
|
1285
|
+
if (terminalReviewLine === REVIEW_RESULT_FINDINGS_MARKER) return 'FINDINGS'
|
|
1286
|
+
return null
|
|
1342
1287
|
}
|
|
1343
1288
|
|
|
1344
1289
|
/**
|
|
1345
|
-
* Decide whether a
|
|
1346
|
-
*
|
|
1347
|
-
*
|
|
1348
|
-
* @param {string|null|undefined} verifyTranscript the verifier's transcript text
|
|
1349
|
-
* @returns {boolean} true only when the last verdict fence reports all_pass true
|
|
1290
|
+
* Decide whether a review transcript reports a passed review.
|
|
1291
|
+
* @param {string|null|undefined} verifyTranscript the review transcript text
|
|
1292
|
+
* @returns {boolean} true only when the final review result is PASS
|
|
1350
1293
|
*/
|
|
1351
|
-
function
|
|
1352
|
-
|
|
1353
|
-
return verdictRecord != null && verdictRecord.all_pass === true
|
|
1294
|
+
function reviewPassed(verifyTranscript) {
|
|
1295
|
+
return parseReviewResult(verifyTranscript) === 'PASS'
|
|
1354
1296
|
}
|
|
1355
1297
|
|
|
1356
|
-
const VERIFY_OBJECTION_FALLBACK = 'The
|
|
1298
|
+
const VERIFY_OBJECTION_FALLBACK = 'The review step rejected the working-tree fixes without an actionable result; re-read the fix-verify transcript and address every concern it raises.'
|
|
1357
1299
|
|
|
1358
1300
|
/**
|
|
1359
1301
|
* Render one verdict finding as a single objection line, tolerant of the shapes a
|
|
@@ -1383,68 +1325,16 @@ function renderVerifyObjectionLine(eachFinding) {
|
|
|
1383
1325
|
}
|
|
1384
1326
|
|
|
1385
1327
|
/**
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
1389
|
-
*
|
|
1390
|
-
*
|
|
1391
|
-
* Anchors through findLastVerdictFence, the single locator parseLastVerdictFence
|
|
1392
|
-
* reads its JSON from, so both readers see the same fence by construction rather
|
|
1393
|
-
* than by two searches that happen to agree: a stray marker after the real fence
|
|
1394
|
-
* — unterminated, or closed but carrying no newline after the marker — moves
|
|
1395
|
-
* neither reader. A candidate paragraph carrying only markdown scaffolding (a
|
|
1396
|
-
* heading, a fence delimiter) is skipped, since handing the fixer a heading is
|
|
1397
|
-
* worse than the generic fallback the caller keeps.
|
|
1398
|
-
*
|
|
1399
|
-
* ::
|
|
1400
|
-
*
|
|
1401
|
-
* 'ran the gates\n\ncheck X never showed red\n\n```verdict\n{}\n```'
|
|
1402
|
-
* -> 'check X never showed red'
|
|
1403
|
-
* '```verdict\n{}\n```' -> null
|
|
1404
|
-
*
|
|
1405
|
-
* @param {string|null|undefined} verifyTranscript the verifier transcript text
|
|
1406
|
-
* @returns {string|null} the last prose paragraph above the final fence, or null when there is none
|
|
1407
|
-
*/
|
|
1408
|
-
function extractPreFenceProse(verifyTranscript) {
|
|
1409
|
-
const lastFence = findLastVerdictFence(verifyTranscript)
|
|
1410
|
-
if (lastFence === null) return null
|
|
1411
|
-
const proseParagraphs = verifyTranscript
|
|
1412
|
-
.slice(0, lastFence.startIndex)
|
|
1413
|
-
.split(/\n\s*\n/)
|
|
1414
|
-
.map((eachParagraph) => eachParagraph.trim())
|
|
1415
|
-
.filter((eachParagraph) =>
|
|
1416
|
-
eachParagraph
|
|
1417
|
-
.split('\n')
|
|
1418
|
-
.some((eachLine) => {
|
|
1419
|
-
const trimmedLine = eachLine.trim()
|
|
1420
|
-
return trimmedLine.length > 0 && !trimmedLine.startsWith('#') && !trimmedLine.startsWith('```') && /[A-Za-z]/.test(trimmedLine)
|
|
1421
|
-
}),
|
|
1422
|
-
)
|
|
1423
|
-
return proseParagraphs.length === 0 ? null : proseParagraphs[proseParagraphs.length - 1]
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
/**
|
|
1427
|
-
* Pull the verifier's stated objections out of a failed verify transcript so the
|
|
1428
|
-
* re-fix step knows what the verdict rejected. Reads the last fenced verdict JSON
|
|
1429
|
-
* (the same block verdictPassed reads) and renders each finding through
|
|
1430
|
-
* renderVerifyObjectionLine into a numbered list. When findings yields no usable
|
|
1431
|
-
* line the verdict is an incomplete one rather than a code-defect one, so the
|
|
1432
|
-
* prose paragraph above the fence — where the fence contract puts the unshown-red
|
|
1433
|
-
* check — carries the objection instead. A missing fence, a parse failure, or an
|
|
1434
|
-
* empty findings list with no prose above the fence falls back to a generic
|
|
1435
|
-
* re-read instruction, so the re-fix step always receives actionable text.
|
|
1436
|
-
* @param {string|null|undefined} verifyTranscript the failed verifier transcript text
|
|
1437
|
-
* @returns {string} a human-readable block of the verifier's objections
|
|
1328
|
+
* Pull required findings from a failed review transcript so the repair step
|
|
1329
|
+
* receives actionable text.
|
|
1330
|
+
* @param {string|null|undefined} verifyTranscript the failed review transcript
|
|
1331
|
+
* @returns {string} the review findings or a fallback instruction
|
|
1438
1332
|
*/
|
|
1439
1333
|
function extractVerifyObjection(verifyTranscript) {
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
.map((eachFinding) => renderVerifyObjectionLine(eachFinding))
|
|
1445
|
-
.filter((eachLine) => eachLine !== null)
|
|
1446
|
-
if (renderedObjections.length === 0) return extractPreFenceProse(verifyTranscript) || VERIFY_OBJECTION_FALLBACK
|
|
1447
|
-
return renderedObjections.map((eachLine, position) => `${position + 1}. ${eachLine}`).join('\n')
|
|
1334
|
+
if (parseReviewResult(verifyTranscript) !== 'FINDINGS') return VERIFY_OBJECTION_FALLBACK
|
|
1335
|
+
const allTranscriptLines = verifyTranscript.trimEnd().split(/\r?\n/)
|
|
1336
|
+
const findingText = allTranscriptLines.slice(0, -1).join('\n').trim()
|
|
1337
|
+
return findingText.length > 0 ? findingText : VERIFY_OBJECTION_FALLBACK
|
|
1448
1338
|
}
|
|
1449
1339
|
|
|
1450
1340
|
/**
|
|
@@ -1974,6 +1864,45 @@ const FIX_RECOVERY_MAX_ATTEMPTS = 2
|
|
|
1974
1864
|
*/
|
|
1975
1865
|
const COMMIT_RECOVERY_MAX_ATTEMPTS = 1
|
|
1976
1866
|
|
|
1867
|
+
function readReviewedSurfaceHash(reportedSurfaceHash) {
|
|
1868
|
+
if (typeof reportedSurfaceHash !== 'string') return null
|
|
1869
|
+
return /^[0-9a-f]{64}$/.test(reportedSurfaceHash) ? reportedSurfaceHash : null
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
async function captureReviewedSurfaceHash(context) {
|
|
1873
|
+
const surfaceRecord = await runWorkingTreeSurfaceTask(context)
|
|
1874
|
+
return readReviewedSurfaceHash(surfaceRecord?.surfaceHash)
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
function reviewedSurfaceUnavailableResult(head) {
|
|
1878
|
+
return {
|
|
1879
|
+
newSha: head,
|
|
1880
|
+
pushed: false,
|
|
1881
|
+
resolvedWithoutCommit: false,
|
|
1882
|
+
summary: 'the reviewed working-tree surface changed or could not be fingerprinted before commit — not committing',
|
|
1883
|
+
blockedNeedingEdit: false,
|
|
1884
|
+
blockerDetail: '',
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
async function rebindReviewedSurface({ reviewedSurfaceHash, captureReviewedSurface, runVerify, runRecoverEdit }) {
|
|
1889
|
+
let boundSurfaceHash = reviewedSurfaceHash
|
|
1890
|
+
let driftAttempt = 0
|
|
1891
|
+
while (driftAttempt <= COMMIT_RECOVERY_MAX_ATTEMPTS) {
|
|
1892
|
+
const currentSurfaceHash = await captureReviewedSurface()
|
|
1893
|
+
if (boundSurfaceHash !== null && currentSurfaceHash === boundSurfaceHash) return boundSurfaceHash
|
|
1894
|
+
if (driftAttempt === COMMIT_RECOVERY_MAX_ATTEMPTS) return null
|
|
1895
|
+
driftAttempt += 1
|
|
1896
|
+
const verifyTranscript =
|
|
1897
|
+
typeof runRecoverEdit === 'function'
|
|
1898
|
+
? await verifyWithRecovery({ runVerify, runRecoverEdit })
|
|
1899
|
+
: await runVerify()
|
|
1900
|
+
if (!reviewPassed(verifyTranscript)) return null
|
|
1901
|
+
boundSurfaceHash = await captureReviewedSurface()
|
|
1902
|
+
}
|
|
1903
|
+
return null
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1977
1906
|
/**
|
|
1978
1907
|
* Run a commit step and, when it is blocked by a commit-time hook or gate that
|
|
1979
1908
|
* requires a code change, route back to a fixer: fix the blocking violation,
|
|
@@ -1986,18 +1915,29 @@ const COMMIT_RECOVERY_MAX_ATTEMPTS = 1
|
|
|
1986
1915
|
* @param {{runCommit: function, runVerify: function, runRecoverEdit: function}} steps the commit, re-verify, and recover-edit thunks
|
|
1987
1916
|
* @returns {Promise<object>} the final FIX_SCHEMA result
|
|
1988
1917
|
*/
|
|
1989
|
-
async function commitWithRecovery({ runCommit, runVerify, runRecoverEdit }) {
|
|
1990
|
-
let
|
|
1918
|
+
async function commitWithRecovery({ head, reviewedSurfaceHash, captureReviewedSurface, runCommit, runVerify, runRecoverEdit }) {
|
|
1919
|
+
let boundSurfaceHash = reviewedSurfaceHash
|
|
1920
|
+
let commitResult = null
|
|
1991
1921
|
let attempt = 0
|
|
1992
|
-
while (
|
|
1922
|
+
while (attempt <= COMMIT_RECOVERY_MAX_ATTEMPTS) {
|
|
1923
|
+
boundSurfaceHash = await rebindReviewedSurface({
|
|
1924
|
+
reviewedSurfaceHash: boundSurfaceHash,
|
|
1925
|
+
captureReviewedSurface,
|
|
1926
|
+
runVerify,
|
|
1927
|
+
runRecoverEdit,
|
|
1928
|
+
})
|
|
1929
|
+
if (boundSurfaceHash === null) return reviewedSurfaceUnavailableResult(head)
|
|
1930
|
+
commitResult = await runCommit()
|
|
1931
|
+
if (!commitNeedsCodeRecovery(commitResult) || attempt === COMMIT_RECOVERY_MAX_ATTEMPTS) return commitResult
|
|
1993
1932
|
attempt += 1
|
|
1994
1933
|
const recoverEdit = await runRecoverEdit(commitResult.blockerDetail, attempt)
|
|
1995
|
-
if (recoverEdit?.edited !== true)
|
|
1934
|
+
if (recoverEdit?.edited !== true) return commitResult
|
|
1996
1935
|
const verifyTranscript = await runVerify()
|
|
1997
|
-
if (!
|
|
1998
|
-
|
|
1936
|
+
if (!reviewPassed(verifyTranscript)) return commitResult
|
|
1937
|
+
boundSurfaceHash = await captureReviewedSurface()
|
|
1938
|
+
if (boundSurfaceHash === null) return reviewedSurfaceUnavailableResult(head)
|
|
1999
1939
|
}
|
|
2000
|
-
return
|
|
1940
|
+
return reviewedSurfaceUnavailableResult(head)
|
|
2001
1941
|
}
|
|
2002
1942
|
|
|
2003
1943
|
/**
|
|
@@ -2013,7 +1953,7 @@ async function commitWithRecovery({ runCommit, runVerify, runRecoverEdit }) {
|
|
|
2013
1953
|
async function verifyWithRecovery({ runVerify, runRecoverEdit }) {
|
|
2014
1954
|
let verifyTranscript = await runVerify()
|
|
2015
1955
|
let attempt = 0
|
|
2016
|
-
while (!
|
|
1956
|
+
while (!reviewPassed(verifyTranscript) && attempt < FIX_RECOVERY_MAX_ATTEMPTS) {
|
|
2017
1957
|
attempt += 1
|
|
2018
1958
|
const objection = extractVerifyObjection(verifyTranscript)
|
|
2019
1959
|
const recoverEdit = await runRecoverEdit(objection, attempt)
|
|
@@ -2024,14 +1964,9 @@ async function verifyWithRecovery({ runVerify, runRecoverEdit }) {
|
|
|
2024
1964
|
}
|
|
2025
1965
|
|
|
2026
1966
|
/**
|
|
2027
|
-
* Fix lens: edit (clean-coder, no commit) ->
|
|
2028
|
-
*
|
|
2029
|
-
*
|
|
2030
|
-
* the verdict that gates the commit comes from a different agent than the one
|
|
2031
|
-
* that edits and pushes — the same editor/verifier separation the repair and
|
|
2032
|
-
* conflict paths use, and the separation a workflow code-verifier needs to
|
|
2033
|
-
* produce the verdict the verified-commit gate requires, which the SubagentStop
|
|
2034
|
-
* minter cannot mint for workflow-spawned agents. When verification fails (or the
|
|
1967
|
+
* Fix lens: edit (clean-coder, no commit) -> review (code-quality-agent under
|
|
1968
|
+
* the canonical review guide) -> commit (clean-coder, one commit + push, no
|
|
1969
|
+
* edits). When review fails (or the
|
|
2035
1970
|
* edit step stalled with no thread to resolve), the commit step is skipped and the
|
|
2036
1971
|
* unchanged HEAD is returned so the round reads as not-progressed.
|
|
2037
1972
|
* @param {string} head PR HEAD SHA the findings were raised against
|
|
@@ -2264,11 +2199,8 @@ function runRefutedThreadResolution(head, refutedFindings) {
|
|
|
2264
2199
|
/**
|
|
2265
2200
|
* Address the gates a convergence check reported as failing, then hand control
|
|
2266
2201
|
* back to the converge phase: edit (clean-coder resolves bot threads, applies
|
|
2267
|
-
* any fix and rebase in the working tree, no push) ->
|
|
2268
|
-
*
|
|
2269
|
-
* commit + push, no edits). Splitting the edit from the push lets a workflow
|
|
2270
|
-
* code-verifier produce the verdict the verified-commit gate requires for the
|
|
2271
|
-
* bot-thread fix commit and the post-rebase force-push. When the edit resolved
|
|
2202
|
+
* any fix and rebase in the working tree, no push) -> review -> commit
|
|
2203
|
+
* (clean-coder, one commit + push, no edits). When the edit resolved
|
|
2272
2204
|
* the gates with neither a code change nor a rebase, or the verify step fails,
|
|
2273
2205
|
* the commit step is skipped and the unchanged HEAD is returned.
|
|
2274
2206
|
* @param {string} head current PR HEAD SHA
|
|
@@ -2289,10 +2221,10 @@ async function repairConvergence(head, failures) {
|
|
|
2289
2221
|
}
|
|
2290
2222
|
}
|
|
2291
2223
|
const verifyTranscript = await verifyWithRecovery({
|
|
2292
|
-
runVerify: () =>
|
|
2224
|
+
runVerify: () => runReviewTask('repair-verify', { head, failures }),
|
|
2293
2225
|
runRecoverEdit: (objection, attempt) => runCodeEditorTask('verify-recover', { head, sourceLabel: 'repair', objection, attempt }),
|
|
2294
2226
|
})
|
|
2295
|
-
if (!
|
|
2227
|
+
if (!reviewPassed(verifyTranscript)) {
|
|
2296
2228
|
return {
|
|
2297
2229
|
newSha: head,
|
|
2298
2230
|
pushed: false,
|
|
@@ -2303,9 +2235,15 @@ async function repairConvergence(head, failures) {
|
|
|
2303
2235
|
}
|
|
2304
2236
|
}
|
|
2305
2237
|
const wasRebased = editResult?.rebased === true
|
|
2238
|
+
const captureReviewedSurface = () => captureReviewedSurfaceHash({ sourceLabel: 'repair', phase: 'Finalize' })
|
|
2239
|
+
const reviewedSurfaceHash = await captureReviewedSurface()
|
|
2240
|
+
if (reviewedSurfaceHash === null) return reviewedSurfaceUnavailableResult(head)
|
|
2306
2241
|
return commitWithRecovery({
|
|
2242
|
+
head,
|
|
2243
|
+
reviewedSurfaceHash,
|
|
2244
|
+
captureReviewedSurface,
|
|
2307
2245
|
runCommit: () => runCodeEditorTask('repair-commit', { head, wasRebased }),
|
|
2308
|
-
runVerify: () =>
|
|
2246
|
+
runVerify: () => runReviewTask('repair-verify', { head, failures }),
|
|
2309
2247
|
runRecoverEdit: (detail, attempt) => runCodeEditorTask('commit-recover', { head, sourceLabel: 'repair', blockerDetail: detail, attempt }),
|
|
2310
2248
|
})
|
|
2311
2249
|
}
|
|
@@ -2314,12 +2252,12 @@ async function repairConvergence(head, failures) {
|
|
|
2314
2252
|
/**
|
|
2315
2253
|
* Pre-flight conflict resolution: when the PR branch conflicts with its base,
|
|
2316
2254
|
* rebase it clean before the bug checks run — edit (clean-coder rebases and
|
|
2317
|
-
* resolves, no push) ->
|
|
2318
|
-
*
|
|
2255
|
+
* resolves, no push) -> review -> commit (clean-coder force-with-lease pushes).
|
|
2256
|
+
* The conflict decision
|
|
2319
2257
|
* comes from the merged 'preflight-git' probe the caller already ran, so this
|
|
2320
2258
|
* function spawns no mergeability agent of its own. Returns the post-rebase
|
|
2321
2259
|
* HEAD so the first converge round runs its lenses on the conflict-free diff.
|
|
2322
|
-
* A non-conflicting PR, a rebase the edit step declined, or a failed
|
|
2260
|
+
* A non-conflicting PR, a rebase the edit step declined, or a failed review
|
|
2323
2261
|
* returns the unchanged HEAD so the run proceeds to the bug checks unchanged.
|
|
2324
2262
|
* A mid-run conflict (origin/main advancing later) is still caught by the
|
|
2325
2263
|
* FINALIZE convergence repair, which also rebases.
|
|
@@ -2334,13 +2272,19 @@ async function resolveMergeConflicts(head, isConflicting) {
|
|
|
2334
2272
|
if (editResult?.rebased !== true) return head
|
|
2335
2273
|
const failures = ['PR branch had merge conflicts with origin/main; the rebase must leave a clean, conflict-free tree']
|
|
2336
2274
|
const verifyTranscript = await verifyWithRecovery({
|
|
2337
|
-
runVerify: () =>
|
|
2275
|
+
runVerify: () => runReviewTask('repair-verify', { head, failures }),
|
|
2338
2276
|
runRecoverEdit: (objection, attempt) => runCodeEditorTask('verify-recover', { head, sourceLabel: 'conflict-rebase', objection, attempt }),
|
|
2339
2277
|
})
|
|
2340
|
-
if (!
|
|
2278
|
+
if (!reviewPassed(verifyTranscript)) return head
|
|
2279
|
+
const captureReviewedSurface = () => captureReviewedSurfaceHash({ sourceLabel: 'conflict-rebase', phase: 'Converge' })
|
|
2280
|
+
const reviewedSurfaceHash = await captureReviewedSurface()
|
|
2281
|
+
if (reviewedSurfaceHash === null) return head
|
|
2341
2282
|
const commitResult = await commitWithRecovery({
|
|
2283
|
+
head,
|
|
2284
|
+
reviewedSurfaceHash,
|
|
2285
|
+
captureReviewedSurface,
|
|
2342
2286
|
runCommit: () => runCodeEditorTask('repair-commit', { head, wasRebased: true }),
|
|
2343
|
-
runVerify: () =>
|
|
2287
|
+
runVerify: () => runReviewTask('repair-verify', { head, failures }),
|
|
2344
2288
|
runRecoverEdit: (detail, attempt) => runCodeEditorTask('commit-recover', { head, sourceLabel: 'conflict-rebase', blockerDetail: detail, attempt }),
|
|
2345
2289
|
})
|
|
2346
2290
|
return commitResult?.newSha || head
|
|
@@ -2426,9 +2370,9 @@ async function applyP2OnlyFix(head, findings, sourceLabel, stallSubject, noPushS
|
|
|
2426
2370
|
}
|
|
2427
2371
|
|
|
2428
2372
|
/**
|
|
2429
|
-
* Route after a successful P2-only fix.
|
|
2430
|
-
*
|
|
2431
|
-
*
|
|
2373
|
+
* Route after a successful P2-only fix. A push that moves HEAD re-enters
|
|
2374
|
+
* CONVERGE so the current review state rebuilds. A no-push progress
|
|
2375
|
+
* (resolvedWithoutCommit) advances to the next
|
|
2432
2376
|
* phase on the same HEAD.
|
|
2433
2377
|
*
|
|
2434
2378
|
* ::
|
|
@@ -2604,10 +2548,8 @@ function parseDeferredPr(prUrl) {
|
|
|
2604
2548
|
* Defer a standards-only round: edit (clean-coder files the follow-up fix issue,
|
|
2605
2549
|
* stages an environment-hardening hooks/rules change in the config repo's
|
|
2606
2550
|
* working tree without committing, and resolves the PR's code-standard threads)
|
|
2607
|
-
* ->
|
|
2608
|
-
*
|
|
2609
|
-
* PR). Splitting the edit from the push lets a workflow code-verifier produce the
|
|
2610
|
-
* verdict the verified-commit gate requires for the cross-repo hardening commit.
|
|
2551
|
+
* -> review -> commit (clean-coder makes one commit, pushes, and opens the DRAFT
|
|
2552
|
+
* hardening PR).
|
|
2611
2553
|
* This PR's own branch is never touched. When a hardening PR already opened for
|
|
2612
2554
|
* this run, the edit staged no hardening, or the verify step fails, the follow-up
|
|
2613
2555
|
* issue still stands and the commit step is skipped.
|
|
@@ -2628,10 +2570,31 @@ async function spawnStandardsFollowUp(head, findings, sourceLabel, hasHardeningP
|
|
|
2628
2570
|
if (editResult?.hardeningEdited !== true || !editResult?.hardeningRepoPath) {
|
|
2629
2571
|
return { followUpIssueFiled, issueUrl: followUpIssueUrl, hardeningPrOpened: false, deferredPr: null }
|
|
2630
2572
|
}
|
|
2631
|
-
const verifyTranscript = await
|
|
2573
|
+
const verifyTranscript = await runReviewTask('hardening-verify', {
|
|
2632
2574
|
head, sourceLabel, hardeningRepoPath: editResult.hardeningRepoPath, hardeningBranch: editResult.hardeningBranch,
|
|
2633
2575
|
})
|
|
2634
|
-
if (!
|
|
2576
|
+
if (!reviewPassed(verifyTranscript)) {
|
|
2577
|
+
return { followUpIssueFiled, issueUrl: followUpIssueUrl, hardeningPrOpened: false, deferredPr: null }
|
|
2578
|
+
}
|
|
2579
|
+
const hardeningReviewContext = {
|
|
2580
|
+
head,
|
|
2581
|
+
sourceLabel,
|
|
2582
|
+
hardeningRepoPath: editResult.hardeningRepoPath,
|
|
2583
|
+
hardeningBranch: editResult.hardeningBranch,
|
|
2584
|
+
}
|
|
2585
|
+
const captureReviewedSurface = () =>
|
|
2586
|
+
captureReviewedSurfaceHash({
|
|
2587
|
+
sourceLabel: `hardening:${sourceLabel}`,
|
|
2588
|
+
phase: 'Converge',
|
|
2589
|
+
repositoryPath: editResult.hardeningRepoPath,
|
|
2590
|
+
})
|
|
2591
|
+
const reviewedSurfaceHash = await captureReviewedSurface()
|
|
2592
|
+
const stableReviewedSurfaceHash = await rebindReviewedSurface({
|
|
2593
|
+
reviewedSurfaceHash,
|
|
2594
|
+
captureReviewedSurface,
|
|
2595
|
+
runVerify: () => runReviewTask('hardening-verify', hardeningReviewContext),
|
|
2596
|
+
})
|
|
2597
|
+
if (stableReviewedSurfaceHash === null) {
|
|
2635
2598
|
return { followUpIssueFiled, issueUrl: followUpIssueUrl, hardeningPrOpened: false, deferredPr: null }
|
|
2636
2599
|
}
|
|
2637
2600
|
const commitResult = await runCodeEditorTask('hardening-commit', {
|