Skip to content

fix(runtime): recover ro event helpers#30

Merged
omarluq merged 5 commits into
mainfrom
fix/ro-runtime-recovery
May 20, 2026
Merged

fix(runtime): recover ro event helpers#30
omarluq merged 5 commits into
mainfrom
fix/ro-runtime-recovery

Conversation

@omarluq
Copy link
Copy Markdown
Owner

@omarluq omarluq commented May 19, 2026

No description provided.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

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: 01cd9989-0ad5-4764-aa8c-b29c4c538ed1

📥 Commits

Reviewing files that changed from the base of the PR and between aa3a6e7 and 6887191.

📒 Files selected for processing (1)
  • internal/event/context_test.go

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Reactive runtime lifecycle events and tool call/result callbacks
    • File watch stream for filesystem changes
    • Signal stream and cancellable signal-aware contexts
    • Context-aware timing helpers (tick/after/done) and diagnostic event logging
  • Chores

    • Added two event-related dependencies
  • Tests

    • Expanded test coverage for runtime events, file watching, signals, diagnostics, and tool callbacks

Walkthrough

Adds CompletionRequest tool callbacks and runtime lifecycle emitters, and introduces reactive event utilities (tick, after-context, context-done, file-watch, signal) plus diagnostics, tests, DI test helper, and go.mod plugin deps.

Changes

Event and Callback Infrastructure

Layer / File(s) Summary
Tool callback contract and wiring
internal/assistant/client.go, internal/assistant/tool_loop.go, internal/assistant/openai_responses.go, internal/assistant/anthropic_internal_test.go
CompletionRequest gains OnEvent, OnToolCall, OnToolResult; ToolCallEvent added; executeToolCalls signature updated and invokes callbacks when present.
Runtime event emitters
internal/assistant/runtime_events.go
New emitters: emitProviderRequest, emitProviderResponse, emitProviderError, emitToolCall, emitToolResult and payload builders including model/session metadata and token usage.
Completion lifecycle wiring
internal/assistant/runtime.go
Centralized modelCompletionRequest wiring tool callbacks to runtime emitters; completeWithRetry emits provider lifecycle events per attempt and reports per-attempt errors.
Runtime event tests
internal/assistant/runtime_events_test.go
Tests validate provider request/response and error lifecycle emissions and that tool callbacks publish tool_call/tool_result events.
Context-aware event utilities
internal/event/context.go, internal/event/context_test.go
Adds TickStream, AfterContext, and ContextDone observables with tests for emission timing and cancellation behavior.
File watch and signal streams
internal/event/fsnotify.go, internal/event/fsnotify_test.go, internal/event/signals.go, internal/event/signals_unix_test.go
Adds FileWatchStream (fsnotify) and SignalStream/SignalContext backed by samber/ro plugins; tests cover event emission and error handling.
Diagnostic observer and tick tests
internal/event/diagnostics.go, internal/event/diagnostics_test.go
DiagnosticObserver logs envelope channels and payload types; tests assert log contents and TickStream emissions.
Assistant tool-loop tests
internal/assistant/tool_loop_test.go
Unit tests for tool-call validation, executeToolCalls callbacks/events/results, toolOutputText, and encodeToolDetails error handling.
Client tests and helpers
internal/assistant/client_test.go, internal/assistant/errors.go, internal/assistant/openai_responses.go
Adds SSE parsing test for extracting tool calls; adds firstNonEmptyString helper and uses it to extract tool call ID/name fallbacks.
DI test helper and dependencies
internal/di/event_service_export_test.go, internal/di/event_service_test.go, go.mod
Adds EventBusAvailableForTest helper, test asserting EventService exposes Bus, and samber/ro fsnotify/signal plugins to go.mod.

Sequence Diagram

sequenceDiagram
  participant Runtime
  participant EventBus
  participant CompletionClient
  participant ToolExecutor
  Runtime->>EventBus: dispatch before_provider_request(payload)
  Runtime->>CompletionClient: Complete(ctx, CompletionRequest{OnToolCall, OnToolResult, OnEvent})
  CompletionClient->>ToolExecutor: executeToolCalls(ctx, cwd, toolCalls, OnEvent, OnToolCall, OnToolResult)
  ToolExecutor->>Runtime: OnToolCall(ctx, ToolCallEvent)
  ToolExecutor->>Runtime: OnToolResult(ctx, ToolEvent)
  CompletionClient->>EventBus: completion result -> dispatch after_provider_response(payload)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • omarluq/librecode#28: Related runtime lifecycle/event plumbing; both PRs modify runtime event emission surfaces.

Poem

🐰 I twitch my whiskers, ears upright,

Events hop in through day and night,
Signals, files, and tool calls too,
Callbacks bounce and logs review,
A merry reactive trail in sight!

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided by the author, so the description content cannot be evaluated for relevance. Add a description explaining the purpose and scope of the changes, such as what 'recovering ro event helpers' entails and why these changes were made.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title references 'recover ro event helpers', which relates to the addition of new reactive event helpers and runtime event emission infrastructure visible in the changeset.
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 fix/ro-runtime-recovery

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

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 19, 2026

