enola-guides 0.2.1 → 0.3.1

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: 8b0ff999485e94088e964c4dd465bb5c1a26fea1a6dece3ed79b301e644786a8
4
- data.tar.gz: 2c015c5cb721592b33c66e274108ead2f537a50e706d0a18d7a97880e9f78664
3
+ metadata.gz: f8f7f20100ffdeea1ae928ad9e76186848aeba81caed9491993e09cfc5bc7461
4
+ data.tar.gz: dcbacd0bba3cf8ee9883b1590a8cd969d1c01150d4549789c1869920dad8de9e
5
5
  SHA512:
6
- metadata.gz: 581a958308b2e03bd6014a30f73007e7b9e6f70cfc5d97dad82d5e733f520c87b557983d3da041ee4234c7d392d510c67ddcf31a145d166909ad791e95ca187c
7
- data.tar.gz: b2b684408b5ec182a1edb87f36483f5159af9520e60c144062b1d9e28f550c4a4176f2c3b256e73a8681b1c776a89aab5999b9816c310692331685e44b14e604
6
+ metadata.gz: f3de1b04dfb1ec8c76f2686d4730052a0f38175721ea288294a09e1f4c2be4fa154d9622748405204b086857bdc5de3969675b49f729b0a5f229f5b8dc5ebebb
7
+ data.tar.gz: 49e2f753fa478c657683dbe69b729774dc94129a8ba8bf3c6614469b7576e0752857daa266f993006098e8a3e36eeeada89377412548d122c83f9d4fa41d1a01
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1 (2026-08-23)
4
+
5
+ The recipes README says where the catalogue ships: built into munola,
6
+ written by `munola init`; the upstream channels carry upstream's built-ins
7
+ only.
8
+
9
+ ## 0.3.0 (2026-08-23)
10
+
11
+ The recipe catalogue munola binds: `data-ownership`, `api-boundaries`,
12
+ `background-work` and the `tenant-foreign-key` template beside
13
+ `ember-conventions`, each law an existing enola form with the number it held
14
+ at on a real monolith in the catalogue table; every file lints clean bound
15
+ to a Rails tree.
16
+
3
17
  ## 0.2.1 (2026-08-23)
4
18
 
5
19
  enola v0.4.4 released the constraints program the guides describe
data/README.md CHANGED
@@ -74,7 +74,7 @@ your agent's skills folder; the hooks they pair with are under
74
74
  ## Templates and recipes
75
75
 
76
76
  [`templates/`](templates): the CI check script and its comment renderer,
77
- a cluster configuration, a constraints starter. [`recipes/`](recipes/README.md):
77
+ a cluster configuration, a constraints starter. [`recipes/`](recipes/README.md) (the catalogue munola binds, each recipe with the number its laws held at):
78
78
  recipes a repository copies into its own `enola/recipes/` and binds in one
79
79
  line, starting with the Ember conventions and a house-recipe template;
80
80
  starting points the team reviews, never defaults the tool applies.
data/lib/enola-guides.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Enola
4
4
  module Guides
5
- VERSION = "0.2.1"
5
+ VERSION = "0.3.1"
6
6
  end
7
7
  end
data/recipes/README.md CHANGED
@@ -40,6 +40,33 @@ breach. Start with ratchet.
40
40
 
41
41
  ## The recipes
42
42
 
43
+ The catalogue munola ships built in: `munola init` writes these recipes
44
+ into a project's `enola/recipes/` and binds those whose roles resolve,
45
+ and the munola binary embeds the parameter-free ones, generated from
46
+ these files at release. The `enola` and `enola-rb` gems carry upstream's
47
+ own built-ins only (`rails-conventions`, `rails-strict`, `vanilla-rails`
48
+ and the six arrangements); a team on those channels copies a file from
49
+ here by hand. Each law is
50
+ an existing enola form; each recipe is here because its laws held on a real
51
+ Rails monolith at a number a team can act on, given beside it.
52
+
53
+ | Recipe | States | Held at |
54
+ |---|---|---|
55
+ | `rails-conventions` (upstream) | jobs, models, serializers and components never render; policies and mailers never enqueue; request API stays in controllers | five of seven laws with zero exceptions, three advisory at 134 crossings |
56
+ | `rails-strict` (upstream) | the above plus no cycles among the Rails roles and independent concerns | 0 of 127 concerns breach |
57
+ | `vanilla-rails` (upstream) | the directories the vanilla arrangement leaves empty stay empty | an arrangement a team chooses |
58
+ | `ember-conventions.yaml` | nine Ember conventions selected by what the code carries | the neutral eight hold; the house ones stay out |
59
+ | `data-ownership.yaml` | readers read the store only | advisory; bind your query objects |
60
+ | `api-boundaries.yaml` | the public API is reached only by controllers; a mutating action reaches a policy | 736 unauthorized mutations reported, advisory |
61
+ | `background-work.yaml` | maintenance tasks live in their namespace and never reach controllers | 0 of 3,250 task call edges reach a controller |
62
+ | `tenant-foreign-key.yaml` | a table carrying the tenant column carries the foreign key | 3,720 declared, 0 new |
63
+
64
+ Two data-ownership laws, one owner per table and storage stays home, are
65
+ declarations rather than recipe rules for now: they live as worked examples
66
+ under [`examples/laws/`](../examples/laws/README.md) because recipe expansion
67
+ in the current release does not rebind the part a graph law names. They join
68
+ `data-ownership.yaml` when it does.
69
+
43
70
  - `ember-conventions.yaml`: nine Ember conventions a team can adopt as laws,
44
71
  selected by what the code carries (a decorator, a written field, a base
45
72
  class's module) rather than by where it sits. Two optional roles are
@@ -47,6 +74,17 @@ breach. Start with ratchet.
47
74
  tracked functions in your app) and `fixture-setup` (the entry points of
48
75
  your fixture library in tests).
49
76
 
77
+ - `data-ownership.yaml`: readers (your query objects, bound by you) reach
78
+ the store and nothing else.
79
+ - `api-boundaries.yaml`: the API layer is entered through controllers, and a
80
+ mutating action reaches a policy; bind `mutating-actions` to the part your
81
+ routes reach with POST, PUT, PATCH or DELETE.
82
+ - `background-work.yaml`: maintenance tasks live in the `Maintenance`
83
+ namespace the runner resolves, and never reach controllers.
84
+ - `tenant-foreign-key.yaml`: a template; replace `TENANT_COLUMN` and
85
+ `TENANT_TABLE` (munola's generator does) and every table carrying the
86
+ column must carry the key.
87
+
50
88
  Laws whose far end is a helper of your own (a promise-unwrapping getter
51
89
  helper, an async-relationship reader) belong in your repository's recipe
52
90
  beside these, with your names in them.
@@ -0,0 +1,25 @@
1
+ recipe: api-boundaries
2
+ roles:
3
+ - name: controllers
4
+ match: ["app/controllers/**"]
5
+ - name: policies
6
+ match: ["app/policies/**"]
7
+ - name: public-api
8
+ match: ["app/controllers/api/**"]
9
+ - name: mutating-actions
10
+ optional: true
11
+ rules:
12
+ - id: public-api-reached-only-by-controllers
13
+ protect: public-api
14
+ owners: [controllers]
15
+ via: calls
16
+ mode: advisory
17
+ because: "The API layer is entered through a request so that authentication, rate limits and serialization run once, at the edge; an internal caller that reaches an API class directly skips all three and nobody reading the API can see it."
18
+
19
+ - id: mutations-are-authorized
20
+ require_edge: mutating-actions
21
+ to: policies
22
+ direction: outbound
23
+ via: calls
24
+ mode: advisory
25
+ because: "Every write passes a policy before it touches a record. Bind the actions that routes reach with POST, PUT, PATCH or DELETE (a part declared with handles: on those methods) and the law names each mutating action that reaches no policy."
@@ -0,0 +1,21 @@
1
+ recipe: background-work
2
+ roles:
3
+ - name: tasks
4
+ match: ["app/tasks/**"]
5
+ - name: task-classes
6
+ match: ["app/tasks/**"]
7
+ kind: symbol
8
+ where: { symbol_kind: "class" }
9
+ - name: controllers
10
+ match: ["app/controllers/**"]
11
+ rules:
12
+ - id: maintenance-tasks-live-in-their-namespace
13
+ require_name: task-classes
14
+ pattern: "Maintenance::*"
15
+ because: "The task runner resolves task constants from that namespace, which is the one its generator creates, so a task declared outside it never appears in the runner and cannot be run by anyone."
16
+
17
+ - id: maintenance-tasks-never-reach-controllers
18
+ forbid: tasks
19
+ to: controllers
20
+ via: calls
21
+ because: "A task runs from the runner or the console with no request around it, so controller code reached from one has nothing to read."
@@ -0,0 +1,13 @@
1
+ recipe: data-ownership
2
+ roles:
3
+ - name: store
4
+ match: ["app/models/**"]
5
+ - name: readers
6
+ optional: true
7
+ rules:
8
+ - id: readers-read-the-store-only
9
+ allow: readers
10
+ only: [store]
11
+ via: calls
12
+ mode: advisory
13
+ because: "A reader derives its answer from the store alone, which is what keeps it replayable against a snapshot; a reader that reaches a service or a client answers differently on a different machine. Bind your query objects or report classes as the readers."
@@ -0,0 +1,12 @@
1
+ recipe: tenant-foreign-key
2
+ roles:
3
+ - name: tables
4
+ match: ["db/schema.rb", "db/structure.sql", "app/models/**"]
5
+ kind: storage
6
+ rules:
7
+ - id: tenant-foreign-key
8
+ require: tables
9
+ when_prop_contains: { prop: columns, value: TENANT_COLUMN }
10
+ must_prop_contain: { prop: fk_constraints, value: "TENANT_COLUMN->TENANT_TABLE" }
11
+ mode: advisory
12
+ because: "A table carrying the tenant column references the tenant, so the database should say so with a foreign key; a row that outlives its tenant is the leak nobody notices until an export. Advisory while a backfill path that ships the column before the constraint is reconciled with the rule; the verdict names every table that carries the column without the key."
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enola-guides
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muhamed Isabegovic
@@ -115,8 +115,12 @@ files:
115
115
  - guides/12-the-wrapper.md
116
116
  - lib/enola-guides.rb
117
117
  - recipes/README.md
118
+ - recipes/api-boundaries.yaml
119
+ - recipes/background-work.yaml
120
+ - recipes/data-ownership.yaml
118
121
  - recipes/ember-conventions.yaml
119
122
  - recipes/house-recipe-template.yaml
123
+ - recipes/tenant-foreign-key.yaml
120
124
  - templates/constraints-starter/background-jobs-never-invoke-controller-code.rb
121
125
  - templates/constraints-starter/models-do-not-reach-controllers.rb
122
126
  - templates/constraints-starter/policies-only-answer.rb