feat(plugins): bridge core domain events to plugin event bus#909
Conversation
The plugin event bus accepts subscriptions for core events like issue.created but nothing emits them. This adds a bridge in logActivity() so every domain action that's already logged also fires a PluginEvent to subscribing plugins. Uses a module-level setter (same pattern as publishLiveEvent) to avoid threading the bus through all route handlers. Only actions matching PLUGIN_EVENT_TYPES are forwarded. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Greptile SummaryThis PR wires core domain events into the plugin event bus by adding a bridge inside Key changes:
Two minor concerns found:
Confidence Score: 4/5
Important Files Changed
Prompt To Fix All With AIThis is a comment left during a code review.
Path: server/src/services/activity-log.ts
Line: 78
Comment:
**Plugin handler errors are silently discarded**
`emit()` returns a `Promise<PluginEventBusEmitResult>` where the `errors` array holds any failures from individual plugin handlers (they're already isolated per-plugin inside the bus). By using `void ... .catch(() => {})`, the result is thrown away entirely — so if a plugin handler throws, there's no log, no metric, and no way to diagnose the failure.
The `.catch(() => {})` is also effectively dead code because the bus never rejects (it catches all handler errors internally). Consider inspecting the result and logging failures:
```suggestion
void _pluginEventBus.emit(event).then(({ errors }) => {
for (const { pluginId, error } of errors) {
console.error(`[plugin-event-bridge] handler error in plugin "${pluginId}":`, error);
}
});
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: server/src/services/activity-log.ts
Line: 16-18
Comment:
**No guard against double-initialization**
`setPluginEventBus` can be called multiple times and silently replaces the current bus with no warning. In tests where `createApp()` is called per-suite or per-test, the module-level `_pluginEventBus` will be overwritten on each call, with no indication that an existing bus has been replaced.
A warn-or-throw guard would make misuse obvious:
```suggestion
export function setPluginEventBus(bus: PluginEventBus): void {
if (_pluginEventBus !== null && _pluginEventBus !== bus) {
console.warn("[activity-log] setPluginEventBus called more than once — replacing existing bus");
}
_pluginEventBus = bus;
}
```
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: 30e2914 |
server/src/services/activity-log.ts
Outdated
| runId: input.runId ?? null, | ||
| }, | ||
| }; | ||
| void _pluginEventBus.emit(event).catch(() => {}); |
There was a problem hiding this comment.
Plugin handler errors are silently discarded
emit() returns a Promise<PluginEventBusEmitResult> where the errors array holds any failures from individual plugin handlers (they're already isolated per-plugin inside the bus). By using void ... .catch(() => {}), the result is thrown away entirely — so if a plugin handler throws, there's no log, no metric, and no way to diagnose the failure.
The .catch(() => {}) is also effectively dead code because the bus never rejects (it catches all handler errors internally). Consider inspecting the result and logging failures:
| void _pluginEventBus.emit(event).catch(() => {}); | |
| void _pluginEventBus.emit(event).then(({ errors }) => { | |
| for (const { pluginId, error } of errors) { | |
| console.error(`[plugin-event-bridge] handler error in plugin "${pluginId}":`, error); | |
| } | |
| }); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: server/src/services/activity-log.ts
Line: 78
Comment:
**Plugin handler errors are silently discarded**
`emit()` returns a `Promise<PluginEventBusEmitResult>` where the `errors` array holds any failures from individual plugin handlers (they're already isolated per-plugin inside the bus). By using `void ... .catch(() => {})`, the result is thrown away entirely — so if a plugin handler throws, there's no log, no metric, and no way to diagnose the failure.
The `.catch(() => {})` is also effectively dead code because the bus never rejects (it catches all handler errors internally). Consider inspecting the result and logging failures:
```suggestion
void _pluginEventBus.emit(event).then(({ errors }) => {
for (const { pluginId, error } of errors) {
console.error(`[plugin-event-bridge] handler error in plugin "${pluginId}":`, error);
}
});
```
How can I resolve this? If you propose a fix, please make it concise.| export function setPluginEventBus(bus: PluginEventBus): void { | ||
| _pluginEventBus = bus; | ||
| } |
There was a problem hiding this comment.
No guard against double-initialization
setPluginEventBus can be called multiple times and silently replaces the current bus with no warning. In tests where createApp() is called per-suite or per-test, the module-level _pluginEventBus will be overwritten on each call, with no indication that an existing bus has been replaced.
A warn-or-throw guard would make misuse obvious:
| export function setPluginEventBus(bus: PluginEventBus): void { | |
| _pluginEventBus = bus; | |
| } | |
| export function setPluginEventBus(bus: PluginEventBus): void { | |
| if (_pluginEventBus !== null && _pluginEventBus !== bus) { | |
| console.warn("[activity-log] setPluginEventBus called more than once — replacing existing bus"); | |
| } | |
| _pluginEventBus = bus; | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: server/src/services/activity-log.ts
Line: 16-18
Comment:
**No guard against double-initialization**
`setPluginEventBus` can be called multiple times and silently replaces the current bus with no warning. In tests where `createApp()` is called per-suite or per-test, the module-level `_pluginEventBus` will be overwritten on each call, with no indication that an existing bus has been replaced.
A warn-or-throw guard would make misuse obvious:
```suggestion
export function setPluginEventBus(bus: PluginEventBus): void {
if (_pluginEventBus !== null && _pluginEventBus !== bus) {
console.warn("[activity-log] setPluginEventBus called more than once — replacing existing bus");
}
_pluginEventBus = bus;
}
```
How can I resolve this? If you propose a fix, please make it concise.Address Greptile review feedback: - Log plugin event handler errors via logger.warn instead of silently discarding the emit() result - Warn if setPluginEventBus is called more than once Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
ty!! |
|
Thanks for merging these, @cryppadotta! |
…vent-bridge feat(plugins): bridge core domain events to plugin event bus
…vent-bridge feat(plugins): bridge core domain events to plugin event bus
Summary
The plugin event bus accepts subscriptions for core events like
issue.created, and the kitchen sink example registers handlers forissue.createdandissue.updated(worker.ts L891-908). But these handlers never fire because no code emits core domain events into the bus.This adds a bridge in
logActivity()so every domain action that's already logged also fires aPluginEventto subscribing plugins.Why this matters
The plugin system shipped today (PR #821) with a full event subscription API, but the core domain events aren't wired to it yet. This is the missing bridge.
issue.createdandissue.updated(L891-908) but handlers never firePLUGIN_EVENT_TYPESdefines 23 event types includingissue.created,issue.updated,agent.run.finishedNotification plugins, issue sync plugins, and any event-driven automation all need this bridge before they can work.
Changes
server/src/services/activity-log.ts(+30 lines)setPluginEventBus()setterpublishLiveEvent()call, emit aPluginEventwhen the action matchesPLUGIN_EVENT_TYPESPLUGIN_EVENT_SET(aSetbuilt fromPLUGIN_EVENT_TYPES) to filter - non-plugin actions are not forwardedserver/src/app.ts(+2 lines)setPluginEventBuscreatePluginEventBus()to wire the bridgeDesign decisions
publishLiveEventwhich uses a module-levelEventEmitter. Zero call-site changes..catch(() => {})- plugin handler errors are already isolated by the bus.PLUGIN_EVENT_TYPESare forwarded - random action strings don't leak through.logActivityinstead of scatteringeventBus.emit()across 10+ route handlers -logActivityalready intercepts every domain action.Testing
pnpm -r typecheckpassespnpm test:runpasses (73 files, 303 tests)pnpm buildpassesThis contribution was developed with AI assistance (Claude Code).