discourse/AI-AGENTS.md
Martin Brennan 9a6fe5c370
FIX: Do not show new upcoming change dot on new sites and new staff (#39682)
When a site is created, any staff users created in that
period should not see a blue dot for the upcoming change
link in the admin sidebar, we don't need to notify them
of "new" upcoming changes when they have only just joined.

Additionally, any new admins or moderators created later
on should also not immediately see a blue dot in the
upcoming change sidebar, though these changes are "new"
to them, they could potentially be quite old upcoming changes.
2026-05-05 12:02:08 +10:00

5.4 KiB
Vendored

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:

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

# 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

# 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. Use user.guardian shorthand not Guardian.new(user)

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