Skip to content

feat: add Slack source indicator to conversation traces and stats#2091

Merged
amikofalvy merged 5 commits intomainfrom
feat/slack-source-indicator
Feb 25, 2026
Merged

feat: add Slack source indicator to conversation traces and stats#2091
amikofalvy merged 5 commits intomainfrom
feat/slack-source-indicator

Conversation

@amikofalvy
Copy link
Copy Markdown
Collaborator

Summary

  • Adds a Slack source indicator to the conversation trace UI (timeline, conversation list, and stats page)
  • Uses x-inkeep-invocation-type: slack HTTP header from Slack integration to set invocation.type='slack' on OpenTelemetry spans
  • Conversation list uses conversation ID prefix (slack-) as a backward-compatible UI hint for the Slack badge

Changes

API (agents-api)

  • chat.ts and chatDataStream.ts: Read x-inkeep-invocation-type header and set invocation.type span attribute

Slack Integration (agents-work-apps)

  • streaming.ts, modal-submission.ts, commands/index.ts: Add x-inkeep-invocation-type: slack header to all /run/api/chat fetch calls

UI (agents-manage-ui)

  • Timeline: Slack messages show a # icon with Slack purple color (matches trigger's Zap icon pattern)
  • Conversation list: Slack badge on conversations with slack- prefixed IDs
  • Conversation detail: "Slack message received" label and Slack subagent name for slack-type activities
  • Stats page: New "Slack Conversations" stat card showing COUNT_DISTINCT of conversations with invocation.type='slack'
  • Hook: Added totalSlackConversations to useProjectOverviewStats

Design decisions

  • Header-based detection (not conversation ID prefix) for span attributes — more stable and extensible
  • ID prefix kept only for conversation list badge — backward-compatible UI hint
  • Forward-only stats — only new conversations with the header will be counted

Test plan

  • Verify Slack conversations show # icon in trace timeline
  • Verify Slack badge appears on conversation list items
  • Verify stats page shows "Slack Conversations" count
  • Verify non-Slack conversations are unaffected

🤖 Generated with Claude Code

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Feb 25, 2026 10:15pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
agents-manage-ui Ignored Ignored Preview Feb 25, 2026 10:15pm
agents-docs Skipped Skipped Feb 25, 2026 10:15pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 17, 2026

🦋 Changeset detected

Latest commit: 77b8998

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@inkeep/agents-api Patch
@inkeep/agents-work-apps Patch
@inkeep/agents-manage-ui Patch
@inkeep/agents-cli Patch
@inkeep/agents-core Patch
@inkeep/agents-mcp Patch
@inkeep/agents-sdk Patch
@inkeep/ai-sdk-provider Patch
@inkeep/create-agents Patch

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

Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(2) Total Issues | Risk: Medium

🟠⚠️ Major (2) 🟠⚠️

🟠 1) system Missing changeset for user-facing feature

Issue: This PR adds user-facing features (Slack Conversations stat card, Slack badges in conversation list, Slack icons in timeline) across three published npm packages (@inkeep/agents-api, @inkeep/agents-manage-ui, @inkeep/agents-work-apps). None of these packages are in the changeset ignore list, but no changeset was added.

Why: Per AGENTS.md: "Create a changeset for any user-facing change to a published package." This helps track version bumps and generates proper changelog entries for consumers. The changeset-bot has flagged this as well.

Fix: Add a changeset describing the feature:

pnpm bump patch --pkg agents-api --pkg agents-work-apps "Add Slack source indicator to conversation traces and stats"

Note: agents-manage-ui is a bundled deployment and may not need separate versioning, but agents-api and agents-work-apps export trace attributes and header handling that other services depend on.

Refs:

Inline Comments:

  • 🟠 Major: stats/page.tsx:305 Asymmetric stat semantics (conversations vs invocations)

💭 Consider (3) 💭

💭 1) conversation-list-item.tsx:26 Dual-detection approach for Slack conversations
Issue: ID prefix (slack-) detection differs from span attribute detection used elsewhere.
Why: This is acknowledged as intentional for backward compatibility, but the split-world behavior should be documented.
Fix: Add a code comment explaining the dual approach.

