enola 0.5.6 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9465910b66ac8c461af7a7169f074d55b8c64c7c228dfe4acb81bdf70980731
4
- data.tar.gz: f5fcd1f86a3cde157a0dc4ae901b56577f08cb1f8735aa065110bec03ad2254e
3
+ metadata.gz: 9982dbf8dda074c423e5b0bb421d366085d18a18fa8a7be9c28dfa554c171a55
4
+ data.tar.gz: 01c9c4c40e9e490007bc83a7745e42a181f068f474b787cac1a117f9c9eb1a76
5
5
  SHA512:
6
- metadata.gz: bf9acda456730768553a8489cde1f527989f0d8e35e425fbb74ff20eb265796a8c6d995ad5062dfcb92017017082610b950cc12b5f0d4d7ed919d8dc1f2aa629
7
- data.tar.gz: 814dd495e17e04801536643b736143644adac776454e27a076256ae07daccdb1f45cd38b88fe7a10ae784f739ecfb1bcc465cd7a30e05d68c022b2a9455d3dcb
6
+ metadata.gz: 62e80fd23f020a2b19b0c07362a94bd6929c5525c451ec0ecf6c8ad5d29662fd996853b7da52896f53db32401bc5a4688cacd4210d774bac3b4f30b328cd9b57
7
+ data.tar.gz: 30979ac59baf52c32711ee8bc28d25206ca63592c2f2a12b9e32aede1c5eb974584c2c64bbc03aaef5c0e8d55159de3e2463b1c74008752b53f4630788314741
data/CHANGELOG.md CHANGED
@@ -1,3 +1,67 @@
1
+ ## enola 0.5.7 and enola-rb 0.5.7 (2026-10-26)
2
+
3
+ The enola and enola-rb gems drive upstream enola 0.4.35. Between 0.4.32 and
4
+ here upstream locked the provider cache, which two Ruby providers running at
5
+ once could corrupt or crash the engine on, and the gem's default config runs
6
+ two (0.4.33); answered a `prism` entry with no `command` from the Rubydex
7
+ index, so call edges no longer need Ruby on the machine (0.4.33); moved the
8
+ Rubydex engine to 0.4.1 (0.4.33); removed false Ruby edges from methods
9
+ declared in several modules and from namespace-opening modules, and followed
10
+ calls through superclasses, included modules and Rails loading (0.4.33,
11
+ 0.4.34); keyed per-file provider cache entries by path as well as content
12
+ (0.4.34); added a packwerk explainer and impact rolled up by pack (0.4.33);
13
+ and added endpoint callers (0.4.35). `cacheVersion` moved from v390 to v465,
14
+ so caches rebuild once and a pinned baseline can show a one-time change.
15
+
16
+ The Rubydex pin the config writes moves from 0.4.0 to 0.4.1, the version
17
+ 0.4.35 fetches. The Rails installer now asks the binary it drives which
18
+ Rubydex version it fetches (`providers list`) and writes that, rather than the
19
+ gem's constant. `munola init` and `rails generate munola:install` wrote the
20
+ enola gem's 0.4.0 while the munola channel fetches 0.4.1, so Rubydex was a
21
+ silent skip there; munola picks the fix up through enola-rb with no release of
22
+ its own. enola-rb now requires enola `~> 0.5.7` for it.
23
+
24
+ The vendored Prism provider script is re-vendored from v0.4.35. Its body is
25
+ unchanged since v0.4.19 and it still answers provider version 0.2.0. The gem
26
+ keeps running the script rather than the built-in prism: upstream documents
27
+ the built-in as close to the script, not identical.
28
+
29
+ The Rails installer binds `layered` advisory. The binary's `constraints init`
30
+ binds it on a Rails app with application on `app/jobs`, so its
31
+ `presentation-reaches-application-only` rule read every controller that calls
32
+ a model as a breach: 147 of Campfire's 178, and as a ratchet it failed
33
+ `enola:check` on the next one, `@rooms = Room.all` in a new controller among
34
+ them. Advisory, its rules report below the gate and never fail; a model
35
+ reaching a controller still fails through the starter law
36
+ `models-do-not-reach-controllers`. Only a `recipes.yaml` that init wrote during
37
+ the install is touched, and the generator says what it did.
38
+
39
+ `rake enola:explain` prints upstream's plain-language repository summary
40
+ (`--explain --summary`, new in 0.4.33): what the app is, where to start
41
+ reading, what is risky to change. It needs no config or baseline, and on a
42
+ binary without `--summary`, such as munola's 0.4.32.1 channel, it prints the
43
+ full `--explain` report instead.
44
+
45
+ The README opens with what the gems catch, shown on 37signals' Campfire, then
46
+ a Rails quick start, a GitHub Actions workflow that grades each pull request
47
+ against its base and annotates the line, the agent setup and how enola sits
48
+ next to RuboCop, Brakeman and Packwerk. The enola and enola-rb gemspecs lead
49
+ with the same, and all three name the README and the issue tracker on
50
+ rubygems.org. All three require Ruby 3.2, the oldest CI runs.
51
+
52
+ ## munola 0.6.4 (2026-10-20)
53
+
54
+ munola drives channel release `0.4.32.1`, cut from the promoted current at
55
+ upstream enola 0.4.32 and published on misabegovic/enola as
56
+ `munola-v0.4.32.1` (five platforms with checksums and a `version.json`). Its
57
+ notes name 32 differences from upstream 0.4.32, among them the recipe
58
+ catalogue at enola-guides v0.3.1 and the init role binding, the member check,
59
+ the providers mode, the built-in prism provider and the larger MCP surface.
60
+
61
+ The Rubydex version the config writes moves from 0.4.0 to 0.4.1, the library
62
+ the 0.4.32.1 channel fetches. A config naming 0.4.0 would have the binary skip
63
+ the provider for a version mismatch, silently.
64
+
1
65
  ## enola 0.5.6 and enola-rb 0.5.6 (2026-10-20)
2
66
 
3
67
  The enola and enola-rb gems drive upstream enola 0.4.32. Between 0.4.19 and
data/README.md CHANGED
@@ -1,19 +1,164 @@
1
1
  # enola-rb
2
2
 
3
- Four pure-Ruby gems, one repository, for running
4
- [enola](https://github.com/enola-labs/enola), the architecture-graph tool,
5
- from Ruby. None of them carries a binary or compiles anything.
3
+ [![Gem Version](https://badge.fury.io/rb/enola-rb.svg)](https://rubygems.org/gems/enola-rb)
4
+ [![CI](https://github.com/misabegovic/enola-rb/actions/workflows/ci.yml/badge.svg)](https://github.com/misabegovic/enola-rb/actions/workflows/ci.yml)
6
5
 
7
- Two channels. `enola` wraps the released upstream binary and `enola-rb` adds
8
- the Rails layer on it, split so a Rubyist can take the wrapper without Rails.
9
- `munola` is one gem for its own channel, wrapper and Rails layer together,
10
- because nobody installs it to avoid Rails. The Rails code is written once, in
11
- `enola-rb`, and munola adds only what is munola's own.
6
+ Fail the build when a change breaks your Rails app's architecture: a model
7
+ that starts calling a controller, a job that renders, a new dependency cycle.
8
+ RuboCop reads one file at a time and your tests run the code; neither sees
9
+ the edge a change adds between two parts of the app. These gems run
10
+ [enola](https://github.com/enola-labs/enola) from Bundler. enola pins your
11
+ app's dependency graph as a baseline and grades each change against it, so
12
+ the hundreds of crossings an app already has stay out of the report and only
13
+ what the change introduced can fail it.
14
+
15
+ ## What it catches
16
+
17
+ [Campfire](https://github.com/basecamp/once-campfire), 37signals' Rails chat
18
+ app, after `bin/rails generate enola:install` and a pinned baseline. One
19
+ method added to a model:
20
+
21
+ ```ruby
22
+ # app/models/boost.rb
23
+ def render_preview
24
+ MessagesController.render(partial: "messages/boost", locals: { boost: self })
25
+ end
26
+ ```
27
+
28
+ ```
29
+ $ bin/rake enola:check
30
+ FAIL — 1 structural regression introduced.
31
+
32
+ Regressions (fail):
33
+ - [constraints] 1.00 — Constraint models-do-not-reach-controllers violated: Boost#render_preview -> MessagesController via calls
34
+ forbidden calls edge
35
+ app/models/boost.rb:7
36
+ def render_preview
37
+ ^^^^^^^^^^^^^^^^^^
38
+
39
+ Policy: fail on new findings from [constraints] at confidence >= 1.00.
40
+
41
+ New findings (advisory — below the failure policy):
42
+ - [constraints] 0.90 — Advisory constraint layered/domain-knows-no-presentation violated: rubydex-ref: Boost#render_preview -> MessagesController -> MessagesController via depends_on
43
+ forbidden depends_on edge
44
+ app/models/boost.rb:8
45
+ MessagesController.render(partial: "messages/boost", locals: { boost: self })
46
+ ```
47
+
48
+ The tests pass and RuboCop is silent; the check exits 1. Campfire already
49
+ carries 178 breaches of the bound rules, and none of them is in the report.
50
+ A new controller that calls a model passes. Output trimmed: the full verdict
51
+ goes on to list what the change resolved, the coupling it added and the
52
+ package metrics that moved. `enola check --summary` writes the same verdict in
53
+ plain words.
54
+
55
+ ## Quick start (Rails)
56
+
57
+ ```sh
58
+ bundle add enola-rb --group "development, test"
59
+ bin/rake enola:explain # what the app is, where to start, what is risky
60
+ bin/rails generate enola:install # mcp-arch.yaml, enola/constraints/, .gitignore
61
+ bin/rake enola:snapshot # snapshot the app and pin it as the baseline
62
+ # ...make a change...
63
+ bin/rake enola:check # exits 1 on a new breach of a declared law
64
+ ```
65
+
66
+ The generator writes four starter laws a Rails team keeps, each with its
67
+ reason (`models-do-not-reach-controllers`, `policies-only-answer`,
68
+ `the-request-api-stays-in-controllers`,
69
+ `background-jobs-never-invoke-controller-code`), and binds the enola recipes
70
+ whose directories the app has. `layered` is bound advisory: Rails
71
+ controllers call models by design, which that recipe reads as a breach, so
72
+ it reports and never fails a check. The laws are plain Ruby files in
73
+ `enola/constraints/`; edit them, delete them, add your own. Upstream's
74
+ [Rails guide](https://github.com/enola-labs/enola/blob/main/docs/RAILS.md)
75
+ walks the same loop with the binary alone, and
76
+ [GATING.md](https://github.com/enola-labs/enola/blob/main/docs/GATING.md)
77
+ says exactly what can fail a build.
78
+
79
+ Not on Rails? `bundle add enola --group development` and every enola command
80
+ runs as `bundle exec enola ...`; see [`enola`](#enola) below.
81
+
82
+ ## In CI
83
+
84
+ Grade each pull request against its base branch. The base is snapshotted in
85
+ a worktree beside the checkout, so the PR's `Gemfile.lock` stays in place,
86
+ and a regression shows up as an annotation on the line that introduced it:
87
+
88
+ ```yaml
89
+ # .github/workflows/architecture.yml
90
+ name: Architecture
91
+ on: pull_request
92
+ jobs:
93
+ enola:
94
+ runs-on: ubuntu-latest
95
+ steps:
96
+ - uses: actions/checkout@v5
97
+ with:
98
+ fetch-depth: 0
99
+ - uses: ruby/setup-ruby@v1
100
+ with:
101
+ bundler-cache: true
102
+ - name: Snapshot the base branch
103
+ run: |
104
+ git worktree add "$RUNNER_TEMP/base" "${{ github.event.pull_request.base.sha }}"
105
+ bundle exec enola --generate "$RUNNER_TEMP/base"
106
+ - name: Grade the pull request
107
+ run: >
108
+ bundle exec enola check --baseline "$RUNNER_TEMP/base/.enola"
109
+ --fail-on constraints --format annotations --host github .
110
+ ```
111
+
112
+ `bundle exec enola` needs no database and does not boot the app. Merge what
113
+ the generator wrote before turning the workflow on, so the base declares the
114
+ same laws the pull request is graded by.
115
+
116
+ ## With a coding agent
117
+
118
+ enola is also an MCP server: it gives Claude Code, Cursor and other agents a
119
+ map of the app to query instead of grepping through it, and lets them check
120
+ their own change before they report it done.
121
+
122
+ ```sh
123
+ claude mcp add enola -- bundle exec enola # serve the graph to Claude Code
124
+ bundle exec enola install # write enola's instructions into the
125
+ # files your agents read; previews first
126
+ ```
127
+
128
+ ## How it compares
129
+
130
+ | Tool | Reads | Fails on |
131
+ |---|---|---|
132
+ | RuboCop, Standard | one file at a time, against style rules | any offence in the tree |
133
+ | Brakeman | data flow, for security | any warning in the tree |
134
+ | Packwerk | constant references across declared packs | references the packs do not allow |
135
+ | enola | the dependency graph across the app, against a pinned baseline | only what this change introduced, and only the rules you name |
136
+
137
+ They are complements. enola reads Packwerk's `package.yml` files, rolls
138
+ impact up by pack and has a packwerk explainer, so an app that already uses
139
+ Packwerk keeps it.
140
+
141
+ ## The gems
142
+
143
+ Three pure-Ruby gems in one repository. None carries a binary or compiles
144
+ anything: the first command that needs enola downloads the release for your
145
+ platform and verifies its checksum.
146
+
147
+ | Gem | Use it for |
148
+ |---|---|
149
+ | `enola-rb` | Rails: the generator and the `enola:*` rake tasks. Depends on `enola`. |
150
+ | `enola` | Any Ruby project: the `enola` command, wrapping the released binary. |
151
+ | `munola` | A fork channel with its own recipe catalogue; see [`munola`](#munola). |
152
+
153
+ `enola` and `enola-rb` are split so a Rubyist can take the wrapper without
154
+ Rails. `munola` is one gem for its own channel, wrapper and Rails layer
155
+ together, because nobody installs it to avoid Rails. The Rails code is written
156
+ once, in `enola-rb`, and munola adds only what is munola's own.
12
157
 
13
158
  ## `enola`
14
159
 
15
160
  The wrapper of the released enola. The gem versions itself; the release it
16
- drives is `Enola::UPSTREAM_VERSION`, v0.4.32 as of `enola 0.5.6`, and every
161
+ drives is `Enola::UPSTREAM_VERSION`, v0.4.35 as of `enola 0.5.7`, and every
17
162
  gemspec summary names it.
18
163
 
19
164
  ```ruby
@@ -29,7 +174,7 @@ bundle exec enola check .
29
174
  The first command that needs the binary downloads the release for your
30
175
  platform (linux, darwin, windows; amd64, arm64), verifies it against the
31
176
  sha256 file the same release publishes, and keeps it under
32
- `~/.cache/enola/upstream/0.4.32/` (`ENOLA_CACHE_DIR` moves the root). An
177
+ `~/.cache/enola/upstream/0.4.35/` (`ENOLA_CACHE_DIR` moves the root). An
33
178
  `enola` already on your PATH is used only when it answers the pinned version;
34
179
  offline with an empty cache is a refusal that names the cache and the release
35
180
  rather than a fallback to whatever is installed. Nothing downloads at
@@ -56,6 +201,7 @@ gem "enola-rb"
56
201
 
57
202
  ```sh
58
203
  bin/rails generate enola:install
204
+ bin/rake enola:explain
59
205
  bin/rake enola:snapshot
60
206
  bin/rake enola:check
61
207
  ```
@@ -64,9 +210,14 @@ The generator writes `enola/constraints/` from the guides' starter laws (four
64
210
  laws a Rails team keeps, each with its reason), asks the binary's own
65
211
  `constraints init` to bind the shipped recipes whose roles resolve in the app,
66
212
  writes every other shipped recipe as a commented binding to uncomment once the
67
- directories exist, and ignores `.enola/`. It reads what `init` wrote rather
213
+ directories exist, and ignores `.enola/`. A `layered` binding the binary's
214
+ init wrote is set to `mode: advisory`; a `recipes.yaml` the app already had
215
+ is never touched. It reads what `init` wrote rather
68
216
  than its exit code, and when the binary cannot be fetched it still writes the
69
- laws and says so. `enola:snapshot` generates and pins the baseline;
217
+ laws and says so. `enola:explain` needs no setup: it prints what the app is,
218
+ where to start reading, what is risky to change and what is worth a look
219
+ (the full statistics report on a binary without `--summary`).
220
+ `enola:snapshot` generates and pins the baseline;
70
221
  `enola:check` grades the working tree against it and fails on a new breach of
71
222
  a declared law. A surface the pinned binary lacks is refused by name with the
72
223
  remedy.
data/lib/enola/config.rb CHANGED
@@ -10,15 +10,17 @@ module Enola
10
10
  File.join(File.expand_path(root), FILE)
11
11
  end
12
12
 
13
- def self.write_default(root, ruby: Providers.ruby)
13
+ # rubydex names the engine version the config pins; pass what the binary
14
+ # being driven lists, since a pin it does not fetch is a silent skip.
15
+ def self.write_default(root, ruby: Providers.ruby, rubydex: Providers::RUBYDEX_EXPECTED_VERSION)
14
16
  target = path(root)
15
17
  return nil if File.exist?(target)
16
18
 
17
- File.write(target, render(ruby: ruby))
19
+ File.write(target, render(ruby: ruby, rubydex: rubydex))
18
20
  target
19
21
  end
20
22
 
21
- def self.render(ruby: Providers.ruby)
23
+ def self.render(ruby: Providers.ruby, rubydex: Providers::RUBYDEX_EXPECTED_VERSION)
22
24
  <<~YAML
23
25
  # Written by the enola gem; edit freely. Both providers are on: Prism runs
24
26
  # under the Ruby that runs the gem, Rubydex is built into the binary and
@@ -33,7 +35,7 @@ module Enola
33
35
  command: [#{ruby.inspect}, #{Providers.prism_script.inspect}]
34
36
  expected_version: "#{Providers.prism_version || '0.1.0'}"
35
37
  - name: rubydex
36
- expected_version: "#{Providers::RUBYDEX_EXPECTED_VERSION}"
38
+ expected_version: "#{rubydex}"
37
39
  YAML
38
40
  end
39
41
  end
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
- # Vendored from enola-labs/enola examples/providers/ruby/prism/enola_prism_provider.rb at v0.4.32 (37d74c08a48dfa6092ae5e37cc15edb390f66ec7), Apache-2.0.
3
+ # Vendored from enola-labs/enola examples/providers/ruby/prism/enola_prism_provider.rb at v0.4.35 (9fe636b68967d113292d90bfca4cc7ebaa2f2b94), Apache-2.0.
4
4
 
5
5
  # enola Prism reference provider.
6
6
  #
@@ -9,7 +9,7 @@ module Enola
9
9
  # script under the Ruby running this gem; Rubydex is built into the binary
10
10
  # and needs its engine library fetched once per binary version.
11
11
  module Providers
12
- RUBYDEX_EXPECTED_VERSION = "0.4.0"
12
+ RUBYDEX_EXPECTED_VERSION = "0.4.1"
13
13
 
14
14
  def self.prism_script
15
15
  File.expand_path("providers/prism/enola_prism_provider.rb", __dir__)
data/lib/enola/version.rb CHANGED
@@ -3,6 +3,6 @@
3
3
  module Enola
4
4
  # The gem versions itself. UPSTREAM_VERSION names the enola release it
5
5
  # fetches and drives.
6
- UPSTREAM_VERSION = "0.4.32"
7
- VERSION = "0.5.6"
6
+ UPSTREAM_VERSION = "0.4.35"
7
+ VERSION = "0.5.7"
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enola
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muhamed Isabegovic
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-20 00:00:00.000000000 Z
11
+ date: 2026-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prism
@@ -25,9 +25,14 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  description: |
28
- A pure-Ruby wrapper around enola, the architecture-graph tool by enola-labs.
28
+ Catch the architecture regressions a change introduces: a new dependency
29
+ cycle, a layer crossed the wrong way, coupling nobody asked for. enola pins
30
+ your codebase's dependency graph as a baseline and grades each change
31
+ against it, and serves the graph to coding agents over MCP. This gem is a
32
+ pure-Ruby wrapper around enola, the architecture-graph tool by enola-labs,
33
+ so it runs from Bundler as `bundle exec enola`; for Rails, use enola-rb.
29
34
  The gem versions itself; the enola release it drives is named in its
30
- UPSTREAM_VERSION, 0.4.32 here. The binary is not in the
35
+ UPSTREAM_VERSION, 0.4.35 here. The binary is not in the
31
36
  gem: the first command that needs it downloads the release for your
32
37
  platform, verifies it against the checksum the release publishes, and keeps
33
38
  it in a per-user cache. Every command and exit code is forwarded unchanged.
@@ -61,7 +66,9 @@ licenses:
61
66
  - Apache-2.0
62
67
  metadata:
63
68
  source_code_uri: https://github.com/misabegovic/enola-rb
69
+ documentation_uri: https://github.com/misabegovic/enola-rb#readme
64
70
  changelog_uri: https://github.com/misabegovic/enola-rb/blob/main/CHANGELOG.md
71
+ bug_tracker_uri: https://github.com/misabegovic/enola-rb/issues
65
72
  post_install_message:
66
73
  rdoc_options: []
67
74
  require_paths:
@@ -70,7 +77,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
70
77
  requirements:
71
78
  - - ">="
72
79
  - !ruby/object:Gem::Version
73
- version: 3.1.0
80
+ version: 3.2.0
74
81
  required_rubygems_version: !ruby/object:Gem::Requirement
75
82
  requirements:
76
83
  - - ">="
@@ -80,6 +87,6 @@ requirements: []
80
87
  rubygems_version: 3.5.22
81
88
  signing_key:
82
89
  specification_version: 4
83
- summary: 'Runs the released enola from Ruby: fetched on first use, verified, cached.
84
- This release drives enola 0.4.32.'
90
+ summary: 'Catch architecture regressions in Ruby code against a pinned baseline: runs
91
+ the released enola 0.4.35, fetched and verified on first use.'
85
92
  test_files: []