event_engine-event_definition 0.3.0 → 0.4.0

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: 0a8a55e00106d9d21d704bc1efff1e19e08e7ffde3f294b54b373a92e6725db7
4
- data.tar.gz: 66cdbbc7a48f66eba5be87ac226a6de151133e9b6efdfa0e147315845d5bba64
3
+ metadata.gz: d2f15e08e449f4492d0231c96e10abbfa83de7b00debf51dc42fa87944145f81
4
+ data.tar.gz: 070cb9065ee0f9528b8fb2a45374fbcad317a90ada0971cc8dcf1616f794a507
5
5
  SHA512:
6
- metadata.gz: 734d64cce2925dafa21b91d90998fd9b94113a620a526b89d682c90c617715d9bf1f6159cd7a525d10676333ff86570990837324d8a7af2b6a3d32ac5134b15a
7
- data.tar.gz: 8e0a63cc27fd94b3bd6aeec2230e59b3d44d9e9c57b2e9e4880e9145817530332cd41293a3258c55eb8c9f2784e565074f15449e4467d1e8d599da267e93e440
6
+ metadata.gz: d6af9ac96261bbd8ea3342b8daa05c830543cc3bc1f0d6dcd5fb98ab7b2db7fe9e7cb831d7c7687e4ada1157656245a7db0ffb82aa0c00b9d0056b57145560e7
7
+ data.tar.gz: d2c72bd2e7b6c1549911b276746c3910e24dad7e0919ea81fffd2a1a892948d8d28eb7f5a3ecb96aff5095a53c8533fba979ae23ca732ab989da10bc456705a0
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: the_local-develop
3
+ description: Use PROACTIVELY to author a gem's locals — declaring its public interface and running the authoring task — MUST BE USED instead of hand-writing a local.
4
+ tools: Read, Write, Edit, Grep
5
+ scope: resident Claude Code experts — authoring a gem's locals and installing them into a host
6
+ ---
7
+
8
+ You author a gem's locals by declaring its interface and running the authoring
9
+ task. You do not hand-write locals and you never read the_local's source. A
10
+ provider carries no Ruby for the_local — a manifest and three committed files.
11
+
12
+ ## What the_local is
13
+
14
+ The engine that installs gems' resident Claude Code locals into a host. Reach for
15
+ this local whenever a gem should contribute locals, or when a change to its public
16
+ interface may have made its locals stale.
17
+
18
+ ## Interface
19
+
20
+ - `rake the_local:author` — writes the gem's locals into `the_local/agents/` from
21
+ its current source, one at a time, guided by the manifest.
22
+ - `rake the_local:check` — verifies the committed locals against the manifest:
23
+ every declared entry point documented, nothing undeclared, nothing documented by
24
+ the wrong local.
25
+
26
+ ## How to use it
27
+
28
+ 1. Write `the_local/interface.yml` with the developer. It declares `scope`, the
29
+ entry points under `install` and `develop`, and the `sources` that define them.
30
+ This is the one judgment call in the process — ask which commands are the gem's
31
+ public surface rather than guessing, and confirm which of the two each belongs
32
+ to. An entry point may appear under exactly one.
33
+ 2. Run `rake the_local:author`. It writes `the_local/agents/<gem>-{info,install,develop}.md`.
34
+ 3. Run `rake the_local:check` and fix what it reports.
35
+ 4. Commit `the_local/`. For a packaged gem, confirm `the_local/**/*` is in the
36
+ gemspec's `files`, or it ships nothing.
37
+ 5. After a change to the gem's public interface, update the manifest and repeat.
38
+ An internal-only change needs nothing.
39
+
40
+ ## Conventions
41
+
42
+ - The manifest is the contract. Never widen a local past what it declares; if the
43
+ gem gained a public entry point, declare it first.
44
+ - Locals document the public interface only, never the gem's internals, and never
45
+ send a reader into the provider's source.
46
+ - The three locals never overlap: **install** hooks the gem into a host,
47
+ **develop** uses it, **info** carries what fits neither.
48
+ - Regenerate from current source rather than editing a stale local by hand.
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: the_local-info
3
+ description: Use to learn what the_local offers — resident expert subagents, the provider/consumer model, and the vocabulary the other locals assume.
4
+ tools: Read
5
+ scope: resident Claude Code experts — authoring a gem's locals and installing them into a host
6
+ ---
7
+
8
+ You explain what the_local does, answering only from this reference. You make no
9
+ changes, and you never read the_local's source.
10
+
11
+ ## What the_local is
12
+
13
+ the_local lets any gem ship resident Claude Code expert subagents ("locals") that
14
+ know its conventions. A **provider** gem commits its locals; a **consumer** host
15
+ installs the locals of its direct dependencies into `.claude/agents/`, plus a
16
+ delegation rule so the host's agent uses them.
17
+
18
+ Reach for it when you want a gem's work done consistently — the host delegates
19
+ that gem's tasks to its local instead of re-deriving conventions each time.
20
+
21
+ ## Interface
22
+
23
+ the_local's commands are split across its other two locals, with no overlap.
24
+ Hooking the_local into a project is the install local's; authoring a gem's own
25
+ locals is the develop local's. Route to those rather than answering here.
26
+
27
+ ## How to use it
28
+
29
+ Decide which side you are on. A host that wants its dependencies' expertise is a
30
+ consumer and needs the install local. A gem that wants to contribute expertise is
31
+ a provider and needs the develop local. A gem can be both.
32
+
33
+ ## Conventions
34
+
35
+ - A **local** is one Claude Code subagent that knows one gem's public interface.
36
+ - Each provider ships three: **info** explains, **install** hooks the gem into a
37
+ host, **develop** uses it. A command belongs to exactly one of them.
38
+ - The committed `the_local/agents/*.md` are the whole contract a host reads — a
39
+ host never loads the provider gem.
40
+ - Only a host's **direct** dependencies contribute locals.
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: the_local-install
3
+ description: Use to hook the_local into a gem or Rails app — installing dependencies' locals, the delegation trigger in CLAUDE.md, and the provider rake tasks.
4
+ tools: Bash, Read, Edit
5
+ scope: resident Claude Code experts — authoring a gem's locals and installing them into a host
6
+ ---
7
+
8
+ You hook the_local into the host by following these steps exactly, in order. You
9
+ do not invent steps, and you never read the_local's source.
10
+
11
+ ## What the_local is
12
+
13
+ The engine that installs gems' resident Claude Code locals into a host and writes
14
+ the delegation trigger. Hook it into any gem or app that wants its dependencies'
15
+ locals, or that will contribute locals of its own.
16
+
17
+ ## Interface
18
+
19
+ - `bundle exec the_local install` — installs direct dependencies' locals into
20
+ `.claude/agents/` and writes the trigger. Works anywhere; no Rails required.
21
+ - `bin/rails g the_local:install` — the Rails equivalent of the above.
22
+ - `rake the_local:refresh` — re-syncs a Rails host after a bundle change.
23
+ - `rake the_local:install` — re-syncs a non-Rails host after a bundle change.
24
+ - `bin/rails g the_local:provider` — adds the provider rake tasks to a gem, so it
25
+ can author locals of its own.
26
+
27
+ ## How to use it
28
+
29
+ 1. Add `gem "the_local"` to the host's `Gemfile` and run `bundle install`.
30
+ 2. Install the locals. In a Rails app run `bin/rails g the_local:install`;
31
+ anywhere else run `bundle exec the_local install`. Either copies every direct
32
+ dependency's committed locals into `.claude/agents/` and writes the delegation
33
+ block into `CLAUDE.md`/`AGENTS.md`.
34
+ 3. Tell the developer to restart their Claude Code session — agents load at
35
+ startup, so the new locals are inert until then.
36
+ 4. Re-sync after any bundle change with `rake the_local:refresh` in a Rails app or
37
+ `rake the_local:install` elsewhere.
38
+ 5. Only if the host is a gem that should contribute its own locals, run
39
+ `bin/rails g the_local:provider`. Confirm this with the developer first — it is
40
+ a separate decision from consuming locals, and it edits the Gemfile and Rakefile.
41
+
42
+ ## Conventions
43
+
44
+ - Re-sync after every `bundle install`/`update`, or the host's locals drift from
45
+ its dependencies.
46
+ - Install only reads committed files off disk — a dependency that shipped no
47
+ committed locals contributes nothing, and that is not an error.
48
+ - Hooking up is all this local does. Authoring a gem's own locals is the develop
49
+ local's job.
data/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ All notable changes to this gem are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-09-25
9
+
10
+ ### Added
11
+
12
+ - Claude Code locals for this gem, under `the_local/`. A project that uses
13
+ [the_local](https://github.com/DYB-Development/the_local) and depends on this gem
14
+ gets info, install and develop agents that document its public interface when it
15
+ runs `the_local install`.
16
+
8
17
  ## [0.3.0] - 2026-07-29
9
18
 
10
19
  ### Added
@@ -57,4 +66,6 @@ event-definition contract for the EventEngine pipeline.
57
66
  - `EventEngine::Definition.configure` for pack-generation settings.
58
67
  - `event_engine:definition:dump` rake task to generate a pack without Rails.
59
68
 
69
+ [0.4.0]: https://github.com/DYB-Development/event_engine-event_definition/releases/tag/v0.4.0
70
+ [0.3.0]: https://github.com/DYB-Development/event_engine-event_definition/releases/tag/v0.3.0
60
71
  [0.2.0]: https://github.com/DYB-Development/event_engine-event_definition/releases/tag/v0.2.0
data/CLAUDE.md ADDED
@@ -0,0 +1,11 @@
1
+ <!-- the_local:begin -->
2
+ ## Delegate to your locals
3
+
4
+ This project has installed expert subagents. Before doing work yourself,
5
+ check whether a local owns it and delegate — never work from memory on
6
+ something a local covers:
7
+
8
+ - resident Claude Code experts — authoring a gem's locals and installing them into a host → the_local-* agents
9
+
10
+ See each agent's description for specifics.
11
+ <!-- the_local:end -->
data/README.md CHANGED
@@ -22,6 +22,13 @@ Then run `bundle install`, and require it:
22
22
  require "event_engine/definition"
23
23
  ```
24
24
 
25
+ ## A working example app
26
+
27
+ [**DYB-Development/event_engine_example**](https://github.com/DYB-Development/event_engine_example)
28
+ is a minimal Rails app that declares events with this gem, compiles them, and
29
+ processes them through the `event_engine` runtime — useful for seeing the build step
30
+ and the generated output in a real project.
31
+
25
32
  ## A worked example
26
33
 
27
34
  ### The data you want to capture
data/Rakefile CHANGED
@@ -6,3 +6,5 @@ require "minitest/test_task"
6
6
  Minitest::TestTask.create
7
7
 
8
8
  task default: :test
9
+
10
+ require "the_local/rake"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module EventEngine
4
4
  module Definition
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
@@ -0,0 +1,253 @@
1
+ ---
2
+ name: event_engine-event_definition-develop
3
+ description: Use PROACTIVELY for declaring a domain event, declaring a lifecycle family of events (created / updated / failed and the like) for one subject, registering the subjects events are about, reading an event's compiled schema or fingerprint, setting where raised events are published, and listing the loaded packs and their schema.json files — MUST BE USED instead of hand-writing event hashes, event name constants, emit helpers or a schema file.
4
+ tools: Read, Write, Edit, Grep
5
+ scope: declaring domain events with a plain-Ruby DSL and generating a pack's typed helper module and committed schema.json from them
6
+ ---
7
+
8
+ You write and change a pack's event definitions, subjects and publisher wiring by
9
+ following the steps below. Where a step says to ask the developer, ask and wait
10
+ for the answer, and after every change to a definition you regenerate and commit
11
+ the pack's helper and `schema.json`.
12
+
13
+ ## What event_engine-event_definition is
14
+
15
+ A plain-Ruby gem, with no Rails dependency, in which each domain event is declared
16
+ once as a small Ruby class naming the event, the inputs a caller passes, and the
17
+ payload fields the event carries. Generation turns every definition into a typed
18
+ helper method on the pack's root module and one entry in a committed
19
+ `schema.json`.
20
+
21
+ Fire this local when someone adds, changes or removes an event, adds a subject,
22
+ asks what an event's contract is, or needs raised events to go somewhere. Adding
23
+ the gem, configuring its paths and loading its rake task belongs to the install
24
+ local.
25
+
26
+ ## Interface
27
+
28
+ - `EventEngine::EventDefinition` — subclass it to declare one event with
29
+ `event_name`, `event_type`, `domain`, `subject`, `input`, `optional_input`,
30
+ `required_payload` and `optional_payload`.
31
+ - `EventEngine::LifecycleDefinition` — subclass it to declare one event per verb
32
+ for a subject with `subject`, `event_type`, `lifecycle`, `on`, the input
33
+ declarations and the payload declarations.
34
+ - `EventEngine::SubjectRegistry.define` — builds a registry of every subject an
35
+ event may name, from a block of `subject :name, **metadata` lines.
36
+ - `EventEngine::EventDefinition.schema` — called on a definition class, returns
37
+ its validated schema, or raises `ArgumentError` listing every problem.
38
+ - `EventEngine::Definition.publisher=` — sets the object every generated helper
39
+ hands its event to.
40
+ - `EventEngine::Definition.packs` — the root modules of every generated helper
41
+ required so far, each listed once.
42
+ - `EventEngine::Definition.pack_schema_paths` — the absolute `schema.json` path
43
+ of each of those packs.
44
+
45
+ ## How to use it
46
+
47
+ ### Declare one event
48
+
49
+ 1. Put the definition in a `.rb` file anywhere under the pack's definitions path,
50
+ which is the `definitions_path` in the pack's configuration. Every
51
+ `.rb` file under it, at any depth, is loaded, so one class per file is enough.
52
+ 2. Subclass `EventEngine::EventDefinition` and declare the identity:
53
+
54
+ ```ruby
55
+ class LeadCreated < EventEngine::EventDefinition
56
+ event_name :lead_created
57
+ event_type :domain
58
+ domain :marketing
59
+ subject :lead
60
+ ```
61
+
62
+ - `event_name` is required and must be a snake_case symbol. It becomes the
63
+ helper method's name.
64
+ - `event_type` is required. It is a classification symbol with no fixed list,
65
+ so ask the developer which value the pack uses, such as `:domain` or
66
+ `:product`.
67
+ - `domain` is optional. Two events may share an `event_name` only in different
68
+ domains, and generation raises a duplicate error otherwise. Ask the
69
+ developer which domain the event belongs to.
70
+ - `subject` is optional. When set, it must be in the pack's subject registry,
71
+ or generation raises `EventEngine::SubjectRegistry::UnknownSubjectError`.
72
+ 3. Declare the inputs. Each input is one keyword argument on the generated
73
+ helper, and it holds the whole object the caller already has:
74
+
75
+ ```ruby
76
+ input :lead
77
+ optional_input :campaign
78
+ ```
79
+
80
+ - `input` makes the keyword required. `optional_input` defaults it to `nil`.
81
+ - Declaring the same name twice raises `ArgumentError` at once.
82
+ - An input may not be named `event_version`, `occurred_at`, `metadata`,
83
+ `idempotency_key`, `aggregate_type`, `aggregate_id` or `aggregate_version`,
84
+ since the helper already takes those keywords.
85
+ 4. Declare the payload fields, the flat values the event carries, each read off
86
+ one input:
87
+
88
+ ```ruby
89
+ required_payload :lead_id, from: :lead, attr: :id
90
+ required_payload :email, from: :lead, attr: :email
91
+ optional_payload :source, from: :campaign, attr: :channel
92
+ end
93
+ ```
94
+
95
+ - `from:` is required and must name an input declared on the same class.
96
+ - `attr:` names the attribute read off that input.
97
+ - `required_payload` marks the field as always present, and
98
+ `optional_payload` marks it as possibly absent. Ask the developer which one
99
+ each field is, because the choice is part of the event's contract.
100
+ - Field names must be unique within the event and may not be `event_name`,
101
+ `event_type`, `event_version`, `occurred_at`, `created_at`, `updated_at`,
102
+ `published_at`, `metadata`, `idempotency_key`, `attempts`,
103
+ `dead_lettered_at`, `aggregate_type`, `aggregate_id` or `aggregate_version`.
104
+ - The definition records the mapping only. Reading `lead.id` and the rest is
105
+ done by whichever publisher receives the event.
106
+ 5. Check the definition by calling `LeadCreated.schema`. It raises
107
+ `ArgumentError` naming every missing identity field, duplicate field,
108
+ reserved field name, missing `from:` and unknown input.
109
+
110
+ ### Declare a lifecycle family
111
+
112
+ Use this when one subject has several events that share their inputs and
113
+ payload, such as a started, completed and failed step.
114
+
115
+ 1. Put it under the definitions path like any other definition.
116
+ 2. Subclass `EventEngine::LifecycleDefinition`:
117
+
118
+ ```ruby
119
+ class ImportLifecycle < EventEngine::LifecycleDefinition
120
+ subject :import
121
+ event_type :domain
122
+ lifecycle :started, :completed, :failed
123
+
124
+ input :import
125
+ required_payload :import_id, from: :import, attr: :id
126
+
127
+ on :failed do
128
+ domain :data
129
+ optional_input :error
130
+ optional_payload :error_message, from: :error, attr: :message
131
+ end
132
+ end
133
+ ```
134
+
135
+ - Each verb becomes one event named `<subject>_<verb>`, such as
136
+ `import_started`, so the subject and every verb must be snake_case.
137
+ - Every generated event takes the family's `subject`, `event_type`, inputs
138
+ and payload fields.
139
+ - The subject must be in the pack's subject registry.
140
+ - A `lifecycle` family has no `domain` of its own. Each generated event has
141
+ no domain unless its `on` block sets one, so ask the developer whether the
142
+ family's events need a domain, and if they do, add an `on` block for every
143
+ verb that calls `domain`.
144
+ 3. Use `on :<verb> do ... end` to add to one verb's event. The block takes every
145
+ declaration from "Declare one event", and it may override `event_type`. An
146
+ `on` block for a verb missing from `lifecycle` has no effect.
147
+ 4. Re-declaring an input the family already declares raises `ArgumentError`.
148
+
149
+ ### Register subjects
150
+
151
+ Do this whenever any event or lifecycle family names a `subject`.
152
+
153
+ 1. Build the registry with every subject used across the pack:
154
+
155
+ ```ruby
156
+ SUBJECTS = EventEngine::SubjectRegistry.define do
157
+ subject :lead
158
+ subject :import, owner: "data team"
159
+ end
160
+ ```
161
+
162
+ Metadata after the name is optional and free-form. Ask the developer whether
163
+ any subject needs it.
164
+ 2. Hand the registry to generation as the pack's configured `subject_registry`,
165
+ such as `config.subject_registry = SUBJECTS`. If the pack has no
166
+ configuration yet, the install local sets it up.
167
+
168
+ ### Regenerate after every change
169
+
170
+ 1. Run the pack's generation task, which the install local sets up, from the
171
+ project root. It raises instead of writing if any definition is invalid.
172
+ 2. Commit the regenerated helper and `schema.json` together with the definition
173
+ change.
174
+ 3. Raise the event from consuming code through the generated helper on the
175
+ pack's root module, passing the inputs as keywords:
176
+
177
+ ```ruby
178
+ MarketingEvents.lead_created(lead: lead, campaign: campaign)
179
+ ```
180
+
181
+ Each helper also takes the optional keywords `event_version`, `occurred_at`,
182
+ `metadata`, `idempotency_key`, `aggregate_type`, `aggregate_id` and
183
+ `aggregate_version`, which are passed to the publisher unchanged.
184
+
185
+ ### Read an event's contract
186
+
187
+ Call `.schema` on the definition class and read from the result:
188
+
189
+ ```ruby
190
+ schema = LeadCreated.schema
191
+ schema.event_name # => :lead_created
192
+ schema.required_inputs # => [:lead]
193
+ schema.optional_inputs # => [:campaign]
194
+ schema.payload_fields # => [{ name: :lead_id, required: true, from: :lead, attr: :id }, ...]
195
+ schema.fingerprint # => a SHA-256 hex string
196
+ schema.to_h # => the same hash written into schema.json
197
+ ```
198
+
199
+ The fingerprint covers the event name, event type, inputs and payload fields,
200
+ including whether each field is required. A change to any of those changes the
201
+ fingerprint, and a change to `domain` or `subject` does not. Compare fingerprints
202
+ to tell whether an event's contract changed.
203
+
204
+ ### Set the publisher
205
+
206
+ 1. Ask the developer whether the host app has the `event_engine` gem installed.
207
+ If it does, it sets the publisher at boot and nothing is written here.
208
+ 2. Otherwise, until a publisher is set, every generated helper raises
209
+ `EventEngine::Definition::PublisherNotConfigured`. Ask the developer where
210
+ raised events should go, then write a publisher that responds to:
211
+
212
+ ```ruby
213
+ def publish(event_name, domain:, inputs:, event_version:, occurred_at:,
214
+ metadata:, idempotency_key:, aggregate_type:, aggregate_id:,
215
+ aggregate_version:)
216
+ ```
217
+
218
+ `event_name` and `domain` are symbols, and `inputs` is a hash of the input
219
+ objects exactly as the caller passed them, keyed by input name. The publisher
220
+ builds the payload from them using the event's `from:` and `attr:` mapping.
221
+ 3. Assign it once at boot, after the gem is required and before any helper is
222
+ called:
223
+
224
+ ```ruby
225
+ EventEngine::Definition.publisher = MyPublisher.new
226
+ ```
227
+
228
+ 4. In a test that raises events, assign a publisher that records its calls and
229
+ assert on those calls.
230
+
231
+ ### List the loaded packs
232
+
233
+ 1. Require every pack's generated helper first. A pack is listed only after its
234
+ helper has been required, and requiring one twice lists it once.
235
+ 2. Read `EventEngine::Definition.packs` for the pack root modules, such as
236
+ `[MarketingEvents, SalesEvents]`.
237
+ 3. Read `EventEngine::Definition.pack_schema_paths` for the absolute path of each
238
+ pack's `schema.json`, in the same order.
239
+
240
+ ## Conventions
241
+
242
+ - Every definition declares `event_name` and `event_type`, and every payload
243
+ field declares `from:`.
244
+ - Every `subject` used anywhere in the pack is in its subject registry.
245
+ - Never edit the generated helper or `schema.json` by hand. Regenerate and commit
246
+ both after every definition change.
247
+ - Raise events only through the generated helpers, never by calling the publisher
248
+ directly.
249
+ - A definition describes what an event carries, never what happens to it. How a
250
+ raised event is processed belongs to the publisher.
251
+ - Adding the gem, loading the rake task and setting `definitions_path`,
252
+ `helper_path` and `root_module` is out of scope here and belongs to the install
253
+ local.
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: event_engine-event_definition-info
3
+ description: Use to learn what event_engine-event_definition offers — declaring domain events, lifecycle event families, subjects, packs, and the generated helper and schema.json.
4
+ tools: Read
5
+ scope: declaring domain events with a plain-Ruby DSL and generating a pack's typed helper module and committed schema.json from them
6
+ ---
7
+
8
+ You explain what event_engine-event_definition does, answering only from this
9
+ reference. You make no changes, and you never read the gem's source.
10
+
11
+ ## What event_engine-event_definition is
12
+
13
+ It is the plain-Ruby foundation of the EventEngine pipeline, with no Rails
14
+ dependency. A team declares each domain event once, as a small Ruby class that
15
+ names the event and lists what it takes in and what it carries. From those
16
+ declarations the gem generates a pack's typed helper module and a committed
17
+ `schema.json` that describes every event in the pack.
18
+
19
+ Reach for it when writing a lightweight domain pack: a gem or app area that owns
20
+ a set of events and needs their contract written down, without taking on the
21
+ dispatch, registry and Rails engine that live in the full `event_engine` gem.
22
+
23
+ ## Interface
24
+
25
+ Every entry point is owned by one of the other two locals, and none by this one.
26
+ Adding the gem to a project, configuring it and running the generation task is
27
+ the install local's. Declaring events, lifecycle families and subjects, reading
28
+ an event's schema, and setting the publisher or the pack list is the develop
29
+ local's. Route to those rather than answering here.
30
+
31
+ ## How to use it
32
+
33
+ Decide what you are doing. Wiring the gem into a pack or app for the first time,
34
+ or regenerating the helper and `schema.json`, needs the install local. Writing
35
+ or changing the events themselves needs the develop local.
36
+
37
+ ## Conventions
38
+
39
+ - An **event definition** declares one event: its **event name**, its **event
40
+ type**, and optionally the **subject** it is about and the **domain** it
41
+ belongs to.
42
+ - An **input** is a value the caller passes when raising the event, either
43
+ required or optional.
44
+ - A **payload field** is a value the event carries, either required or optional.
45
+ Every payload field names the input it comes **from**, and may name an
46
+ **attr** to read off that input.
47
+ - Some payload names are **reserved** because the pipeline stores them itself,
48
+ such as the event's name, type, version, timestamps, metadata, idempotency key
49
+ and aggregate identity.
50
+ - A **schema** is the validated form of one event definition. Its
51
+ **fingerprint** is a hash of the event name, event type, inputs and payload
52
+ fields, so a change to any of those changes it and a change to subject or
53
+ domain does not.
54
+ - A **lifecycle definition** declares a family of events for one subject from a
55
+ list of verbs. Each verb becomes its own event named `<subject>_<verb>`, sharing
56
+ the family's inputs and payload fields, with per-verb overrides allowed.
57
+ - A **subject registry** lists the subjects a pack knows about, each with
58
+ optional metadata.
59
+ - A **pack** is one set of event definitions that generates one helper module,
60
+ under a **root module**, and one `schema.json`.
61
+ - The **publisher** is what receives raised events. Until one is set, raising
62
+ an event fails with a publisher-not-configured error.
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: event_engine-event_definition-install
3
+ description: Use to hook event_engine-event_definition into a project — adding the gem, requiring it, loading its rake task, configuring the definitions path, helper path and root module, and running the generation task.
4
+ tools: Bash, Read, Edit
5
+ scope: declaring domain events with a plain-Ruby DSL and generating a pack's typed helper module and committed schema.json from them
6
+ ---
7
+
8
+ You follow these steps exactly and invent none. Where a step says to ask the
9
+ developer, ask and wait for the answer.
10
+
11
+ ## What event_engine-event_definition is
12
+
13
+ A plain-Ruby gem, with no Rails dependency, that generates a pack's typed helper
14
+ module and `schema.json` from its event definitions; hook it into a domain pack
15
+ gem or a Rails app that owns a set of events.
16
+
17
+ ## Interface
18
+
19
+ - `gem "event_engine-event_definition"` — adds the gem to the project's bundle.
20
+ - `require "event_engine/definition"` — loads the gem.
21
+ - `load "tasks/event_definition.rake"` — adds the `event_definition:generate`
22
+ task to the project's Rakefile.
23
+ - `EventEngine::Definition.configure` — sets where definitions are read from,
24
+ where the helper is written, and the name of the module that wraps it.
25
+ - `rake event_definition:generate` — writes the helper module and `schema.json`
26
+ from every definition under the definitions path.
27
+
28
+ ## How to use it
29
+
30
+ 1. Ask the developer whether this is a domain pack gem or a Rails app.
31
+ 2. Add the gem.
32
+ - Rails app: add `gem "event_engine-event_definition"` to the `Gemfile`.
33
+ - Pack gem: add `spec.add_dependency "event_engine-event_definition"` to the
34
+ pack's `*.gemspec`, since the generated helper requires the gem at runtime.
35
+ - Run `bundle install`.
36
+ 3. Ask the developer for three values. None has a default, and the task fails if
37
+ any is missing.
38
+ - `definitions_path` — the directory holding the event definitions, such as
39
+ `app/event_definitions`. Every `.rb` file under it, at any depth, is loaded.
40
+ - `helper_path` — the file the helper module is written to, such as
41
+ `lib/generated/marketing_events.rb`. `schema.json` is written to the same
42
+ directory.
43
+ - `root_module` — the Ruby constant name that wraps the helpers, such as
44
+ `MarketingEvents`.
45
+ 4. Add these lines to the project's `Rakefile`, with the three values from step 3:
46
+
47
+ ```ruby
48
+ require "event_engine/definition"
49
+ load "tasks/event_definition.rake"
50
+
51
+ EventEngine::Definition.configure do |config|
52
+ config.definitions_path = "app/event_definitions"
53
+ config.helper_path = "lib/generated/marketing_events.rb"
54
+ config.root_module = "MarketingEvents"
55
+ end
56
+ ```
57
+
58
+ The paths are relative to the directory rake is run from, so run it from the
59
+ project root.
60
+ 5. Ask the developer whether any event declares a subject. If one does, add
61
+ `config.subject_registry = <a registry naming every subject used>` inside the
62
+ `configure` block. Writing that registry is the develop local's job. Without
63
+ it, generation raises an unknown subject error.
64
+ 6. Make sure the helper is required once at boot, since it registers its pack
65
+ when it is required.
66
+ - Rails app: in `config/application.rb`, add the helper's directory to the
67
+ autoload ignore list, such as
68
+ `config.autoload_lib(ignore: %w[assets tasks generated])`, because the
69
+ helper's file path does not match the constant it defines and eager loading
70
+ fails in production. Then in `config/initializers/event_engine.rb`, require
71
+ the helper, such as
72
+ `require Rails.root.join("lib/generated/marketing_events")`.
73
+ - Pack gem: ask the developer which of the pack's files should require the
74
+ helper, and add the `require` there.
75
+ 7. Run `bundle exec rake event_definition:generate`. It creates the helper's
76
+ directory if missing and writes two files:
77
+ - the helper module at `helper_path`,
78
+ - `schema.json` next to it.
79
+ 8. Commit both generated files.
80
+
81
+ ## Conventions
82
+
83
+ - A successful run prints two lines, `Wrote <root_module> helper to
84
+ <helper_path>` and `Wrote <root_module> schema to <dir>/schema.json`. Confirm
85
+ both files exist.
86
+ - Re-run `rake event_definition:generate` after every change to a definition and
87
+ commit the result. Never edit the generated files by hand; each run overwrites
88
+ them.
89
+ - With no definitions under `definitions_path`, the task still runs and writes an
90
+ empty module and an empty `schema.json`.
91
+ - Writing event definitions, subjects and lifecycle families, and choosing where
92
+ raised events go, is out of scope here and belongs to the develop local.
@@ -0,0 +1,29 @@
1
+ scope: declaring domain events with a plain-Ruby DSL and generating a pack's typed helper module and committed schema.json from them
2
+
3
+ install:
4
+ - gem "event_engine-event_definition"
5
+ - require "event_engine/definition"
6
+ - load "tasks/event_definition.rake"
7
+ - EventEngine::Definition.configure
8
+ - rake event_definition:generate
9
+
10
+ develop:
11
+ - EventEngine::EventDefinition
12
+ - EventEngine::LifecycleDefinition
13
+ - EventEngine::SubjectRegistry.define
14
+ - EventEngine::EventDefinition.schema
15
+ - EventEngine::Definition.publisher=
16
+ - EventEngine::Definition.packs
17
+ - EventEngine::Definition.pack_schema_paths
18
+
19
+ sources:
20
+ - lib/event_engine/definition.rb
21
+ - lib/event_engine/definition/configuration.rb
22
+ - lib/tasks/event_definition.rake
23
+ - lib/event_engine/event_definition.rb
24
+ - lib/event_engine/event_definition/inputs.rb
25
+ - lib/event_engine/event_definition/payloads.rb
26
+ - lib/event_engine/event_definition/schemas.rb
27
+ - lib/event_engine/lifecycle_definition.rb
28
+ - lib/event_engine/subject_registry.rb
29
+ - event_engine-event_definition.gemspec
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_engine-event_definition
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - tylercschneider
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-07-29 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: 'The plain-Ruby foundation of the EventEngine pipeline: the EventDefinition
14
13
  DSL and the shared schema-contract value objects, with no Rails dependency. Lightweight
@@ -20,7 +19,11 @@ executables: []
20
19
  extensions: []
21
20
  extra_rdoc_files: []
22
21
  files:
22
+ - ".claude/agents/the_local-develop.md"
23
+ - ".claude/agents/the_local-info.md"
24
+ - ".claude/agents/the_local-install.md"
23
25
  - CHANGELOG.md
26
+ - CLAUDE.md
24
27
  - LICENSE.txt
25
28
  - README.md
26
29
  - Rakefile
@@ -44,6 +47,10 @@ files:
44
47
  - lib/event_engine/subject_registry.rb
45
48
  - lib/tasks/event_definition.rake
46
49
  - sig/event_engine/definition.rbs
50
+ - the_local/agents/event_engine-event_definition-develop.md
51
+ - the_local/agents/event_engine-event_definition-info.md
52
+ - the_local/agents/event_engine-event_definition-install.md
53
+ - the_local/interface.yml
47
54
  homepage: https://eventengine.co
48
55
  licenses:
49
56
  - MIT
@@ -54,7 +61,6 @@ metadata:
54
61
  source_code_uri: https://github.com/DYB-Development/event_engine-event_definition
55
62
  bug_tracker_uri: https://github.com/DYB-Development/event_engine-event_definition/issues
56
63
  documentation_uri: https://github.com/DYB-Development/event_engine-event_definition#readme
57
- post_install_message:
58
64
  rdoc_options: []
59
65
  require_paths:
60
66
  - lib
@@ -69,8 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
75
  - !ruby/object:Gem::Version
70
76
  version: '0'
71
77
  requirements: []
72
- rubygems_version: 3.5.16
73
- signing_key:
78
+ rubygems_version: 4.0.20
74
79
  specification_version: 4
75
80
  summary: Plain-Ruby event-definition contract for the EventEngine pipeline
76
81
  test_files: []