Skip to content

main has almost no merge protection: 1 of 11 checks required, no review gate, admins exempt (decision needed) #1373

Open

Description

@lilyshen0722

Surfaced while investigating why two PRs I had reviewed with findings merged with the findings unaddressed (#1365, #1366). My first explanation was wrong — I said they "merged over my BLOCK." @pod-architect corrected it: none of my reviews carried blocking state, and then found the reason that supersedes both explanations. main has almost no merge protection. Verified independently against GET /repos/Team-Commonly/commonly/branches/main/protection at 2026-08-30T21:32Z.

This needs a human decision — tightening protection is an admin action with a real velocity cost, and it is not mine to make. Filing the measurement so the decision can be made on facts.

What is actually enforced

"required_status_checks": { "strict": false, "contexts": ["Test & Coverage"] }

That is the entire gate. required_pull_request_reviews is absent from the response entirely — not null-with-settings, just not configured.

Consequences, each following directly

1. No review gates a merge, in any state. This is why "I filed a BLOCK" meant nothing: a COMMENTED review and a CHANGES_REQUESTED review are equally non-binding here. It also means the fix I initially proposed — a second reviewer identity, since all seats share lilyshen0722 and GitHub forbids self-approval — would have bought nothing. Branch protection is upstream of the identity question.

2. Ten of eleven CI checks cannot block anything. Only Test & Coverage is required. Everything else runs, reports, and is advisory:

Analyze (actions) · Analyze (javascript-typescript) · Analyze (python)
CodeQL · Detect secrets · E2E Tests · Service Tests (Tier 1 — real DBs)
Chart Lint (Tier 1.5) · Source changed ⇒ version bumped
Stale-base merge guard · Release Branch Guard

A PR red on CodeQL and Detect secrets merges today. This is a larger hole than the review-state one and was invisible from the PR UI, where all eleven render identically.

3. strict: false — branches need not be current with main before merging. Note the interaction: Stale-base merge guard exists precisely to catch this, and it is in the advisory list. The guard runs, reports, and cannot stop the merge it was built to stop.

4. enforce_admins: false — admins bypass even the single required check.

5. required_conversation_resolution: false — unresolved review threads do not block. A reviewer's findings can be open on the PR at the moment it merges, which is exactly what happened on #1355 and #1346.

Suggested shape, not a prescription

The cheap high-value change is promoting the checks that are already green almost all the time — CodeQL, Detect secrets, the three Analyze jobs — into contexts. Those catch security regressions and are not the ones that flake, so the velocity cost is close to zero.

required_conversation_resolution: true is the one that most directly addresses how #1355 and #1346 merged, and it costs a click per resolved thread.

strict: true and required_pull_request_reviews are real tradeoffs at this team size and merge cadence — strict: true in particular forces a rebase-and-rerun cycle on every PR whenever main moves, which at today's cadence would be constant. I would not turn those on without someone weighing it deliberately; that is the decision this issue is asking for.

NOT VERIFIED: I read the protection on main only — I did not check rulesets (/rulesets), which can add enforcement that does not appear in the legacy protection payload, nor org-level policy. If a ruleset is doing work here, some of the above is over-stated, and that is worth checking before anyone changes settings. I also did not verify my claim that GitHub forbids self-approval (the probe was refused by my permission layer); it is not load-bearing for this issue either way.

Activity

  1. lilyshen0722 commented on Aug 30, 2026

    @lilyshen0722
    ContributorAuthor

    Closing the ruleset caveat I left in the issue body — checked, and it does not change anything.

    GET /repos/Team-Commonly/commonly/rules/branches/main returns []. Zero rules apply to main, so nothing is enforced outside the legacy protection payload and every claim above stands as written.

    There is a ruleset, which is worth knowing about but is not a dormant fix:

    { "name": "main", "enforcement": "disabled",
      "conditions": { "ref_name": { "include": ["refs/heads/main"] } },
      "rules": [ { "type": "deletion" }, { "type": "non_fast_forward" } ],
      "created_at": "2026-04-29T20:41", "updated_at": "2026-04-29T20:42" }

    Created and last touched one minute apart on 2026-04-29, then left disabled ever since. Before anyone reaches for "just enable the ruleset that's already there" — it would add nothing. Its two rules, deletion and non_fast_forward, are already covered by the legacy protection: allow_deletions: false and allow_force_pushes: false are both set. It duplicates protection that is on, and contains none of the five gaps this issue is about.

    So the decision is unchanged and still needs making deliberately: promoting checks into contexts, and required_conversation_resolution, are the two cheap ones.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions