discourse/AI-AGENTS.md
Sam d9c0c2da53
DEV: add plugin post-migration generator and migration skill (#39125)
Add a dedicated Rails generator for plugin post-deployment migrations
and document the migration skill so plugin authors can place destructive
database changes in the correct directory.
2026-04-08 09:34:55 +10:00

123 lines
5.3 KiB
Markdown

# AI Coding Agent Guide
Project-specific instructions for AI agents. MUST be loaded at conversation start.
## Default Mode
- Architect mode enabled by default: detailed analysis, patterns, trade-offs, architectural guidance
- Stop and ask for context if unable to write code meeting guidelines
## Development Rules
Discourse is large with long history. Understand context before changes.
### All Files
- Always lint changed files
- Create subagent to review changes against this file after completing tasks
### Toolset
- Use `pnpm` for JavaScript, `bundle` for Ruby
- Use helpers in bin over bundle exec (bin/rspec, bin/rake, bin/lint)
### JavaScript and UI
- No empty backing classes for template-only components unless requested
- Use FormKit for forms, see ./docs/developer-guides/docs/03-code-internals/21-form-kit.md (`frontend/discourse/app/form-kit`)
- Use BEM for CSS, see ./docs/developer-guides/docs/03-code-internals/25-css-guidelines-bem.md
- Make display strings translatable (use placeholders, not split strings)
- Use "Sentence case" for strings, not "Proper Case" or "lower case"
### JSDoc
- Do not add JSDoc to any new code you write.
- If JSDoc already exists, ensure any changes you make keep it accurate and up to date.
## Testing
- Do not write unnecessary comments in tests, every single assertion doesn't need a comment
- Don't test functionality handled by other classes/components
- Don't write obvious tests
- Ruby: use `fab!` over `let()`, system tests for UI (`spec/system`), use page objects for system spec finders (`spec/system/page_objects`)
### fab! Syntax
- `fab!(:user)` - creates object using Fabricator defaults (name matches fabricator)
- `fab!(:user_1, :user)` - preferred when variable name differs from fabricator, no custom attributes
- `fab!(:user) { Fabricate(:user, username: "some_username") }` - with block for custom attributes
### Page Objects (System Specs)
- Located in `spec/system/page_objects/pages/`, inherit from `PageObjects::Pages::Base`
- NEVER store `find()` results - causes stale element references after re-renders
- Use `has_x?` / `has_no_x?` patterns for state checks (finds fresh each time)
- Action methods find+interact atomically, return `self` for chaining
- Don't assert immediate UI feedback after clicks (tests browser, not app logic)
### Commands
#### Ruby RSpec tests
To run all RSpec examples in file use `bin/rspec <path>`. Example:
```bash
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:
```bash
bin/rspec spec/path/file_spec.rb:123
```
#### JavaScript Tests
```bash
# 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
```bash
# 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
## Site Settings
- Configured in `config/site_settings.yml` or `config/settings.yml` for plugins
- Functionality in `lib/site_setting_extension.rb`
- Access: `SiteSetting.setting_name` (Ruby), `siteSettings.setting_name` (JS with `@service siteSettings`)
## Services
- Extract business logic (validation, models, permissions) from controllers
- docs/developer-guides/docs/03-code-internals/19-service-objects.md
- Use the skill at .skills/discourse-service-authoring
- Examples: `app/services` (only classes with `Service::Base`)
## Database & Performance
- ActiveRecord: use `includes()`/`preload()` (N+1), `find_each()`/`in_batches()` (large sets), `update_all`/`delete_all` (bulk), `exists?` over `present?`
- Migrations: use the skill at .skills/discourse-migration before writing or reviewing any migration
- Queries: use `explain`, specify columns, strategic indexing, `counter_cache` for counts
## HTTP Response Codes
- **204 No Content**: Use `head :no_content` for successful operations that don't return data
- DELETE operations that successfully remove a resource
- UPDATE/PUT operations that succeed but don't need to return modified data
- POST operations that perform an action without creating/returning resources (mark as read, clear notifications)
- **200 OK**: Use `render json: success_json` when returning confirmation data or when clients expect a response body
- **201 Created**: Use when creating resources, include location header or resource data
- **Do NOT use 204 when**:
- Creating resources (use 201 with data)
- Returning modified/useful data to the client
- Clients expect confirmation data beyond success/failure
## Security
- XSS: use `{{}}` (escaped) not `{{{ }}}`, sanitize with `sanitize`/`cook`, no `innerHTML`, careful with `@html`
- Auth: Guardian classes (`lib/guardian.rb`), POST/PUT/DELETE for state changes, CSRF tokens, `protect_from_forgery`
- Input: validate client+server, strong parameters, length limits, don't trust client-only validation
- Authorization: Guardian classes, route+action permissions, scope limiting, `can_see?`/`can_edit?` patterns
## Knowledge Sharing
- ALWAYS persist information for ALL developers (no conversational-only memory)
- Follow project conventions, prevent knowledge silos
- Recommend storage locations by info type
- Inform when this file changes and reloads