diff --git a/apps/api/src/agents/coordinator_agent.py b/apps/api/src/agents/coordinator_agent.py index 2422e0a2..ad21d908 100644 --- a/apps/api/src/agents/coordinator_agent.py +++ b/apps/api/src/agents/coordinator_agent.py @@ -148,9 +148,33 @@ class CoordinatorAgent(BaseAgent): blocked_reason=f"Reviewer REQUEST_REVISION:{verdict.reason}", ) - # ── 3. Critic critical challenge → 信心懲罰 ───────────────────── + # ── 3. Critic REJECT(critical challenge)→ 硬閘強制人工 ───────── + # 驗證發現:penalty 策略(0.82-0.30=0.52)仍可穿透 0.4 閾值 + # Critic 投 REJECT 代表「這個決策不能執行」,應等同 Reviewer REJECT 效力 + if critic.vote == AgentVote.REJECT: + top_challenge = critic.challenges[0] if critic.challenges else None + return DecisionPackage( + recommended_action=selected.action if selected else None, + confidence=base_confidence, + requires_human_approval=True, + debate_summary=_build_summary(diagnosis, plan, verdict, critic), + session_status=AgentSessionStatus.COMPLETED, + latency_ms=0, + diagnosis=diagnosis, + action_plan=plan, + reviewer_verdict=verdict, + critic_report=critic, + blocked_reason=( + f"Critic REJECT:{top_challenge.argument[:100]}" + if top_challenge else "Critic 強烈反對此方案" + ), + ) + + # ── 3.5 Critic major/minor challenge → 信心懲罰(軟降,不強制人工) adjusted_confidence = base_confidence if critic.has_critical_challenge: + # has_critical_challenge 為 True 但 vote != REJECT 理論上不應發生 + # 保留 penalty 作為 defense-in-depth adjusted_confidence = max(0.0, base_confidence - CRITIC_PENALTY) logger.info( "coordinator_critic_penalty", diff --git a/apps/api/src/agents/reviewer_agent.py b/apps/api/src/agents/reviewer_agent.py index 59aad587..6437c243 100644 --- a/apps/api/src/agents/reviewer_agent.py +++ b/apps/api/src/agents/reviewer_agent.py @@ -57,7 +57,8 @@ _HARD_BLOCK_PATTERNS = [ re.compile(r"\bDROP\s+TABLE\b", re.IGNORECASE), re.compile(r"\bDELETE\s+FROM\b(?!.*\bWHERE\b)", re.IGNORECASE | re.DOTALL), # Gate 2: lookahead 必須在 FROM 後而非 .* 後 re.compile(r"rm\s+-rf\s+/", re.IGNORECASE), - re.compile(r"force.{0,5}push.{0,20}main", re.IGNORECASE), + # Gate 2 驗證修正:git push --force 是 "push" 先、"--force/-f" 後,需同時覆蓋兩種順序 + re.compile(r"(?:force.{0,5}push|push.{0,30}(?:--force|-f\b)).{0,30}main", re.IGNORECASE), ]