enola-rb

Four pure-Ruby gems, one repository, for running enola, the architecture-graph tool, from Ruby. None of them carries a binary or compiles anything.

Two channels. enola wraps the released upstream binary and enola-rb adds the Rails layer on it, split so a Rubyist can take the wrapper without Rails. munola is one gem for its own channel, wrapper and Rails layer together, because nobody installs it to avoid Rails. The Rails code is written once, in enola-rb, and munola adds only what is munola's own.

enola

The wrapper of the released enola. The gem versions itself; the release it drives is Enola::UPSTREAM_VERSION, v0.4.4 as of enola 0.5.0, and every gemspec summary names it.

gem "enola"
bundle exec enola --generate .
bundle exec enola baseline pin .
bundle exec enola check .

The first command that needs the binary downloads the release for your platform (linux, darwin, windows; amd64, arm64), verifies it against the sha256 file the same release publishes, and keeps it under ~/.cache/enola/upstream/0.4.4/ (ENOLA_CACHE_DIR moves the root). An enola already on your PATH is used only when it answers the pinned version; offline with an empty cache is a refusal that names the cache and the release rather than a fallback to whatever is installed. Nothing downloads at bundle install.

Every argument and exit code is forwarded unchanged; the wrapper adds no flag of its own to enola's surface. --verbose on any command writes one line to stderr first, naming the channel, the version and where the binary came from. enola --wrapper-probe prints the channel, the pin, the version the binary answers and which surfaces it has (constraints, providers, check, hook), read by running them, never by comparing version strings.

This gem is not an enola-labs release; it runs theirs.

enola-rb

The Rails layer over enola. It depends on enola at the same minor and on enola-guides, and adds nothing to the binary's surface.

gem "enola-rb"
bin/rails generate enola:install
bin/rake enola:snapshot
bin/rake enola:check

The generator writes enola/constraints/ from the guides' starter laws (four laws a Rails team keeps, each with its reason), asks the binary's own constraints init to bind the shipped recipes whose roles resolve in the app, writes every other shipped recipe as a commented binding to uncomment once the directories exist, and ignores .enola/. It reads what init wrote rather than its exit code, and when the binary cannot be fetched it still writes the laws and says so. enola:snapshot generates and pins the baseline; enola:check grades the working tree against it and fails on a new breach of a declared law. A surface the pinned binary lacks is refused by name with the remedy.

munola

One person's taste on top of enola: the same wrapper over another channel, the builds cut from a fork of enola, each release naming what differs from that upstream. The gem versions itself; the channel release it fetches is Munola::CHANNEL_VERSION, which carries the upstream it is built on plus a segment of its own (0.4.4.2 on v0.4.4). It is offered upstream where it fits and is not positioned against it. What it adds is the channel and the catalogue.

gem "munola"
bundle exec munola init . --tenant-column company_id
bundle exec munola --version

munola init does what enola:install does, then what only munola carries: it writes the recipe catalogue the enola-guides gem ships (Ember conventions, data ownership, API boundaries, background work, a tenant foreign key) into enola/recipes/, binds the recipes the tree justifies (ember-cli-build.js, a schema with app/models, config/routes.rb with app/policies, app/tasks, a column most tables share confirmed against db/schema.rb) and writes every other one as a commented binding; it fills the tenant template from the schema's own table names, never from an inflection table; it writes mcp-arch.yaml with both Ruby providers on by default, Prism through the script the enola gem carries and Rubydex built into the binary, and fetches the Rubydex library, a failed fetch reported as a named skip. It never asks a question. munola --version names the munola version, the channel release and upstream it is built on, and which binary answered.

The binary comes from the fork's releases the way enola's comes from upstream's, fetched on first use and verified against the sha256 the release publishes; MUNOLA_BINARY=/path/to/enola names one to drive instead, and every command says which answered. munola depends on enola at the same minor and on enola-guides 0.3.1 or later; requiring it loads no Rails.

munola under Rails

munola carries its own generator, and the rake tasks are enola-rb's:

bin/rails generate munola:install --tenant-column company_id
bin/rake enola:snapshot
bin/rake enola:check

The generator runs the same install munola init does. munola installs its own resolver when it loads, so enola:snapshot and enola:check drive the munola binary in an app that has this gem, and the upstream binary in one that does not. Requiring munola loads no Rails; the generator registers when the application has already loaded it.