💭 2) timeline-item.tsx:140 Hardcoded Slack brand color
Issue: The Slack purple #4A154B appears in multiple files.
Why: Minor maintenance concern if color needs updating later.
Fix: Consider extracting to a shared constant or Tailwind config.

💭 3) timeline-item.tsx:140 + conversation-list-item.tsx:42 Hash icon semantics
Issue: The Hash (#) icon is used for Slack, but '#' is typically associated with channels, not Slack as a platform.
Why: Users may interpret this as "channel-related" rather than "from Slack." However, Slack does use # for channels, and the "Slack" text label provides clarity.
Fix: No action required — current approach is acceptable. If a Slack-specific icon becomes available in lucide-react, consider using it.

🧹 While You're Here (1) 🧹

🧹 1) timeline-item.tsx:199-220 Deeply nested ternary expression
Issue: The typeForIcon assignment uses 11+ levels of nested ternaries, which this PR extends by one more condition.
Why: This is pre-existing technical debt that makes the code harder to read and maintain. Adding future invocation types will continue to deepen nesting.
Fix: Consider refactoring to a lookup-based approach or helper function in a follow-up PR:

function getTypeForIcon(activity: ActivityItem): string {
  if (activity.type === ACTIVITY_TYPES.USER_MESSAGE) {
    if (activity.invocationType === 'trigger') return 'trigger_invocation';
    if (activity.invocationType === 'slack') return 'slack_message';
    return activity.type;
  }
  // ... other cases
}

🚫 REQUEST CHANGES

Summary: The implementation follows existing patterns well and the code is clean. The main blocking issue is the missing changeset — this is a user-facing feature across published packages that requires version tracking per project guidelines. The stats semantics concern (conversations vs invocations) is worth discussing but could be addressed in a follow-up if the current design is intentional.

Discarded (5)
Location Issue Reason Discarded
signoz-stats.ts Stats query semantic difference (consistency reviewer) Duplicate of Main #1 (stats asymmetry)
conversation-list-item.tsx:26 ID prefix heuristic is fragile (frontend reviewer) Duplicate of Consider #1 (dual-detection)
chat.ts:324 Concept economy concern about scaling invocation types Too speculative — no action required for this PR
Various files 6x INFO findings confirming patterns are correct Not issues — positive confirmations
timeline-item.tsx:140 Hash icon semantics (frontend reviewer) Merged into Consider #3
Reviewers (5)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-devops 1 1 0 0 0 0 0
pr-review-product 4 0 2 0 1 0 1
pr-review-consistency 7 0 0 0 0 0 7
pr-review-frontend 4 0 1 1 0 0 2
pr-review-standards 0 0 0 0 0 0 0
Total 16 1 3 1 1 0 10

statDescription="Conversations from Slack"
isLoading={overviewLoading}
Icon={Hash}
/>
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.

🟠 MAJOR: Asymmetric stat semantics may confuse users

Issue: "Slack Conversations" counts unique conversations (COUNT_DISTINCT on conversation.id) while "Trigger Invocations" counts individual invocation events (COUNT_DISTINCT on trigger.invocation.id). Users comparing these metrics side-by-side may be confused about what each number represents.

Why: A single trigger-initiated conversation could have multiple invocations (retries, follow-ups), while Slack conversations are counted once per thread. This asymmetry makes the stats incomparable and could lead to misinterpretation.

Fix: Consider one of:

  1. Rename to "Slack Messages" and count message spans to parallel trigger invocations
  2. Add a companion "Trigger Conversations" stat that counts unique conversations for comparison
  3. Update descriptions to explicitly differentiate units (e.g., "Unique conversation threads from Slack" vs "Total webhook executions")

Refs:

startTime,
} = conversation;

const isSlackConversation = conversationId.startsWith('slack-');
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.

💭 Consider: Document the dual-detection approach

Issue: The conversation list uses ID prefix (slack-) for Slack detection while timeline/stats use invocation.type='slack' from span attributes. This creates two sources of truth that may diverge.

Why: I see from the PR description this is intentional as a "backward-compatible UI hint" — the ID prefix handles historical data while the span attribute is the authoritative source going forward. This is a reasonable design choice, but the split-world behavior could surprise maintainers later.

Fix: Consider adding a brief code comment explaining the dual approach:

// Backward-compat: Use ID prefix for historical Slack conversations that predate the invocation.type span attribute.
// For new conversations, the authoritative source is the `invocation.type='slack'` span attribute (used in timeline/stats).
const isSlackConversation = conversationId.startsWith('slack-');

Alternatively, if invocationType becomes available on the conversation object, you could unify detection:

const isSlackConversation = conversation.invocationType === 'slack' || conversationId.startsWith('slack-');

) {
const base: Record<string, { Icon: any; cls: string }> = {
trigger_invocation: { Icon: Zap, cls: 'text-amber-500' },
slack_message: { Icon: Hash, cls: 'text-[#4A154B]' },
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.

💭 Consider: Extract Slack brand color to a constant

Issue: The Slack purple color #4A154B is hardcoded here and in conversation-list-item.tsx.

Why: If the color needs adjustment (e.g., for dark mode contrast or brand updates), all instances must be found and updated manually.

Fix: Consider extracting to a shared constant or Tailwind config:

// In a shared constants file or Tailwind config
const SLACK_BRAND_COLOR = '#4A154B';
// or in tailwind.config.js: colors: { slack: '#4A154B' }

This is a minor maintenance concern — the current approach works fine.

Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low

✅ All Prior Feedback Addressed

The delta commit (a936a1f93) cleanly addresses all issues raised in the previous review:

Prior Issue Resolution
🟠 Missing changeset (MAJOR) ✅ Added .changeset/advisory-magenta-cobra.md with patch bumps for @inkeep/agents-api and @inkeep/agents-work-apps
🟠 Asymmetric stat semantics (MAJOR inline) ✅ Renamed "Slack Conversations" → "Slack Messages" and changed query from COUNT_DISTINCT(conversation.id) to COUNT(span_id) — now semantically consistent with "Trigger Invocations"
💭 Hardcoded Slack brand color (Consider) ✅ Extracted SLACK_BRAND_COLOR = '#4A154B' to constants/theme.ts and updated all usages in timeline-item.tsx, conversation-list-item.tsx, and work-apps/common/types.ts
💭 Dual-detection approach undocumented (Consider) ✅ Added clear backward-compat comment explaining the ID prefix vs span attribute approach

Delta Quality Notes

  • Clean implementation: The fixes are targeted and don't introduce new concerns
  • Consistent patterns: The SLACK_BRAND_COLOR constant follows the existing INKEEP_BRAND_COLOR pattern
  • Good semantics: "Slack Messages" with span counting is the right parallel to "Trigger Invocations"

✅ APPROVE

Summary: All blocking and suggested issues from the previous review have been properly addressed. The Slack source indicator feature is now ready to ship — clean implementation with proper versioning, consistent stat semantics, and well-documented design decisions. Nice work! 🎉

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
Delta review — no sub-agents dispatched

Note: This is a delta re-review. The prior review already covered the full scope; this review validates only the fixes in commit a936a1f93.

amikofalvy and others added 2 commits February 25, 2026 13:20
Slack-originated conversations now show a Slack indicator in the trace
timeline, conversation list, and stats page. Uses x-inkeep-invocation-type
header from Slack integration to set invocation.type='slack' on spans.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…omments

- Add missing changeset for agents-api and agents-work-apps
- Rename "Slack Conversations" stat to "Slack Messages" and count message
  spans (COUNT on user message spans) instead of unique conversations
  (COUNT_DISTINCT on conversation.id) to parallel trigger invocations
- Add backward-compat comment explaining dual Slack detection approach
  (ID prefix vs invocation.type span attribute)
- Extract Slack brand color #4A154B to shared SLACK_BRAND_COLOR constant
  in theme.ts, used in conversation-list-item, timeline-item, and
  work-apps types

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(2) Total Issues | Risk: Medium

🟠⚠️ Major (1) 🟠⚠️

Inline Comments:

  • 🟠 Major: streaming.ts:1 Missing x-inkeep-invocation-type header in resume-intent.ts — Slack messages via smart link intent flow won't be tracked

🟡 Minor (1) 🟡

Inline Comments:

  • 🟡 Minor: conversation-list-item.tsx:46 Decorative icon missing aria-hidden="true"

💭 Consider (1) 💭

💭 1) block-actions.ts:95-102 Header in tool approval flow
Issue: The block-actions.ts file also calls /run/api/chat without the x-inkeep-invocation-type: slack header.
Why: This is a tool approval response that continues an existing conversation. The original invocation type should already be set on the parent span from the initial request. However, for consistency, you could add the header here as well.
Fix: Optionally add 'x-inkeep-invocation-type': 'slack' to the headers in block-actions.ts:97-101.

Refs:

🧹 While You're Here (1) 🧹

🧹 1) timeline-item.tsx:243 Decorative icons missing aria-hidden (pre-existing)
Issue: The Icon component rendered at line 243 is decorative (accompanies text descriptions) but will be announced by screen readers. This affects all activity icons including the new slack_message type.
Why: This is a pre-existing pattern issue — the PR follows the existing codebase pattern. Fixing it would improve accessibility for all timeline icons.
Fix: Add aria-hidden="true" to the Icon component:

