Skip to content

feat: emit PENDING status on invitation resources - #184

Draft
mstanbCO wants to merge 2 commits into
mainfrom
mstanbCO/IGA-1212/emit-pending-invitation-status
Draft

feat: emit PENDING status on invitation resources#184
mstanbCO wants to merge 2 commits into
mainfrom
mstanbCO/IGA-1212/emit-pending-invitation-status

Conversation

@mstanbCO

Copy link
Copy Markdown
Contributor

What changes

Pending GitHub org invitations are modeled as a dedicated invitation resource type. Until now they had no way to say "pending": the connector emitted UserTrait_Status_STATUS_UNSPECIFIED through the deprecated WithStatus trait option, with a //nolint:staticcheck suppression, because the SDK had no PENDING value and NewUserTrait force-defaults an unset trait status to ENABLED.

The SDK now has that value, so invitations emit it explicitly:

  • Resource levelWithResourceStatus(v2.Status_RESOURCE_STATUS_PENDING, <detail>), the same non-deprecated option the accepted-member path in user.go uses.
  • Trait levelWithDetailedStatus(v2.UserTrait_Status_STATUS_PENDING, <detail>). Still needed: SDK mirroring runs trait → resource, not resource → trait, so setting only the resource status would leave the trait defaulted to ENABLED. Both trait-status options remain deprecated in the SDK, so the //nolint stays, but its justification is now narrow and accurate.

The <detail> string is the existing invitation_status value, so consumers can tell the two flavors apart:

Invitation Resource status Trait status Status details
Pending acceptance RESOURCE_STATUS_PENDING STATUS_PENDING invitation_pending_acceptance
Expired RESOURCE_STATUS_PENDING STATUS_PENDING invitation_expired

Expired invitations stay PENDING rather than getting a distinct enum value. An expired invite is still not a usable account, and the details field preserves the distinction without inventing new enum semantics.

What changes for consumers

Invitation resources move from STATUS_UNSPECIFIED to STATUS_PENDING, at both the trait and the resource level. Nothing else about the resource changes — profile, IDs, entitlements, grants, and provisioning are untouched.

Dependency

Depends on ConductorOne/baton-sdk#1104, which adds UserTrait_Status_STATUS_PENDING = 4 and Status_RESOURCE_STATUS_PENDING = 4.

The SDK is currently pinned by pseudo-version (v0.25.1-0.20260825204020-991ca45253a7) in its own commit, separate from the code change, so swapping to the tagged release before undrafting is a one-commit change.

Rollout ordering

c1 should ship its PENDING handling before this connector releases. That handling treats explicit UNSPECIFIED as pending too, so the ordering is safe in both directions — a c1 that understands PENDING reads today's UNSPECIFIED invitations correctly, and a connector emitting PENDING against a c1 that already handles it is a no-op change in behavior.

Testing

pkg/connector/invitation_test.go asserts both status levels for both fixtures via a new requireInvitationPending helper. Verified the trait-level assertion is load-bearing: removing the WithDetailedStatus line makes the trait status come back as 1 (ENABLED) instead of 4 (PENDING) while the resource level stays PENDING — exactly the trap the removed comment described — and three subtests fail.

go build ./cmd/...   # ok
go test ./...        # ok
golangci-lint run ./pkg/connector/...  # only a pre-existing gosec finding in repository.go, untouched here

🤖 Generated with Claude Code

mstanbCO and others added 2 commits August 25, 2026 20:48
Point go.mod at the baton-sdk branch that adds
UserTrait_Status_STATUS_PENDING and Status_RESOURCE_STATUS_PENDING so
invitation resources can express a pending state. Kept as its own commit
so swapping to a tagged SDK release is a single-commit change.

Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Invitation resources previously emitted STATUS_UNSPECIFIED via the
deprecated trait-level WithStatus option, because the SDK had no way to
express "pending" and NewUserTrait force-defaults an unset trait status
to ENABLED.

Now that the SDK has a PENDING value, emit it explicitly:

  - resource level via the non-deprecated WithResourceStatus, matching
    the accepted-member path in user.go
  - trait level via WithDetailedStatus, which is still required to
    override NewUserTrait's ENABLED default

Both levels carry the invitation_status value (pending acceptance vs
expired) as status details. Expired invitations stay PENDING rather than
getting a distinct enum value: an expired invite is still not a usable
account, and the details field preserves the distinction.

Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown

IGA-1212

// attributes. Expired invitations stay PENDING - they are still not a
// usable account - and carry the distinction in the status details.
resourceSdk.WithResourceProfile(profile),
resourceSdk.WithResourceStatus(v2.Status_RESOURCE_STATUS_PENDING, status),

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.

🟡 Suggestion: docs/connector.mdx:26 still says "if an invitation is pending, the account status will be shown as Unspecified", which this change makes wrong. Update it to Pending in this PR so the docs don't lag the emitted status.

