plutonium 0.63.0 → 0.65.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 (280) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +4 -1
  3. data/.claude/skills/plutonium-app/SKILL.md +14 -0
  4. data/.claude/skills/plutonium-dashboard/SKILL.md +161 -0
  5. data/.claude/skills/plutonium-resource/SKILL.md +94 -14
  6. data/.claude/skills/plutonium-ui/SKILL.md +22 -1
  7. data/.standard.yml +1 -1
  8. data/CHANGELOG.md +65 -0
  9. data/CLAUDE.md +282 -210
  10. data/SECURITY.md +1 -1
  11. data/app/assets/plutonium-charts.js +20604 -0
  12. data/app/assets/plutonium-charts.js.map +7 -0
  13. data/app/assets/plutonium-charts.min.js +41 -0
  14. data/app/assets/plutonium-charts.min.js.map +7 -0
  15. data/app/assets/plutonium.css +1 -1
  16. data/app/assets/plutonium.js +794 -629
  17. data/app/assets/plutonium.js.map +4 -4
  18. data/app/assets/plutonium.min.js +48 -48
  19. data/app/assets/plutonium.min.js.map +4 -4
  20. data/app/views/plutonium/_flash_alerts.html.erb +2 -2
  21. data/app/views/plutonium/_resource_header.html.erb +2 -2
  22. data/app/views/plutonium/_resource_sidebar.html.erb +11 -2
  23. data/app/views/plutonium/_toast.html.erb +2 -2
  24. data/app/views/rodauth/_login_form.html.erb +1 -1
  25. data/app/views/rodauth/_password_visibility.html.erb +1 -1
  26. data/app/views/rodauth/add_recovery_codes.html.erb +2 -2
  27. data/app/views/rodauth/change_login.html.erb +2 -2
  28. data/app/views/rodauth/create_account.html.erb +4 -4
  29. data/app/views/rodauth/reset_password_request.html.erb +1 -1
  30. data/app/views/rodauth/verify_account_resend.html.erb +1 -1
  31. data/app/views/rodauth/webauthn_remove.html.erb +1 -1
  32. data/config/brakeman.ignore +4 -4
  33. data/config/initializers/rabl.rb +0 -40
  34. data/config/locales/en/api_client.yml +22 -0
  35. data/config/locales/en/async.yml +34 -0
  36. data/config/locales/en/dashboard.yml +25 -0
  37. data/config/locales/en/invites.yml +37 -0
  38. data/config/locales/en/js.yml +40 -0
  39. data/config/locales/en/kanban.yml +28 -0
  40. data/config/locales/en/plutonium.yml +26 -0
  41. data/config/locales/en/positioning.yml +14 -0
  42. data/config/locales/en/profile.yml +29 -0
  43. data/config/locales/en/resource.yml +60 -0
  44. data/config/locales/en/ui.yml +93 -0
  45. data/config/locales/en/wizard.yml +46 -0
  46. data/docs/.vitepress/config.ts +34 -1
  47. data/docs/.vitepress/theme/components/HomeHero.vue +1 -1
  48. data/docs/.vitepress/theme/components/HomeInTheBox.vue +14 -0
  49. data/docs/.vitepress/theme/custom.css +34 -377
  50. data/docs/blog/introducing-plutonium-dashboards.md +175 -0
  51. data/docs/blog/introducing-plutonium-i18n.md +91 -0
  52. data/docs/blog/introducing-plutonium.md +4 -5
  53. data/docs/blog/whats-new-async-kanban-wizards.md +1 -1
  54. data/docs/guides/dashboards.md +300 -0
  55. data/docs/guides/index.md +1 -0
  56. data/docs/guides/kanban.md +2 -2
  57. data/docs/public/images/blog/dashboards-card-error.png +0 -0
  58. data/docs/public/images/blog/dashboards-loading.png +0 -0
  59. data/docs/public/images/blog/dashboards-overview.png +0 -0
  60. data/docs/public/images/blog/dashboards-sidebar.png +0 -0
  61. data/docs/public/images/blog/i18n-es-admin.png +0 -0
  62. data/docs/public/images/guides/dashboard-card-error.png +0 -0
  63. data/docs/public/images/guides/dashboard-loading.png +0 -0
  64. data/docs/public/images/guides/dashboard-overview.png +0 -0
  65. data/docs/public/images/guides/dashboard-sidebar.png +0 -0
  66. data/docs/reference/app/generators.md +2 -2
  67. data/docs/reference/app/portals.md +2 -0
  68. data/docs/reference/configuration.md +84 -27
  69. data/docs/reference/dashboard/dsl.md +123 -0
  70. data/docs/reference/dashboard/index.md +35 -0
  71. data/docs/reference/dashboard/registration.md +76 -0
  72. data/docs/reference/i18n.md +239 -0
  73. data/docs/reference/index.md +10 -1
  74. data/docs/reference/kanban/dsl.md +1 -1
  75. data/docs/reference/kanban/positioning.md +2 -2
  76. data/docs/reference/resource/actions.md +1 -1
  77. data/docs/reference/resource/definition.md +96 -18
  78. data/docs/reference/resource/export.md +5 -1
  79. data/docs/reference/resource/index.md +2 -0
  80. data/docs/reference/{positioning.md → resource/positioning.md} +2 -1
  81. data/docs/reference/resource/query.md +2 -0
  82. data/docs/reference/ui/assets.md +8 -0
  83. data/docs/reference/ui/displays.md +5 -8
  84. data/docs/reference/ui/index.md +1 -1
  85. data/docs/reference/wizard/dsl.md +2 -2
  86. data/esbuild.config.js +5 -2
  87. data/gemfiles/rails_7.gemfile.lock +3 -3
  88. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  89. data/gemfiles/rails_8.1.gemfile.lock +2 -2
  90. data/lib/active_model/validations/array_validator.rb +1 -1
  91. data/lib/active_model/validations/attached_validator.rb +1 -1
  92. data/lib/active_model/validations/url_validator.rb +2 -2
  93. data/lib/generators/pu/core/assets/assets_generator.rb +36 -21
  94. data/lib/generators/pu/core/assets/templates/postcss.config.js +1 -1
  95. data/lib/generators/pu/core/update/update_generator.rb +3 -2
  96. data/lib/generators/pu/dashboard/dashboard_generator.rb +96 -0
  97. data/lib/generators/pu/dashboard/templates/dashboard.rb.tt +25 -0
  98. data/lib/generators/pu/docker/install/install_generator.rb +58 -0
  99. data/lib/generators/pu/docker/install/templates/Dockerfile.dev.tt +18 -3
  100. data/lib/generators/pu/docker/install/templates/Dockerfile.tt +20 -5
  101. data/lib/generators/pu/docker/install/templates/{docker-compose.yml → docker-compose.yml.tt} +1 -1
  102. data/lib/generators/pu/gem/dotenv/dotenv_generator.rb +1 -1
  103. data/lib/generators/pu/invites/invitable_generator.rb +18 -1
  104. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +0 -95
  105. data/lib/generators/pu/lib/plutonium_generators/concerns/js_package_manager.rb +146 -0
  106. data/lib/generators/pu/lib/plutonium_generators/generator.rb +1 -17
  107. data/lib/generators/pu/pkg/package/package_generator.rb +1 -0
  108. data/lib/generators/pu/pkg/package/templates/config/locales/en.yml.tt +28 -0
  109. data/lib/generators/pu/pkg/portal/portal_generator.rb +1 -0
  110. data/lib/generators/pu/pkg/portal/templates/config/locales/en.yml.tt +30 -0
  111. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +0 -8
  112. data/lib/generators/pu/rodauth/concerns/feature_selector.rb +0 -18
  113. data/lib/plutonium/action/base.rb +27 -3
  114. data/lib/plutonium/action/interactive.rb +17 -4
  115. data/lib/plutonium/api_client/concerns/create_api_client.rb +12 -11
  116. data/lib/plutonium/api_client/concerns/disable_api_client.rb +3 -3
  117. data/lib/plutonium/auth/sequel_adapter.rb +1 -1
  118. data/lib/plutonium/configuration.rb +5 -0
  119. data/lib/plutonium/core/controller.rb +25 -1
  120. data/lib/plutonium/core/controllers/authorizable.rb +1 -1
  121. data/lib/plutonium/dashboard/base.rb +114 -0
  122. data/lib/plutonium/dashboard/card.rb +200 -0
  123. data/lib/plutonium/dashboard/controller.rb +101 -0
  124. data/lib/plutonium/dashboard/dsl.rb +104 -0
  125. data/lib/plutonium/dashboard/register.rb +40 -0
  126. data/lib/plutonium/dashboard/route_resolution.rb +30 -0
  127. data/lib/plutonium/dashboard.rb +42 -0
  128. data/lib/plutonium/definition/actions.rb +6 -1
  129. data/lib/plutonium/definition/base.rb +41 -3
  130. data/lib/plutonium/definition/index_views.rb +1 -1
  131. data/lib/plutonium/definition/positioning.rb +1 -1
  132. data/lib/plutonium/definition/presentable.rb +2 -2
  133. data/lib/plutonium/helpers/assets_helper.rb +15 -2
  134. data/lib/plutonium/helpers/display_helper.rb +10 -2
  135. data/lib/plutonium/interaction/async/context.rb +6 -8
  136. data/lib/plutonium/interaction/async/executor.rb +6 -7
  137. data/lib/plutonium/interaction/async/run.rb +2 -2
  138. data/lib/plutonium/interaction/async/run_definition.rb +1 -1
  139. data/lib/plutonium/interaction/base.rb +2 -0
  140. data/lib/plutonium/interaction/concerns/dispatchable.rb +15 -3
  141. data/lib/plutonium/invites/concerns/cancel_invite.rb +3 -3
  142. data/lib/plutonium/invites/concerns/invite_token.rb +25 -7
  143. data/lib/plutonium/invites/concerns/invite_user.rb +10 -5
  144. data/lib/plutonium/invites/concerns/resend_invite.rb +4 -4
  145. data/lib/plutonium/invites/controller.rb +11 -11
  146. data/lib/plutonium/kanban/column.rb +13 -3
  147. data/lib/plutonium/kanban/dsl.rb +7 -4
  148. data/lib/plutonium/models/has_cents.rb +1 -1
  149. data/lib/plutonium/profile/security_section.rb +10 -18
  150. data/lib/plutonium/query/filter.rb +10 -1
  151. data/lib/plutonium/query/filters/association.rb +27 -2
  152. data/lib/plutonium/query/filters/boolean.rb +4 -4
  153. data/lib/plutonium/query/filters/date.rb +4 -17
  154. data/lib/plutonium/query/filters/date_range.rb +2 -2
  155. data/lib/plutonium/query/filters/select.rb +10 -1
  156. data/lib/plutonium/query/filters/text.rb +9 -12
  157. data/lib/plutonium/railtie.rb +17 -0
  158. data/lib/plutonium/resource/controller.rb +10 -5
  159. data/lib/plutonium/resource/controllers/crud_actions.rb +17 -20
  160. data/lib/plutonium/resource/controllers/export_csv.rb +19 -9
  161. data/lib/plutonium/resource/controllers/kanban_actions.rb +16 -16
  162. data/lib/plutonium/resource/controllers/position_actions.rb +5 -5
  163. data/lib/plutonium/resource/controllers/typeahead.rb +24 -1
  164. data/lib/plutonium/resource/query_object.rb +30 -3
  165. data/lib/plutonium/routing/dashboard_registration.rb +137 -0
  166. data/lib/plutonium/routing/resource_registration.rb +5 -0
  167. data/lib/plutonium/routing/route_set_extensions.rb +2 -1
  168. data/lib/plutonium/translation.rb +227 -0
  169. data/lib/plutonium/ui/actions_dropdown.rb +2 -2
  170. data/lib/plutonium/ui/breadcrumbs.rb +3 -3
  171. data/lib/plutonium/ui/color_mode_selector.rb +1 -1
  172. data/lib/plutonium/ui/component/behaviour.rb +46 -0
  173. data/lib/plutonium/ui/component/methods.rb +4 -0
  174. data/lib/plutonium/ui/dashboard/board.rb +80 -0
  175. data/lib/plutonium/ui/dashboard/card.rb +127 -0
  176. data/lib/plutonium/ui/dashboard/chart.rb +43 -0
  177. data/lib/plutonium/ui/dashboard/custom.rb +32 -0
  178. data/lib/plutonium/ui/dashboard/frame.rb +25 -0
  179. data/lib/plutonium/ui/dashboard/metric.rb +137 -0
  180. data/lib/plutonium/ui/dashboard/skeleton.rb +40 -0
  181. data/lib/plutonium/ui/display/components/attachment.rb +1 -1
  182. data/lib/plutonium/ui/display/components/badge.rb +7 -1
  183. data/lib/plutonium/ui/display/components/boolean.rb +2 -2
  184. data/lib/plutonium/ui/display/resource.rb +23 -16
  185. data/lib/plutonium/ui/export_button.rb +3 -3
  186. data/lib/plutonium/ui/form/components/key_value_store.rb +5 -5
  187. data/lib/plutonium/ui/form/components/password.rb +2 -2
  188. data/lib/plutonium/ui/form/components/resource_select.rb +27 -0
  189. data/lib/plutonium/ui/form/components/secure_association.rb +2 -0
  190. data/lib/plutonium/ui/form/components/uppy.rb +11 -3
  191. data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +1 -1
  192. data/lib/plutonium/ui/form/concerns/renders_repeater_row_controls.rb +5 -4
  193. data/lib/plutonium/ui/form/concerns/renders_structured_inputs.rb +1 -1
  194. data/lib/plutonium/ui/form/query.rb +8 -8
  195. data/lib/plutonium/ui/form/resource.rb +54 -26
  196. data/lib/plutonium/ui/frame_navigator_panel.rb +4 -4
  197. data/lib/plutonium/ui/grid/card.rb +3 -3
  198. data/lib/plutonium/ui/grid/resource.rb +3 -3
  199. data/lib/plutonium/ui/interaction/async/run_progress.rb +6 -6
  200. data/lib/plutonium/ui/interaction/async/running_banner.rb +2 -2
  201. data/lib/plutonium/ui/kanban/column.rb +6 -6
  202. data/lib/plutonium/ui/layout/base.rb +19 -1
  203. data/lib/plutonium/ui/layout/header.rb +1 -1
  204. data/lib/plutonium/ui/layout/icon_rail.rb +4 -4
  205. data/lib/plutonium/ui/layout/rodauth_layout.rb +1 -1
  206. data/lib/plutonium/ui/layout/sidebar.rb +1 -1
  207. data/lib/plutonium/ui/layout/topbar.rb +2 -2
  208. data/lib/plutonium/ui/modal/base.rb +4 -4
  209. data/lib/plutonium/ui/nav_grid_menu.rb +1 -1
  210. data/lib/plutonium/ui/nav_user.rb +2 -2
  211. data/lib/plutonium/ui/page/base.rb +10 -2
  212. data/lib/plutonium/ui/page/dashboard.rb +54 -0
  213. data/lib/plutonium/ui/page/edit.rb +3 -3
  214. data/lib/plutonium/ui/page/interactive_action.rb +2 -2
  215. data/lib/plutonium/ui/page/new.rb +3 -3
  216. data/lib/plutonium/ui/page/show.rb +2 -2
  217. data/lib/plutonium/ui/page/wizard.rb +22 -16
  218. data/lib/plutonium/ui/page/wizard_chooser.rb +10 -10
  219. data/lib/plutonium/ui/page/wizard_completed.rb +2 -2
  220. data/lib/plutonium/ui/skeleton_table.rb +1 -1
  221. data/lib/plutonium/ui/table/base.rb +2 -2
  222. data/lib/plutonium/ui/table/components/attachment.rb +1 -1
  223. data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +19 -3
  224. data/lib/plutonium/ui/table/components/drag_handle.rb +3 -3
  225. data/lib/plutonium/ui/table/components/filter_form.rb +11 -10
  226. data/lib/plutonium/ui/table/components/filter_pills.rb +4 -4
  227. data/lib/plutonium/ui/table/components/pagy_info.rb +61 -17
  228. data/lib/plutonium/ui/table/components/pagy_pagination.rb +9 -5
  229. data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +1 -1
  230. data/lib/plutonium/ui/table/components/scopes_bar.rb +6 -2
  231. data/lib/plutonium/ui/table/components/scopes_pills.rb +3 -3
  232. data/lib/plutonium/ui/table/components/selection_column.rb +1 -1
  233. data/lib/plutonium/ui/table/components/toolbar.rb +2 -2
  234. data/lib/plutonium/ui/table/components/view_switcher.rb +12 -8
  235. data/lib/plutonium/ui/table/resource.rb +53 -17
  236. data/lib/plutonium/ui/wizard/review.rb +7 -7
  237. data/lib/plutonium/ui/wizard/stepper.rb +1 -1
  238. data/lib/plutonium/ui/wizard/summary_display.rb +6 -0
  239. data/lib/plutonium/version.rb +1 -1
  240. data/lib/plutonium/wizard/base.rb +2 -0
  241. data/lib/plutonium/wizard/driving.rb +8 -2
  242. data/lib/plutonium/wizard/dsl.rb +4 -2
  243. data/lib/plutonium/wizard/field_importer.rb +1 -8
  244. data/lib/plutonium/wizard/review_step.rb +8 -1
  245. data/lib/plutonium/wizard/step.rb +14 -3
  246. data/package.json +4 -2
  247. data/plutonium.gemspec +15 -30
  248. data/postcss-gem-import.cjs +34 -0
  249. data/postcss-gem-import.js +3 -28
  250. data/src/css/components.css +91 -0
  251. data/src/css/slim_select.css +0 -38
  252. data/src/css/tokens.css +24 -0
  253. data/src/js/controllers/attachment_input_controller.js +15 -6
  254. data/src/js/controllers/chart_controller.js +162 -0
  255. data/src/js/controllers/clipboard_controller.js +3 -2
  256. data/src/js/controllers/dirty_form_guard_controller.js +4 -3
  257. data/src/js/controllers/easymde_controller.js +3 -0
  258. data/src/js/controllers/filter_panel_controller.js +15 -6
  259. data/src/js/controllers/flatpickr_controller.js +8 -0
  260. data/src/js/controllers/frame_refresh_controller.js +59 -0
  261. data/src/js/controllers/intl_tel_input_controller.js +5 -0
  262. data/src/js/controllers/register_controllers.js +4 -0
  263. data/src/js/controllers/slim_select_controller.js +79 -176
  264. data/src/js/core.js +9 -1
  265. data/src/js/i18n.js +69 -0
  266. data/src/js/plutonium-charts.js +15 -0
  267. data/src/js/turbo/turbo_confirm.js +5 -3
  268. metadata +71 -31
  269. data/.yarnrc.yml +0 -8
  270. data/config/initializers/hotwire_turbo_monkey_patches.rb +0 -12
  271. data/lib/generators/pu/field/input/input_generator.rb +0 -32
  272. data/lib/generators/pu/field/input/templates/input.rb.tt +0 -15
  273. data/lib/generators/pu/lib/plutonium_generators/concerns/config.rb +0 -39
  274. data/lib/generators/pu/lib/plutonium_generators/concerns/serializer.rb +0 -39
  275. data/lib/generators/pu/lib/plutonium_generators/installer.rb +0 -205
  276. data/lib/generators/pu/res/scaffold/templates/presenter.rb.tt +0 -13
  277. data/lib/generators/pu/res/scaffold/templates/query_object.rb.tt +0 -17
  278. data/public/plutonium-assets/application.js +0 -31419
  279. data/public/plutonium-assets/plutonium.ico +0 -0
  280. data/yarn.lock +0 -6858
