Skip to content

Ev3 new support - #4025

Open
proto-aiken-13 wants to merge 14 commits into
source-academy:masterfrom
proto-aiken-13:ev3NewSupport
Open

Ev3 new support#4025
proto-aiken-13 wants to merge 14 commits into
source-academy:masterfrom
proto-aiken-13:ev3NewSupport

Conversation

@proto-aiken-13

Copy link
Copy Markdown

Description

Created a new feature directory remoteExecutionConductor, which serves the same purpose as the remoteExecution feature in a manner that supports the conductor framework

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • [X ] New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update
  • Code quality improvements

How to test

Checklist

  • I have tested this code
  • I have updated the documentation

Introduces a new remoteExecutionConductor feature that replaces the
js-slang compilation step in the existing EV3 remote execution flow
with a conductor-based pipeline using py-slang's EV3Engine.

New files:
- src/features/remoteExecutionConductor/flagConductorEv3Enable.ts:
  feature flag and selector to gate the new pipeline
- src/features/remoteExecutionConductor/RemoteExecutionConductorActions.ts:
  conductor-specific redux actions for connect, disconnect, and run
- src/features/remoteExecutionConductor/RemoteExecutionConductorSaga.ts:
  saga handling the conductor EV3 run flow, reusing existing SlingClient
  connection infrastructure from RemoteExecutionSaga tailored for the Conductor framework
- src/features/remoteExecutionConductor/createEv3Conductor.ts:
  creates a conductor Conduit with a Web Worker loading ev3-pyslang.js,
  wires receiveResult to forward compiled SVML to the EV3 via
  SlingClient.sendRun()

Modified files:
- src/commons/utils/ActionsHelper.ts: registered
  RemoteExecutionConductorActions
- src/commons/sagas/MainSaga.ts: forked RemoteExecutionConductorSaga
  alongside the legacy RemoteExecutionSaga
- src/commons/sagas/WorkspaceSaga/helpers/evalEditorSaga.ts: added
  flag check to dispatch remoteExecConductorRun vs remoteExecRun
  based on conductor.ev3.enable feature flag
Added remoteExecutionConductor to support the new Ev3 remote runner under the conductor framework
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ad557117-8dc9-461e-98c7-1d123eeefb2c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a conductor-based EV3 remote execution pipeline with a browser worker evaluator, typed messaging, device-event bridging, Redux actions and sagas, feature-flag routing, and a VS Code references setting.

Changes

EV3 conductor execution

Layer / File(s) Summary
Execution contracts and plugin protocol
src/features/remoteExecutionConductor/RemoteExecutionTypes.ts, src/features/remoteExecutionConductor/RemoteExecutionConductorActions.ts, src/features/remoteExecutionConductor/Ev3WebPlugin.ts
Defines execution result/session types, conductor actions, and the typed plugin message protocol.
Worker parsing, compilation, and execution
public/evaluators/ev3-remote-runner.js
Adds the Python-like parser, semantic validation, numeric runtime, SVML compiler, bytecode executor, and worker protocol.
Worker and device bridge
src/features/remoteExecutionConductor/createEv3Conductor.ts
Connects the worker and plugin to SlingClient events, run results, interpreter errors, display messages, and peripheral session updates.
Saga orchestration and feature-gated execution
src/features/remoteExecutionConductor/RemoteExecutionConductorSaga.ts, src/commons/sagas/MainSaga.ts, src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts, src/commons/utils/ActionsHelper.ts, src/features/remoteExecutionConductor/flagConductorEv3Enable.ts
Registers conductor sagas, manages run/disconnect handling, exposes actions, starts the saga, and selects conductor execution when enabled.

Editor configuration

Layer / File(s) Summary
Reference location setting
.vscode/settings.json
Sets references.preferredLocation to "view".

Estimated code review effort: 4 (Complex) | ~60 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the change, but it's too generic to convey the main EV3 Conductor support update. Rename it to something specific like 'Add EV3 remote execution support for Conductor' so the primary change is clear.
Description check ❓ Inconclusive The description matches the template, but it lacks issue references, motivation, dependencies, and any testing details. Add the fixed issue, clearer motivation/context, required dependencies, and concrete test steps or reproduction instructions.
✅ Passed checks (2 passed)
Check name Status Explanation
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.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces a Conductor-based EV3 execution pipeline using py-slang, adding the necessary actions, sagas, types, and a web plugin, integrated via a new feature flag. Key feedback points out a critical compilation error caused by an accidental import path replacement in evalEditor.ts, a potential runtime ReferenceError from using Node's Buffer in a browser environment, and a potential stale reference issue when reusing the active conductor across different client sessions.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts Outdated
const plugin = conduit.registerPlugin(Ev3WebPlugin);

