claude-dev-env 1.95.0 → 2.0.1
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/_shared/advisor/CLAUDE.md +2 -2
- package/_shared/advisor/advisor-protocol.md +20 -20
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +15 -12
- package/_shared/advisor/scripts/model_tier_run_validator.py +11 -10
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +25 -19
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +17 -17
- package/_shared/advisor/scripts/tier_model_ids.py +18 -18
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +2 -1
- package/_shared/pr-loop/scripts/README.md +1 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +253 -1980
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +32 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/__init__.py +7 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +268 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/enforcer_loading.py +172 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_arguments.py +70 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +326 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +85 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +331 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +369 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/conftest.py +14 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_added_line_maps.py +118 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_enforcer_loading.py +17 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_arguments.py +29 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +99 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_blob_readers.py +69 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_file_sets.py +137 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +116 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_violation_scoping.py +75 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_wrapper_plumb_check.py +49 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/violation_scoping.py +328 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/wrapper_plumb_check.py +206 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +24 -17
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +1 -0
- package/_shared/pr-loop/scripts/reviews_disabled.py +19 -2
- package/_shared/pr-loop/scripts/test_code_rules_gate.py +278 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +6 -39
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +43 -0
- package/_shared/pr-loop/worker-spawn.md +186 -0
- package/agents/code-verifier.md +1 -1
- package/bin/ever-shipped-skills.mjs +3 -0
- package/bin/expand_home_directory_tokens.mjs +1 -1
- package/bin/install.mjs +5 -2
- package/hooks/advisory/refactor_guard.py +3 -4
- package/hooks/blocking/CLAUDE.md +7 -1
- package/hooks/blocking/block_main_commit.py +2 -2
- package/hooks/blocking/claude_md_orphan_file_blocker.py +75 -699
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +28 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/orphan_blocker_constants.py +18 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/decision.py +81 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/references.py +307 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/scan_plan.py +124 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/subtree_scan.py +179 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_decision.py +34 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_references.py +42 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_scan_plan.py +27 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_subtree_scan.py +30 -0
- package/hooks/blocking/code_rules_boolean_mustcheck.py +1 -1
- package/hooks/blocking/code_rules_mock_completeness.py +1 -1
- package/hooks/blocking/code_rules_optional_params.py +2 -2
- package/hooks/blocking/code_rules_shared.py +1 -1
- package/hooks/blocking/code_rules_test_assertions.py +1 -1
- package/hooks/blocking/code_rules_typeddict_stub.py +1 -1
- package/hooks/blocking/gh_pr_author_enforcer.py +1 -1
- package/hooks/blocking/inventory_intent_records/CLAUDE.md +26 -0
- package/hooks/blocking/inventory_intent_records/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/intent_records_constants.py +20 -0
- package/hooks/blocking/inventory_intent_records/records.py +271 -0
- package/hooks/blocking/inventory_intent_records/tests/conftest.py +10 -0
- package/hooks/blocking/inventory_intent_records/tests/test_records.py +80 -0
- package/hooks/blocking/package_inventory_stale_blocker.py +54 -384
- package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +26 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/inventory_blocker_constants.py +16 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/decision.py +84 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/inventory_detection.py +307 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_decision.py +38 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_inventory_detection.py +61 -0
- package/hooks/blocking/pii_payload_scan.py +138 -42
- package/hooks/blocking/pii_prevention_blocker.py +185 -291
- package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +24 -0
- package/hooks/blocking/pii_prevention_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/repository_resolution_constants.py +28 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +214 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_resolution.py +208 -0
- package/hooks/blocking/pr_description_command_parser.py +8 -4
- package/hooks/blocking/precommit_code_rules_gate.py +3 -3
- package/hooks/blocking/tdd_enforcer.py +97 -608
- package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +30 -0
- package/hooks/blocking/tdd_enforcer_parts/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/candidate_paths.py +142 -0
- package/hooks/blocking/tdd_enforcer_parts/config/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/config/tdd_enforcer_constants.py +32 -0
- package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +268 -0
- package/hooks/blocking/tdd_enforcer_parts/decisions.py +92 -0
- package/hooks/blocking/tdd_enforcer_parts/freshness.py +80 -0
- package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +63 -0
- package/hooks/blocking/tdd_enforcer_parts/path_classification.py +119 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/conftest.py +10 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_candidate_paths.py +31 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +30 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_decisions.py +34 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_freshness.py +28 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_git_tracking.py +48 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_path_classification.py +36 -0
- package/hooks/blocking/test_inventory_deadlock_resolution.py +154 -0
- package/hooks/blocking/test_pii_payload_scan.py +168 -0
- package/hooks/blocking/test_tdd_enforcer_restore.py +108 -0
- package/hooks/blocking/test_verifier_verdict_minter.py +55 -158
- package/hooks/blocking/tests/conftest.py +10 -0
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +260 -0
- package/hooks/blocking/tests/test_repository_exemption.py +105 -0
- package/hooks/blocking/tests/test_repository_resolution.py +108 -0
- package/hooks/diagnostic/hook_log_extractor.py +12 -10
- package/hooks/git-hooks/post_commit.py +3 -4
- package/hooks/hooks_constants/CLAUDE.md +2 -2
- package/hooks/hooks_constants/banned_identifiers_constants.py +0 -1
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
- package/hooks/hooks_constants/local_identity.py +59 -8
- package/hooks/hooks_constants/pii_prevention_constants.py +0 -6
- package/hooks/hooks_constants/test_local_identity.py +105 -3
- package/hooks/pyproject.toml +13 -36
- package/hooks/session/plugin_data_dir_cleanup.py +0 -1
- package/hooks/validation/mypy_validator.py +2 -2
- package/hooks/validators/health_check.py +1 -0
- package/hooks/validators/mypy_integration.py +2 -0
- package/hooks/validators/ruff_integration.py +3 -0
- package/hooks/workflow/auto_formatter.py +5 -4
- package/package.json +1 -1
- package/scripts/CLAUDE.md +4 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +6 -4
- package/scripts/dev_env_scripts_constants/code_review_constants.py +71 -0
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +435 -0
- package/scripts/dev_env_scripts_constants/timing.py +7 -1
- package/scripts/grok_headless_runner.py +294 -0
- package/scripts/grok_worker_preflight.py +410 -0
- package/scripts/invoke_code_review.py +463 -0
- package/scripts/resolve_worker_spawn.py +619 -0
- package/scripts/spawn_grok_batch.py +672 -0
- package/scripts/test_grok_headless_runner.py +626 -0
- package/scripts/test_grok_worker_preflight.py +1054 -0
- package/scripts/test_invoke_code_review.py +672 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -0
- package/scripts/test_spawn_grok_batch.py +1017 -0
- package/skills/CLAUDE.md +5 -3
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +72 -13
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +121 -14
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +78 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +121 -0
- package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +196 -6
- package/skills/autoconverge/CLAUDE.md +3 -3
- package/skills/autoconverge/SKILL.md +9 -3
- package/skills/autoconverge/reference/CLAUDE.md +2 -2
- package/skills/autoconverge/reference/convergence.md +33 -11
- package/skills/autoconverge/reference/stop-conditions.md +16 -5
- package/skills/autoconverge/workflow/CLAUDE.md +2 -1
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +7 -2
- package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +300 -0
- package/skills/autoconverge/workflow/converge.contract.test.mjs +5 -5
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +29 -29
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +200 -16
- package/skills/bugteam/CLAUDE.md +2 -2
- package/skills/bugteam/CONSTRAINTS.md +3 -2
- package/skills/bugteam/PROMPTS.md +7 -6
- package/skills/bugteam/SKILL.md +18 -13
- package/skills/bugteam/reference/audit-and-teammates.md +215 -35
- package/skills/bugteam/reference/design-rationale.md +1 -1
- package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -1
- package/skills/bugteam/reference/team-setup.md +8 -2
- package/skills/codex-review/CLAUDE.md +46 -0
- package/skills/codex-review/SKILL.md +181 -0
- package/skills/codex-review/reference/CLAUDE.md +15 -0
- package/skills/codex-review/reference/cli-contract.md +253 -0
- package/skills/codex-review/reference/loop-integration.md +118 -0
- package/skills/codex-review/scripts/codex_down_classifier.py +98 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/__init__.py +1 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +86 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +45 -0
- package/skills/codex-review/scripts/codex_usage_probe.py +573 -0
- package/skills/codex-review/scripts/fixtures/auth_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
- package/skills/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
- package/skills/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
- package/skills/codex-review/scripts/fixtures/structured_findings.txt +13 -0
- package/skills/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
- package/skills/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/usage_limit_synthetic.txt +1 -0
- package/skills/codex-review/scripts/parse_codex_findings.py +207 -0
- package/skills/codex-review/scripts/run_codex_review.py +415 -0
- package/skills/codex-review/scripts/test_codex_down_classifier.py +143 -0
- package/skills/codex-review/scripts/test_codex_usage_probe.py +678 -0
- package/skills/codex-review/scripts/test_parse_codex_findings.py +130 -0
- package/skills/codex-review/scripts/test_run_codex_review.py +812 -0
- package/skills/codex-review/test_skill_scaffold.py +192 -0
- package/skills/grok-spawn/CLAUDE.md +28 -0
- package/skills/grok-spawn/SKILL.md +226 -0
- package/skills/grok-spawn/reference/flag-profiles.md +132 -0
- package/skills/grok-spawn/reference/worker-briefs.md +152 -0
- package/skills/grokify/SKILL.md +9 -1
- package/skills/grokify/capability-claims.test.mjs +28 -0
- package/skills/grokify/evals/README.md +72 -0
- package/skills/grokify/evals/parse-payload.test.mjs +171 -0
- package/skills/grokify/evals/run-capability-evals.mjs +545 -0
- package/skills/orchestrator/SKILL.md +42 -29
- package/skills/orchestrator-refresh/SKILL.md +17 -9
- package/skills/pr-converge/SKILL.md +34 -13
- package/skills/pr-converge/reference/convergence-gates.md +42 -15
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +1 -1
- package/skills/pr-converge/reference/per-tick.md +130 -42
- package/skills/pr-converge/reference/state-schema.md +10 -0
- package/skills/pr-converge/scripts/CLAUDE.md +2 -0
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +5 -1
- package/skills/pr-converge/scripts/check_convergence.py +605 -29
- package/skills/pr-converge/scripts/check_convergence_availability.py +232 -0
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -235
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +1 -1
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -2
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +4 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +71 -3
- package/skills/pr-converge/scripts/test_check_convergence_availability.py +326 -0
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -0
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +89 -17
- package/skills/pr-converge/scripts/test_check_convergence_fixture.py +179 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -68
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +24 -0
- package/skills/pr-converge/test_step5_host_branch.py +106 -0
- package/skills/pr-loop-cloud-transport/SKILL.md +2 -0
- package/skills/reviewer-gates/SKILL.md +7 -5
- package/skills/team-advisor/SKILL.md +7 -7
|
@@ -11,23 +11,27 @@
|
|
|
11
11
|
|
|
12
12
|
export const meta = {
|
|
13
13
|
name: 'autoconverge',
|
|
14
|
-
description: 'Drive one draft PR to convergence in a single autonomous run: each round runs a deterministic static sweep first, then code-review, bug-audit, and self-review lenses in parallel on the same HEAD, dedups findings, fixes once, and re-verifies; Bugbot and
|
|
14
|
+
description: 'Drive one draft PR to convergence in a single autonomous run: each round runs a deterministic static sweep first, then code-review, bug-audit, and self-review lenses in parallel on the same HEAD, dedups findings, fixes once, and re-verifies; Bugbot, Copilot, and Codex then run as terminal confirmation gates before a final convergence check marks the PR ready.',
|
|
15
15
|
whenToUse: 'Launched by the /autoconverge skill after it resolves PR scope, enters a worktree, and grants project .claude permissions.',
|
|
16
16
|
phases: [
|
|
17
17
|
{ title: 'Reuse', detail: 'Before convergence, one reuse lens scans the full diff for reuse improvements that are certain, behaviorally identical, and autonomously implementable, and applies the qualifying ones in one commit' },
|
|
18
18
|
{ title: 'Converge', detail: 'A deterministic static sweep runs first each round; then code-review, bug-audit, and self-review run in parallel on the same HEAD; one clean-coder applies all fixes; the loop repeats until every lens is clean on a stable HEAD' },
|
|
19
19
|
{ title: 'Bugbot gate', detail: 'A terminal confirmation gate that runs once the internal lenses are clean; when Bugbot is disabled or unavailable it spawns no agent and passes, otherwise it fetches Bugbot\'s verdict and routes any finding back into Converge' },
|
|
20
|
-
{ title: 'Copilot gate', detail: 'When the quota pre-check reports Copilot out of premium-request quota or unavailable, skip the gate with no agent spawned; otherwise request a Copilot review and poll up to the configured cap, then route findings by tier — self-healing findings flow back into Converge, and each code-concern finding goes to its own verifier agent that must execute a check against HEAD: a confirmed finding (defect reproduced by a run command) joins the fix round carrying its repro, a refuted finding (correct behavior demonstrated by a run command) is replied-to and resolved with the check evidence, and only inconclusive findings return blocker user-review for the orchestrator to gate on rather than auto-fixing or marking the PR ready; when Copilot is down or out of quota log a notice and
|
|
20
|
+
{ title: 'Copilot gate', detail: 'When the quota pre-check reports Copilot out of premium-request quota or unavailable, skip the gate with no agent spawned; otherwise request a Copilot review and poll up to the configured cap, then route findings by tier — self-healing findings flow back into Converge, and each code-concern finding goes to its own verifier agent that must execute a check against HEAD: a confirmed finding (defect reproduced by a run command) joins the fix round carrying its repro, a refuted finding (correct behavior demonstrated by a run command) is replied-to and resolved with the check evidence, and only inconclusive findings return blocker user-review for the orchestrator to gate on rather than auto-fixing or marking the PR ready; when Copilot is down or out of quota log a notice and proceed with the gate bypassed' },
|
|
21
|
+
{ title: 'Codex gate', detail: 'A conditional-required terminal gate that runs after Bugbot and Copilot: when CLAUDE_REVIEWS_DISABLED lists codex, when weekly usage is at or below the shared probe threshold (or null), or when the wrapper classifies codex_down, the gate skips without blocking; when usage is above the threshold it runs the codex-review wrapper against the PR base branch, routes findings into the fix path, and on a clean pass records the codex-clean HEAD for the convergence check' },
|
|
21
22
|
{ title: 'Finalize', detail: 'Run check_convergence.py; mark draft=false on a full pass' },
|
|
22
23
|
],
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
const homeDirectory = (process.env.HOME || process.env.USERPROFILE || '').replace(/\\/g, '/')
|
|
27
|
+
|
|
25
28
|
const CONFIG = {
|
|
26
29
|
maxIterations: 20,
|
|
27
30
|
maxConsecutiveNoLensRounds: 3,
|
|
28
31
|
copilotMaxPolls: 8,
|
|
29
32
|
sharedScripts: '$HOME/.claude/skills/pr-converge/scripts',
|
|
30
33
|
prLoopScripts: '$HOME/.claude/_shared/pr-loop/scripts',
|
|
34
|
+
codexScripts: `${homeDirectory}/.claude/skills/codex-review/scripts`,
|
|
31
35
|
bugteamRubric: '$HOME/.claude/_shared/pr-loop/audit-contract.md',
|
|
32
36
|
precatchRubric: '$HOME/.claude/_shared/pr-loop/precatch-rubric.md',
|
|
33
37
|
}
|
|
@@ -539,13 +543,15 @@ function runGeneralUtilityTask(task, context) {
|
|
|
539
543
|
* the FINALIZE loop routes to the repair path, which loops back and re-runs this
|
|
540
544
|
* same combined check; only the passing path ever attempts gh pr ready. The agent
|
|
541
545
|
* edits no code, so it runs on the cheapest model at low effort.
|
|
542
|
-
* @param {object} context carries head, bugbotDown, copilotDown, and bugteamPostBlocked
|
|
546
|
+
* @param {object} context carries head, bugbotDown, copilotDown, codexDown, codexCleanAt, and bugteamPostBlocked
|
|
543
547
|
* @returns {Promise<object>} FINALIZE_SCHEMA result
|
|
544
548
|
*/
|
|
545
549
|
function runConvergenceCheck(context) {
|
|
546
550
|
const label = 'finalize-check'
|
|
547
551
|
const bugbotDownFlag = context.bugbotDown ? ' --bugbot-down' : ''
|
|
548
552
|
const copilotDownFlag = context.copilotDown ? ' --copilot-down' : ''
|
|
553
|
+
const codexDownFlag = context.codexDown ? ' --codex-down' : ''
|
|
554
|
+
const codexCleanAtFlag = context.codexCleanAt ? ` --codex-clean-at ${context.codexCleanAt}` : ''
|
|
549
555
|
const bugteamPostBlockedFlag = context.bugteamPostBlocked ? ' --bugteam-post-blocked' : ''
|
|
550
556
|
const bugteamPostBlockedNote = context.bugteamPostBlocked
|
|
551
557
|
? ` The --bugteam-post-blocked flag is set because the environment refused the CLEAN bugteam review post this run. The review lenses already cleared this HEAD, so the check skips the bugteam CLEAN-review gate and reports that gate as PASS — bypassed (bugteam_post_blocked) rather than failing on a review that was never allowed to land.\n`
|
|
@@ -553,19 +559,33 @@ function runConvergenceCheck(context) {
|
|
|
553
559
|
const reviewerOptOutTokens = []
|
|
554
560
|
if (context.bugbotDown) reviewerOptOutTokens.push('bugbot')
|
|
555
561
|
if (context.copilotDown) reviewerOptOutTokens.push('copilot')
|
|
562
|
+
if (context.codexDown) reviewerOptOutTokens.push('codex')
|
|
556
563
|
if (context.bugteamPostBlocked) reviewerOptOutTokens.push('bugteam')
|
|
564
|
+
const jobDirName = process.env.CLAUDE_JOB_DIR
|
|
565
|
+
const needsCodexCleanExportFallback = Boolean(context.codexCleanAt) && !jobDirName
|
|
566
|
+
if (needsCodexCleanExportFallback && !reviewerOptOutTokens.includes('codex')) {
|
|
567
|
+
reviewerOptOutTokens.push('codex')
|
|
568
|
+
}
|
|
557
569
|
const reviewerOptOut = reviewerOptOutTokens.length > 0
|
|
558
|
-
? ` Reviewer(s) opted out this run (${reviewerOptOutTokens.join(', ')}), so before gh pr ready export the token(s) in this same shell session so the independent mark-ready blocker hook's convergence re-check — which re-runs check_convergence.py with no flags — inherits the bypass through the env:\n bash: export CLAUDE_REVIEWS_DISABLED="${reviewerOptOutTokens.join(',')}" (PowerShell: $env:CLAUDE_REVIEWS_DISABLED = "${reviewerOptOutTokens.join(',')}")\n`
|
|
570
|
+
? ` Reviewer(s) opted out this run (${reviewerOptOutTokens.join(', ')}), so before gh pr ready export the token(s) in this same shell session so the independent mark-ready blocker hook's convergence re-check — which re-runs check_convergence.py with no flags — inherits the bypass through the env. Emit exactly one export (merge every token into a single value; never overwrite with a second assignment):\n bash: export CLAUDE_REVIEWS_DISABLED="${reviewerOptOutTokens.join(',')}" (PowerShell: $env:CLAUDE_REVIEWS_DISABLED = "${reviewerOptOutTokens.join(',')}")\n`
|
|
571
|
+
: ''
|
|
572
|
+
const codexCleanAtNote = context.codexCleanAt
|
|
573
|
+
? (
|
|
574
|
+
jobDirName
|
|
575
|
+
? ` The Codex gate reported clean on this HEAD, so the --codex-clean-at flag above satisfies this same-shell check. The independent mark-ready blocker hook re-runs check_convergence.py with no flags, and it reads the Codex clean stamp only from the single-PR job-dir state file, so before gh pr ready persist the stamp there: merge {"codex_clean_at": "${context.codexCleanAt}"} into the file named pr-converge-state.json inside the directory named by the CLAUDE_JOB_DIR environment variable (create the file with just that key when it does not exist yet, and preserve any keys already in it).\n`
|
|
576
|
+
: ` The Codex gate reported clean on this HEAD, so the --codex-clean-at flag above satisfies this same-shell check. CLAUDE_JOB_DIR is unset in this shell, so there is no durable stamp file for the flagless mark-ready re-check — the codex token is already included in the single CLAUDE_REVIEWS_DISABLED export above (do not emit a second export that overwrites other tokens).\n`
|
|
577
|
+
)
|
|
559
578
|
: ''
|
|
560
579
|
return convergeReadOnlyAgent(
|
|
561
580
|
`Run the convergence gate for ${prCoordinates} on HEAD ${context.head} and, only when every gate passes, mark the PR ready. Do not edit code.\n\n` +
|
|
562
|
-
`1. Run: python "${CONFIG.sharedScripts}/check_convergence.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber}${bugbotDownFlag}${copilotDownFlag}${bugteamPostBlockedFlag}\n` +
|
|
581
|
+
`1. Run: python "${CONFIG.sharedScripts}/check_convergence.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber}${bugbotDownFlag}${copilotDownFlag}${codexDownFlag}${codexCleanAtFlag}${bugteamPostBlockedFlag}\n` +
|
|
563
582
|
bugteamPostBlockedNote +
|
|
564
583
|
` Exit 0 -> every gate passed: set pass:true, failures:[].\n` +
|
|
565
584
|
` Exit 1 -> set pass:false and failures to each printed FAIL line verbatim.\n` +
|
|
566
585
|
` Exit 2 -> retry once; if it still errors, set pass:false, failures:["check_convergence gh error"].\n\n` +
|
|
567
586
|
`2. Only when step 1 set pass:true, mark the PR ready and confirm it left draft state:\n` +
|
|
568
587
|
reviewerOptOut +
|
|
588
|
+
codexCleanAtNote +
|
|
569
589
|
` Run: gh pr ready ${input.prNumber} --repo ${input.owner}/${input.repo}\n` +
|
|
570
590
|
` Re-query the draft state: gh api repos/${input.owner}/${input.repo}/pulls/${input.prNumber} --jq .draft\n` +
|
|
571
591
|
` Set ready:true only when the re-query prints false (the PR is no longer a draft); set ready:false when gh pr ready errors or the re-query still prints true.\n` +
|
|
@@ -639,13 +659,51 @@ const COPILOT_SCHEMA = {
|
|
|
639
659
|
properties: {
|
|
640
660
|
sha: { type: 'string' },
|
|
641
661
|
clean: { type: 'boolean' },
|
|
642
|
-
down: { type: 'boolean', description: 'true when Copilot is down or out of quota — it posts an out-of-usage notice or never surfaces a review on HEAD after the poll cap; the gate is bypassed and the run proceeds to
|
|
662
|
+
down: { type: 'boolean', description: 'true when Copilot is down or out of quota — it posts an out-of-usage notice or never surfaces a review on HEAD after the poll cap; the gate is bypassed and the run proceeds to the Codex gate' },
|
|
643
663
|
reviewUrl: { type: 'string', description: 'the Copilot review html_url when the gate carries findings, otherwise an empty string — the user-review payload links the orchestrator and the user to the review' },
|
|
644
664
|
findings: COPILOT_FINDINGS_SCHEMA,
|
|
645
665
|
},
|
|
646
666
|
required: ['sha', 'clean', 'down', 'findings'],
|
|
647
667
|
}
|
|
648
668
|
|
|
669
|
+
const CODEX_SKIP_REASONS = ['', 'token', 'usage']
|
|
670
|
+
|
|
671
|
+
const CODEX_FINDINGS_SCHEMA = {
|
|
672
|
+
type: 'array',
|
|
673
|
+
items: {
|
|
674
|
+
type: 'object',
|
|
675
|
+
additionalProperties: false,
|
|
676
|
+
properties: {
|
|
677
|
+
file: { type: 'string' },
|
|
678
|
+
line: { type: 'integer' },
|
|
679
|
+
severity: { type: 'string', enum: ['P0', 'P1', 'P2'] },
|
|
680
|
+
category: { type: 'string', enum: ['bug', 'code-standard'], description: 'code-standard for pure CODE_RULES/style violations with no behavioral impact; bug otherwise' },
|
|
681
|
+
title: { type: 'string' },
|
|
682
|
+
detail: { type: 'string' },
|
|
683
|
+
replyToCommentId: { type: ['integer', 'null'], description: 'GitHub review comment id to reply to and resolve, or null when the finding has no thread' },
|
|
684
|
+
},
|
|
685
|
+
required: ['file', 'line', 'severity', 'category', 'title', 'detail', 'replyToCommentId'],
|
|
686
|
+
},
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const CODEX_SCHEMA = {
|
|
690
|
+
type: 'object',
|
|
691
|
+
additionalProperties: false,
|
|
692
|
+
properties: {
|
|
693
|
+
sha: { type: 'string', description: 'PR HEAD SHA this gate evaluated' },
|
|
694
|
+
clean: { type: 'boolean', description: 'true when Codex reported no findings on sha, or when the gate skipped without requiring a review' },
|
|
695
|
+
down: { type: 'boolean', description: 'true when Codex is down (wrapper classifies codex_down) or opted out via the codex token — the gate is bypassed' },
|
|
696
|
+
skipped: { type: 'boolean', description: 'true when the gate did not run the review wrapper (opt-out token or usage at/below the shared threshold)' },
|
|
697
|
+
skipReason: {
|
|
698
|
+
type: 'string',
|
|
699
|
+
enum: CODEX_SKIP_REASONS,
|
|
700
|
+
description: 'token when CLAUDE_REVIEWS_DISABLED lists codex; usage when weekly percent_left is null or at/below the shared probe threshold; empty string when the review ran or classified down',
|
|
701
|
+
},
|
|
702
|
+
findings: CODEX_FINDINGS_SCHEMA,
|
|
703
|
+
},
|
|
704
|
+
required: ['sha', 'clean', 'down', 'skipped', 'skipReason', 'findings'],
|
|
705
|
+
}
|
|
706
|
+
|
|
649
707
|
const COPILOT_VERIFY_VERDICTS = ['confirmed', 'refuted', 'inconclusive']
|
|
650
708
|
|
|
651
709
|
const COPILOT_VERIFY_SCHEMA = {
|
|
@@ -1415,6 +1473,30 @@ function classifyReviewerGateOutcome(reviewerGate) {
|
|
|
1415
1473
|
return { kind: 'retry' }
|
|
1416
1474
|
}
|
|
1417
1475
|
|
|
1476
|
+
/**
|
|
1477
|
+
* Classify a Codex terminal-gate result into the loop's next action. Codex is a
|
|
1478
|
+
* conditional-required sibling of Bugbot and Copilot: the agent may skip without
|
|
1479
|
+
* running the review (opt-out token or weekly usage at/below the shared probe
|
|
1480
|
+
* threshold), report down when the wrapper classifies codex_down, return findings
|
|
1481
|
+
* for the fix path, or report clean so the run stamps codex_clean_at for the
|
|
1482
|
+
* convergence check. A dead agent is a retry rather than an approval.
|
|
1483
|
+
* @param {object|null|undefined} codexGate the CODEX_SCHEMA result, or null on agent failure
|
|
1484
|
+
* @returns {{kind: string, findings?: Array<object>}} the next action
|
|
1485
|
+
*/
|
|
1486
|
+
function classifyCodexGateOutcome(codexGate) {
|
|
1487
|
+
if (codexGate == null) return { kind: 'retry' }
|
|
1488
|
+
if (codexGate.skipped === true) {
|
|
1489
|
+
if (codexGate.skipReason === 'token') return { kind: 'skip-token' }
|
|
1490
|
+
if (codexGate.skipReason === 'usage') return { kind: 'skip-usage' }
|
|
1491
|
+
return { kind: 'retry' }
|
|
1492
|
+
}
|
|
1493
|
+
if (codexGate.down === true) return { kind: 'down' }
|
|
1494
|
+
const allFindings = codexGate.findings || []
|
|
1495
|
+
if (allFindings.length > 0) return { kind: 'fix', findings: allFindings }
|
|
1496
|
+
if (codexGate.clean === true) return { kind: 'clean' }
|
|
1497
|
+
return { kind: 'retry' }
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1418
1500
|
/**
|
|
1419
1501
|
* Split a Copilot round's findings by routing tier. Any tier other than the
|
|
1420
1502
|
* explicit 'self-healing' counts as a code concern, so a missing or unexpected
|
|
@@ -1938,7 +2020,7 @@ function resolveCleanAuditNote(auditResult, head, rounds) {
|
|
|
1938
2020
|
* poll cap is hit; return Copilot's findings or a down signal. Copilot is down
|
|
1939
2021
|
* when it posts an out-of-usage notice (the requester hit their quota) rather
|
|
1940
2022
|
* than a review, or surfaces no review at all after the poll cap; the gate
|
|
1941
|
-
* reports either as down so the run logs a notice and proceeds to
|
|
2023
|
+
* reports either as down so the run logs a notice and proceeds to the Codex gate with
|
|
1942
2024
|
* the gate bypassed rather than waiting on a review that will not arrive.
|
|
1943
2025
|
* @param {string} head converged PR HEAD SHA
|
|
1944
2026
|
* @returns {Promise<object>} COPILOT_SCHEMA result
|
|
@@ -1964,6 +2046,44 @@ function runCopilotGate(head) {
|
|
|
1964
2046
|
)
|
|
1965
2047
|
}
|
|
1966
2048
|
|
|
2049
|
+
/**
|
|
2050
|
+
* Codex gate: conditional-required terminal confirmation after Bugbot and
|
|
2051
|
+
* Copilot. Honors the shared opt-out token, the weekly usage probe threshold
|
|
2052
|
+
* (via is_codex_review_required — never an inline percent), and the wrapper's
|
|
2053
|
+
* codex_down classification. When required, runs the codex-review wrapper
|
|
2054
|
+
* against the PR base branch and returns findings or a clean stamp for the
|
|
2055
|
+
* convergence check.
|
|
2056
|
+
* @param {string} head converged PR HEAD SHA
|
|
2057
|
+
* @returns {Promise<object>} CODEX_SCHEMA result
|
|
2058
|
+
*/
|
|
2059
|
+
function runCodexGate(head) {
|
|
2060
|
+
return convergeReadOnlyAgent(
|
|
2061
|
+
`You are the Codex gate for ${prCoordinates}, HEAD ${head}. Do not edit code, commit, or push.\n\n` +
|
|
2062
|
+
`Codex is a conditional-required terminal gate. Follow these steps in order and return strictly the schema.\n\n` +
|
|
2063
|
+
`1. Opt-out gate. Run exactly:\n` +
|
|
2064
|
+
` python "${CONFIG.prLoopScripts}/reviews_disabled.py" --reviewer codex\n` +
|
|
2065
|
+
` Exit 0 means CLAUDE_REVIEWS_DISABLED lists codex -> return {sha:${'`'}${head}${'`'}, clean:true, down:true, skipped:true, skipReason:'token', findings:[]} and stop. Exit 1 means continue.\n\n` +
|
|
2066
|
+
`2. Usage probe. Run the probe CLI first and capture its exit code and stdout:\n` +
|
|
2067
|
+
` python "${CONFIG.codexScripts}/codex_usage_probe.py"\n` +
|
|
2068
|
+
` Non-zero exit means the Codex CLI or probe is broken — not a usage-threshold skip. Return {sha:${'`'}${head}${'`'}, clean:false, down:true, skipped:false, skipReason:'', findings:[]} and stop (same shape as codex_down). Never map a failed probe to skipReason:'usage'.\n` +
|
|
2069
|
+
` Exit 0 prints one JSON object (percent_left may be a number or null). Decide required vs skip ONLY through the shared helper — never restate a percent threshold. Feed that stdout into:\n` +
|
|
2070
|
+
` python -c "import json,sys; sys.path.insert(0, r'${CONFIG.codexScripts}'); from codex_usage_probe import is_codex_review_required; report=json.load(sys.stdin); print('required' if is_codex_review_required(report.get('percent_left')) else 'skip')"\n` +
|
|
2071
|
+
` When it prints skip -> return {sha:${'`'}${head}${'`'}, clean:true, down:false, skipped:true, skipReason:'usage', findings:[]} and stop. When it prints required -> continue.\n\n` +
|
|
2072
|
+
`3. Resolve the PR base branch (the review target is HEAD vs base, never an invented commit range). Run exactly:\n` +
|
|
2073
|
+
` gh api repos/${input.owner}/${input.repo}/pulls/${input.prNumber} --jq .base.ref\n` +
|
|
2074
|
+
` Capture the printed base branch name.\n\n` +
|
|
2075
|
+
`4. Run the codex-review wrapper against that base. From the PR worktree root, run a short Python driver that imports the skill scripts (add "${CONFIG.codexScripts}" to sys.path) and:\n` +
|
|
2076
|
+
` - creates a temp run-state directory under the OS temp dir\n` +
|
|
2077
|
+
` - calls run_codex_review.run_codex_review(repository_directory=<repo root Path>, run_state_directory=<temp Path>, base_branch=<base ref from step 3>)\n` +
|
|
2078
|
+
` - when outcome_class is codex_down (or classify_codex_run on a non-zero exit reports codex_down) -> return {sha:${'`'}${head}${'`'}, clean:false, down:true, skipped:false, skipReason:'', findings:[]} and stop\n` +
|
|
2079
|
+
` - when outcome_class is completed: parse findings with parse_codex_findings.parse_codex_findings(agent_message)\n` +
|
|
2080
|
+
` - empty findings -> return {sha:${'`'}${head}${'`'}, clean:true, down:false, skipped:false, skipReason:'', findings:[]}\n` +
|
|
2081
|
+
` - non-empty findings -> map each to {file, line (integer start of line_range, or 1 when missing), severity (priority or P2), category ('code-standard' for pure CODE_RULES/style with no behavioral impact, 'bug' otherwise), title, detail (body), replyToCommentId:null} and return {sha:${'`'}${head}${'`'}, clean:false, down:false, skipped:false, skipReason:'', findings:[...]}\n\n` +
|
|
2082
|
+
`Never hard-code a usage percent threshold. Never re-parse CLAUDE_REVIEWS_DISABLED by hand — only reviews_disabled.py decides the token. Return strictly the schema.`,
|
|
2083
|
+
{ label: 'codex-gate', phase: 'Codex gate', schema: CODEX_SCHEMA, agentType: 'general-purpose', ...TIERS.haikuLow },
|
|
2084
|
+
)
|
|
2085
|
+
}
|
|
2086
|
+
|
|
1967
2087
|
/**
|
|
1968
2088
|
* Spawn the verifier agent for one Copilot code-concern finding. The verifier
|
|
1969
2089
|
* decides confirmed / refuted / inconclusive by executing a check against the
|
|
@@ -2413,6 +2533,8 @@ const noLensRoundCauses = new Set()
|
|
|
2413
2533
|
let blocker = null
|
|
2414
2534
|
let bugbotDown = input.bugbotDisabled || false
|
|
2415
2535
|
let copilotDown = input.copilotDisabled || false
|
|
2536
|
+
let codexDown = false
|
|
2537
|
+
let codexCleanAt = null
|
|
2416
2538
|
let copilotNote = null
|
|
2417
2539
|
let standardsNote = null
|
|
2418
2540
|
let cleanAuditNote = null
|
|
@@ -2617,9 +2739,9 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2617
2739
|
if (phase === 'COPILOT') {
|
|
2618
2740
|
if (resolveReviewerDown(reviewerAvailability?.copilot, input.copilotDisabled || false)) {
|
|
2619
2741
|
copilotDown = true
|
|
2620
|
-
copilotNote = 'Copilot was unavailable or out of premium-request quota this round — the Copilot gate was bypassed with no agent spawned
|
|
2621
|
-
log('Copilot gate: the shared reviewer-availability probe (or the run input) reported Copilot unavailable — skipping the Copilot gate with no agent spawned and proceeding to
|
|
2622
|
-
phase = '
|
|
2742
|
+
copilotNote = 'Copilot was unavailable or out of premium-request quota this round — the Copilot gate was bypassed with no agent spawned; remaining gates including Codex still run before ready'
|
|
2743
|
+
log('Copilot gate: the shared reviewer-availability probe (or the run input) reported Copilot unavailable — skipping the Copilot gate with no agent spawned and proceeding to the Codex gate with the Copilot gate bypassed.')
|
|
2744
|
+
phase = 'CODEX'
|
|
2623
2745
|
continue
|
|
2624
2746
|
}
|
|
2625
2747
|
const copilot = await runCopilotGate(head)
|
|
@@ -2631,10 +2753,10 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2631
2753
|
continue
|
|
2632
2754
|
}
|
|
2633
2755
|
if (copilotOutcome.kind === 'down') {
|
|
2634
|
-
log('Copilot gate: Copilot is down or out of quota — no review on HEAD after the poll cap. Logging a notice and proceeding to
|
|
2756
|
+
log('Copilot gate: Copilot is down or out of quota — no review on HEAD after the poll cap. Logging a notice and proceeding to the Codex gate with the Copilot gate bypassed.')
|
|
2635
2757
|
copilotDown = true
|
|
2636
|
-
copilotNote = 'Copilot was down or out of quota — the Copilot gate was bypassed
|
|
2637
|
-
phase = '
|
|
2758
|
+
copilotNote = 'Copilot was down or out of quota — the Copilot gate was bypassed; remaining gates including Codex still run before ready'
|
|
2759
|
+
phase = 'CODEX'
|
|
2638
2760
|
continue
|
|
2639
2761
|
}
|
|
2640
2762
|
if (copilotOutcome.kind === 'fix') {
|
|
@@ -2662,7 +2784,7 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2662
2784
|
log('Every code-concern finding was refuted with executed-check evidence and the round carries no self-healing findings — the Copilot gate passes')
|
|
2663
2785
|
copilotDown = false
|
|
2664
2786
|
copilotNote = null
|
|
2665
|
-
phase = '
|
|
2787
|
+
phase = 'CODEX'
|
|
2666
2788
|
continue
|
|
2667
2789
|
}
|
|
2668
2790
|
}
|
|
@@ -2673,7 +2795,7 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2673
2795
|
if (standardsOutcome?.deferredPr) deferredPrs.push(standardsOutcome.deferredPr)
|
|
2674
2796
|
copilotDown = false
|
|
2675
2797
|
copilotNote = null
|
|
2676
|
-
phase = '
|
|
2798
|
+
phase = 'CODEX'
|
|
2677
2799
|
continue
|
|
2678
2800
|
}
|
|
2679
2801
|
log(`Copilot raised ${roundFindings.length} finding(s) — fixing and re-converging`)
|
|
@@ -2692,12 +2814,74 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2692
2814
|
}
|
|
2693
2815
|
copilotDown = false
|
|
2694
2816
|
copilotNote = null
|
|
2817
|
+
phase = 'CODEX'
|
|
2818
|
+
continue
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
if (phase === 'CODEX') {
|
|
2822
|
+
const codex = await runCodexGate(head)
|
|
2823
|
+
const codexOutcome = classifyCodexGateOutcome(codex)
|
|
2824
|
+
if (codexOutcome.kind === 'retry') {
|
|
2825
|
+
log('Codex gate agent died or returned an unreliable result — re-running the gate on the same HEAD')
|
|
2826
|
+
continue
|
|
2827
|
+
}
|
|
2828
|
+
if (codexOutcome.kind === 'skip-token') {
|
|
2829
|
+
log('Codex gate: CLAUDE_REVIEWS_DISABLED lists codex — skipping the terminal Codex gate with no review spawned and proceeding to mark-ready with the gate bypassed.')
|
|
2830
|
+
codexDown = true
|
|
2831
|
+
codexCleanAt = null
|
|
2832
|
+
phase = 'FINALIZE'
|
|
2833
|
+
continue
|
|
2834
|
+
}
|
|
2835
|
+
if (codexOutcome.kind === 'skip-usage') {
|
|
2836
|
+
log('Codex gate: weekly usage is at/below the shared probe threshold (or null) — skipping the terminal Codex gate; the convergence check applies the same rule.')
|
|
2837
|
+
codexDown = false
|
|
2838
|
+
codexCleanAt = null
|
|
2839
|
+
phase = 'FINALIZE'
|
|
2840
|
+
continue
|
|
2841
|
+
}
|
|
2842
|
+
if (codexOutcome.kind === 'down') {
|
|
2843
|
+
log('Codex gate: Codex classified codex_down — bypassing the terminal Codex gate and proceeding to mark-ready with the gate bypassed.')
|
|
2844
|
+
codexDown = true
|
|
2845
|
+
codexCleanAt = null
|
|
2846
|
+
phase = 'FINALIZE'
|
|
2847
|
+
continue
|
|
2848
|
+
}
|
|
2849
|
+
if (codexOutcome.kind === 'fix') {
|
|
2850
|
+
if (isStandardsOnlyRound(codexOutcome.findings)) {
|
|
2851
|
+
log(`Codex raised ${codexOutcome.findings.length} code-standard-only finding(s) — deferring to follow-up PRs and treating the gate as passed`)
|
|
2852
|
+
const standardsOutcome = await openStandardsFollowUpOnce(head, codexOutcome.findings, 'codex', { copilotDisabled: copilotDown, bugbotDisabled: bugbotDown })
|
|
2853
|
+
standardsNote = standardsDeferralNote(codexOutcome.findings.length, buildStandardsDeferral())
|
|
2854
|
+
if (standardsOutcome?.deferredPr) deferredPrs.push(standardsOutcome.deferredPr)
|
|
2855
|
+
codexDown = false
|
|
2856
|
+
codexCleanAt = head
|
|
2857
|
+
phase = 'FINALIZE'
|
|
2858
|
+
continue
|
|
2859
|
+
}
|
|
2860
|
+
log(`Codex raised ${codexOutcome.findings.length} finding(s) — fixing and re-converging`)
|
|
2861
|
+
const fixResult = await applyFixes(head, codexOutcome.findings, 'codex')
|
|
2862
|
+
const hadThreadBearingFinding = codexOutcome.findings.some((each) => collectFindingThreadIds(each).length > 0)
|
|
2863
|
+
const fixProgress = detectFixProgress(fixResult, head, hadThreadBearingFinding)
|
|
2864
|
+
if (!fixProgress.progressed) {
|
|
2865
|
+
blocker = fixResult?.resolvedWithoutCommit === true && !hadThreadBearingFinding
|
|
2866
|
+
? `fix stalled: codex gate raised ${codexOutcome.findings.length} in-memory finding(s) with no GitHub thread, the fix judged them all stale (resolvedWithoutCommit) and moved no code on HEAD ${head} — re-raising would loop to the iteration cap`
|
|
2867
|
+
: `codex fix lens landed no push for ${codexOutcome.findings.length} finding(s) on HEAD ${head}`
|
|
2868
|
+
break
|
|
2869
|
+
}
|
|
2870
|
+
head = null
|
|
2871
|
+
codexDown = false
|
|
2872
|
+
codexCleanAt = null
|
|
2873
|
+
phase = 'CONVERGE'
|
|
2874
|
+
continue
|
|
2875
|
+
}
|
|
2876
|
+
log(`Codex gate: clean on ${head?.slice(0, 7)} — recording codex-clean HEAD for the convergence check`)
|
|
2877
|
+
codexDown = false
|
|
2878
|
+
codexCleanAt = head
|
|
2695
2879
|
phase = 'FINALIZE'
|
|
2696
2880
|
continue
|
|
2697
2881
|
}
|
|
2698
2882
|
|
|
2699
2883
|
if (phase === 'FINALIZE') {
|
|
2700
|
-
const finalizeResult = await runConvergenceCheck({ head, bugbotDown, copilotDown, bugteamPostBlocked: cleanAuditNote !== null })
|
|
2884
|
+
const finalizeResult = await runConvergenceCheck({ head, bugbotDown, copilotDown, codexDown, codexCleanAt, bugteamPostBlocked: cleanAuditNote !== null })
|
|
2701
2885
|
const convergenceOutcome = classifyConvergenceOutcome(finalizeResult)
|
|
2702
2886
|
if (convergenceOutcome.kind === 'retry') {
|
|
2703
2887
|
log('Convergence check agent died or returned no FAIL lines — re-running the check on the same HEAD')
|
package/skills/bugteam/CLAUDE.md
CHANGED
|
@@ -4,14 +4,14 @@ Runs an audit-fix loop on an open pull request until all findings are resolved o
|
|
|
4
4
|
|
|
5
5
|
## Purpose
|
|
6
6
|
|
|
7
|
-
Each loop: a `code-quality-agent` (fresh context, all A–
|
|
7
|
+
Each loop: a `code-quality-agent` (fresh context, all A–Q audit categories) produces an outcome XML; a `clean-coder` agent applies every fix; the lead commits, pushes, and posts a GitHub PR review (APPROVE on clean, REQUEST_CHANGES with inline anchored comments on dirty). Grants `.claude/**` write permissions at the start and revokes them at the end.
|
|
8
8
|
|
|
9
9
|
## Key files
|
|
10
10
|
|
|
11
11
|
| File | Purpose |
|
|
12
12
|
|---|---|
|
|
13
13
|
| `SKILL.md` | Hub — pre-flight call, refusals, the audit-posting step (runs the shared `post_audit_thread.py` helper), progress checklist, and situation-to-reference table. Read this first. |
|
|
14
|
-
| `PROMPTS.md` | Spawn XML, A–
|
|
14
|
+
| `PROMPTS.md` | Spawn XML, A–Q category bindings, outcome XML schemas. |
|
|
15
15
|
| `CONSTRAINTS.md` | Invariants — what the loop must never violate. |
|
|
16
16
|
| `EXAMPLES.md` | Exit scenarios: converged, cap-reached, stuck, refusal, mixed-outcome. |
|
|
17
17
|
| `sources.md` | Doc URLs and verbatim quotes cited in the skill body. |
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Constraints
|
|
4
4
|
|
|
5
|
-
- **Full A–
|
|
5
|
+
- **Full A–Q audit every loop, no exceptions.** PR size, "focused audit," "team overhead," "CODE_RULES already passed" — not valid reasons. Empty `<findings/>` for any category is a valid result. The audit agent walks all A–Q rubrics each loop.
|
|
6
6
|
- **One run per invocation, multi-PR supported.** All PRs in a single /bugteam invocation share one `run_temp_dir`. Per-PR identity lives in the subagent name prefix (`bugfind-pr<N>-loop<L>` / `bugfix-pr<N>-loop<L>`) and the `<run_temp_dir>/pr-<N>/` subfolder containing that PR's git worktree, diff patches, and outcome XML files.
|
|
7
7
|
- **Grant before any spawn, revoke before any return.** Step 0 grants project `.claude/**` permissions; Step 4 (`pr-loop-lifecycle` Close) revokes. Both are mandatory. Revoke runs on every exit path including error, cap-reached, and stuck.
|
|
8
|
-
- **Fresh
|
|
8
|
+
- **Fresh worker per loop.** Both bugfind and bugfix route through the worker-spawn dispatcher (or a fresh Agent-tool context on tier 2). Reusing a worker across loops accumulates context — defeats clean-room.
|
|
9
|
+
|
|
9
10
|
- **One up-front confirmation = whole cycle.** The `/bugteam` invocation authorizes the entire cycle; every subsequent decision runs on that single authorization.
|
|
10
11
|
- **20-loop hard cap.** Counted as **AUDIT** completions (increment in Step 3). Standards-fix passes before an audit do not advance `loop_count`. Worst case includes extra clean-coder spawns for the code-rules gate.
|
|
11
12
|
- **Code rules gate before every AUDIT.** Run `python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/code_rules_gate.py" --base origin/<baseRefName>` until exit **0** before spawning **bugfind**. Same `validate_content` logic as `hooks/blocking/code_rules_enforcer.py`.
|
|
@@ -24,7 +24,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
24
24
|
</scope>
|
|
25
25
|
|
|
26
26
|
<bug_categories>
|
|
27
|
-
Investigate each of the
|
|
27
|
+
Investigate each of the seventeen categories (A–Q) explicitly. For each,
|
|
28
28
|
return either at least one finding OR a verified-clean entry with the
|
|
29
29
|
evidence backing the verdict. A category is verified-clean only when one
|
|
30
30
|
complete execution path through the changed code has been traced from
|
|
@@ -37,7 +37,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
37
37
|
When evidence contains any of these phrases, the category is not
|
|
38
38
|
verified-clean -- re-audit with a concrete trace.
|
|
39
39
|
|
|
40
|
-
Categories A–
|
|
40
|
+
Categories A–Q (one-line summary; full rubric and sub-bucket
|
|
41
41
|
decomposition for each is in
|
|
42
42
|
`$HOME/.claude/audit-rubrics/category_rubrics/`; ready-to-send Variant
|
|
43
43
|
C prompts — each with a PR/repo-independent generalized skeleton above
|
|
@@ -60,6 +60,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
60
60
|
N. Test-name scenario verifier
|
|
61
61
|
O. Docstring / fixture-prose vs implementation drift
|
|
62
62
|
P. Name / regex / word-list vs behavior-contract precision
|
|
63
|
+
Q. Cross-surface claim consistency (terminology, PR-description claims, message-vs-guard)
|
|
63
64
|
</bug_categories>
|
|
64
65
|
|
|
65
66
|
<rubric_reference>
|
|
@@ -79,7 +80,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
79
80
|
</constraints>
|
|
80
81
|
|
|
81
82
|
<comment_posting>
|
|
82
|
-
Load all A–
|
|
83
|
+
Load all A–Q rubrics from
|
|
83
84
|
`$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/`. The prompt file
|
|
84
85
|
is a template for output shape, not a straitjacket — reorganize when the
|
|
85
86
|
diff demands it. The diff supplies the findings; the rubric supplies the
|
|
@@ -90,7 +91,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
90
91
|
done.
|
|
91
92
|
|
|
92
93
|
<self_audit_checklist>
|
|
93
|
-
[ ] Walk all
|
|
94
|
+
[ ] Walk all 17 categories (A–Q), each with Shape A or Shape B
|
|
94
95
|
[ ] Assign finding IDs (loop<L>-<K>)
|
|
95
96
|
[ ] Capture excerpts, validate anchors, format finding bodies
|
|
96
97
|
[ ] Build findings JSON, invoke post_audit_thread.py, capture html_url
|
|
@@ -98,7 +99,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
98
99
|
[ ] Write outcome XML
|
|
99
100
|
</self_audit_checklist>
|
|
100
101
|
|
|
101
|
-
1. Audit the diff against the
|
|
102
|
+
1. Audit the diff against the 17 categories above. Buffer the findings
|
|
102
103
|
in memory; all posting happens at step 4 once anchors are validated.
|
|
103
104
|
2. Assign each finding a stable finding_id of exactly the form `loop<L>-<K>`
|
|
104
105
|
where <K> is 1-based within this loop.
|
|
@@ -218,7 +219,7 @@ attributes.
|
|
|
218
219
|
</bugteam_audit>
|
|
219
220
|
```
|
|
220
221
|
|
|
221
|
-
Verified-clean evidence per A–
|
|
222
|
+
Verified-clean evidence per A–Q category is surfaced in the agent's text-mode
|
|
222
223
|
final report, not in this outcome XML (the writer accepts a flat findings list
|
|
223
224
|
only).
|
|
224
225
|
|
package/skills/bugteam/SKILL.md
CHANGED
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
name: bugteam
|
|
3
3
|
description: >-
|
|
4
4
|
Open pull request audit–fix until convergence: CODE_RULES gate, clean-room
|
|
5
|
-
audit (`code-quality-agent`, opus) and fix (`clean-coder
|
|
6
|
-
GitHub reviews, 20-audit cap; grant then revoke `.claude/**`.
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
audit (`code-quality-agent`, opus) and fix (`clean-coder`, opus), per-loop
|
|
6
|
+
GitHub reviews, 20-audit cap; grant then revoke `.claude/**`. AUDIT, FIX, and
|
|
7
|
+
standards-fix route through `resolve_worker_spawn.py` (headless tiers or
|
|
8
|
+
Agent-tool fallback). Triggers: '/bugteam', 'run the bug team', 'auto-fix
|
|
9
|
+
the PR until clean', 'loop audit and fix'.
|
|
9
10
|
---
|
|
10
11
|
|
|
11
12
|
# Bugteam
|
|
12
13
|
|
|
13
|
-
Audit–fix until convergence. Bugfind: `code-quality-agent`, fresh
|
|
14
|
-
loop, auditing all A–
|
|
14
|
+
Audit–fix until convergence. Bugfind: `code-quality-agent`, fresh process or
|
|
15
|
+
fresh agent context each loop, auditing all A–Q categories via the
|
|
16
|
+
worker-spawn dispatcher. Bugfix and standards-fix: `clean-coder` via the same
|
|
17
|
+
dispatcher (role `clean-coder`; tier 2 pins model opus). Hard cap: 20 audit
|
|
15
18
|
loops. Grant `.claude/**` at start, revoke always at end.
|
|
16
19
|
|
|
17
|
-
The audit agent loads the A–
|
|
20
|
+
The audit agent loads the A–Q category rubrics from
|
|
18
21
|
`$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` alongside
|
|
19
22
|
[`PROMPTS.md`](PROMPTS.md) and produces a single outcome XML per loop.
|
|
20
23
|
|
|
@@ -49,9 +52,11 @@ First match wins; respond with the quoted line exactly and stop:
|
|
|
49
52
|
- **No PR or upstream diff.** `No PR or upstream diff. /bugteam needs a target.`
|
|
50
53
|
- **Dirty tree.** `Uncommitted changes detected. Stash, commit, or revert before
|
|
51
54
|
/bugteam.`
|
|
52
|
-
- **Missing subagents.** Before
|
|
53
|
-
`clean-coder` exist. Else: `Required
|
|
54
|
-
/bugteam needs both code-quality-agent
|
|
55
|
+
- **Missing subagents (tier 2 only).** Before a tier-2 Agent-tool spawn,
|
|
56
|
+
confirm `code-quality-agent` and `clean-coder` exist. Else: `Required
|
|
57
|
+
subagent type <name> not installed. /bugteam needs both code-quality-agent
|
|
58
|
+
and clean-coder available.` Headless tiers rely on the preflight's
|
|
59
|
+
agent-definition-file check instead.
|
|
55
60
|
|
|
56
61
|
## Audit posting
|
|
57
62
|
|
|
@@ -106,7 +111,7 @@ end-to-end mental model before starting Step 0.
|
|
|
106
111
|
| Posting the end-of-pass audit review (APPROVE on CLEAN, REQUEST_CHANGES with inline anchored comments on DIRTY) | [§ Audit posting](#audit-posting), which runs [`_shared/pr-loop/scripts/post_audit_thread.py`](../../_shared/pr-loop/scripts/post_audit_thread.py) |
|
|
107
112
|
| Posting per-finding fix replies via GitHub MCP `add_reply_to_pull_request_comment` (rendered with the unified template at [`_shared/pr-loop/audit-reply-template.md`](../../_shared/pr-loop/audit-reply-template.md)) | [reference/github-pr-reviews.md](reference/github-pr-reviews.md) |
|
|
108
113
|
| Teardown, PR description rewrite composed by the lead, permission revoke, final report | [../pr-loop-lifecycle/reference/teardown-publish-permissions.md](../pr-loop-lifecycle/reference/teardown-publish-permissions.md) |
|
|
109
|
-
| Spawn-prompt XML, A–
|
|
114
|
+
| Spawn-prompt XML, A–Q category bindings, outcome XML schemas | [PROMPTS.md](PROMPTS.md) |
|
|
110
115
|
| Per-category audit content (sub-buckets, decision criteria, ready-to-send Variant C templates) | `$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` |
|
|
111
116
|
| Invariants and design rationale | [CONSTRAINTS.md](CONSTRAINTS.md), [reference/design-rationale.md](reference/design-rationale.md) |
|
|
112
117
|
| Audit-contract finding shape (Shape A / B), Haiku secondary, post-fix self-audit | [../../_shared/pr-loop/audit-contract.md](../../_shared/pr-loop/audit-contract.md) |
|
|
@@ -119,11 +124,11 @@ end-to-end mental model before starting Step 0.
|
|
|
119
124
|
- `SKILL.md` — this hub.
|
|
120
125
|
- `reference/` — workflow detail per situation.
|
|
121
126
|
- `scripts/` — utility scripts executed, not loaded as primary context.
|
|
122
|
-
- `PROMPTS.md` — spawn XML, A–
|
|
127
|
+
- `PROMPTS.md` — spawn XML, A–Q category bindings, outcome schemas.
|
|
123
128
|
- `CONSTRAINTS.md` — invariants.
|
|
124
129
|
- `EXAMPLES.md` — exit scenarios.
|
|
125
130
|
- `sources.md` — doc URLs and verbatim quotes.
|
|
126
131
|
- `~/.claude/audit-rubrics/` — installed by `npx claude-dev-env` from
|
|
127
|
-
`packages/claude-dev-env/audit-rubrics/`; the audit agent reads all A–
|
|
132
|
+
`packages/claude-dev-env/audit-rubrics/`; the audit agent reads all A–Q
|
|
128
133
|
rubrics under `category_rubrics/` and prompts under `prompts/`. Required
|
|
129
134
|
at audit time alongside `PROMPTS.md`.
|