Reviewed-on: #362 Co-authored-by: miteruzo <miteruzo@naver.com> Co-committed-by: miteruzo <miteruzo@naver.com>
8.4 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.
Do not create, modify, or run tests unless the user explicitly asks for test work. When the user asks for tests, keep working and rerun them until they pass or the remaining failure is clearly blocked.
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 only when the user explicitly asks for tests.
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 only when the user explicitly asks for tests, 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. Cover conflicts in request specs only when the user explicitly asks for tests.
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.