AI-AGENTS.md
Project-specific instructions for AI agents. MUST be loaded at conversation start.
Discourse is large with long history. Understand context before changes.
pnpm for JavaScript, bundle for Rubyfrontend/discourse/app/form-kit)fab! over let(), system tests for UI (spec/system), use page objects for system spec finders (spec/system/page_objects)fab!(:user) - creates object using Fabricator defaults (name matches fabricator)fab!(:user_1, :user) - preferred when variable name differs from fabricator, no custom attributesfab!(:user) { Fabricate(:user, username: "some_username") } - with block for custom attributesspec/system/page_objects/pages/, inherit from PageObjects::Pages::Basefind() results - causes stale element references after re-rendershas_x? / has_no_x? patterns for state checks (finds fresh each time)self for chainingTo run all RSpec examples in file use bin/rspec <path>. Example:
bin/rspec spec/path/file_spec.rb
To run one or more RSpec examples or groups, append the line number to the path. bin/rspec <path>:<line_number>. Example:
bin/rspec spec/path/file_spec.rb:123
# JavaScript tests - bin/qunit
bin/qunit --help # detailed help
bin/qunit path/to/test-file.js # Run all tests in file
bin/qunit path/to/tests/directory # Run all tests in directory
# Linting
bin/lint path/to/file path/to/another/file
bin/lint --fix path/to/file path/to/another/file
bin/lint --fix --recent # Lint all recently changed files
ALWAYS lint any changes you make
config/site_settings.yml or config/settings.yml for pluginslib/site_setting_extension.rbSiteSetting.setting_name (Ruby), siteSettings.setting_name (JS with @service siteSettings)app/services (only classes with Service::Base)includes()/preload() (N+1), find_each()/in_batches() (large sets), update_all/delete_all (bulk), exists? over present?explain, specify columns, strategic indexing, counter_cache for countshead :no_content for successful operations that don't return data
render json: success_json when returning confirmation data or when clients expect a response body{{}} (escaped) not {{{ }}}, sanitize with sanitize/cook, no innerHTML, careful with @htmllib/guardian.rb), POST/PUT/DELETE for state changes, CSRF tokens, protect_from_forgerycan_see?/can_edit? patterns