fix(github-bot): use numeric user ID in prompt authorId#577
Conversation
Follow-up to #570. Show a toast when archiving fails instead of silently logging to console. Add a failure-path test, clarify the split cache responsibility with a comment, and narrow the dialog onConfirm type.
Switch authorId from `github:${sender.login}` to `github:${sender.id}`
in all four handler functions. This aligns with D1 user_identities which
are keyed by provider_user_id (numeric), enabling the router-level
identity enrichment in the next step of the attribution fix. Numeric IDs
are also stable — logins can be renamed.
📝 WalkthroughWalkthroughThe PR changes GitHub webhook handlers to use numeric sender Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
Terraform Validation Results
Pushed by: @ColeMurray, Action: |
There was a problem hiding this comment.
Summary
PR Title and number: fix(github-bot): use numeric user ID in prompt authorId (#577)
Author: @ColeMurray
Files changed: 6 files, +58 / -12
This updates the github-bot handlers to use the stable numeric GitHub user ID in authorId, and includes a small web follow-up to surface archive failures to users. I reviewed the changed files and did not find any correctness, security, performance, or maintainability issues that should block merging.
Critical Issues
None.
Suggestions
None.
Nitpicks
None.
Positive Feedback
- The
authorIdchange is applied consistently across all four github-bot entry points, and the handler assertions were updated to match. - The archive-session follow-up improves user-facing behavior by replacing silent console logging with an error toast.
- The added failure-path sidebar test covers the key regression risk for the web change.
Questions
None.
Verdict
Approve
Verified locally:
npm test -w @open-inspect/github-bot: all 100 tests passednpm test -w @open-inspect/web -- session-sidebar: all 6 tests passed
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/web/src/lib/archive-session.ts (1)
44-46: Preserve diagnostics in catch path.Line 44 swallows the thrown error entirely. Keep the toast, but also capture/log the exception so production failures remain debuggable.
Suggested patch
- } catch { + } catch (error) { + console.error("Failed to archive session", error); toast.error("Failed to archive session"); return false; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/web/src/lib/archive-session.ts` around lines 44 - 46, The catch block in archiveSession currently swallows exceptions; update the catch to accept the error (e.g., catch (err)) and log or report it before returning false — for example call console.error("Failed to archive session", err) and, if a telemetry helper (Sentry.captureException or appLogger) exists in this module, forward the error there as well — keep the existing toast.error call and then return false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/web/src/components/archive-session-dialog.tsx`:
- Line 17: The onConfirm prop on ArchiveSessionDialog is currently typed as ()
=> void but callers pass async functions; update its type to () => Promise<void>
(or () => void | Promise<void> if you prefer to allow sync handlers) in the
component props/interface (the onConfirm declaration) and then ensure the local
confirmation handler that invokes onConfirm (e.g., the component's
confirm/handleConfirm function) awaits the call (make it async) so the async
contract is respected.
---
Nitpick comments:
In `@packages/web/src/lib/archive-session.ts`:
- Around line 44-46: The catch block in archiveSession currently swallows
exceptions; update the catch to accept the error (e.g., catch (err)) and log or
report it before returning false — for example call console.error("Failed to
archive session", err) and, if a telemetry helper (Sentry.captureException or
appLogger) exists in this module, forward the error there as well — keep the
existing toast.error call and then return false.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 86a19bfd-9009-4f1e-837b-09cc95b6b0dd
📒 Files selected for processing (6)
packages/github-bot/src/handlers.tspackages/github-bot/test/handlers.test.tspackages/web/src/components/archive-session-dialog.tsxpackages/web/src/components/session-sidebar.test.tsxpackages/web/src/components/session-sidebar.tsxpackages/web/src/lib/archive-session.ts
Terraform Validation Results
Pushed by: @ColeMurray, Action: |
## Summary Fixes git commit and PR attribution for bot-originated sessions (GitHub, Slack, Linear). This is Steps 2-4 of the attribution fix plan (Step 1 was #577). **Problem**: Bot sessions created owner participants with `user_id: "anonymous"` (unfindable by later prompts), and resolved D1 identity was discarded — never forwarded to the DO. Commits showed incorrect author identity and PRs always opened as `open-inspect[bot]`. **Solution**: Enrich participants with linked GitHub identity at two points: - **Session creation** (owner): `deriveUserId()` constructs canonical userId matching prompt `authorId` format. `resolveGitHubEnrichment()` looks up linked GitHub identity from D1 (display name, email, OAuth tokens) and forwards to DO init. - **Prompt time** (non-owner): `parseAuthorId()` extracts provider info from bot authorIds, resolves linked GitHub identity, and forwards enrichment fields through `EnqueuePromptRequest` to the DO for COALESCE update. **Key design decisions**: - Email resolution: actual GitHub identity email preferred, noreply format as fallback - D1 queries parallelized where independent (`getUserById` + `getEncryptedTokens`) - Best-effort enrichment (try/catch) — D1 failures degrade gracefully to existing behavior - No DO changes needed — existing init/COALESCE/token refresh infrastructure handles enriched data - Web client path unaffected — `parseAuthorId` returns null for plain user IDs, `deriveUserId` passes through explicit `userId` ### Files changed | File | Change | |---|---| | `router.ts` | `deriveUserId()`, `parseAuthorId()`, `resolveGitHubEnrichment()`, session creation + prompt time enrichment | | `user-scm-tokens.ts` | `getEncryptedTokens()` — returns raw D1 ciphertext without decrypting | | `message.service.ts` | Expand `EnqueuePromptRequest` with identity + token fields | | `message-queue.ts` | Use `EnqueuePromptRequest` type, COALESCE update for non-owner participants | | `router.identity.test.ts` | 16 tests for `parseAuthorId` and `deriveUserId` | | `user-scm-tokens.test.ts` | 2 tests for `getEncryptedTokens` | | `message-queue.test.ts` | 4 tests for `enqueuePromptFromApi` enrichment path | ## Test plan - [x] `parseAuthorId` — 7 cases (github/slack/linear parse, web client null, anonymous null, unknown prefix null, empty null) - [x] `deriveUserId` — 9 cases (explicit userId, each bot with/without identity fields, unknown source, empty input) - [x] `getEncryptedTokens` — round-trip returns ciphertext not plaintext, null for unknown user - [x] `enqueuePromptFromApi` — COALESCE runs with enrichment fields, skipped without them, `authorDisplayName` used for new participant name, fallback to `authorId` - [x] Typecheck passes (only pre-existing `CacheStore` errors) - [x] 1019 control-plane unit tests pass (10 pre-existing failures in `models.test.ts`) - [x] 93 github-bot tests pass (7 pre-existing failures in `webhook.test.ts`) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * GitHub identity enrichment during session creation; bot-origin prompts are optionally enriched with author display/email/login and SCM token/identity fields. * Canonical user ID derivation for bot authors. * Prompt requests now carry enrichment fields; participant creation prefers provided display name and will coalesce SCM identity/token data when present. * **Tests** * Added tests for identity parsing, canonical user ID derivation, encrypted token retrieval, and message-queue participant coalescing behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Changes `authorId` in all 4 github-bot handlers from
`github:${sender.login}` to `github:${sender.id}`, using the stable
numeric GitHub user ID instead of the mutable login name.
- This is **Step 1** of the [git attribution fix
plan](docs/internal/2026-04-27-git-attribution-fix-plan.md). The D1
`user_identities` table is keyed by `provider_user_id` (numeric), so
this change enables the router-level identity enrichment in Step 2 to
resolve user identity via `UserStore.getIdentity(provider,
providerUserId)` for all three bot types without additional query
methods.
### Affected handlers
| Handler | Trigger |
|---|---|
| `handleReviewRequested` | Review assignment on a PR |
| `handlePullRequestOpened` | Auto-review when a PR is opened |
| `handleIssueComment` | Issue or PR conversation comment |
| `handleReviewComment` | PR review comment |
### Backward compatibility
Existing participants created with the old `github:${login}` format will
not be found by `getByUserId("github:${id}")`, so a new participant will
be created on the next prompt. This is expected — the enrichment in Step
2/3 will create it with correct identity data.
## Test plan
- [x] All 39 handler tests pass with updated assertions (`github:alice`
→ `github:1001`, etc.)
- [ ] Verify no regressions in CI (lint, typecheck, full test suite)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved error messaging when archiving sessions fails—users now
receive toast notifications instead of silent failures.
* **Tests**
* Added coverage for archive failures on mobile devices.
* **Refactor**
* Updated author identification mechanism in GitHub integrations.
* Simplified callback signatures for dialog components.
* **Documentation**
* Added clarifying comments to session management code.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…eMurray#579) ## Summary Fixes git commit and PR attribution for bot-originated sessions (GitHub, Slack, Linear). This is Steps 2-4 of the attribution fix plan (Step 1 was ColeMurray#577). **Problem**: Bot sessions created owner participants with `user_id: "anonymous"` (unfindable by later prompts), and resolved D1 identity was discarded — never forwarded to the DO. Commits showed incorrect author identity and PRs always opened as `open-inspect[bot]`. **Solution**: Enrich participants with linked GitHub identity at two points: - **Session creation** (owner): `deriveUserId()` constructs canonical userId matching prompt `authorId` format. `resolveGitHubEnrichment()` looks up linked GitHub identity from D1 (display name, email, OAuth tokens) and forwards to DO init. - **Prompt time** (non-owner): `parseAuthorId()` extracts provider info from bot authorIds, resolves linked GitHub identity, and forwards enrichment fields through `EnqueuePromptRequest` to the DO for COALESCE update. **Key design decisions**: - Email resolution: actual GitHub identity email preferred, noreply format as fallback - D1 queries parallelized where independent (`getUserById` + `getEncryptedTokens`) - Best-effort enrichment (try/catch) — D1 failures degrade gracefully to existing behavior - No DO changes needed — existing init/COALESCE/token refresh infrastructure handles enriched data - Web client path unaffected — `parseAuthorId` returns null for plain user IDs, `deriveUserId` passes through explicit `userId` ### Files changed | File | Change | |---|---| | `router.ts` | `deriveUserId()`, `parseAuthorId()`, `resolveGitHubEnrichment()`, session creation + prompt time enrichment | | `user-scm-tokens.ts` | `getEncryptedTokens()` — returns raw D1 ciphertext without decrypting | | `message.service.ts` | Expand `EnqueuePromptRequest` with identity + token fields | | `message-queue.ts` | Use `EnqueuePromptRequest` type, COALESCE update for non-owner participants | | `router.identity.test.ts` | 16 tests for `parseAuthorId` and `deriveUserId` | | `user-scm-tokens.test.ts` | 2 tests for `getEncryptedTokens` | | `message-queue.test.ts` | 4 tests for `enqueuePromptFromApi` enrichment path | ## Test plan - [x] `parseAuthorId` — 7 cases (github/slack/linear parse, web client null, anonymous null, unknown prefix null, empty null) - [x] `deriveUserId` — 9 cases (explicit userId, each bot with/without identity fields, unknown source, empty input) - [x] `getEncryptedTokens` — round-trip returns ciphertext not plaintext, null for unknown user - [x] `enqueuePromptFromApi` — COALESCE runs with enrichment fields, skipped without them, `authorDisplayName` used for new participant name, fallback to `authorId` - [x] Typecheck passes (only pre-existing `CacheStore` errors) - [x] 1019 control-plane unit tests pass (10 pre-existing failures in `models.test.ts`) - [x] 93 github-bot tests pass (7 pre-existing failures in `webhook.test.ts`) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * GitHub identity enrichment during session creation; bot-origin prompts are optionally enriched with author display/email/login and SCM token/identity fields. * Canonical user ID derivation for bot authors. * Prompt requests now carry enrichment fields; participant creation prefers provided display name and will coalesce SCM identity/token data when present. * **Tests** * Added tests for identity parsing, canonical user ID derivation, encrypted token retrieval, and message-queue participant coalescing behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
authorIdin all 4 github-bot handlers fromgithub:${sender.login}togithub:${sender.id}, using the stable numeric GitHub user ID instead of the mutable login name.user_identitiestable is keyed byprovider_user_id(numeric), so this change enables the router-level identity enrichment in Step 2 to resolve user identity viaUserStore.getIdentity(provider, providerUserId)for all three bot types without additional query methods.Affected handlers
handleReviewRequestedhandlePullRequestOpenedhandleIssueCommenthandleReviewCommentBackward compatibility
Existing participants created with the old
github:${login}format will not be found bygetByUserId("github:${id}"), so a new participant will be created on the next prompt. This is expected — the enrichment in Step 2/3 will create it with correct identity data.Test plan
github:alice→github:1001, etc.)Summary by CodeRabbit
Bug Fixes
Tests
Refactor
Documentation