seams 0.1.0 → 0.2.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.
Files changed (222) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +212 -12
  3. data/README.md +235 -82
  4. data/lib/generators/seams/accounts/accounts_generator.rb +31 -3
  5. data/lib/generators/seams/accounts/templates/app/controllers/memberships_controller.rb.tt +100 -0
  6. data/lib/generators/seams/accounts/templates/app/views/memberships/index.html.erb.tt +52 -0
  7. data/lib/generators/seams/accounts/templates/config/routes.rb.tt +10 -4
  8. data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +35 -2
  9. data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +13 -0
  10. data/lib/generators/seams/accounts/templates/spec/runtime/authorization_spec.rb.tt +102 -0
  11. data/lib/generators/seams/accounts/templates/spec/runtime/memberships_flow_spec.rb.tt +145 -0
  12. data/lib/generators/seams/admin/admin_generator.rb +51 -4
  13. data/lib/generators/seams/admin/templates/README.md.tt +33 -3
  14. data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +156 -8
  15. data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +9 -1
  16. data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +9 -1
  17. data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +6 -0
  18. data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +7 -1
  19. data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +6 -0
  20. data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +6 -0
  21. data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +6 -0
  22. data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +6 -0
  23. data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +6 -0
  24. data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +6 -0
  25. data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +12 -2
  26. data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +7 -1
  27. data/lib/generators/seams/admin/templates/app/fields/admin/fields/belongs_to.rb.tt +12 -0
  28. data/lib/generators/seams/admin/templates/app/fields/admin/fields/dashboard_option.rb.tt +22 -0
  29. data/lib/generators/seams/admin/templates/app/fields/admin/fields/has_many.rb.tt +12 -0
  30. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +4 -0
  31. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +6 -0
  32. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +51 -10
  33. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +4 -0
  34. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +6 -0
  35. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +6 -0
  36. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +6 -0
  37. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +6 -0
  38. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +6 -0
  39. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +7 -0
  40. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +6 -0
  41. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +6 -0
  42. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +6 -0
  43. data/lib/generators/seams/admin/templates/app/views/seams/admin/application/_index_header.html.erb.tt +57 -0
  44. data/lib/generators/seams/admin/templates/app/views/seams/admin/application/_navigation.html.erb.tt +14 -0
  45. data/lib/generators/seams/admin/templates/config/routes.rb.tt +24 -14
  46. data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +3 -0
  47. data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +3 -1
  48. data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +4 -2
  49. data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +1 -2
  50. data/lib/generators/seams/auth/auth_generator.rb +1 -2
  51. data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +3 -0
  52. data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +4 -0
  53. data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +5 -0
  54. data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +5 -0
  55. data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +5 -1
  56. data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +12 -1
  57. data/lib/generators/seams/auth/templates/lib/engine.rb.tt +27 -0
  58. data/lib/generators/seams/billing/billing_generator.rb +4 -3
  59. data/lib/generators/seams/billing/templates/README.md.tt +26 -0
  60. data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +2 -0
  61. data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +2 -0
  62. data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +8 -7
  63. data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +9 -7
  64. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +18 -3
  65. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +7 -21
  66. data/lib/generators/seams/billing/templates/lib/engine.rb.tt +15 -0
  67. data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +10 -10
  68. data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +40 -14
  69. data/lib/generators/seams/billing/templates/lib/stripe/payload.rb.tt +90 -0
  70. data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +11 -1
  71. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +1 -0
  72. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +1 -0
  73. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +15 -4
  74. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +7 -1
  75. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +18 -1
  76. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +26 -5
  77. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +16 -2
  78. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +16 -2
  79. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +17 -2
  80. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +16 -2
  81. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +16 -2
  82. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +1 -0
  83. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +1 -0
  84. data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +47 -1
  85. data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +39 -1
  86. data/lib/generators/seams/design/design_generator.rb +647 -0
  87. data/lib/generators/seams/design/templates/README.md.tt +129 -0
  88. data/lib/generators/seams/design/templates/app/assets/tailwind/_tokens.css +322 -0
  89. data/lib/generators/seams/design/templates/app/assets/tailwind/themes/_quire.css +42 -0
  90. data/lib/generators/seams/design/templates/app/controllers/design/dashboard_controller.rb.tt +30 -0
  91. data/lib/generators/seams/design/templates/app/controllers/design/guide_controller.rb.tt +28 -0
  92. data/lib/generators/seams/design/templates/app/form_builders/design/form_builder.rb.tt +90 -0
  93. data/lib/generators/seams/design/templates/app/helpers/design/ui_helper.rb.tt +37 -0
  94. data/lib/generators/seams/design/templates/app/views/design/dashboard/index.html.erb.tt +38 -0
  95. data/lib/generators/seams/design/templates/app/views/design/guide/index.html.erb.tt +19 -0
  96. data/lib/generators/seams/design/templates/app/views/layouts/application.html.erb.tt +61 -0
  97. data/lib/generators/seams/design/templates/app/views/layouts/design/guide.html.erb.tt +21 -0
  98. data/lib/generators/seams/design/templates/app/views/ui/_banner.html.erb.tt +10 -0
  99. data/lib/generators/seams/design/templates/app/views/ui/_breadcrumb.html.erb.tt +8 -0
  100. data/lib/generators/seams/design/templates/app/views/ui/_build_row.html.erb.tt +14 -0
  101. data/lib/generators/seams/design/templates/app/views/ui/_button.html.erb.tt +4 -0
  102. data/lib/generators/seams/design/templates/app/views/ui/_card.html.erb.tt +2 -0
  103. data/lib/generators/seams/design/templates/app/views/ui/_chapter_row.html.erb.tt +11 -0
  104. data/lib/generators/seams/design/templates/app/views/ui/_checkbox.html.erb.tt +12 -0
  105. data/lib/generators/seams/design/templates/app/views/ui/_counter.html.erb.tt +2 -0
  106. data/lib/generators/seams/design/templates/app/views/ui/_data_table.html.erb.tt +25 -0
  107. data/lib/generators/seams/design/templates/app/views/ui/_dialog.html.erb.tt +14 -0
  108. data/lib/generators/seams/design/templates/app/views/ui/_diff.html.erb.tt +12 -0
  109. data/lib/generators/seams/design/templates/app/views/ui/_drawer.html.erb.tt +7 -0
  110. data/lib/generators/seams/design/templates/app/views/ui/_empty.html.erb.tt +5 -0
  111. data/lib/generators/seams/design/templates/app/views/ui/_field.html.erb.tt +14 -0
  112. data/lib/generators/seams/design/templates/app/views/ui/_icon.html.erb.tt +2 -0
  113. data/lib/generators/seams/design/templates/app/views/ui/_icon_sprite.html.erb.tt +22 -0
  114. data/lib/generators/seams/design/templates/app/views/ui/_input_group.html.erb.tt +11 -0
  115. data/lib/generators/seams/design/templates/app/views/ui/_kbd.html.erb.tt +4 -0
  116. data/lib/generators/seams/design/templates/app/views/ui/_menu.html.erb.tt +18 -0
  117. data/lib/generators/seams/design/templates/app/views/ui/_meter.html.erb.tt +15 -0
  118. data/lib/generators/seams/design/templates/app/views/ui/_note.html.erb.tt +2 -0
  119. data/lib/generators/seams/design/templates/app/views/ui/_outline.html.erb.tt +9 -0
  120. data/lib/generators/seams/design/templates/app/views/ui/_pagination.html.erb.tt +12 -0
  121. data/lib/generators/seams/design/templates/app/views/ui/_panel.html.erb.tt +2 -0
  122. data/lib/generators/seams/design/templates/app/views/ui/_popover.html.erb.tt +2 -0
  123. data/lib/generators/seams/design/templates/app/views/ui/_radio.html.erb.tt +5 -0
  124. data/lib/generators/seams/design/templates/app/views/ui/_savestate.html.erb.tt +5 -0
  125. data/lib/generators/seams/design/templates/app/views/ui/_segmented.html.erb.tt +7 -0
  126. data/lib/generators/seams/design/templates/app/views/ui/_stepper.html.erb.tt +12 -0
  127. data/lib/generators/seams/design/templates/app/views/ui/_switch.html.erb.tt +6 -0
  128. data/lib/generators/seams/design/templates/app/views/ui/_tag.html.erb.tt +2 -0
  129. data/lib/generators/seams/design/templates/app/views/ui/_toast.html.erb.tt +12 -0
  130. data/lib/generators/seams/design/templates/app/views/ui/_toolbar.html.erb.tt +15 -0
  131. data/lib/generators/seams/design/templates/app/views/ui/previews/_banner.html.erb.tt +8 -0
  132. data/lib/generators/seams/design/templates/app/views/ui/previews/_breadcrumb.html.erb.tt +5 -0
  133. data/lib/generators/seams/design/templates/app/views/ui/previews/_build_row.html.erb.tt +6 -0
  134. data/lib/generators/seams/design/templates/app/views/ui/previews/_button.html.erb.tt +9 -0
  135. data/lib/generators/seams/design/templates/app/views/ui/previews/_card.html.erb.tt +12 -0
  136. data/lib/generators/seams/design/templates/app/views/ui/previews/_chapter_row.html.erb.tt +6 -0
  137. data/lib/generators/seams/design/templates/app/views/ui/previews/_checkbox.html.erb.tt +6 -0
  138. data/lib/generators/seams/design/templates/app/views/ui/previews/_counter.html.erb.tt +6 -0
  139. data/lib/generators/seams/design/templates/app/views/ui/previews/_data_table.html.erb.tt +14 -0
  140. data/lib/generators/seams/design/templates/app/views/ui/previews/_dialog.html.erb.tt +8 -0
  141. data/lib/generators/seams/design/templates/app/views/ui/previews/_diff.html.erb.tt +10 -0
  142. data/lib/generators/seams/design/templates/app/views/ui/previews/_drawer.html.erb.tt +5 -0
  143. data/lib/generators/seams/design/templates/app/views/ui/previews/_empty.html.erb.tt +5 -0
  144. data/lib/generators/seams/design/templates/app/views/ui/previews/_field.html.erb.tt +5 -0
  145. data/lib/generators/seams/design/templates/app/views/ui/previews/_input_group.html.erb.tt +5 -0
  146. data/lib/generators/seams/design/templates/app/views/ui/previews/_kbd.html.erb.tt +5 -0
  147. data/lib/generators/seams/design/templates/app/views/ui/previews/_menu.html.erb.tt +9 -0
  148. data/lib/generators/seams/design/templates/app/views/ui/previews/_meter.html.erb.tt +6 -0
  149. data/lib/generators/seams/design/templates/app/views/ui/previews/_note.html.erb.tt +2 -0
  150. data/lib/generators/seams/design/templates/app/views/ui/previews/_outline.html.erb.tt +8 -0
  151. data/lib/generators/seams/design/templates/app/views/ui/previews/_pagination.html.erb.tt +5 -0
  152. data/lib/generators/seams/design/templates/app/views/ui/previews/_panel.html.erb.tt +5 -0
  153. data/lib/generators/seams/design/templates/app/views/ui/previews/_popover.html.erb.tt +2 -0
  154. data/lib/generators/seams/design/templates/app/views/ui/previews/_radio.html.erb.tt +6 -0
  155. data/lib/generators/seams/design/templates/app/views/ui/previews/_savestate.html.erb.tt +4 -0
  156. data/lib/generators/seams/design/templates/app/views/ui/previews/_segmented.html.erb.tt +15 -0
  157. data/lib/generators/seams/design/templates/app/views/ui/previews/_stepper.html.erb.tt +6 -0
  158. data/lib/generators/seams/design/templates/app/views/ui/previews/_switch.html.erb.tt +5 -0
  159. data/lib/generators/seams/design/templates/app/views/ui/previews/_tag.html.erb.tt +8 -0
  160. data/lib/generators/seams/design/templates/app/views/ui/previews/_toast.html.erb.tt +6 -0
  161. data/lib/generators/seams/design/templates/app/views/ui/previews/_toolbar.html.erb.tt +11 -0
  162. data/lib/generators/seams/design/templates/lib/design/components.rb.tt +22 -0
  163. data/lib/generators/seams/design/templates/lib/design.rb.tt +18 -0
  164. data/lib/generators/seams/design/templates/lib/engine.rb.tt +38 -0
  165. data/lib/generators/seams/design/templates/lib/generators/design/component/component_generator.rb.tt +52 -0
  166. data/lib/generators/seams/design/templates/lib/generators/design/component/templates/component.html.erb.tt +5 -0
  167. data/lib/generators/seams/design/templates/lib/generators/design/component/templates/preview.html.erb.tt +3 -0
  168. data/lib/generators/seams/design/templates/spec/runtime/design_boot_spec.rb.tt +79 -0
  169. data/lib/generators/seams/design/templates/spec/runtime/form_builder_spec.rb.tt +97 -0
  170. data/lib/generators/seams/design/templates/spec/runtime/guide_spec.rb.tt +40 -0
  171. data/lib/generators/seams/design/templates/spec/runtime/ui_components_spec.rb.tt +70 -0
  172. data/lib/generators/seams/engine/engine_generator.rb +6 -0
  173. data/lib/generators/seams/engine/templates/Gemfile.tt +1 -1
  174. data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
  175. data/lib/generators/seams/engine/templates/rubocop.yml.tt +9 -0
  176. data/lib/generators/seams/install/install_generator.rb +72 -6
  177. data/lib/generators/seams/install/templates/Dockerfile.tt +2 -2
  178. data/lib/generators/seams/install/templates/bin_seams.tt +7 -4
  179. data/lib/generators/seams/install/templates/ci.yml.tt +18 -8
  180. data/lib/generators/seams/install/templates/deploy.yml.tt +4 -2
  181. data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +1 -1
  182. data/lib/generators/seams/install/templates/docker-entrypoint.tt +4 -2
  183. data/lib/generators/seams/install/templates/herb.yml.tt +10 -0
  184. data/lib/generators/seams/install/templates/lefthook.yml.tt +25 -0
  185. data/lib/generators/seams/install/templates/ruby-version.tt +1 -1
  186. data/lib/generators/seams/install/templates/seams.rake.tt +13 -1
  187. data/lib/generators/seams/install/templates/strong_migrations.rb.tt +26 -0
  188. data/lib/generators/seams/notifications/notifications_generator.rb +3 -2
  189. data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +5 -13
  190. data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +12 -0
  191. data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +1 -0
  192. data/lib/generators/seams/notifications/templates/lib/preferences.rb.tt +36 -0
  193. data/lib/generators/seams/permissions/permissions_generator.rb +97 -0
  194. data/lib/generators/seams/permissions/templates/config/initializers/seams_permissions.rb.tt +35 -0
  195. data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +27 -11
  196. data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +5 -13
  197. data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +2 -4
  198. data/lib/generators/seams/teams/templates/lib/engine.rb.tt +13 -0
  199. data/lib/seams/cli/list.rb +15 -0
  200. data/lib/seams/cli/quality.rb +46 -5
  201. data/lib/seams/cli/resolve.rb +9 -5
  202. data/lib/seams/cli/test_changed.rb +5 -1
  203. data/lib/seams/cli.rb +20 -0
  204. data/lib/seams/configuration.rb +24 -1
  205. data/lib/seams/cops/no_cross_engine_dependency.rb +39 -7
  206. data/lib/seams/cops/no_cross_engine_model_access.rb +79 -1
  207. data/lib/seams/event_registry.rb +19 -0
  208. data/lib/seams/events/adapter.rb +9 -0
  209. data/lib/seams/events/adapters/active_support.rb +4 -0
  210. data/lib/seams/events/publisher.rb +29 -0
  211. data/lib/seams/events.rb +1 -0
  212. data/lib/seams/generators/dummy_app_writer.rb +21 -0
  213. data/lib/seams/generators/follow_up_generator.rb +1 -2
  214. data/lib/seams/generators/host_injector.rb +9 -2
  215. data/lib/seams/generators/splicer.rb +2 -2
  216. data/lib/seams/observability/adapter.rb +22 -0
  217. data/lib/seams/observability.rb +6 -0
  218. data/lib/seams/permission_registry.rb +66 -0
  219. data/lib/seams/permissions.rb +120 -0
  220. data/lib/seams/version.rb +1 -1
  221. data/lib/seams.rb +7 -0
  222. metadata +107 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48050ae5ddd2075cf436343c837ad75370660410f96c3a3d1c12e4d58612ec65