Separately, both status fields carry defined_only enum validation, so a reader still on the pre-PENDING proto descriptor would reject enum value 4 rather than fall back to unspecified. The PR description covers the c1-first rollout ordering, but nothing in the connector enforces it — worth confirming that ordering is tracked before this leaves draft, since there is no config gate to fall back to.

Comment thread go.mod

require (
github.com/conductorone/baton-sdk v0.24.6
github.com/conductorone/baton-sdk v0.25.1-0.20260825204020-991ca45253a7

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.

🟡 Suggestion: two things on this bump.

  1. It pins an unreleased pseudo-version (v0.25.1-0.20260825204020-991ca45253a7). The PR already commits to swapping to the tagged release before undrafting — flagging so it isn't lost.
  2. The bump is scoped in the description to "adds the two PENDING enum values", but v0.24.6 → v0.25.x also changes runtime behavior the connector inherits: the default dotc1z storage engine flips from SQLite to Pebble for new files (pkg/dotc1z/engine_registry.go, pkg/field/defaults.go), and the --diff-syncs / --base-sync-id / --applied-sync-id flags are removed along with diff-sync support. Nothing in this repo references those flags, so there is no build or CI break, but the storage-engine default change affects the artifact this connector produces and is worth calling out in the PR description and release notes rather than shipping silently.

@github-actions

Copy link
Copy Markdown
Contributor

Connector PR Review: feat: emit PENDING status on invitation resources

Blocking Issues: 0 | Suggestions: 3 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base acdfc4b64ace.
Review mode: full
View review run

Review Summary

Scanned the full PR diff for security and correctness: the connector change (invitation.go, connector.go, invitation_test.go), the go.mod/go.sum manifest, and the vendored baton-sdk bump. The status change itself is correct — NewUserResource applies caller resource options before WithUserTrait, and syncUserTraitToResource only mirrors trait status when the resource has none, so the explicit WithResourceStatus(PENDING, status) wins while the trait WithDetailedStatus(PENDING, status) sets the same value with the same details. Both levels agree, and the new requireInvitationPending helper asserts exactly that. No security or correctness defects found. The three suggestions cover the docs lagging the new status, the unreleased pseudo-version pin, and behavior changes the SDK bump carries beyond the two PENDING enum values.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

  • docs/connector.mdx:26 — still documents pending invitations as showing status Unspecified; this PR changes that to Pending.
  • pkg/connector/invitation.go:77 — both status fields carry defined_only enum validation, so a reader on the pre-PENDING descriptor rejects enum 4 rather than degrading; the c1-first rollout ordering is documented in the PR but is not enforced by any config gate.
  • go.mod:6 — unreleased pseudo-version pin (already acknowledged), and the v0.24.6 to v0.25.x bump also flips the default dotc1z storage engine from SQLite to Pebble and removes the --diff-syncs / --base-sync-id / --applied-sync-id flags. No repo code references those flags, so nothing breaks, but the engine default change is not mentioned in the PR description.
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

Suggestions:

In `docs/connector.mdx`:
- Around line 26: The sentence stating that if an invitation is pending the account
  status will be shown as **Unspecified** is now stale. This PR emits
  Status_RESOURCE_STATUS_PENDING / UserTrait_Status_STATUS_PENDING for invitation
  resources instead of UNSPECIFIED. Update the sentence to say the account status
  will be shown as **Pending**, and optionally note that expired invitations also
  report Pending, distinguished only by `invitation_expired` in the status details.

In `pkg/connector/invitation.go`:
- Around line 70-77: Both UserTrait.Status.status and Status.status carry
  protovalidate defined_only enum rules, so any consumer still compiled against the
  pre-PENDING proto descriptor rejects enum value 4 outright rather than treating
  it as unspecified. The code is correct as written; the action is to confirm the
  c1-side PENDING handling is deployed before this connector releases, since there
  is no config flag to fall back to UNSPECIFIED. If that ordering cannot be
  guaranteed, gate the PENDING emission behind an opt-in config field that
  defaults to the previous UNSPECIFIED behavior.

In `go.mod`:
- Around line 6: Two items. (1) The baton-sdk dependency is pinned to the
  unreleased pseudo-version v0.25.1-0.20260825204020-991ca45253a7; swap it to the
  tagged release and re-run go mod tidy and go mod vendor before this leaves draft.
  (2) The v0.24.6 to v0.25.x bump carries behavior changes beyond the two PENDING
  enum values: the default dotc1z storage engine changes from SQLite to Pebble for
  newly created c1z files (see
  vendor/github.com/conductorone/baton-sdk/pkg/dotc1z/engine_registry.go and
  vendor/github.com/conductorone/baton-sdk/pkg/field/defaults.go), and the
  --diff-syncs / --base-sync-id / --applied-sync-id flags plus diff-sync task
  support are removed. No code in this repo references those flags, so there is no
  build break; add the storage-engine default change to the PR description and
  release notes so it is not shipped silently.

@github-actions github-actions Bot 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.

No blocking issues found.

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