Skip to content

refactor(extension): split Lua manager#66

Merged
omarluq merged 3 commits into
mainfrom
refactor/extension-manager-split
May 31, 2026
Merged

refactor(extension): split Lua manager#66
omarluq merged 3 commits into
mainfrom
refactor/extension-manager-split

Conversation

@omarluq

@omarluq omarluq commented May 30, 2026

Copy link
Copy Markdown
Owner

Summary\n- split the Lua extension manager into focused loader, dispatch, and source files\n- keep public Manager API and behavior unchanged\n\n## Validation\n- mise exec -- go test ./internal/extension\n- mise exec -- task ci

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f8db79d1-4245-4eb6-80ba-4bddbef8294c

📥 Commits

Reviewing files that changed from the base of the PR and between 17dc1b0 and 09704df.

📒 Files selected for processing (1)
  • internal/extension/manager_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/extension/manager_test.go

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Extensions can register and run commands, tools, and terminal event handlers; Lua sources and module roots are auto-discovered and wired.
  • Bug Fixes
    • Command/tool execution and event dispatch respect context cancellation.
    • Partial registrations are rolled back on extension load or setup failures to avoid stale state.
  • Refactor
    • Extension manager reorganized into clear dispatch vs. loading responsibilities.
  • Tests
    • Added tests covering cancellation and rollback behavior during extension load/setup.

Walkthrough

Splits the extension manager into source discovery, loader with injected librecode API, and Lua dispatch layers; consolidates manager.go for metadata/shutdown with unregister helpers; adds tests for cancellation and rollback.

Changes

Extension Manager Refactoring

Layer / File(s) Summary
Source discovery and module root management
internal/extension/manager_sources.go
Filesystem scanning identifies Lua entry points, treats init.lua as manifests, collects .lua sources, and manages module roots and package.path configuration with defensive snapshots.
Extension loading and Lua API installation
internal/extension/manager_loader.go
Loads manifests and entry files into isolated Lua runtimes, opens a fixed set of Lua libs, configures module roots/package.path, injects the librecode API (register_command/register_tool/on/log), supports optional setup functions, and records registrations for rollback on failure.
Lua command, tool, and event dispatch
internal/extension/manager_dispatch.go
Implements ExecuteCommand/ExecuteTool/HandleTerminalEvent/Emit/HasTerminalEventHandlers; marshals arguments to Lua, serializes runtime access with per-runtime locks, orders handlers by priority/order, records Lua call durations, and respects context cancellation.
Code extraction and manager.go consolidation
internal/extension/manager.go
Removes in-file loading/dispatch implementations, trims imports, retains metadata accessors (Extensions, Commands, Tools), and adds unregistration helpers used during Shutdown to remove commands/tools/handlers/keymaps/timers and runtimes.
Tests
internal/extension/manager_test.go
Adds tests ensuring commands/tools do not run with canceled contexts and that partial registrations are rolled back when loads or setups fail.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • omarluq/librecode#63: Related caller surface changes—slash-command path calls extensions.ExecuteCommand, which this refactor moves/implements across loader/dispatch.
  • omarluq/librecode#23: Builds on the refactored dispatch for lifecycle/event emission into extension Lua handlers.
  • omarluq/librecode#21: Related to Lua handler dispatch and lifecycle invocation that reuse the same call/dispatch utilities.

Poem

🐰 A rabbit nods where files divide,

Sources scout, loaders spin with pride.
Dispatch sings to commands and keys,
Rollbacks tidy any unease.
Hops away — the manager refreshed and spry.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: refactoring the Lua extension manager by splitting it into focused, separate files.
Description check ✅ Passed The description is directly related to the changeset, explaining the split into loader, dispatch, and source files while maintaining the public API.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/extension-manager-split

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

@codecov-commenter

codecov-commenter commented May 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.59584% with 110 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.64%. Comparing base (218ab27) to head (09704df).

Files with missing lines Patch % Lines
internal/extension/manager_dispatch.go 65.09% 24 Missing and 13 partials ⚠️
internal/extension/manager_loader.go 82.53% 20 Missing and 13 partials ⚠️
internal/extension/manager_sources.go 65.51% 18 Missing and 12 partials ⚠️
internal/extension/manager.go 80.39% 6 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #66      +/-   ##
==========================================
+ Coverage   62.35%   62.64%   +0.28%     
==========================================
  Files         185      188       +3     
  Lines       17852    17910      +58     