data/CLAUDE.md CHANGED
@@ -2,206 +2,305 @@
2
2
 
3
3
  This guide helps AI assistants contribute to the Plutonium framework itself.
4
4
 
5
+ > Personal, cross-project rules live in `~/.claude/CLAUDE.md` (never stage/commit
6
+ > unless asked, `rails runner` over console, `Rails.logger.warn { ... }` block
7
+ > syntax, `with_connection` over `connection`, inline migration indexes, Stimulus
8
+ > for interactivity and always register controllers, no defensive `respond_to?`
9
+ > checks, no Claude attribution in commits/PRs). Those are not repeated here.
10
+ > This file carries only what is specific to this repo.
11
+
5
12
  ## Project Overview
6
13
 
7
- Plutonium is a Rails RAD framework distributed as a Ruby gem. It provides resource-oriented architecture with automatic CRUD, policies, definitions, and multi-tenancy.
14
+ Plutonium is a Rails RAD framework distributed as a Ruby gem. It provides
15
+ resource-oriented architecture with automatic CRUD, policies, definitions, and
16
+ multi-tenancy. Assets (JS/CSS) ship inside the gem and are also published to npm.
8
17
 
9
18
  ## Repository Structure
10
19
 
11
20
  ```
12
21
  plutonium-core/
13
- ├── lib/plutonium/ # Core framework code
14
- │ ├── resource/ # Resource system (controllers, policies, definitions)
15
- │ ├── portal/ # Portal engines and multi-tenancy
16
- │ ├── interaction/ # Business logic encapsulation
17
- │ ├── ui/ # Phlex view components
18
- │ ├── definition/ # Definition DSL and field types
19
- │ ├── query/ # Search, filters, scopes
20
- │ ├── auth/ # Authentication (Rodauth integration)
21
- │ ├── routing/ # Route helpers and extensions
22
- │ └── generators/ # Rails generators (pu:res:scaffold, etc.)
23
- ├── app/ # Rails app components
24
- │ ├── assets/ # JavaScript, CSS
25
- │ ├── controllers/ # Base controllers
26
- │ └── views/ # Phlex components
27
- ├── test/ # Test suite
28
- │ ├── plutonium/ # Unit tests mirroring lib/ structure
29
- │ ├── dummy/ # Test Rails app
30
- │ └── system/ # System/integration tests
31
- ├── docs/ # VitePress documentation site
32
- │ ├── getting-started/
33
- │ ├── guides/
34
- │ └── reference/
35
- └── .claude/skills/ # AI assistant skills (documentation for Claude)
22
+ ├── lib/
23
+ │ ├── plutonium/ # Core framework code
24
+ │ │ ├── resource/ # Record, Policy, Definition, Controller, Interaction
25
+ │ │ ├── portal/ # Portal engines and multi-tenancy
26
+ │ │ ├── package/ # Feature-package engine mixin
27
+ │ │ ├── interaction/ # Business logic encapsulation
28
+ │ │ ├── dashboard/ # Dashboard DSL (metric / chart / card)
29
+ │ │ ├── kanban/ # Kanban board support
30
+ │ │ ├── wizard/ # Multi-step wizard DSL
31
+ │ │ ├── invites/ # Invitation model + token lifecycle
32
+ │ │ ├── rodauth/ # Rodauth integration
33
+ │ │ ├── positioning/ # Drag-to-reorder
34
+ │ │ ├── ui/ # Phlex view components
35
+ │ │ ├── definition/ # Definition DSL and field types
36
+ │ │ ├── query/ # Search, filters, scopes
37
+ │ │ ├── routing/ # Route helpers and extensions
38
+ │ │ └── testing/ # Test helpers shipped to consumers
39
+ │ └── generators/pu/ # Rails generators (pu:*), NOT under lib/plutonium/
40
+ ├── app/ # Rails app components (assets, controllers, Phlex views)
41
+ ├── src/ # JS/CSS sources (built into app/assets and src/build)
42
+ ├── test/
43
+ │ ├── plutonium/ # Unit tests mirroring lib/
44
+ │ ├── integration/ # Request/integration tests
45
+ │ ├── generators/ # Generator tests
46
+ │ ├── system/ # System/browser tests
47
+ │ ├── support/ # Test helpers (generator_test_helper.rb, etc.)
48
+ │ └── dummy/ # Test Rails app (see below)
49
+ ├── docs/ # VitePress docs; docs/public/skills/ is generated
50
+ ├── .claude/skills/ # AI assistant skills (source of the shipped skills)
51
+ └── lib/tasks/release.rake # Release automation
36
52
  ```
