# Module map

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:

- an immediately repeated segment collapses (`frameworks/actions/actions.py`
  → `Monty.Frameworks.Actions`, not `...Actions.Actions`);
- the class is dropped when it repeats the file name
  (`cmp.py::Message` → `Monty.Cmp.Message`, `hypotheses.py::Hypotheses`
  → `Monty.Frameworks.Models.EvidenceMatching.Hypotheses`).

| Python                                                                    | Elixir                                                                    |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `cmp.py::Message`                                                         | `Monty.Cmp.Message`                                                       |
| `geometry.py::SurfacePoseVectors`                                         | `Monty.Geometry.SurfacePoseVectors`                                       |
| `frameworks/models/buffer.py::FeatureAtLocationBuffer`                    | `Monty.Frameworks.Models.Buffer.FeatureAtLocationBuffer`                  |
| `frameworks/models/abstract_monty_classes.py::Monty`                      | `Monty.Frameworks.Models.AbstractMontyClasses.Monty`                      |
| `frameworks/models/evidence_matching/learning_module.py::EvidenceGraphLM` | `Monty.Frameworks.Models.EvidenceMatching.LearningModule.EvidenceGraphLM` |
| `frameworks/models/motor_policies.py::SurfacePolicy`                      | `Monty.Frameworks.Models.MotorPolicies.SurfacePolicy`                     |
| `frameworks/experiments/monty_experiment.py::MontyExperiment`             | `Monty.Frameworks.Experiments.MontyExperiment`                            |
| `frameworks/actions/actions.py::MoveForward`                              | `Monty.Frameworks.Actions.MoveForward`                                    |
| `sensor_modules/sensor_module.py::SensorModule`                           | `Monty.SensorModules.SensorModule`                                        |
| `simulators/mujoco/simulator.py::MuJoCoSimulator`                         | `Monty.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:

| Python                                        | Elixir                           |
| --------------------------------------------- | -------------------------------- |
| `src/tbp/teleop/controls.py::Controls`        | `Teleop.Controls.Controls`       |
| `src/tbp/teleop/panels.py::Panel`             | `Teleop.Panels.Panel`            |
| `src/tbp/teleop/plotter.py::Plotter`          | `Teleop.Plotter.Plotter`         |
| `src/tbp/teleop/live_plotter.py::LivePlotter` | `Teleop.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`](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`](../lib/monty/module_map/data.ex) is generated
from the checkouts:

```bash
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`](../lib/monty/module_map/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`](../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`](../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`](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`](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.
