Files
Agentswarm/scripts/test-queen.py
T
gongzhiyongandClaude Opus 4.8 2672a3d4b6
CI / guardrails (pull_request) Successful in 11s
CI / tests (pull_request) Failing after 11m52s
CI / tests (push) Failing after 11m53s
CI / guardrails (push) Failing after 11m53s
feat(orchestrator): SC-7 蜂后落 main — git push 最优产物(#16)
让蜂后选最优后把产物合并到产物仓 main,run 交付一份连贯产物而非 N 个碎片分支:
- create 存 git grant 引用(repo_url + secret_ref,均非明文)到 run.metadata;凭据不存
- queen.promote_to_main:从 secret_ref 现取凭据 → clone base_branch → 写最优产物 → commit → push;
  _auth_url 嵌入并 URL-encode 凭据(不入日志);best-effort 不破坏终态
- main.py 终态:winner → promote_to_main → deliverable.promoted_to_main{branch,commit_sha}
- Dockerfile.orchestrator 加 git CLI
- test-queen 扩展(auth_url 编码 + no_git_grant 分支),14 检查全过;全套契约通过

注:真实 git push e2e 需重建 orchestrator 镜像 + 部署 + gitea 产物仓验证(后续);
当前纯代码 + 单元测完成。凭据经 secret_ref 现取、不存 run、不入日志(组织规则#8)。

影响:仅 orchestrator 终态聚合;不涉及 Manager 契约/计费/发布链路。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:38:43 +08:00

83 lines
3.5 KiB
Python

"""Queen aggregation (agent_swarm#8/#12, M2).
Verifies the Queen's best-of-N selection: among the fan-out agents' candidates, the one whose impl
passes the most tests wins; selection is deterministic; honest fail-closed (unscored != zero).
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.environ.setdefault("REDIS_FAKE", "1")
os.environ.setdefault("ALLOW_MEMORY_STORE", "1")
os.environ.setdefault("OPENAI_API_KEY", "test-key")
import asyncio
from orchestrator.queen import Candidate, select_best, should_bounce, _auth_url, promote_to_main
_failures = []
def check(name, cond):
print(("PASS" if cond else "FAIL") + " - " + name)
if not cond:
_failures.append(name)
F = ["impl.py"] # non-empty impl marker
# highest pass_rate wins (best-of-N)
check("highest score wins",
select_best([Candidate("t1", "a1", F, score=40.0), Candidate("t2", "a2", F, score=90.0)]).task_id == "t2")
# scored ranks above unscored
check("scored beats unscored",
select_best([Candidate("t1", "a1", F), Candidate("t2", "a2", F, score=10.0)]).task_id == "t2")
# no impl anywhere → None (nothing to deliver)
check("no impl → None", select_best([Candidate("t1", "a1", [])]) is None)
# all unscored → deterministic fallback to first with impl
check("all unscored → first with impl",
select_best([Candidate("t1", "a1", F), Candidate("t2", "a2", F)]).task_id == "t1")
# tie on score → stable (first input order wins)
check("score tie → stable first",
select_best([Candidate("t1", "a1", F, score=100.0), Candidate("t2", "a2", F, score=100.0)]).task_id == "t1")
# tie on score, more passed wins
check("score tie → more passed wins",
select_best([Candidate("t1", "a1", F, score=100.0, passed=2, total=2),
Candidate("t2", "a2", F, score=100.0, passed=5, total=5)]).task_id == "t2")
# --- should_bounce (M3/SC-9 quality gate decision) ---
def _summary(score):
return {"winner": {"task_id": "t1", "score": score}, "candidates": [{"task_id": "t1"}]}
# below threshold + under cap → bounce
check("below bar under cap → bounce", should_bounce(_summary(40.0), 80.0, cycles=0, max_cycles=2) is True)
# meets bar → accept
check("meets bar → no bounce", should_bounce(_summary(90.0), 80.0, cycles=0, max_cycles=2) is False)
# cap reached → accept best-so-far
check("cap reached → no bounce", should_bounce(_summary(40.0), 80.0, cycles=2, max_cycles=2) is False)
# no threshold (disabled) → never bounce
check("no threshold → no bounce", should_bounce(_summary(0.0), None, cycles=0, max_cycles=2) is False)
# unscored → honest, don't bounce
check("unscored → no bounce", should_bounce(_summary(None), 80.0, cycles=0, max_cycles=2) is False)
# --- SC-7 promote_to_main ---
# _auth_url embeds + URL-encodes creds; passthrough when missing
check("auth url embeds + encodes creds", _auth_url("http://h/r.git", "u", "p@ss") == "http://u:p%40ss@h/r.git")
check("auth url passthrough w/o creds", _auth_url("http://h/r.git", None, None) == "http://h/r.git")
class _RunNoGrant:
def __init__(self):
self.metadata = {}
self.swarm_id = "s1"
# promote is a no-op (not an error) when the run has no git grant
_promo = asyncio.run(promote_to_main(_RunNoGrant(), [], "t1"))
check("promote without grant → not promoted", _promo.get("promoted") is False and _promo.get("reason") == "no_git_grant")
if _failures:
print(f"\nFAILED: {len(_failures)} check(s): {_failures}")
raise SystemExit(1)
print("\nALL PASSED")