The port is only recognisable if a Monty developer can find any file they know. One rule does that.

The rule

Monty.<CamelCase dirs>.<CamelCase file>.<Class>

with two collapses:

PythonElixir
cmp.py::MessageMonty.Cmp.Message
geometry.py::SurfacePoseVectorsMonty.Geometry.SurfacePoseVectors
frameworks/models/buffer.py::FeatureAtLocationBufferMonty.Frameworks.Models.Buffer.FeatureAtLocationBuffer
frameworks/models/abstract_monty_classes.py::MontyMonty.Frameworks.Models.AbstractMontyClasses.Monty
frameworks/models/evidence_matching/learning_module.py::EvidenceGraphLMMonty.Frameworks.Models.EvidenceMatching.LearningModule.EvidenceGraphLM
frameworks/models/motor_policies.py::SurfacePolicyMonty.Frameworks.Models.MotorPolicies.SurfacePolicy
frameworks/experiments/monty_experiment.py::MontyExperimentMonty.Frameworks.Experiments.MontyExperiment
frameworks/actions/actions.py::MoveForwardMonty.Frameworks.Actions.MoveForward
sensor_modules/sensor_module.py::SensorModuleMonty.SensorModules.SensorModule
simulators/mujoco/simulator.py::MuJoCoSimulatorMonty.Simulators.MuJoCo.Simulator.MuJoCoSimulator

The actor layer is the one addition: Monty.Runtime.* has no Python counterpart, because in Python the loop itself is the runtime.

The second package: tbp.teleop

Teleop is the real-time GUI and teleoperation tool for Monty (matplotlib panels, buttons, sliders, a live plotter). The same recognisability rule applies with its own namespace:

PythonElixir
src/tbp/teleop/controls.py::ControlsTeleop.Controls.Controls
src/tbp/teleop/panels.py::PanelTeleop.Panels.Panel
src/tbp/teleop/plotter.py::PlotterTeleop.Plotter.Plotter
src/tbp/teleop/live_plotter.py::LivePlotterTeleop.LivePlotter.LivePlotter
src/tbp/teleop/policies.py::...Teleop.Policies....
src/tbp/teleop/helpers.py::...Teleop.Helpers....
src/tbp/teleop/default.py::...Teleop.Default....

tbp.teleop is MIT-licensed like Monty; the point of the namespace is that a teleop developer can find their module here without a translation table.

How each part is meant to land in a browser is a separate decision, made in architecture.md: the matplotlib figure becomes LiveView components, a StepHook stays a hook, and the mouse/keyboard controls become LiveView events. The module map records where the logic lives, not how it is drawn.

The map

lib/monty/module_map/data.ex is generated from the checkouts:

mix monty.module_map.gen             # writes the module (both packages)
mix monty.module_map.verify          # fails on drift

It is written as Elixir rather than as a file read at run time. What the map says decides which modules this application claims to implement, and every port question starts there, so it belongs beside the code it describes — not at the end of a path handed to a reader.

Each entry carries the Python path, the Elixir module, a status (implemented, partial, planned, excluded) and, for exclusions and partial ports, a reason. The verifier checks three things, for both packages:

  • every Python file in a checkout has an entry;
  • every entry marked implemented or partial points at a module that exists;
  • every entry marked excluded or partial says why.

With no checkout it checks the last two only, and says which packages it could not check from source rather than pretending it did: the map names the commit it was generated from, and nothing a build or a test does needs the checkout. A checkout that has moved since is reported too — the map still describes the commits it names, which is a fact to read, not a failure.

The current counts are what mix monty.module_map.verify prints. They live there and in data.ex, never here: a number copied into prose is wrong by the next commit.

The generated file's counts are the honest progress bar for the port: the UI will show per-experiment how much of the config it needs is already implemented.

Claiming a module

  1. Pick an entry from lib/monty/module_map/data.ex.
  2. Create the module at its mapped name.
  3. Port the behaviour as pure functions, keeping Monty's vocabulary.
  4. Test it — against a Monty fixture when numbers are involved.
  5. mix monty.module_map.gen && mix monty.module_map.verify.
  6. scripts/quality.sh.

Marking something excluded is allowed; leaving it unexplained is not. A verdict of excluded is a claim about somebody else's code, so planned-classes.md keeps the evidence for each one and says which classes are still owed.

A module that is here but only as far as anything here uses it is partial, and it makes a claim about our code that has to be checked the same way: partial-classes.md argues each one from the module's own admission of what it leaves out, and says how the list was swept for.