Skip to content

Fix/branch gates org inherited - #1044

Merged
hyperpolymath merged 4 commits into
mainfrom
fix/branch-gates-org-inherited
Sep 23, 2026
Merged

hyperpolymath merged 4 commits into
mainfrom
fix/branch-gates-org-inherited

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Closes #

Type of change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (would change existing behaviour)
  • 🕳️ Soundness fix (fixes a checker/proof false-negative)
  • 📖 Documentation
  • 🧹 Refactor / tech debt (behaviour-preserving)
  • ⚡ Performance
  • 🔧 Build / CI / tooling

How has this been verified?

Checklist

  • My commits are signed (git commit -S).
  • I ran the project's own checks/tests locally and they pass.
  • New files carry the correct SPDX-License-Identifier (code/config MPL-2.0,
    prose CC-BY-SA-4.0); I did not relicense existing files.
  • Docs are updated, and no public claim now overstates what the code does.
  • I have not introduced a soundness hole (or I have flagged where I might have).

Notes for reviewers

hyperpolymath and others added 2 commits September 23, 2026 00:45
repos/{r}/rulesets RETURNS the organisation's rulesets alongside the
repository's own. An inherited one reads back IN FULL at
repos/{r}/rulesets/{id}, so every GET succeeds and nothing warns you --
and the PUT to that same path 404s.

apply-branch-gates.sh selected `.target=="branch" and .enforcement=="active"`
with no ownership filter, so on every metadatastician repo it picked up the
org-level EstateBranching (18225024) and then PUT to the repo path. That is
the failure already measured 67 times on this estate, once per reached repo.

The discriminator is .source_type, which the LIST endpoint does return --
verified against the live API, every entry carries it. The population is now
fetched and classified LOCALLY; a server-side select whose empty result is
also its success result cannot fail closed.

Three outcomes, none of them a guess:

  * only inherited rulesets active  -> ORG-INHERITED, naming the ids and the
    /orgs/{org}/rulesets/{id} endpoint. The cure is at the org, applied ONCE;
    67 doomed per-repo writes are not a smaller version of it.
  * inherited BESIDE a repo-level one -> the repo-level one is selected and
    the inherited one is REPORTED, because rulesets are additive and it goes
    on enforcing. Counting both as candidates would have turned an ordinary
    repo into AMBIGUOUS and left it permanently ungated.
  * .source_type absent -> UNKNOWN. Writability is exactly what that field
    decides; defaulting a missing one to the writable arm is a silent 404.

Verified report-only against the live estate: metadatastician/paint-type,
whose three active rulesets are ALL source_type=Organization, now reports
ORG-INHERITED instead of attempting the write.

Tests: CASE 12/13/14 plus MUTANT F, which restores the original selection and
dies by PUTting to repos/{r}/rulesets/18225024 -- the 404 reproduced. Cases 12
and 13 deliberately ship NO fixture for the inherited ruleset's own path, so
the shim's exit-1-on-missing-fixture asserts the fixed applier never reads it.

The 14 existing rulesets-list fixtures omitted source_type, which the live API
always sends; they are now faithful to it. 46/46 green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR
Two hardening follow-ups on the org-inherited fix:

1. `grep -P` is a GNU extension. The classification of active branch
   rulesets into repo-level vs org-inherited is the load-bearing step of
   this script; it must not depend on which grep the runner ships. awk
   with an explicit `-F'\t'` does the same field test and is portable.
   Mutant F is retargeted at the awk line so the control still dies.

2. The ORG-INHERITED detail named the endpoint but not the credential.
   An org-level ruleset write needs `admin:org`; a repo-scoped token
   reads the ruleset in full and cannot write it, which is precisely the
   asymmetry that produced the original 404. Saying so in the report
   line saves the operator a second 403 chase.

Suite: 46/46, mutant F still killed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR
@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@hyperpolymath
hyperpolymath enabled auto-merge (squash) September 23, 2026 09:29
Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath merged commit db10ad3 into main Sep 23, 2026
43 of 45 checks passed
@hyperpolymath
hyperpolymath deleted the fix/branch-gates-org-inherited branch September 23, 2026 09:33
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.

1 participant