When a member wasn't in the guild at create() time (or drifted afterwards),
read() only reported the drift. Pulumi would not trigger update() because
the inputs hadn't changed, so the member stayed permanently out of sync.
Call syncMemberRoles() directly from read() when drift is detected so roles
are applied on the next refresh without needing an input change.
Cloudflare edge errors (5xx, edge-level 429s) return plain-text bodies
like "upstream connect error..." or "error code: 1015" that crash a
naive response.json() call. Add a tryParseJson helper and use it for
all error paths so we surface the actual HTTP status/body instead of a
JSON parse exception.
Also add exponential backoff retry for 5xx responses, matching the
existing 429 retry behavior.
🏠 Remote-Dev: homespace
The 250ms jitter was too narrow for ~68 parallel refresh reads — all
retries clustered after the same retry_after and collided again. Scale
jitter linearly with attempt number so the herd progressively spreads
out across attempts.
Split `make up` into two phases: a plain `pulumi up` first, then
`pulumi up --refresh`. Dynamic providers serialize their implementation
into state at create/update time. During refresh, the serialized `read()`
runs — so any provider-code change (like the Discord 429 retry logic)
doesn't take effect until a successful update writes the new `__provider`
to state. Running refresh first used the stale serialized code from the
last successful deploy, which had no rate-limit handling, failing on
parallel Discord member lookups.
Also bump discordFetch maxRetries from 5 to 10 for headroom against ~68
parallel refresh reads.
- Handle 429 responses from Discord API with automatic retry based on
retry_after, preventing transient failures during role sync
- Surface underlying error details in role and member role read failures
to make debugging easier
- Bump @pulumi/github to ^6.11.0 (resolves 6.12.1)
🏠 Remote-Dev: homespace
- Add discordImplies field to Role interface for Discord-specific role hierarchy
- Add expandDiscordRoles() function to traverse both GitHub parent chain
and discordImplies relationships when syncing Discord roles
- SDK_MAINTAINERS now implies MAINTAINERS for Discord
This ensures that users with SDK roles (GO_SDK, PYTHON_SDK, etc.) automatically
get the 'maintainers (synced)' Discord role through:
GO_SDK -> SDK_MAINTAINERS (via GitHub parent) -> MAINTAINERS (via discordImplies)
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 5
Claude-Permission-Prompts: 1
Claude-Escapes: 0
When a user with a Discord ID leaves the server (or the ID is invalid),
the sync now logs a warning and continues instead of failing the entire
deploy. This prevents deploys from breaking when someone leaves Discord.
The memberNotFound flag is tracked in Pulumi state so we can identify
stale Discord IDs.
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 0
Claude-Permission-Prompts: 0
Claude-Escapes: 0
* Add Discord role sync with role-centric architecture
This refactors the access management to use a role-centric model where
each role explicitly defines which platforms it exists on (GitHub,
Discord, Google).
Key changes:
- Add Discord role sync via Pulumi Dynamic Provider
- Replace groups.ts with roleIds.ts + roles.ts for type-safe role definitions
- Use ROLE_IDS constants to prevent typos in memberOf references
- Each role has platform-specific config (github/discord/google keys)
- Discord-only roles (e.g., administrators) and GitHub-only roles
(e.g., typescript-sdk-auth) are now explicitly modeled
- Add PR preview workflow to show Pulumi changes before merge
- Add test-config.ts for config structure validation
- Fix typo: docs-maintaners -> docs-maintainers
Discord roles will match existing server roles:
- administrators, lead maintainers, core maintainers
- sdk maintainers, working groups, interest groups
- etc.
Requires new GitHub secrets:
- DISCORD_BOT_TOKEN
- DISCORD_GUILD_ID
* Fix GitHub team ordering and make Discord integration optional
- Add topological sort for GitHub teams to ensure parent teams are
created before child teams, fixing the registry-wg parentTeamId issue
- Make Discord integration optional by using config.get() instead of
config.require(), allowing CI preview to succeed without Discord secrets
- Add sortRolesByGitHubDependency() utility function in config/utils.ts
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=20%)
Claude-Steers: 2
Claude-Permission-Prompts: 1
Claude-Escapes: 0
* Reconcile Discord roles for listed users
Replace per-role-membership approach with per-user role sync that:
- Adds missing roles users should have
- Removes managed roles users shouldn't have
- Only touches roles defined in config (won't affect admin roles, etc.)
Users not in config remain untouched.
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 4
Claude-Permission-Prompts: 1
Claude-Escapes: 0
* Fix descriptions to match existing resources
* Cache Pulumi plugins and fix GITHUB_TOKEN for pulumi install
* Remove accidentally committed state backup
* Use github.token for pulumi install
* Fix formatting
Fix deploy: add pulumi login before config set
Fix deploy: set PULUMI_CONFIG_PASSPHRASE_FILE for config commands
Import existing docs-maintainers team (remove after deploy)
Remove docs-maintainers import workaround
Add docs-maintainers cleanup to state cleanup
Add pulumi refresh before up to sync state with reality
Rename Discord roles to add (synced) suffix
Cleanup Makefile and add lead-maintainers GitHub team