Skip to content

fix(branch-protection): stop requiring a conditionally-posted status context - #2872

Merged
stranske merged 1 commit into
mainfrom
fix/health40-real-contexts-and-unblind-guard
Aug 1, 2026
Merged

fix(branch-protection): stop requiring a conditionally-posted status context#2872
stranske merged 1 commit into
mainfrom
fix/health40-real-contexts-and-unblind-guard

Conversation

@stranske

@stranske stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Closes the code-side half of #2858. The ruleset edit itself still needs an admin.

Why

Health 45 Agents Guard / guard cannot be a required status check.

It is a commit status posted by agents-guard.yml:690-701, inside the step "Report agents guard commit status" (:594), whose condition is:

if: always() && steps.eligibility.outputs.should-run == 'true'

steps.eligibility (:46) requires one of agent:auto, agent:codex, agent:claude, agent:copilot, agents:auto-pilot, agents:keepalive on the PR. On a PR without an agent label the status is never posted, and a required check that never reports leaves the PR permanently un-mergeable.

Verified: on the head of #2856 the statuses API returns only Gate / gate [success] and CodeRabbit [success]. The guard status is absent there and on all 12 most recent PRs (#2850-#2862).

This was not theoretical. health-44-gate-branch-protection.yml:220-225 passes this same config to enforce_gate_branch_protection.py --apply whenever github.event_name != 'pull_request' and an enforcement token is present. Dispatching health-44 today would have added the context and frozen every non-agent PR in the repo — including the ones fixing it.

What changed

  • .github/config/required-contexts.json — drop the context, leaving Gate / gate, and record the reason inline so it is not re-added. (Not in the sync manifest, so consumers are unaffected.)
  • tools/enforce_gate_branch_protection.pyDEFAULT_CONTEXTS reduced to Gate / gate with the rationale as a comment; docstring example and --context help text updated to match.
  • health-40-repo-selfcheck.yml — remove --context "Health 45 Agents Guard / guard" from both invocations.
  • health-40-repo-selfcheck.yml — give "Root allowlist guard" if: always(). Steps run sequentially, so the failing snapshot step was skipping it; that is how six unlisted repo-root files accumulated unseen for a month. The comment is explicit that this rescues only that step — Collect repository signals and Aggregate & Summarize still skip, because the aggregate consumes the collect step's outputs and making the whole job resilient needs a different shape.

agents-guard.yml is unchanged: it still posts its status, now purely informational. Requiring it would need it to post on every PR (e.g. success / "not applicable" when ineligible) — noted as the alternative on #2858, deliberately not done here since it edits a security-sensitive guard with a pull_request_target path.

Gate / gate remains required-worthy: posted on every PR by pr-00-gate.yml, pass on all 12 most recent PRs.

Verification

pytest tests/test_enforce_gate_branch_protection.py tests/tools/test_enforce_gate_branch_protection.py \
       tests/test_post_ci_summary.py                      -> 32 passed
yaml.safe_load(health-40-repo-selfcheck.yml)               -> OK
ruff check / format --check                                -> clean

enforce_gate_branch_protection.py --check --require-strict --config .github/config/required-contexts.json
  Current contexts: summary
  Target contexts:  Gate / gate, summary
  Would add contexts: Gate / gate
  exit 1   <- correct; goes to 0 once the ruleset adds `Gate / gate` + strict

The existing tests that pass Health 45 Agents Guard / guard explicitly via --context or as payload fixtures are left alone — they exercise arbitrary-context handling and do not depend on the default.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Updated branch protection checks to rely on the Gate status check.
    • Ensured repository validation continues running even when an earlier check fails.
    • Aligned enforcement guidance and command-line help with the updated required-check configuration.

…context

