Skip to content

robot.move() in the fleet view drove every unassigned board at once#1

Merged
jonasneves merged 1 commit into
mainfrom
ide-target-by-board-2026-07-17
Jul 17, 2026
Merged

robot.move() in the fleet view drove every unassigned board at once#1
jonasneves merged 1 commit into
mainfrom
ide-target-by-board-2026-07-17

Conversation

@jonasneves

Copy link
Copy Markdown
Member

The last of the day's fixes, and the one that isn't mechanical — chosen deliberately over the alternatives (firmware-refuses-untargeted, or document-and-leave).

The bug

Each robot the IDE exposed was keyed by topic id. Every fresh board answers to unassigned and publishes to robots/unassigned/*, so the whole pool collapsed into one entry — and robot.move() published to robots/unassigned/pwm with no target, which every unnamed board on the desk obeys. Day one of a class, IDE open, is exactly when boards are unnamed.

Why it needed more than a one-liner

A board has two ids:

  • the topic id (its name, or unassigned) that robots/<id>/<channel> is built from;
  • the stable board id in sys.board (rover-b79c) that the firmware matches target against — rover_role.c compares target to s_id, the board field, not the topic.

The topic groups boards; target disambiguates within a group. So "just send target" isn't enough — the IDE couldn't produce a per-board target because its data model had already merged the pool.

The fix

  • Fleet view (instructor/anonymous, robots/+/#): the handle is now the board id, sourced from the sys beacon, and every command carries target — the topic reaches the pool, target picks the one.
  • Team login: untouched. It's scoped to one board on its own topic, so the topic id is already a unique handle and no target is needed. This path is byte-for-byte the old behaviour.
  • Non-sys channels (imu) on a shared pool topic carry no board id and are genuinely ambiguous — attributed only when one board owns the topic, dropped otherwise, rather than smeared across the pool.

Testing

Headless (mocked mqtt), all passing: team publishes to its topic with no target; two unassigned boards get distinct board-id handles and move() targets exactly one on the shared topic; imu attributes to a sole-owner topic and drops on the shared one.

Not exercised through Blockly → MicroPython → real boards. blocks.js binds no ids and renderTelemetry only displays the id, so the id string is opaque end to end — but the multi-board flow should be run at the bench before this ships to a class. Holding this PR for that, unlike the day's others.

🤖 Generated with Claude Code

Each robot the IDE exposed was keyed by TOPIC id. Every fresh board answers to
`unassigned` and publishes to robots/unassigned/*, so the whole pool collapsed
into one entry — and a robot.move() published to robots/unassigned/pwm with no
target, which every unnamed board on the desk obeys. Day one of a class, IDE
open, is exactly when boards are unnamed.

A board has two ids: the topic id (its name, or `unassigned`) that the topic is
built from, and the stable MAC-derived board id in sys.board (rover-b79c) that
the firmware matches `target` against (rover_role.c compares target to s_id, the
board field, not the topic). The topic GROUPS boards; target DISAMBIGUATES
within a group.

So in the fleet view (instructor/anonymous, robots/+/#) the handle is now the
board id, sourced from the sys beacon, and every command carries target — the
topic reaches the pool, target picks the one. A team login is untouched: it is
scoped to one board on its own topic, so the topic id is already a unique handle
and no target is needed. Non-sys channels (imu) on a shared pool topic carry no
board id and are genuinely ambiguous — attributed only when one board owns the
topic, dropped otherwise, rather than smeared across the pool.

Headless-tested (mocked mqtt): team publishes to its topic with no target;
two unassigned boards get distinct board-id handles and move() targets exactly
one; imu attributes to a sole-owner topic and is dropped on the shared one.

NOT tested through Blockly + MicroPython + real boards — that wants the bench.
The student-facing id string is now the board id in fleet mode; blocks.js binds
no ids and renderTelemetry only displays it, so the string is opaque end to end,
but the multi-board flow should be exercised before this ships to a class.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Copilot AI review requested due to automatic review settings July 17, 2026 15:14

Copilot AI 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.

Pull request overview

Fixes a fleet-view safety issue where multiple unnamed boards sharing the robots/unassigned/* topic could be merged into one logical robot, causing robot.move() to publish an untargeted command that every unassigned board would obey. The update refactors the client-side identity model so fleet mode keys robots by stable sys.board and stamps target on commands when operating on a shared topic.

Changes:

  • Add explicit “topic id vs board id” model and switch fleet view addressing to use sys.board as the handle.
  • In fleet mode, publish commands to the topic while adding target to disambiguate the intended board.
  • Avoid smearing ambiguous non-sys telemetry across multiple boards on the same shared topic by dropping it unless a sole owner can be determined.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread robot-api.js
}, timeoutMs);
pendingLed.set(id, { resolve, timer });
publish(id, "led", { method: "set_led", on, red, green, blue });
pendingLed.set(replyKey, { resolve, timer });
Comment thread robot-api.js
Comment on lines +86 to +88
const here = [...topicOf].filter(([, t]) => t === topicId).map(([h]) => h);
if (here.length !== 1) return;
handle = here[0];
@jonasneves
jonasneves merged commit 7137b59 into main Jul 17, 2026
1 check passed
@jonasneves
jonasneves deleted the ide-target-by-board-2026-07-17 branch July 17, 2026 15:18
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