4
- data.tar.gz: debc50e9f6570fa7361e8a03d23bab7c1c84e30214cb4b49ff2e66b957e493d9
3
+ metadata.gz: '080facbda9aa03ebeec4885f3745dd0c100d698465dcfcd7bbf35c62dd634eff'
4
+ data.tar.gz: d9a6b438e0ff12c37ba4006e680a7d01f30358c1bb351cdf2e47fee9110f13be
5
5
  SHA512:
6
- metadata.gz: bd7f31ec043899f985949285a8f3b47cfd9fda83885d06c9f89fc5052c7374113427c054c7868501c34d484ae62c4f58377d915300777df0ff150ea7d34068b4
7
- data.tar.gz: ae8884205c3f95c991722517358897b1bb5ee559be3bca06d513f477e76e58fbb8bfeb4a20602be40517d51a8178e7e1d0cd2e744e069eefd1cae5b6950afa6e
6
+ metadata.gz: 1e607f46a0b749397ad5b7f7a8a1927dcd267ca14dbf77107c938269bd17c17d426ac38b98f88ba734c7d7a27adceb150973fa331e2d1b302515102b96afdccd
7
+ data.tar.gz: b708e636f64a8502215b811102640df371ed57846ecd54a44dbd8ea0391f84848f4f42350b3111ddab113eb96a19a75d529f19c45b8a0726b7e6e0cd4c62b9cd
data/CHANGELOG.md CHANGED
@@ -7,6 +7,206 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] — 2026-09-27
11
+
12
+ > [!WARNING]
13
+ > Breaking changes for hosts (pre-1.0, so this is a minor bump):
14
+ >
15
+ > - Generated billing code targets `stripe ~> 19.0` (was `~> 13.0`) and
16
+ > raises `Billing::GatewayError` where Stripe errors used to escape.
17
+ > Hosts that rescued `Stripe::*` from the generated services must rescue
18
+ > `Billing::GatewayError`. Match your Stripe webhook endpoint's API
19
+ > version to `2026-08-26.dahlia`.
20
+ > - Generated code is not rewritten by a gem upgrade. See "Fixed" for the
21
+ > manual steps (engine `spec/dummy/config/cable.yml`, admin engine).
22
+
23
+ ### Added
24
+
25
+ - Integration suite: a default `seams:install` host must resolve its
26
+ bundle on a multi-platform lockfile (x86_64-linux, aarch64-linux,
27
+ arm64-darwin, x86_64-darwin). Guards the #42 class, where a gem shipping
28
+ only some native platforms broke `bundle` on Rails 8 lockfiles. Completes
29
+ #48.
30
+ - Community and AI-era repository standards: `SUPPORT.md`, `GOVERNANCE.md`,
31
+ `MAINTAINERS.md`, `CITATION.cff`, `.editorconfig`, `.github/FUNDING.yml`,
32
+ an `llms.txt` machine-readable index, and `AGENTS.md` instructions for AI
33
+ coding agents. `doc/explanation/ARCHITECTURE.md` gains a "Where this design would
34
+ strain" section. Blank issues are now routed through the issue chooser.
35
+
36
+ - Install generator: ships an opinionated quality toolchain — a host is hardened
37
+ from the first CLI run. `seams:install` installs **strong_migrations**
38
+ (+ initializer, audits all migrations via `start_after = 0`) and **lefthook**
39
+ (`lefthook.yml`: pre-commit rubocop, pre-push specs) by default, and wires a
40
+ from-scratch `db:migrate` safety check into the generated CI. The rubocop/
41
+ brakeman/bundle-audit gems the generated CI shells out to are now injected
42
+ into the host Gemfile too. **herb** (HTML+ERB lint + `.herb.yml`) is available
43
+ but **off by default** — opt in with `--herb` — because its platform-specific
44
+ native gem breaks cold installs on multi-platform lockfiles (see commit
45
+ 6c16c52). strong_migrations and lefthook are opt-out via
46
+ `--no-strong-migrations` / `--no-lefthook`. Also closes two gaps where Seams
47
+ generated migrations that nothing validated — the host CI now vets them.
48
+ - Permissions engine (`bin/seams permissions`): generates a host-editable
49
+ role → ability grant map at `config/initializers/seams_permissions.rb`, backed
50
+ by `Seams::PermissionRegistry` / `Seams::Permissions` and the
51
+ `authorize_permission!` controller helper. See [doc/reference/PERMISSIONS.md].
52
+ - Admin engine (`bin/seams admin`): opt-in Administrate dashboards with a Pundit
53
+ `Platform`/`Tenant` policy split and an admin audit trail writing
54
+ `Core::AuditLog` rows. See [doc/explanation/ARCHITECTURE_WAVE_11.md].
55
+ - Design engine (`bin/seams design`, `--shell`): a themeable design system —
56
+ 33 `ui_*` components, Tailwind v4 `@theme` tokens + component CSS,
57
+ `Design::FormBuilder`, a `/design/guide` gallery, and a `design:component`
58
+ generator; `--shell` also generates an application layout and a starter
59
+ signed-in dashboard. See [doc/design-system/DESIGN_SYSTEM.md].
60
+
61
+ ### Changed
62
+
63
+ - Generated host pins: rspec-rails is injected as `">= 7.1", "< 9"` (was
64
+ `"~> 7.1"`) in the host Gemfile and each engine Gemfile, so Rails 7.2+
65
+ hosts get rspec-rails 8.x. The generated CI uses `actions/checkout@v7`
66
+ and a `postgres:18` service; the Kamal Postgres accessory comment moves
67
+ to `postgres:18` and its volume to `/var/lib/postgresql` (18's layout).
68
+ - Billing generator: the generated engine now targets stripe-ruby 19
69
+ (`gem "stripe", "~> 19.0"`, was `~> 13.0`), which pins Stripe API version
70
+ `2026-08-26.dahlia`. Existing hosts run `bundle update stripe` and should
71
+ move their webhook endpoint to the same API version (the handlers accept
72
+ both shapes meanwhile). Changes in the generated code:
73
+ - New `Billing::Stripe::Payload` reads Stripe objects and webhook hashes
74
+ alike. Handlers and `Invoices::SyncService` read the invoice's
75
+ subscription from `invoice.parent.subscription_details.subscription` and
76
+ the period end from `items.data[0].current_period_end` (both moved in
77
+ 2025-03-31.basil). The old top-level `invoice.subscription` and
78
+ `subscription.current_period_end` are still read as fallbacks.
79
+ - `invoice.paid` now stores Stripe's `status_transitions.paid_at` as
80
+ `paid_at` instead of the time the webhook arrived.
81
+ - Fixed `#dig` calls on SDK responses (`Gateways::Stripe#normalise_subscription`,
82
+ `Invoices::SyncService#paid_at_for`). `Stripe::StripeObject` has no `#dig`,
83
+ so these raised `NoMethodError` on real API responses.
84
+ - `Billing::Stripe::Client` re-raises every `Stripe::StripeError` as
85
+ `Billing::GatewayError` (original on `#cause`). Every generated caller
86
+ already rescued `Billing::GatewayError`, but the gem-backed client never
87
+ raised it, so Stripe errors escaped `StripeService`, the checkout and
88
+ portal services, and `CreateLifetimeSessionService`. Hosts that rescued
89
+ `Stripe::*` errors from these services should rescue
90
+ `Billing::GatewayError` instead.
91
+ - The webhook endpoint answers `400` (not `500`) to a signed v2 thin event;
92
+ stripe-ruby 19's `Stripe::Webhook.construct_event` raises `ArgumentError`
93
+ for those.
94
+ - Stripe event fixtures under `spec/fixtures/stripe/` use the dahlia shapes
95
+ and carry `api_version`.
96
+ - Dependencies: Ruby 4.0.7 (repo, CI, and the generated host's
97
+ `.ruby-version` / Dockerfile default), Rails 8.1.4, RuboCop 1.91 (+
98
+ rubocop-rails 2.38, rubocop-performance 1.27), SimpleCov 1.3, brakeman
99
+ 8.0.6, sqlite3 2.9.6, yard 0.9.45, and the docs-site on Astro 7.3 / Starlight 0.42. Patches the
100
+ websocket-driver 0.8.0 advisories (CVE-2026-54463/54464/54465/61666)
101
+ and the docs-site devalue, nanoid, and postcss advisories. CI moves to
102
+ `actions/setup-node@v7`.
103
+ - Documentation: `doc/` is now organised into Diátaxis folders —
104
+ `tutorials/`, `how-to/`, `reference/`, `design-system/`,
105
+ `explanation/`, plus `internal/` for maintainer-facing working
106
+ documents. Published URLs on the docs site are unchanged (the site
107
+ flattens the folders), and the site's in-page cross-links — which
108
+ previously shipped as raw `.md` hrefs and 404'd — now resolve to the
109
+ real page URLs.
110
+ - Engine generator: ApplicationController now requires authentication by default. Opt out via `skip_before_action :authenticate_identity!` in controllers serving public flows. [BREAKING for hosts that explicitly relied on engines being unauthenticated by default.]
111
+ - Notifications generator: preferences controller now uses an explicit
112
+ permit list from the channel/type registry rather than `permit!`.
113
+ Eliminates the brakeman mass-assignment warning shipped with generated
114
+ engines. Hosts that have already ejected
115
+ `app/controllers/notifications/preferences_controller.rb` will not
116
+ re-generate it (per the `template_unless_ejected` contract); to pick
117
+ up the new behaviour, replace
118
+ `params.require(:preferences).permit!` in the ejected copy with
119
+ `params.require(:preferences).permit(*Notifications::Preferences.allowed_keys)`
120
+ and require `notifications/preferences` from
121
+ `lib/notifications.rb`.
122
+ - Teams generator: `TeamsController` now includes `Teams::Authorization` and applies `require_team_member!` as a `before_action` on `show`, and `require_team_admin!` on `edit`, `update`, and `destroy`. Previously those actions had no resource-level membership guard, letting any authenticated identity act on any team by ID. The `require_team_member!` and `require_team_admin!` predicates in the concern now redirect to `teams_path` with an alert rather than returning a bare 403, matching the convention used by the other team controllers.
123
+
124
+ ### Fixed
125
+
126
+ - Admin engine UI: the sidebar and every New / show / edit / destroy link
127
+ were missing, because Administrate reads the host's routes and a mounted
128
+ engine's routes are not in them. `existing_action?` and a navigation
129
+ partial now read the engine's routes; `authorized_action?` uses the
130
+ policy of the resource being linked. An `_index_header` override builds
131
+ the New link from the controller. Creates now write an admin audit row
132
+ (Administrate's `create` never set `requested_resource`). The admin
133
+ README documents the authenticator and membership resolver that tenant
134
+ mode needs.
135
+ - `bin/audit` (the pre-push gate) failed from a linked git worktree:
136
+ the hook's exported `GIT_DIR` made `bundle-audit --update` pull its
137
+ advisory database against this repo. It now clears git's hook
138
+ variables for that command.
139
+ - gemspec: `required_ruby_version` is now `>= 3.3.0` (was `>= 4.0.0`), so
140
+ `bundle add seams` resolves on Ruby 3.3 and 3.4 hosts. CI runs the spec
141
+ suite on 3.3, 3.4, and 4.0. RuboCop targets 3.3 so 4.0-only syntax is
142
+ caught. Ruby 3.2 is end-of-life and SimpleCov 1.x requires 3.3.
143
+ Fixes #79.
144
+ - `host_inject_gem` dropped every version requirement after the first, so
145
+ a range like `(">= 7.1", "< 9")` was written as `">= 7.1"` only.
146
+ - Admin generator: the host no longer gets `mount Admin::Engine` and an
147
+ `Admin.configure` initializer stub from the base engine step. The engine
148
+ lives at `Seams::Admin::Engine`, so every host that ran `seams:admin`
149
+ raised `NameError` on boot. `seams:engine` gains `--skip-host-wiring`
150
+ for canonical generators that wire the host themselves. The admin engine
151
+ now runs in the rails-new integration suite.
152
+ - Admin engine: every dashboard request failed. Fixed the chain found by
153
+ driving a generated host over HTTP:
154
+ - routes point at `/admin/...` controllers (isolate_namespace resolved
155
+ `admin/identities` to `Seams::Admin::Admin::IdentitiesController`) and
156
+ sit in `scope as: :admin` so Administrate's `admin_*` helpers exist;
157
+ - the base controller includes `Auth::Authentication`, so
158
+ `current_identity` resolves; signed-out visitors are redirected to
159
+ sign-in, non-staff get 403;
160
+ - policies resolve as `Admin::<Platform|Tenant>::<Resource>Policy` via
161
+ Pundit's `policy_class:` (the array lookup produced
162
+ `Module::Auth::IdentityPolicy`);
163
+ - dashboards declare `self.model`; association fields take a
164
+ `dashboard:` option (`Admin::Fields::BelongsTo` / `HasMany`);
165
+ - the `Admin::*` controllers get the engine's route helpers, the
166
+ `accounts`/`teams` membership route-key clash is dispatched by record
167
+ class, flash messages use the real model name, and `verify_authorized`
168
+ no longer names a non-action (ActionNotFound on Rails 7.1+);
169
+ - the admin dummy app registers the abilities its tenant policies
170
+ check, and the tenant role specs use a concrete policy.
171
+ The integration suite signs in as anonymous, non-staff, and staff and
172
+ asserts every dashboard's index and new page plus a create.
173
+ - Generated `bin/docker-entrypoint` now runs `db:prepare` for the
174
+ Dockerfile's own `bundle exec rails server -b 0.0.0.0` command. It only
175
+ checked the first three arguments, where `server` never appears, so a
176
+ seams-built container started against an empty database. Verified by
177
+ building and running a generated host (all engines) against Postgres 18.
178
+ - Engine dummy apps now ship `spec/dummy/config/cable.yml` (test adapter).
179
+ solid_cable 4.1 reads `config_for("cable")` at boot, so every generated
180
+ engine spec suite failed to load in a host that bundles solid_cable.
181
+ Engines generated before this release need the file added by hand:
182
+ `engines/*/spec/dummy/config/cable.yml` containing `test:` /
183
+ ` adapter: test`.
184
+ - Nightly link check: lychee retries each link 5 times (10s apart) before
185
+ reporting it, and a failing run updates the open `broken-link` issue
186
+ instead of filing a new one. Closes #93 and #109, both transient
187
+ connection resets on contributor-covenant.org.
188
+ - Teams generator: `InvitationsController#accept` now redirects unauthenticated requests to the sign-in path (stashing the token in the session under `pending_invitation_token`) instead of raising `ActiveRecord::RecordInvalid`. The host's sign-in flow is responsible for reading `return_to` and redirecting back after authentication.
189
+ - Teams generator: `InvitationsController#accept` now verifies that the signed-in identity's email matches the invitation email (case-insensitively) before creating the membership. Previously any authenticated identity holding a valid token could accept the invitation under a different account.
190
+ - Billing generator: `scoped_invoices` / `scoped_subscriptions` now return
191
+ `.none` when the current customer ref is nil. The previous behaviour was
192
+ an **information disclosure**: Rails translates
193
+ `.where(customer_ref: nil)` to `WHERE customer_ref IS NULL`, which
194
+ matches every invoice and subscription row whose `customer_ref` has not
195
+ yet been linked to a Stripe customer — typically including manual,
196
+ draft, or test records seeded by other staff. The fix returns an empty
197
+ relation when there is no customer to scope by.
198
+
199
+ Hosts that have already ejected
200
+ `app/controllers/billing/invoices_controller.rb` or
201
+ `subscriptions_controller.rb` will not re-generate them (per the
202
+ `template_unless_ejected` contract). To pick up the fix, prepend each
203
+ scope helper with:
204
+
205
+ return Billing::Invoice.none if current_billing_customer_ref.nil?
206
+
207
+ (likewise `Billing::Subscription.none` in the subscriptions helper)
208
+ before the existing `.where(customer_ref: …)` line.
209
+
10
210
  ## [0.1.0] — 2026-05-10
11
211
 
12
212
  First public release on rubygems.org. The cumulative changelog from
@@ -21,9 +221,9 @@ notifications, billing, teams) can mount an Administrate-backed admin
21
221
  surface at `/admin` covering all twelve canonical seams models with a
22
222
  single command. Dashboards, two-mode authorization, audit-log
23
223
  auto-write, and the four config knobs all ship out of the box. See
24
- [`doc/ARCHITECTURE_WAVE_11.md`](doc/ARCHITECTURE_WAVE_11.md) for the
224
+ [`doc/explanation/ARCHITECTURE_WAVE_11.md`](doc/explanation/ARCHITECTURE_WAVE_11.md) for the
25
225
  new architecture material; the framework selection rationale lives in
26
- [`proposals/admin_engine_administrate.md`](proposals/admin_engine_administrate.md).
226
+ `proposals/admin_engine_administrate.md`.
27
227
 
28
228
  #### Added
29
229
 
@@ -97,8 +297,8 @@ follow-up generators target those points to add features without
97
297
  re-templating the whole engine; and `bin/seams resolve --eject`
98
298
  marks any single host file as host-owned so subsequent
99
299
  `bin/seams <engine>` runs leave it alone. See
100
- [`doc/ARCHITECTURE_WAVE_10.md`](doc/ARCHITECTURE_WAVE_10.md) for the
101
- addendum and [`doc/WRITING_FOLLOW_UP_GENERATORS.md`](doc/WRITING_FOLLOW_UP_GENERATORS.md)
300
+ [`doc/explanation/ARCHITECTURE_WAVE_10.md`](doc/explanation/ARCHITECTURE_WAVE_10.md) for the
301
+ addendum and [`doc/how-to/WRITING_FOLLOW_UP_GENERATORS.md`](doc/how-to/WRITING_FOLLOW_UP_GENERATORS.md)
102
302
  for the author's guide.