`Health 45 Agents Guard / guard` cannot be a required status check. It is a
commit status posted by agents-guard.yml only inside the "Report agents guard
commit status" step, whose condition is
`always() && steps.eligibility.outputs.should-run == 'true'`, and eligibility
requires one of the agent labels (agent:codex, agents:auto-pilot, ...). On any
PR without such a label the status is never posted, and a required check that
never reports leaves the PR permanently un-mergeable. Verified absent on the
heads of all 12 most recent PRs (#2850-#2862), where the only statuses are
`Gate / gate` and `CodeRabbit`.

This was not merely theoretical: health-44 passes
.github/config/required-contexts.json to
`enforce_gate_branch_protection.py --apply` on workflow_dispatch/workflow_call
whenever an enforcement token is present, so dispatching that workflow today
would have added the context and frozen every non-agent PR in the repo.

Remove it from the required-contexts set (shared config, the tool's
DEFAULT_CONTEXTS, both health-40 invocations) and record why in the config so it
is not re-added. `Gate / gate` remains required-worthy: it is posted on every PR
by pr-00-gate.yml and passes on all 12 most recent PRs. agents-guard.yml still
posts its status; it is simply informational rather than gating.

Also give health-40's "Root allowlist guard" `if: always()`. Steps run
sequentially, so the failing branch-protection snapshot step was skipping it —
which is how six unlisted repo-root files accumulated unseen for a month.

Refs #2858.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 1, 2026 13:14
@stranske
stranske enabled auto-merge (squash) August 1, 2026 13:14
@cursor

cursor Bot commented Aug 1, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 94e6c4d4-6ce4-4a61-bdc1-32595b40ed4b

📥 Commits

Reviewing files that changed from the base of the PR and between e42f6af and 5c1d729.

📒 Files selected for processing (3)
  • .github/config/required-contexts.json
  • .github/workflows/health-40-repo-selfcheck.yml
  • tools/enforce_gate_branch_protection.py

📝 Walkthrough

Walkthrough

The PR standardizes branch protection on the universally posted Gate / gate context. It removes the conditional health guard from configuration and tooling, updates examples and help text, and makes the root allowlist guard run after earlier failures.

Changes

Gate enforcement

Layer / File(s) Summary
Required context policy
.github/config/required-contexts.json, tools/enforce_gate_branch_protection.py
Required checks, ruleset examples, and CLI help now reference only Gate / gate.
Self-check workflow guard
.github/workflows/health-40-repo-selfcheck.yml
Branch protection and snapshot commands target Gate / gate. The root allowlist guard runs with if: always().

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • stranske/Workflows#2858 — The changes update the same branch-protection checks and make the root allowlist step run with if: always().

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes removing a conditionally posted status context from required branch protection checks.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/health40-real-contexts-and-unblind-guard

Comment @coderabbitai help to get the list of available commands.

@stranske
stranske temporarily deployed to agent-high-privilege August 1, 2026 13:16 — with GitHub Actions Inactive
@agents-workflows-bot

Copy link
Copy Markdown
Contributor

Workflow source needed

PR #2872 needs either a linked GitHub issue or one valid non-issue Workflow Source before PR metadata automation can manage it safely.

Please do one of:

  • Add <!-- meta:issue:123 --> or a normal Closes #123 / Related to #123 line.
  • Check one Workflow Source option in the PR body.
  • Add a hidden marker such as <!-- workflow-source:local_request -->, <!-- workflow-source:manual_remote -->, <!-- workflow-source:review_followup -->, <!-- workflow-source:sync_campaign -->, or <!-- workflow-source:dependabot -->.
  • Add a workflow source label such as workflow:source-direct-pr, workflow:source-local-request, workflow:source-review-followup, workflow:source-sync, or workflow:no-automation.

Once a valid source is present, this warning will not be reposted.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes a conditionally-posted status context (Health 45 Agents Guard / guard) from the repository’s “required status checks” enforcement surfaces, ensuring branch protection cannot accidentally require a check that is absent on non-agent-labelled PRs (preventing those PRs from becoming permanently un-mergeable). It also adjusts the repo selfcheck workflow so the repo-root allowlist guard runs even if earlier branch-protection snapshotting fails.

Changes:

  • Reduce branch-protection “default required contexts” to only Gate / gate, and update related help/docstring examples.
  • Update repo selfcheck (health-40) to stop passing the guard context to the enforcement tool and ensure “Root allowlist guard” runs with if: always().
  • Update .github/config/required-contexts.json to only list Gate / gate and document why the guard context must not be re-added.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
tools/enforce_gate_branch_protection.py Removes the guard context from DEFAULT_CONTEXTS and aligns CLI/docs with the new default.
.github/workflows/health-40-repo-selfcheck.yml Stops enforcing the guard context and ensures the root allowlist guard step runs even after earlier failures.
.github/config/required-contexts.json Drops the guard context from the required-contexts config and adds an inline rationale to prevent reintroduction.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c1d729b48

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

]
"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."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the universally posted guard check required

For an agent-labelled PR that violates the guard policy, this removal allows the PR to merge once Gate passes even though the guard job fails. In .github/workflows/agents-guard.yml, the workflow is triggered for every pull request and the guard job itself has no job-level condition; only the legacy commit-status reporting step is conditional. Consequently, Health 45 Agents Guard / guard is also emitted as a workflow check run on every PR, so its absence from the commit statuses API does not show that the required check is missing. Retain this required context so the failure at the workflow's final guard step continues to block protected changes.

Useful? React with 👍 / 👎.

@agents-workflows-bot

Copy link
Copy Markdown
Contributor

Automated Status Summary

