Skip to content

feat(extensions): expand event surface - #619

Merged
benvinegar merged 2 commits into
mainfrom
feat/extension-event-surface
Jul 28, 2026
Merged

feat(extensions): expand event surface#619
benvinegar merged 2 commits into
mainfrom
feat/extension-event-surface

Conversation

@benvinegar

Copy link
Copy Markdown
Member

Summary

  • add UI lifecycle events plus sidebar controls for extension event handlers
  • add the hunk.events inter-extension bus
  • document the API and add release notes/tests

Validation

  • bun run typecheck
  • bun run lint
  • bun run check:pack
  • focused event, watch-controller, and AppHost tests
  • bun test timed out on the existing PTY atomic-save watch test

This PR description was generated by Pi using OpenAI GPT-5.4

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
hunk-web Ready Ready Preview Jul 28, 2026 2:40pm

Request Review

@benvinegar
benvinegar marked this pull request as ready for review July 28, 2026 14:31
@benvinegar
benvinegar force-pushed the feat/extension-event-surface branch from b6dd20b to 83a31eb Compare July 28, 2026 14:31
Comment thread src/ui/App.tsx
Comment on lines +741 to 744
emitExtensionEvent(extensions, "file_viewed", { file: selectedFile, hunkIndex });
}
}, SELECTION_CHANGED_DEBOUNCE_MS);

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.

P1 Same-ID reloads suppress file events

When a soft reload replaces the selected file or extension set while preserving the file ID, lastViewedFileIdRef still matches and suppresses file_viewed, causing file-scoped extension panes to remain uninitialized or display data from before the reload.

Knowledge Base Used: UI App Shell: AppHost, App, and Controller Hooks

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/App.tsx
Line: 741-744

Comment:
**Same-ID reloads suppress file events**

When a soft reload replaces the selected file or extension set while preserving the file ID, `lastViewedFileIdRef` still matches and suppresses `file_viewed`, causing file-scoped extension panes to remain uninitialized or display data from before the reload.

**Knowledge Base Used:** [UI App Shell: AppHost, App, and Controller Hooks](https://app.greptile.com/modem/-/custom-context/knowledge-base/modem-dev/hunk/-/docs/ui-app-host.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 77bb3a1. file_viewed now tracks the reviewed file object rather than only its stable id, and an AppHost regression test covers a same-id soft reload.

Responded by Pi using model/version unavailable in this environment.

This comment was generated by Pi using model/version unavailable in this environment

Comment on lines +298 to +301
emit(event: string, payload: unknown) {
assertNonEmptyString(event, "events.emit requires a non-empty event name.");
registry.emitCustomEvent?.(event, payload);
},

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.

P1 Factory-time events are silently dropped

When an extension emits during its factory after an earlier extension registered a listener, emitCustomEvent is still undefined and optional chaining silently discards the event, breaking inter-extension coordination without an error or warning.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/extensions/runExtension.ts
Line: 298-301

Comment:
**Factory-time events are silently dropped**

When an extension emits during its factory after an earlier extension registered a listener, `emitCustomEvent` is still undefined and optional chaining silently discards the event, breaking inter-extension coordination without an error or warning.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 77bb3a1. Factory-time bus events now queue until all extensions have subscribed; queued events from failed factories roll back. Added regression coverage.

Responded by Pi using model/version unavailable in this environment.

This comment was generated by Pi using model/version unavailable in this environment

@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR expands the extension event surface and runtime coordination APIs.

  • Adds UI lifecycle events for selection, files, filters, themes, layouts, watch activity, and review notes.
  • Adds an inter-extension event bus with isolated, fire-and-forget listener delivery.
  • Gives lifecycle and bus handlers extension-scoped sidebar controls.
  • Updates watch integration, extension reload handling, API documentation, tests, and release notes.

Confidence Score: 3/5

The PR should not merge until current-file events survive soft reloads and event-bus emissions made during extension initialization are no longer silently lost.

Same-ID reloads suppress file_viewed despite replacing file data or extensions, while the public bus accepts emit calls before its dispatcher is installed and drops those events.

Files Needing Attention: src/ui/App.tsx and src/extensions/runExtension.ts

Important Files Changed

Filename Overview
src/ui/App.tsx Wires the new UI lifecycle events and handler contexts, but file_viewed remains incorrectly suppressed across same-ID soft reloads and extension replacement.
src/extensions/runExtension.ts Adds event-bus registration and emission to the extension API, but factory-time emissions are silently discarded before bus binding.
src/extensions/events.ts Implements lifecycle and custom-event payload isolation, per-extension contexts, listener error isolation, and runtime bus binding.
src/ui/AppHost.tsx Detaches retired event buses during extension reload while preserving the existing host reload lifecycle.
src/core/watchController.ts Adds a single pending-reload notification per filesystem-event debounce window.
src/extension-api/types.ts Defines the expanded lifecycle payloads, review-note model, event contexts, and public custom-event bus API.

Sequence Diagram

sequenceDiagram
  participant Factory as Extension factory
  participant Registry as Extension registry
  participant Host as Extension host
  participant App as App UI
  participant Handler as Extension handler
  Factory->>Registry: events.on(name, handler)
  Factory->>Registry: events.emit(name, payload)
  Note over Registry: Dispatcher is not bound during factory loading
  Host->>Registry: bindExtensionEventBus()
  App->>Registry: emit lifecycle/UI event
  Registry->>Handler: payload + sidebar/event context
Loading
Prompt To Fix All With AI
### Issue 1
src/ui/App.tsx:741-744
**Same-ID reloads suppress file events**

When a soft reload replaces the selected file or extension set while preserving the file ID, `lastViewedFileIdRef` still matches and suppresses `file_viewed`, causing file-scoped extension panes to remain uninitialized or display data from before the reload.

### Issue 2
src/extensions/runExtension.ts:298-301
**Factory-time events are silently dropped**

When an extension emits during its factory after an earlier extension registered a listener, `emitCustomEvent` is still undefined and optional chaining silently discards the event, breaking inter-extension coordination without an error or warning.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat(extensions): expand event surface" | Re-trigger Greptile

@benvinegar
benvinegar enabled auto-merge (squash) July 28, 2026 14:43
@benvinegar
benvinegar merged commit 926324d into main Jul 28, 2026
12 checks passed
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