Turns slices from an eventmodelers.ai board into Ruby, using event sourcing on RailsEventStore — classic aggregate-per-stream, ActiveRecord read models, RSpec specifications generated from the board's own given/when/then scenarios.
npx @eventmodelers/cli init --stack rails-event-store \
--git https://github.com/RailsEventStore/eventmodelers-build-kit-res
.claude/skills/build-* |
three skills: state-change, state-view, automation |
.build-kit/CLAUDE.md |
the blueprint — "how we build things here" |
.build-kit/AGENTS.md |
seeded RES learnings, read before every slice |
.build-kit/lib/*.md |
the agent loop prompts |
| a Rails 8 app | event store wired, migration, install smoke test, event browser at /res — no domain code |
The shared skills — connect, learn-eventmodelers-api, load-slice,
update-slice-status, request-feedback — come from the CLI itself.
Nothing. templates/root/ is a complete, runnable Rails 8 app — there is
no rails new to do first. It uses SQLite so bundle exec rspec works with
nothing else installed.
Read INSTALL.md, which lands in your project root: rename the app, bundle install, bin/rails db:prepare, bundle exec rspec.
slice.json has |
skill |
|---|---|
non-empty processors |
build-automation |
non-empty readmodels |
build-state-view |
default (has commands / events) |
build-state-change |
An events[] element whose own context is "EXTERNAL" is an inbound event
from another system — build-state-change covers it in its own section rather
than as a separate skill.
| on the board | in the code |
|---|---|
context |
a Rails bounded context directory, made by RES's own rails_event_store:bounded_context generator |
events[] |
class Deposited < RubyEventStore::Event |
aggregate on an element |
an AggregateRoot class — the only place invariants live |
idAttribute: true |
the id in the stream name, "Banking::Wallet$<id>" |
commands[] |
a Data.define command plus a handler that loads, calls, stores |
readmodels[] |
an ActiveRecord table, written by a subscriber |
processors[] |
a subscriber that issues a command |
specifications[] |
one RSpec example each, using the board's own example data |
screens[] |
a written brief under docs/screens/, never an invented view |
The last one is deliberate: slice.json carries a screen as metadata and
prose, not as a design. The scaffold is --api and this kit builds domain code.
This kit uses classic aggregate-per-stream. That is RES's documented, mainstream
idiom, it maps idAttribute straight onto a stream name, and it uses the
aggregate / createsAggregate fields the board already exports — which a
DCB-based kit has to throw away.
RES can do dynamic consistency boundaries too. That is a different blueprint and belongs in a separate kit rather than as a second mode in this one, because the blueprint's only value is being unambiguous.
- No worked domain example in the scaffold. Like the official Nebulit stacks, this ships infrastructure only — the first slice is the first bounded context. The skills carry exact paths and full templates instead.
- No worked automation.
build-automationdescribes the shape, but no automation slice has been built with this kit yet — state-change and state-view have. - No stateful process managers. A slice needing correlation across several
events escalates via
request-feedbackrather than pulling inruby_event_store-process_manageron its own. - No
piihandling. RES has no per-field encryption layer here. The flag is carried into the event's documentation comment and otherwise not acted on. - No views. See above.
Ruby >= 3.3 (RES 3.x requires it), Rails 8.x, RailsEventStore ~> 3.1.
MIT — see LICENSE.
The loop prompts in templates/build-kit/lib/ are based on the blank-kit
templates shipped with
@eventmodelers/cli
(MIT, © Martin Dilger).