plugin.onResult = (svml: string) => {
client.sendRun(Buffer.from(svml));

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.

high

Using Buffer.from in a browser environment can lead to a runtime ReferenceError: Buffer is not defined because Buffer is a Node.js global and is not polyfilled by default in modern bundlers. Consider using the standard Web API TextEncoder instead.

Suggested change
client.sendRun(Buffer.from(svml));
client.sendRun(new TextEncoder().encode(svml));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Necessary due to the need to decode the svml, and that sendRun needs a Buffer argument

Comment on lines +27 to +29
if (!activeConductor) {
activeConductor = yield call(createEv3Conductor, session.connection.client);
}

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.

medium

Reusing activeConductor across different runs without checking if the session.connection.client has changed can lead to stale references. If the connection is re-established or a different device is connected, the active conductor will still use the old client instance passed during its creation. Consider recreating the conductor if the client instance changes.

@proto-aiken-13
proto-aiken-13 marked this pull request as ready for review July 21, 2026 01:57

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/features/remoteExecutionConductor/createEv3Conductor.ts (1)

33-36: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Wrap client.sendRun against synchronous failures.

sendRun is an external device write invoked from an async plugin callback; if it throws, the error is unhandled in this callback. Consider a try/catch that surfaces the failure via actions.evalInterpreterError (consistent with the onError path) so a send failure doesn't silently drop.

🤖 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 `@src/features/remoteExecutionConductor/createEv3Conductor.ts` around lines 33
- 36, Update the plugin.onResult callback to wrap client.sendRun in try/catch,
and route any synchronous send failure through actions.evalInterpreterError
consistently with the existing onError path. Preserve the base64 conversion and
normal sendRun behavior when no exception occurs.
src/features/remoteExecutionConductor/Ev3WebPlugin.ts (1)

3-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider more descriptive channel/plugin identifiers.

CHANNEL_ID = 'test' and WEB_ID = '__web_test' look like placeholders for what is a production EV3 execution channel. They must stay in sync with the worker (Du.channelAttach = ["test"]), so renaming both sides to something like 'ev3' / '__web_ev3' would make the protocol contract clearer without behavior change.

🤖 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 `@src/features/remoteExecutionConductor/Ev3WebPlugin.ts` around lines 3 - 15,
Replace the placeholder identifiers CHANNEL_ID and WEB_ID in Ev3WebPlugin with
descriptive EV3-specific values, such as an “ev3” channel and matching
“__web_ev3” plugin ID. Update the corresponding worker Du.channelAttach value to
the same channel identifier, preserving the existing protocol behavior and
synchronization.
🤖 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 `@src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts`:
- Around line 58-59: Update evalEditor’s feature-flag selection to use the
EV3-specific flagConductorEv3Enable selector instead of flagConductorEnable, and
replace the corresponding import with the selector from the new flag module.
Keep the isConductorEv3 gating logic unchanged.

In `@src/features/remoteExecutionConductor/createEv3Conductor.ts`:
- Around line 46-49: Update the monitor handler in createEv3Conductor to return
when store.getState().session.remoteExecutionSession is absent instead of
asserting non-null, matching the onError and display handlers. Also validate
message[0] and the parsed port before calling substring or updating the session,
ignoring malformed monitor messages safely.

In `@src/features/remoteExecutionConductor/RemoteExecutionConductorSaga.ts`:
- Around line 28-30: Track the client associated with the active conductor and
update handleConductorRun to recreate the conductor when
session.connection.client changes, terminating the existing conduit before
replacement. Set the tracked client after creation, and clear both
activeConductor and the tracked client in handleConductorDisconnect.

---

Nitpick comments:
In `@src/features/remoteExecutionConductor/createEv3Conductor.ts`:
- Around line 33-36: Update the plugin.onResult callback to wrap client.sendRun
in try/catch, and route any synchronous send failure through
actions.evalInterpreterError consistently with the existing onError path.
Preserve the base64 conversion and normal sendRun behavior when no exception
occurs.

In `@src/features/remoteExecutionConductor/Ev3WebPlugin.ts`:
- Around line 3-15: Replace the placeholder identifiers CHANNEL_ID and WEB_ID in
Ev3WebPlugin with descriptive EV3-specific values, such as an “ev3” channel and
matching “__web_ev3” plugin ID. Update the corresponding worker Du.channelAttach
value to the same channel identifier, preserving the existing protocol behavior
and synchronization.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f3f9174b-d90c-4055-8c61-e3b22de5bece

📥 Commits

Reviewing files that changed from the base of the PR and between b58b653 and 7547ca9.

📒 Files selected for processing (11)
  • .vscode/settings.json
  • public/evaluators/ev3-remote-runner.js
  • src/commons/sagas/MainSaga.ts
  • src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts
  • src/commons/utils/ActionsHelper.ts
  • src/features/remoteExecutionConductor/Ev3WebPlugin.ts
  • src/features/remoteExecutionConductor/RemoteExecutionConductorActions.ts
  • src/features/remoteExecutionConductor/RemoteExecutionConductorSaga.ts
  • src/features/remoteExecutionConductor/RemoteExecutionTypes.ts
  • src/features/remoteExecutionConductor/createEv3Conductor.ts
  • src/features/remoteExecutionConductor/flagConductorEv3Enable.ts

Comment on lines +58 to +59
const isConductorEv3: boolean = yield select(featureSelector(flagConductorEnable));
if (isConductorEv3) {

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the EV3-specific feature flag selector.

This code incorrectly imports and selects the generic flagConductorEnable flag. It should use the newly introduced flagConductorEv3Enable flag so that the EV3 execution path is gated correctly and independently of other conductor features.

You can directly use the exported selector from the new flag module to simplify the select call.

💡 Proposed fix
-    const isConductorEv3: boolean = yield select(featureSelector(flagConductorEnable));
+    const isConductorEv3: boolean = yield select(selectConductorEv3Enable);

Remember to also update the imports at the top of the file:

-import { flagConductorEnable } from 'src/features/conductor/flagConductorEnable';
+import { selectConductorEv3Enable } from 'src/features/remoteExecutionConductor/flagConductorEv3Enable';
🤖 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 `@src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts` around lines 58 - 59,
Update evalEditor’s feature-flag selection to use the EV3-specific
flagConductorEv3Enable selector instead of flagConductorEnable, and replace the
corresponding import with the selector from the new flag module. Keep the
isConductorEv3 gating logic unchanged.

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.

I think I agree on this one, if we are just depending on conductor to be enabled, then there is not much point adding a new flag flagConductorEv3Enable instead.

Comment on lines +46 to +49
client.on('monitor', message => {
const port = message[0].split(':')[1];
const key = `port${port.substring(port.length - 1)}` as keyof Ev3DevicePeripherals;
const currentSession = store.getState().session.remoteExecutionSession!;

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard remoteExecutionSession in the monitor handler (inconsistent with the other handlers).

The onError (Line 40) and display (Line 85) handlers both guard if (!currentSession) return, but the monitor handler asserts non-null via !. A monitor event delivered after the session is cleared (e.g. after disconnect) will spread undefined into remoteExecUpdateSession, throwing at runtime. Line 47–48 (message[0].split(':')[1]port.substring(...)) is also unguarded against a missing/misformatted message[0].

🛡️ Proposed guard
   client.on('monitor', message => {
+    const currentSession = store.getState().session.remoteExecutionSession;
+    if (!currentSession) return;
     const port = message[0].split(':')[1];
     const key = `port${port.substring(port.length - 1)}` as keyof Ev3DevicePeripherals;
-    const currentSession = store.getState().session.remoteExecutionSession!;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
client.on('monitor', message => {
const port = message[0].split(':')[1];
const key = `port${port.substring(port.length - 1)}` as keyof Ev3DevicePeripherals;
const currentSession = store.getState().session.remoteExecutionSession!;
client.on('monitor', message => {
const currentSession = store.getState().session.remoteExecutionSession;
if (!currentSession) return;
const port = message[0].split(':')[1];
const key = `port${port.substring(port.length - 1)}` as keyof Ev3DevicePeripherals;
🤖 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 `@src/features/remoteExecutionConductor/createEv3Conductor.ts` around lines 46
- 49, Update the monitor handler in createEv3Conductor to return when
store.getState().session.remoteExecutionSession is absent instead of asserting
non-null, matching the onError and display handlers. Also validate message[0]
and the parsed port before calling substring or updating the session, ignoring
malformed monitor messages safely.

Comment on lines +28 to +30
if (!activeConductor) {
activeConductor = yield call(createEv3Conductor, session.connection.client);
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Recreate the conductor if the connection client changes.

If a user connects to a different EV3 device, session.connection.client will change, but activeConductor will not be recreated because it is only cleared on an explicit disconnect action. As a result, the conductor will continue interacting with the stale client, and its event listeners will remain tied to the old connection.

Track the active client and recreate the conductor when the connection changes.

💡 Proposed fix

Declare a module-level variable to track the client at the top of the file:

let activeClient: any = null;

Update the initialization logic in handleConductorRun:

  if (!activeConductor || activeClient !== session.connection.client) {
    if (activeConductor) {
      activeConductor.conduit.terminate?.();
    }
    activeConductor = yield call(createEv3Conductor, session.connection.client);
    activeClient = session.connection.client;
  }

Also, remember to clear activeClient in handleConductorDisconnect:

function* handleConductorDisconnect(): any {
  activeConductor?.conduit.terminate?.();
  activeConductor = null;
  activeClient = null;
  yield; // satisfies require-yield
}
🤖 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 `@src/features/remoteExecutionConductor/RemoteExecutionConductorSaga.ts` around
lines 28 - 30, Track the client associated with the active conductor and update
handleConductorRun to recreate the conductor when session.connection.client
changes, terminating the existing conduit before replacement. Set the tracked
client after creation, and clear both activeConductor and the tracked client in
handleConductorDisconnect.

@proto-aiken-13
proto-aiken-13 marked this pull request as draft July 21, 2026 10:00
@proto-aiken-13
proto-aiken-13 marked this pull request as ready for review July 21, 2026 10:00
Comment thread src/commons/sagas/MainSaga.ts Outdated
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.

3 participants