fix(ci): status-reaper compensates non-required governance PR shadows (#2770 #2767) #2814

Merged
devops-engineer merged 4 commits from fix/2770-governance-pr-status-shadows into main 2026-06-14 02:56:08 +00:00
2 changed files with 440 additions and 1 deletions
+103 -1
View File
@@ -143,6 +143,11 @@ PR_SHADOW_COMPENSATION_DESCRIPTION = (
"shadowed by successful push status on same SHA; see "
".gitea/scripts/status-reaper.py)"
)
GOVERNANCE_SHADOW_COMPENSATION_DESCRIPTION = (
"Compensated by status-reaper (non-required pull_request/pull_request_review "
"governance shadow overridden by successful pull_request_target status; see "
".gitea/scripts/status-reaper.py)"
)
CANCELLED_PUSH_COMPENSATION_DESCRIPTION = (
"Compensated by status-reaper (push run was cancelled/superseded; "
"Gitea 1.22.6 reports cancelled runs as failure statuses)"
@@ -153,6 +158,20 @@ CANCELLED_DESCRIPTION = "Has been cancelled"
# default-branch workflow runs.
PUSH_SUFFIX = " (push)"
PULL_REQUEST_SUFFIX = " (pull_request)"
PULL_REQUEST_TARGET_SUFFIX = " (pull_request_target)"
PULL_REQUEST_REVIEW_SUFFIX = " (pull_request_review)"
# Governance workflows whose non-required `(pull_request)` / `(pull_request_review)`
# shadows may be compensated when the trusted `(pull_request_target)` variant is
# green. This is an EXACT active allowlist — every other workflow is preserved,
# even if it has no `push:` trigger, to avoid masking real failures.
GOVERNANCE_SHADOW_ALLOWLIST = frozenset(
{"sop-checklist", "qa-review", "security-review"}
)
# Retired workflows whose historical shadow contexts still appear on old commits
# and must remain compensatable even though the workflow YAML has been removed.
# They are treated as known non-push when absent from the trigger map.
GOVERNANCE_SHADOW_RETIRED_ALLOWLIST = frozenset({"sop-tier-check"})
# --------------------------------------------------------------------------
# Conductor snapshot (operator-config#158)
@@ -488,6 +507,51 @@ def push_equivalent_context(context: str) -> str | None:
return f"{workflow_name} / {job_name}{PUSH_SUFFIX}"
def target_equivalent_context(context: str, source_suffix: str) -> str | None:
"""Return the matching `(pull_request_target)` context for a suffixed context.
Handles `(pull_request)` and `(pull_request_review)` governance shadows.
"""
parsed = parse_suffixed_context(context, source_suffix)
if parsed is None:
return None
workflow_name, job_name = parsed
return f"{workflow_name} / {job_name}{PULL_REQUEST_TARGET_SUFFIX}"
def is_governance_shadow_context(
context: str, workflow_trigger_map: dict[str, bool]
) -> bool:
"""True if `context` is a compensatable governance shadow.
Active governance workflows (`sop-checklist`, `qa-review`, `security-review`)
are compensatable only when their trigger map entry is explicitly `False`.
Retired workflows (`sop-tier-check`) may be absent from the trigger map
because their YAML was removed; they are treated as known non-push so their
historical shadow contexts remain compensatable.
Workflows that DO have a `push:` trigger are excluded even if they are in an
allowlist — their PR/review status is an independent gate signal. Unknown
workflows or workflows not in any allowlist are preserved (fail-closed).
"""
for suffix in (PULL_REQUEST_SUFFIX, PULL_REQUEST_REVIEW_SUFFIX):
parsed = parse_suffixed_context(context, suffix)
if parsed is not None:
workflow_name, _job_name = parsed
if workflow_name in GOVERNANCE_SHADOW_RETIRED_ALLOWLIST:
# Retired workflow: absent from the trigger map is expected.
# Only a push-triggered retired workflow is preserved.
has_push = workflow_trigger_map.get(workflow_name)
return has_push is not True
if workflow_name not in GOVERNANCE_SHADOW_ALLOWLIST:
return False
# Active allowlist workflow: require an explicit known-no-push entry.
# If the parser ever misses the workflow, fail-closed (preserve).
has_push = workflow_trigger_map.get(workflow_name)
return has_push is False
return False
# --------------------------------------------------------------------------
# Compensating POST
# --------------------------------------------------------------------------
@@ -562,6 +626,8 @@ def reap(
"compensated_pr_shadowed_by_push_success": 0,
"compensated_cancelled_push": 0,
"preserved_pr_without_push_success": 0,
"compensated_governance_shadow": 0,
"preserved_governance_without_target_success": 0,
"compensated_contexts": [],
}
@@ -594,6 +660,36 @@ def reap(
counters["preserved_non_failure"] += 1
continue
# Governance shadow compensation (#2770 / #2767).
# Non-required `(pull_request)` and `(pull_request_review)` contexts
# emitted by governance workflows (sop-checklist, qa-review,
# security-review, retired sop-tier-check) are informational shadows
# of the required `(pull_request_target)` context. When the trusted
# target context succeeded, the shadow must not keep the aggregate
# commit status red. CI workflows that also have a `push:` trigger are
# excluded — their `(pull_request)` status is an independent gate.
if is_governance_shadow_context(context, workflow_trigger_map):
source_suffix = (
PULL_REQUEST_SUFFIX
if context.endswith(PULL_REQUEST_SUFFIX)
else PULL_REQUEST_REVIEW_SUFFIX
)
target_equivalent = target_equivalent_context(context, source_suffix)
if target_equivalent is not None and target_equivalent in successful_contexts:
post_compensating_status(
sha,
context,
s.get("target_url"),
description=GOVERNANCE_SHADOW_COMPENSATION_DESCRIPTION,
dry_run=dry_run,
)
counters["compensated"] += 1
counters["compensated_governance_shadow"] += 1
counters["compensated_contexts"].append(context)
else:
counters["preserved_governance_without_target_success"] += 1
continue
# Default-branch `pull_request` contexts can be stale shadows of
# the exact same workflow/job already proven by the successful
# `push` context on the same SHA. Compensate only that narrow
@@ -618,7 +714,7 @@ def reap(
# Only `(push)`-suffix contexts hit the hardcoded-suffix bug.
# Other failed contexts are preserved unless handled by the
# pull-request-shadow rule above.
# governance-shadow or pull-request-shadow rules above.
if not context.endswith(PUSH_SUFFIX):
counters["preserved_non_push_suffix"] += 1
continue
@@ -766,6 +862,8 @@ def reap_branch(
"compensated_pr_shadowed_by_push_success": 0,
"compensated_cancelled_push": 0,
"preserved_pr_without_push_success": 0,
"compensated_governance_shadow": 0,
"preserved_governance_without_target_success": 0,
"compensated_per_sha": {},
"sha_api_errors": 0,
"skipped": True,
@@ -783,6 +881,8 @@ def reap_branch(
"compensated_pr_shadowed_by_push_success": 0,
"compensated_cancelled_push": 0,
"preserved_pr_without_push_success": 0,
"compensated_governance_shadow": 0,
"preserved_governance_without_target_success": 0,
"compensated_per_sha": {},
"sha_api_errors": 0,
}
@@ -825,6 +925,8 @@ def reap_branch(
"compensated_pr_shadowed_by_push_success",
"compensated_cancelled_push",
"preserved_pr_without_push_success",
"compensated_governance_shadow",
"preserved_governance_without_target_success",
):
aggregate[key] += per_sha[key]
@@ -1,6 +1,7 @@
import importlib.util
import json
import pathlib
import pytest
import urllib.error
@@ -250,3 +251,339 @@ def test_get_combined_status_self_fetches_when_sha_not_in_snapshot(monkeypatch):
assert combined["state"] == "success"
finally:
os.unlink(path)
def test_reap_compensates_governance_shadow_when_target_passed(monkeypatch):
mod = load_reaper()
posted = []
def fake_post(sha, context, target_url, *, description="", dry_run=False):
posted.append((sha, context, target_url, description, dry_run))
monkeypatch.setattr(mod, "post_compensating_status", fake_post)
# sop-checklist has no push trigger, so its failed (pull_request) shadow is
# noise when the required (pull_request_target) context is green.
counters = mod.reap(
{"sop-checklist": False, "qa-review": False, "security-review": False},
{
"statuses": [
{
"context": "sop-checklist / all-items-acked (pull_request)",
"status": "failure",
"target_url": "https://git.example.test/sop-pr",
},
{
"context": "sop-checklist / all-items-acked (pull_request_target)",
"status": "success",
},
{
"context": "qa-review / approved (pull_request_review)",
"status": "failure",
"target_url": "https://git.example.test/qa-pr-review",
},
{
"context": "qa-review / approved (pull_request_target)",
"status": "success",
},
],
},
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
)
assert counters["compensated_governance_shadow"] == 2
assert counters["preserved_governance_without_target_success"] == 0
assert posted == [
(
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
"sop-checklist / all-items-acked (pull_request)",
"https://git.example.test/sop-pr",
mod.GOVERNANCE_SHADOW_COMPENSATION_DESCRIPTION,
False,
),
(
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
"qa-review / approved (pull_request_review)",
"https://git.example.test/qa-pr-review",
mod.GOVERNANCE_SHADOW_COMPENSATION_DESCRIPTION,
False,
),
]
def test_reap_preserves_governance_shadow_when_target_missing_or_failed(monkeypatch):
mod = load_reaper()
posted = []
monkeypatch.setattr(
mod,
"post_compensating_status",
lambda sha, context, target_url, *, description="", dry_run=False: posted.append(
context
),
)
counters = mod.reap(
{"sop-checklist": False},
{
"statuses": [
{
"context": "sop-checklist / all-items-acked (pull_request)",
"status": "failure",
},
# target context failed → preserve the shadow as a real signal.
{
"context": "sop-checklist / all-items-acked (pull_request_target)",
"status": "failure",
},
],
},
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
)
assert counters["compensated_governance_shadow"] == 0
assert counters["preserved_governance_without_target_success"] == 1
assert posted == []
def test_reap_preserves_ci_pull_request_failure_even_when_target_passed(monkeypatch):
mod = load_reaper()
posted = []
monkeypatch.setattr(
mod,
"post_compensating_status",
lambda sha, context, target_url, *, description="", dry_run=False: posted.append(
context
),
)
# A CI workflow that also has a push trigger is NOT a governance shadow;
# its (pull_request) failure is an independent gate signal and must be
# preserved even if a (pull_request_target) variant happens to be green.
counters = mod.reap(
{"CI": True},
{
"statuses": [
{
"context": "CI / Platform (Go) (pull_request)",
"status": "failure",
},
{
"context": "CI / Platform (Go) (pull_request_target)",
"status": "success",
},
],
},
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
)
assert counters["compensated_governance_shadow"] == 0
assert counters["preserved_pr_without_push_success"] == 1
assert posted == []
def test_reap_preserves_non_governance_no_push_shadow_when_target_passed(monkeypatch):
mod = load_reaper()
posted = []
monkeypatch.setattr(
mod,
"post_compensating_status",
lambda sha, context, target_url, *, description="", dry_run=False: posted.append(
context
),
)
# A no-push workflow that is NOT in the governance allowlist must be
# preserved even when its (pull_request_target) variant is green.
counters = mod.reap(
{"custom-audit": False},
{
"statuses": [
{
"context": "custom-audit / check (pull_request_review)",
"status": "failure",
},
{
"context": "custom-audit / check (pull_request_target)",
"status": "success",
},
],
},
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
)
assert counters["compensated_governance_shadow"] == 0
assert counters["compensated"] == 0
assert posted == []
def test_reap_compensates_retired_sop_tier_check_shadow_when_target_passed(monkeypatch):
mod = load_reaper()
posted = []
def fake_post(sha, context, target_url, *, description="", dry_run=False):
posted.append((sha, context, target_url, description, dry_run))
monkeypatch.setattr(mod, "post_compensating_status", fake_post)
counters = mod.reap(
{"sop-tier-check": False},
{
"statuses": [
{
"context": "sop-tier-check / tier-verify (pull_request)",
"status": "failure",
"target_url": "https://git.example.test/tier-pr",
},
{
"context": "sop-tier-check / tier-verify (pull_request_target)",
"status": "success",
},
],
},
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
)
assert counters["compensated_governance_shadow"] == 1
assert counters["preserved_governance_without_target_success"] == 0
assert posted == [
(
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
"sop-tier-check / tier-verify (pull_request)",
"https://git.example.test/tier-pr",
mod.GOVERNANCE_SHADOW_COMPENSATION_DESCRIPTION,
False,
),
]
def test_reap_compensates_retired_sop_tier_check_when_missing_from_trigger_map(monkeypatch):
"""The retired sop-tier-check workflow file is intentionally removed, so the
real workflow trigger map will not contain it. It must still be compensatable
because it is explicitly allowlisted as a retired governance shadow."""
mod = load_reaper()
posted = []
def fake_post(sha, context, target_url, *, description="", dry_run=False):
posted.append((sha, context, target_url, description, dry_run))
monkeypatch.setattr(mod, "post_compensating_status", fake_post)
counters = mod.reap(
# Deliberately omit sop-tier-check from the trigger map.
{},
{
"statuses": [
{
"context": "sop-tier-check / tier-verify (pull_request)",
"status": "failure",
"target_url": "https://git.example.test/tier-pr",
},
{
"context": "sop-tier-check / tier-verify (pull_request_target)",
"status": "success",
},
],
},
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
)
assert counters["compensated_governance_shadow"] == 1
assert counters["preserved_governance_without_target_success"] == 0
assert posted == [
(
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
"sop-tier-check / tier-verify (pull_request)",
"https://git.example.test/tier-pr",
mod.GOVERNANCE_SHADOW_COMPENSATION_DESCRIPTION,
False,
),
]
def test_reap_preserves_active_governance_shadow_when_missing_from_trigger_map(monkeypatch):
"""Active governance workflows must be explicitly known-no-push in the trigger
map. If the parser/discovery misses them, the reaper must fail-closed and
preserve their shadow rather than auto-green it."""
mod = load_reaper()
posted = []
monkeypatch.setattr(
mod,
"post_compensating_status",
lambda sha, context, target_url, *, description="", dry_run=False: posted.append(
context
),
)
counters = mod.reap(
# Deliberately omit qa-review from the trigger map.
{},
{
"statuses": [
{
"context": "qa-review / approved (pull_request_review)",
"status": "failure",
},
{
"context": "qa-review / approved (pull_request_target)",
"status": "success",
},
],
},
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
)
assert counters["compensated_governance_shadow"] == 0
assert counters["compensated"] == 0
assert posted == []
@pytest.mark.parametrize(
"context",
[
"gate-check-v3 / gate (pull_request)",
"reserved-path-review / check (pull_request_review)",
"lint-required-no-paths / lint (pull_request)",
"lint-required-context-exists-in-bp / lint (pull_request_review)",
"audit-force-merge / audit (pull_request)",
"status-reaper / reap (pull_request_review)",
"umbrella-reaper / reap (pull_request)",
],
)
def test_reap_preserves_named_non_governance_no_push_shadows(context, monkeypatch):
"""Real merge-control/lint/audit workflows that are NOT in the governance
allowlist must be preserved even when they have no push trigger and their
(pull_request_target) variant is green. Auto-greening these would mask real
failures."""
mod = load_reaper()
posted = []
monkeypatch.setattr(
mod,
"post_compensating_status",
lambda sha, context, target_url, *, description="", dry_run=False: posted.append(
context
),
)
workflow_name = context.split(" / ", 1)[0]
counters = mod.reap(
{workflow_name: False},
{
"statuses": [
{
"context": context,
"status": "failure",
},
{
"context": context.replace(
" (pull_request)", " (pull_request_target)"
).replace(" (pull_request_review)", " (pull_request_target)"),
"status": "success",
},
],
},
"db3b7a93e31adc0cb072a6d177d92dd73275a191",
)
assert counters["compensated_governance_shadow"] == 0
assert counters["compensated"] == 0
assert posted == []