<Icon className={`w-4 h-4 ${className}`} style={iconStyle} aria-hidden="true" />

Refs:

🕐 Pending Recommendations (3)

Prior feedback that remains relevant:

All prior blocking issues have been resolved ✅


💡 APPROVE WITH SUGGESTIONS

Summary: The feature implementation is clean and follows established patterns well. The main issue is the incomplete header coverageresume-intent.ts makes Slack-originated /run/api/chat calls without the x-inkeep-invocation-type: slack header, meaning smart link intent resumption flows won't be tracked as Slack messages in traces/stats. This should be a quick fix to add the header to that file.

Discarded (4)
Location Issue Reason Discarded
modal-submission.ts PR description mentions this file File doesn't actually call /run/api/chat — no issue
commands/index.ts PR description mentions this file File doesn't exist or call chat API — no issue
Various INFO findings confirming patterns are correct Not issues — positive confirmations
Various Duplicate findings across reviewers Merged into single Major finding
Reviewers (6)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-standards 1 0 0 0 1 0 0
pr-review-consistency 1 0 0 0 0 0 1
pr-review-product 0 0 0 0 0 0 0
pr-review-frontend 2 0 0 1 1 0 0
pr-review-devops 2 0 0 0 0 0 2
pr-review-sre 0 0 0 0 0 0 0
Total 6 0 1 1 2 0 3

Note: Standards, Consistency, and DevOps all flagged the same header coverage issue — merged into a single inline comment.

className="flex items-center gap-1"
style={{ color: SLACK_BRAND_COLOR, borderColor: `${SLACK_BRAND_COLOR}4D` }}
>
<Hash className="h-3 w-3" />
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.

🟡 Minor: Decorative icon missing aria-hidden="true"

Issue: The Hash icon is purely decorative (accompanies the "Slack" text label) but will be announced by screen readers.

Why: Screen readers may announce "Hash Slack" instead of just "Slack", creating redundant/confusing announcements for assistive technology users.

Fix: (1-click apply)

Suggested change
<Hash className="h-3 w-3" />
<Hash className="h-3 w-3" aria-hidden="true" />

Refs:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added aria-hidden="true" to the decorative Hash icon in f7dd7e3.

Regarding the Major finding about resume-intent.ts: both x-inkeep-invocation-type: 'slack' and x-inkeep-invocation-entry-point: 'smart_link_resume' headers were already added in commit 52afc30 (see lines 466-467 of resume-intent.ts). Same for block-actions.ts (lines 102-103). That finding appears to be a false positive.

@github-actions github-actions Bot deleted a comment from claude Bot Feb 25, 2026
Add x-inkeep-invocation-entry-point header alongside existing
x-inkeep-invocation-type to distinguish how a Slack conversation was
initiated (app_mention, direct_message, slash_command, message_shortcut,
modal_submission, smart_link_resume, tool_approval).

- Add entryPoint param to PublicExecutionParams and streamAgentResponse
- Pass entry point from all 7 Slack execution paths
- Add invocation.entryPoint span attribute in chat and chatDataStream routes
- Add INVOCATION_ENTRY_POINT to SPAN_KEYS in agents-core
- Display entry point label in trace timeline descriptions
- Fix resume-intent to use getInProcessFetch() instead of global fetch
- Add missing x-inkeep-invocation-type header to tool approval and
  resume-intent direct API calls

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(1) Total Issues | Risk: Low

🟡 Minor (1) 🟡

Inline Comments:

  • 🟡 Minor: execution.ts:9-15 Missing 'tool_approval' in SlackEntryPoint type definition

✅ Delta Validation

The delta commits cleanly address all prior feedback and extend the feature appropriately:

Prior Issue Resolution
🟠 Missing headers in resume-intent.ts (MAJOR — prior review) False positive — Headers were added in commit 52afc3019. Author's feedback confirmed, code verified at lines 466-467.
🟡 Decorative icon missing aria-hidden (Minor — prior review) ✅ Fixed in commit f7dd7e353

Delta Quality Notes

Entry point tracking implementation:

  • ✅ New SlackEntryPoint type with 6 values (7 with suggested fix)
  • ✅ All 9 Slack /run/api/chat call sites correctly include both headers
  • streaming.ts conditionally spreads entry point header
  • resume-intent.ts now uses getInProcessFetch() for multi-instance safety (important fix)
  • ✅ UI shows entry point in timeline description (e.g., "Slack message received (app mention)")
  • ✅ Pattern is consistent: snake_case naming, matches invocation.type precedent

API routes (chat.ts, chatDataStream.ts):

  • ✅ Clean header parsing for x-inkeep-invocation-entry-point
  • ✅ Span attribute set as invocation.entryPoint

✅ APPROVE

Summary: The delta commits are clean and well-implemented. The entry point tracking feature adds valuable granularity for observability. The one Minor finding (type completeness) is a quick 1-click fix that doesn't block merge. The getInProcessFetch() change in resume-intent.ts is a valuable fix for multi-instance deployments. Nice work on the accessibility fix too! 🎉

Discarded (1)
Location Issue Reason Discarded
resume-intent.ts:466-467 Missing headers (prior Major finding) False positive — Author correctly identified that headers were added in 52afc3019. Verified code has both x-inkeep-invocation-type and x-inkeep-invocation-entry-point headers.
Reviewers (2)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-standards 1 0 0 0 1 0 0
pr-review-consistency 2 0 0 0 0 0 1
Total 3 0 0 0 1 0 1

Note: Both reviewers identified the same type completeness issue — merged into single inline comment.

Comment on lines +9 to +15
export type SlackEntryPoint =
| 'app_mention'
| 'direct_message'
| 'slash_command'
| 'message_shortcut'
| 'modal_submission'
| 'smart_link_resume';
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.

🟡 Minor: Missing 'tool_approval' in SlackEntryPoint type

Issue: The SlackEntryPoint type lists 6 entry points, but block-actions.ts:103 uses 'tool_approval' as an entry point value that isn't included in this type.

Why: The type serves as documentation and enables type safety. When block-actions.ts uses a string literal directly ('tool_approval'), it bypasses the type constraint, creating silent drift between the type definition and actual usage.

Fix: (1-click apply)

Suggested change
export type SlackEntryPoint =
| 'app_mention'
| 'direct_message'
| 'slash_command'
| 'message_shortcut'
| 'modal_submission'
| 'smart_link_resume';
export type SlackEntryPoint =
| 'app_mention'
| 'direct_message'
| 'slash_command'
| 'message_shortcut'
| 'modal_submission'
| 'smart_link_resume'
| 'tool_approval';

Refs:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 77b8998 — added 'tool_approval' to SlackEntryPoint type. Also updated resume-intent.test.ts to mock getInProcessFetch instead of global fetch, fixing 3 test failures.

@github-actions github-actions Bot deleted a comment from claude Bot Feb 25, 2026
@vercel vercel Bot temporarily deployed to Preview – agents-docs February 25, 2026 22:13 Inactive
Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low

✅ Delta Validation Complete

The delta commit (77b899842) cleanly addresses the prior review feedback:

Prior Issue Resolution
🟡 Missing 'tool_approval' in SlackEntryPoint type (Minor — prior review) ✅ Fixed in execution.ts:16 — type now includes all 7 entry points
Test failures from getInProcessFetch migration ✅ Fixed in resume-intent.test.ts — tests now mock mockInProcessFetch instead of global fetch

