graformer: name organisation resources in the plan summary - #74
Merged
Conversation
The formatter knew nine repository-level resource types and none of the three the organisation feature introduced, so every team, membership and team membership fell through to the unknown-type branch. That is the output a reviewer reads when approving a change to who belongs to the organisation. The bootstrap import rendered entirely as unknown-type lines, and so does the case that matters more: a plan removing a person shows an address rather than a statement of who is being removed. The membership role is included because promotion and demotion between admin and member are otherwise invisible in the summary. Team membership takes its name from the resource address, which already carries "<username>/<team>". teamMap covers only data.github_team lookups, so organisation teams are absent from it and would render as undefined.
Two defects in the organisation formatting. Updates are passed change.before, so the role rendered for a promotion was the one being replaced: member to admin printed (member), and admin to member printed (admin) - inverted, on the single line a reviewer uses to approve an organisation membership change. A team rename printed the old name for the same reason. This did not show up before because every previously handled type renders only stable identity fields, where before and after agree. Both sides are now read, and a change is shown as a transition. The membership key pattern was unanchored, so it took the first bracketed key in the address. That is correct while these resources are root-level, but if they ever moved into a keyed module it would silently return the module key, and the fallback would not fire because a match did occur. It is now anchored on the resource type, the way the branch protection pattern already is.
dev-milos
marked this pull request as ready for review
August 14, 2026 11:05
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#1433
The plan summary formatter knew nine repository-level resource types and none of the three
the organisation feature introduced, so every team, membership and team membership fell
through to the unknown-type branch:
After:
Why it matters
This is the output a reviewer reads when approving a change to who belongs to the
organisation. The bootstrap import rendered entirely as unknown-type lines, and so does the
case that matters more: a plan removing a person shows an address rather than a statement
of who is being removed.
Everything else in the summary was given readable formatting, including the deployment policy
types added most recently. These three were missed.
Two decisions worth a look
The role is included for
github_membership. Promotion and demotion betweenadminandmemberare otherwise invisible in the summary — the line would be identical either way. Sincethe role is a value rather than an identity, both sides of the change are read and a change is
rendered as a transition:
Callers pass
change.beforefor updates, which is harmless for the stable identity fieldsevery other type renders, but would have shown the role being replaced rather than the one
being applied.
Team membership takes its name from the resource address, not
teamMap.teamMapisbuilt from
planJson.data, so it covers onlydata.github_teamlookups; organisation teamsare resources and are absent from it, which would render
undefined. The address alreadycarries
<username>/<team>in exactly the form wanted. The pattern is anchored on the resourcetype, the way the branch protection one already is — unanchored it would take the first
bracketed key, which is correct while these are root-level but would silently return a module
key if they ever moved, without the fallback firing. There is a comment explaining the choice
so it does not get "corrected" to
teamMaplater.Verification
The function was extracted from the action and exercised against the real address shapes
observed during release testing:
github_team.team["<team>"]github_team :: <team>github_membership.member["<user>"], roleadmingithub_membership :: <user> (admin)membergithub_membership :: <user> (member)github_team_membership.membership["<user>/<team>"]github_team_membership :: <user>/<team> (maintainer)No other branch is touched.