Skip to content

fix(maimu): register as Maimu, the name saved experiments already use - #138

Merged
gbradham merged 3 commits into
mainfrom
fix/maimu-entry-point-names
Aug 21, 2026
Merged

fix(maimu): register as Maimu, the name saved experiments already use#138
gbradham merged 3 commits into
mainfrom
fix/maimu-entry-point-names

Conversation

@gbradham

Copy link
Copy Markdown
Member

Found from a startup log: installing glider-maimu and running GLIDER logged two successful plugin loads and produced a device type called maimu and a node type called maimu_node. Neither is a name anything refers to.

Maimu was a built-in before #137 moved it, so every .glider file written to date names both Maimu — and none of them would have loaded.

Why

PluginManager registers an entry point that names a class under the entry point's own name, not under any name the class picks. And the class branch returns before reaching the module's DEVICE_TYPES / NODE_TYPES tables — so the tables that say Maimu never ran at all.

glider-harp gets this right by accident of shape: its module-style glider_harp = "glider_harp" entry point is what registers its Harp device. This plugin now declares one of those and nothing else.

before:  device types: ['maimu']        node types: ['maimu_node']
after:   device types: ['Maimu']        node types: ['Maimu']

Two things made a wrong name look like a working install

The log said nothing useful. Successfully loaded plugin: maimu was true and unhelpful. _register_component now logs what it registered under — Plugin maimu registered device 'maimu' is the entire diagnosis, and it is the one line that was missing from the log that prompted this.

My first regression test skipped instead of failing. Its guard checked that a plugin named glider_maimu had been discovered — which is the very thing that was wrong, so on the bug it skipped silently and went green. It now checks that the distribution is installed. Verified by reverting the entry points: 5 failures, not 5 skips.

Also fixes a latent CI break from #135

Three tests there asserted the plugin registry was empty (plugin_components("node") == {}). True on a stock checkout, false on any machine with a plugin installed — including CI, which installs both in-repo plugins, a step #137 added. They now assert about their own fixture's node rather than about global emptiness.

Nothing had noticed because the merge commits that would have run them were skipped as redundant, so #135's tests never ran in an environment with #137's install step.

Tests — 5 new

plugins/glider-maimu/tests/test_packaging.py: the device and the node each register under Maimu; no stray lowercase aliases (a second Add Device entry invites saving a file against a name the next version won't have); both attributed to glider_maimu; and create_device_from_dict — the path loading a .glider file goes through — still builds the device from device_type: "Maimu".

Verification

  • Core: 3607 passed, 4 skipped (with both plugins installed, matching CI)
  • Plugins: 503 passed
  • ruff and black clean

Note for the pending hardware run: the catalogue is a separate problem. glider-maimu is not in src/glider/plugins/index.json and not on PyPI, and the remote index 404s — so Tools → Plugins… cannot install it, contrary to what the plugin README says. The editable install is the only path today. Worth fixing, but it is not this PR.

Installing the plugin and starting GLIDER logged two successful loads and
produced a device type called `maimu` and a node type called `maimu_node`.
Neither is the name anything refers to. Maimu was a built-in before it was a
plugin, so every .glider file written to date names both `Maimu`, and none of
them would have loaded.

PluginManager registers an entry point that names a class under the *entry
point's own name*, not under any name the class picks, and the class branch
returns before reaching the module's DEVICE_TYPES/NODE_TYPES tables -- so the
tables that say `Maimu` never ran. glider-harp gets this right by accident of
shape: its module-style `glider_harp = "glider_harp"` entry point is what
registers its `Harp` device. This plugin now declares one of those and nothing
else.

Two things made a wrong name look like a working install, and both are fixed
here rather than left for the next plugin author.

_register_component now logs what it registered under. "Successfully loaded
plugin: maimu" was true and useless; "Plugin maimu registered device 'maimu'"
is the whole diagnosis, and it was the one line missing from the log that
prompted this.

The new packaging tests guard the names, and their skip condition checks that
the *distribution* is installed rather than that a plugin of the expected name
was discovered. The first version keyed on the name and therefore skipped --
silently, and green -- in exactly the case it exists to catch. Verified by
reverting the entry points: five failures, not five skips.

Also fixes three tests from the node extension-point work that asserted the
plugin registry was empty. That was true on a stock checkout and false on any
machine with a plugin installed, including CI, which installs both in-repo
plugins. They now assert about their own fixture's node instead. Nothing had
noticed because the merge commits that would have run them were skipped as
redundant.
A closed-loop experiment has a lot of links -- pose inference, the behavior
model, the node watching for a behavior, the canvas wiring, the device binding,
the peripheral -- and any one of them can be wrong in a way that looks fine
until an animal is in the box. This plays a recording through the live path
instead of the camera, so the whole chain runs on footage where you already
know what the animal did. The hardware fires for real, which is the point: a
rehearsal that faked the output would not tell you the stimulator works.

It needed almost nothing new downstream. _handle_frame_input is a single funnel
that every frame already goes through, so a pump emitting _frame_received with
the camera's (frame, timestamp) shape gets classification, node triggers and
BLE writes unchanged. It emits that signal rather than calling _on_frame,
deliberately skipping the _preview_active guard: a rehearsal must not require
an open camera, since most machines running one have none.

Frames are never skipped. compute_features computes velocity and acceleration
over unit frame spacing, so a dropped frame doubles the apparent displacement
across the gap and inflates exactly the kinematics the model keys on -- a pump
that skipped to keep up would report confident, wrong behavior. It reports its
worst lag instead, which is also the number worth reading: a run that ends a
second behind will miss stimulus timing on a live animal by the same margin.

Both speeds classify identically, for the same reason: unit frame spacing means
the playback rate cannot change what the model sees. They answer different
questions. Real time answers whether inference keeps up and what the end-to-end
latency is; as-fast-as-possible answers whether any of it is wired up, sooner.
Offering only the first would have made the wiring check needlessly slow, and
offering only the second would have made the latency question unanswerable.

A consumer that raises ends the run rather than being swallowed, because
continuing would deliver a gap-riddled stream -- the one thing this must never
produce.
feat(vision): rehearse a closed loop from a recording
@gbradham
gbradham merged commit 1a9261b into main Aug 21, 2026
4 checks passed
@gbradham
gbradham deleted the fix/maimu-entry-point-names branch August 21, 2026 13:59
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