fix(gate-check): add workspace-agent login aliases + handle -agent suffix in tags
Some checks failed
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m14s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 52s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 1m26s
audit-force-merge / audit (pull_request) Waiting to run
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m24s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 25s
Harness Replays / detect-changes (pull_request) Successful in 31s
Harness Replays / Harness Replays (pull_request) Failing after 9m2s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 9m22s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 26s
CI / Detect changes (pull_request) Successful in 1m49s
MCP Stdio Transport Regression / MCP stdio with regular-file stdout (pull_request) Successful in 1m52s
E2E API Smoke Test / detect-changes (pull_request) Successful in 1m46s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Waiting to run
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m51s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Waiting to run
publish-runtime-autobump / bump-and-tag (pull_request) Has been skipped
Runtime PR-Built Compatibility / detect-changes (pull_request) Has started running
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 36s
gate-check-v3 / gate-check (pull_request) Successful in 32s
publish-runtime-autobump / pr-validate (pull_request) Successful in 1m9s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m40s
qa-review / approved (pull_request) Successful in 30s
security-review / approved (pull_request) Successful in 41s
sop-checklist / all-items-acked (pull_request) Successful in 25s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 3m32s
sop-tier-check / tier-check (pull_request) Successful in 17s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 2m0s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 3m3s
lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request) Successful in 3m22s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 3m13s
CI / Shellcheck (E2E scripts) (pull_request) Has started running
CI / Python Lint & Test (pull_request) Successful in 8m9s
CI / Canvas (Next.js) (pull_request) Successful in 20m1s
CI / Platform (Go) (pull_request) Failing after 22m0s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Has been cancelled
CI / all-required (pull_request) Has been cancelled
Some checks failed
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m14s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 52s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 1m26s
audit-force-merge / audit (pull_request) Waiting to run
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m24s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 25s
Harness Replays / detect-changes (pull_request) Successful in 31s
Harness Replays / Harness Replays (pull_request) Failing after 9m2s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 9m22s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 26s
CI / Detect changes (pull_request) Successful in 1m49s
MCP Stdio Transport Regression / MCP stdio with regular-file stdout (pull_request) Successful in 1m52s
E2E API Smoke Test / detect-changes (pull_request) Successful in 1m46s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Waiting to run
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m51s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Waiting to run
publish-runtime-autobump / bump-and-tag (pull_request) Has been skipped
Runtime PR-Built Compatibility / detect-changes (pull_request) Has started running
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 36s
gate-check-v3 / gate-check (pull_request) Successful in 32s
publish-runtime-autobump / pr-validate (pull_request) Successful in 1m9s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m40s
qa-review / approved (pull_request) Successful in 30s
security-review / approved (pull_request) Successful in 41s
sop-checklist / all-items-acked (pull_request) Successful in 25s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 3m32s
sop-tier-check / tier-check (pull_request) Successful in 17s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 2m0s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 3m3s
lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request) Successful in 3m22s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 3m13s
CI / Shellcheck (E2E scripts) (pull_request) Has started running
CI / Python Lint & Test (pull_request) Successful in 8m9s
CI / Canvas (Next.js) (pull_request) Successful in 20m1s
CI / Platform (Go) (pull_request) Failing after 22m0s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Has been cancelled
CI / all-required (pull_request) Has been cancelled
Discovery #1275: QA/security agents post comments/reviews using their full workspace-agent login (e.g. core-qa-agent) rather than the canonical org login (core-qa). The gate was not resolving these aliases, so their sign-offs were invisible to the agent-tag matching even when the comment body format was correct. Two-part fix: 1. LOGIN_ALIASES: map all core-{role}-agent logins to their canonical core-{role} form (infra-sre already handled; add the full set of workspace-agent logins). 2. AGENT_TAG_RE: make the -agent suffix optional so both [core-qa] and [core-qa-agent] formats match the same role. Tests added for: - core-qa-agent posting [qa-agent] APPROVED as issue comment - core-security-agent posting formal APPROVED review Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
1b8190d271
commit
ffa51bc554
@ -94,8 +94,11 @@ class GiteaError(Exception):
|
||||
# (/pulls/{N}/comments) since agents post on both.
|
||||
|
||||
# Matches [core-{role}-agent] VERDICT anywhere in the comment body.
|
||||
# Matches [core-{role}] or [core-{role}-agent] VERDICT.
|
||||
# Handles both canonical logins and workspace-agent naming conventions.
|
||||
# The role is extracted and the -agent suffix is stripped so both formats match.
|
||||
AGENT_TAG_RE = re.compile(
|
||||
r"\[core-([a-z]+)-agent\]\s+(APPROVED|N/?A|CHANGES_REQUESTED|COMMENT|BLOCKED|ACK)\b",
|
||||
r"\[core-([a-z]+)(?:-agent)?\]\s+(APPROVED|N/?A|CHANGES_REQUESTED|COMMENT|BLOCKED|ACK)\b",
|
||||
)
|
||||
|
||||
# Map agent role → canonical login (from workspace registry)
|
||||
@ -113,8 +116,19 @@ AGENT_LOGIN_MAP = {
|
||||
# Map alternate Gitea logins → canonical logins for gate matching.
|
||||
# infra-sre is the engineers/core-devops agent (same team, same work).
|
||||
# Without this alias, infra-sre comments/reviews never satisfy the engineers gate.
|
||||
# core-qa-agent / core-security-agent: QA/security agents post with their full workspace
|
||||
# login names rather than the canonical form — alias so their reviews/comments satisfy
|
||||
# the gate even without the agent's canonical login being added to the org.
|
||||
LOGIN_ALIASES = {
|
||||
"infra-sre": "core-devops",
|
||||
"core-qa-agent": "core-qa",
|
||||
"core-security-agent": "core-security",
|
||||
"core-uiux-agent": "core-uiux",
|
||||
"core-be-agent": "core-be",
|
||||
"core-fe-agent": "core-fe",
|
||||
"core-offsec-agent": "core-offsec",
|
||||
"core-lead-agent": "core-lead",
|
||||
"core-devops-agent": "core-devops",
|
||||
}
|
||||
|
||||
# SOP-6 tier → required agent groups
|
||||
|
||||
@ -74,3 +74,126 @@ def test_signal_1_infra_sre_login_alias_resolved_to_core_devops(monkeypatch):
|
||||
engineers = result["results"]["core-devops"]
|
||||
assert engineers["verdict"] == "APPROVED"
|
||||
assert engineers["group"] == "engineers"
|
||||
|
||||
|
||||
def test_signal_1_core_qa_agent_alias_issue_comment(monkeypatch):
|
||||
"""core-qa-agent posts [qa-agent] APPROVED as issue comment → gate satisfied via alias."""
|
||||
mod = load_gate_check()
|
||||
|
||||
def fake_api_get(path):
|
||||
# PR 901 has tier:medium label (AND gate: all of managers, engineers, qa, security)
|
||||
if path == "/repos/molecule-ai/molecule-core/pulls/901":
|
||||
return {
|
||||
"number": 901,
|
||||
"labels": [{"name": "tier:medium"}],
|
||||
}
|
||||
raise AssertionError(f"unexpected api_get: {path}")
|
||||
|
||||
def fake_api_list(path):
|
||||
if path == "/repos/molecule-ai/molecule-core/issues/901/comments":
|
||||
# All four agents post on the issue. QA uses the -agent workspace login alias.
|
||||
return [
|
||||
{
|
||||
"id": 100,
|
||||
"user": {"login": "core-lead"},
|
||||
"body": "[core-lead-agent] APPROVED\n\nLGTM managers",
|
||||
"created_at": "2026-05-14T08:00:00Z",
|
||||
},
|
||||
{
|
||||
"id": 101,
|
||||
"user": {"login": "core-devops"},
|
||||
"body": "[core-devops-agent] APPROVED\n\nLGTM engineers",
|
||||
"created_at": "2026-05-14T09:00:00Z",
|
||||
},
|
||||
{
|
||||
"id": 200,
|
||||
"user": {"login": "core-qa-agent"},
|
||||
"body": "[core-qa-agent] APPROVED\n\nGo tests pass, coverage 100%",
|
||||
"created_at": "2026-05-14T10:00:00Z",
|
||||
},
|
||||
{
|
||||
"id": 300,
|
||||
"user": {"login": "core-security"},
|
||||
"body": "[core-security-agent] APPROVED\n\nNo secrets exposed",
|
||||
"created_at": "2026-05-14T11:00:00Z",
|
||||
},
|
||||
]
|
||||
if path == "/repos/molecule-ai/molecule-core/pulls/901/comments":
|
||||
return []
|
||||
if path == "/repos/molecule-ai/molecule-core/pulls/901/reviews":
|
||||
return []
|
||||
raise AssertionError(f"unexpected api_list: {path}")
|
||||
|
||||
monkeypatch.setattr(mod, "api_get", fake_api_get)
|
||||
monkeypatch.setattr(mod, "api_list", fake_api_list)
|
||||
|
||||
result = mod.signal_1_comment_scan(901, "molecule-ai/molecule-core")
|
||||
|
||||
assert result["verdict"] == "CLEAR"
|
||||
assert result["signal"] == "agent_tag_comments"
|
||||
# core-qa-agent (aliased to core-qa) should satisfy qa gate
|
||||
qa_gate = result["results"]["core-qa"]
|
||||
assert qa_gate["verdict"] == "APPROVED"
|
||||
assert qa_gate["group"] == "qa"
|
||||
|
||||
|
||||
def test_signal_1_core_security_agent_alias_formal_review(monkeypatch):
|
||||
"""core-security-agent posts formal APPROVED review → gate satisfied via alias."""
|
||||
mod = load_gate_check()
|
||||
|
||||
def fake_api_get(path):
|
||||
if path == "/repos/molecule-ai/molecule-core/pulls/902":
|
||||
return {
|
||||
"number": 902,
|
||||
"labels": [{"name": "tier:medium"}],
|
||||
}
|
||||
raise AssertionError(f"unexpected api_get: {path}")
|
||||
|
||||
def fake_api_list(path):
|
||||
if path == "/repos/molecule-ai/molecule-core/issues/902/comments":
|
||||
# managers + engineers + qa post via issue comment
|
||||
return [
|
||||
{
|
||||
"id": 100,
|
||||
"user": {"login": "core-lead"},
|
||||
"body": "[core-lead-agent] APPROVED",
|
||||
"created_at": "2026-05-15T08:00:00Z",
|
||||
},
|
||||
{
|
||||
"id": 101,
|
||||
"user": {"login": "core-devops"},
|
||||
"body": "[core-devops-agent] APPROVED",
|
||||
"created_at": "2026-05-15T09:00:00Z",
|
||||
},
|
||||
{
|
||||
"id": 200,
|
||||
"user": {"login": "core-qa"},
|
||||
"body": "[core-qa-agent] APPROVED",
|
||||
"created_at": "2026-05-15T10:00:00Z",
|
||||
},
|
||||
]
|
||||
if path == "/repos/molecule-ai/molecule-core/pulls/902/comments":
|
||||
return []
|
||||
if path == "/repos/molecule-ai/molecule-core/pulls/902/reviews":
|
||||
# security posts formal APPROVED review (not an issue comment)
|
||||
return [
|
||||
{
|
||||
"id": 300,
|
||||
"user": {"login": "core-security-agent"},
|
||||
"state": "APPROVED",
|
||||
"submitted_at": "2026-05-15T11:00:00Z",
|
||||
}
|
||||
]
|
||||
raise AssertionError(f"unexpected api_list: {path}")
|
||||
|
||||
monkeypatch.setattr(mod, "api_get", fake_api_get)
|
||||
monkeypatch.setattr(mod, "api_list", fake_api_list)
|
||||
|
||||
result = mod.signal_1_comment_scan(902, "molecule-ai/molecule-core")
|
||||
|
||||
assert result["verdict"] == "CLEAR"
|
||||
assert result["signal"] == "agent_tag_comments"
|
||||
# core-security-agent (aliased to core-security) should satisfy security gate
|
||||
security_gate = result["results"]["core-security"]
|
||||
assert security_gate["verdict"] == "APPROVED"
|
||||
assert security_gate["group"] == "security"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user