feat(code-review): ADR-020 全自動修復政策 — 拆掉 CRITICAL/HIGH HITL 閘門
All checks were successful
CD Pipeline / deploy (push) Successful in 2m23s

post-deploy code review pipeline 改為「任何 finding 一律觸發 AiderHeal」,
局部覆寫 ADR-012 L3 HITL(不影響 schema migration / 流量切換 /
customer-facing 廣播 / AIOps prod SSH 等其他 L3 場景)。安全網改為
Git revert + Gitea CI/CD 健康檢查 + 主開關 CODE_REVIEW_AUTO_FIX_ENABLED。

實作:
  • _ea_orchestrate / _guard_ea_decision / rule fallback 三條路徑統一為
    has_findings AND AUTO_FIX_ENABLED → auto_fix=true
  • _guard 強制 LLM 即使回 auto_fix=False 也升級為 true(核心保證)
  • CODE_REVIEW_AUTO_FIX_ENABLED 預設 false → true
  • Telegram 文案移除「需人工審查」,改顯示主開關狀態
  • action_plan status pending_review → auto_disabled(語意對齊)
  • aider_heal_executor 標頭 ADR-014 → ADR-020、補「直推 main」分支策略

文件:
  • 新增 docs/adr/ADR-020-code-review-full-autoheal.md
  • ADR-012 加 Note 行反向引用 ADR-020
  • README 索引收錄

測試:tests/test_code_review_pipeline_security.py 反轉 HITL 期望,
新增 5 case(含 LLM 降級被 guard 拒絕、LLM human_review_needed=true 被改 false)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
OoO
2026-05-02 23:44:01 +08:00
parent b5a2b09445
commit 6cad59f83e
7 changed files with 203 additions and 34 deletions

View File

@@ -17,23 +17,28 @@ def test_verify_internal_token_allows_explicit_dev_override(monkeypatch):
assert module.verify_internal_token("") is True
def test_code_review_guard_blocks_high_risk_auto_fix(monkeypatch):
def test_code_review_guard_auto_fixes_high_risk_findings(monkeypatch):
"""ADR-020CRITICAL/HIGH 不再走 HITL一律 auto_fix=true。"""
import services.code_review_pipeline_service as module
monkeypatch.setattr(module, "AUTO_FIX_ENABLED", True)
pipeline = module.CodeReviewPipeline("abcdef123456", ["services/example.py"])
pipeline.state["severity_summary"] = {"critical": 0, "high": 1, "medium": 0, "low": 0}
pipeline.state["severity_summary"] = {"critical": 1, "high": 1, "medium": 0, "low": 0}
guarded = pipeline._guard_ea_decision(
{"priority": "high", "auto_fix": True, "reasoning": "建議修復", "fix_files": ["services/example.py"]},
[{"severity": "HIGH", "file": "services/example.py"}],
{"priority": "critical", "auto_fix": True, "reasoning": "建議修復", "fix_files": ["services/example.py"]},
[
{"severity": "CRITICAL", "file": "services/example.py"},
{"severity": "HIGH", "file": "services/example.py"},
],
)
assert guarded["auto_fix"] is False
assert guarded["human_review_needed"] is True
assert guarded["auto_fix"] is True
assert guarded["human_review_needed"] is False
def test_code_review_guard_requires_auto_fix_feature_flag(monkeypatch):
def test_code_review_guard_main_switch_can_disable_auto_fix(monkeypatch):
"""ADR-020主開關 CODE_REVIEW_AUTO_FIX_ENABLED=false 可即時切斷自動修復鏈,但不再回退到 HITL。"""
import services.code_review_pipeline_service as module
monkeypatch.setattr(module, "AUTO_FIX_ENABLED", False)
@@ -46,4 +51,63 @@ def test_code_review_guard_requires_auto_fix_feature_flag(monkeypatch):
)
assert guarded["auto_fix"] is False
assert guarded["human_review_needed"] is True
# ADR-020即使主開關關掉也不再標記 human_review_needed不存在 HITL 通道)
assert guarded["human_review_needed"] is False
def test_code_review_no_findings_no_auto_fix(monkeypatch):
"""無 finding 時auto_fix=false 但不應標記為 human_review_needed。"""
import services.code_review_pipeline_service as module
monkeypatch.setattr(module, "AUTO_FIX_ENABLED", True)
pipeline = module.CodeReviewPipeline("abcdef123456", ["services/example.py"])
pipeline.state["severity_summary"] = {"critical": 0, "high": 0, "medium": 0, "low": 0}
guarded = pipeline._guard_ea_decision(
{"priority": "low", "auto_fix": False, "reasoning": "無 finding", "fix_files": []},
[],
)
assert guarded["auto_fix"] is False
assert guarded["human_review_needed"] is False
def test_guard_upgrades_llm_false_to_true_when_findings_exist(monkeypatch):
"""ADR-020 核心保證:即使 LLM EA 回傳 auto_fix=False只要有 finding 且主開關開guard 必升級為 true。"""
import services.code_review_pipeline_service as module
monkeypatch.setattr(module, "AUTO_FIX_ENABLED", True)
pipeline = module.CodeReviewPipeline("abcdef123456", ["services/example.py"])
pipeline.state["severity_summary"] = {"critical": 0, "high": 0, "medium": 1, "low": 0}
guarded = pipeline._guard_ea_decision(
{"priority": "medium", "auto_fix": False, "reasoning": "LLM 保守判斷", "fix_files": ["services/example.py"]},
[{"severity": "MEDIUM", "file": "services/example.py"}],
)
# ADR-020 不允許 LLM 把決策降級為 HITL
assert guarded["auto_fix"] is True
assert guarded["human_review_needed"] is False
def test_guard_upgrades_llm_human_review_true_to_false(monkeypatch):
"""ADR-020即使 LLM 回 human_review_needed=trueguard 也應強制改為 false。"""
import services.code_review_pipeline_service as module
monkeypatch.setattr(module, "AUTO_FIX_ENABLED", True)
pipeline = module.CodeReviewPipeline("abcdef123456", ["services/example.py"])
pipeline.state["severity_summary"] = {"critical": 1, "high": 0, "medium": 0, "low": 0}
guarded = pipeline._guard_ea_decision(
{
"priority": "critical",
"auto_fix": False,
"human_review_needed": True,
"reasoning": "LLM 想走人工審查",
"fix_files": ["services/example.py"],
},
[{"severity": "CRITICAL", "file": "services/example.py"}],
)
assert guarded["auto_fix"] is True
assert guarded["human_review_needed"] is False