Skip to content

Add ephemeral channel join summary feature - #879

Open
Sebagrei wants to merge 2 commits into
mattermost:masterfrom
Sebagrei:channel-join-summary
Open

Add ephemeral channel join summary feature#879
Sebagrei wants to merge 2 commits into
mattermost:masterfrom
Sebagrei:channel-join-summary

Conversation

@Sebagrei

@Sebagrei Sebagrei commented Jul 8, 2026

Copy link
Copy Markdown

This pull request introduces a new feature for generating and delivering an ephemeral "catch-up" summary to users when they join a channel. The summary is generated by an LLM agent based on recent channel activity and is visible only to the joining user. The implementation includes backend logic, configuration options, API integration, a web-based admin panel, and documentation updates.

The most important changes are:

Channel Join Summary Feature Implementation

  • Added the joinsummary package, which handles generation and delivery of ephemeral channel summaries when a user joins a channel. It fetches recent messages, filters out system/deleted posts, invokes the LLM agent, and sends the summary as an ephemeral post. Includes tests for core logic. (joinsummary/joinsummary.go, joinsummary/joinsummary_test.go) [1] [2]

Configuration and Integration

  • Extended the main configuration (config.Config) with a new ChannelJoinSummaryConfig struct, exposing options for enabling the feature, selecting the agent bot, lookback window, and minimum post count. Provided a getter for the new config. (config/config.go) [1] [2] [3]
  • Integrated the join summary service into the plugin lifecycle and hooked it into the UserHasJoinedChannel event, ensuring asynchronous, best-effort operation. (server/main.go) [1] [2] [3] [4] [5]

Admin UI and Documentation

  • Added a new admin panel in the system console for configuring the channel join summary feature, including toggles and controls for all options. (webapp/src/components/system_console/channel_join_summary/channel_join_summary_panel.tsx)
  • Updated the admin guide documentation to describe the feature, its use cases, and configuration steps. (docs/admin_guide.md)
grafik

Summary by CodeRabbit

  • New Features

    • Added a new “Channel Join Summary” option in System Console to generate a private catch-up summary when someone joins a channel.
    • Let admins choose an agent, set how far back to review recent messages, and define the minimum number of messages needed before a summary is created.
    • Added user-facing help text explaining when summaries appear and which channel types are excluded.
  • Bug Fixes

    • Summaries are only shown when the feature is enabled and the channel meets eligibility requirements.

Sebagrei and others added 2 commits July 8, 2026 19:54
When enabled, joining a public or private channel shows the joining user a
private, ephemeral LLM-generated summary of the channel's recent activity.
Only the joining user sees it and it is not persisted. DMs, group messages,
and archived channels are skipped, as are bot users and channels with too
little recent activity.

The new joinsummary service is wired to the UserHasJoinedChannel hook. Cheap
gating runs synchronously; the LLM completion runs in a goroutine so joins are
never blocked. Configurable via System Console (enable, summary agent,
lookback days, minimum messages) behind a default-off toggle.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a channel join catch-up summary feature: a new config schema, a joinsummary service that generates an ephemeral LLM summary when eligible users join channels, plugin hook wiring, a system console configuration panel, and supporting documentation and i18n strings.

Changes

Channel Join Summary

Layer / File(s) Summary
Config schema and accessor
config/config.go
Adds ChannelJoinSummaryConfig struct (Enabled, Bot, LookbackDays, MinPosts), a ChannelJoinSummary field on Config, and a Container.ChannelJoinSummary() accessor.
joinsummary service implementation
joinsummary/joinsummary.go
Implements Service, New, HandleUserJoinedChannel gating/async dispatch, generateAndSend LLM flow, and helper functions for eligibility, post filtering, channel naming, and default resolution.
joinsummary unit tests
joinsummary/joinsummary_test.go
Adds table-driven tests covering channel eligibility, post filtering, default resolution, and channel name selection.
Plugin hook wiring
server/main.go
Adds joinSummaryService field, constructs it in OnActivate, and adds UserHasJoinedChannel hook delegating to the service.
System console panel and config wiring
webapp/src/components/system_console/channel_join_summary/channel_join_summary_panel.tsx, webapp/src/components/system_console/config.tsx, webapp/src/components/system_console/plugin_config_types.tsx
Adds ChannelJoinSummaryPanel component, extends PluginConfig type, and integrates the panel with defaults and update handling in the config screen.
Documentation and i18n strings
docs/admin_guide.md, webapp/src/i18n/en.json
Adds an admin guide section and new i18n labels/copy describing the feature and its configuration options.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Mattermost
  participant Plugin
  participant Service as joinsummary.Service
  participant LLM

  Mattermost->>Plugin: UserHasJoinedChannel(channelMember)
  Plugin->>Service: HandleUserJoinedChannel(ctx, channelMember)
  Service->>Service: shouldSummarizeChannel(channel)
  alt eligible
    Service->>Service: spawn goroutine generateAndSend
    Service->>Mattermost: fetch recent posts since lookback
    Service->>Service: filterSummarizablePosts(posts)
    alt enough posts
      Service->>LLM: ChatCompletionNoStream(prompt)
      LLM-->>Service: summary text
      Service->>Mattermost: send ephemeral post to joining user
    end
  end
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding an ephemeral channel join summary feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
joinsummary/joinsummary.go (1)

116-178: 🧹 Nitpick | 🔵 Trivial

LGTM! The flow from post fetch through LLM completion to ephemeral delivery is well-structured, with appropriate error handling at each step.

One operational note: GetPostsSince may return a large number of posts for very active channels within the lookback window. All of them are formatted via format.ThreadData and sent to the LLM in a single completion. Consider adding a configurable maximum post cap (e.g., 200) to bound token cost and prevent context-window overflow, especially since admins can set LookbackDays to large values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@joinsummary/joinsummary.go` around lines 116 - 178, `generateAndSend`
currently sends every post returned by `GetPostsSince` through
`format.ThreadData` into a single LLM request, which can spike token usage and
overflow the context window for busy channels. Add a configurable maximum post
cap (for example via `config.ChannelJoinSummaryConfig` or a helper used in
`generateAndSend`) and trim `threadData.Posts` before formatting and building
the `llm.CompletionRequest`, while keeping the existing min-posts and
error-handling flow intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/main.go`:
- Around line 616-621: `UserHasJoinedChannel` is starting the join-summary LLM
path with `context.Background()`, which prevents cancellation during plugin
shutdown. Add a plugin-level cancellable `context.Context` on `Plugin`
(initialized in `OnActivate` and canceled in `OnDeactivate`) and pass that
context into `joinSummaryService.HandleUserJoinedChannel` instead of creating a
background context. Keep the context threaded through the existing
`HandleUserJoinedChannel` → `generateAndSend` → `ChatCompletionNoStream` path so
the request can stop cleanly when the plugin deactivates.

---

Nitpick comments:
In `@joinsummary/joinsummary.go`:
- Around line 116-178: `generateAndSend` currently sends every post returned by
`GetPostsSince` through `format.ThreadData` into a single LLM request, which can
spike token usage and overflow the context window for busy channels. Add a
configurable maximum post cap (for example via `config.ChannelJoinSummaryConfig`
or a helper used in `generateAndSend`) and trim `threadData.Posts` before
formatting and building the `llm.CompletionRequest`, while keeping the existing
min-posts and error-handling flow intact.
🪄 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: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 0687480d-34fd-446a-8083-05f3b9a48ddb

📥 Commits

Reviewing files that changed from the base of the PR and between 302b4b7 and 1194eb1.

📒 Files selected for processing (9)
  • config/config.go
  • docs/admin_guide.md
  • joinsummary/joinsummary.go
  • joinsummary/joinsummary_test.go
  • server/main.go
  • webapp/src/components/system_console/channel_join_summary/channel_join_summary_panel.tsx
  • webapp/src/components/system_console/config.tsx
  • webapp/src/components/system_console/plugin_config_types.tsx
  • webapp/src/i18n/en.json

Comment thread server/main.go
Comment on lines +616 to +621
func (p *Plugin) UserHasJoinedChannel(c *plugin.Context, channelMember *model.ChannelMember, actor *model.User) {
if p.joinSummaryService == nil {
return
}
p.joinSummaryService.HandleUserJoinedChannel(context.Background(), channelMember, actor)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Replace context.Background() with a plugin-level cancellable context.

The UserHasJoinedChannel hook is an entry point to an LLM call path (HandleUserJoinedChannelgenerateAndSendChatCompletionNoStream). The coding guidelines state: "Thread ctx context.Context as the first parameter through every entry point to LLM call code paths; never introduce context.Background() shortcuts in production code." Using context.Background() means the async goroutine cannot be cancelled during plugin deactivation—it will run until the 2-minute summaryTimeout expires.

Consider creating a plugin-level context.Context in OnActivate (cancelled in OnDeactivate) and passing it here instead. This enables clean shutdown and satisfies the guideline.

As per coding guidelines: "Thread ctx context.Context as the first parameter through every entry point to LLM call code paths; never introduce context.Background() shortcuts in production code."

🔧 Suggested fix: plugin-level cancellable context
 type Plugin struct {
 	plugin.MattermostPlugin
 	configuration config.Container
+	pluginCtx      context.Context
+	pluginCancel   context.CancelFunc
 	// ... existing fields
 }

 func (p *Plugin) OnActivate() error {
+	p.pluginCtx, p.pluginCancel = context.WithCancel(context.Background())
 	// ... existing activation code
 	return nil
 }

+func (p *Plugin) OnDeactivate() error {
+	if p.pluginCancel != nil {
+		p.pluginCancel()
+	}
+	return nil
+}

 func (p *Plugin) UserHasJoinedChannel(c *plugin.Context, channelMember *model.ChannelMember, actor *model.User) {
 	if p.joinSummaryService == nil {
 		return
 	}
-	p.joinSummaryService.HandleUserJoinedChannel(context.Background(), channelMember, actor)
+	p.joinSummaryService.HandleUserJoinedChannel(p.pluginCtx, channelMember, actor)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/main.go` around lines 616 - 621, `UserHasJoinedChannel` is starting
the join-summary LLM path with `context.Background()`, which prevents
cancellation during plugin shutdown. Add a plugin-level cancellable
`context.Context` on `Plugin` (initialized in `OnActivate` and canceled in
`OnDeactivate`) and pass that context into
`joinSummaryService.HandleUserJoinedChannel` instead of creating a background
context. Keep the context threaded through the existing
`HandleUserJoinedChannel` → `generateAndSend` → `ChatCompletionNoStream` path so
the request can stop cleanly when the plugin deactivates.

Source: Coding guidelines

@mattermost-build

Copy link
Copy Markdown
Collaborator

This PR has been automatically labelled "stale" because it hasn't had recent activity.
A core team member will check in on the status of the PR to help with questions.
Thank you for your contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants