diff --git a/.github/config/required-contexts.json b/.github/config/required-contexts.json index f5433ef71..5b6ceeaad 100644 --- a/.github/config/required-contexts.json +++ b/.github/config/required-contexts.json @@ -1,6 +1,6 @@ { "required_contexts": [ - "Gate / gate", - "Health 45 Agents Guard / guard" - ] + "Gate / gate" + ], + "_note": "Only universally-posted contexts belong here: health-44 can pass this file to enforce_gate_branch_protection.py --apply, so any context listed becomes a REQUIRED status check. 'Health 45 Agents Guard / guard' is deliberately absent: agents-guard.yml posts that status only when the PR carries an agent label (agent:codex, agents:auto-pilot, ...), so requiring it would leave every other PR permanently un-mergeable. See issue #2858." } diff --git a/.github/workflows/health-40-repo-selfcheck.yml b/.github/workflows/health-40-repo-selfcheck.yml index 0d86d6838..1717cafb8 100644 --- a/.github/workflows/health-40-repo-selfcheck.yml +++ b/.github/workflows/health-40-repo-selfcheck.yml @@ -119,7 +119,6 @@ jobs: --apply \ --branch "${DEFAULT_BRANCH}" \ --context "Gate / gate" \ - --context "Health 45 Agents Guard / guard" \ --no-clean - name: Snapshot branch protection state @@ -134,7 +133,6 @@ jobs: --require-strict \ --branch "${DEFAULT_BRANCH}" \ --context "Gate / gate" \ - --context "Health 45 Agents Guard / guard" \ --no-clean \ --snapshot repo-health-branch-protection.json @@ -403,6 +401,14 @@ jobs: core.setOutput('branch_fetch_error', branchFetchError || ''); - name: Root allowlist guard + # `always()` so an earlier failure (e.g. the branch-protection snapshot) + # cannot silently skip this guard. That is how six unlisted root files + # accumulated undetected for a month — see #2858. NOTE: this only rescues + # THIS step; "Collect repository signals" and "Aggregate & Summarize" + # still skip when an earlier step fails, because the aggregate depends on + # the collect step's outputs. Making the whole job resilient needs a + # different shape than a per-step always(). + if: always() # Fail when a tracked file appears at the repo root (depth 0) that is # not on the reviewed allowlist (config/root-allowlist.txt). Keeps # one-off debris (stray fixer scripts, per-run CI snapshots, *.patch) diff --git a/tools/enforce_gate_branch_protection.py b/tools/enforce_gate_branch_protection.py index 8dd5fb860..5ed589fa5 100755 --- a/tools/enforce_gate_branch_protection.py +++ b/tools/enforce_gate_branch_protection.py @@ -30,8 +30,10 @@ def resolve_api_root(explicit: str | None = None) -> str: DEFAULT_CONTEXTS = ( + # Only universally-posted contexts may be required. "Health 45 Agents Guard / + # guard" is posted by agents-guard.yml ONLY for agent-labelled PRs, so requiring + # it would block every other PR forever (issue #2858). "Gate / gate", - "Health 45 Agents Guard / guard", ) DEFAULT_CONFIG_PATH = Path(".github/config/required-contexts.json") @@ -248,7 +250,7 @@ def _fetch_ruleset_status_checks( Example return values: - None: No rulesets found, or no rulesets apply to the branch, or no status checks required. - - StatusCheckState(strict=False, contexts=['Gate / gate', 'Health 45 Agents Guard / guard']): + - StatusCheckState(strict=False, contexts=['Gate / gate', 'summary']): Required status checks found for the branch, with strict mode disabled. - StatusCheckState(strict=True, contexts=['ci/test', 'lint']): Required status checks found for the branch, with strict mode enabled. @@ -643,7 +645,7 @@ def main(argv: Sequence[str] | None = None) -> int: action="append", help=( "Status check context to require. May be passed multiple times. Defaults to" - " 'Gate / gate' and 'Health 45 Agents Guard / guard'." + " 'Gate / gate'." ), ) parser.add_argument(