claude-dev-env 1.95.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_shared/advisor/CLAUDE.md +2 -2
- package/_shared/advisor/advisor-protocol.md +20 -20
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +15 -12
- package/_shared/advisor/scripts/model_tier_run_validator.py +11 -10
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +25 -19
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +17 -17
- package/_shared/advisor/scripts/tier_model_ids.py +18 -18
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +2 -1
- package/_shared/pr-loop/scripts/README.md +1 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +253 -1980
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +32 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/__init__.py +7 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +268 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/enforcer_loading.py +172 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_arguments.py +70 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +326 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +85 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +331 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +369 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/conftest.py +14 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_added_line_maps.py +118 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_enforcer_loading.py +17 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_arguments.py +29 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +99 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_blob_readers.py +69 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_file_sets.py +137 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +116 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_violation_scoping.py +75 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_wrapper_plumb_check.py +49 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/violation_scoping.py +328 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/wrapper_plumb_check.py +206 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +24 -17
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +1 -0
- package/_shared/pr-loop/scripts/reviews_disabled.py +19 -2
- package/_shared/pr-loop/scripts/test_code_rules_gate.py +278 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +6 -39
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +43 -0
- package/_shared/pr-loop/worker-spawn.md +186 -0
- package/agents/code-verifier.md +1 -1
- package/bin/ever-shipped-skills.mjs +3 -0
- package/bin/expand_home_directory_tokens.mjs +1 -1
- package/bin/install.mjs +5 -2
- package/hooks/advisory/refactor_guard.py +3 -4
- package/hooks/blocking/CLAUDE.md +7 -1
- package/hooks/blocking/block_main_commit.py +2 -2
- package/hooks/blocking/claude_md_orphan_file_blocker.py +75 -699
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +28 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/orphan_blocker_constants.py +18 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/decision.py +81 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/references.py +307 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/scan_plan.py +124 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/subtree_scan.py +179 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_decision.py +34 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_references.py +42 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_scan_plan.py +27 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_subtree_scan.py +30 -0
- package/hooks/blocking/code_rules_boolean_mustcheck.py +1 -1
- package/hooks/blocking/code_rules_mock_completeness.py +1 -1
- package/hooks/blocking/code_rules_optional_params.py +2 -2
- package/hooks/blocking/code_rules_shared.py +1 -1
- package/hooks/blocking/code_rules_test_assertions.py +1 -1
- package/hooks/blocking/code_rules_typeddict_stub.py +1 -1
- package/hooks/blocking/gh_pr_author_enforcer.py +1 -1
- package/hooks/blocking/inventory_intent_records/CLAUDE.md +26 -0
- package/hooks/blocking/inventory_intent_records/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/intent_records_constants.py +20 -0
- package/hooks/blocking/inventory_intent_records/records.py +271 -0
- package/hooks/blocking/inventory_intent_records/tests/conftest.py +10 -0
- package/hooks/blocking/inventory_intent_records/tests/test_records.py +80 -0
- package/hooks/blocking/package_inventory_stale_blocker.py +54 -384
- package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +26 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/inventory_blocker_constants.py +16 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/decision.py +84 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/inventory_detection.py +307 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_decision.py +38 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_inventory_detection.py +61 -0
- package/hooks/blocking/pii_payload_scan.py +138 -42
- package/hooks/blocking/pii_prevention_blocker.py +185 -291
- package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +24 -0
- package/hooks/blocking/pii_prevention_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/repository_resolution_constants.py +28 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +214 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_resolution.py +208 -0
- package/hooks/blocking/pr_description_command_parser.py +8 -4
- package/hooks/blocking/precommit_code_rules_gate.py +3 -3
- package/hooks/blocking/tdd_enforcer.py +97 -608
- package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +30 -0
- package/hooks/blocking/tdd_enforcer_parts/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/candidate_paths.py +142 -0
- package/hooks/blocking/tdd_enforcer_parts/config/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/config/tdd_enforcer_constants.py +32 -0
- package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +268 -0
- package/hooks/blocking/tdd_enforcer_parts/decisions.py +92 -0
- package/hooks/blocking/tdd_enforcer_parts/freshness.py +80 -0
- package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +63 -0
- package/hooks/blocking/tdd_enforcer_parts/path_classification.py +119 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/conftest.py +10 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_candidate_paths.py +31 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +30 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_decisions.py +34 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_freshness.py +28 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_git_tracking.py +48 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_path_classification.py +36 -0
- package/hooks/blocking/test_inventory_deadlock_resolution.py +154 -0
- package/hooks/blocking/test_pii_payload_scan.py +168 -0
- package/hooks/blocking/test_tdd_enforcer_restore.py +108 -0
- package/hooks/blocking/test_verifier_verdict_minter.py +55 -158
- package/hooks/blocking/tests/conftest.py +10 -0
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +260 -0
- package/hooks/blocking/tests/test_repository_exemption.py +105 -0
- package/hooks/blocking/tests/test_repository_resolution.py +108 -0
- package/hooks/diagnostic/hook_log_extractor.py +12 -10
- package/hooks/git-hooks/post_commit.py +3 -4
- package/hooks/hooks_constants/CLAUDE.md +2 -2
- package/hooks/hooks_constants/banned_identifiers_constants.py +0 -1
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
- package/hooks/hooks_constants/local_identity.py +59 -8
- package/hooks/hooks_constants/pii_prevention_constants.py +0 -6
- package/hooks/hooks_constants/test_local_identity.py +105 -3
- package/hooks/pyproject.toml +13 -36
- package/hooks/session/plugin_data_dir_cleanup.py +0 -1
- package/hooks/validation/mypy_validator.py +2 -2
- package/hooks/validators/health_check.py +1 -0
- package/hooks/validators/mypy_integration.py +2 -0
- package/hooks/validators/ruff_integration.py +3 -0
- package/hooks/workflow/auto_formatter.py +5 -4
- package/package.json +1 -1
- package/scripts/CLAUDE.md +4 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +6 -4
- package/scripts/dev_env_scripts_constants/code_review_constants.py +71 -0
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +435 -0
- package/scripts/dev_env_scripts_constants/timing.py +7 -1
- package/scripts/grok_headless_runner.py +294 -0
- package/scripts/grok_worker_preflight.py +410 -0
- package/scripts/invoke_code_review.py +463 -0
- package/scripts/resolve_worker_spawn.py +619 -0
- package/scripts/spawn_grok_batch.py +672 -0
- package/scripts/test_grok_headless_runner.py +626 -0
- package/scripts/test_grok_worker_preflight.py +1054 -0
- package/scripts/test_invoke_code_review.py +672 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -0
- package/scripts/test_spawn_grok_batch.py +1017 -0
- package/skills/CLAUDE.md +5 -3
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +72 -13
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +121 -14
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +78 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +121 -0
- package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +196 -6
- package/skills/autoconverge/CLAUDE.md +3 -3
- package/skills/autoconverge/SKILL.md +9 -3
- package/skills/autoconverge/reference/CLAUDE.md +2 -2
- package/skills/autoconverge/reference/convergence.md +33 -11
- package/skills/autoconverge/reference/stop-conditions.md +16 -5
- package/skills/autoconverge/workflow/CLAUDE.md +2 -1
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +7 -2
- package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +300 -0
- package/skills/autoconverge/workflow/converge.contract.test.mjs +5 -5
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +29 -29
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +200 -16
- package/skills/bugteam/CLAUDE.md +2 -2
- package/skills/bugteam/CONSTRAINTS.md +3 -2
- package/skills/bugteam/PROMPTS.md +7 -6
- package/skills/bugteam/SKILL.md +18 -13
- package/skills/bugteam/reference/audit-and-teammates.md +215 -35
- package/skills/bugteam/reference/design-rationale.md +1 -1
- package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -1
- package/skills/bugteam/reference/team-setup.md +8 -2
- package/skills/codex-review/CLAUDE.md +46 -0
- package/skills/codex-review/SKILL.md +181 -0
- package/skills/codex-review/reference/CLAUDE.md +15 -0
- package/skills/codex-review/reference/cli-contract.md +253 -0
- package/skills/codex-review/reference/loop-integration.md +118 -0
- package/skills/codex-review/scripts/codex_down_classifier.py +98 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/__init__.py +1 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +86 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +45 -0
- package/skills/codex-review/scripts/codex_usage_probe.py +573 -0
- package/skills/codex-review/scripts/fixtures/auth_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
- package/skills/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
- package/skills/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
- package/skills/codex-review/scripts/fixtures/structured_findings.txt +13 -0
- package/skills/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
- package/skills/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/usage_limit_synthetic.txt +1 -0
- package/skills/codex-review/scripts/parse_codex_findings.py +207 -0
- package/skills/codex-review/scripts/run_codex_review.py +415 -0
- package/skills/codex-review/scripts/test_codex_down_classifier.py +143 -0
- package/skills/codex-review/scripts/test_codex_usage_probe.py +678 -0
- package/skills/codex-review/scripts/test_parse_codex_findings.py +130 -0
- package/skills/codex-review/scripts/test_run_codex_review.py +812 -0
- package/skills/codex-review/test_skill_scaffold.py +192 -0
- package/skills/grok-spawn/CLAUDE.md +28 -0
- package/skills/grok-spawn/SKILL.md +226 -0
- package/skills/grok-spawn/reference/flag-profiles.md +132 -0
- package/skills/grok-spawn/reference/worker-briefs.md +152 -0
- package/skills/grokify/SKILL.md +9 -1
- package/skills/grokify/capability-claims.test.mjs +28 -0
- package/skills/grokify/evals/README.md +72 -0
- package/skills/grokify/evals/parse-payload.test.mjs +171 -0
- package/skills/grokify/evals/run-capability-evals.mjs +545 -0
- package/skills/orchestrator/SKILL.md +42 -29
- package/skills/orchestrator-refresh/SKILL.md +17 -9
- package/skills/pr-converge/SKILL.md +34 -13
- package/skills/pr-converge/reference/convergence-gates.md +42 -15
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +1 -1
- package/skills/pr-converge/reference/per-tick.md +130 -42
- package/skills/pr-converge/reference/state-schema.md +10 -0
- package/skills/pr-converge/scripts/CLAUDE.md +2 -0
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +5 -1
- package/skills/pr-converge/scripts/check_convergence.py +605 -29
- package/skills/pr-converge/scripts/check_convergence_availability.py +232 -0
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -235
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +1 -1
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -2
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +4 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +71 -3
- package/skills/pr-converge/scripts/test_check_convergence_availability.py +326 -0
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -0
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +89 -17
- package/skills/pr-converge/scripts/test_check_convergence_fixture.py +179 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -68
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +24 -0
- package/skills/pr-converge/test_step5_host_branch.py +106 -0
- package/skills/pr-loop-cloud-transport/SKILL.md +2 -0
- package/skills/reviewer-gates/SKILL.md +7 -5
- package/skills/team-advisor/SKILL.md +7 -7
|
@@ -8,10 +8,23 @@ import sys
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from types import ModuleType
|
|
10
10
|
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
11
13
|
_SCRIPTS_DIR = Path(__file__).resolve().parent
|
|
12
14
|
if str(_SCRIPTS_DIR) not in sys.path:
|
|
13
15
|
sys.path.insert(0, str(_SCRIPTS_DIR))
|
|
14
16
|
|
|
17
|
+
from skills_pr_loop_constants.path_resolver_constants import (
|
|
18
|
+
FINDINGS_JSON_MUST_BE_LIST_MESSAGE,
|
|
19
|
+
FIX_COMMENT_POSTING_AGENT_TEXT,
|
|
20
|
+
FIX_COMMENT_POSTING_HEADLESS_TEXT,
|
|
21
|
+
FIX_OUTCOME_XML_TEMPLATE,
|
|
22
|
+
FIX_OUTPUT_FORMAT_AGENT_TEXT,
|
|
23
|
+
FIX_OUTPUT_FORMAT_HEADLESS_TEMPLATE,
|
|
24
|
+
FIX_PROMPT_FLAVOR_AGENT,
|
|
25
|
+
FIX_PROMPT_FLAVOR_HEADLESS,
|
|
26
|
+
)
|
|
27
|
+
|
|
15
28
|
|
|
16
29
|
def _load_build_fix_prompt() -> ModuleType:
|
|
17
30
|
module_path = _SCRIPTS_DIR / "build_fix_prompt.py"
|
|
@@ -27,12 +40,17 @@ def _load_build_fix_prompt() -> ModuleType:
|
|
|
27
40
|
build_fix_prompt = _load_build_fix_prompt()
|
|
28
41
|
|
|
29
42
|
|
|
30
|
-
def
|
|
43
|
+
def _write_findings_json(tmp_path: Path) -> Path:
|
|
31
44
|
findings_json_path = tmp_path / "findings.json"
|
|
32
45
|
findings_json_path.write_text(
|
|
33
46
|
json.dumps([{"severity": "P1", "file": "a.py", "line": 1}]),
|
|
34
47
|
encoding="utf-8",
|
|
35
48
|
)
|
|
49
|
+
return findings_json_path
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_context_worktree_path_renders_with_forward_slashes(tmp_path: Path) -> None:
|
|
53
|
+
findings_json_path = _write_findings_json(tmp_path)
|
|
36
54
|
root = build_fix_prompt.build_fix_prompt_xml(
|
|
37
55
|
owner="jl-cmd",
|
|
38
56
|
repo="claude-dev-env",
|
|
@@ -50,11 +68,7 @@ def test_context_worktree_path_renders_with_forward_slashes(tmp_path: Path) -> N
|
|
|
50
68
|
|
|
51
69
|
|
|
52
70
|
def test_emit_fix_prompt_returns_xml_string_with_worktree_path(tmp_path: Path) -> None:
|
|
53
|
-
findings_json_path = tmp_path
|
|
54
|
-
findings_json_path.write_text(
|
|
55
|
-
json.dumps([{"severity": "P1", "file": "a.py", "line": 1}]),
|
|
56
|
-
encoding="utf-8",
|
|
57
|
-
)
|
|
71
|
+
findings_json_path = _write_findings_json(tmp_path)
|
|
58
72
|
worktree_path = Path("C:/Users/example/AppData/Local/Temp/bugteam-pr-376/worktree")
|
|
59
73
|
xml_text = build_fix_prompt.emit_fix_prompt(
|
|
60
74
|
owner="jl-cmd",
|
|
@@ -70,3 +84,179 @@ def test_emit_fix_prompt_returns_xml_string_with_worktree_path(tmp_path: Path) -
|
|
|
70
84
|
assert "C:/Users/example/AppData/Local/Temp/bugteam-pr-376/worktree" in xml_text
|
|
71
85
|
assert 'role="fix"' in xml_text
|
|
72
86
|
assert "<spawn_prompt" in xml_text
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_parse_arguments_defaults_flavor_to_agent() -> None:
|
|
90
|
+
arguments = build_fix_prompt.parse_arguments([
|
|
91
|
+
"--owner", "jl-cmd",
|
|
92
|
+
"--repo", "claude-dev-env",
|
|
93
|
+
"--pr-number", "422",
|
|
94
|
+
"--loop", "1",
|
|
95
|
+
"--head-ref", "feat/branch",
|
|
96
|
+
"--base-ref", "main",
|
|
97
|
+
"--worktree-path", "/tmp/wt",
|
|
98
|
+
"--findings-json", "/tmp/findings.json",
|
|
99
|
+
])
|
|
100
|
+
assert arguments.flavor == FIX_PROMPT_FLAVOR_AGENT
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_parse_arguments_reads_headless_flavor() -> None:
|
|
104
|
+
arguments = build_fix_prompt.parse_arguments([
|
|
105
|
+
"--owner", "jl-cmd",
|
|
106
|
+
"--repo", "claude-dev-env",
|
|
107
|
+
"--pr-number", "422",
|
|
108
|
+
"--loop", "1",
|
|
109
|
+
"--head-ref", "feat/branch",
|
|
110
|
+
"--base-ref", "main",
|
|
111
|
+
"--worktree-path", "/tmp/wt",
|
|
112
|
+
"--findings-json", "/tmp/findings.json",
|
|
113
|
+
"--flavor", FIX_PROMPT_FLAVOR_HEADLESS,
|
|
114
|
+
])
|
|
115
|
+
assert arguments.flavor == FIX_PROMPT_FLAVOR_HEADLESS
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_agent_flavor_comment_posting_and_output_format(tmp_path: Path) -> None:
|
|
119
|
+
findings_json_path = _write_findings_json(tmp_path)
|
|
120
|
+
root = build_fix_prompt.build_fix_prompt_xml(
|
|
121
|
+
owner="jl-cmd",
|
|
122
|
+
repo="claude-dev-env",
|
|
123
|
+
pr_number=422,
|
|
124
|
+
loop=1,
|
|
125
|
+
head_ref="feat/branch",
|
|
126
|
+
base_ref="main",
|
|
127
|
+
worktree_path=Path("/tmp/bugteam-pr-422/worktree"),
|
|
128
|
+
findings_json_path=findings_json_path,
|
|
129
|
+
flavor=FIX_PROMPT_FLAVOR_AGENT,
|
|
130
|
+
)
|
|
131
|
+
comment_posting = root.find("comment_posting")
|
|
132
|
+
assert comment_posting is not None
|
|
133
|
+
assert comment_posting.text == FIX_COMMENT_POSTING_AGENT_TEXT
|
|
134
|
+
output_format = root.find("output_format")
|
|
135
|
+
assert output_format is not None
|
|
136
|
+
assert output_format.text == FIX_OUTPUT_FORMAT_AGENT_TEXT
|
|
137
|
+
execution = root.find("execution")
|
|
138
|
+
assert execution is not None
|
|
139
|
+
all_step_texts = [each_step.text or "" for each_step in execution]
|
|
140
|
+
assert any("commit" in each_step.lower() for each_step in all_step_texts)
|
|
141
|
+
assert any("push" in each_step.lower() for each_step in all_step_texts)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_headless_flavor_forbids_commit_push_and_mcp(tmp_path: Path) -> None:
|
|
145
|
+
findings_json_path = _write_findings_json(tmp_path)
|
|
146
|
+
worktree_path = Path("/tmp/bugteam-pr-422/worktree")
|
|
147
|
+
pr_number = 422
|
|
148
|
+
loop = 3
|
|
149
|
+
root = build_fix_prompt.build_fix_prompt_xml(
|
|
150
|
+
owner="jl-cmd",
|
|
151
|
+
repo="claude-dev-env",
|
|
152
|
+
pr_number=pr_number,
|
|
153
|
+
loop=loop,
|
|
154
|
+
head_ref="feat/branch",
|
|
155
|
+
base_ref="main",
|
|
156
|
+
worktree_path=worktree_path,
|
|
157
|
+
findings_json_path=findings_json_path,
|
|
158
|
+
flavor=FIX_PROMPT_FLAVOR_HEADLESS,
|
|
159
|
+
)
|
|
160
|
+
comment_posting = root.find("comment_posting")
|
|
161
|
+
assert comment_posting is not None
|
|
162
|
+
assert comment_posting.text == FIX_COMMENT_POSTING_HEADLESS_TEXT
|
|
163
|
+
assert "MCP" not in (comment_posting.text or "")
|
|
164
|
+
assert "TaskCreate" not in (comment_posting.text or "")
|
|
165
|
+
assert "gh" in (comment_posting.text or "")
|
|
166
|
+
|
|
167
|
+
expected_outcome_path = (
|
|
168
|
+
worktree_path
|
|
169
|
+
/ FIX_OUTCOME_XML_TEMPLATE.format(number=pr_number, loop=loop)
|
|
170
|
+
).as_posix()
|
|
171
|
+
output_format = root.find("output_format")
|
|
172
|
+
assert output_format is not None
|
|
173
|
+
assert output_format.text == FIX_OUTPUT_FORMAT_HEADLESS_TEMPLATE.format(
|
|
174
|
+
outcome_path=expected_outcome_path,
|
|
175
|
+
)
|
|
176
|
+
assert expected_outcome_path in (output_format.text or "")
|
|
177
|
+
assert "TaskCreate" in (output_format.text or "")
|
|
178
|
+
assert "MCP" in (output_format.text or "")
|
|
179
|
+
|
|
180
|
+
execution = root.find("execution")
|
|
181
|
+
assert execution is not None
|
|
182
|
+
all_step_texts = [each_step.text or "" for each_step in execution]
|
|
183
|
+
joined_steps = " ".join(all_step_texts).lower()
|
|
184
|
+
assert "do not stage, commit, or push" in joined_steps
|
|
185
|
+
assert "push the commit" not in joined_steps
|
|
186
|
+
|
|
187
|
+
constraints = root.find("constraints")
|
|
188
|
+
assert constraints is not None
|
|
189
|
+
all_constraint_texts = [each_constraint.text or "" for each_constraint in constraints]
|
|
190
|
+
joined_constraints = " ".join(all_constraint_texts).lower()
|
|
191
|
+
assert "do not stage, commit, or push" in joined_constraints
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_main_headless_flavor_emits_outcome_path_on_stdout(
|
|
195
|
+
tmp_path: Path,
|
|
196
|
+
capsys: pytest.CaptureFixture[str],
|
|
197
|
+
) -> None:
|
|
198
|
+
findings_json_path = _write_findings_json(tmp_path)
|
|
199
|
+
worktree_path = "/tmp/bugteam-pr-99/worktree"
|
|
200
|
+
exit_code = build_fix_prompt.main([
|
|
201
|
+
"--owner", "jl-cmd",
|
|
202
|
+
"--repo", "claude-dev-env",
|
|
203
|
+
"--pr-number", "99",
|
|
204
|
+
"--loop", "2",
|
|
205
|
+
"--head-ref", "feat/branch",
|
|
206
|
+
"--base-ref", "main",
|
|
207
|
+
"--worktree-path", worktree_path,
|
|
208
|
+
"--findings-json", str(findings_json_path),
|
|
209
|
+
"--flavor", FIX_PROMPT_FLAVOR_HEADLESS,
|
|
210
|
+
])
|
|
211
|
+
assert exit_code == 0
|
|
212
|
+
captured = capsys.readouterr()
|
|
213
|
+
expected_outcome = (
|
|
214
|
+
Path(worktree_path)
|
|
215
|
+
/ FIX_OUTCOME_XML_TEMPLATE.format(number=99, loop=2)
|
|
216
|
+
).as_posix()
|
|
217
|
+
assert expected_outcome in captured.out
|
|
218
|
+
assert "Do not post replies" in captured.out
|
|
219
|
+
assert "Do not stage, commit, or push" in captured.out
|
|
220
|
+
assert "add_reply_to_pull_request_comment" not in captured.out
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_main_rejects_object_root_findings_json(
|
|
224
|
+
tmp_path: Path,
|
|
225
|
+
capsys: pytest.CaptureFixture[str],
|
|
226
|
+
) -> None:
|
|
227
|
+
findings_json_path = tmp_path / "findings.json"
|
|
228
|
+
findings_json_path.write_text(
|
|
229
|
+
json.dumps({"findings": [{"severity": "P1", "file": "a.py"}]}),
|
|
230
|
+
encoding="utf-8",
|
|
231
|
+
)
|
|
232
|
+
exit_code = build_fix_prompt.main([
|
|
233
|
+
"--owner", "jl-cmd",
|
|
234
|
+
"--repo", "claude-dev-env",
|
|
235
|
+
"--pr-number", "99",
|
|
236
|
+
"--loop", "1",
|
|
237
|
+
"--head-ref", "feat/branch",
|
|
238
|
+
"--base-ref", "main",
|
|
239
|
+
"--worktree-path", "/tmp/wt",
|
|
240
|
+
"--findings-json", str(findings_json_path),
|
|
241
|
+
])
|
|
242
|
+
assert exit_code == 1
|
|
243
|
+
captured = capsys.readouterr()
|
|
244
|
+
expected_message = FINDINGS_JSON_MUST_BE_LIST_MESSAGE % "dict"
|
|
245
|
+
assert expected_message in captured.err
|
|
246
|
+
assert captured.out == ""
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def test_build_fix_prompt_xml_rejects_null_root(tmp_path: Path) -> None:
|
|
250
|
+
findings_json_path = tmp_path / "findings.json"
|
|
251
|
+
findings_json_path.write_text("null", encoding="utf-8")
|
|
252
|
+
with pytest.raises(TypeError, match="JSON array"):
|
|
253
|
+
build_fix_prompt.build_fix_prompt_xml(
|
|
254
|
+
owner="jl-cmd",
|
|
255
|
+
repo="claude-dev-env",
|
|
256
|
+
pr_number=1,
|
|
257
|
+
loop=1,
|
|
258
|
+
head_ref="feat/branch",
|
|
259
|
+
base_ref="main",
|
|
260
|
+
worktree_path=Path("/tmp/wt"),
|
|
261
|
+
findings_json_path=findings_json_path,
|
|
262
|
+
)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Trigger:** `/autoconverge`, "autoconverge this PR", "converge this PR in one run", "run the converge workflow", "drive the PR to ready autonomously".
|
|
4
4
|
|
|
5
|
-
Drives one draft PR to convergence in a single autonomous workflow run. Each round runs a deterministic static sweep first, then a code-review pass, a bug-audit (with its adversarial second pass), and a self-review parity pass in parallel on the same HEAD, deduplicates findings, applies every fix in one commit, and re-verifies. Once the internal lenses are clean, Cursor Bugbot and
|
|
5
|
+
Drives one draft PR to convergence in a single autonomous workflow run. Each round runs a deterministic static sweep first, then a code-review pass, a bug-audit (with its adversarial second pass), and a self-review parity pass in parallel on the same HEAD, deduplicates findings, applies every fix in one commit, and re-verifies. Once the internal lenses are clean, Cursor Bugbot, Copilot, and Codex run as terminal confirmation gates, and the run marks the PR ready on convergence. State lives in the workflow journal; no `ScheduleWakeup` ticks.
|
|
6
6
|
|
|
7
7
|
## Subdirectories
|
|
8
8
|
|
|
@@ -16,11 +16,11 @@ Drives one draft PR to convergence in a single autonomous workflow run. Each rou
|
|
|
16
16
|
| File | Role |
|
|
17
17
|
|---|---|
|
|
18
18
|
| `SKILL.md` | Full entry-point protocol: pre-flight steps, worktree setup, `Workflow` call, budget-aware round boundaries, teardown, and the per-round convergence loop summary. |
|
|
19
|
-
| `workflow/converge.mjs` | Main convergence workflow. Runs a static sweep then the internal lenses each round, applies fixes, runs Bugbot and
|
|
19
|
+
| `workflow/converge.mjs` | Main convergence workflow. Runs a static sweep then the internal lenses each round, applies fixes, runs Bugbot, Copilot, and Codex as terminal gates, checks convergence, and marks the PR ready. |
|
|
20
20
|
| `workflow/aggregate_runs.py` | Merges every autoconverge journal for a PR into one deduped journal. |
|
|
21
21
|
| `workflow/convergence_summary.py` | Builds the convergence-summary agent prompt over the merged findings. |
|
|
22
22
|
| `workflow/render_report.py` | Builds the closing HTML report from the merged journal and summary. |
|
|
23
|
-
| `reference/convergence.md` | Round shape: the static sweep, the three parallel internal lenses, deduplication, fix commit, the terminal Bugbot and
|
|
23
|
+
| `reference/convergence.md` | Round shape: the static sweep, the three parallel internal lenses, deduplication, fix commit, the terminal Bugbot, Copilot, and Codex gates, and the ready definition. |
|
|
24
24
|
| `reference/stop-conditions.md` | Every way the run ends short of ready, including the budget stop. |
|
|
25
25
|
| `reference/gotchas.md` | Hard-won failure lessons. |
|
|
26
26
|
| `reference/closing-report.md` | The closing HTML report: data source, build steps, publishing. |
|
|
@@ -3,7 +3,7 @@ name: autoconverge
|
|
|
3
3
|
description: >-
|
|
4
4
|
Drives one draft PR to convergence in one autonomous run: a deterministic static
|
|
5
5
|
sweep, then code review, bug audit, and self-review on one HEAD, all fixes in one
|
|
6
|
-
commit, then Bugbot and
|
|
6
|
+
commit, then Bugbot, Copilot, and Codex as terminal confirmation gates before ready.
|
|
7
7
|
Use when the user says '/autoconverge', 'autoconverge this PR', 'converge this
|
|
8
8
|
PR in one run', 'run the converge workflow', or 'drive the PR to ready
|
|
9
9
|
autonomously'.
|
|
@@ -45,6 +45,12 @@ Scan the tool list at the top of this conversation for the literal string
|
|
|
45
45
|
aborting` and stop. The workflow also needs the `gh` CLI authenticated for the
|
|
46
46
|
PR's owner.
|
|
47
47
|
|
|
48
|
+
## Review-lens boundary
|
|
49
|
+
|
|
50
|
+
The code-review lens boundary (workflow agent versus the built-in `/code-review`
|
|
51
|
+
command) is defined on the **Code-review lens** bullet in
|
|
52
|
+
[`reference/convergence.md`](reference/convergence.md).
|
|
53
|
+
|
|
48
54
|
## Transport check (before any GitHub step)
|
|
49
55
|
|
|
50
56
|
Run `command -v gh`; when it succeeds, run `gh auth status`; once the PR
|
|
@@ -290,8 +296,8 @@ ready again — then run the checkpoints.
|
|
|
290
296
|
[`reference/convergence.md`](reference/convergence.md) defines the whole loop:
|
|
291
297
|
the merge-conflict pre-flight, the reuse pass and its three landing criteria,
|
|
292
298
|
the round shape (static sweep, the three parallel reading lenses, dedup, the
|
|
293
|
-
one-commit fix flow, the standards-deferral path), the terminal Bugbot
|
|
294
|
-
|
|
299
|
+
one-commit fix flow, the standards-deferral path), the terminal Bugbot, Copilot,
|
|
300
|
+
and Codex gates, the per-role model tiers, the full-diff rule, and the exact
|
|
295
301
|
ready definition `check_convergence.py` enforces.
|
|
296
302
|
[`reference/stop-conditions.md`](reference/stop-conditions.md) lists every way
|
|
297
303
|
the run ends short of ready. Hard-won failure lessons live in
|
|
@@ -6,8 +6,8 @@ Reference documentation for the `autoconverge` skill. The `converge.mjs` workflo
|
|
|
6
6
|
|
|
7
7
|
| File | Role |
|
|
8
8
|
|---|---|
|
|
9
|
-
| `convergence.md` | Round shape: the static sweep, the three parallel internal lenses (code-review, bug-audit, self-review), deduplication, the fix commit step, the terminal Bugbot and
|
|
10
|
-
| `stop-conditions.md` | Every condition that ends the run short of ready: budget cap, iteration cap, blocker exit, static-sweep stall, Bugbot and
|
|
9
|
+
| `convergence.md` | Round shape: the static sweep, the three parallel internal lenses (code-review, bug-audit, self-review), deduplication, the fix commit step, the terminal Bugbot, Copilot, and Codex gates, and the definition of a clean convergence. |
|
|
10
|
+
| `stop-conditions.md` | Every condition that ends the run short of ready: budget cap, iteration cap, blocker exit, static-sweep stall, Bugbot, Copilot, and Codex bypass. |
|
|
11
11
|
| `gotchas.md` | Hard-won lessons from failed runs: PR title validation, conflicting PRs, worktree branch lock, resumed sessions rerooting, and minter issues. |
|
|
12
12
|
| `closing-report.md` | The closing HTML convergence report the teardown step builds and publishes: data source, build steps, publishing. |
|
|
13
13
|
| `multi-pr.md` | The several-PRs path: per-PR worktrees, the `converge_multi.mjs` launch, per-PR teardown. |
|
|
@@ -40,7 +40,7 @@ landed.
|
|
|
40
40
|
|
|
41
41
|
## The round loop
|
|
42
42
|
|
|
43
|
-
The workflow holds
|
|
43
|
+
The workflow holds five states and moves between them until the PR is ready or
|
|
44
44
|
a blocker ends the run. A single iteration counter increments on every pass
|
|
45
45
|
through any phase and caps the whole run at 20 loop iterations; the round counter
|
|
46
46
|
tracks CONVERGE passes only and is never the cap. The internal lenses drive the
|
|
@@ -63,8 +63,14 @@ confirmation gates that are expected to return zero.
|
|
|
63
63
|
`origin/main...HEAD` diff. Each lens receives the preflight's changed-file
|
|
64
64
|
list and diffstat and reads only the files it needs from that list rather than
|
|
65
65
|
re-deriving the diff; each lens forms its own review judgment.
|
|
66
|
-
- **Code-review lens** — a correctness-focused review pass (`code-quality-agent`)
|
|
67
|
-
|
|
66
|
+
- **Code-review lens** — a correctness-focused review pass (`code-quality-agent`),
|
|
67
|
+
report-only workflow agent — see runCodeReviewLens in workflow/converge.mjs for its configuration.
|
|
68
|
+
The built-in `/code-review` command is a separate surface outside this
|
|
69
|
+
workflow path. Autoconverge runs only where the Workflow tool exists (the
|
|
70
|
+
skill hub Requirements sentence `autoconverge requires the Workflow tool`).
|
|
71
|
+
Harness portability of that command is a pr-converge concern: pr-converge's
|
|
72
|
+
CODE_REVIEW phase runs Claude Code's built-in `/code-review high --fix`
|
|
73
|
+
([`../../pr-converge/reference/per-tick.md`](../../pr-converge/reference/per-tick.md)).
|
|
68
74
|
- **Bug-audit lens** — the bug-audit (`code-quality-agent`) applying the
|
|
69
75
|
shared A–P rubric from `_shared/pr-loop/audit-contract.md`, then its
|
|
70
76
|
adversarial second pass, and the doc-parity, test-assertion, and
|
|
@@ -118,11 +124,26 @@ any bot threads with a deferral note, and reports the deferral in
|
|
|
118
124
|
- Request a Copilot review on HEAD (skipping a duplicate request), then poll up
|
|
119
125
|
to the configured cap, 360 seconds apart.
|
|
120
126
|
- Copilot findings → fix them and return to CONVERGE on the new HEAD.
|
|
121
|
-
- Copilot clean or approved → move to the
|
|
127
|
+
- Copilot clean or approved → move to the Codex gate.
|
|
122
128
|
- Copilot down or out of quota (an out-of-usage notice, or no review after the
|
|
123
|
-
configured cap) → log a notice and move to the
|
|
129
|
+
configured cap) → log a notice and move to the Codex gate with the Copilot gate
|
|
124
130
|
bypassed.
|
|
125
131
|
|
|
132
|
+
**CODEX** gate (conditional-required terminal confirmation):
|
|
133
|
+
|
|
134
|
+
- Runs after Bugbot and Copilot. Honors `reviews_disabled.py --reviewer codex`,
|
|
135
|
+
the weekly usage probe via `is_codex_review_required` (shared threshold
|
|
136
|
+
constant — no inline percent), and the wrapper's `codex_down` class.
|
|
137
|
+
- Opt-out token or `codex_down` → set `codexDown`, no stamp, move to the
|
|
138
|
+
convergence check with `--codex-down`.
|
|
139
|
+
- Usage at/below threshold or null → skip with no stamp; the machine checklist
|
|
140
|
+
applies the same rule.
|
|
141
|
+
- Above threshold → run the codex-review wrapper against the PR base branch.
|
|
142
|
+
Non–code-standard findings → fix and return to CONVERGE.
|
|
143
|
+
Standards-only findings → defer a follow-up, stamp `codexCleanAt`, and move to the
|
|
144
|
+
convergence check (no fix push).
|
|
145
|
+
Clean → stamp `codexCleanAt` and pass `--codex-clean-at` into the convergence check.
|
|
146
|
+
|
|
126
147
|
**Convergence check**:
|
|
127
148
|
|
|
128
149
|
- One agent runs `check_convergence.py` and, on a full pass, marks the PR ready
|
|
@@ -140,8 +161,8 @@ the strongest model only where judgment is dense:
|
|
|
140
161
|
- **opus** — the reading lenses (code-review, bug-audit, self-review,
|
|
141
162
|
reuse), the terminal Bugbot gate, and the code-editing steps that fix findings
|
|
142
163
|
(fix-edit, conflict-edit, repair-edit, standards-edit).
|
|
143
|
-
- **haiku** — the mechanical probes: preflight, the Copilot gate, the
|
|
144
|
-
CLEAN-audit post, and the convergence check.
|
|
164
|
+
- **haiku** — the mechanical probes: preflight, the Copilot gate, the Codex
|
|
165
|
+
gate, the CLEAN-audit post, and the convergence check.
|
|
145
166
|
|
|
146
167
|
## Full-diff rule
|
|
147
168
|
|
|
@@ -170,7 +191,8 @@ checklist.
|
|
|
170
191
|
## Audit-trail design
|
|
171
192
|
|
|
172
193
|
Bugbot and Copilot post their own review threads, which the fix lens replies to
|
|
173
|
-
and resolves.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
194
|
+
and resolves. Codex findings are local to the gate (no GitHub review thread) and
|
|
195
|
+
enter the same fix path with `replyToCommentId: null`. The bug-audit lens keeps
|
|
196
|
+
its findings in memory across the round and posts only the terminal CLEAN bugteam
|
|
197
|
+
review once every lens is clean on a stable HEAD — that single artifact is what
|
|
198
|
+
gate 3 reads. This keeps thread churn to the threads the bots raise themselves.
|
|
@@ -34,7 +34,7 @@ skill still runs teardown (revoke permissions, final report).
|
|
|
34
34
|
stalled HEAD. An all-stale round that makes no commit but resolves every
|
|
35
35
|
finding thread (`resolvedWithoutCommit: true` with at least one thread-bearing
|
|
36
36
|
finding) is not a stall — the run re-converges on the unchanged HEAD and
|
|
37
|
-
reaches the terminal Bugbot, Copilot, and convergence gates.
|
|
37
|
+
reaches the terminal Bugbot, Copilot, Codex, and convergence gates.
|
|
38
38
|
- **Static-sweep stalled** — the deterministic static sweep (`code_rules_gate.py
|
|
39
39
|
--base origin/main`, `ruff`, `mypy`, stem-matched `pytest`) raises gate or test
|
|
40
40
|
failures the fixer cannot clear on the current HEAD, so HEAD does not move. The
|
|
@@ -80,10 +80,21 @@ Each reviewer-down condition below skips its own convergence-check gate. The fla
|
|
|
80
80
|
- **Copilot down or out of quota** — when Copilot posts an out-of-usage notice on
|
|
81
81
|
the current HEAD (the user who requested the review reached their quota limit)
|
|
82
82
|
rather than a code review, or surfaces no review at all after the configured cap, the
|
|
83
|
-
Copilot gate returns `down: true`. The run logs a notice,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
records the bypass for the
|
|
83
|
+
Copilot gate returns `down: true`. The run logs a notice, sets `copilotDown`,
|
|
84
|
+
and proceeds to the Codex gate. The later convergence check receives
|
|
85
|
+
`--copilot-down` (the Copilot review gate and the pending-requested-reviews
|
|
86
|
+
gate bypassed), then mark-ready. `copilotNote` records the bypass for the
|
|
87
|
+
final report.
|
|
88
|
+
- **Terminal Codex gate skipped or down** — the conditional-required Codex gate
|
|
89
|
+
runs after Bugbot and Copilot. When `CLAUDE_REVIEWS_DISABLED` lists `codex`
|
|
90
|
+
(`reviews_disabled.py --reviewer codex` exit 0), the gate sets `codexDown` and
|
|
91
|
+
advances with no review. When the weekly usage probe reports `percent_left`
|
|
92
|
+
null or at/below the shared threshold (`is_codex_review_required` false), the
|
|
93
|
+
gate skips without a stamp; the convergence check applies the same rule. When
|
|
94
|
+
the wrapper classifies `codex_down`, the gate sets `codexDown` and advances.
|
|
95
|
+
A clean required run stamps `codexCleanAt` for `--codex-clean-at`. Non-code-standard
|
|
96
|
+
findings re-enter CONVERGE through the existing fix path. Standards-only findings
|
|
97
|
+
defer a follow-up, stamp `codexCleanAt`, and advance to FINALIZE (no fix push).
|
|
87
98
|
- **A lens agent dies** — when one parallel reading lens returns null (a terminal
|
|
88
99
|
agent failure), the round proceeds on the surviving lenses. A real defect it
|
|
89
100
|
would have caught surfaces in a later round or at the convergence check. A dead
|
|
@@ -6,7 +6,7 @@ Workflow scripts and report utilities for the `autoconverge` skill.
|
|
|
6
6
|
|
|
7
7
|
| File | Role |
|
|
8
8
|
|---|---|
|
|
9
|
-
| `converge.mjs` | Main convergence workflow script. Each round runs a deterministic static sweep first, then code-review, bug-audit, and self-review lenses in parallel, deduplicates findings, applies fixes, and pushes; Bugbot and
|
|
9
|
+
| `converge.mjs` | Main convergence workflow script. Each round runs a deterministic static sweep first, then code-review, bug-audit, and self-review lenses in parallel, deduplicates findings, applies fixes, and pushes; Bugbot, Copilot, and Codex then run as terminal confirmation gates before it checks convergence and marks the PR ready. Runs via the `Workflow` tool — not directly with Node. |
|
|
10
10
|
| `aggregate_runs.py` | Merges every autoconverge journal for a given PR (matched by run id) into one merged journal. Prints a JSON line with `mergedJournal`, `roundCount`, `finalSha`, and `findingCount`. |
|
|
11
11
|
| `convergence_summary.py` | Builds the convergence-summary agent prompt over the merged journal's findings. The teardown step spawns a `general-purpose` subagent on this prompt. |
|
|
12
12
|
| `render_report.py` | Builds the closing HTML insights report. Takes `--journal`, `--summary-file`, `--out`, `--pr`, `--final-sha`, and `--rounds`. Writes the HTML to `--out` and prints the output path on stdout. |
|
|
@@ -14,6 +14,7 @@ Workflow scripts and report utilities for the `autoconverge` skill.
|
|
|
14
14
|
| `converge.contract.test.mjs` | Contract tests for `converge.mjs` — verify the workflow interface and step ordering. |
|
|
15
15
|
| `converge.clean-audit.test.mjs` | Tests the clean-audit path (all lenses clean on first round). |
|
|
16
16
|
| `converge.copilot-gate.test.mjs` | Tests Copilot gate behavior (bypass on quota exhaustion). |
|
|
17
|
+
| `converge.codex-gate.test.mjs` | Tests Codex gate classification (required+clean, skip-token, skip-usage, codex_down) and phase wiring. |
|
|
17
18
|
| `converge.precatch.test.mjs` | Tests the pre-catch stage: static-sweep ordering and gate coverage, the adversarial second pass, the self-review lens, the terminal Bugbot gate, the read-only/edit preamble split, and the commit-recovery cap. |
|
|
18
19
|
| `converge.fix-progress.test.mjs` | Tests fix-progress tracking across rounds. |
|
|
19
20
|
| `converge.fix-recovery.test.mjs` | Tests recovery when a fix commit fails. |
|
|
@@ -563,7 +563,7 @@ test('the standards-only call site relays lens provenance, the deferred standard
|
|
|
563
563
|
|
|
564
564
|
test('every standardsDeferralNote call site passes the shared deferral state with no legacy argument', () => {
|
|
565
565
|
const noteCalls = convergeSource.match(/standardsNote = standardsDeferralNote\([^\n]*/g) || [];
|
|
566
|
-
assert.equal(noteCalls.length,
|
|
566
|
+
assert.equal(noteCalls.length, 4);
|
|
567
567
|
assert.equal(
|
|
568
568
|
noteCalls.filter((eachCall) => /standardsDeferralNote\(findings\.length, standardsDeferral\)/.test(eachCall)).length,
|
|
569
569
|
1,
|
|
@@ -579,6 +579,11 @@ test('every standardsDeferralNote call site passes the shared deferral state wit
|
|
|
579
579
|
1,
|
|
580
580
|
'expected the copilot call site to pass buildStandardsDeferral() inline',
|
|
581
581
|
);
|
|
582
|
+
assert.equal(
|
|
583
|
+
noteCalls.filter((eachCall) => /standardsDeferralNote\(codexOutcome\.findings\.length, buildStandardsDeferral\(\)\)/.test(eachCall)).length,
|
|
584
|
+
1,
|
|
585
|
+
'expected the codex call site to pass buildStandardsDeferral() inline',
|
|
586
|
+
);
|
|
582
587
|
for (const eachCall of noteCalls) {
|
|
583
588
|
assert.doesNotMatch(eachCall, /hardeningPrOpened|standardsOutcome|\b(?:true|false)\b/);
|
|
584
589
|
}
|
|
@@ -642,7 +647,7 @@ test('resolveCleanAuditNote records a bypass note on a refused post and resets t
|
|
|
642
647
|
test('the finalize call derives bugteamPostBlocked from a set cleanAuditNote', () => {
|
|
643
648
|
assert.match(
|
|
644
649
|
convergeSource,
|
|
645
|
-
/runConvergenceCheck\(\{ head, bugbotDown, copilotDown, bugteamPostBlocked: cleanAuditNote !== null \}\)/,
|
|
650
|
+
/runConvergenceCheck\(\{ head, bugbotDown, copilotDown, codexDown, codexCleanAt, bugteamPostBlocked: cleanAuditNote !== null \}\)/,
|
|
646
651
|
);
|
|
647
652
|
});
|
|
648
653
|
|