8.1 KiB
8.1 KiB
backend/AGENTS.md
Scope
These rules apply to work under backend/.
This is a Rails API app using Active Record, RSpec, request specs, service objects, representation classes, and version tables for post/tag/wiki history.
Commands
Use commands backed by files and dependencies in this directory:
bin/setup
bin/dev
bin/rails
bin/rake
bin/rubocop
bin/brakeman
bundle exec rspec
Common checks:
bundle exec rspec
bin/rubocop
bin/brakeman
Common Rails commands:
bin/rails db:prepare
bin/rails db:migrate
bin/rails routes
bin/rails server
After backend behavior changes, run the relevant RSpec files. For broad backend changes, run:
bundle exec rspec
If a command cannot be run or fails, report the exact command and failure.
Rails structure
app/controllers: API controllers.app/models: Active Record models and concerns.app/representations: JSON response shaping.app/services: domain services such as version recorders, wiki commit, YouTube sync, and similarity calculation.config/routes.rb: public API routes.db/migrate: migrations.db/schema.rb: schema snapshot.lib/tasks: custom Rake tasks.spec: RSpec tests.
Before changing behavior, inspect the matching route, controller, model, service, representation, and spec.
Ruby style
- Prefer precise, minimal changes.
- Use single quotes unless interpolation or escaping makes double quotes better.
- Do not put a space before Ruby method-call parentheses.
- Never put a line break immediately before
)in Ruby. - Do not use
%wor%iin new Ruby code. - Never write a Ruby line longer than 99 characters.
- Aim to keep Ruby lines within 79 characters where practical.
- For small Ruby method definitions that take keyword arguments, match the local no-parentheses style when nearby code uses it.
- Treat Ruby hash
{ ... }style and Ruby block{ ... }style as separate rules. - Do not format Ruby hashes like Ruby blocks.
- For Ruby hashes, keep the closing
}on the same line as the final pair. - Keep the first pair on the same line as
{by default. - If the hash would exceed the line limit, break after
{and indent pairs by 4 spaces. - Put one logical pair per line when the expression would otherwise become dense.
- For Ruby arrays, never put whitespace or a line break immediately before
]. - Keep the first element on the same line as
[by default. - If an array would exceed the line limit, break after
[and indent elements by 4 spaces. - For Ruby blocks, use 2-space indentation for the block body.
- Keep comments short and useful; avoid narrating obvious code.
- Do not add production dependencies without approval.
Authentication and authorization
- Authentication is handled through the
X-Transfer-Codeheader inApplicationController#authenticate_user. current_useris set by looking upUser.inheritance_code.- Do not bypass or weaken the
X-Transfer-Codeflow unless the task explicitly changes authentication. - Unauthenticated write actions should return
:unauthorizedconsistently with existing controllers. - Role checks use
Userenum roles:guest,member, andadmin. - Use
current_user.gte_member?for member-or-admin write permissions where existing controllers do so. - Use
current_user.admin?only for admin-only paths, such as tag child relationship changes. - Do not replace role checks with looser presence checks.
BAN and IP BAN
ApplicationControllerruns these before actions in order:reject_banned_ip_address!authenticate_userreject_banned_user!
- User and IP bans use
banned_at, not a booleanbannedcolumn. User#banned?andIpAddress#banned?checkbanned_at.present?.- Do not weaken BAN or IP BAN behavior.
- If changing request authentication or controller before actions, add or update request specs covering banned users and banned IP addresses.
RSpec
- Prefer RSpec for new backend tests.
- Put API behavior coverage under
spec/requests. - Put model behavior under
spec/models. - Put service behavior under
spec/services. - Put Rake task coverage under
spec/tasks. spec/rails_helper.rbloadsspec/support/**/*.rb.- Request specs include
AuthHelperandJsonHelper. AuthHelper#sign_in_as(user)stubsApplicationController#current_user; use it when matching existing request spec style.- Add or update request specs for API behavior changes, especially status codes, permissions, response shape, and version conflict behavior.
Migrations
- Keep migrations and
db/schema.rbconsistent. - Use reversible migrations where practical; otherwise define explicit
upanddown. - For data backfills inside migrations, follow the existing pattern of
defining migration-local
ActiveRecord::Baseclasses withself.table_name. - Preserve existing indexes, foreign keys, check constraints, and null constraints.
- Be careful with MySQL-specific options already present in migrations, such as
after:. - Do not edit old migrations just to change current behavior unless explicitly requested; add a new migration.
Version tables
- Versioned records include posts, tags, nico tags, and wiki pages.
- Current records have
version_no; version tables have positiveversion_nowith unique indexes scoped to the parent record. - Version event types are
create,update,discard, andrestore. - Version rows are readonly through the
VersionRecordconcern. - Use the existing recorder services instead of manually inserting version
rows in application code:
PostVersionRecorderTagVersionRecorderNicoTagVersionRecorderWikiVersionRecorderTagVersioning
VersionRecorderlocks the current record, validates sequence consistency, skips unchanged update snapshots, creates the next version row, and updates the recordversion_no.- Do not update versioned records without considering whether a version snapshot must be created.
- For optimistic concurrency paths, preserve
base_version_no,force, andmergesemantics and cover conflicts in request specs.
Domain cautions
- Posts have tag snapshots, parent post implications, original-created ranges, viewed state, and version conflict behavior.
- Tags have canonical names, aliases through
TagName, categories, parent implications, discard behavior, and version snapshots. - Nico tags have separate relation/version behavior; do not treat them like normal editable tags without checking existing code.
- Wiki pages involve page content, revisions/history, version rows, title/tag-name behavior, and diff/restore paths.
- Materials, theatres, and comments have user and permission checks; inspect the controller before changing them.
API responses
- Use representation classes under
app/representationswhen existing endpoints do. - Keep response keys consistent with existing JSON contracts.
- Frontend code expects camelCase conversion client-side, while Rails params and JSON keys are generally snake_case.
- Preserve existing HTTP status conventions:
:unauthorizedfor no user,:forbiddenfor insufficient role or banned user,:not_foundfor missing records, and:unprocessable_entityfor validation failures. - For diagnostic or internal helper JSON, prefer a deliberately light response shape over full representation classes when callers only need identifiers, labels, URLs, or weights.
Active Record performance
- When a controller action serializes nested associations, preload the associations it will touch instead of allowing N+1 queries.
- Be sensitive to N+1 queries in all backend work.
- Avoid introducing N+1 queries, and proactively fix existing N+1 issues when you find them in the code path you are editing.
- When an association may already be preloaded, prefer loaded-association checks that reuse the preloaded data without losing the efficient database path.
Files to avoid in routine work
- Do not inspect or edit
tmp/,log/,storage/,vendor/, or dependency directories unless explicitly needed. - Do not modify generated schema or migration output without the corresponding migration when schema changes are made.