* Document instruction inventory and preservation decisions Trace main history from September 2025 through September 2026, including earlier policy origins. Record preserved requirements, detailed-guide destinations, stale facts, harness boundaries and explicit policy-strength decisions before consolidating instruction sources. * Consolidate repository instructions into shared guidance Keep AGENTS concise and vendor neutral, move detailed conventions into shared guides, and use thin adapters with preserved Cursor scopes. Preserve the strict pre-PR checks globally and document the stronger scope, retired migration pin and rule-generation trigger. Update existing API guidance verification without changing application behavior. * Narrow the always-on Cursor UI adapter and correct the SimpleFIN comment Split the design-system guidance out of docs/llm-guides/ui.md into docs/llm-guides/design-system.md. The ui-ux-design-guidelines rule is alwaysApply: true, so importing all of ui.md loaded the Stimulus, localization and ViewComponent guidance (previously confined to scoped rules) on every Cursor session; the always-on adapter now imports only the design-system guide, matching the scope it had before the consolidation. view_conventions and stimulus_conventions keep the full UI guide. Also correct the stale Provider::Simplefin header comment: pending inclusion defaults on and is resolved by the importer (explicit argument, then SIMPLEFIN_INCLUDE_PENDING, then Setting.syncs_include_pending); the previous comment described the flag as default-off. * Read guidance files as UTF-8 in the API consistency validators The frontmatter regex match ran against content read with the locale default external encoding; the Cursor rule's description contains an em dash, so under US-ASCII (LC_ALL=C) Regexp#match raised ArgumentError, breaking the standalone no-Rails fallback the docs point contributors to. Read all checked files with an explicit UTF-8 encoding in both the standalone script and the Rails test.
2.9 KiB
Views, components and interaction
Design tokens, DS::* reuse and reviewer severity live in the
design system guide, which is also the always-on Cursor
design adapter. This guide covers templates, Hotwire, Stimulus and localization.
Hotwire and templates
Prefer semantic native HTML, including <dialog> and <details><summary>, using
existing DS wrappers where available. Use Turbo Frames for page sections. Turbo
Streams should enhance functionality, not be its sole dependency.
Keep state in URL query parameters before reaching for local storage or sessions; use the database when persistent state is necessary. Format money, numbers and dates server-side and pass display values to Stimulus. Client-side behavior is appropriate for interactions such as bulk selection where server round trips would hurt usability.
Use an existing component first, then an existing partial. Create a ViewComponent for reusable or complex styling/logic, variants/sizes, interaction, slots or a configurable API, and accessibility/ARIA behavior. Use partials for mostly static, simple or context-specific template content. Keep domain logic out of ERB; compute presentation logic in helpers or component Ruby files.
Follow the names of nearby components, including the DS::* namespace; do not
impose the obsolete ButtonComponent/DialogComponent examples on this design
system. Partials use an underscore prefix; shared partials live in
app/views/shared/, context-specific partials under their controller's views.
Stimulus
- Declare actions in HTML (
data-action="click->toggle#toggle") rather than imperatively registering event listeners in controller initialization. - Keep controllers focused on one responsibility or tightly related responsibilities; domain logic belongs on the server.
- Aim for fewer than seven targets. Use private helpers and a clear public API, with Stimulus callbacks, actions, targets, values and classes.
- Controllers in
app/components/belong only to their component templates; global controllers inapp/javascript/controllers/may be shared across views. - Pass data through
data-*-valueattributes rather than inline JavaScript. Use Stimulus targets rather than manualgetElementByIdlookups.
Localization
Use t() for all user-facing strings and update the corresponding locale files
under config/locales/. Follow the feature's existing locale organization; there
is no requirement to put every new key in a single en.yml.
Use descriptive, hierarchical keys such as accounts.index.title and
components.transaction_details.show_details. Group related keys together; use
interpolation (t("users.greeting", name: user.name)) and Rails pluralization
(t("transactions.count", count: count)) for dynamic text. Keep examples localized
too. Configure missing translations to raise during development; do not assume that this option is currently enabled in the
checked-in environment configuration.