validate-org: reject a plain member team role for an organisation owner - #75
Merged
Conversation
GitHub reports organisation owners as maintainers of every team they belong to, so a team membership declaring the member role for an owner never settles. Apply reports success, GitHub keeps maintainer, and the next plan proposes the same change again, indefinitely. Nothing objected to it. The schema accepts either team role with no relationship to the person's organisation role, and both facts sit in the same file, so this is catchable without any API call. The omitted role is covered too: Terraform reads a missing team role as member, and the importer omits it for plain members, so leaving it out produces exactly the same non-convergence as writing it. Two existing fixtures encoded that shape and asserted it was valid. They were wrong - the same shape was observed producing a perpetual diff against a real organisation - so both now set the role explicitly.
…Role The previous commit removed the wrong one. members.go now carries no comment change relative to main.
dev-milos
marked this pull request as ready for review
August 14, 2026 11:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes G-Research/gr-oss#1434
GitHub reports organisation owners as maintainers of every team they belong to, so a team
membership declaring the
memberrole for an owner never settles:1 added, 0 changed, 1 destroyedmaintainer1 to changeon that membershipNothing objected to it. The schema accepts either team role with no relationship to the
person's organisation role, and validation passed cleanly. The importer never generates this
shape — it reads
maintainerfrom GitHub — so it only arises from editingmembers.yamlbyhand, which is the normal way to manage membership.
Why it is worth a rule rather than a note
The damage is to trust in the plan. A workspace that never reaches "no changes" trains
reviewers to skim past
1 to change, which is precisely the habit that makes a genuinedestroy easy to miss — on the plans that decide who belongs to the organisation.
Both facts live in the same file, so this needs no API call:
The omitted role counts too
members.tfreads a missing team role asmember, and the importer omits it for plainmembers. Leaving it out therefore produces exactly the same non-convergence as writing it, so
both are rejected — with different messages, since the cause differs:
role: member... cannot use role "member": ...... needs role "maintainer": the role defaults to "member" when omitted, ...Two existing fixtures had to change
Both encoded an owner with an omitted team role and asserted it was valid:
TestMembersConfigValidate/ "valid config with team member and maintainer roles"TestValidateOrg_StagedMembersResolveAgainstPromotedTeamsChanging existing tests to make a new rule pass is exactly how a mistake gets hidden, so it is
worth being explicit: the claim is that the fixtures were wrong, not the rule. The same
shape was observed producing a perpetual diff against a real organisation. Both now set the
role explicitly, which also makes them realistic — the importer always writes it.
That it was baked into two fixtures independently is itself a sign the constraint was not
known.
Tests
Three cases added, including one asserting the rule does not over-reach: a plain member may
hold either team role.
Verified against the real configuration that produced the perpetual diff — it now fails at
validate, before the plan:The organisation's current config still validates cleanly, since the importer wrote explicit
roles throughout.