Okay, my plans for the Fragment API are conflicting with some of my recollections of plans for the Project API, but I'm having trouble tracking down my notes to confirm this. So, I'm going to need to try to re-derive some of this, and maybe have some new layouts in light of the Fragment API.
One of the key things going on with the Fragment API is that it makes it non-trivial to combine a Project and a Platform. This is because the choices associated with the Project get baked into the final Fragment, and the Platform does not feed into the contents of the Fragment. At least, not inherently... Because each command execution should be creating distinct artifacts, the Fragment API needs to be extended with a system of parameterization or templating in order to have some degree of polymorphism depending on the choice of Platform or other considerations.
My choices for templating systems include the built-in options like format() and string.Template, as well as Jinja, which must be installed, otherwise MOTR is very broken.
Another possibility is to forgo changing the data model significantly, and simply work with Callabables that use f-string interpolation instead. Going down that route requires me to remember which order I wanted to pass the objects in. I believe the idea was that a Session/Runner/Whatever would be first parameterized by all possible Platforms, and secondarily by all possible Packages/Projects/Whatevers.
Although... Perhaps the runner shouldn't know about the Packages, and should just get a collection of Callables that take information about the environment... But at that point, I think something's gone wrong with the abstraction. Fundamentally, an environment-type object is being constructed to run a single command, which must exist in a single location, so there is always a "main" binary directory, even if, I don't know, I contrive some way to run a command that requires some combination of Node, Python, and Rust packages.
Now, a Fragment contains the data required to construct the environment that can execute it, except for details about the exact Platforms used. We could suppose that the requirements are encoded as pairs of strings containing a platform alias and a requirement, and the interface for combining a group of Platforms with a Fragment would look something like
@attr.dataclass(frozen=True) class Platforms: platforms: PMap[str, Platform] main: str
Now, the contents of a Fragment could depend on any Platform in a Platforms, and once the full Fragment is completed, it's then ready to be passed to the main Platform to be converted into an executable call...
Something about this whole setup is striking me as not quite right, but it's in the interaction between "how this must be used" and "the data structures I want". To settle this, I need to get a firmer understanding of the usage, so I can change the data structures.
I'm not getting that tonight, so I'll wrap up for now.