Head SHA: 331d24d
Latest Runs: ⏳ pending — Gate
Required contexts: Gate / gate
Required: core tests (3.12): ⏳ pending, core tests (3.13): ⏳ pending, docker smoke: ⏳ pending, gate: ⏳ pending

Workflow / Job Result Logs
(no jobs reported) ⏳ pending

Coverage Overview

  • Coverage history entries: 1

Coverage Trend

Metric Value
Current 75.91%
Baseline 85.00%
Delta -9.09%
Minimum 70.00%
Status ✅ Pass

Top Coverage Hotspots (lowest coverage)

File Coverage Missing
scripts/issue_dedup_smoke.py 0.0% 4
scripts/runner_lib/__main__.py 0.0% 3
scripts/validate_template_sync.py 0.0% 81
scripts/langchain/topic_splitter.py 19.1% 57
tools/codex_log_analyzer.py 19.6% 140
scripts/repo_review_round2_runner.py 25.1% 344
scripts/prune_agent_stubs.py 39.7% 26
scripts/repo_review_round1_runner.py 40.7% 133
tools/ensure_workflow_timeout_variables.py 42.1% 74
scripts/sync_label_docs.py 42.9% 64
tools/discover_model_catalog.py 44.8% 55
scripts/repo_review_backlog_scan.py 45.3% 116
scripts/repo_review_body_writer.py 46.5% 86
tools/codex_session_analyzer.py 47.9% 59
scripts/create_verifier_labels.py 48.3% 58

Low Coverage Files (<50.0%)

File Coverage Missing
scripts/issue_dedup_smoke.py 0.0% 4
scripts/runner_lib/__main__.py 0.0% 3
scripts/validate_template_sync.py 0.0% 81
scripts/langchain/topic_splitter.py 19.1% 57
tools/codex_log_analyzer.py 19.6% 140
scripts/repo_review_round2_runner.py 25.1% 344
scripts/prune_agent_stubs.py 39.7% 26
scripts/repo_review_round1_runner.py 40.7% 133
tools/ensure_workflow_timeout_variables.py 42.1% 74
scripts/sync_label_docs.py 42.9% 64
tools/discover_model_catalog.py 44.8% 55
scripts/repo_review_backlog_scan.py 45.3% 116
scripts/repo_review_body_writer.py 46.5% 86
tools/codex_session_analyzer.py 47.9% 59
scripts/create_verifier_labels.py 48.3% 58

Updated automatically; will refresh on subsequent CI/Docker completions.


Keepalive checklist

Scope

No scope information available

Tasks

  • No tasks defined

Acceptance criteria

  • No acceptance criteria defined

@stranske
stranske merged commit 3bf9a1a into main Aug 1, 2026
63 checks passed
@stranske
stranske deleted the fix/health40-real-contexts-and-unblind-guard branch August 1, 2026 13:23
stranske added a commit that referenced this pull request Aug 1, 2026
 (#2889)

testgen_gate.py was a single-purpose acceptance harness for
#2620, which closed on 2026-06-28. It is dead weight:

- Nothing references it. The only two matches for "testgen_gate" in the repo are
  its own usage-print strings.
- It hardcodes exactly one target, tests/scripts/test_repo_review_round1_schema.py,
  which still exists, passes 22 tests, and is already collected by the normal
  pytest suite that Gate runs. The gate re-validates what CI covers.
- It was the last tracked file at the repo root not on config/root-allowlist.txt,
  so it is precisely the "one-off debris / stray fixer script" that health-40's
  Root allowlist guard exists to catch (issue #2277).

Deleting rather than allowlisting: allowlisting would bless redundant code and
leave the guard permanently unable to flag it.

With this removed, the root allowlist guard reports clean — which matters now
that #2872 gave it `if: always()` so it actually runs.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
stranske added a commit that referenced this pull request Aug 1, 2026
 (#2889)

testgen_gate.py was a single-purpose acceptance harness for
#2620, which closed on 2026-06-28. It is dead weight:

- Nothing references it. The only two matches for "testgen_gate" in the repo are
  its own usage-print strings.
- It hardcodes exactly one target, tests/scripts/test_repo_review_round1_schema.py,
  which still exists, passes 22 tests, and is already collected by the normal
  pytest suite that Gate runs. The gate re-validates what CI covers.
- It was the last tracked file at the repo root not on config/root-allowlist.txt,
  so it is precisely the "one-off debris / stray fixer script" that health-40's
  Root allowlist guard exists to catch (issue #2277).

Deleting rather than allowlisting: allowlisting would bless redundant code and
leave the guard permanently unable to flag it.

With this removed, the root allowlist guard reports clean — which matters now
that #2872 gave it `if: always()` so it actually runs.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants