Skip to content

ref(briefs): Run Conversation Briefs as a core feature - #3410

Open
sentry-junior[bot] wants to merge 1 commit into
mainfrom
feat/core-conversation-briefs
Open

sentry-junior[bot] wants to merge 1 commit into
mainfrom
feat/core-conversation-briefs

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

Briefs are core behavior. Core always registers them, and no installed plugin owns them. But they were still built as a fake PluginRegistration with a task context, event emitter, and report hook. That made core route its own feature through the plugin contracts and through special cases in the plugin catalog loops. This PR removes that layer, so Briefs run through direct core calls and core types. Follow-up to #3334.

The branch is based on main after #3334 merged. The Conversation embedding write from #3334 stays in the Brief path.

Changes

  • updateConversationBrief({ conversationId, turnId, operationId }) takes core arguments instead of a PluginTaskContext. It reads the Turn record and Source directly and uses the core withLock, getDb(), and logInfo.
  • The session-completed task runner adds the Brief task when Briefs are enabled and calls it directly. Slack Turns still queue it; mailbox and local Turns still run it after the reply.
  • briefs/events.ts appends and renders brief_updated itself, the same way junior native events work. The Conversation event API renders the briefs namespace through it.
  • buildBriefsOperationalReport(nowMs) reads its own costs through the new readConversationEventCostsByDay and returns the core report type. The report feed lists it first. A failure still shows a failed card, so the other reports still load.
  • Deleted: briefsTaskRegistration, coreTaskRegistrations(), and the Brief entries in the plugin report and plugin event render loops. briefs/registration.ts is now briefs/config.ts and holds only the enabled setting.
  • Reply notifications stay on the plugin registration path (notificationTaskRegistrations()) because they still use it.

Design calls

  • Shared task queue kept. Briefs still use junior_plugin_tasks and the { plugin: "briefs", name: "updateBrief" } message. A new topic would need new routes, triggers, and workers. Keeping the message shape lets queued messages from before the deploy still run.
  • Stored names unchanged. The briefs namespace, brief_updated v1/v2, the plugin:briefs:operation:…@1 idempotency key, the cost-cache keys, and the report name briefs stay the same.
  • defineConversationEvent kept for the event schemas. Core native events use the same helper.
  • Annotation reads unchanged. input.ts and schema.ts still use the shared annotation contract, since Briefs read plugin-written annotations as data.
  • New lock key. The Brief lock moved from the hashed plugin-state key to briefs:conversation:<id>. During a deploy, an old and a new worker can miss each other's lock. The per-Turn row and the turn_already_stored / turn_already_covered checks then stop a second Brief for the same Turn.
  • New instruction check. The user-instruction check now reads the Turn's Pi messages and provenance (getUserMessageInstructionText) instead of the plugin run projection.

via @dcramer.

--

View Session [Sentry]

Briefs no longer register as a plugin. The session-completed task runner
calls the Brief task directly, the Brief code writes and renders its own
`brief_updated` events, and the System report feed builds the Briefs
report itself. Stored events, task ids, and the report name do not change.

Reply notifications stay on the core task registration path.

Co-Authored-By: David Cramer <23610+dcramer@users.noreply.github.com>
@vercel

vercel Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
junior-docs Ready Ready Preview Nov 14, 2026 12:04am UTC

Request Review

@dcramer dcramer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

one thing thats worth considering here: this is really just becoming core conversation metadata

we should challenge if and what is needed, but certainly the summary of the conversation is adding value as we use it in search

@sentry-junior

sentry-junior Bot commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Agreed, the Brief is now core Conversation metadata. Here is where each field is read today, outside the Brief code and the CLI:

  • summary, outcome: search (conversations/search.ts), the MCP get-conversation tool, mentions, catch-up, Slack unfurls / continue-in-Slack, object cards, and the dashboard.
  • searchText: embedded as the Conversation vector (feat(conversations): Rank Conversation search by Conversation embeddings #3334). This is the main reason to keep generation.
  • intent, decisions: catch-up, mentions, MCP get-conversation, and the dashboard.
  • links: search, MCP get-conversation, and the dashboard.
  • facts: mentions, MCP get-conversation, and the dashboard.
  • keywords: only the dashboard Brief card renders them. Their wider use is in searchText.
  • record (counts, participants, code changes): no reader outside the Brief code. It is computed from SQL, not by the model.

I'd keep this PR as the mechanical move, with no change to stored data. Trimming the schema (record, possibly facts/keywords) or renaming Brief to Conversation metadata touches stored rows, the prompt, and these readers, so it fits better as a follow-up. Tell me if you want me to open it.

@github-actions

Copy link
Copy Markdown
Contributor

17 screenshot changes — 17 changed · 0 added · 0 removed

Review screenshots in Frameshift

Automation Run Details · Desktop
Automation Run Details · Desktop
Changed
Automations Runs Filters · Desktop
Automations Runs Filters · Desktop
Changed
Conversation Attachment · Desktop
Conversation Attachment · Desktop
Changed
Conversation Attachment · Mobile
Conversation Attachment · Mobile
Changed
Conversation Attachment Modal · Desktop
Conversation Attachment Modal · Desktop
Changed
Conversation Attachment Zoom · Desktop
Conversation Attachment Zoom · Desktop
Changed
Show 11 more changed screenshots
Conversation Captured Memories Popover · Desktop
Conversation Captured Memories Popover · Desktop
Changed
Conversation Child Agents · Desktop
Conversation Child Agents · Desktop
Changed
Conversation Details Watched Linked Work · Desktop
Conversation Details Watched Linked Work · Desktop
Changed
Conversation Long Transcript Touch · Mobile
Conversation Long Transcript Touch · Mobile
Changed
Conversation Memory List Jump · Desktop
Conversation Memory List Jump · Desktop
Changed
Conversation Memory List Jump · Mobile
Conversation Memory List Jump · Mobile
Changed
Conversation Stop Active · Mobile
Conversation Stop Active · Mobile
Changed
Conversation Text Attachment · Desktop
Conversation Text Attachment · Desktop
Changed
Memories · Mobile
Memories · Mobile
Changed
Push Notifications Tip · Desktop
Push Notifications Tip · Desktop
Changed
Settings Mcp · Desktop
Settings Mcp · Desktop
Changed

@dcramer

dcramer commented Nov 14, 2026

Copy link
Copy Markdown
Member

is there a way to do tasks that arent from a plugin? its ok if we have like core plugins that cant be uninstalled i suppose, if its easier to organize the code

@sentry-junior

sentry-junior Bot commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Yes. Core already runs background work outside plugins, on its own Vercel Queue topics: context compaction (junior_context_compaction), cache warmups, workspace snapshots, and conversation work. Each one has its own signed queue module, a callback route, a nitro.ts trigger, and a pulling consumer.

For the work that runs after a Turn completes, there are three options:

  1. What this PR does. Briefs reuse the session-completed task queue (junior_plugin_tasks), and processPluginTask calls updateConversationBrief directly. No plugin contract is involved. The cost is one core special case in the plugin runner.
  2. A core task list in the runner. Rename the runner to "session tasks" and give it a small core table, { name, enabled(), run(params) }, next to plugin tasks. Briefs and reply notifications would both live there. The catch: notifications read the plugin run projection (PluginTaskContext), so moving them means giving them core inputs too.
  3. A dedicated topic for Briefs, like compaction. That cleanly separates them, but adds a topic, a route, a trigger, and a consumer for a single task.

The "core plugins that can't be uninstalled" shape is what main had before this PR: briefsTaskRegistration and notificationsTaskRegistration kept outside the catalog. It works, but core features then depend on the plugin contracts, and the plugin loops pick up special cases.

My pick: keep 1 for this PR. Do 2 as a follow-up if notifications should leave the plugin contract too. That's the point where a shared core task list has a second user. I can open that follow-up if you want it.

@dcramer

dcramer commented Nov 14, 2026

Copy link
Copy Markdown
Member

which notifications?

we want to move brief's summary => conversation.summary or something akin to that

@sentry-junior

sentry-junior Bot commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Which notifications: the dashboard reply push notifications, notifyReply in src/chat/notifications/task.ts. After a Turn completes, it sends a Web Push to the installed dashboard apps (PWA) of the people who should hear about the reply. It follows their notification schedule and pauses while they are active in Slack. It only registers when Web Push is configured (readWebPushConfig()). It's the only other core task on the session-completed queue, and it still uses the plugin PluginTaskContext run projection.

conversation.summary: makes sense. Today juniorConversations has only title, and every reader joins the latest row in junior_conversation_briefs to get summary, including search (conversations/search.ts), MCP get-conversation, unfurls, catch-up, and the dashboard. A follow-up could look like this:

  1. Add a summary column to juniorConversations. The Brief task writes it in the same transaction as the Brief row.
  2. Backfill it from the latest Brief.
  3. Point the summary readers at the column, and keep the full Brief for the fields that need it.
  4. Then decide which other Brief fields still earn their place.

I'd keep this PR as the move and do that as its own PR, because it adds a migration and changes readers. Say the word if you want it folded in here instead.

This branch was successfully deployed

1 active deployment
Preview – junior-docs — 21bb5534 Deployed Nov 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant