feat: channel-based agent authorization for Slack#2033
Conversation
🦋 Changeset detectedLatest commit: 0381ae0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
PR Review Summary
(4) Total Issues | Risk: Medium
🟠⚠️ Major (3) 🟠⚠️
🟠 1) packages/agents-core Missing changeset for published package
Issue: This PR modifies exported types (SlackAccessTokenPayloadSchema, SignSlackUserTokenParams, BaseExecutionContext) from the published @inkeep/agents-core package. Per CLAUDE.md changeset requirements: "Create a changeset for any user-facing change to a published package."
Why: Without a changeset, version bumps will not be triggered, and consumers will not see release notes for these changes. While the changes are additive and non-breaking, they still modify the public API surface of an npm-published package.
Fix:
pnpm bump patch --pkg agents-core "Add channel-based authorization claims to Slack JWT schema and BaseExecutionContext type"Refs:
Inline Comments:
- 🟠 Major:
slack-user-token.ts:42Schema allows incomplete authorization states —authorized: truewithout requiringauthorizedProjectId - 🟠 Major:
app-mention.test.ts:270Missing test verification of channel auth params passed tosignSlackUserToken
🟡 Minor (1) 🟡
Inline Comments:
- 🟡 Minor:
utility.ts:309-314Type allowsauthorized: falsebut runtime only populates when true
💭 Consider (3) 💭
💭 1) agents-api/src/middleware/runAuth.ts:247 Add debug log when bypass is skipped
Issue: When slackAuthorized evaluates to false, there's no log explaining why the bypass wasn't applied.
Why: Makes it harder to diagnose authorization issues when admins expect channel-based auth to work but it silently falls through to SpiceDB.
Fix: Add debug log with slackAuthorizedClaim, slackAuthorizedProjectId, requestedProjectId, and projectMatch fields.
💭 2) app-mention.ts:242 Consider stricter slackAuthorized condition
Issue: slackAuthorized is set to agentConfig != null, which means ANY resolved agent config triggers it, even if source === 'none'.
Fix: Consider slackAuthorized: agentConfig != null && agentConfig.source !== 'none' to ensure the bypass is only granted when there's true admin-configured channel or workspace default.
💭 3) api-key-auth.test.ts Missing test for authSource default fallback
Issue: The implementation defaults authSource to 'channel' when missing (line 313), but no test verifies this fallback behavior.
Fix: Add test case for authorized: true with missing authSource to document expected behavior.
🚫 REQUEST CHANGES
Summary: This PR implements a well-designed channel-based authorization bypass for Slack with appropriate security controls (project binding, JWT signature verification, short TTL, graceful fallback to SpiceDB). The security architecture is sound. However, there are 3 major issues to address before merging:
- Missing changeset — Required for any change to published packages
- Schema invariant enforcement — The JWT schema should enforce that
authorized: truerequiresauthorizedProjectId - Test coverage gap — The app-mention tests don't verify the critical channel auth params are passed correctly
Once these are addressed, this PR is ready to ship. 🚀
Discarded (12)
| Location | Issue | Reason Discarded |
|---|---|---|
runAuth.ts:243-280 |
Security design is sound | INFO — positive validation, not an issue |
runAuth.ts:282-296 |
Auth success logging follows patterns | INFO — confirms conventions |
slack-user-token.ts:38-41 |
JWT claim naming follows conventions | INFO — confirms conventions |
slack-user-token.ts:57-60 |
SignSlackUserTokenParams follows prefixing convention | INFO — confirms conventions |
utility.ts:309-314 |
metadata.slack follows established pattern | INFO — confirms conventions |
api-key-auth.test.ts:664-996 |
Test coverage follows established patterns | INFO — confirms conventions |
runAuth.ts:307-318 |
Authorization source metadata enables audit trails | INFO — positive validation |
runAuth.ts:243-280 |
Channel auth bypass is architecturally sound | INFO — positive validation |
relationTools.ts |
Sub-agent delegation inherits bypass without re-verification | Acceptable design per D9 — documented in PR description |
runAuth.ts:243-280 |
Establishes precedent for capability-based authorization | INFO — observation for future reference |
slack-user-token.test.ts |
Missing test for partial claims (authorized without authorizedProjectId) | Already covered by auth middleware tests — implicit coverage |
api-key-auth.test.ts |
Missing test for JWT verification throwing unexpectedly | Edge case caught by general error handling |
Reviewers (8)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
pr-review-security-iam |
3 | 0 | 1 | 0 | 1 | 0 | 1 |
pr-review-architecture |
5 | 0 | 0 | 0 | 0 | 0 | 5 |
pr-review-standards |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
pr-review-consistency |
6 | 0 | 0 | 0 | 0 | 0 | 6 |
pr-review-breaking-changes |
5 | 1 | 0 | 0 | 0 | 0 | 4 |
pr-review-tests |
5 | 0 | 1 | 0 | 1 | 0 | 3 |
pr-review-types |
2 | 0 | 0 | 0 | 2 | 0 | 0 |
pr-review-errors |
2 | 0 | 1 | 0 | 0 | 0 | 1 |
| Total | 28 | 1 | 3 | 0 | 4 | 0 | 20 |
Note: Schema invariant finding merged from pr-review-types and pr-review-security-iam (same underlying issue).
Documents the new channel-based agent authorization feature from PR #2033. Users in channels with configured default agents are now automatically authorized to execute those agents without needing explicit project membership.
Review Response (commit 39f6eef)Major — All addressed
Minor — Addressed
Consider — Assessed
|
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Re-Review Assessment
This is a re-review scoped to the 2 commits since the last automated review (786a3317, 39f6eef1). The delta addresses all 5 issues raised in the prior review:
| Prior Issue | Status | Verification |
|---|---|---|
🟠 Missing changeset for @inkeep/agents-core |
✅ RESOLVED | .changeset/christian-lime-piranha.md added with correct content |
| 🟠 Schema allows incomplete authorization states | ✅ RESOLVED | .refine() added to enforce authorized: true requires both authorizedProjectId and authSource |
| 🟠 Missing test verification of channel auth params | ✅ RESOLVED | app-mention.test.ts now verifies signSlackUserToken is called with correct params |
🟡 Type allows authorized: false but runtime only populates when true |
✅ RESOLVED | Changed to authorized: true literal type |
| 💭 Add debug log when bypass is skipped | ✅ RESOLVED | Debug log added with slackAuthorizedClaim, slackAuthorizedProjectId, requestedProjectId, and projectMatch fields |
Delta Quality Assessment
The delta changes are well-implemented:
- Schema invariant (
.refine()) is clean and follows Zod best practices - Debug logging includes all relevant fields for debugging authorization issues
- Type narrowing (
authorized: trueliteral) accurately reflects runtime behavior - Test coverage verifies the critical integration point between work app and JWT signing
- Two new tests for schema invariant enforcement (authorized without authorizedProjectId, authorized without authSource)
No New Issues
The delta introduces no new concerns. All changes are focused remediation of prior feedback.
✅ APPROVE
Summary: All prior review feedback has been addressed comprehensively. The implementation is well-designed with appropriate security controls (project binding, JWT signature verification, short TTL, graceful fallback to SpiceDB), proper schema invariants, and good observability. Ready to ship! 🚀
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| (none dispatched — delta is remediation only) | — | — | — | — | — | — | — |
Note: Reviewer dispatch skipped because the delta consists entirely of remediation for prior review feedback. No new functional changes to review.
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Re-Review Assessment
This is a re-review scoped to the 1 commit since the last automated review (39f6eef1). The delta (cdbb8556) adds "Phase 2" channel auth changes:
| Delta Change | Location | Assessment |
|---|---|---|
| New OTel span attribute keys | tracer.ts:41-42 |
✅ Clean — follows existing SLACK_SPAN_KEYS naming conventions |
| Span attributes for app mentions | app-mention.ts:162-163 |
✅ Clean — slack.authorized=true, slack.auth_source={channel|workspace} |
slackAuthorized: false for modal submissions |
modal-submission.ts:94,173,295,330 |
✅ Correct — manual agent selection requires SpiceDB auth |
slackAuthorized: false for /inkeep run and /inkeep list |
commands/index.ts:637,715 |
✅ Correct — explicit commands require SpiceDB auth |
| New modal submission tests | modal-submission.test.ts (200 lines) |
✅ Adequate coverage |
| Extended app mention tests | app-mention.test.ts:+51 lines |
✅ Verifies span attributes and JWT params |
Security Model Validation
The authorization distinctions are correctly implemented:
| Entry Point | slackAuthorized |
SpiceDB Check | Design Intent |
|---|---|---|---|
@mention (admin-configured default) |
true |
Bypassed | Admin delegated auth ✅ |
/inkeep <question> (default agent) |
true |
Bypassed | Admin delegated auth ✅ |
/inkeep run "agent" (explicit agent) |
false |
Required | User selection ✅ |
/inkeep list |
false |
Required | Listing only ✅ |
| Modal submission | false |
Required | User selection ✅ |
| Follow-up submission | false |
Required | User selection ✅ |
Observability
New span attributes enable debugging authorization flows:
slack.authorized(boolean) — low cardinality ✅slack.auth_source('channel'|'workspace') — low cardinality ✅
No New Issues
The delta introduces no new concerns. All changes are well-implemented Phase 2 additions to the channel-based authorization feature.
✅ APPROVE
Summary: Phase 2 channel auth changes are clean and correctly implemented. The authorization model properly distinguishes between admin-configured defaults (slackAuthorized: true → SpiceDB bypass) and user-initiated selections (slackAuthorized: false → SpiceDB required). Test coverage is adequate, observability additions follow conventions, and all prior review feedback remains addressed. Ship it! 🚀
Reviewers (5)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
pr-review-standards |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
pr-review-security-iam |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
pr-review-tests |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
pr-review-sre |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
pr-review-consistency |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Total | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Note: All reviewers found the delta clean with no issues meeting the severity/confidence thresholds.
Enable linked Slack users in channels with configured default agents to execute those agents without explicit SpiceDB project membership. Changes: - Extend JWT schema with channel auth claims (authorized, authSource, channelId, authorizedProjectId) - Add SpiceDB bypass in trySlackUserJwtAuth when channel-authorized with project binding verification (D8) - Extend BaseExecutionContext metadata with slack auth context - Switch app-mention.ts from resolveChannelAgentConfig to resolveEffectiveAgent (D7) and pass channel auth params - Update slash command background exec to pass channel auth params - Add comprehensive tests for bypass logic, project mismatch rejection, and graceful fallback Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Tests were mocking the old resolveChannelAgentConfig from utils, but the implementation now imports resolveEffectiveAgent from agent-resolution. Updated all test cases to mock the correct module and include the required `source` field in mock return values. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Review feedback addressed: Major: - Add .refine() to SlackAccessTokenPayloadSchema enforcing that when authorized=true, authorizedProjectId and authSource are required - Add changeset for agents-core (patch) - Add signSlackUserToken channel auth param verification in app-mention test Minor: - Change BaseExecutionContext.metadata.slack.authorized to literal `true` since the field is only populated when authorization is granted Consider: - Add debug log in runAuth.ts when channel auth bypass is not applied - Add test for authSource default fallback to 'channel' - Add schema invariant enforcement tests (authorized without authorizedProjectId, authorized without authSource) Co-Authored-By: Claude Opus 4.6 <[email protected]>
…+ OTel spans - Modal submissions (call sites 3-4): explicit slackAuthorized: false - /inkeep run and /inkeep list (call sites 6-7): explicit slackAuthorized: false - OTel: add AUTHORIZED and AUTH_SOURCE to SLACK_SPAN_KEYS - Set slack.authorized and slack.auth_source span attributes in app-mention and modal-submission handlers - Add modal-submission test file verifying slackAuthorized: false and span attributes - Add workspace auth source test to app-mention tests Co-Authored-By: Claude Opus 4.6 <[email protected]>
…auth Adds a per-channel-config boolean (default: true) that controls whether channel members get implicit project access via the Slack auth bypass. When disabled, members need explicit project access even if an agent is configured for the channel. Full stack: DB schema + migration, data access, agent resolution, JWT signing, API validation, and Manage UI toggle in channel agent popover. Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Add aria-hidden to decorative ShieldCheck icon - Add grantAccessToMembers to listChannels API return type - Explicitly set grantAccessToMembers: true in bulk set operations - Add test for workspace config with explicit grantAccessToMembers: false - Add command-question tests verifying JWT authorization for grantAccessToMembers true/false cases Co-Authored-By: Claude Opus 4.6 <[email protected]>
…ll-stack toggle Co-Authored-By: Claude Opus 4.6 <[email protected]>
… to Slack docs Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
…ndlers These handlers referenced three undefined functions (findAgentByIdentifier, fetchAgentsFromManageApi, createAgentListMessage) that were never implemented, breaking CI typecheck. The handlers were also not wired into the handleCommand switch statement. Removed them along with the orphaned createAgentListMessage test mock. The /inkeep run and /inkeep list commands can be added in a follow-up PR. Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Update configuration.mdx to say workspace defaults "grant access by default" instead of "always grant access", matching the implementation which supports explicit false via API - Replace double-negative toggle logic (!== false) with nullish coalescing (?? true) for clearer intent in channel-agent-cell.tsx Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
…le links - Surface actual API error messages in Slack instead of generic "Something went wrong" by parsing response body. Falls back to classified errors when body can't be parsed. Applied to streaming, commands, and modal submissions. - Add grantAccessToMembers toggle to workspace default section, mirroring the channel-level toggle. Includes handler, toast feedback, and read-only state for non-admins. - Add "View in Slack" profile link to MyLinkStatus and LinkedUsersSection using https://app.slack.com/team/{slackUserId}. Opens in new tab. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Make it explicit that the toggle grants access to any Slack workspace member, not just vaguely "workspace members". Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Re-Review Assessment
This is a re-review scoped to 2 commits (0d7f59427, 629afb8ca) since the last automated review (7a0f3dfb7ce8). The delta is remediation-only — it addresses prior feedback and improves UX copy.
| Delta Change | Location | Assessment |
|---|---|---|
Add aria-hidden to icons |
channel-agent-cell.tsx:66,68,70 |
✅ Resolves Prior Feedback — decorative icons now properly hidden from assistive technology |
| Clarify toast messages | index.tsx:160-163 |
✅ Clean — clearer distinction between "Any Slack workspace member" vs "users with Inkeep project invite" |
| Improve toggle label + tooltip | workspace-default-section.tsx:148-155 |
✅ Clean — "Authorize via Slack" with expanded tooltip explanation |
Prior Feedback Resolution
| Prior Issue | Status |
|---|---|
💭 channel-agent-cell.tsx:60-76 Missing aria-hidden on icons inside accessible span |
✅ RESOLVED — All 3 icons now have aria-hidden="true" |
UX Copy Improvements
The messaging changes improve user clarity:
| Component | Before | After |
|---|---|---|
| Toast (enabled) | "Workspace members can now use this agent" | "Any Slack workspace member can now use this agent" |
| Toast (disabled) | "Workspace members need explicit project access" | "Only users with an Inkeep project invite can use this agent" |
| Toggle label | "Grant access to members" | "Authorize via Slack" |
| Tooltip | "When enabled, workspace members can use this agent without explicit project access." | "When enabled, any member of this Slack workspace can use this agent without an explicit Inkeep project invite. When disabled, only users with direct project access can use it." |
The updated copy is more explicit about:
- Scope: "Any Slack workspace member" (vs vague "members")
- Mechanism: "Inkeep project invite" (concrete action)
- Both states: Tooltip now explains enabled AND disabled behavior
Implementation Quality (Full PR)
The core channel-based authorization feature has been reviewed 16+ times with consistent approval:
| Aspect | Assessment |
|---|---|
| Security Model | ✅ Sound — Project-binding (D8) prevents cross-project token reuse; JWT signature verification + 5-min TTL; graceful SpiceDB fallback |
| Authorization Flow | ✅ Correct — Trust chain model (work-apps signs → agents-api validates) with .refine() schema invariant |
| Test Coverage | ✅ Comprehensive — 40+ tests covering JWT schema, auth bypass, agent resolution, app mentions, slash commands, modal submissions |
| Type Safety | ✅ Precise — authorized: true literal type reflects runtime invariant; Zod validation enforces complete authorization states |
| Migration Safety | ✅ Safe — DEFAULT true NOT NULL is additive and backward compatible |
| UI Implementation | ✅ Clean — Toggle follows existing patterns with proper accessibility |
| Documentation | ✅ Accurate — Docs correctly describe toggle behavior and scope |
| Observability | ✅ Good — OTel span attributes (slack.authorized, slack.auth_source) enable debugging |
No New Issues
The delta introduces no new concerns. All changes are focused remediation of prior accessibility feedback and UX copy improvements.
✅ APPROVE
Summary: This delta resolves the one remaining accessibility concern (decorative icons now have aria-hidden="true") and improves UX messaging for the workspace grant access toggle. The core channel-based Slack authorization feature has been thoroughly reviewed across 16+ iterations with appropriate security controls (project binding, JWT signature verification, short TTL, graceful SpiceDB fallback), comprehensive test coverage (40+ tests), and proper documentation. Ship it! 🚀
Discarded (0)
No items discarded — delta is remediation-only.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| (none dispatched — delta is remediation-only) | — | — | — | — | — | — | — |
Note: Reviewer dispatch skipped because the delta consists entirely of accessibility fixes and UX copy improvements. No new functional code to review.
The streaming code uses getInProcessFetch() not global.fetch. Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Re-Review Assessment
This is a re-review scoped to 1 commit (94dd34201) since the last automated review (629afb8ca). The delta is a test consistency fix only.
| Delta Change | Location | Assessment |
|---|---|---|
Use mockFetch instead of vi.spyOn(global, 'fetch') |
streaming.test.ts:165,179 |
✅ Clean — aligns with test file conventions |
Delta Details
The commit fixes two test cases in streaming.test.ts:
Before:
vi.spyOn(global, 'fetch').mockResolvedValue(new Response(errorBody, { status: 403 }));After:
mockFetch.mockResolvedValue(new Response(errorBody, { status: 403 }));This is the correct pattern because:
mockFetchis already defined at the top of the file viavi.hoisted()(line 13-14)- It's wired up through
getInProcessFetchmock (line 21) - All other tests in the file use
mockFetchconsistently - Using
vi.spyOn(global, 'fetch')bypasses thegetInProcessFetchabstraction that the production code uses
Prior Review Status
This PR has been reviewed 17+ times by automated review, with all issues resolved since the initial review:
| Review | Outcome | Notes |
|---|---|---|
| Initial | REQUEST CHANGES | 4 issues raised |
| Re-review 1 | ✅ APPROVE | All issues resolved |
| Re-review 2–16 | ✅ APPROVE | Clean deltas, 0 new issues |
| This review | ✅ APPROVE | Test consistency fix only |
Implementation Quality (Full PR)
The core channel-based authorization feature has been thoroughly reviewed across 17+ iterations:
| Aspect | Assessment |
|---|---|
| Security Model | ✅ Sound — Project-binding (D8) prevents cross-project token reuse; JWT signature verification + 5-min TTL; graceful SpiceDB fallback |
| Authorization Flow | ✅ Correct — Trust chain model (work-apps signs → agents-api validates) with .refine() schema invariant |
| Test Coverage | ✅ Comprehensive — 40+ tests covering JWT schema, auth bypass, agent resolution, app mentions, slash commands, modal submissions, error surfacing |
| Type Safety | ✅ Precise — authorized: true literal type reflects runtime invariant; Zod validation enforces complete authorization states |
| Migration Safety | ✅ Safe — DEFAULT true NOT NULL is additive and backward compatible |
| UI Implementation | ✅ Clean — Toggle follows existing patterns with proper accessibility (aria-hidden) |
| Documentation | ✅ Accurate — Docs correctly describe toggle behavior and scope |
| Observability | ✅ Good — OTel span attributes (slack.authorized, slack.auth_source) enable debugging |
No New Issues
The delta introduces no new concerns. This is a trivial test consistency fix that improves test reliability by using the proper mock mechanism.
✅ APPROVE
Summary: This delta is a 2-line test fix that uses the correct mockFetch mock instead of vi.spyOn(global, 'fetch'), aligning with the test file's established patterns. The channel-based Slack authorization feature has been thoroughly reviewed across 17+ iterations with comprehensive security controls (project binding, JWT signature verification, short TTL, graceful SpiceDB fallback), 40+ tests, and proper documentation. Ship it! 🚀
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| (none dispatched — delta is test-only fix) | — | — | — | — | — | — | — |
Note: Reviewer dispatch skipped because the delta consists entirely of a 2-line test mock consistency fix. No functional code changes to review.
The canUseProjectStrict call now includes tenantId after a change on main. Update test expectations to match. Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Re-Review Assessment
This is a re-review scoped to 1 commit (9c783562c) since the last automated review (94dd34201). The delta is a test assertion fix only.
| Delta Change | Location | Assessment |
|---|---|---|
Add tenantId to SpiceDB mock assertions |
api-key-auth.test.ts:724,820 |
✅ Clean — aligns test assertions with actual canUseProjectStrict call signature |
Delta Details
The commit adds tenantId: 'tenant_456' to two test assertions in api-key-auth.test.ts:
Line 722-726 (test: "should accept valid slack user JWT with SpiceDB check"):
expect(canUseProjectStrictMock).toHaveBeenCalledWith({
userId: 'user_123',
tenantId: 'tenant_456', // ← Added
projectId: 'project_789',
});Line 818-822 (test: "should fall through to SpiceDB when authorizedProjectId does not match"):
expect(canUseProjectStrictMock).toHaveBeenCalledWith({
userId: 'user_123',
tenantId: 'tenant_456', // ← Added
projectId: 'project_789',
});This is a correct fix because:
- The actual
canUseProjectStrictcall inrunAuth.ts:253-257passes{ userId, tenantId, projectId } - The test assertions were incomplete — they verified
userIdandprojectIdbut nottenantId - Strict mock assertions ensure test accuracy and catch regressions in the parameter shape
Prior Review Status
This PR has been reviewed 18+ times by automated review, with all issues resolved since the initial review:
| Review | Outcome | Notes |
|---|---|---|
| Initial | REQUEST CHANGES | 4 issues raised |
| Re-review 1 | ✅ APPROVE | All issues resolved |
| Re-review 2–17 | ✅ APPROVE | Clean deltas, 0 new issues |
| This review | ✅ APPROVE | Test assertion fix only |
No New Issues
The delta introduces no new concerns. This is a trivial test improvement that makes assertions more complete.
✅ APPROVE
Summary: This delta adds tenantId to two SpiceDB mock assertions in the Slack JWT authentication tests, making the test assertions match the actual function call signature. The channel-based Slack authorization feature has been thoroughly reviewed across 18+ iterations with comprehensive security controls (project binding, JWT signature verification, short TTL, graceful SpiceDB fallback), 40+ tests, and proper documentation. Ship it! 🚀
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| (none dispatched — delta is test-only fix) | — | — | — | — | — | — | — |
Note: Reviewer dispatch skipped because the delta consists entirely of a 2-line test assertion fix. No functional code changes to review.
|
🔎💬 Inkeep AI search and chat service is syncing content for source 'Inkeep Agent Framework Docs' |
There was a problem hiding this comment.
PR Review Summary
(0) Total Issues | Risk: Low
Re-Review Assessment
This is a re-review scoped to 1 commit (0381ae03c) since the last automated review (9c783562c). The delta is test infrastructure only — it improves screenshot comparison for browser tests.
| Delta Change | Location | Assessment |
|---|---|---|
Add pixelmatch devDependency |
package.json:140 |
✅ Clean — standard screenshot comparison library |
Custom tolerantPixelmatch comparator |
vitest.config.ts:97-147 |
✅ Clean — handles cross-platform rendering differences |
cropRgba helper function |
vitest.config.ts:18-41 |
✅ Clean — handles minor dimension variations |
| Delete baseline screenshots | 3 .png files |
✅ Clean — no longer needed with tolerant comparator |
Delta Details
The commit implements a tolerant screenshot comparator that handles cross-platform rendering differences between macOS and Linux CI:
- Dimension tolerance: ±4px difference allowed (handles sub-pixel rendering variations)
- Pixel mismatch threshold: 2% of pixels allowed to differ
- Per-pixel threshold: 0.1 sensitivity (handles anti-aliasing differences)
The cropRgba helper crops images to a common size when dimensions differ slightly, enabling comparison despite minor platform-specific rendering variations.
This is a well-implemented improvement that reduces flaky screenshot tests without compromising test reliability.
Prior Review Status
This PR has been reviewed 18+ times by automated review, with all issues resolved since the initial review:
| Review | Outcome | Notes |
|---|---|---|
| Initial | REQUEST CHANGES | 4 issues raised |
| Re-review 1 | ✅ APPROVE | All issues resolved |
| Re-review 2–18 | ✅ APPROVE | Clean deltas, 0 new issues |
| This review | ✅ APPROVE | Test infrastructure only |
No New Issues
The delta introduces no functional changes to review. All PR code related to the channel-based Slack authorization feature remains unchanged from the previously approved state.
✅ APPROVE
Summary: This delta adds a tolerant screenshot comparator to reduce CI flakiness from cross-platform rendering differences. The implementation is clean and well-designed. The core channel-based Slack authorization feature has been thoroughly reviewed across 18+ iterations with comprehensive security controls (project binding, JWT signature verification, short TTL, graceful SpiceDB fallback), 40+ tests, and proper documentation. Ship it! 🚀
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| (none dispatched — delta is test infrastructure only) | — | — | — | — | — | — | — |
Note: Reviewer dispatch skipped because the delta consists entirely of test infrastructure improvements (screenshot comparison). No functional code changes to review.
Summary
Implements channel-based agent authorization for Slack, allowing linked Slack users in channels with configured default agents to execute those agents without explicit SpiceDB project membership. Includes a configurable
grantAccessToMemberstoggle so admins can control whether channel-based auth bypass is enabled per channel and workspace.Problem: When an admin assigns a default agent to a Slack channel, users must also be explicit project members in SpiceDB. This creates a high-friction onboarding barrier — admins configure the channel, but users get silently denied.
Solution: Extend the Slack JWT with channel authorization claims (
slack.authorized,slack.authorizedProjectId). When the work app resolves an agent via channel/workspace config andgrantAccessToMembersis enabled, it setsslackAuthorized: truein the JWT. The Run API'strySlackUserJwtAuththen bypasses SpiceDB when channel-authorized AND the authorized project matches the request.Spec: SPEC.md
UX Improvements
Security badge iconography in channel table
Each channel's agent column now shows a visual indicator of the authorization mode:
Slack channel membership badge (hover tooltip):
Explicit project access badge (hover tooltip):
Workspace default
grantAccessToMemberstoggleThe workspace default section now has a toggleable "Grant access to members" switch (matching the channel-level toggle pattern). When a default agent is configured, admins can control whether workspace members get automatic agent access.
Slack profile links in Linked Users section
Usernames in the Linked Users section are now clickable links to the user's Slack profile (
https://app.slack.com/team/{slackUserId}). The "Your Account Link" card also shows a "View in Slack" link.Improved API error messages in Slack
When the Run API returns an error with a
messagefield, it is now surfaced directly in Slack instead of a generic "Something went wrong". Falls back to classified error messages when the response body can't be parsed.Changes
JWT Schema (
agents-core)authorized,authSource,channelId,authorizedProjectIdtoSlackAccessTokenPayloadSchema.refine()constraint enforcingauthorizedProjectIdandauthSourceare required whenauthorized: trueSignSlackUserTokenParamssignSlackUserTokenclaims constructionAuth Bypass (
agents-api)canUseProjectStrictintrySlackUserJwtAuthwhenslack.authorized === trueANDauthorizedProjectIdmatchesx-inkeep-project-idheadermetadata.slackcontext for channel-authorized requestsExecution Context (
agents-core)metadata.slacktoBaseExecutionContexttype (authorized: trueliteral,authSource,channelId,teamId)Database Schema + Migration (
agents-core)grant_access_to_membersboolean column towork_app_slack_channel_agent_configstable (defaulttrue,NOT NULL)0014_odd_oracle.sql— singleALTER TABLE ADD COLUMNfindWorkAppSlackChannelAgentConfignow returnsgrantAccessToMembersfieldAgent Resolution (
agents-work-apps)resolveEffectiveAgentpropagatesgrantAccessToMembersfrom channel config (explicit value) or workspace config (defaults totrue)ResolvedAgentConfigtype extended withgrantAccessToMembers: booleanCall Sites — JWT Signing (
agents-work-apps)resolveChannelAgentConfigtoresolveEffectiveAgent+ pass channel auth params.slackAuthorizedgated byagentConfig.grantAccessToMembersslackAuthorized: agentConfig.grantAccessToMembers./inkeep run nameand/inkeep listpassslackAuthorized: falseslackAuthorized: false— correct because modal submissions bypass channel contextError Message Surfacing (
agents-work-apps)extractApiErrorMessage()helper parses response body JSON formessagefieldOTel Observability (
agents-work-apps)AUTHORIZEDandAUTH_SOURCEtoSLACK_SPAN_KEYSintracer.tsManage UI —
grantAccessToMembersToggle (agents-manage-ui)channel-agent-cell.tsx: Security badge icons (Slack vs Inkeep) in channel table with hover tooltips.Switchtoggle in channel agent popoverindex.tsx(AgentConfigurationCard):handleToggleGrantAccess+handleToggleWorkspaceGrantAccesshandlersworkspace-default-section.tsx:Switchtoggle for workspace-levelgrantAccessToMemberswith Slack/Inkeep icon +ShieldCheckbadge and tooltipslack-api.ts:DefaultAgentConfigincludesgrantAccessToMembers?: booleanManage UI — Slack Profile Links (
agents-manage-ui)my-link-status.tsx: "View in Slack" link withExternalLinkicon, conditional onslackUserIdlinked-users-section.tsx: Username inUserRowis a clickable link to Slack profileChannel Config API Route (
agents-work-apps)PUT /workspaces/:teamId/channels/:channelId/settingsacceptsgrantAccessToMembersinagentConfigbodywork_app_slack_channel_agent_configstableGET /workspaces/:teamId/channelsresponseKey Design Decisions
resolveEffectiveAgent(has source field) instead ofresolveChannelAgentConfigauthorizedProjectIdmust matchx-inkeep-project-idheadertruefor backward compatibility — existing channel configs automatically grant accessTest plan
JWT Schema Tests (
agents-core)authorized: truerequiresauthorizedProjectIdandauthSourceAuth Bypass Tests (
agents-api)slack.authorized: true+ matching projectId)channelauthorized: falseAgent Resolution Tests (
agents-work-apps)grantAccessToMembers: true→ returned in resultgrantAccessToMembers: false→ returned in resultgrantAccessToMembers: truegrantAccessToMembers: false→ propagatedError Message Surfacing Tests (
agents-work-apps)extractApiErrorMessageparses valid JSON with message fieldApp Mention / Command / Modal Tests (
agents-work-apps)slackAuthorizedcorrectly reflectsgrantAccessToMembersvalueslackAuthorized: falseQuality Gates
pnpm typecheck— 14/14 tasks cleanpnpm lint— 13/13 tasks cleanpnpm test— agents-work-apps 375/375 passing🤖 Generated with Claude Code