Codecov Report

❌ Patch coverage is 80.38278% with 41 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.13%. Comparing base (c39493d) to head (6887191).

Files with missing lines Patch % Lines
internal/assistant/runtime_events.go 82.75% 5 Missing and 5 partials ⚠️
internal/assistant/openai_responses.go 20.00% 8 Missing ⚠️
internal/assistant/runtime.go 80.55% 7 Missing ⚠️
internal/event/diagnostics.go 81.25% 6 Missing ⚠️
internal/event/signals.go 68.75% 5 Missing ⚠️
internal/event/context.go 90.47% 4 Missing ⚠️
internal/assistant/errors.go 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #30      +/-   ##
==========================================
+ Coverage   57.36%   58.13%   +0.77%     
==========================================
  Files         160      165       +5     
  Lines       15782    15973     +191     
==========================================
+ Hits         9053     9286     +233     
+ Misses       5767     5719      -48     
- Partials      962      968       +6     
Flag Coverage Δ
unittests 58.13% <80.38%> (+0.77%) ⬆️

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
internal/event/diagnostics.go (1)

36-36: ⚡ Quick win

Avoid an empty observer callback body to prevent quality-gate failures.

The no-op func(context.Context, Envelope) {} is currently flagged by Sonar. Add a brief inline comment in the body (or route through a named no-op helper) so CI/static analysis doesn’t fail on this path.

