diff --git a/AGENTS.md b/AGENTS.md index 168db54..cc4ad04 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,16 +12,21 @@ BTRC Hub / タグ広場 is a split Rails API and React frontend repository. ## Stack - Backend: Ruby `3.2.2` from `backend/.ruby-version`, Rails `~> 8.0.2`. -- Backend dependencies include `mysql2`, `sqlite3`, `rspec-rails`, `factory_bot_rails`, `rack-cors`, `jwt`, `discard`, `gollum`, `whenever`, `aws-sdk-s3`, `brakeman`, and `rubocop-rails-omakase`. +- Backend dependencies include `mysql2`, `sqlite3`, `rspec-rails`, + `factory_bot_rails`, `rack-cors`, `jwt`, `discard`, `gollum`, `whenever`, + `aws-sdk-s3`, `brakeman`, and `rubocop-rails-omakase`. - Frontend: React `^19.1.0`, TypeScript `~5.8.3`, Vite `^6.3.5`. -- Frontend data/UI dependencies include Axios, TanStack Query, Tailwind CSS, Framer Motion, Radix UI components, lucide-react, MDX/Markdown tooling, and Zustand. +- Frontend data/UI dependencies include Axios, TanStack Query, Tailwind CSS, + Framer Motion, Radix UI components, lucide-react, MDX/Markdown tooling, and + Zustand. ## Main directories - `backend/app/controllers`: Rails API controllers. - `backend/app/models`: Active Record models. - `backend/app/representations`: API response representation classes. -- `backend/app/services`: domain services such as version recording, wiki commit, YouTube sync, and similarity calculation. +- `backend/app/services`: domain services such as version recording, + wiki commit, YouTube sync, and similarity calculation. - `backend/config/routes.rb`: API routes. - `backend/db/migrate`: migrations. - `backend/db/schema.rb`: current schema snapshot. @@ -84,52 +89,210 @@ cd frontend npm run dev npm run build npm run lint +npm run test +npm run test:run npm run preview ``` -`npm run build` runs `tsc -b && vite build`, then `postbuild` runs `node scripts/generate-sitemap.js`. +`npm run build` runs `tsc -b && vite build`, then `postbuild` runs +`node scripts/generate-sitemap.js`. -Do not write or report `npm test` as a repository command unless a `test` script is added to `frontend/package.json`. +`npm run test` runs Vitest in watch mode. Use `npm run test:run` for a non-watch frontend test run. ## Coding style - Prefer precise, minimal changes. - Do not flatter or over-explain. - Explain risks directly. -- Prefer single quotes for strings unless interpolation or escaping makes double quotes better. +- Prefer single quotes for strings unless interpolation or escaping makes + double quotes better. - Ruby: never put a space before method-call parentheses. +- Ruby: `render` 系メソッド呼び出しでは、keyword 引数付きでも括弧を書かない。 +- Ruby: never put a line break immediately before `)`. - Ruby: do not use `%w` or `%i`. -- TypeScript and Python: use GNU-style spacing before parentheses where syntactically valid. +- In Ruby, when an `if` condition is split across multiple lines and combines + clauses with `&&` or `||`, wrap the whole condition in parentheses. +- Ruby hashes are not blocks; keep `}` on the same line as the final pair. +- Ruby hashes keep the first pair on the same line as `{` unless line length + requires a break. +- Short Ruby hashes may stay visually compact across two lines with the first + pair kept on the opening line and aligned continuation pairs below it. +- Ruby blocks use separate `{ ... }` rules from hashes, with 2-space body + indentation. +- For 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. +- TypeScript and Python: use GNU-style spacing before parentheses where + syntactically valid. +- Never write Ruby, TypeScript, or TSX lines longer than 99 characters. +- Aim to keep Ruby, TypeScript, and TSX lines within 79 characters where practical. +- TypeScript and TSX use 4-space logical indentation. +- In TypeScript and TSX only, replace every leading run of 8 spaces with a tab. +- Tabs are only for leading indentation, never for spaces after non-space text. +- TypeScript and TSX imports may stay on one line if they remain within the + line limit; do not expand short type-only imports mechanically. +- In TypeScript and TSX, when a function takes one destructured object + argument plus an inline type, prefer this shape when it fits locally: + +```ts +const helper = ( + { value, flag }: { value: string + flag: boolean }, +): Result => { + // ... +} +``` + +- In TypeScript and TSX, put `switch` case block braces on their own lines + when a case needs a lexical block: + +```ts +case 'yes': +case 'no': + { + const expected = valueFor (item) + return expected == null || expected === answer + } +``` + +- In TypeScript and TSX, use `value == null` and `value != null` as the + default nullish checks. Do not use `=== null`, `=== undefined`, + `!== null`, or `!== undefined`. +- If code appears to need a distinction between `null` and `undefined`, treat + that as a design smell and revise the logic to avoid the distinction. + External library APIs that explicitly require distinguishing the two are the + only exception. +- In TypeScript and TSX, keep short arrays on one line when they fit under the + line limit; break arrays only when readability or line length requires it. +- In TypeScript and TSX, when a ternary expression is split across multiple + lines, align `?` and `:` with the condition expression. Do not indent `?` and + `:` one extra level under the condition. + +```ts +const value = + condition + ? consequent + : alternate +``` + +- In TypeScript and TSX, keep short ternary expressions on one line when they + fit cleanly under the line limit. +- In TypeScript and TSX, prefer ternary expressions for simple conditional + value selection. Do not replace a clear ternary with `if` statements, and do + not introduce immediately invoked functions just to avoid or reformat a + ternary expression. +- In TypeScript and TSX, do not write `let` followed by later `if` assignments + when the value can be expressed as a single `const` initializer. Prefer + `const` because it prevents accidental later reassignment. +- When fixing formatting, change formatting only. Do not change expression + structure, control flow, or variable mutability unless the requested style + explicitly requires it. - Do not add production dependencies without explicit approval. +- 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. ## Backend rules -- Inspect existing routes, controllers, models, services, and specs before editing backend behavior. -- For API behavior changes, add or update request specs under `backend/spec/requests`. -- Prefer RSpec for new backend tests; existing minitest files under `backend/test` do not make minitest the default for new coverage. +- Inspect existing routes, controllers, models, services, and specs before + editing backend behavior. +- For API behavior changes, add or update request specs under + `backend/spec/requests` only when the user explicitly asks for tests. +- Prefer RSpec for new backend tests; existing minitest files under + `backend/test` do not make minitest the default for new coverage. - Do not weaken authentication, BAN user checks, or IP BAN checks. -- Preserve the `X-Transfer-Code` user identification flow unless the task explicitly changes authentication. -- Be careful with version tables, `version_no`, optimistic concurrency, wiki revisions, and restore/diff behavior. +- Preserve the `X-Transfer-Code` user identification flow unless the task + explicitly changes authentication. +- Be careful with version tables, `version_no`, optimistic concurrency, + wiki revisions, and restore/diff behavior. - Be careful with tag names, tag normalization, implications, similarities, and discard behavior. +- Be sensitive to N+1 queries; avoid introducing them and proactively fix + existing N+1 issues in the code path being edited. - Keep migration files and `backend/db/schema.rb` consistent when changing schema. ## Frontend rules - Use `frontend/src/lib/api.ts` for API calls so headers and camelCase conversion stay consistent. -- Add or reuse TanStack Query keys through `frontend/src/lib/queryKeys.ts`; avoid ad hoc query key arrays. +- Add or reuse TanStack Query keys through `frontend/src/lib/queryKeys.ts`; + avoid ad hoc query key arrays. - Encode URL path-segment values with `encodeURIComponent`. - React hooks must be called unconditionally. -- Keep page-level code under `frontend/src/pages` and shared UI/feature code under `frontend/src/components` unless existing patterns point elsewhere. +- Keep page-level code under `frontend/src/pages` and shared UI/feature code + under `frontend/src/components` unless existing patterns point elsewhere. - Match existing Tailwind, component, and import alias conventions. +- In TypeScript and TSX, prefer direct comparison operators such as `===` and + `!==` over negating a comparison like `!(a === b)`. +- In TypeScript and TSX, prefer `++i` or `--i` over `i += 1` or `i -= 1` for + simple unit-step counter updates. +- For user-facing Japanese text, prefer modern kana usage and natural current + phrasing over historical spellings or awkward literal wording. +- For user-facing Japanese ellipses, prefer `……` over ASCII `...`. + +### Frontend TSX style + +- Preserve the local TSX formatting style. +- Do not normalize TSX to common Prettier-style React formatting unless + explicitly asked. +- Prefer `const` arrow functions for TypeScript/TSX component and helper declarations. +- Put two blank lines before and after top-level `const` function + declarations, unless imports, exports, or file boundaries make that awkward. +- In TSX, indent with 4-space logical indentation. +- A leading tab is exactly equivalent to 8 leading spaces. +- Keep a tag's closing marker on the same line as the final prop when the tag + spans multiple lines. +- Do not put `/>` or `>` on its own line unless the existing surrounding code + does so. +- Keep JSX closing parentheses in the existing compact style, for example + `)` rather than moving `)` onto a separate line. +- Do not add braces around `if`, `else`, or `for` bodies when the body is a + single physical line. +- Always add braces around `if`, `else`, or `for` bodies when the body spans + two or more physical lines, even if it is one statement. +- Do not use a leading semicolon for expression statements such as + `;([...]).forEach(...)`; rewrite the expression to avoid ASI hazards + explicitly, for example with `void`. + +Preferred: + +```tsx +const PostFormTagsArea: FC = ({ tags, setTags, errors, ...rest }) => { + return ( +