fix(maimu): register as Maimu, the name saved experiments already use - #138
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found from a startup log: installing
glider-maimuand running GLIDER logged two successful plugin loads and produced a device type calledmaimuand a node type calledmaimu_node. Neither is a name anything refers to.Maimu was a built-in before #137 moved it, so every
.gliderfile written to date names bothMaimu— and none of them would have loaded.Why
PluginManagerregisters 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'sDEVICE_TYPES/NODE_TYPEStables — so the tables that sayMaimunever ran at all.glider-harpgets this right by accident of shape: its module-styleglider_harp = "glider_harp"entry point is what registers itsHarpdevice. This plugin now declares one of those and nothing else.Two things made a wrong name look like a working install
The log said nothing useful.
Successfully loaded plugin: maimuwas true and unhelpful._register_componentnow 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_maimuhad 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 underMaimu; no stray lowercase aliases (a second Add Device entry invites saving a file against a name the next version won't have); both attributed toglider_maimu; andcreate_device_from_dict— the path loading a.gliderfile goes through — still builds the device fromdevice_type: "Maimu".Verification
Note for the pending hardware run: the catalogue is a separate problem.
glider-maimuis not insrc/glider/plugins/index.jsonand 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.