claude-dev-env 1.93.1 → 1.95.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/_shared/advisor/CLAUDE.md +2 -2
- package/_shared/advisor/advisor-protocol.md +35 -27
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +3 -2
- package/_shared/advisor/scripts/model_tier_run_validator.py +23 -15
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +81 -17
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/audit-contract.md +1 -1
- package/_shared/pr-loop/gh-payloads.md +3 -3
- package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
- package/_shared/pr-loop/scripts/README.md +2 -2
- package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -69
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
- package/_shared/pr-loop/state-schema.md +3 -22
- package/bin/CLAUDE.md +11 -2
- package/bin/ever-shipped-skills.mjs +70 -0
- package/bin/install.mjs +138 -13
- package/bin/install.prune.test.mjs +457 -0
- package/bin/install.test.mjs +10 -4
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/code_rules_enforcer.py +4 -0
- package/hooks/blocking/code_rules_shared.py +82 -0
- package/hooks/blocking/code_rules_test_layout.py +9 -3
- package/hooks/blocking/pii_commit_command.py +398 -0
- package/hooks/blocking/pii_payload_scan.py +133 -0
- package/hooks/blocking/pii_prevention_blocker.py +151 -449
- package/hooks/blocking/plain_language_blocker.py +138 -4
- package/hooks/blocking/sensitive_file_protector.py +114 -48
- package/hooks/blocking/tdd_enforcer.py +9 -2
- package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_code_rules_shared.py +181 -0
- package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
- package/hooks/blocking/test_plain_language_blocker_allowlist.py +184 -0
- package/hooks/blocking/test_sensitive_file_protector.py +185 -0
- package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
- package/hooks/hooks_constants/CLAUDE.md +4 -2
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
- package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
- package/hooks/hooks_constants/local_identity.py +65 -15
- package/hooks/hooks_constants/pii_prevention_constants.py +16 -0
- package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
- package/hooks/hooks_constants/test_local_identity.py +68 -0
- package/hooks/pyproject.toml +75 -4
- package/hooks/validators/CLAUDE.md +1 -1
- package/hooks/validators/README.md +2 -0
- package/hooks/validators/python_style_checks.py +114 -136
- package/hooks/validators/python_style_helpers.py +95 -0
- package/hooks/validators/test_python_style_checks.py +0 -164
- package/hooks/validators/test_python_style_checks_decorator_gap.py +119 -0
- package/hooks/validators/test_python_style_fixes.py +251 -0
- package/hooks/validators/test_python_style_helpers.py +125 -0
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/anti-corollary-tests.md +69 -0
- package/rules/bdd.md +1 -3
- package/rules/code-reviews.md +1 -1
- package/rules/gh-paginate.md +1 -1
- package/rules/plain-language.md +2 -0
- package/scripts/CLAUDE.md +10 -1
- package/scripts/Get-SessionAccount.ps1 +210 -0
- package/scripts/tests/CLAUDE.md +15 -1
- package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
- package/skills/CLAUDE.md +6 -27
- package/skills/anthropic-plan/SKILL.md +1 -13
- package/skills/autoconverge/CLAUDE.md +5 -2
- package/skills/autoconverge/SKILL.md +112 -387
- package/skills/autoconverge/reference/CLAUDE.md +4 -1
- package/skills/autoconverge/reference/closing-report.md +9 -2
- package/skills/autoconverge/reference/convergence.md +26 -22
- package/skills/autoconverge/reference/headless-safety.md +44 -0
- package/skills/autoconverge/reference/multi-pr.md +88 -0
- package/skills/autoconverge/reference/self-closing-loop.md +84 -0
- package/skills/autoconverge/reference/stop-conditions.md +21 -13
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
- package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +286 -96
- package/skills/bugteam/CLAUDE.md +1 -1
- package/skills/bugteam/CONSTRAINTS.md +3 -3
- package/skills/bugteam/PROMPTS.md +8 -19
- package/skills/bugteam/SKILL.md +13 -14
- package/skills/bugteam/reference/CLAUDE.md +1 -1
- package/skills/bugteam/reference/README.md +2 -3
- package/skills/bugteam/reference/audit-and-teammates.md +2 -2
- package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
- package/skills/bugteam/reference/github-pr-reviews.md +56 -38
- package/skills/bugteam/reference/team-setup.md +11 -11
- package/skills/bugteam/scripts/CLAUDE.md +6 -6
- package/skills/bugteam/scripts/README.md +20 -8
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
- package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
- package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
- package/skills/closeout/SKILL.md +153 -0
- package/skills/closeout/reference/handoff-prompt-template.md +72 -0
- package/skills/closeout/reference/issue-body-templates.md +108 -0
- package/skills/closeout/reference/pii-redaction-checklist.md +36 -0
- package/skills/copilot-finding-triage/SKILL.md +80 -28
- package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
- package/skills/copilot-finding-triage/templates/notification.md +8 -3
- package/skills/everything-search/SKILL.md +1 -2
- package/skills/grokify/SKILL.md +58 -0
- package/skills/grokify/templates/handoff-template.md +92 -0
- package/skills/orchestrator/SKILL.md +35 -25
- package/skills/orchestrator-refresh/SKILL.md +15 -11
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +132 -233
- package/skills/pr-converge/reference/convergence-gates.md +102 -66
- package/skills/pr-converge/reference/examples.md +37 -25
- package/skills/pr-converge/reference/fix-protocol.md +7 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
- package/skills/pr-converge/reference/per-tick.md +64 -41
- package/skills/pr-converge/reference/state-schema.md +27 -2
- package/skills/pr-converge/scripts/CLAUDE.md +8 -0
- package/skills/pr-converge/scripts/README.md +4 -3
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
- package/skills/pr-converge/scripts/check_convergence.py +189 -645
- package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
- package/skills/pr-converge/scripts/conftest.py +8 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
- package/skills/pr-fix-protocol/SKILL.md +20 -20
- package/skills/privacy-hygiene/SKILL.md +4 -3
- package/skills/reviewer-gates/SKILL.md +26 -8
- package/skills/session-log/SKILL.md +11 -12
- package/skills/session-tidy/SKILL.md +40 -30
- package/skills/skill-builder/CLAUDE.md +13 -10
- package/skills/skill-builder/SKILL.md +31 -31
- package/skills/skill-builder/references/CLAUDE.md +10 -6
- package/skills/skill-builder/references/delegation-map.md +13 -4
- package/skills/skill-builder/references/description-field.md +113 -0
- package/skills/skill-builder/references/self-audit-checklist.md +21 -4
- package/skills/skill-builder/references/skill-modularity.md +113 -0
- package/skills/skill-builder/references/skill-types.md +4 -0
- package/skills/skill-builder/templates/CLAUDE.md +4 -4
- package/skills/skill-builder/templates/gap-analysis.md +25 -1
- package/skills/skill-builder/workflows/CLAUDE.md +6 -4
- package/skills/skill-builder/workflows/improve-skill.md +17 -16
- package/skills/skill-builder/workflows/new-skill.md +34 -10
- package/skills/skill-builder/workflows/polish-skill.md +59 -43
- package/skills/team-advisor/SKILL.md +15 -11
- package/skills/usage-pause/SKILL.md +10 -14
- package/system-prompts/software-engineer.xml +7 -6
- package/hooks/validators/test_verify_paths.py +0 -32
- package/hooks/validators/verify_paths.py +0 -57
- package/skills/bdd-protocol/CLAUDE.md +0 -26
- package/skills/bdd-protocol/SKILL.md +0 -30
- package/skills/bdd-protocol/references/CLAUDE.md +0 -10
- package/skills/bdd-protocol/references/anti-patterns.md +0 -26
- package/skills/bdd-protocol/references/example-mapping.md +0 -23
- package/skills/bg-agent/CLAUDE.md +0 -17
- package/skills/bg-agent/SKILL.md +0 -69
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
- package/skills/caveman/CLAUDE.md +0 -15
- package/skills/caveman/SKILL.md +0 -38
- package/skills/code/CLAUDE.md +0 -17
- package/skills/code/SKILL.md +0 -178
- package/skills/deep-research/CLAUDE.md +0 -17
- package/skills/deep-research/SKILL.md +0 -80
- package/skills/findbugs/CLAUDE.md +0 -20
- package/skills/findbugs/SKILL.md +0 -241
- package/skills/fixbugs/CLAUDE.md +0 -19
- package/skills/fixbugs/SKILL.md +0 -142
- package/skills/gh-paginate/CLAUDE.md +0 -18
- package/skills/gh-paginate/SKILL.md +0 -84
- package/skills/gotcha/CLAUDE.md +0 -33
- package/skills/gotcha/SKILL.md +0 -73
- package/skills/implement/CLAUDE.md +0 -27
- package/skills/implement/SKILL.md +0 -66
- package/skills/implement/scripts/CLAUDE.md +0 -22
- package/skills/implement/scripts/append_note.py +0 -133
- package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
- package/skills/implement/scripts/test_append_note.py +0 -191
- package/skills/log-audit/CLAUDE.md +0 -20
- package/skills/log-audit/SKILL.md +0 -66
- package/skills/log-audit/reference/CLAUDE.md +0 -9
- package/skills/log-audit/reference/charter.md +0 -52
- package/skills/log-audit/scripts/CLAUDE.md +0 -27
- package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
- package/skills/log-audit/scripts/collect_log_window.py +0 -199
- package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
- package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
- package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
- package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
- package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
- package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
- package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
- package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
- package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
- package/skills/logifix/CLAUDE.md +0 -36
- package/skills/logifix/SKILL.md +0 -69
- package/skills/logifix/scripts/CLAUDE.md +0 -16
- package/skills/logifix/scripts/logifix.ps1 +0 -205
- package/skills/monitor-open-prs/CLAUDE.md +0 -34
- package/skills/monitor-open-prs/SKILL.md +0 -88
- package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
- package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
- package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
- package/skills/monitor-open-prs/test_skill_contract.py +0 -27
- package/skills/post-audit-findings/SKILL.md +0 -80
- package/skills/pr-consistency-audit/CLAUDE.md +0 -34
- package/skills/pr-consistency-audit/SKILL.md +0 -116
- package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
- package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
- package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
- package/skills/pr-review-responder/CLAUDE.md +0 -35
- package/skills/pr-review-responder/EXAMPLES.md +0 -590
- package/skills/pr-review-responder/PRINCIPLES.md +0 -533
- package/skills/pr-review-responder/README.md +0 -168
- package/skills/pr-review-responder/SKILL.md +0 -203
- package/skills/pr-review-responder/TESTING.md +0 -350
- package/skills/pr-scope-resolve/SKILL.md +0 -45
- package/skills/pre-compact/CLAUDE.md +0 -24
- package/skills/pre-compact/SKILL.md +0 -134
- package/skills/qbug/CLAUDE.md +0 -40
- package/skills/qbug/SKILL.md +0 -387
- package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
- package/skills/refine/CLAUDE.md +0 -44
- package/skills/refine/SKILL.md +0 -262
- package/skills/refine/templates/CLAUDE.md +0 -17
- package/skills/refine/templates/implementation-notes-template.html +0 -56
- package/skills/refine/templates/plan-template.md +0 -60
- package/skills/research-mode/CLAUDE.md +0 -35
- package/skills/research-mode/SKILL.md +0 -53
- package/skills/structure-prompt/CLAUDE.md +0 -42
- package/skills/structure-prompt/SKILL.md +0 -44
- package/skills/structure-prompt/reference/CLAUDE.md +0 -28
- package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
- package/skills/structure-prompt/reference/block-classification.md +0 -27
- package/skills/structure-prompt/reference/canonical-case.md +0 -48
- package/skills/structure-prompt/reference/citation-depth.md +0 -70
- package/skills/structure-prompt/reference/cleanup.md +0 -33
- package/skills/structure-prompt/reference/constraints.md +0 -33
- package/skills/structure-prompt/reference/directives.md +0 -37
- package/skills/structure-prompt/reference/examples.md +0 -72
- package/skills/structure-prompt/reference/instantiation.md +0 -51
- package/skills/structure-prompt/reference/output-contract.md +0 -72
- package/skills/structure-prompt/reference/per-category.md +0 -23
- package/skills/structure-prompt/reference/persona.md +0 -38
- package/skills/structure-prompt/reference/research.md +0 -33
- package/skills/structure-prompt/reference/structure.md +0 -28
- package/skills/verified-build/CLAUDE.md +0 -33
- package/skills/verified-build/SKILL.md +0 -33
|
@@ -17,7 +17,7 @@ export const meta = {
|
|
|
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
|
|
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 mark the PR ready with the gate bypassed' },
|
|
21
21
|
{ title: 'Finalize', detail: 'Run check_convergence.py; mark draft=false on a full pass' },
|
|
22
22
|
],
|
|
23
23
|
}
|
|
@@ -481,7 +481,9 @@ function serializeOneLineJson(valueToSerialize) {
|
|
|
481
481
|
/**
|
|
482
482
|
* Spawn a fresh general-utility general-purpose agent for its administrative task:
|
|
483
483
|
* 'post-clean-audit' posts the terminal CLEAN bugteam review. The agent edits no
|
|
484
|
-
* code.
|
|
484
|
+
* code. The post_audit_thread.py invocation and exit-code contract this prompt
|
|
485
|
+
* drives is owned by _shared/pr-loop/post-audit-thread-contract.md; the clean-audit
|
|
486
|
+
* bypass policy for a refused post is in reference/stop-conditions.md.
|
|
485
487
|
* @param {'post-clean-audit'} task the short task name
|
|
486
488
|
* @param {object} context task-specific context
|
|
487
489
|
* @returns {Promise<object>} the task result
|
|
@@ -537,24 +539,33 @@ function runGeneralUtilityTask(task, context) {
|
|
|
537
539
|
* the FINALIZE loop routes to the repair path, which loops back and re-runs this
|
|
538
540
|
* same combined check; only the passing path ever attempts gh pr ready. The agent
|
|
539
541
|
* edits no code, so it runs on the cheapest model at low effort.
|
|
540
|
-
* @param {object} context carries head, bugbotDown, and
|
|
542
|
+
* @param {object} context carries head, bugbotDown, copilotDown, and bugteamPostBlocked
|
|
541
543
|
* @returns {Promise<object>} FINALIZE_SCHEMA result
|
|
542
544
|
*/
|
|
543
545
|
function runConvergenceCheck(context) {
|
|
544
546
|
const label = 'finalize-check'
|
|
545
547
|
const bugbotDownFlag = context.bugbotDown ? ' --bugbot-down' : ''
|
|
546
548
|
const copilotDownFlag = context.copilotDown ? ' --copilot-down' : ''
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
+
const bugteamPostBlockedFlag = context.bugteamPostBlocked ? ' --bugteam-post-blocked' : ''
|
|
550
|
+
const bugteamPostBlockedNote = context.bugteamPostBlocked
|
|
551
|
+
? ` 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`
|
|
552
|
+
: ''
|
|
553
|
+
const reviewerOptOutTokens = []
|
|
554
|
+
if (context.bugbotDown) reviewerOptOutTokens.push('bugbot')
|
|
555
|
+
if (context.copilotDown) reviewerOptOutTokens.push('copilot')
|
|
556
|
+
if (context.bugteamPostBlocked) reviewerOptOutTokens.push('bugteam')
|
|
557
|
+
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`
|
|
549
559
|
: ''
|
|
550
560
|
return convergeReadOnlyAgent(
|
|
551
561
|
`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` +
|
|
552
|
-
`1. Run: python "${CONFIG.sharedScripts}/check_convergence.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber}${bugbotDownFlag}${copilotDownFlag}\n` +
|
|
562
|
+
`1. Run: python "${CONFIG.sharedScripts}/check_convergence.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber}${bugbotDownFlag}${copilotDownFlag}${bugteamPostBlockedFlag}\n` +
|
|
563
|
+
bugteamPostBlockedNote +
|
|
553
564
|
` Exit 0 -> every gate passed: set pass:true, failures:[].\n` +
|
|
554
565
|
` Exit 1 -> set pass:false and failures to each printed FAIL line verbatim.\n` +
|
|
555
566
|
` Exit 2 -> retry once; if it still errors, set pass:false, failures:["check_convergence gh error"].\n\n` +
|
|
556
567
|
`2. Only when step 1 set pass:true, mark the PR ready and confirm it left draft state:\n` +
|
|
557
|
-
|
|
568
|
+
reviewerOptOut +
|
|
558
569
|
` Run: gh pr ready ${input.prNumber} --repo ${input.owner}/${input.repo}\n` +
|
|
559
570
|
` Re-query the draft state: gh api repos/${input.owner}/${input.repo}/pulls/${input.prNumber} --jq .draft\n` +
|
|
560
571
|
` 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` +
|
|
@@ -635,6 +646,24 @@ const COPILOT_SCHEMA = {
|
|
|
635
646
|
required: ['sha', 'clean', 'down', 'findings'],
|
|
636
647
|
}
|
|
637
648
|
|
|
649
|
+
const COPILOT_VERIFY_VERDICTS = ['confirmed', 'refuted', 'inconclusive']
|
|
650
|
+
|
|
651
|
+
const COPILOT_VERIFY_SCHEMA = {
|
|
652
|
+
type: 'object',
|
|
653
|
+
additionalProperties: false,
|
|
654
|
+
properties: {
|
|
655
|
+
verdict: {
|
|
656
|
+
type: 'string',
|
|
657
|
+
enum: COPILOT_VERIFY_VERDICTS,
|
|
658
|
+
description: 'confirmed when the executed check tangibly reproduces the defect; refuted when the executed check tangibly demonstrates correct behavior in the exact scenario the finding claims is broken; inconclusive (the default) for everything else — no runnable check exists for the claim, the check is infeasible in this environment, the results are ambiguous, or the fix would require a product decision between defensible behaviors',
|
|
659
|
+
},
|
|
660
|
+
checkCommand: { type: 'string', description: 'the exact command(s) executed against the flagged HEAD; non-empty for confirmed and refuted — the workflow downgrades a conclusive verdict with an empty checkCommand to inconclusive' },
|
|
661
|
+
checkOutput: { type: 'string', description: 'the captured output of checkCommand demonstrating the behavior in question; non-empty for confirmed and refuted — the workflow downgrades a conclusive verdict with an empty checkOutput to inconclusive' },
|
|
662
|
+
evidence: { type: 'string', description: 'one line naming what check was attempted and what it showed, or why it was not decisive' },
|
|
663
|
+
},
|
|
664
|
+
required: ['verdict', 'checkCommand', 'checkOutput', 'evidence'],
|
|
665
|
+
}
|
|
666
|
+
|
|
638
667
|
const REVIEWER_AVAILABILITY_SCHEMA = {
|
|
639
668
|
type: 'object',
|
|
640
669
|
additionalProperties: false,
|
|
@@ -1387,47 +1416,79 @@ function classifyReviewerGateOutcome(reviewerGate) {
|
|
|
1387
1416
|
}
|
|
1388
1417
|
|
|
1389
1418
|
/**
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1395
|
-
*
|
|
1396
|
-
* wrapper; the terminal Bugbot gate keeps the base classifier, whose findings
|
|
1397
|
-
* carry no tier and always route to a fix.
|
|
1398
|
-
* @param {object|null|undefined} copilot the Copilot gate result, or null on agent failure
|
|
1399
|
-
* @returns {{kind: string, findings?: Array<object>}} the next action
|
|
1419
|
+
* Split a Copilot round's findings by routing tier. Any tier other than the
|
|
1420
|
+
* explicit 'self-healing' counts as a code concern, so a missing or unexpected
|
|
1421
|
+
* tier routes to the verification stage rather than silently into the auto-fix
|
|
1422
|
+
* flow.
|
|
1423
|
+
* @param {Array<object>} findings the Copilot findings for the round
|
|
1424
|
+
* @returns {{selfHealing: Array<object>, codeConcern: Array<object>}} the tier partition
|
|
1400
1425
|
*/
|
|
1401
|
-
function
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1426
|
+
function partitionFindingsByTier(findings) {
|
|
1427
|
+
return {
|
|
1428
|
+
selfHealing: findings.filter((each) => each.tier === 'self-healing'),
|
|
1429
|
+
codeConcern: findings.filter((each) => each.tier !== 'self-healing'),
|
|
1405
1430
|
}
|
|
1406
|
-
return outcome
|
|
1407
1431
|
}
|
|
1408
1432
|
|
|
1409
1433
|
/**
|
|
1410
|
-
*
|
|
1411
|
-
*
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1415
|
-
*
|
|
1416
|
-
* @
|
|
1434
|
+
* Enforce the executed-check rule on one verifier result. A verdict is
|
|
1435
|
+
* conclusive (confirmed or refuted) only when an actual check ran: a dead
|
|
1436
|
+
* verifier agent (null result) is inconclusive, and a conclusive verdict whose
|
|
1437
|
+
* checkCommand or checkOutput is empty carries no executed check, so it is
|
|
1438
|
+
* downgraded to inconclusive rather than trusted — source-reading reasoning
|
|
1439
|
+
* alone never confirms or refutes a finding.
|
|
1440
|
+
* @param {object|null|undefined} verifier the COPILOT_VERIFY_SCHEMA result, or null on agent failure
|
|
1441
|
+
* @returns {object} a verifier result whose verdict honors the executed-check rule
|
|
1417
1442
|
*/
|
|
1418
|
-
function
|
|
1419
|
-
|
|
1443
|
+
function normalizeVerifierVerdict(verifier) {
|
|
1444
|
+
if (verifier == null) {
|
|
1445
|
+
return { verdict: 'inconclusive', checkCommand: '', checkOutput: '', evidence: 'verifier agent died — no check was executed' }
|
|
1446
|
+
}
|
|
1447
|
+
const isConclusive = verifier.verdict === 'confirmed' || verifier.verdict === 'refuted'
|
|
1448
|
+
const hasExecutedCheck = verifier.checkCommand.trim() !== '' && verifier.checkOutput.trim() !== ''
|
|
1449
|
+
if (isConclusive && !hasExecutedCheck) {
|
|
1450
|
+
return {
|
|
1451
|
+
...verifier,
|
|
1452
|
+
verdict: 'inconclusive',
|
|
1453
|
+
evidence: `conclusive verdict arrived with an empty checkCommand or checkOutput, so no executed check backs it — downgraded to inconclusive. ${verifier.evidence || ''}`.trim(),
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
return verifier
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* Fold a confirmed finding's executed repro into the finding detail the fix
|
|
1461
|
+
* steps render, so the fix prompt carries the exact repro command and captured
|
|
1462
|
+
* failing output, requires the before/after re-run of that same command, and
|
|
1463
|
+
* asks for the repro as a regression test. The verification field is dropped so
|
|
1464
|
+
* the finding matches the shape the fix flow renders.
|
|
1465
|
+
* @param {object} finding a code-concern finding carrying a confirmed verification
|
|
1466
|
+
* @returns {object} the finding with the repro folded into its detail
|
|
1467
|
+
*/
|
|
1468
|
+
function attachVerifiedRepro(finding) {
|
|
1469
|
+
const { verification, ...bareFinding } = finding
|
|
1470
|
+
return {
|
|
1471
|
+
...bareFinding,
|
|
1472
|
+
detail:
|
|
1473
|
+
`${finding.detail}\n\n` +
|
|
1474
|
+
`CONFIRMED BY EXECUTED CHECK — this repro already demonstrates the defect on HEAD.\n` +
|
|
1475
|
+
` Repro command(s):\n${verification.checkCommand}\n` +
|
|
1476
|
+
` Captured failing output:\n${verification.checkOutput}\n` +
|
|
1477
|
+
` After fixing: re-run the exact repro command(s) above and capture the output showing the wrong behavior is gone; include that before/after output in the thread reply. Where the repo's test suite covers this surface, add the repro as a regression test.`,
|
|
1478
|
+
}
|
|
1420
1479
|
}
|
|
1421
1480
|
|
|
1422
1481
|
/**
|
|
1423
1482
|
* Build the user-review payload the orchestrator hands to the
|
|
1424
|
-
* copilot-finding-triage skill: the Copilot review link plus the
|
|
1425
|
-
* findings pared to the fields the ntfy summary and the review gate read
|
|
1426
|
-
*
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1483
|
+
* copilot-finding-triage skill: the Copilot review link plus the inconclusive
|
|
1484
|
+
* findings pared to the fields the ntfy summary and the review gate read, each
|
|
1485
|
+
* carrying its verifier's one-line evidence note (what check was attempted and
|
|
1486
|
+
* why it was not decisive). The workflow returns this alongside blocker
|
|
1487
|
+
* 'user-review' so the orchestrating session runs the notify-and-wait gate; a
|
|
1488
|
+
* background workflow cannot hold for a human, so it carries the findings out
|
|
1489
|
+
* rather than deciding them.
|
|
1429
1490
|
* @param {object} copilot the COPILOT_SCHEMA gate result carrying the review URL
|
|
1430
|
-
* @param {Array<object>} findings the
|
|
1491
|
+
* @param {Array<object>} findings the inconclusive findings for this HEAD, each carrying its verification
|
|
1431
1492
|
* @returns {{reviewUrl: string, findings: Array<object>}} the triage payload
|
|
1432
1493
|
*/
|
|
1433
1494
|
function buildUserReview(copilot, findings) {
|
|
@@ -1439,6 +1500,7 @@ function buildUserReview(copilot, findings) {
|
|
|
1439
1500
|
severity: each.severity,
|
|
1440
1501
|
tier: each.tier,
|
|
1441
1502
|
title: each.title,
|
|
1503
|
+
evidence: each.verification?.evidence || '',
|
|
1442
1504
|
})),
|
|
1443
1505
|
}
|
|
1444
1506
|
}
|
|
@@ -1660,7 +1722,7 @@ function runAuditLens(head, preflightResult) {
|
|
|
1660
1722
|
* Self-review lens: the semantic pre-catch parity pass over the full diff. It
|
|
1661
1723
|
* covers the doc-vs-code parity, test-assertion completeness, and
|
|
1662
1724
|
* PR-description-vs-diff lanes that sit outside the A-P bug categories, reusing
|
|
1663
|
-
* the pr-consistency-audit
|
|
1725
|
+
* the pr-consistency-audit prompt's canonical-source cross-reference method. It
|
|
1664
1726
|
* stays on opus because line-citation accuracy, symbol attribution, and
|
|
1665
1727
|
* inventory or count claims are semantic judgments a cheaper tier misreads. It
|
|
1666
1728
|
* reports findings without editing.
|
|
@@ -1673,7 +1735,7 @@ function runSelfReviewLens(head, preflightResult) {
|
|
|
1673
1735
|
`You are the self-review parity lens for ${prCoordinates}, HEAD ${head}. Review the FULL origin/main...HEAD diff — every file the PR touches. Do NOT edit, commit, or push.\n\n` +
|
|
1674
1736
|
renderLensDiffContext(preflightResult) +
|
|
1675
1737
|
`Read the pre-catch rubric at ${CONFIG.precatchRubric} for the three lane checklists, and cover each lane:\n` +
|
|
1676
|
-
`1. Doc-vs-code parity: reuse the pr-consistency-audit
|
|
1738
|
+
`1. Doc-vs-code parity: reuse the pr-consistency-audit prompt's canonical-source cross-reference method ($HOME/.claude/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml) and the drift rubric at $HOME/.claude/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md. Verify every line citation resolves, every referenced file or script path exists, every symbol is attributed to the file that defines it, and every inventory, count, and ordering claim matches the code.\n` +
|
|
1677
1739
|
`2. Test-assertion completeness: every changed or new production path has a paired test that calls it and asserts on its behavior, and a changed test pins behavior rather than hiding it behind a mock.\n` +
|
|
1678
1740
|
`3. PR-description-vs-diff two-way parity: fetch the PR body read-only, then confirm every PR-body claim maps to a hunk in the diff and every hunk maps to a claim; flag invented paths, invented counts, and out-of-scope changes.\n\n` +
|
|
1679
1741
|
`Before returning clean, state one proof-of-absence line per lane naming what you checked. Return strictly the schema: clean=true with empty findings when all three lanes pass, otherwise one entry per finding (severity P0/P1/P2; category 'code-standard' for a pure doc or style parity gap with no behavioral impact, 'bug' otherwise; replyToCommentId=null). Set sha=${'`'}${head}${'`'}, down=false.`,
|
|
@@ -1818,27 +1880,59 @@ async function applyFixes(head, findings, sourceLabel) {
|
|
|
1818
1880
|
}
|
|
1819
1881
|
|
|
1820
1882
|
/**
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
*
|
|
1824
|
-
*
|
|
1825
|
-
*
|
|
1826
|
-
*
|
|
1827
|
-
*
|
|
1828
|
-
*
|
|
1829
|
-
*
|
|
1830
|
-
*
|
|
1883
|
+
* Reason a CLEAN bugteam audit post did not land, read from a not-posted result.
|
|
1884
|
+
*
|
|
1885
|
+
* ::
|
|
1886
|
+
*
|
|
1887
|
+
* {posted: false, reason: 'denied by the classifier'} -> 'denied by the classifier'
|
|
1888
|
+
* null -> 'the post agent returned no result' (the spawn was refused)
|
|
1889
|
+
*
|
|
1890
|
+
* A null result is the environment-refused case: the post agent, or its very
|
|
1891
|
+
* spawn, returned nothing, so a fixed phrase stands in for the reason the agent
|
|
1892
|
+
* would otherwise carry.
|
|
1893
|
+
* @param {object} auditResult CLEAN_AUDIT_SCHEMA result from the post-clean-audit task, or null when the agent never ran
|
|
1894
|
+
* @returns {string} the one-line refusal reason
|
|
1831
1895
|
*/
|
|
1832
|
-
function
|
|
1833
|
-
|
|
1896
|
+
function cleanAuditPostReason(auditResult) {
|
|
1897
|
+
return auditResult?.reason || 'the post agent returned no result'
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* Build the run-level bypass note naming the HEAD and the refusal reason for a
|
|
1902
|
+
* CLEAN bugteam post the environment refused. See reference/stop-conditions.md
|
|
1903
|
+
* § "Clean-audit post bypassed" for the full policy.
|
|
1904
|
+
* @param {string} head converged PR HEAD SHA the CLEAN post targeted
|
|
1905
|
+
* @param {object} auditResult CLEAN_AUDIT_SCHEMA result from the post-clean-audit task, or null when the agent never ran
|
|
1906
|
+
* @returns {string} the run-level bypass note
|
|
1907
|
+
*/
|
|
1908
|
+
function cleanAuditBypassNote(head, auditResult) {
|
|
1834
1909
|
return (
|
|
1835
|
-
`
|
|
1836
|
-
`
|
|
1837
|
-
`
|
|
1838
|
-
`
|
|
1910
|
+
`The CLEAN bugteam review could not be posted on HEAD ${head} ` +
|
|
1911
|
+
`(${cleanAuditPostReason(auditResult)}) — every review lens already cleared this HEAD, ` +
|
|
1912
|
+
`so the CLEAN post is bypassed and the run proceeds to the terminal Bugbot gate ` +
|
|
1913
|
+
`with the convergence check's bugteam-review gate skipped.`
|
|
1839
1914
|
)
|
|
1840
1915
|
}
|
|
1841
1916
|
|
|
1917
|
+
/**
|
|
1918
|
+
* Resolve the run-level clean-audit note from a CLEAN bugteam post attempt.
|
|
1919
|
+
*
|
|
1920
|
+
* A landed post clears any earlier-round bypass note so FINALIZE runs the
|
|
1921
|
+
* bugteam gate; a refused post records the bypass note and logs it. This one
|
|
1922
|
+
* helper handles both outcomes at both post sites.
|
|
1923
|
+
* @param {object} auditResult CLEAN_AUDIT_SCHEMA result from the post task, or null when the agent never ran
|
|
1924
|
+
* @param {string} head converged PR HEAD SHA the CLEAN post targeted
|
|
1925
|
+
* @param {number} rounds current round number, named in the log line
|
|
1926
|
+
* @returns {string|null} the bypass note when the post was refused, or null when it landed
|
|
1927
|
+
*/
|
|
1928
|
+
function resolveCleanAuditNote(auditResult, head, rounds) {
|
|
1929
|
+
if (auditResult?.posted) {
|
|
1930
|
+
return null
|
|
1931
|
+
}
|
|
1932
|
+
log(`Round ${rounds}: CLEAN bugteam post bypassed on ${head?.slice(0, 7)} (${cleanAuditPostReason(auditResult)}) — recording the bypass and proceeding to the terminal Bugbot gate`)
|
|
1933
|
+
return cleanAuditBypassNote(head, auditResult)
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1842
1936
|
/**
|
|
1843
1937
|
* Copilot gate: request a Copilot review on HEAD and poll until it lands or the
|
|
1844
1938
|
* poll cap is hit; return Copilot's findings or a down signal. Copilot is down
|
|
@@ -1862,7 +1956,7 @@ function runCopilotGate(head) {
|
|
|
1862
1956
|
` - No review after ${CONFIG.copilotMaxPolls} attempts -> Copilot is down for this run (unreachable, or silently out of quota with no notice): return {sha:${'`'}${head}${'`'}, clean:false, down:true, findings:[]}.\n\n` +
|
|
1863
1957
|
`Tier every finding for routing, separate from its category:\n` +
|
|
1864
1958
|
` - tier 'self-healing': pure style, type hints, misplaced or unused imports, formatting, magic-value extraction, test-only changes, doc-or-description vs code mismatches, or code de-duplication — any fix that cannot change observable runtime behavior for production callers. These route into the fix flow automatically.\n` +
|
|
1865
|
-
` - tier 'code-concern': logic or correctness, security, data handling, error-handling semantics, or concurrency — anything behavior-changing or needing a product decision.
|
|
1959
|
+
` - tier 'code-concern': logic or correctness, security, data handling, error-handling semantics, or concurrency — anything behavior-changing or needing a product decision. Each of these goes to a verification agent that must execute a check against HEAD; only findings the check leaves inconclusive hold for a user-review gate.\n` +
|
|
1866
1960
|
` - Classify as 'code-concern' whenever the tier is in doubt.\n\n` +
|
|
1867
1961
|
`A down verdict is valid ONLY in two cases: the review request itself failed, or the FULL poll budget (${CONFIG.copilotMaxPolls} attempts x 360 seconds) elapsed with no review on HEAD. A successful review request means the review is in flight; returning down:true on a partial poll is an invalid result. Never end the poll early for any reason other than a received review on HEAD or an out-of-usage notice — not tooling friction, not turn-length pressure, not a failed attempt to write a polling helper. When your wait tooling fails, re-arm it and keep polling until the budget is spent.\n\n` +
|
|
1868
1962
|
`Return strictly the schema.`,
|
|
@@ -1870,6 +1964,88 @@ function runCopilotGate(head) {
|
|
|
1870
1964
|
)
|
|
1871
1965
|
}
|
|
1872
1966
|
|
|
1967
|
+
/**
|
|
1968
|
+
* Spawn the verifier agent for one Copilot code-concern finding. The verifier
|
|
1969
|
+
* decides confirmed / refuted / inconclusive by executing a check against the
|
|
1970
|
+
* flagged HEAD; normalizeVerifierVerdict then enforces the executed-check rule
|
|
1971
|
+
* on its result. It routes through convergeAgent (not the read-only spawn)
|
|
1972
|
+
* because building a purpose-built check may write scratch files, but it fixes
|
|
1973
|
+
* nothing and never commits or pushes.
|
|
1974
|
+
* @param {string} head converged PR HEAD SHA
|
|
1975
|
+
* @param {object} finding one code-concern finding from the Copilot gate
|
|
1976
|
+
* @returns {Promise<object>} COPILOT_VERIFY_SCHEMA result
|
|
1977
|
+
*/
|
|
1978
|
+
function runCopilotFindingVerifier(head, finding) {
|
|
1979
|
+
return convergeAgent(
|
|
1980
|
+
`You are the VERIFICATION step for one Copilot code-concern finding on ${prCoordinates}, HEAD ${head}. Decide whether the finding is tangibly real by EXECUTING a check. Do not fix anything, do not commit, and do not push.\n\n` +
|
|
1981
|
+
`The finding:\n${renderFindingsBlock([finding])}\n\n` +
|
|
1982
|
+
`THE HARD RULE — a verdict is conclusive ONLY if an actual check was executed. Reading the source and reasoning about it, however sound, never produces a conclusive verdict. A check is a concrete command you run against this HEAD — executing the flagged code path with crafted inputs, forcing the claimed error condition, or running a purpose-built test — whose captured output demonstrates the behavior in question. Source inspection may inform where to aim the check, but is never itself grounds for confirmed or refuted.\n\n` +
|
|
1983
|
+
`Steps:\n` +
|
|
1984
|
+
`1. Confirm the working tree is on the PR branch at HEAD ${head} with no uncommitted edits.\n` +
|
|
1985
|
+
`2. Read the flagged code only to aim the check, then build and run it. Keep any purpose-built test or scratch input in the OS temp dir, and leave the repo working tree exactly as you found it (git status clean when you finish).\n` +
|
|
1986
|
+
`3. Choose the verdict:\n` +
|
|
1987
|
+
` - confirmed: your executed check tangibly reproduces the defect the finding claims — the captured output shows the wrong behavior.\n` +
|
|
1988
|
+
` - refuted: your executed check tangibly demonstrates the code already behaves correctly in the exact scenario the finding claims is broken — the captured output shows the correct behavior.\n` +
|
|
1989
|
+
` - inconclusive (the DEFAULT): everything else — no runnable check exists for the claim, the check is infeasible in this environment, the results are ambiguous, or the fix would require a product decision between defensible behaviors.\n\n` +
|
|
1990
|
+
`Return strictly the schema. For confirmed and refuted, checkCommand is the exact command(s) you ran and checkOutput is their captured output — both non-empty; the workflow downgrades a conclusive verdict with an empty checkCommand or checkOutput to inconclusive. evidence is one line naming what check was attempted and what it showed (for inconclusive: why it was not decisive).`,
|
|
1991
|
+
{ label: `copilot-verify:${finding.file}:${finding.line}`, phase: 'Copilot gate', schema: COPILOT_VERIFY_SCHEMA, agentType: 'general-purpose', ...TIERS.sonnetMedium },
|
|
1992
|
+
)
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
/**
|
|
1996
|
+
* Verify each code-concern Copilot finding with its own executed-check verifier,
|
|
1997
|
+
* all in parallel, and partition the round by normalized verdict. Reachable only
|
|
1998
|
+
* from the COPILOT phase's code-concern path, so a round with no code-concern
|
|
1999
|
+
* findings spawns no verifier.
|
|
2000
|
+
* @param {string} head converged PR HEAD SHA
|
|
2001
|
+
* @param {Array<object>} codeConcernFindings the round's code-concern findings
|
|
2002
|
+
* @returns {Promise<{confirmed: Array<object>, refuted: Array<object>, inconclusive: Array<object>}>} the findings, each carrying its verification
|
|
2003
|
+
*/
|
|
2004
|
+
async function verifyCodeConcernFindings(head, codeConcernFindings) {
|
|
2005
|
+
const verdicts = await parallel(
|
|
2006
|
+
codeConcernFindings.map((each) => () => runCopilotFindingVerifier(head, each)),
|
|
2007
|
+
)
|
|
2008
|
+
const verified = codeConcernFindings.map((each, position) => ({
|
|
2009
|
+
...each,
|
|
2010
|
+
verification: normalizeVerifierVerdict(verdicts[position]),
|
|
2011
|
+
}))
|
|
2012
|
+
return {
|
|
2013
|
+
confirmed: verified.filter((each) => each.verification.verdict === 'confirmed'),
|
|
2014
|
+
refuted: verified.filter((each) => each.verification.verdict === 'refuted'),
|
|
2015
|
+
inconclusive: verified.filter((each) => each.verification.verdict === 'inconclusive'),
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
/**
|
|
2020
|
+
* Reply to and resolve the review threads of findings refuted by an executed
|
|
2021
|
+
* check, quoting the check command and captured output so the thread records why
|
|
2022
|
+
* the finding counts clean. Findings with no review thread (replyToCommentId
|
|
2023
|
+
* null) need no reply, so a batch carrying no thread ids spawns no agent. Makes
|
|
2024
|
+
* no code edits, no commit, and no push.
|
|
2025
|
+
* @param {string} head converged PR HEAD SHA
|
|
2026
|
+
* @param {Array<object>} refutedFindings the refuted findings, each carrying its verification
|
|
2027
|
+
* @returns {Promise<string|null>} the agent transcript, or null when no finding carries a thread
|
|
2028
|
+
*/
|
|
2029
|
+
function runRefutedThreadResolution(head, refutedFindings) {
|
|
2030
|
+
const threadBearing = refutedFindings.filter((each) => collectFindingThreadIds(each).length > 0)
|
|
2031
|
+
if (threadBearing.length === 0) return Promise.resolve(null)
|
|
2032
|
+
const findingsBlock = threadBearing
|
|
2033
|
+
.map((each, position) =>
|
|
2034
|
+
`${position + 1}. [${each.severity}] ${each.file}:${each.line} — ${each.title}\n` +
|
|
2035
|
+
` (GitHub review comment ids: ${collectFindingThreadIds(each).join(', ')})\n` +
|
|
2036
|
+
` Check command(s): ${each.verification.checkCommand}\n` +
|
|
2037
|
+
` Captured output: ${each.verification.checkOutput}\n` +
|
|
2038
|
+
` Evidence note: ${each.verification.evidence}`)
|
|
2039
|
+
.join('\n')
|
|
2040
|
+
return convergeAgent(
|
|
2041
|
+
`You are the THREAD-RESOLUTION step for ${threadBearing.length} Copilot finding(s) on ${prCoordinates}, HEAD ${head}, each refuted by an executed check: the check's captured output demonstrates the code already behaves correctly in the exact scenario the finding claims is broken. Make NO code edits, NO commit, and NO push — only reply to and resolve the review threads below.\n\n` +
|
|
2042
|
+
`Refuted findings with their check evidence:\n${findingsBlock}\n\n` +
|
|
2043
|
+
`For each finding: 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 "<the check command(s) and the captured output demonstrating the correct behavior>". 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` +
|
|
2044
|
+
`Return a one-line summary naming the threads you resolved.`,
|
|
2045
|
+
{ label: 'copilot-refuted-resolve', phase: 'Copilot gate', agentType: 'general-purpose', ...TIERS.sonnetMedium },
|
|
2046
|
+
)
|
|
2047
|
+
}
|
|
2048
|
+
|
|
1873
2049
|
/**
|
|
1874
2050
|
* Address the gates a convergence check reported as failing, then hand control
|
|
1875
2051
|
* back to the converge phase: edit (clean-coder resolves bot threads, applies
|
|
@@ -2239,6 +2415,7 @@ let bugbotDown = input.bugbotDisabled || false
|
|
|
2239
2415
|
let copilotDown = input.copilotDisabled || false
|
|
2240
2416
|
let copilotNote = null
|
|
2241
2417
|
let standardsNote = null
|
|
2418
|
+
let cleanAuditNote = null
|
|
2242
2419
|
let hasStandardsFollowUpFiled = false
|
|
2243
2420
|
let wasStandardsHardeningPrOpened = false
|
|
2244
2421
|
let standardsFollowUpIssueUrl = ''
|
|
@@ -2246,6 +2423,15 @@ let reuseNote = null
|
|
|
2246
2423
|
let reviewerAvailability = null
|
|
2247
2424
|
const deferredPrs = []
|
|
2248
2425
|
|
|
2426
|
+
const assembleResult = (outcomeFields) => ({
|
|
2427
|
+
...outcomeFields,
|
|
2428
|
+
standardsNote,
|
|
2429
|
+
copilotNote,
|
|
2430
|
+
cleanAuditNote,
|
|
2431
|
+
reuseNote,
|
|
2432
|
+
deferredPrs,
|
|
2433
|
+
})
|
|
2434
|
+
|
|
2249
2435
|
const preflight = await runGitTask('preflight-git')
|
|
2250
2436
|
reviewerAvailability = preflight
|
|
2251
2437
|
if (isResolvedHeadUsable(preflight?.sha)) {
|
|
@@ -2333,10 +2519,7 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2333
2519
|
deferredStandardsFindings: findings,
|
|
2334
2520
|
standardsDeferral,
|
|
2335
2521
|
})
|
|
2336
|
-
|
|
2337
|
-
blocker = cleanAuditBlocker(head, auditResult)
|
|
2338
|
-
break
|
|
2339
|
-
}
|
|
2522
|
+
cleanAuditNote = resolveCleanAuditNote(auditResult, head, rounds)
|
|
2340
2523
|
phase = 'BUGBOT'
|
|
2341
2524
|
continue
|
|
2342
2525
|
}
|
|
@@ -2377,10 +2560,7 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2377
2560
|
head = null
|
|
2378
2561
|
continue
|
|
2379
2562
|
}
|
|
2380
|
-
|
|
2381
|
-
blocker = cleanAuditBlocker(head, auditResult)
|
|
2382
|
-
break
|
|
2383
|
-
}
|
|
2563
|
+
cleanAuditNote = resolveCleanAuditNote(auditResult, head, rounds)
|
|
2384
2564
|
resetNoLensRounds()
|
|
2385
2565
|
phase = 'BUGBOT'
|
|
2386
2566
|
continue
|
|
@@ -2443,7 +2623,7 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2443
2623
|
continue
|
|
2444
2624
|
}
|
|
2445
2625
|
const copilot = await runCopilotGate(head)
|
|
2446
|
-
const copilotOutcome =
|
|
2626
|
+
const copilotOutcome = classifyReviewerGateOutcome(copilot)
|
|
2447
2627
|
copilotDown = resolveCopilotDown(copilotOutcome)
|
|
2448
2628
|
copilotNote = null
|
|
2449
2629
|
if (copilotOutcome.kind === 'retry') {
|
|
@@ -2457,39 +2637,53 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2457
2637
|
phase = 'FINALIZE'
|
|
2458
2638
|
continue
|
|
2459
2639
|
}
|
|
2460
|
-
if (copilotOutcome.kind === 'user-review') {
|
|
2461
|
-
log(`Copilot raised ${copilotOutcome.findings.length} code-concern finding(s) — holding for user review: the workflow does not auto-fix them and does not mark the PR ready`)
|
|
2462
|
-
return {
|
|
2463
|
-
converged: false,
|
|
2464
|
-
rounds,
|
|
2465
|
-
finalSha: head,
|
|
2466
|
-
blocker: 'user-review',
|
|
2467
|
-
userReview: buildUserReview(copilot, copilotOutcome.findings),
|
|
2468
|
-
standardsNote,
|
|
2469
|
-
copilotNote,
|
|
2470
|
-
reuseNote,
|
|
2471
|
-
deferredPrs,
|
|
2472
|
-
}
|
|
2473
|
-
}
|
|
2474
2640
|
if (copilotOutcome.kind === 'fix') {
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2641
|
+
const { selfHealing, codeConcern } = partitionFindingsByTier(copilotOutcome.findings)
|
|
2642
|
+
let roundFindings = copilotOutcome.findings
|
|
2643
|
+
if (codeConcern.length > 0) {
|
|
2644
|
+
log(`Copilot raised ${codeConcern.length} code-concern finding(s) — verifying each with an executed check before any routing`)
|
|
2645
|
+
const { confirmed, refuted, inconclusive } = await verifyCodeConcernFindings(head, codeConcern)
|
|
2646
|
+
if (refuted.length > 0) {
|
|
2647
|
+
log(`${refuted.length} finding(s) refuted by an executed check — replying with the evidence and resolving their threads`)
|
|
2648
|
+
await runRefutedThreadResolution(head, refuted)
|
|
2649
|
+
}
|
|
2650
|
+
if (inconclusive.length > 0) {
|
|
2651
|
+
log(`${inconclusive.length} finding(s) stayed inconclusive after verification — holding for user review: the workflow does not auto-fix them and does not mark the PR ready`)
|
|
2652
|
+
return assembleResult({
|
|
2653
|
+
converged: false,
|
|
2654
|
+
rounds,
|
|
2655
|
+
finalSha: head,
|
|
2656
|
+
blocker: 'user-review',
|
|
2657
|
+
userReview: buildUserReview(copilot, inconclusive),
|
|
2658
|
+
})
|
|
2659
|
+
}
|
|
2660
|
+
roundFindings = [...selfHealing, ...confirmed.map((each) => attachVerifiedRepro(each))]
|
|
2661
|
+
if (roundFindings.length === 0) {
|
|
2662
|
+
log('Every code-concern finding was refuted with executed-check evidence and the round carries no self-healing findings — the Copilot gate passes')
|
|
2663
|
+
copilotDown = false
|
|
2664
|
+
copilotNote = null
|
|
2665
|
+
phase = 'FINALIZE'
|
|
2666
|
+
continue
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
if (isStandardsOnlyRound(roundFindings)) {
|
|
2670
|
+
log(`Copilot raised ${roundFindings.length} code-standard-only finding(s) — deferring to follow-up PRs and treating the gate as passed`)
|
|
2671
|
+
const standardsOutcome = await openStandardsFollowUpOnce(head, roundFindings, 'copilot', { copilotDisabled: copilotDown, bugbotDisabled: bugbotDown })
|
|
2672
|
+
standardsNote = standardsDeferralNote(roundFindings.length, buildStandardsDeferral())
|
|
2479
2673
|
if (standardsOutcome?.deferredPr) deferredPrs.push(standardsOutcome.deferredPr)
|
|
2480
2674
|
copilotDown = false
|
|
2481
2675
|
copilotNote = null
|
|
2482
2676
|
phase = 'FINALIZE'
|
|
2483
2677
|
continue
|
|
2484
2678
|
}
|
|
2485
|
-
log(`Copilot raised ${
|
|
2486
|
-
const fixResult = await applyFixes(head,
|
|
2487
|
-
const hadThreadBearingFinding =
|
|
2679
|
+
log(`Copilot raised ${roundFindings.length} finding(s) — fixing and re-converging`)
|
|
2680
|
+
const fixResult = await applyFixes(head, roundFindings, 'copilot')
|
|
2681
|
+
const hadThreadBearingFinding = roundFindings.some((each) => collectFindingThreadIds(each).length > 0)
|
|
2488
2682
|
const fixProgress = detectFixProgress(fixResult, head, hadThreadBearingFinding)
|
|
2489
2683
|
if (!fixProgress.progressed) {
|
|
2490
2684
|
blocker = fixResult?.resolvedWithoutCommit === true && !hadThreadBearingFinding
|
|
2491
|
-
? `fix stalled: copilot round raised ${
|
|
2492
|
-
: `copilot fix lens landed no push for ${
|
|
2685
|
+
? `fix stalled: copilot round raised ${roundFindings.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`
|
|
2686
|
+
: `copilot fix lens landed no push for ${roundFindings.length} finding(s) on HEAD ${head}`
|
|
2493
2687
|
break
|
|
2494
2688
|
}
|
|
2495
2689
|
head = null
|
|
@@ -2503,7 +2697,7 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2503
2697
|
}
|
|
2504
2698
|
|
|
2505
2699
|
if (phase === 'FINALIZE') {
|
|
2506
|
-
const finalizeResult = await runConvergenceCheck({ head, bugbotDown, copilotDown })
|
|
2700
|
+
const finalizeResult = await runConvergenceCheck({ head, bugbotDown, copilotDown, bugteamPostBlocked: cleanAuditNote !== null })
|
|
2507
2701
|
const convergenceOutcome = classifyConvergenceOutcome(finalizeResult)
|
|
2508
2702
|
if (convergenceOutcome.kind === 'retry') {
|
|
2509
2703
|
log('Convergence check agent died or returned no FAIL lines — re-running the check on the same HEAD')
|
|
@@ -2512,7 +2706,7 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2512
2706
|
if (convergenceOutcome.kind === 'ready') {
|
|
2513
2707
|
const readyOutcome = classifyReadyOutcome(finalizeResult)
|
|
2514
2708
|
if (readyOutcome.converged) {
|
|
2515
|
-
return { converged: true, rounds, finalSha: head, blocker: null
|
|
2709
|
+
return assembleResult({ converged: true, rounds, finalSha: head, blocker: null })
|
|
2516
2710
|
}
|
|
2517
2711
|
blocker = readyOutcome.blocker
|
|
2518
2712
|
break
|
|
@@ -2525,13 +2719,9 @@ while (iterations < CONFIG.maxIterations) {
|
|
|
2525
2719
|
}
|
|
2526
2720
|
}
|
|
2527
2721
|
|
|
2528
|
-
return {
|
|
2722
|
+
return assembleResult({
|
|
2529
2723
|
converged: false,
|
|
2530
2724
|
rounds,
|
|
2531
2725
|
finalSha: head,
|
|
2532
2726
|
blocker: blocker || `iteration cap reached (${CONFIG.maxIterations})`,
|
|
2533
|
-
|
|
2534
|
-
copilotNote,
|
|
2535
|
-
reuseNote,
|
|
2536
|
-
deferredPrs,
|
|
2537
|
-
}
|
|
2727
|
+
})
|
package/skills/bugteam/CLAUDE.md
CHANGED
|
@@ -10,7 +10,7 @@ Each loop: a `code-quality-agent` (fresh context, all A–P audit categories) pr
|
|
|
10
10
|
|
|
11
11
|
| File | Purpose |
|
|
12
12
|
|---|---|
|
|
13
|
-
| `SKILL.md` | Hub — pre-flight call, refusals, the audit-posting step (
|
|
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
14
|
| `PROMPTS.md` | Spawn XML, A–P 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. |
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
- **Full A–P 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–P 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
|
-
- **Grant before any spawn, revoke before any return.** Step 0 grants project `.claude/**` permissions; Step
|
|
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
8
|
- **Fresh subagent per loop.** Both bugfind and bugfix are spawned new each loop. Reusing a subagent across loops accumulates context inside that subagent's window — defeats clean-room.
|
|
9
9
|
- **One up-front confirmation = whole cycle.** The `/bugteam` invocation authorizes the entire cycle; every subsequent decision runs on that single authorization.
|
|
10
10
|
- **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
|
-
- **Code rules gate before every AUDIT.** Run
|
|
11
|
+
- **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`.
|
|
12
12
|
- **Clean-room audits, every loop.** Each bugfind subagent's spawn prompt contains only the PR scope, audit rubric, and the current loop number. Prior loop history stays in the lead.
|
|
13
13
|
- **Targeted fixes.** Each fix subagent sees ONLY the most recent audit's findings. Prior loops are invisible to the fix subagent.
|
|
14
14
|
- **Fix subagent receives the latest audit as its input contract.** Each loop's fix run operates on the current audit's output and only that.
|
|
15
|
-
- **Lead owns the final PR description rewrite only** (Step 4
|
|
15
|
+
- **Lead owns the final PR description rewrite only** (Step 4 Close substep via `pr-loop-lifecycle`), composed directly against `docs/PR_DESCRIPTION_GUIDE.md`.
|
|
16
16
|
|
|
17
17
|
## Why this design
|
|
18
18
|
|