==========================================
+ Hits        11132    11220      +88     
+ Misses       5622     5591      -31     
- Partials     1098     1099       +1     
Flag Coverage Δ
unittests 62.64% <74.59%> (+0.28%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@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: 2

🤖 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 `@internal/extension/manager_dispatch.go`:
- Around line 13-27: The ExecuteCommand implementation may call into Lua after
the context is already canceled; add an early context-cancellation guard (check
ctx.Err()) immediately after verifying the command exists and before invoking
callLua so you return the context error without running extension code; apply
the same pattern wherever similar logic exists (e.g., other manager methods that
call callLua or call extension.function) to check ctx.Err() before executing any
extension code.

In `@internal/extension/manager_loader.go`:
- Around line 143-153: If DoFile or PCall fails, existing registrations from the
partially-initialized luaExtension remain pointing at a closed runtime; call a
rollback routine on the Manager to remove those registrations before closing the
runtime and returning. Add a method like Manager.unregisterRuntime(rt
*luaExtension) that locks the manager and deletes entries in manager.commands
and manager.tools using rt.commands and rt.tools (and removes any
handlers/keymaps tied to rt), then invoke
manager.unregisterRuntime(extensionRuntime) immediately on any error path
(before calling extensionRuntime.state.Close() and returning) for both the
DoFile failure and the PCall/setup failure paths (and similarly for the later
failure paths referenced around 157-159).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c3ffb770-3fc5-4368-b352-587b00eefa5a

📥 Commits

Reviewing files that changed from the base of the PR and between 218ab27 and c1641c3.

📒 Files selected for processing (4)
  • internal/extension/manager.go
  • internal/extension/manager_dispatch.go
  • internal/extension/manager_loader.go
  • internal/extension/manager_sources.go
💤 Files with no reviewable changes (1)
  • internal/extension/manager.go

Comment thread internal/extension/manager_dispatch.go
Comment thread internal/extension/manager_loader.go

@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.

🧹 Nitpick comments (1)
internal/extension/manager_test.go (1)

598-600: ⚡ Quick win

Tighten failure-path assertions to avoid false-positive passes.

Both rollback tests only assert that an error exists. Please assert a stable substring that proves the intended failure path (load vs setup) so unrelated failures don’t satisfy the test.

Proposed diff
-	err := manager.LoadFile(context.Background(), extensionPath)
-	require.Error(t, err)
+	err := manager.LoadFile(context.Background(), extensionPath)
+	require.ErrorContains(t, err, "extension: load")
+	require.ErrorContains(t, err, "boom")
@@
-	err := manager.LoadPaths(context.Background(), []string{extensionRoot})
-	require.Error(t, err)
+	err := manager.LoadPaths(context.Background(), []string{extensionRoot})
+	require.ErrorContains(t, err, "extension: setup")
+	require.ErrorContains(t, err, "boom")

Also applies to: 629-631

🤖 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 `@internal/extension/manager_test.go` around lines 598 - 600, The tests
currently only check that manager.LoadFile(...) returns an error and
manager.Commands() is empty, which can hide unrelated failures; update the
assertions to assert that the returned error contains a stable substring
indicating the intended failure path (e.g., assert.ErrorContains(t, err, "load
failed" or the exact load-specific message from LoadFile for the first test, and
similarly assert.ErrorContains(t, err, "setup failed" or the setup-specific
message for the second test), and keep the assert.Empty(t, manager.Commands())
checks; apply the same change to the other occurrence referenced (the block
around the lines noted for 629-631) so both rollback tests validate a specific
failure path rather than any error.
🤖 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.

Nitpick comments:
In `@internal/extension/manager_test.go`:
- Around line 598-600: The tests currently only check that manager.LoadFile(...)
returns an error and manager.Commands() is empty, which can hide unrelated
failures; update the assertions to assert that the returned error contains a
stable substring indicating the intended failure path (e.g.,
assert.ErrorContains(t, err, "load failed" or the exact load-specific message
from LoadFile for the first test, and similarly assert.ErrorContains(t, err,
"setup failed" or the setup-specific message for the second test), and keep the
assert.Empty(t, manager.Commands()) checks; apply the same change to the other
occurrence referenced (the block around the lines noted for 629-631) so both
rollback tests validate a specific failure path rather than any error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bba51c09-6439-4102-8bf4-3fde4c2feadc

📥 Commits

Reviewing files that changed from the base of the PR and between c1641c3 and 17dc1b0.

📒 Files selected for processing (4)
  • internal/extension/manager.go
  • internal/extension/manager_dispatch.go
  • internal/extension/manager_loader.go
  • internal/extension/manager_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/extension/manager.go
  • internal/extension/manager_dispatch.go
  • internal/extension/manager_loader.go

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 30, 2026
@sonarqubecloud

Copy link
Copy Markdown

@omarluq
omarluq merged commit 567508c into main May 31, 2026
13 checks passed
@omarluq
omarluq deleted the refactor/extension-manager-split branch May 31, 2026 00:14
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.

2 participants