103
303
 
104
304
  #### Added
@@ -116,8 +316,8 @@ for the author's guide.
116
316
  core Configuration class). Marker shape:
117
317
  `# seams:insertion-point <engine>.<area>.<scope>` — ASCII only,
118
318
  greppable, parses through every Ruby linter. Format spec in
119
- [`doc/INSERTION_POINTS.md`](doc/INSERTION_POINTS.md); canonical list
120
- in [`doc/INSERTION_POINTS_CATALOGUE.md`](doc/INSERTION_POINTS_CATALOGUE.md).
319
+ [`doc/reference/INSERTION_POINTS.md`](doc/reference/INSERTION_POINTS.md); canonical list
320
+ in [`doc/reference/INSERTION_POINTS_CATALOGUE.md`](doc/reference/INSERTION_POINTS_CATALOGUE.md).
121
321
  - **`bin/seams resolve` CLI.** Three modes:
122
322
  - `--eject <engine>/<file>` — prepends a
123
323
  `# seams:ejected from <engine>.<path>` header to the host file;
@@ -144,12 +344,12 @@ for the author's guide.
144
344
  `auth.configuration.oauth_providers` marker, and writes a matching
145
345
  spec. Idempotent on rerun.
146
346
  - **Documentation.**
147
- [`doc/INSERTION_POINTS.md`](doc/INSERTION_POINTS.md) (format spec),
148
- [`doc/INSERTION_POINTS_CATALOGUE.md`](doc/INSERTION_POINTS_CATALOGUE.md)
347
+ [`doc/reference/INSERTION_POINTS.md`](doc/reference/INSERTION_POINTS.md) (format spec),
348
+ [`doc/reference/INSERTION_POINTS_CATALOGUE.md`](doc/reference/INSERTION_POINTS_CATALOGUE.md)
149
349
  (canonical 33-marker list),
150
- [`doc/WRITING_FOLLOW_UP_GENERATORS.md`](doc/WRITING_FOLLOW_UP_GENERATORS.md)
350
+ [`doc/how-to/WRITING_FOLLOW_UP_GENERATORS.md`](doc/how-to/WRITING_FOLLOW_UP_GENERATORS.md)
151
351
  (author's guide), and
152
- [`doc/ARCHITECTURE_WAVE_10.md`](doc/ARCHITECTURE_WAVE_10.md)
352
+ [`doc/explanation/ARCHITECTURE_WAVE_10.md`](doc/explanation/ARCHITECTURE_WAVE_10.md)
153
353
  (architecture addendum with splice + eject sequence diagrams).
154
354
  - **Host-facing surfaces updated.**
155
355
  `bin/seams help` and `bin/seams resolve --help` document the new
@@ -161,7 +361,7 @@ for the author's guide.
161
361
 
162
362
  Replaces the conflated `Auth::User` (which owned credentials AND
163
363
  tenant-membership concepts) with three peer engines, each with one
164
- clear responsibility. See `doc/UPGRADING_FROM_WAVE_8.md` for the
364
+ clear responsibility. See `doc/how-to/UPGRADING_FROM_WAVE_8.md` for the
165
365
  migration story.
166
366
 
167
367
  #### Added
@@ -245,7 +445,7 @@ migration story.
245
445
  state holders are intentionally readable from any engine. The
246
446
  cop now treats `Current` as a framework-level constant and
247
447
  exempts it from the boundary rule. Documented in
248
- `doc/CURRENT_ATTRIBUTES.md` and inline in the cop's docstring.
448
+ `doc/reference/CURRENT_ATTRIBUTES.md` and inline in the cop's docstring.
249
449
  - `Teams::AccountScoped` now references `Teams::Current.team`
250
450
  (previously a bare `Current.team` that resolved to nothing
251
451
  inside `module Teams` — silently making the default_scope a
data/README.md CHANGED
@@ -1,104 +1,257 @@
1
1
  # Seams
2
2
 
3
- > A CLI framework that generates modular Rails engines.
3
+ [![Gem Version](https://img.shields.io/gem/v/seams.svg)](https://rubygems.org/gems/seams)
4
+ [![CI](https://github.com/Davidslv/seams/actions/workflows/ci.yml/badge.svg)](https://github.com/Davidslv/seams/actions/workflows/ci.yml)
5
+ [![Docs site](https://img.shields.io/badge/docs-davidslv.github.io%2Fseams-blue.svg)](https://davidslv.github.io/seams/)
6
+ [![API docs](https://img.shields.io/badge/api-rubydoc.info-blue.svg)](https://rubydoc.info/gems/seams)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
4
8
 
5
- Seams gives you the architectural benefits of microservices — clear
6
- boundaries, independent testing, team autonomy — without the
7
- operational cost. You ship a single Rails app. You think in
8
- independent engines.
9
+ Seams generates modular Rails engines inside your Rails app.
9
10
 
10
- > Seams materialises the patterns David Silva teaches in **[Modular
11
- > Rails: Architecture for the Long Game](https://davidslv.uk/modular-rails/)**.
12
- > The book is the artefact; this gem is the executable shorthand. See
13
- > [seams-example](https://github.com/Davidslv/seams-example) for a
14
- > reference host that wires every canonical engine end-to-end.
11
+ You ship one Rails app. Inside it, each feature (auth, accounts, billing, teams, and so on) lives in its own engine under `engines/`. Each engine has its own models, tests, and boundaries. Engines talk to each other through events, not by reaching into each other's code. Custom RuboCop cops enforce that.
15
12
 
16
- ## Quick start
13
+ Every generated file is plain Rails code in your repo. You can read it, change it, or delete it. Nothing is hidden behind the gem.
17
14
 
18
- ```ruby
19
- # Gemfile
20
- gem "seams"
21
- ```
15
+ > [!NOTE]
16
+ > Seams is the executable companion to the book **[Modular Rails: Architecture for the Long Game](https://davidslv.uk/modular-rails/)**. The full guides live on the **[documentation site](https://davidslv.github.io/seams/)**. [seams-example](https://github.com/Davidslv/seams-example) is a reference host with every engine wired up.
17
+
18
+ ## Requirements
19
+
20
+ | | Supported | Tested in CI |
21
+ | --- | --- | --- |
22
+ | Ruby | 3.3 or newer | 3.3, 3.4, 4.0.7 |
23
+ | Rails | 7.1 or newer (below 9) | 8.1.4 |
24
+ | Database | PostgreSQL | PostgreSQL 18 |
25
+
26
+ > [!IMPORTANT]
27
+ > **PostgreSQL is required.** The generated engines use `jsonb` columns, and each engine's test suite runs against Postgres. SQLite and MySQL are not supported.
28
+
29
+ ## Installation
30
+
31
+ > [!WARNING]
32
+ > **Install from GitHub for now.** The only release on RubyGems is `0.1.0` (May 2026). It requires Ruby 4.0 or newer and predates many fixes, including the admin engine working at all and Ruby 3.3 support. Until a new version is released, point your Gemfile at the repository:
33
+ >
34
+ > ```ruby
35
+ > # Gemfile
36
+ > gem "seams", github: "Davidslv/seams"
37
+ > ```
38
+ >
39
+ > Once a newer version is on RubyGems, use `gem "seams"` instead.
40
+
41
+ Then install the framework:
22
42
 
23
43
  ```bash
24
44
  bundle install
25
45
  bin/rails generate seams:install
26
- bin/seams auth
27
- bin/seams accounts
28
- bin/seams notifications
29
- bin/seams billing
30
- bin/seams teams
31
- bin/seams list
32
46
  ```
33
47
 
34
- That's auth, tenant boundary, transactional email/SMS, Stripe
35
- subscriptions, and multi-tenant teams generated as five real Rails
36
- engines under `engines/`. Every file is yours to edit. Nothing is
37
- hidden behind the gem.
48
+ `seams:install` adds the framework files, a CI workflow, and a `bin/seams` command. Every step after this uses `bin/seams`.
49
+
50
+ ## Quick start
51
+
52
+ Generate the engines you need. The order matters, because later engines build on earlier ones:
53
+
54
+ ```bash
55
+ bin/seams core # shared building blocks (always first)
56
+ bin/seams auth # sign-in, sessions, OAuth, API tokens
57
+ bin/seams accounts # the tenant (Account) and its members
58
+ bin/seams notifications # in-app, email, and SMS notifications
59
+ bin/seams billing # Stripe subscriptions
60
+ bin/seams teams # optional teams inside an account
61
+ bin/seams design --shell # UI components and an app layout
62
+
63
+ bundle install
64
+ bin/rails db:migrate
65
+ bin/seams list # show engines, their events, and subscribers
66
+ ```
67
+
68
+ > [!TIP]
69
+ > New to Seams? Follow **[Getting Started](doc/tutorials/GETTING_STARTED.md)**. It goes step by step from `bundle install` to a running app.
38
70
 
39
71
  ## What you get
40
72
 
41
- - `bin/seams install` — adds the framework + CI workflow + bin/seams wrapper
42
- - `bin/seams engine <name>` — generic engine scaffold
43
- - `bin/seams core` — canonical Core engine (Current attributes, AuditLog, TenantScoped, EmailFormatValidator)
44
- - `bin/seams auth` — canonical Auth engine (Identity, Session, OAuth, API tokens, GDPR-encrypted PII)
45
- - `bin/seams accounts` — canonical Accounts engine (Account tenant, Membership, AccountScoped, system actor)
46
- - `bin/seams notifications` — canonical Notifications engine (STI strategies, ActionCable bell, TypeRegistry, --channels flag)
47
- - `bin/seams billing` — canonical Billing engine (official Stripe gem, 13-handler webhook router, Lifetime Deals)
48
- - `bin/seams teams` — canonical Teams engine (Team, Membership, Invitation, AccountScoped, --with flag)
49
- - `bin/seams remove <name>` — clean removal + sibling cleanup + drop-table migration
50
- - `bin/seams list` — engines, the events they emit, and what they subscribe to
51
- - `bin/seams resolve` — eject host files / list insertion-point markers / list ejected files
52
- - `bin/rails generate seams:auth:add_oauth_provider <name>` — first follow-up generator (adds an OAuth provider adapter to an installed Auth engine)
53
- - Four custom RuboCop cops that enforce cross-engine boundaries
54
- - A GitHub Actions CI workflow that runs every engine's specs in parallel
73
+ ### The engines
74
+
75
+ | Command | What it generates |
76
+ | --- | --- |
77
+ | `bin/seams core` | Shared basics: per-request `Current` attributes, an audit log, tenant scoping, an email validator. |
78
+ | `bin/seams auth` | `Identity` (the person signing in), sessions, OAuth providers, API tokens. Personal data is encrypted at rest. |
79
+ | `bin/seams accounts` | `Account` (the tenant), memberships with roles, account scoping. |
80
+ | `bin/seams notifications` | Notifications over in-app, email, and SMS. Choose channels with `--channels in_app,email,sms` (default: all). |
81
+ | `bin/seams billing` | Stripe subscriptions using the official `stripe` gem (19.x), a webhook router with 13 handlers, and lifetime deals. |
82
+ | `bin/seams teams` | Teams, team memberships, and invitations. Choose features with `--with invitations,roles` (default: all). |
83
+ | `bin/seams admin` | An admin area built on Administrate and Pundit. Optional. See [Admin engine](#admin-engine) below. |
84
+ | `bin/seams design` | A design system: 33 `ui_*` components, Tailwind v4 theme tokens, a form builder, and a `/design/guide` gallery. `--shell` also adds an app layout and a starter dashboard. |
85
+ | `bin/seams permissions` | An editable map of which roles can do what, in `config/initializers/seams_permissions.rb`. See [Permissions](doc/reference/PERMISSIONS.md). |
86
+
87
+ ### Framework and tools
88
+
89
+ | Command | What it does |
90
+ | --- | --- |
91
+ | `bin/rails generate seams:install` | Installs the framework, the CI workflow, and `bin/seams`. |
92
+ | `bin/seams engine <name>` | Generates an empty engine for your own feature. |
93
+ | `bin/seams remove <name>` | Removes an engine, cleans up references to it, and adds a migration that drops its tables. |
94
+ | `bin/seams list` | Lists engines, the events they publish, and who subscribes to them. |
95
+ | `bin/seams test <engine>` | Runs one engine's tests. |
96
+ | `bin/seams quality <engine>` | Runs RuboCop on one engine. |
97
+ | `bin/seams resolve --eject <engine>/<file>` | Marks a generated file as yours, so future runs of the generator leave it alone. Also `--list-markers <engine>` and `--list-ejected`. |
98
+ | `bin/rails generate seams:auth:add_oauth_provider <name>` | Adds an OAuth provider to the auth engine. |
99
+
100
+ You also get:
101
+
102
+ - **Four custom RuboCop cops.** Two fail the build when one engine reaches into another's code or models. The other two check background job queue names and migration comments.
103
+ - **A GitHub Actions workflow** that runs each engine's tests in parallel.
104
+ - **A quality toolchain** set up by `seams:install`:
105
+ - strong_migrations and lefthook git hooks are on by default. Turn them off with `--no-strong-migrations` or `--no-lefthook`.
106
+ - herb (ERB lint) is off by default. Turn it on with `--herb`.
107
+ - **A Dockerfile and a Kamal `deploy.yml`**, written only if your app doesn't already have them. Rails 8 apps ship their own Dockerfile, which Seams leaves in place.
108
+
109
+ ## Admin engine
110
+
111
+ `bin/seams admin` adds an admin area at `/admin`. It needs the `auth` engine. It adds the `administrate` and `pundit` gems to your Gemfile.
112
+
113
+ > [!IMPORTANT]
114
+ > **Only staff can open the admin area by default.** Set `staff: true` on an `Auth::Identity` to let that person in:
115
+ >
116
+ > ```ruby
117
+ > Auth::Identity.find_by(email: "you@example.com").update!(staff: true)
118
+ > ```
119
+ >
120
+ > Signed-out visitors are sent to the sign-in page. Signed-in identities that aren't staff get a 403.
121
+
122
+ <details>
123
+ <summary><strong>Tenant mode: let each customer's admins manage their own account</strong></summary>
124
+
125
+ By default the admin area runs in **platform** mode: staff see every account. In **tenant** mode, an account's admins see only their own account's data.
126
+
127
+ Tenant mode needs three settings. Without the last two, tenant admins get a 403.
128
+
129
+ ```ruby
130
+ # config/initializers/seams_admin.rb
131
+ Seams::Admin.configure do |c|
132
+ c.tenancy_scope = :tenant
133
+
134
+ # Let any signed-in identity past the gate. The tenant policies then
135
+ # decide what each role may do.
136
+ c.authenticator = ->(ctrl) { ctrl.current_identity.present? }
137
+
138
+ # Tell the admin area which membership the request belongs to.
139
+ # This example takes the identity's first active membership. Use
140
+ # however your app picks the current account (subdomain, session, a switcher).
141
+ c.current_membership_resolver = lambda do |ctrl|
142
+ identity = ctrl.current_identity
143
+ identity && Accounts::Membership.find_by(identity_id: identity.id, active: true)
144
+ end
145
+ end
146
+ ```
147
+
148
+ With these set:
149
+ - An `admin` or `owner` of an account sees only that account's rows.
150
+ - Requests for another account's records return 404.
151
+ - A `member` gets a 403.
152
+
153
+ The generated `engines/admin/README.md` has the full reference.
154
+
155
+ </details>
156
+
157
+ ## Upgrading an existing Seams app
158
+
159
+ Seams writes code into your app. So updating the gem does not change engines you already generated. Read the [CHANGELOG](CHANGELOG.md) before you update, and apply the changes that affect you.
160
+
161
+ > [!NOTE]
162
+ > If you generated engines before the September 2026 changes, check these:
163
+ > - **solid_cable 4.1 or newer:** each engine's test app needs `engines/*/spec/dummy/config/cable.yml`, containing `test:` with `adapter: test`. Without it, the engine's tests fail to load.
164
+ > - **Billing:** the generated code now targets `stripe ~> 19.0`. The generated services now raise `Billing::GatewayError` instead of `Stripe::*` errors. Set your Stripe webhook endpoint to the same API version as the gem.
165
+ > - **Admin:** regenerate the admin engine, or copy the fixes listed in the CHANGELOG. Earlier versions could not boot or serve any admin page.
166
+ >
167
+ > If you adopted Seams before Wave 9, start with the [Wave 8 upgrade guide](doc/how-to/UPGRADING_FROM_WAVE_8.md).
55
168
 
56
169
  ## Documentation
57
170
 
58
- - [doc/GETTING_STARTED.md](doc/GETTING_STARTED.md)
59
- - [doc/ARCHITECTURE.md](doc/ARCHITECTURE.md) — short overview
60
- - [doc/ARCHITECTURE_WAVE_9.md](doc/ARCHITECTURE_WAVE_9.md) — full system walk-through (post-Wave-9)
61
- - [doc/ARCHITECTURE_WAVE_10.md](doc/ARCHITECTURE_WAVE_10.md) — Wave 10 addendum: insertion points, follow-up generators, eject CLI
62
- - [doc/ENGINE_CATALOGUE.md](doc/ENGINE_CATALOGUE.md)
63
- - [doc/CURRENT_ATTRIBUTES.md](doc/CURRENT_ATTRIBUTES.md) — per-request namespaces (Auth::Current, Accounts::Current, Teams::Current, Core::Current)
64
- - [doc/INSERTION_POINTS.md](doc/INSERTION_POINTS.md) — marker format spec
65
- - [doc/INSERTION_POINTS_CATALOGUE.md](doc/INSERTION_POINTS_CATALOGUE.md) — the canonical 33 markers
66
- - [doc/WRITING_FOLLOW_UP_GENERATORS.md](doc/WRITING_FOLLOW_UP_GENERATORS.md) — write your own follow-up generator
67
- - [doc/ADDING_AN_ENGINE.md](doc/ADDING_AN_ENGINE.md)
68
- - [doc/REMOVING_AN_ENGINE.md](doc/REMOVING_AN_ENGINE.md)
69
- - [doc/WRITING_AN_ADAPTER.md](doc/WRITING_AN_ADAPTER.md)
70
- - [doc/TESTING.md](doc/TESTING.md)
71
- - [doc/UPGRADING_FROM_WAVE_8.md](doc/UPGRADING_FROM_WAVE_8.md) — if you adopted seams pre-Wave-9
72
-
73
- ## Why Seams instead of...
74
-
75
- | ... | Seams gives you |
171
+ The **[documentation site](https://davidslv.github.io/seams/)** has every guide below, with search. The API reference is on **[rubydoc.info](https://rubydoc.info/gems/seams)**.
172
+
173
+ <details>
174
+ <summary><strong>Start here</strong></summary>
175
+
176
+ - [Getting Started](doc/tutorials/GETTING_STARTED.md): install, first engine, running app
177
+ - [Engine catalogue](doc/reference/ENGINE_CATALOGUE.md): every engine in detail
178
+ - [Architecture overview](doc/explanation/ARCHITECTURE.md): why Seams is built this way
179
+
180
+ </details>
181
+
182
+ <details>
183
+ <summary><strong>Building and extending</strong></summary>
184
+
185
+ - [Adding an engine](doc/how-to/ADDING_AN_ENGINE.md)
186
+ - [Removing an engine](doc/how-to/REMOVING_AN_ENGINE.md)
187
+ - [Writing an adapter](doc/how-to/WRITING_AN_ADAPTER.md): swap in Mailgun, Twilio, Paddle, and others
188
+ - [Writing follow-up generators](doc/how-to/WRITING_FOLLOW_UP_GENERATORS.md)
189
+ - [Insertion points](doc/reference/INSERTION_POINTS.md) and the [catalogue of markers](doc/reference/INSERTION_POINTS_CATALOGUE.md)
190
+ - [Deploying](doc/how-to/DEPLOYING.md)
191
+
192
+ </details>
193
+
194
+ <details>
195
+ <summary><strong>Reference</strong></summary>
196
+
197
+ - [Current attributes](doc/reference/CURRENT_ATTRIBUTES.md): `Auth::Current`, `Accounts::Current`, `Teams::Current`, `Core::Current`
198
+ - [Permissions](doc/reference/PERMISSIONS.md): ability codes, roles, the grant map
199
+ - [Observability](doc/reference/OBSERVABILITY.md): logging, tracing, metrics
200
+ - [Testing](doc/reference/TESTING.md)
201
+
202
+ </details>
203
+
204
+ <details>
205
+ <summary><strong>Design system</strong></summary>
206
+
207
+ - [Design system overview](doc/design-system/DESIGN_SYSTEM.md) (start here)
208
+ - [Foundations](doc/design-system/DESIGN_SYSTEM_FOUNDATIONS.md): tokens and scales
209
+ - [Components](doc/design-system/DESIGN_SYSTEM_COMPONENTS.md): the 33 `ui_*` components
210
+ - [Forms](doc/design-system/DESIGN_SYSTEM_FORMS.md): `Design::FormBuilder`
211
+ - [Theming](doc/design-system/DESIGN_SYSTEM_THEMING.md)
212
+ - [Accessibility](doc/design-system/DESIGN_SYSTEM_ACCESSIBILITY.md)
213
+
214
+ </details>
215
+
216
+ <details>
217
+ <summary><strong>Architecture history</strong></summary>
218
+
219
+ - [Wave 9](doc/explanation/ARCHITECTURE_WAVE_9.md): the full system walk-through, including the identity / account / team split
220
+ - [Wave 10](doc/explanation/ARCHITECTURE_WAVE_10.md): insertion points, follow-up generators, the eject command
221
+ - [Wave 11](doc/explanation/ARCHITECTURE_WAVE_11.md): the admin engine
222
+ - [Personal data and GDPR](doc/explanation/WAVE_11_PII_GDPR.md)
223
+ - [Architecture Decision Records](doc/adr/)
224
+
225
+ </details>
226
+
227
+ ## How Seams compares
228
+
229
+ | Instead of | Seams gives you |
76
230
  | --- | --- |
77
- | Bullet Train | The substrate, not a starter kit. Code is in your repo, not behind a gem. |
78
- | Jumpstart Pro | Same. Plus the boundary cops. |
79
- | `rails plugin new --mountable` | Engines come pre-wired with events, registry, observability, boundary enforcement, CI. |
80
- | Hand-rolled microservices | One process. No HTTP between services. Synchronous events with explicit subscribers. |
81
-
82
- ## Status
83
-
84
- Waves 1–9 complete: foundation, auth (with OAuth, API tokens,
85
- GDPR-encrypted PII), notifications (with TypeRegistry,
86
- ActionCable bell, multipart mailers), billing (official Stripe
87
- gem, 13-handler webhook router, Lifetime Deals), teams (with
88
- team scoping, role-based authz). Wave 9 added the `accounts`
89
- engine and reworked the identity/account/team boundary —
90
- `Auth::Identity` (the human), `Accounts::Account` (the tenant),
91
- `Teams::Team` (the optional grouping) are now three peer engines
92
- with one clear responsibility each. See
93
- [CHANGELOG.md](CHANGELOG.md#wave-9--identity--account--team-rework-breaking)
94
- and [doc/UPGRADING_FROM_WAVE_8.md](doc/UPGRADING_FROM_WAVE_8.md).
95
-
96
- See [issue #5](https://github.com/Davidslv/seams/issues/5) for the live
97
- work tracker.
98
-
99
- Suite: RuboCop clean, brakeman + bundle-audit clean. Run
100
- `bin/audit` before any push.
231
+ | Bullet Train or Jumpstart Pro | The code lives in your repo, not behind a gem. Seams also enforces engine boundaries with RuboCop cops. |
232
+ | `rails plugin new --mountable` | Engines come wired with events, registries, observability, boundary checks, and CI. |
233
+ | Microservices | One process and one deploy. No HTTP between services. Engines talk through synchronous events with explicit subscribers. |
234
+
235
+ ## Project status
236
+
237
+ > [!NOTE]
238
+ > Seams is in active development and has not reached 1.0. Breaking changes are listed in the [CHANGELOG](CHANGELOG.md). Open work is tracked in [issue #5](https://github.com/Davidslv/seams/issues/5).
239
+
240
+ Each change is checked in CI:
241
+ - RuboCop, Brakeman, and bundle-audit
242
+ - the gem's own tests, on Ruby 3.3, 3.4, and 4.0.7
243
+ - an end-to-end run that creates a new Rails app with `rails new`, generates every engine, runs each engine's tests, and boots the app against Postgres
244
+
245
+ ## Contributing
246
+
247
+ - **How to contribute:** [CONTRIBUTING.md](CONTRIBUTING.md) covers setup, the checks, and the pull request workflow. Run `bin/audit` before you push.
248
+ - **Security issues:** [SECURITY.md](SECURITY.md)
249
+ - **Getting help:** [SUPPORT.md](SUPPORT.md)
250
+ - **How the project is run:** [GOVERNANCE.md](GOVERNANCE.md) and [MAINTAINERS.md](MAINTAINERS.md)
251
+ - **Community expectations:** [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
252
+ - **AI coding agents:** [AGENTS.md](AGENTS.md) and [llms.txt](llms.txt)
253
+ - **Citing Seams:** [CITATION.cff](CITATION.cff)
101
254
 
102
255
  ## License
103
256
 
104
- MIT — see [LICENSE](LICENSE).
257
+ MIT. See [LICENSE](LICENSE).