🤖 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/event/diagnostics.go` at line 36, The inline anonymous no-op
observer func(context.Context, Envelope) {} in diagnostics should not have an
empty body; either add a brief inline comment inside the function body (e.g. //
intentionally no-op) or replace the literal with a named helper like
noOpObserver defined as func(context.Context, Envelope) { /* no-op */ } and use
that identifier where the observer callback is registered; this addresses the
Sonar quality-gate by ensuring the no-op has an explicit comment or named helper
while leaving the observable signature (context.Context, Envelope) unchanged.
internal/event/signals.go (1)

29-29: ⚡ Quick win

Document the intentional no-op completion callback.

The empty completion handler can fail static analysis; add an inline comment explaining it is intentionally ignored because cancellation is handled in next/error.

Proposed tweak
-		func(context.Context) {},
+		func(context.Context) {
+			// Intentionally no-op: context cancellation is driven by next/error handlers.
+		},
🤖 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/event/signals.go` at line 29, Add an inline comment to the no-op
completion callback literal func(context.Context) {} in
internal/event/signals.go explaining it's intentionally empty and ignored
because cancellation/cleanup is handled via the next/error path; locate the
anonymous completion handler (the literal func(context.Context) {}) and append a
short comment like "intentionally no-op; cancellation handled in next/error" so
static analysis and reviewers understand this is deliberate.
🤖 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/di/event_service_test.go`:
- Around line 17-20: Replace the unbounded context used in the cleanup with a
timeout-bound context: inside the t.Cleanup lambda that calls
container.ShutdownWithContext, create a context with context.WithTimeout (e.g.,
5s or appropriate CI-safe duration), defer cancel(), pass that ctx to
container.ShutdownWithContext, and keep the existing require.True assertion on
report.Succeed (report.Error()) so the test can't hang if shutdown blocks;
reference the t.Cleanup closure and the container.ShutdownWithContext invocation
when making the change.

In `@internal/event/diagnostics_test.go`:
- Line 46: The test's empty onError callback (func(context.Context, error) {})
silently ignores stream errors; replace it with a callback that fails the test
on error by asserting the error is nil (e.g., func(ctx context.Context, err
error) { require.NoError(t, err) } or func(ctx context.Context, err error) { if
err != nil { t.Fatalf("stream error: %v", err) } }), ensuring the callback used
in the test observer (the onError argument) explicitly handles and fails the
test on any non-nil error.

In `@internal/event/fsnotify_test.go`:
- Around line 29-31: The test currently calls require.ErrorIs(t, err,
context.Canceled) from the async observer callback (func(_ context.Context, err
error) { ... }) which can fail from a non-test goroutine; instead, capture the
callback error into a synchronized location (e.g., make an errCh chan error or
an atomic.Value before registering the observer) and in the observer body do a
non-blocking send or atomic.Store of err; then in the main test goroutine, after
the Eventually/timeout has passed, read the error from errCh/atomic and assert
require.ErrorIs(t, observedErr, context.Canceled). Update the anonymous observer
function, add the err channel/atomic variable and the final assertion in the
main test flow.

In `@internal/event/signals_unix_test.go`:
- Line 20: The tests in internal/event/signals_unix_test.go call t.Parallel()
which causes flakes because they mutate process-global signal state; remove the
t.Parallel() invocation(s) (the call in the test functions in this file, e.g.,
the test that currently contains t.Parallel()) so the signal-related tests run
serially instead of in parallel and no longer interfere with each other or other
tests.

---

Nitpick comments:
In `@internal/event/diagnostics.go`:
- Line 36: The inline anonymous no-op observer func(context.Context, Envelope)
{} in diagnostics should not have an empty body; either add a brief inline
comment inside the function body (e.g. // intentionally no-op) or replace the
literal with a named helper like noOpObserver defined as func(context.Context,
Envelope) { /* no-op */ } and use that identifier where the observer callback is
registered; this addresses the Sonar quality-gate by ensuring the no-op has an
explicit comment or named helper while leaving the observable signature
(context.Context, Envelope) unchanged.

In `@internal/event/signals.go`:
- Line 29: Add an inline comment to the no-op completion callback literal
func(context.Context) {} in internal/event/signals.go explaining it's
intentionally empty and ignored because cancellation/cleanup is handled via the
next/error path; locate the anonymous completion handler (the literal
func(context.Context) {}) and append a short comment like "intentionally no-op;
cancellation handled in next/error" so static analysis and reviewers understand
this is deliberate.
🪄 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: 71fc4563-64df-4866-85ba-80034e0134d0

📥 Commits

Reviewing files that changed from the base of the PR and between c39493d and bb9c587.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (18)
  • go.mod
  • internal/assistant/anthropic_internal_test.go
  • internal/assistant/client.go
  • internal/assistant/openai_responses.go
  • internal/assistant/runtime.go
  • internal/assistant/runtime_events.go
  • internal/assistant/runtime_events_test.go
  • internal/assistant/tool_loop.go
  • internal/di/event_service_export_test.go
  • internal/di/event_service_test.go
  • internal/event/context.go
  • internal/event/context_test.go
  • internal/event/diagnostics.go
  • internal/event/diagnostics_test.go
  • internal/event/fsnotify.go
  • internal/event/fsnotify_test.go
  • internal/event/signals.go
  • internal/event/signals_unix_test.go

Comment thread internal/di/event_service_test.go
Comment thread internal/event/diagnostics_test.go Outdated
Comment thread internal/event/fsnotify_test.go
Comment thread internal/event/signals_unix_test.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/event/context_test.go (1)

14-66: ⚡ Quick win

Consolidate these core behavior cases into a table-driven test.

The scenarios here are closely related and repeat similar setup/assertion flow; converting to table-driven form will make future case additions cleaner and reduce drift across assertions.

As per coding guidelines, **/*_test.go: Prefer table-driven tests for core behavior and regression tests for terminal rendering bugs.

🤖 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/event/context_test.go` around lines 14 - 66, Consolidate the five
tests (TestAfterContextEmitsOnce,
TestAfterContextEmitsImmediatelyForNonPositiveDelay,
TestAfterContextStopsOnSourceCancel, TestAfterContextStopsOnSubscriberCancel,
TestContextDoneEmitsOnCancel) into a single table-driven test that iterates
cases with fields like name, source (a function returning event.Source),
subscriberCtx (nil or a cancellable context), expectValues, expectErr; for
AfterContext cases provide the delay parameter and for ContextDone use the
appropriate source generator, then in each row call either ro.Collect or
ro.CollectWithContext based on whether subscriberCtx is set and assert
expectErr/expectValues using require, ensuring each case cancels any created
contexts (source or subscriber) as needed and runs t.Parallel() per subtest.
🤖 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/event/context_test.go`:
- Around line 14-66: Consolidate the five tests (TestAfterContextEmitsOnce,
TestAfterContextEmitsImmediatelyForNonPositiveDelay,
TestAfterContextStopsOnSourceCancel, TestAfterContextStopsOnSubscriberCancel,
TestContextDoneEmitsOnCancel) into a single table-driven test that iterates
cases with fields like name, source (a function returning event.Source),
subscriberCtx (nil or a cancellable context), expectValues, expectErr; for
AfterContext cases provide the delay parameter and for ContextDone use the
appropriate source generator, then in each row call either ro.Collect or
ro.CollectWithContext based on whether subscriberCtx is set and assert
expectErr/expectValues using require, ensuring each case cancels any created
contexts (source or subscriber) as needed and runs t.Parallel() per subtest.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 78b5ae43-3d0b-4847-9385-781da90db9bc

📥 Commits

Reviewing files that changed from the base of the PR and between bb9c587 and aa3a6e7.

📒 Files selected for processing (12)
  • internal/assistant/client_test.go
  • internal/assistant/errors.go
  • internal/assistant/openai_responses.go
  • internal/assistant/runtime.go
  • internal/assistant/tool_loop_test.go
  • internal/di/event_service_test.go
  • internal/event/context_test.go
  • internal/event/diagnostics.go
  • internal/event/diagnostics_test.go
  • internal/event/fsnotify_test.go
  • internal/event/signals.go
  • internal/event/signals_unix_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/assistant/errors.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • internal/di/event_service_test.go
  • internal/event/signals_unix_test.go
  • internal/event/fsnotify_test.go
  • internal/event/diagnostics_test.go
  • internal/assistant/runtime.go
  • internal/event/diagnostics.go

@sonarqubecloud
Copy link
Copy Markdown

@omarluq omarluq merged commit d9eacf6 into main May 20, 2026
13 checks passed
@omarluq omarluq deleted the fix/ro-runtime-recovery branch May 23, 2026 04:40
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