Delta Quality Notes

Type fix (execution.ts:9-16):

  • SlackEntryPoint now includes 'tool_approval' — matches actual usage in block-actions.ts:103
  • ✅ Maintains consistent snake_case naming convention with other entry points

Test fix (resume-intent.test.ts):

  • ✅ Properly mocks getInProcessFetch via vi.hoisted() pattern
  • ✅ Removes vi.stubGlobal('fetch', ...) / vi.unstubAllGlobals() calls that were stubbing the wrong function
  • ✅ Uses mockInProcessFetch.mockResolvedValue() for chat API responses
  • ✅ Preserves global fetch stub only for external API calls (projects/agents list endpoints) — correct separation

✅ APPROVE

Summary: All prior review feedback has been addressed. The type is now complete and type-safe, and tests correctly mock the in-process fetch pattern. This PR is ready to ship — clean implementation with proper versioning, consistent patterns, and good observability for Slack integration sources. Nice work on the quick turnaround! 🎉

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
Delta review — direct validation

Note: This is a delta re-review. The delta was minimal (2 files, type fix + test fix) and directly addressed prior feedback, so no sub-agents were dispatched.

@github-actions github-actions Bot deleted a comment from claude Bot Feb 25, 2026
@amikofalvy amikofalvy merged commit 50b63a3 into main Feb 25, 2026
15 checks passed
@amikofalvy amikofalvy deleted the feat/slack-source-indicator branch February 25, 2026 22:26
inkeep Bot added a commit that referenced this pull request Feb 25, 2026
… page

Documents the Slack source indicator feature from PR #2091:
- Conversation list badge, trace timeline icon, and Stats page card
- SigNoz filtering via invocation.type='slack' span attribute
inkeep Bot added a commit that referenced this pull request Feb 25, 2026
…o spans guide

Documents the new OTEL span attributes for tracking invocation sources
(e.g., Slack) introduced in PR #2091.
@itoqa
Copy link
Copy Markdown

itoqa Bot commented Feb 25, 2026

Ito Test Report ✅

24 test cases ran. 24 passed.

All test cases for the Slack Source Indicator feature (PR #2091) passed verification. The feature adds visual indicators for Slack-originated conversations throughout the application: a new "Slack Messages" stat card on the stats page, a Slack badge in the conversation list for conversations with slack- prefixed IDs, and Hash icons with Slack purple (#4A154B) branding in conversation timelines for messages with invocationType='slack' span attributes. Edge cases including historical data compatibility, detection mechanism gaps, zero-state rendering, and SigNoz unavailability were all handled correctly. Adversarial tests confirmed that while the API accepts invocation headers without server-side validation (a known security consideration), XSS is prevented by React JSX escaping and header injection is blocked by the HTTP library.

✅ Passed (24)
Test Case Summary Timestamp Screenshot
ROUTE-1 Stats page displays Slack Messages StatCard with title, description, Hash icon, and numeric value 0:30 ROUTE-1_0-30.png
ROUTE-2 Conversation list shows Slack badge (outline variant, Hash icon, purple #4A154B) for slack-prefixed conversations 19:42 ROUTE-2_19-42.png
ROUTE-3 Non-slack conversation (normal-conv-001) correctly renders without Slack badge 21:06 ROUTE-3_21-06.png
ROUTE-4 Timeline shows Hash icon with Slack purple (#4A154B) for slack-type user messages, no text-primary class 31:13 ROUTE-4_31-13.png
ROUTE-5 Timeline entry point labels render correctly: 'Slack message received (app mention)' and '(slash command)' 31:16 ROUTE-5_31-16.png
ROUTE-6 Non-Slack conversation timeline shows default User icon with text-primary class, 'User sent a message' 32:04 ROUTE-6_32-04.png
ROUTE-7 Stats page loading state shows animate-pulse skeleton elements including one for Slack Messages card 2:02 ROUTE-7_2-02.png
EDGE-1 Historical Slack conversation (slack- prefix, no invocation headers) shows badge in list but User icon in timeline 36:33 EDGE-1_36-33.png
EDGE-2 Conversation with invocationType=slack but no slack- prefix shows Hash icon in timeline but no badge in list 38:41 EDGE-2_38-41.png
EDGE-3 Stats page with zero Slack messages renders all 5 stat cards correctly without crash 2:25 EDGE-3_2-25.png
EDGE-4 SigNoz unavailable - stats page gracefully falls back to zero, page does not crash 6:14 EDGE-4_6-14.png
EDGE-5 Empty entry point renders 'Slack message received' without trailing parentheses or empty () 32:26 EDGE-5_32-26.png
EDGE-6 Code inspection confirms per-span invocationType handling in route.ts activities loop 8:48 EDGE-6_8-48.png
EDGE-7 Prefix matching uses exact 'slack-' - 'slackbot-123'.startsWith('slack-') returns false 8:49 EDGE-7_8-49.png
ADV-1 Spoofed invocation type header accepted by API (known security consideration), would show Slack indicators in timeline 52:14 ADV-1_52-14.png
ADV-2 XSS payload in entry point header accepted by API but prevented at render layer by React JSX escaping 52:15 ADV-2_52-15.png
ADV-3 Extremely long (10,000 char) entry point header accepted without HTTP layer rejection 52:16 ADV-3_52-16.png
ADV-4 Header injection via CRLF handled safely - HTTP library strips/rejects newlines, no X-Injected header in response 52:18 ADV-4_52-18.png
ADV-5 Fake slack- prefix without invocation headers shows badge in list (false positive) but not Slack icon in timeline 52:18 ADV-5_52-18.png
LOGIC-1 All 10 resume-intent unit tests passed, confirming getInProcessFetch usage (not global fetch) 8:50 LOGIC-1_8-50.png
LOGIC-2 All 7 Slack entry points correctly set x-inkeep-invocation-type: 'slack' and entry point headers 58:26 LOGIC-2_58-26.png
LOGIC-3 chat.ts and chatDataStream.ts handle invocation headers identically (code parity verified) 58:49 LOGIC-3_58-49.png
LOGIC-4 SigNoz totalSlackMessages query correctly COUNTs spanId where invocation.type='slack', fallback to 0 on error 3:11 LOGIC-4_3-11.png
LOGIC-5 SLACK_BRAND_COLOR (#4A154B) used consistently in conversation list badge and timeline icon 32:59 LOGIC-5_32-59.png
📋 View Recording

Screen Recording

Gaurav-Narayan-Varma pushed a commit that referenced this pull request Feb 26, 2026
)

* feat: add Slack source indicator to conversation traces and stats

Slack-originated conversations now show a Slack indicator in the trace
timeline, conversation list, and stats page. Uses x-inkeep-invocation-type
header from Slack integration to set invocation.type='slack' on spans.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Address PR review feedback: changeset, stat rename, color constant, comments

- Add missing changeset for agents-api and agents-work-apps
- Rename "Slack Conversations" stat to "Slack Messages" and count message
  spans (COUNT on user message spans) instead of unique conversations
  (COUNT_DISTINCT on conversation.id) to parallel trigger invocations
- Add backward-compat comment explaining dual Slack detection approach
  (ID prefix vs invocation.type span attribute)
- Extract Slack brand color #4A154B to shared SLACK_BRAND_COLOR constant
  in theme.ts, used in conversation-list-item, timeline-item, and
  work-apps types

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: add Slack entry point tracking to invocation traces

Add x-inkeep-invocation-entry-point header alongside existing
x-inkeep-invocation-type to distinguish how a Slack conversation was
initiated (app_mention, direct_message, slash_command, message_shortcut,
modal_submission, smart_link_resume, tool_approval).

- Add entryPoint param to PublicExecutionParams and streamAgentResponse
- Pass entry point from all 7 Slack execution paths
- Add invocation.entryPoint span attribute in chat and chatDataStream routes
- Add INVOCATION_ENTRY_POINT to SPAN_KEYS in agents-core
- Display entry point label in trace timeline descriptions
- Fix resume-intent to use getInProcessFetch() instead of global fetch
- Add missing x-inkeep-invocation-type header to tool approval and
  resume-intent direct API calls

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Add aria-hidden to decorative Slack badge icon

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* Address PR feedback: add tool_approval to SlackEntryPoint, fix tests for getInProcessFetch

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
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