claude-dev-env 1.93.0 → 1.94.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/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/code_rules_gate.py +127 -13
- package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +4 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +146 -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 +1 -1
- package/bin/install.mjs +2 -7
- package/bin/install.test.mjs +10 -4
- package/hooks/blocking/CLAUDE.md +2 -0
- 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/pii_scanner.py +26 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +2 -2
- package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
- package/hooks/blocking/test_pii_scanner.py +27 -2
- package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
- package/hooks/hooks_constants/CLAUDE.md +2 -2
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
- package/hooks/hooks_constants/local_identity.py +65 -15
- package/hooks/hooks_constants/pii_prevention_constants.py +20 -8
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
- package/hooks/hooks_constants/test_local_identity.py +68 -0
- package/package.json +1 -1
- 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 +3 -25
- 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 +284 -94
- 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/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/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 +12 -8
- package/skills/orchestrator-refresh/SKILL.md +3 -3
- 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/privacy-hygiene/SKILL.md +8 -7
- 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/usage-pause/SKILL.md +10 -14
- 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-fix-protocol/SKILL.md +0 -65
- 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
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
"""Tests for check_convergence.
|
|
2
2
|
|
|
3
3
|
Covers the bugteam audit gate (`_check_bugteam_clean`) which identifies
|
|
4
|
-
bugteam reviews by body header signature
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- a clean bugteam review on the current HEAD passes the gate
|
|
8
|
-
- a dirty bugteam review on the current HEAD fails the gate
|
|
9
|
-
- the absence of any bugteam review on the current HEAD fails the gate
|
|
4
|
+
bugteam reviews by body header signature, the down-flag resolvers, the
|
|
5
|
+
reviewer-bypass paths through ``check_all``, and the ``main`` entry point.
|
|
10
6
|
"""
|
|
11
7
|
|
|
12
8
|
from __future__ import annotations
|
|
@@ -20,12 +16,12 @@ from typing import Callable
|
|
|
20
16
|
|
|
21
17
|
import pytest
|
|
22
18
|
|
|
19
|
+
import _pr_converge_path_setup # noqa: F401
|
|
20
|
+
from pr_converge_skill_constants.constants import EXIT_CODE_GH_ERROR
|
|
21
|
+
|
|
23
22
|
_SCRIPTS_DIRECTORY = Path(__file__).absolute().parent
|
|
24
23
|
_PR_CONVERGE_DIRECTORY = _SCRIPTS_DIRECTORY.parent
|
|
25
24
|
|
|
26
|
-
if str(_PR_CONVERGE_DIRECTORY) not in sys.path:
|
|
27
|
-
sys.path.insert(0, str(_PR_CONVERGE_DIRECTORY))
|
|
28
|
-
|
|
29
25
|
|
|
30
26
|
def _load_module() -> ModuleType:
|
|
31
27
|
for each_cached_name in [
|
|
@@ -72,6 +68,16 @@ NON_BUGTEAM_BODY = (
|
|
|
72
68
|
"Cursor Bugbot has reviewed your changes and found 0 potential issues."
|
|
73
69
|
)
|
|
74
70
|
|
|
71
|
+
_ALL_LEAF_GATE_NAMES = [
|
|
72
|
+
"_check_bugbot",
|
|
73
|
+
"_check_bugbot_not_dirty",
|
|
74
|
+
"_check_bugteam_clean",
|
|
75
|
+
"_check_bot_review",
|
|
76
|
+
"_count_unresolved_bot_threads",
|
|
77
|
+
"_get_mergeable",
|
|
78
|
+
"_check_no_pending_reviews",
|
|
79
|
+
]
|
|
80
|
+
|
|
75
81
|
|
|
76
82
|
def _make_stub_gh_paginated(
|
|
77
83
|
all_review_objects: list[dict[str, object]],
|
|
@@ -85,6 +91,27 @@ def _make_stub_gh_paginated(
|
|
|
85
91
|
return stub_gh_api_paginated
|
|
86
92
|
|
|
87
93
|
|
|
94
|
+
def _passing_head_sha(**_call_keywords: object) -> str:
|
|
95
|
+
return CURRENT_HEAD_SHA
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _passing_gate(**_call_keywords: object) -> tuple[bool, str]:
|
|
99
|
+
return True, "stub passing"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _raise_if_called(gate_label: str) -> Callable[..., tuple[bool, str]]:
|
|
103
|
+
def _stub(**_call_keywords: object) -> tuple[bool, str]:
|
|
104
|
+
raise AssertionError(f"{gate_label} must not run when its gate is bypassed")
|
|
105
|
+
|
|
106
|
+
return _stub
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _patch_all_gates_passing(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
110
|
+
monkeypatch.setattr(check_convergence, "_get_pr_head_sha", _passing_head_sha)
|
|
111
|
+
for each_gate_name in _ALL_LEAF_GATE_NAMES:
|
|
112
|
+
monkeypatch.setattr(check_convergence, each_gate_name, _passing_gate)
|
|
113
|
+
|
|
114
|
+
|
|
88
115
|
def should_pass_when_clean_bugteam_review_present_on_current_head(
|
|
89
116
|
monkeypatch: pytest.MonkeyPatch,
|
|
90
117
|
) -> None:
|
|
@@ -96,13 +123,11 @@ def should_pass_when_clean_bugteam_review_present_on_current_head(
|
|
|
96
123
|
"submitted_at": "2026-05-17T12:00:00Z",
|
|
97
124
|
}
|
|
98
125
|
]
|
|
99
|
-
|
|
100
|
-
|
|
126
|
+
monkeypatch.setattr(
|
|
127
|
+
check_convergence, "_gh_api_paginated", _make_stub_gh_paginated(reviews_payload)
|
|
128
|
+
)
|
|
101
129
|
passed, detail = check_convergence._check_bugteam_clean(
|
|
102
|
-
owner="JonEcho",
|
|
103
|
-
repo="tests",
|
|
104
|
-
number=42,
|
|
105
|
-
head_sha=CURRENT_HEAD_SHA,
|
|
130
|
+
owner="JonEcho", repo="tests", number=42, head_sha=CURRENT_HEAD_SHA
|
|
106
131
|
)
|
|
107
132
|
assert passed is True
|
|
108
133
|
assert "clean bugteam audit" in detail
|
|
@@ -120,13 +145,11 @@ def should_fail_when_dirty_bugteam_review_present_on_current_head(
|
|
|
120
145
|
"submitted_at": "2026-05-17T12:00:00Z",
|
|
121
146
|
}
|
|
122
147
|
]
|
|
123
|
-
|
|
124
|
-
|
|
148
|
+
monkeypatch.setattr(
|
|
149
|
+
check_convergence, "_gh_api_paginated", _make_stub_gh_paginated(reviews_payload)
|
|
150
|
+
)
|
|
125
151
|
passed, detail = check_convergence._check_bugteam_clean(
|
|
126
|
-
owner="JonEcho",
|
|
127
|
-
repo="tests",
|
|
128
|
-
number=42,
|
|
129
|
-
head_sha=CURRENT_HEAD_SHA,
|
|
152
|
+
owner="JonEcho", repo="tests", number=42, head_sha=CURRENT_HEAD_SHA
|
|
130
153
|
)
|
|
131
154
|
assert passed is False
|
|
132
155
|
assert "dirty bugteam audit" in detail
|
|
@@ -150,13 +173,11 @@ def should_fail_when_no_bugteam_review_present_on_current_head(
|
|
|
150
173
|
"submitted_at": "2026-05-17T11:00:00Z",
|
|
151
174
|
},
|
|
152
175
|
]
|
|
153
|
-
|
|
154
|
-
|
|
176
|
+
monkeypatch.setattr(
|
|
177
|
+
check_convergence, "_gh_api_paginated", _make_stub_gh_paginated(reviews_payload)
|
|
178
|
+
)
|
|
155
179
|
passed, detail = check_convergence._check_bugteam_clean(
|
|
156
|
-
owner="JonEcho",
|
|
157
|
-
repo="tests",
|
|
158
|
-
number=42,
|
|
159
|
-
head_sha=CURRENT_HEAD_SHA,
|
|
180
|
+
owner="JonEcho", repo="tests", number=42, head_sha=CURRENT_HEAD_SHA
|
|
160
181
|
)
|
|
161
182
|
assert passed is False
|
|
162
183
|
assert "no bugteam review found" in detail
|
|
@@ -175,10 +196,7 @@ def should_fail_with_shape_detail_when_gh_returns_non_list_payload(
|
|
|
175
196
|
check_convergence, "_gh_api_paginated", stub_gh_api_paginated_returning_object
|
|
176
197
|
)
|
|
177
198
|
passed, detail = check_convergence._check_bugteam_clean(
|
|
178
|
-
owner="JonEcho",
|
|
179
|
-
repo="tests",
|
|
180
|
-
number=42,
|
|
181
|
-
head_sha=CURRENT_HEAD_SHA,
|
|
199
|
+
owner="JonEcho", repo="tests", number=42, head_sha=CURRENT_HEAD_SHA
|
|
182
200
|
)
|
|
183
201
|
assert passed is False
|
|
184
202
|
assert "unexpected gh api response shape" in detail
|
|
@@ -199,12 +217,8 @@ def test_private_helpers_reject_non_bugteam_body() -> None:
|
|
|
199
217
|
assert check_convergence._is_clean_bugteam_review(NON_BUGTEAM_BODY) is False
|
|
200
218
|
|
|
201
219
|
|
|
202
|
-
CLEAN_LEGACY_BUGTEAM_BODY =
|
|
203
|
-
|
|
204
|
-
)
|
|
205
|
-
DIRTY_LEGACY_BUGTEAM_BODY = (
|
|
206
|
-
"## /bugteam loop 1 audit: 1 P0 / 0 P1 / 0 P2 → dirty"
|
|
207
|
-
)
|
|
220
|
+
CLEAN_LEGACY_BUGTEAM_BODY = "## /bugteam loop 1 audit: 0 P0 / 0 P1 / 0 P2 → clean"
|
|
221
|
+
DIRTY_LEGACY_BUGTEAM_BODY = "## /bugteam loop 1 audit: 1 P0 / 0 P1 / 0 P2 → dirty"
|
|
208
222
|
|
|
209
223
|
|
|
210
224
|
def test_private_helpers_recognize_clean_legacy_header_body() -> None:
|
|
@@ -217,60 +231,44 @@ def test_private_helpers_recognize_dirty_legacy_header_body() -> None:
|
|
|
217
231
|
assert check_convergence._is_clean_bugteam_review(DIRTY_LEGACY_BUGTEAM_BODY) is False
|
|
218
232
|
|
|
219
233
|
|
|
220
|
-
def should_resolve_bugbot_down_true_when_flag_set(
|
|
221
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
222
|
-
) -> None:
|
|
234
|
+
def should_resolve_bugbot_down_true_when_flag_set(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
223
235
|
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
224
236
|
assert check_convergence._resolve_bugbot_down(True) is True
|
|
225
237
|
|
|
226
238
|
|
|
227
|
-
def should_resolve_bugbot_down_true_when_env_disables_bugbot(
|
|
228
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
229
|
-
) -> None:
|
|
239
|
+
def should_resolve_bugbot_down_true_when_env_disables_bugbot(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
230
240
|
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugbot")
|
|
231
241
|
assert check_convergence._resolve_bugbot_down(False) is True
|
|
232
242
|
|
|
233
243
|
|
|
234
|
-
def should_resolve_bugbot_down_true_when_flag_unset_and_env_empty(
|
|
235
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
236
|
-
) -> None:
|
|
244
|
+
def should_resolve_bugbot_down_true_when_flag_unset_and_env_empty(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
237
245
|
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
238
246
|
monkeypatch.delenv("CLAUDE_REVIEWS_ENABLED", raising=False)
|
|
239
247
|
assert check_convergence._resolve_bugbot_down(False) is True
|
|
240
248
|
|
|
241
249
|
|
|
242
|
-
def should_resolve_bugbot_down_false_when_enabled_lists_bugbot(
|
|
243
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
244
|
-
) -> None:
|
|
250
|
+
def should_resolve_bugbot_down_false_when_enabled_lists_bugbot(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
245
251
|
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
246
252
|
monkeypatch.setenv("CLAUDE_REVIEWS_ENABLED", "bugbot")
|
|
247
253
|
assert check_convergence._resolve_bugbot_down(False) is False
|
|
248
254
|
|
|
249
255
|
|
|
250
|
-
def should_resolve_copilot_down_true_when_flag_set(
|
|
251
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
252
|
-
) -> None:
|
|
256
|
+
def should_resolve_copilot_down_true_when_flag_set(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
253
257
|
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
254
258
|
assert check_convergence._resolve_copilot_down(True) is True
|
|
255
259
|
|
|
256
260
|
|
|
257
|
-
def should_resolve_copilot_down_true_when_env_disables_copilot(
|
|
258
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
259
|
-
) -> None:
|
|
261
|
+
def should_resolve_copilot_down_true_when_env_disables_copilot(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
260
262
|
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "copilot")
|
|
261
263
|
assert check_convergence._resolve_copilot_down(False) is True
|
|
262
264
|
|
|
263
265
|
|
|
264
|
-
def should_resolve_copilot_down_false_when_flag_unset_and_env_empty(
|
|
265
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
266
|
-
) -> None:
|
|
266
|
+
def should_resolve_copilot_down_false_when_flag_unset_and_env_empty(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
267
267
|
monkeypatch.delenv("CLAUDE_REVIEWS_DISABLED", raising=False)
|
|
268
268
|
assert check_convergence._resolve_copilot_down(False) is False
|
|
269
269
|
|
|
270
270
|
|
|
271
|
-
def should_resolve_copilot_down_false_when_env_disables_only_bugbot(
|
|
272
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
273
|
-
) -> None:
|
|
271
|
+
def should_resolve_copilot_down_false_when_env_disables_only_bugbot(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
274
272
|
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugbot")
|
|
275
273
|
assert check_convergence._resolve_copilot_down(False) is False
|
|
276
274
|
|
|
@@ -278,176 +276,30 @@ def should_resolve_copilot_down_false_when_env_disables_only_bugbot(
|
|
|
278
276
|
def should_bypass_bugbot_gates_when_bugbot_down_is_true(
|
|
279
277
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
280
278
|
) -> None:
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
def stub_get_pr_head_sha(*, owner: str, repo: str, number: int) -> str:
|
|
284
|
-
all_invocation_names.append("_get_pr_head_sha")
|
|
285
|
-
return CURRENT_HEAD_SHA
|
|
286
|
-
|
|
287
|
-
def stub_check_bugbot_should_not_be_called(
|
|
288
|
-
*, owner: str, repo: str, sha: str
|
|
289
|
-
) -> tuple[bool, str]:
|
|
290
|
-
all_invocation_names.append("_check_bugbot")
|
|
291
|
-
raise AssertionError("_check_bugbot must not be invoked when bugbot_down=True")
|
|
292
|
-
|
|
293
|
-
def stub_check_bugbot_not_dirty_should_not_be_called(
|
|
294
|
-
*, owner: str, repo: str, number: int, head_sha: str
|
|
295
|
-
) -> tuple[bool, str]:
|
|
296
|
-
all_invocation_names.append("_check_bugbot_not_dirty")
|
|
297
|
-
raise AssertionError(
|
|
298
|
-
"_check_bugbot_not_dirty must not be invoked when bugbot_down=True"
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
def stub_check_bugteam_clean(
|
|
302
|
-
*, owner: str, repo: str, number: int, head_sha: str
|
|
303
|
-
) -> tuple[bool, str]:
|
|
304
|
-
all_invocation_names.append("_check_bugteam_clean")
|
|
305
|
-
return True, "stub passing"
|
|
306
|
-
|
|
307
|
-
def stub_check_bot_review(
|
|
308
|
-
*,
|
|
309
|
-
owner: str,
|
|
310
|
-
repo: str,
|
|
311
|
-
number: int,
|
|
312
|
-
head_sha: str,
|
|
313
|
-
login_substring: str,
|
|
314
|
-
clean_states: tuple[str, ...],
|
|
315
|
-
dirty_states: tuple[str, ...],
|
|
316
|
-
label: str,
|
|
317
|
-
) -> tuple[bool, str]:
|
|
318
|
-
all_invocation_names.append("_check_bot_review")
|
|
319
|
-
return True, "stub passing"
|
|
320
|
-
|
|
321
|
-
def stub_count_unresolved_bot_threads(
|
|
322
|
-
*, owner: str, repo: str, number: int
|
|
323
|
-
) -> tuple[bool, str]:
|
|
324
|
-
all_invocation_names.append("_count_unresolved_bot_threads")
|
|
325
|
-
return True, "stub passing"
|
|
326
|
-
|
|
327
|
-
def stub_get_mergeable(
|
|
328
|
-
*, owner: str, repo: str, number: int
|
|
329
|
-
) -> tuple[bool, str]:
|
|
330
|
-
all_invocation_names.append("_get_mergeable")
|
|
331
|
-
return True, "stub passing"
|
|
332
|
-
|
|
333
|
-
def stub_check_no_pending_reviews(
|
|
334
|
-
*, owner: str, repo: str, number: int
|
|
335
|
-
) -> tuple[bool, str]:
|
|
336
|
-
all_invocation_names.append("_check_no_pending_reviews")
|
|
337
|
-
return True, "stub passing"
|
|
338
|
-
|
|
339
|
-
monkeypatch.setattr(check_convergence, "_get_pr_head_sha", stub_get_pr_head_sha)
|
|
340
|
-
monkeypatch.setattr(check_convergence, "_check_bugbot", stub_check_bugbot_should_not_be_called)
|
|
279
|
+
_patch_all_gates_passing(monkeypatch)
|
|
280
|
+
monkeypatch.setattr(check_convergence, "_check_bugbot", _raise_if_called("_check_bugbot"))
|
|
341
281
|
monkeypatch.setattr(
|
|
342
|
-
check_convergence,
|
|
343
|
-
"_check_bugbot_not_dirty",
|
|
344
|
-
stub_check_bugbot_not_dirty_should_not_be_called,
|
|
282
|
+
check_convergence, "_check_bugbot_not_dirty", _raise_if_called("_check_bugbot_not_dirty")
|
|
345
283
|
)
|
|
346
|
-
monkeypatch.setattr(check_convergence, "_check_bugteam_clean", stub_check_bugteam_clean)
|
|
347
|
-
monkeypatch.setattr(check_convergence, "_check_bot_review", stub_check_bot_review)
|
|
348
|
-
monkeypatch.setattr(
|
|
349
|
-
check_convergence, "_count_unresolved_bot_threads", stub_count_unresolved_bot_threads
|
|
350
|
-
)
|
|
351
|
-
monkeypatch.setattr(check_convergence, "_get_mergeable", stub_get_mergeable)
|
|
352
|
-
monkeypatch.setattr(
|
|
353
|
-
check_convergence, "_check_no_pending_reviews", stub_check_no_pending_reviews
|
|
354
|
-
)
|
|
355
|
-
|
|
356
284
|
exit_code = check_convergence.check_all(
|
|
357
285
|
owner="o", repo="r", number=1, is_bugbot_down=True, is_copilot_down=False
|
|
358
286
|
)
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
assert "_check_bugbot" not in all_invocation_names
|
|
362
|
-
assert "_check_bugbot_not_dirty" not in all_invocation_names
|
|
363
|
-
assert "bypassed (bugbot_down)" in captured_stdout
|
|
287
|
+
assert "bypassed (bugbot_down)" in capsys.readouterr().out
|
|
364
288
|
assert exit_code == 0
|
|
365
289
|
|
|
366
290
|
|
|
367
291
|
def should_bypass_copilot_gates_when_copilot_down_is_true(
|
|
368
292
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
369
293
|
) -> None:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
def stub_get_pr_head_sha(*, owner: str, repo: str, number: int) -> str:
|
|
373
|
-
return CURRENT_HEAD_SHA
|
|
374
|
-
|
|
375
|
-
def stub_check_bugbot(*, owner: str, repo: str, sha: str) -> tuple[bool, str]:
|
|
376
|
-
return True, "stub passing"
|
|
377
|
-
|
|
378
|
-
def stub_check_bugbot_not_dirty(
|
|
379
|
-
*, owner: str, repo: str, number: int, head_sha: str
|
|
380
|
-
) -> tuple[bool, str]:
|
|
381
|
-
return True, "stub passing"
|
|
382
|
-
|
|
383
|
-
def stub_check_bugteam_clean(
|
|
384
|
-
*, owner: str, repo: str, number: int, head_sha: str
|
|
385
|
-
) -> tuple[bool, str]:
|
|
386
|
-
return True, "stub passing"
|
|
387
|
-
|
|
388
|
-
def stub_check_bot_review_should_not_be_called(
|
|
389
|
-
*,
|
|
390
|
-
owner: str,
|
|
391
|
-
repo: str,
|
|
392
|
-
number: int,
|
|
393
|
-
head_sha: str,
|
|
394
|
-
login_substring: str,
|
|
395
|
-
clean_states: tuple[str, ...],
|
|
396
|
-
dirty_states: tuple[str, ...],
|
|
397
|
-
label: str,
|
|
398
|
-
) -> tuple[bool, str]:
|
|
399
|
-
all_invocation_names.append("_check_bot_review")
|
|
400
|
-
raise AssertionError(
|
|
401
|
-
"_check_bot_review must not be invoked when copilot_down=True"
|
|
402
|
-
)
|
|
403
|
-
|
|
404
|
-
def stub_count_unresolved_bot_threads(
|
|
405
|
-
*, owner: str, repo: str, number: int
|
|
406
|
-
) -> tuple[bool, str]:
|
|
407
|
-
return True, "stub passing"
|
|
408
|
-
|
|
409
|
-
def stub_get_mergeable(
|
|
410
|
-
*, owner: str, repo: str, number: int
|
|
411
|
-
) -> tuple[bool, str]:
|
|
412
|
-
return True, "stub passing"
|
|
413
|
-
|
|
414
|
-
def stub_check_no_pending_reviews_should_not_be_called(
|
|
415
|
-
*, owner: str, repo: str, number: int
|
|
416
|
-
) -> tuple[bool, str]:
|
|
417
|
-
all_invocation_names.append("_check_no_pending_reviews")
|
|
418
|
-
raise AssertionError(
|
|
419
|
-
"_check_no_pending_reviews must not be invoked when copilot_down=True"
|
|
420
|
-
)
|
|
421
|
-
|
|
422
|
-
monkeypatch.setattr(check_convergence, "_get_pr_head_sha", stub_get_pr_head_sha)
|
|
423
|
-
monkeypatch.setattr(check_convergence, "_check_bugbot", stub_check_bugbot)
|
|
294
|
+
_patch_all_gates_passing(monkeypatch)
|
|
295
|
+
monkeypatch.setattr(check_convergence, "_check_bot_review", _raise_if_called("_check_bot_review"))
|
|
424
296
|
monkeypatch.setattr(
|
|
425
|
-
check_convergence, "
|
|
297
|
+
check_convergence, "_check_no_pending_reviews", _raise_if_called("_check_no_pending_reviews")
|
|
426
298
|
)
|
|
427
|
-
monkeypatch.setattr(check_convergence, "_check_bugteam_clean", stub_check_bugteam_clean)
|
|
428
|
-
monkeypatch.setattr(
|
|
429
|
-
check_convergence,
|
|
430
|
-
"_check_bot_review",
|
|
431
|
-
stub_check_bot_review_should_not_be_called,
|
|
432
|
-
)
|
|
433
|
-
monkeypatch.setattr(
|
|
434
|
-
check_convergence, "_count_unresolved_bot_threads", stub_count_unresolved_bot_threads
|
|
435
|
-
)
|
|
436
|
-
monkeypatch.setattr(check_convergence, "_get_mergeable", stub_get_mergeable)
|
|
437
|
-
monkeypatch.setattr(
|
|
438
|
-
check_convergence,
|
|
439
|
-
"_check_no_pending_reviews",
|
|
440
|
-
stub_check_no_pending_reviews_should_not_be_called,
|
|
441
|
-
)
|
|
442
|
-
|
|
443
299
|
exit_code = check_convergence.check_all(
|
|
444
300
|
owner="o", repo="r", number=1, is_bugbot_down=False, is_copilot_down=True
|
|
445
301
|
)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
assert "_check_bot_review" not in all_invocation_names
|
|
449
|
-
assert "_check_no_pending_reviews" not in all_invocation_names
|
|
450
|
-
assert "bypassed (copilot_down)" in captured_stdout
|
|
302
|
+
assert "bypassed (copilot_down)" in capsys.readouterr().out
|
|
451
303
|
assert exit_code == 0
|
|
452
304
|
|
|
453
305
|
|
|
@@ -480,24 +332,18 @@ def should_return_one_from_print_conditions_when_any_condition_fails(
|
|
|
480
332
|
assert "One or more pre-conditions not met" in captured_stdout
|
|
481
333
|
|
|
482
334
|
|
|
483
|
-
def should_propagate_systemexit_from_get_pr_head_sha(
|
|
484
|
-
|
|
485
|
-
)
|
|
486
|
-
def stub_get_pr_head_sha_raising_systemexit(
|
|
487
|
-
*, owner: str, repo: str, number: int
|
|
488
|
-
) -> str:
|
|
489
|
-
raise SystemExit(check_convergence.EXIT_CODE_GH_ERROR)
|
|
335
|
+
def should_propagate_systemexit_from_get_pr_head_sha(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
336
|
+
def stub_get_pr_head_sha_raising_systemexit(**_call_keywords: object) -> str:
|
|
337
|
+
raise SystemExit(EXIT_CODE_GH_ERROR)
|
|
490
338
|
|
|
491
339
|
monkeypatch.setattr(
|
|
492
340
|
check_convergence, "_get_pr_head_sha", stub_get_pr_head_sha_raising_systemexit
|
|
493
341
|
)
|
|
494
|
-
|
|
495
342
|
with pytest.raises(SystemExit) as exc_info:
|
|
496
343
|
check_convergence.check_all(
|
|
497
344
|
owner="o", repo="r", number=1, is_bugbot_down=False, is_copilot_down=False
|
|
498
345
|
)
|
|
499
|
-
|
|
500
|
-
assert exc_info.value.code == check_convergence.EXIT_CODE_GH_ERROR
|
|
346
|
+
assert exc_info.value.code == EXIT_CODE_GH_ERROR
|
|
501
347
|
|
|
502
348
|
|
|
503
349
|
def should_derive_copilot_down_from_env_when_main_omits_the_flag(
|
|
@@ -513,13 +359,12 @@ def should_derive_copilot_down_from_env_when_main_omits_the_flag(
|
|
|
513
359
|
number: int,
|
|
514
360
|
is_bugbot_down: bool,
|
|
515
361
|
is_copilot_down: bool,
|
|
362
|
+
is_bugteam_post_blocked: bool = False,
|
|
516
363
|
) -> int:
|
|
517
364
|
captured_copilot_down.append(is_copilot_down)
|
|
518
365
|
return 0
|
|
519
366
|
|
|
520
367
|
monkeypatch.setattr(check_convergence, "check_all", stub_check_all)
|
|
521
|
-
exit_code = check_convergence.main(
|
|
522
|
-
["--owner", "o", "--repo", "r", "--pr-number", "1"]
|
|
523
|
-
)
|
|
368
|
+
exit_code = check_convergence.main(["--owner", "o", "--repo", "r", "--pr-number", "1"])
|
|
524
369
|
assert exit_code == 0
|
|
525
370
|
assert captured_copilot_down == [True]
|