37
53
 
54
+ Generators live at `lib/generators/pu/<category>/`. Categories on disk:
55
+ `async_interactions, core, dashboard, docker, eject, extra, field, gem, gen,
56
+ invites, lib, lite, pkg, profile, res, rodauth, saas, service, skills, test,
57
+ wizards`. There is **no** `pu:res:interaction` generator: interactions are
58
+ hand-authored.
59
+
38
60
  ## Key Abstractions
39
61
 
40
62
  ### Resource System
41
- - `Plutonium::Resource::Record` - Model mixin with `associated_with` scopes
42
- - `Plutonium::Resource::Policy` - ActionPolicy-based authorization
43
- - `Plutonium::Resource::Definition` - Declarative UI configuration
44
- - `Plutonium::Resource::Controller` - CRUD controller mixin
45
- - `Plutonium::Resource::Interaction` - Business logic encapsulation
46
-
47
- ### Portal System
48
- - `Plutonium::Portal::Engine` - Rails engine mixin for web interfaces
49
- - `Plutonium::Package::Engine` - Rails engine mixin for feature packages
63
+ - `Plutonium::Resource::Record`: model mixin with `associated_with` scopes
64
+ - `Plutonium::Resource::Policy`: ActionPolicy-based authorization
65
+ - `Plutonium::Resource::Definition`: declarative UI configuration
66
+ - `Plutonium::Resource::Controller`: CRUD controller mixin
67
+ - `Plutonium::Resource::Interaction`: business logic encapsulation
68
+
69
+ ### Portal / Package System
70
+ - `Plutonium::Portal::Engine`: Rails engine mixin for web interfaces
71
+ - `Plutonium::Package::Engine`: Rails engine mixin for feature packages
50
72
  - Entity scoping for multi-tenancy (path or custom strategy)
51
73
 
52
74
  ### UI Components
53
- Located in `lib/plutonium/ui/` - Phlex-based components:
54
- - `page/` - Page layouts (index, show, new, edit)
55
- - `form/` - Form builders and field components
56
- - `table/` - Data tables with sorting, pagination
57
- - `display/` - Field display components
75
+ Phlex-based, under `lib/plutonium/ui/`: `page/`, `form/`, `table/`, `display/`,
76
+ `dashboard/`.
58
77
 
59
- ## Development Workflow
78
+ ## The Dummy Test App (`test/dummy`)
79
+
80
+ This is where you exercise the framework end to end. Its packages are:
81
+ `admin_portal, blogging, catalog, locus_portal, org_portal, storefront_portal`.
82
+ Packages are auto-loaded by `config/packages.rb` globbing `packages/**/lib/engine.rb`.
83
+
84
+ ### Always use generators here
85
+
86
+ Use the `pu:*` generators (`pu:res:scaffold`, `pu:res:conn`, `pu:pkg:portal`,
87
+ `pu:pkg:package`, etc.) to create dummy-app files, then customize the output.
88
+ **Why:** generators produce the correct file structure, inheritance, and marker
89
+ comments. Manual creation drifts into wrong patterns (making `ResourcePolicy` a
90
+ class instead of a module, missing the portal-module `include`, wrong inheritance
91
+ chain), and running the generator also validates that it works. Use `--policy`
92
+ and `--definition` on `pu:res:conn` when portal-specific overrides are needed.
93
+
94
+ ### Databases
60
95
 
61
- ### Environment Setup
96
+ Every environment is SQLite (`test/dummy/config/database.yml`):
97
+ - `development` → `storage/development.sqlite3`
98
+ - `test` → `storage/test.sqlite3`
99
+ - `production` → multi-database: `primary`, `cache`, `queue`, `cable`
62
100
 
63
- When working on Plutonium itself, set:
101
+ The **test** database is dropped, recreated, and migrated on every test boot
102
+ (`test/test_helper.rb`), so it has the current schema but zero rows.
103
+
104
+ ### Running rails commands against the dummy
105
+
106
+ The dummy's own `Gemfile` is not installed; run everything through an appraisal
107
+ gemfile, from `test/dummy`:
64
108
 
65
109
  ```bash
66
- export PLUTONIUM_DEV=1
110
+ cd test/dummy && env -u BUNDLE_GEMFILE \
111
+ BUNDLE_GEMFILE="$(cd ../.. && pwd)/gemfiles/rails_8.1.gemfile" \
112
+ bundle exec rails runner '<code>'
67
113
  ```
68
114
 
69
- This enables development mode which:
70
- - Uses local assets instead of packaged ones
71
- - Enables hot reloading of components
72
- - Shows more detailed error messages
115
+ To seed data a running server will see, use a plain runner (do **not**
116
+ `require "test_helper"`, which drops the DB). Create a `User`
117
+ (`status = 2`, `password_hash = BCrypt::Password.create("password123")`), an
118
+ `Organization`, an `OrganizationUser` membership, then your records.
73
119
 
74
- ### Building Assets
120
+ ### Logins (seeds in `db/seeds.rb`)
75
121
 
76
- When working on JavaScript or CSS in `src/`:
122
+ Both logins are email-first (submit email, the same page reveals the password):
123
+ - User portal: `/users/login`, e.g. `alice@acme.com` / `password123`
124
+ - Admin portal: `/admins/login`, `admin@plutonium.dev` / `password123`. After
125
+ admin login, `/` redirects to `/users/login` (home wants a user session), so
126
+ navigate straight to `/admin/<resource>`.
77
127
 
78
- ```bash
79
- # Watch mode (rebuilds on changes to src/build/)
80
- yarn dev
128
+ Confirm which env/DB a running server is bound to before trusting it:
129
+ `lsof -p <pid> | grep sqlite3`, or watch which `*.sqlite3` mtime changes after an
130
+ action. A test-env server on an emptied `test.sqlite3` shows "no matching login"
131
+ because the users table is empty, not because credentials are wrong. Seeding the
132
+ wrong DB fails silently.
81
133
 
82
- # Production build (to app/assets/)
83
- yarn build
84
- ```
134
+ ## Development Workflow
135
+
136
+ ### Environment: `PLUTONIUM_DEV=1`
137
+
138
+ Set `PLUTONIUM_DEV=1` when working on the framework. It serves local assets,
139
+ hot-reloads components, and shows detailed errors.
85
140
 
86
- - `yarn dev` - watches and rebuilds to `src/build/` for development
87
- - `yarn build` - compiles to `app/assets/` for release
141
+ Asset-source gotcha: `PLUTONIUM_DEV=1` reads `src/build/*.manifest`, which is the
142
+ output of `yarn dev`. If you only ran `yarn build` (which writes `app/assets/`),
143
+ a `PLUTONIUM_DEV=1` server 500s with a missing `src/build/css.manifest`. To serve
144
+ the packaged `app/assets/`, boot **without** `PLUTONIUM_DEV`. A stale
145
+ `src/build/css.manifest` can point at an old bundle, so when verifying anything
146
+ visual, grep the served CSS for the rule you expect rather than trusting the
147
+ manifest's presence.
88
148
 
89
- **Always run `yarn dev`** in a terminal when working on frontend code.
149
+ Hot reload covers `lib/plutonium/**` and `packages/**` in **development** only
150
+ (the Listen-based `Plutonium::Reloader`). The **test**-env server does not reload,
151
+ so `lib/` edits there need a restart.
90
152
 
91
- ### Running Tests
153
+ ### Building assets
92
154
 
93
- Tests use [Appraisal](https://github.com/thoughtbot/appraisal) for multiple Rails versions:
155
+ There is no `Procfile.dev`. Frontend work runs through yarn:
94
156
 
95
157
  ```bash
96
- # Full test suite (all Rails versions)
97
- bundle exec appraisal rake test
158
+ yarn dev # concurrently: css:dev (chokidar → src/build) + js:dev (esbuild --dev)
159
+ yarn build # js:prod + css:prod → app/assets/ (release artifacts)
160
+ ```
98
161
 
99
- # Specific Rails version
100
- bundle exec appraisal rails-8.1 rake test
162
+ Keep `yarn dev` running in a terminal while editing JS/CSS in `src/`.
163
+
164
+ ### Running tests
165
+
166
+ Tests use [Appraisal](https://github.com/thoughtbot/appraisal). Gemfiles:
167
+ `rails_7`, `rails_8.0`, `rails_8.1`, and `postgres` (some concurrency tests skip
168
+ unless run under PostgreSQL, e.g. invite token row locks).
101
169
 
102
- # Specific test file
170
+ ```bash
171
+ bundle exec appraisal rake test # unit + integration, one rails version
172
+ bundle exec appraisal rails-8.1 rake test
103
173
  bundle exec appraisal rails-8.1 ruby -Itest test/plutonium/resource/policy_test.rb
104
174
  ```
105
175
 
106
- Available: `rails-7`, `rails-8.0`, `rails-8.1`
176
+ Task layout (`Rakefile`): `rake test` **excludes** `test/system` and
177
+ `test/generators`. Generator tests run via `rake test_generators`; system tests
178
+ via `rake test:system`; `rake test_all` runs `test` + `test_generators`.
179
+
180
+ **Schema/working-tree hazard: stage dummy edits before running generator tests.**
181
+ Generator tests call `git_restore_dummy_app` in `setup`
182
+ (`test/support/generator_test_helper.rb`), which runs `git checkout -- test/dummy`
183
+ **and** `git clean -fd test/dummy` before every test. `git checkout --` restores
184
+ from the index, so **staged** `test/dummy` changes survive but unstaged ones are
185
+ reverted; `git clean -fd` **permanently deletes** untracked files under
186
+ `test/dummy` with no recovery. Before `rake test_generators` / `rake test:system`,
187
+ `git add` any `test/dummy` changes the run depends on, and clear or stash
188
+ untracked scratch files there first.
107
189
 
108
- ### Testing Generators
190
+ ### Worktrees
109
191
 
110
- Generators are in `lib/generators/pu/`. Test by:
111
- 1. Running in the dummy app: `cd test/dummy && rails g pu:res:scaffold ...`
112
- 2. Generator tests in `test/generators/`
192
+ The long-running dummy servers run the main checkout (master). To exercise code
193
+ on a branch in a worktree, boot a fresh server from that worktree directory; a
194
+ server started elsewhere is running master's code, not yours.
113
195
 
114
- ### Documentation
196
+ ### Docs
115
197
 
116
198
  ```bash
117
- yarn docs:dev # Local preview at localhost:5173
118
- yarn docs:build # Build for production
199
+ yarn docs:dev # localhost:5173 (runs docs:sync-skills first)
200
+ yarn docs:build # must pass; dead internal links fail the build
119
201
  ```
120
202
 
121
- ## Code Conventions
203
+ ## Code Conventions (project-specific)
122
204
 
123
- ### Ruby Style
124
- - Follow Rails conventions
125
- - Use `with_connection` for database operations (fiber-safe)
126
- - Avoid excessive defensive programming (no `respond_to?` checks)
127
- - Use `Rails.logger.warn { "message" }` block syntax in production code
205
+ Shared Ruby style lives in `~/.claude/CLAUDE.md`. Project-specific rules:
206
+
207
+ ### Policies: gate per-record actions in the policy, not with `condition:`
208
+
209
+ For a record action whose availability depends on record state, gate it in the
210
+ **policy**, not with a `condition: -> { ... }` proc on the `action` declaration:
211
+
212
+ ```ruby
213
+ def resend_invite?
214
+ record.unverified?
215
+ end
216
+ ```
217
+
218
+ A record action's policy method always receives the **instance** (the table binds
219
+ `policy_for(record:)` to the row; the show page's `current_policy` is
220
+ instance-bound on member routes). So do **not** add a `record.is_a?(Model)` guard
221
+ on a record-action method. The class subject only appears on collection routes
222
+ (`index`/`new`), where it backs `read?`, `create?`/`new?`, `export_csv?`,
223
+ `search?`/`typeahead?`, and resource-action gates (kanban column actions are
224
+ auto-registered resource actions). Note `read?` gets the class via `index?` but
225
+ the instance via `show?`. Bulk actions are checked per record, not on the class.
226
+
227
+ **Why:** authorization belongs in the policy (single source of truth). A
228
+ `condition:` only hides UI; it does not deny a forced request, which splits the
229
+ guard. Keep a defense-in-depth re-check in the interaction's `execute` if desired.
128
230
 
129
231
  ### Generators
130
- - Always provide `--dest=` flag support to avoid interactive prompts
131
- - Use `--dest=main_app` for main application, `--dest=package_name` for packages
132
- - Quote shell arguments with special characters: `'field:type?'`, `'field:decimal{10,2}'`
133
-
134
- ### Migrations
135
- - Inline indexes and constraints in `create_table` blocks
136
- - Use `foreign_key: {on_delete: :cascade}` where appropriate
137
-
138
- ### Frontend
139
- - Stimulus controllers for interactivity (always register controllers)
140
- - TailwindCSS 4 for styling
141
- - Phlex for view components
142
-
143
- ## Adding New Features
144
-
145
- ### New Field Type
146
- 1. Add renderer in `lib/plutonium/ui/display/components/`
147
- 2. Add input in `lib/plutonium/ui/form/components/`
148
- 3. Register in field type mappings
149
- 4. Add tests and documentation
150
-
151
- ### New Generator
152
- 1. Create in `lib/generators/pu/category/name_generator.rb`
153
- 2. Add templates in `lib/generators/pu/category/name/templates/`
154
- 3. Add tests in `test/generators/`
155
- 4. Document in `docs/reference/generators/`
232
+ - Provide `--dest=` to avoid interactive prompts: `--dest=main_app` for the main
233
+ app, `--dest=<package_name>` for a package.
234
+ - Quote shell args with special characters: `'field:type?'`, `'field:decimal{10,2}'`.
156
235
 
157
- ### New Interaction Response
158
- 1. Add response class in `lib/plutonium/interaction/response/`
159
- 2. Add helper method in `Plutonium::Interaction::Outcome::Success`
160
- 3. Document usage
236
+ ## Git, Commits, and Releases
161
237
 
162
- ## Writing Docs and Blog Posts
238
+ ### Conventional commits, including PR titles
163
239
 
164
- Docs and posts in this repo are read by two audiences: developers, and AI assistants
165
- (the skills in `.claude/skills/` are synced to `docs/public/skills/` and shipped).
166
- A wrong example does not just mislead a reader. It gets reproduced as code.
240
+ Use conventional commits (`type(scope): subject`) for every commit **and** every
241
+ PR title. PRs are squash-merged, so the PR title lands on master verbatim with
242
+ `(#NN)` appended; a non-conventional title becomes a malformed commit.
167
243
 
168
- ### Verify every code sample
244
+ **Why:** `git-cliff` generates the changelog and computes the next version from
245
+ the commit history. A prose title like "Wizard DSL Updates and Cleanups" is
246
+ invisible to the changelog and can throw off version inference.
247
+
248
+ Check `git log origin/master --oneline` for the scopes in use (`wizard`, `ui`,
249
+ `policy`, `positioning`, `generators`, `auth`, `inputs`, `test`, `chore`,
250
+ `dashboard`, `i18n`, ...) and match the format when writing a PR title.
251
+
252
+ ### Release process
253
+
254
+ Publishing happens from a laptop. CI (`.github/workflows/release.yml`) does
255
+ **not** push to any registry: when the `vX.Y.Z` tag lands it only cuts the GitHub
256
+ Release (notes + built gem attached), so it cannot race the local publish.
257
+
258
+ ```bash
259
+ bundle exec rake release:prepare # or release:prepare[1.2.3]; bump + changelog + assets, commit (no push)
260
+ git show # review the bump commit
261
+ bundle exec rake release:publish # push gem + npm, then tag + push → CI cuts the Release
262
+ ```
263
+
264
+ `release:publish` is idempotent and resumable (skips a gem/npm already live, tags
265
+ only if missing). `rake release:version` shows the next version git-cliff would
266
+ pick. The bare `rake release` from bundler is neutralized on purpose; use the
267
+ `prepare` then `publish` flow.
169
268
 
170
- **Never publish a snippet you have not checked against this codebase.** Reading the
171
- docs is not enough; the docs have been wrong. Two real cases:
269
+ ## Writing Rules (docs, blog posts, skills)
172
270
 
173
- - `as: :phlexi_tag` was in three docs and a skill. `ResolvesTags#component_for`
174
- dispatches via `send(:"#{tag}_tag")`, so it resolved to `phlexi_tag_tag` and
175
- raised `NoMethodError`. The working values are `:phlexi_render` and `:phlexi`.
176
- - A per-portal override example declared `as: :text` on a text column (dead code)
177
- and commented that the definition decides which fields admins see (it does not).
271
+ Docs and posts here are read by developers **and** by AI assistants (the
272
+ `.claude/skills/` are synced to `docs/public/skills/` and shipped). A wrong
273
+ example does not just mislead a reader; it gets reproduced as code.
274
+
275
+ ### Verify every code sample
178
276
 
179
- When a claim is mechanical, prove it at runtime rather than by reading:
277
+ Never publish a snippet you have not checked against this codebase. The docs have
278
+ been wrong before (`as: :phlexi_tag` resolved to `phlexi_tag_tag` and raised;
279
+ working values are `:phlexi_render` and `:phlexi`). When a claim is mechanical,
280
+ prove it at runtime:
180
281
 
181
282
  ```bash
182
283
  cd test/dummy && RAILS_ENV=test bin/rails runner 'puts SomeClass.instance_methods.grep(/x/)'
183
284
  ```
184
285
 
185
- ### Invariants a sample must not violate
286
+ ### Document behavior, not internals
186
287
 
187
- - **Don't declare what is auto-detected.** `field :title` matching the detected type
188
- is dead code (`reference/resource/definition.md`).
189
- - **Definition = how a field renders. Policy = whether it appears.** "Only admins see
190
- this" is `permitted_attributes_for_*`, never a definition line or a `condition:`.
191
- - **Pages expose the record as `object`**, not `resource`.
192
- - **Interactions:** `attribute :resource` (no `class:`); rescue
288
+ - Don't declare what is auto-detected. `field :title` matching the detected type
289
+ is dead code.
290
+ - **Definition = how a field renders. Policy = whether it appears.** "Only admins
291
+ see this" is `permitted_attributes_for_*`, never a definition line or a
292
+ `condition:`.
293
+ - Pages expose the record as `object`, not `resource`.
294
+ - Interactions: `attribute :resource` (no `class:`); rescue
193
295
  `ActiveRecord::RecordInvalid` around any `create!`/`update!`/`save!`.
194
- - **Policies:** never put `*_attributes` hashes in `permitted_attributes_for_*`.
296
+ - Policies: never put `*_attributes` hashes in `permitted_attributes_for_*`.
195
297
 
196
- ### Voice
197
-
198
- Match the reference docs: direct, concrete, no throat-clearing. Specifically avoid
199
- the tells that make writing read as machine-generated:
298
+ ### Voice: avoid the machine-generated tells
200
299
 
201
300
  - **No em dashes.** Use commas, colons, parentheses, or two sentences.
202
- - No "It's not X, it's Y" or "The pitch isn't… The pitch is…" constructions.
203
- - No tidy-summary line after every example ("That's the Rails bargain in miniature").
204
- - No stacked negation before the real claim, no "critically", no "genuinely".
301
+ - No "It's not X, it's Y" or "The pitch isn't... The pitch is..." constructions.
302
+ - No tidy-summary line after every example.
303
+ - No stacked negation before the real claim; no "critically"; no "genuinely".
205
304
  - Cut a sentence that restates the one before it.
206
305
 
207
306
  Deliberate parallelism and a stated opinion are fine. The tell is *unearned*
@@ -209,71 +308,60 @@ rhetoric, not rhetoric itself.
209
308
 
210
309
  ### Blog mechanics
211
310
 
212
- Posts are markdown in `docs/blog/`. Frontmatter:
213
-
214
- ```yaml
215
- ---
216
- title: "Introducing Plutonium: Rails conventions, past CRUD" # must read cold
217
- titleTemplate: "Plutonium Blog"
218
- date: 2026-08-19
219
- description: One sentence. Becomes the RSS description and the index subtitle.
220
- author: Stefan Froelich
221
- tags: [announcement, rails]
222
- draft: true # omit to publish
223
- ---
224
- ```
225
-
226
- - Put `<BlogMeta />` directly under the `# ` heading; it renders date, author and
227
- the back link from frontmatter.
228
- - **A title must be legible with no context.** It travels via RSS, social and pasted
229
- links, where the site name is absent, so include both "Plutonium" and "Rails".
230
- "Plutonium" alone collides with the element and other projects.
231
- - **The slug must match the title.** Renaming the title means renaming the file;
232
- URLs are permanent once published.
233
- - A post is distinguished from the section index by having a `date`. `draft: true`
234
- hides it from the index and the feed.
235
- - `yarn docs:build` must pass. Dead internal links fail the build.
311
+ Posts are markdown in `docs/blog/` with frontmatter (`title`, `titleTemplate`,
312
+ `date`, `description`, `author`, `tags`, optional `draft: true`). Put `<BlogMeta />`
313
+ under the `#` heading. A title must be legible cold (it travels via RSS and social
314
+ with no site name), so include both "Plutonium" and "Rails". The slug must match
315
+ the title; renaming the title means renaming the file. A post is distinguished
316
+ from a section index by having a `date`. `yarn docs:build` must pass.
236
317
 
237
318
  ### Screenshots
238
319
 
239
- Match the existing ones or they look foreign next to each other:
240
-
241
- - **2480px wide** (1240 CSS at `deviceScaleFactor: 2`); height varies with content.
242
- - **Light mode, icon rail collapsed.** In `test/dummy`, set
243
- `localStorage.theme = 'light'` and `localStorage.pu_rail_pinned = 'false'`.
244
- - Capture from the running dummy app with realistic, **distinct** data. Generated
245
- titles that repeat across rows read as broken data.
246
- - Crop dead space: `magick shot.png -crop 2480x1290+0+0 +repage shot.png`.
247
- - Live under `docs/public/images/<section>/`.
320
+ Match the existing ones: 2480px wide (1240 CSS at `deviceScaleFactor: 2`), light
321
+ mode, icon rail collapsed (`localStorage.theme = 'light'`,
322
+ `localStorage.pu_rail_pinned = 'false'`). Use realistic, distinct data (repeated
323
+ generated titles read as broken data). Crop dead space with `magick`. Store under
324
+ `docs/public/images/<section>/`.
325
+
326
+ ## Settled Decisions (do not re-litigate)
327
+
328
+ - **SQLite for the dummy app** across all environments; production is multi-db
329
+ (primary/cache/queue/cable). Postgres is a test-only appraisal for concurrency
330
+ tests, not the app default.
331
+ - **Package + portal engine architecture** for multi-tenancy and features. New
332
+ features go in packages, wired through portal engines.
333
+ - **Generators-first**: every dummy-app artifact is generated, then customized.
334
+ - **The SaaS post-login flow ships as `pu:saas:welcome`** (welcome controller +
335
+ onboarding/entity-selection views + Rodauth redirects). This bridges
336
+ login → invite check → onboarding → entity selection → portal. It is built; do
337
+ not re-plan it.
248
338
 
249
339
  ## Skills System
250
340
 
251
- The `.claude/skills/` directory contains documentation for AI assistants:
252
- - Each skill is a focused guide on one concept
253
- - Skills are loaded based on user queries
254
- - Update skills when changing related functionality
255
- - Skills require a gem release to take effect
256
-
257
- ## Release Process
341
+ `.claude/skills/` holds the AI-assistant skills. They are synced into
342
+ `docs/public/skills/` by `yarn docs:sync-skills` (run automatically by
343
+ `docs:dev` / `docs:build`) and shipped with the docs. Update the relevant skill
344
+ when you change related behavior; skills require a gem release to take effect for
345
+ consumers.
258
346
 
259
- Publishing happens from a laptop. CI (`.github/workflows/release.yml`) does **not**
260
- push to any registry — when the `vX.Y.Z` tag lands it only cuts the GitHub Release
261
- (release notes + the built gem attached), so it can't race the local publish.
262
-
263
- ```bash
264
- # 1. Bump version + regenerate changelog + rebuild assets, commit (no push).
265
- # Version is optional — git-cliff computes it from conventional commits.
266
- bundle exec rake release:prepare # or release:prepare[1.2.3]
347
+ ## Adding Features
267
348
 
268
- git show # 2. review the bump commit
349
+ ### New field type
350
+ 1. Renderer in `lib/plutonium/ui/display/components/`
351
+ 2. Input in `lib/plutonium/ui/form/components/`
352
+ 3. Register in field-type mappings
353
+ 4. Tests and docs
269
354
 
270
- bundle exec rake release:publish # 3. push gem + npm, then tag + push → CI cuts the Release
271
- ```
272
-
273
- `release:publish` is idempotent and resumable: it skips a gem/npm already live and
274
- only tags if the tag is missing, so a partial failure can be re-run safely.
355
+ ### New generator
356
+ 1. Create in `lib/generators/pu/<category>/<name>_generator.rb`
357
+ 2. Templates in `lib/generators/pu/<category>/<name>/templates/`
358
+ 3. Tests in `test/generators/`
359
+ 4. Document in `docs/reference/generators/`
275
360
 
276
- Helper: `rake release:version` shows the next version git-cliff would pick.
361
+ ### New interaction response
362
+ 1. Response class in `lib/plutonium/interaction/response/`
363
+ 2. Helper on `Plutonium::Interaction::Outcome::Success`
364
+ 3. Document usage
277
365
 
278
366
  ## Key Files Reference
279
367
 
@@ -287,22 +375,6 @@ Helper: `rake release:version` shows the next version git-cliff would pick.
287
375
  | Base definition | `lib/plutonium/resource/definition.rb` |
288
376
  | Interactions | `lib/plutonium/interaction/base.rb` |
289
377
  | Generators | `lib/generators/pu/` |
290
-
291
- ## Common Tasks
292
-
293
- ### Fix a bug in policies
294
- 1. Find the issue in `lib/plutonium/resource/policy.rb` or related
295
- 2. Write a failing test in `test/plutonium/resource/policy_test.rb`
296
- 3. Fix the bug
297
- 4. Update docs if behavior changed
298
-
299
- ### Add a generator option
300
- 1. Add option in generator class
301
- 2. Update templates to use option
302
- 3. Test with dummy app
303
- 4. Document in `docs/reference/generators/`
304
-
305
- ### Update documentation
306
- 1. Edit files in `docs/`
307
- 2. If user-facing behavior, update relevant skill in `.claude/skills/`
308
- 3. Run `yarn docs:build` to verify no broken links
378
+ | Release automation | `lib/tasks/release.rake` |
379
+ | Dummy DB config | `test/dummy/config/database.yml` |
380
+ | Generator test helper | `test/support/generator_test_helper.rb` |
data/SECURITY.md CHANGED
@@ -12,7 +12,7 @@ please upgrade before reporting an issue to confirm it still reproduces.
12
12
 
13
13
  | Version | Supported |
14
14
  | ------- | ------------------ |
15
- | Latest release (`0.63.x`) | :white_check_mark: |
15
+ | Latest release (`0.65.x`) | :white_check_mark: |
16
16
  | Older releases | :x: |
17
17
 
18
18
  ## Reporting a Vulnerability