# AGENTS.md

## Project overview

BTRC Hub / タグ広場 is a split Rails API and React frontend repository.

- Backend: Rails API under `backend/`.
- Frontend: React + TypeScript + Vite under `frontend/`.
- Docs: lightweight command notes under `docs/`.
- There is no README or Makefile at the repository root as of this inspection.

## 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`.
- 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.

## 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/config/routes.rb`: API routes.
- `backend/db/migrate`: migrations.
- `backend/db/schema.rb`: current schema snapshot.
- `backend/lib/tasks`: custom Rake tasks.
- `backend/spec`: RSpec tests.
- `backend/test`: Rails minitest files that still exist in the tree.
- `frontend/src/App.tsx`: frontend route definitions and initial user setup.
- `frontend/src/pages`: page-level React components.
- `frontend/src/components`: shared and feature components.
- `frontend/src/lib`: API client helpers, query keys, prefetchers, and domain helpers.
- `frontend/src/stores`: Zustand stores.
- `docs/commands.md`: command notes.

## Commands

Only list commands that are backed by files inspected in this repository.

### Backend

The following binstubs exist under `backend/bin`:

```sh
cd backend
bin/setup
bin/dev
bin/rails
bin/rake
bin/rubocop
bin/brakeman
bin/kamal
bin/thrust
```

Common Rails/Rake usage through existing binstubs:

```sh
cd backend
bin/rails db:prepare
bin/rails db:migrate
bin/rails routes
bin/rails server
bin/rake
bin/rubocop
bin/brakeman
```

RSpec is present in `Gemfile` and `.rspec` exists:

```sh
cd backend
bundle exec rspec
```

### Frontend

The following npm scripts exist in `frontend/package.json`:

```sh
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 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.
- For Japanese text, follow the 1986 Cabinet Notice
  《現代仮名遣い》 as the default orthography.
- For Japanese kanji spelling, do not apply
  《当用漢字による書きかえ》; prefer the original spelling as the formal one.
- 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`.
- Ruby: TypeScript / TSX の associative syntax に近い感覚で読むこと。
  Hash の `}` は block の `}` ではない。call の `)` も function 定義の
  parameter-list `)` ではない。delimiter の役割を見誤らないこと。
- In Ruby, when an `if` condition is split across multiple lines and combines
  clauses with `&&` or `||`, wrap the whole condition in parentheses.
- Ruby hash / block / call delimiter の詳細は、下の
  `Ruby delimiter and wrapping rules` を正本として扱ふこと。
- 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 4 spaces deeper than the statement's base indentation.

### Ruby delimiter and wrapping rules

- Ruby でも、closing delimiter は glyph ではなく syntax role で判定する。
  `}` が block close なのか hash close なのか、`)` が method call なのか
  grouping なのかを区別してから配置すること。
- Ruby の multi-line method call では、receiver / method と opening `(` を
  同じ行に保ち、closing `)` を単独行へ落とさない。
- Ruby の multi-line hash literal / keyword-like argument hash では、opening
  `{` を最初の pair と同じ行に置き、closing `}` を最後の pair と同じ行に
  置く。Prettier 的な縦開き・縦閉じをしない。
- Ruby の guard 条件は、1 行で収まるなら modifier 形式を優先する。
  99 文字を超えるなら block 形式へ切り替へるか、message 定数化などで縮める。
- Ruby の method chain や call argument を折り返す際、call-site の `)` を
  block close のやうに独立させない。
- Ruby では、行末の `\` を用途を問はず一切使用しない。
- Ruby では、文字列連結、logger message、method call、条件式、SQL 断片、
  正規表現その他すべての式で、行末バックスラッシュによる継続を禁止する。
- Ruby の block body は、その基準位置から 2 空白深くする。
- Ruby の wrapped expression、method argument、array element、hash pair などの
  continuation indentation は、その statement の基準位置から 4 空白深くする。
- Ruby の continuation indentation を、行頭からの絶対空白数として扱はない。
- Ruby では、暗黙的に継続可能な構文を優先し、method call、array、Hash 及び
  括弧内ではバックスラッシュなしで改行する。
- Ruby では、行長制限を守るために行末バックスラッシュを導入してはならない。
- Ruby で行末バックスラッシュが必要に見える場合は、括弧内で自然に改行する、
  一つの文字列補間へまとめる、中間変数へ分ける、`format` を使ふ、heredoc を
  使ふ、array 又は Hash を組み立ててから処理する、条件式全体を括弧で囲む、
  method へ抽出する、のいづれかへ書き換へる。
- Ruby では、一つの文字列を、改行をまたいだ隣接文字列 literal として記述しない。
- Ruby では、method argument 内でも、複数の文字列 literal を区切りなしで縦に
  並べない。
- RSpec の `describe`、`context`、`it` 等の description が長い場合は、意味を
  保ったまま一行へ収まる文言へ短縮する。
- 文字列を短縮できない場合は、用途に応じて `format`、heredoc 又は中間変数を
  検討する。
- ただし RSpec description では、原則として簡潔な一行の文字列を使ふ。
- formatter 又は自動修正にも、Ruby の行末バックスラッシュを生成させない。
- 新規 code だけでなく、今回触れる Ruby code にも行末バックスラッシュを残さない。
- 例へば class body 内の array は、class body の 2 空白を基準に、更に 4 空白
  深くするため、結果として行頭から 6 空白になる。

Bad:

```rb
response = Example.fetch(
  value,
  option: option,
)
```

Good:

```rb
response = Example.fetch(
    value,
    option: option)
```

Bad:

```rb
payload = {
  title: title,
  url: url,
}
```

Good:

```rb
payload = {
    title: title,
    url: url }
```

Bad:

```rb
raise ArgumentError, 'URL が長すぎます．' if url.bytesize > MAX_URL_BYTES && flag.present?
```

Good:

```rb
if url.bytesize > MAX_URL_BYTES && flag.present?
  raise ArgumentError, 'URL が長すぎます．'
end
```

Bad:

```rb
Rails.logger.info(
    "post_import_metadata_fetch_failure "\
    "#{ payload.to_json }")
```

Good:

```rb
payload = {
    error: e.class.name,
    message: e.message }

Rails.logger.info(
    "post_import_metadata_fetch_failure #{ payload.to_json }")
```

Bad:

```rb
message = "first "\
          "second"
```

Good:

```rb
message = format(
    '%<first>s %<second>s',
    first: 'first',
    second: 'second')
```

Bad:

```rb
result = first_value + \
         second_value
```

Bad:

```rb
it(
    'returns 409 when stale changes '
    'do not conflict'
) do
```

Good:

```rb
it 'returns mergeable 409 for stale non-conflicting changes' do
```

Bad:

```rb
records.each {
  do_work(_1) }
```

Good:

```rb
records.each {
  do_work(_1)
}
```
- 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.
- In TypeScript and TSX, use 2-space block indentation.
- In TypeScript and TSX, block bodies for components, functions, callbacks,
  `if`, `try`, `catch`, `finally`, loops, and JSX nesting use 2 spaces per
  level.
- In TypeScript and TSX, put the opening brace of `try`, `catch`, and
  `finally` blocks on the next line at the same indentation as the keyword.
- Do not indent the opening `{` one level deeper than `try`, `catch`, or
  `finally`.
- Indent the block body 2 spaces deeper than the keyword and opening brace.
- Put the closing `}` on its own line at the same indentation as the keyword.
- Do not write `try {`, `catch {`, or `finally {`.
- In TypeScript and TSX, use 4-space continuation indentation for wrapped
  expressions, arguments, conditions, arrays, object literals, JSX
  attributes, and similar continuations.
- Treat the user's `PostImportSourcePage.tsx` and `PostImportReviewPage.tsx`
  formatting as the local reference shape: component and callback bodies use
  2-space block indentation, single-line bodies do not gain unnecessary
  braces, wrapped expressions use 4-space continuation indentation, multi-
  stage ternaries use explicit parentheses, and TypeScript / TSX indentation
  and alignment whitespace compress every complete run of 8 spaces to tabs.
- A tab does not represent one indentation level.
- In TypeScript and TSX, first determine visible indentation using 2-space
  block indentation and 4-space continuation indentation, then compress every
  complete run of 8 spaces used for indentation or column alignment to tabs.
- In TypeScript and TSX, 8-space compression is mandatory, not optional.
- In TypeScript and TSX, this applies both to leading indentation and to
  alignment whitespace after non-space text, such as aligned inline type
  columns.
- In TypeScript and TSX, keep residual 2, 4, or 6 spaces after each tab
  compression.
- In TypeScript and TSX, do not treat a tab as one logical indentation level.
- In TypeScript and TSX, do not alter string literals, template-literal
  contents, regular expressions, or user-facing text merely to apply tab
  compression.
- Examples: 2 columns = 2 spaces, 4 columns = 4 spaces, 6 columns = 6
  spaces, 8 columns = 1 tab, 10 columns = 1 tab + 2 spaces, 12 columns = 1
  tab + 4 spaces, 14 columns = 1 tab + 6 spaces, 16 columns = 2 tabs.
- When TypeScript or TSX code already uses column alignment, apply the same
  8-space compression rule to that alignment whitespace.
- Example:

```ts
type Props = {
    row:	      PostImportRow
    displayNumber?:   number
    onEdit?:	  () => void
    onRetry?:	  () => void
    onToggleSkip?:    (checked: boolean) => void }
```
- TypeScript and TSX imports may stay on one line if they remain within the
  line limit; do not expand short type-only imports mechanically.
- Keep runtime value imports and type imports in separate declarations.
- Do not write `import { value, type TypeName } from ...`.
- Type-only declarations must use `import type`.
- Do not merge type imports into value imports merely to save lines.
- In TypeScript and TSX, order imports as four groups with a blank line
  between groups: external value imports, `@/...` value imports, external
  type imports, `@/...` type imports.
- Do not mechanically split a short value import from one module across
  multiple lines when it still fits within 99 characters.
- In TypeScript and TSX, when breaking a line at an operator, break before the
  operator and put the operator at the beginning of the next line. A trailing
  operator at end of line is unacceptable. This rule does not apply to Ruby,
  where it can change the syntactic structure.
- 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`.
- In JavaScript, JSX, TypeScript, and TSX, never use `_1`, `_2`, or similar
  Ruby-style numbered parameter names. Reserve numbered parameters for Ruby.
  Use a meaningful callback parameter name such as `row`, `item`, `value`,
  `entry`, or `result`.
- In JavaScript, JSX, TypeScript, and TSX, use `cn` from `@/lib/utils`
  whenever `className` combines multiple values, conditional classes, or a
  caller-provided `className` prop.
- Do not construct `className` with template literals, `${ ... }`, string
  concatenation, arrays joined with spaces, or feature-local class-merging
  helpers.
- A static `className="..."` containing only fixed classes does not need `cn`.
- 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, multi-stage ternary expressions must make branch
  boundaries explicit. Wrap each condition group in parentheses instead of
  relying on indentation alone to show which `?` matches which `:`.
- In TypeScript and TSX, wrap nested ternary expressions in parentheses. Do
  not write flat vertical chains of `?` and `:` without explicit grouping.
- In TypeScript and TSX, when a ternary condition contains `&&` or `||`, wrap
  the whole condition in parentheses before `?`.
- In TypeScript and TSX, if a ternary reaches three or more stages and still
  reads poorly after explicit grouping, extract a helper function or use `if`
  statements instead of keeping a flat multi-stage ternary.

Bad:

```ts
row.skipReason === 'existing' || row.importStatus === 'skipped'
? 'skipped'
: Object.keys (row.validationErrors ?? { }).length > 0
  || row.importStatus === 'failed'
  || row.importStatus === 'created'
? null
: hasWarnings (row) || row.status === 'warning'
? 'warning'
: 'ready'
```

Good:

```ts
(row.skipReason === 'existing' || row.importStatus === 'skipped')
? 'skipped'
: ((Object.keys (row.validationErrors ?? { }).length > 0
    || row.importStatus === 'failed'
    || row.importStatus === 'created')
   ? null
   : ((hasWarnings (row) || row.status === 'warning')
      ? 'warning'
      : 'ready'))
```

## Shared-system discovery and reuse

Before creating a new component, hook, service, helper, utility, concern,
representation, normaliser, validator, parser, fetcher, store, context, event
bus, API client, query key, permission helper, dialogue, toast, form field, or
version recorder, search the existing repository first.

Do not search by name alone. Search by responsibility, behaviour, and usage
intent as well. Typical search themes include:

- dialogue, modal, confirm, alert, choice
- validation error, field error, unprocessable entity
- permission, role, member, admin, editable
- URL normalise, sanitise, canonicalise
- API call, query key, prefetch, cache invalidation
- version, snapshot, history, restore
- thumbnail, metadata, HTTP fetch, URL safety
- form, field, input, textarea, warning, status badge
- file storage, Active Storage, ZIP, export
- transaction, locking, race, idempotency

Before deciding that something new is needed, confirm at least:

1. the existing definition
2. its public API
3. representative call sites
4. other implementations with similar responsibility
5. the nearest directory-level `AGENTS.md`

Do not reject an existing implementation by name alone. Read the code and its
usage first.

When new behaviour is needed, make the decision in this order:

1. use the existing common API as-is
2. use the existing extension points of that API
3. extend the existing common API minimally
4. keep the implementation local when the meaning is feature-specific
5. add a new common system only when multiple real users and a stable contract
   are already clear

Do not create a parallel foundation merely because the existing one feels
slightly awkward, because a new one seems faster, or because the current task
looks special.

Do not create a second common platform with names such as `CommonFoo`,
`SharedFoo`, `BaseFoo`, `FooManager`, `FooService`, `FooProvider`,
`FooWrapper`, `FooUtils`, or `useFoo` when an existing system already owns the
same responsibility. Judge by responsibility, not by spelling.

Wrapper aliases, barrel exports, and re-export shims do not count as reuse.
Using a different import path that merely forwards to the existing system does
not satisfy a reuse requirement.

Keep this boundary explicit:

- business meaning stays in feature code
- visual and mechanical shell stays in common code

Common code may hold generic primitives, interaction shells, API transport,
auth and permission helpers, query-key and prefetch conventions, validation
error conversion, shared normalisation, version-recording mechanisms, storage
helpers, HTTP safety, and other contracts that carry the same meaning across
multiple features.

Feature code should keep feature-specific states, labels, input fields,
workflow steps, payload shapes, validation rules, and business decisions.

Do not commonise business logic merely because the visuals look similar or two
code fragments resemble each other.

Create a new common system only when all of the following are true:

- no existing implementation already owns the responsibility
- there are multiple real consumers, or a clearly defined platform contract
- the differences between consumers do not require option bloat
- feature-specific vocabulary does not leak into common types, props, tones, or
  state names
- the location matches the existing directory structure
- the new abstraction does not compete with an existing common system
- it is not just a one-off wrapper for a single task

Do not add unused tones, variants, options, callbacks, states, or abstractions
for speculative future use.

Before finishing work that touches shared systems, verify:

- you searched for an existing implementation with the same responsibility
- you are using the canonical import path or entrypoint
- feature code is not reaching directly for a low-level primitive that already
  has a higher-level common API
- you did not bypass an existing common API
- any new wrapper or shim is genuinely necessary
- feature-specific vocabulary did not leak into common code
- the feature did not reimplement a common shell
- existing unrelated consumers were not changed without need
- one task did not trigger a needless redesign of the whole foundation
- any newly introduced common system really has multiple consumers

For backend-specific discovery, Rails structure, services, representation
selection, versioning, normalisation, and HTTP safety, follow
`backend/AGENTS.md`.

For frontend-specific component hierarchy, low-level primitive reuse, dialogue
entrypoints, API/query helpers, validation/form infrastructure, state/storage,
and layout reuse, follow `frontend/AGENTS.md`.
- 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 add user-facing copy, helper text, descriptions, notes, tooltips,
  placeholders, empty-state messages, loading messages, or explanatory text
  unless the user explicitly specified the wording.
- When new user-facing wording appears necessary, ask the user for the exact
  wording and placement before implementing it.
- Do not invent replacement copy when removing unrequested wording.
- Do not create, modify, or run tests unless the user explicitly asks for
  test work. When the user asks for tests, keep working within the permitted
  test-file scope and rerun them until they pass or the remaining failure is
  clearly blocked.
- Test-only work includes adding, updating, deleting, reorganising, or fixing
  SyntaxError in tests. During test-only work, do not modify production code.
- During test-only work, do not change production constants, behaviour, API
  contracts, validation, routes, authentication, permissions, UI, copy,
  dependencies, limits, thresholds, defaults, migrations, schema, or
  environment settings to satisfy tests.
- Do not make production code match failing tests, mock assumptions, fixtures,
  snapshots, old expectations, or stale setup. This includes changing
  production constants merely because a test expects a different value.
- If test work reveals a production bug, spec mismatch, or missing behaviour,
  stop without modifying production code and report: the failing test or
  discovered issue, the related production file, the actual behaviour, the
  expected behaviour, and why a production change appears necessary.
- Modify production code for test failures only when the user explicitly asks
  for that production change. Do not expand a test task into a production task
  on your own authority.
- If the user explicitly asks for both production implementation and test
  updates, implement production code to the confirmed specification first,
  then add or update tests to verify that specification. Never roll production
  behaviour back to satisfy old tests.
- If it is unclear whether the test or the production implementation is stale,
  or a test cannot be corrected without changing production code, ask the user
  instead of guessing.

## Backend rules

- Inspect existing routes, controllers, models, services, and specs before
  editing backend behaviour.
- Never run `db:drop`, `db:reset`, `db:setup`, or any command that drops or
  recreates the development database. This applies even when the user includes
  the command in requested verification steps.
- Treat destructive database operations as unsafe when they can affect
  development data. Ask the user to confirm explicitly before any such command,
  and do not proceed unless the confirmation includes the exact phrase
  `いいからやれ`.
- Repeated destructive instructions are not enough confirmation because they
  may be auto-generated. Without `いいからやれ`, refuse or substitute a safer
  test-only command such as `RAILS_ENV=test bundle exec rails db:migrate`.
- For API behaviour 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 behaviour.
- Be careful with tag names, tag normalisation, implications, similarities, and discard behaviour.
- 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.
- 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.
- Match existing Tailwind, component, and import alias conventions.
- `<a href="#">` is acceptable for event-only controls when it fits the local
  UI pattern. Do not use `<a>` for internal navigation or other non-external
  links.
- Internal links must use `PrefetchLink`.
- External links must use `<a>` with `target="_blank"`.
- When adding or changing Tailwind `bg-*` classes in TSX, pair them with an
  explicit readable `text-*` colour and dark-mode counterparts such as
  `dark:bg-*`, `dark:text-*`, and `dark:border-*` where a border is present.
- Do not rely on inherited text colour for light backgrounds. This is especially
  important for chips, cards, buttons, and panels that may inherit white text in
  dark mode.
- Mobile UI must be checked as a first-class layout. Avoid wide fixed content,
  make dense controls wrap or scroll intentionally, and keep tag/filter
  controls usable without horizontal page overflow.
- Frontend のスマホ／PC表示境界は原則 `md` とする。
- button stack、footer action、dialogue action は `md` 未満で縦並び、
  `md` 以上で横並びとする。
- 同じ画面内で `sm` と `md` を混在させて中間 layout を作らない。
- 明確に別の responsive 要件がある component だけを例外とする。
- For mobile horizontal scrollers, make the scroll direction and item sizing
  explicit, and ensure chip text remains readable in both light and dark modes.
- 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, follow the 1986 Cabinet Notice
  《現代仮名遣い》 and avoid historical kana spellings unless the task
  explicitly requires them.
- For user-facing Japanese kanji spelling, do not normalize to
  《当用漢字による書きかえ》; prefer original forms such as `編輯`.
- For user-facing Japanese ellipses, prefer `……` over ASCII `...`.
- Frontend dialogue work must use `@/lib/dialogues/useDialogue` as the
  feature-facing entrypoint.
- Reuse the existing common dialogue API and common dialogue component
  instead of building feature-local overlay, close button, header, footer,
  focus handling, outside click handling, Escape handling, or confirmation
  flows.
- Do not import `@/components/ui/dialog` directly in feature code to build a
  one-off dialogue, and do not evade this rule with aliases such as
  `Dialog as Dialogue`.
- Keep business-specific form content in feature code, and keep the visual
  and behavioural dialogue shell in common code.
- Use British spelling `Dialogue` for project-defined dialogue identifiers.
  Keep an exact third-party API spelling only at the external boundary where
  compatibility requires it.

### Frontend TypeScript and TSX style

- The delimiter-placement and line-breaking rules in this section apply to
  both plain TypeScript `.ts` and TSX `.tsx`, unless a bullet explicitly says
  it is JSX- or React-specific.
- Preserve the local TypeScript and TSX formatting style.
- Do not normalize TSX to common Prettier-style React formatting unless
  explicitly asked.
- Treat TypeScript and TSX formatting rules as hard constraints, not
  preferences. Before finishing a TypeScript or TSX edit, inspect the edited
  hunks for closing `)`, `]`, and `}` placement and fix violations instead of
  relying on formatter defaults.
- After every TypeScript or TSX edit, perform a style-only self-review of the
  edited hunks before running verification or reporting completion. The task is
  not complete while any edited TypeScript or TSX hunk violates these local
  formatting rules.
- The TypeScript/TSX self-review must classify every edited leading or trailing
  `)`, `]`, and `}` by syntax role before deciding whether it is valid. Do not
  apply a rule by glyph alone. A closing `)` for a function parameter list is
  different from a closing `)` for a function call. A closing `}` for a block
  is different from a closing `}` for an object, type literal, import list, or
  destructuring pattern.
- The TSX-specific self-review must confirm there are no common Prettier-style
  React component declarations with a multi-line destructured parameter.
- The TypeScript/TSX self-review must confirm multi-line function declaration
  parameter `)` placement follows the detailed parameter-list rules below.
- The TypeScript/TSX self-review must confirm call-expression `)` is never at
  the beginning of a line.
- The TypeScript/TSX self-review must confirm
  object/type/import/destructuring `}` is not at the beginning of a line.
- The TypeScript/TSX self-review must confirm multi-line
  function/lambda/callback/block `}` is on its own line and never at the end
  of the previous line.
- The TypeScript/TSX self-review must confirm array `]` is not at the
  beginning of a line.
- The TSX-specific self-review must confirm JSX closing markers and closing
  parentheses keep the surrounding compact style.
- The TypeScript/TSX self-review must confirm block indentation uses 2 spaces
  per level, wrapped continuations use the repository's 4-space continuation
  alignment, and every complete run of 8 spaces used for indentation or
  alignment has been compressed to tabs.
- 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, use 2-space block indentation and 4-space continuation
  indentation.
- In TypeScript and TSX, put the opening brace of `try`, `catch`, and
  `finally` blocks on the next line at the same indentation as the keyword.
- Do not indent the opening `{` one level deeper than `try`, `catch`, or
  `finally`.
- Indent the block body 2 spaces deeper than the keyword and opening brace.
- Put the closing `}` on its own line at the same indentation as the keyword.
- Do not write `try {`, `catch {`, or `finally {`.
- In TypeScript and TSX, convert every complete run of 8 spaces used for
  indentation or alignment to a tab character.
- In TypeScript and TSX, a tab is exactly equivalent to 8 columns, whether it
  appears at the beginning of a line or in alignment whitespace after
  non-space text.
- In TSX, JSX nesting uses 2-space block indentation and wrapped JSX
  attributes use 4-space continuation indentation; after visible columns are
  determined, compress every complete run of 8 spaces in the resulting
  indentation or alignment to tabs.
- In TSX, do not leave JSX subtree indentation at 8, 10, 12, 14, or 16
  columns as spaces alone; convert each complete run of 8 spaces to tabs and
  keep only the residual 2, 4, or 6 spaces.
- In TypeScript and TSX function declarations, including `const` arrow
  function declarations, classify the parameter list before placing the closing
  `)`.
- If the parameter list itself is given its own multi-line block after the
  function's opening `(`, put the closing parameter `)` at the beginning of its
  own line before the return type or `=>`.
- If the only line break is inside a parameter's inline type, object type, or
  destructuring shape, and the parameter list itself is not split as a separate
  block, keep the closing parameter `)` on the same line as that parameter's
  final `}`. In this case, moving `)` to a new line is wrong.
- Do not write React component declarations in the common Prettier form
  `const Component = ({ ... }) => (` when the destructured parameter spans
  multiple lines. Use the project form with the opening `(` after `=`, the
  destructured object as the argument, and the closing parameter `)` on its own
  line before `=>`.
- In TypeScript and TSX, never place a closing parenthesis at the beginning of
  a line except for a multi-line function declaration parameter list.
- Never place a closing square bracket at the beginning of a line.
- For object literals, type literals, import/export named bindings, destructuring
  patterns, and other associative-array-style braces, do not place the closing
  brace at the beginning of a line. Keep `}` on the same line as the final
  property, binding, or specifier unless that would violate the line limit.
  Function, lambda, callback, and block closing braces are exempt and should
  stay on their own line when that fits the local style. When a function,
  lambda, callback, or block body spans multiple lines, do not put its closing
  `}` at the end of the previous line.
- For arrays and tuple-like lists, do not place the closing `]` at the
  beginning of a line. Keep `]` on the same line as the final element unless
  that would violate the line limit.
- For function and method calls, do not place the closing `)` at the beginning
  of a line. The only TypeScript/TSX exception is the closing parameter `)` of a
  multi-line function declaration.
- When writing braces on a single line in TypeScript or TSX JavaScript
  context, put exactly one space inside the braces, as in `{ value }` or
  `{ key: value }`.
- Do not add inner spaces to React/JSX expression braces, as in
  `prop={value}`, `{children}`, or `<Component>{{...props}}</Component>`.
- 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
  `</div>)` 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.
- `try` / `catch` / `finally` brace placement example:

```ts
try
{
  doWork ()
}
catch
{
  recover ()
}
finally
{
  cleanUp ()
}
```
- Do not use a leading semicolon for expression statements such as
  `;([...]).forEach(...)`; rewrite the expression to avoid ASI hazards
  explicitly, for example with `void`.
- Do not insert `void` mechanically before ordinary event-handler or callback
  calls merely because the callee returns a `Promise`. If the surrounding code
  already uses a plain expression statement such as
  `onClick={() => requestActiveTab ('theme')}`, preserve that style unless a
  local rule or type requirement truly forces `void`. Adding unnecessary
  `void` is a medium-severity style violation.
- When fixing formatting, never change operator binding, evaluation order,
  tuple/comma behaviour, or any other expression structure. A formatting pass
  that changes how `!==`, `??`, `?:`, `&&`, `||`, or `,` binds is a capital
  offence.
- In TypeScript and TSX, do not mechanically verticalise short call tails,
  short object literals, or short callback bodies that already fit the local
  line-length and delimiter rules. Expanding compact local code without a
  readability need is a minor style violation.
- In TypeScript and TSX, treat short object literals, short returned objects,
  short typed object shapes, and short function-call object arguments as
  compact associative syntax by default. If they fit locally, keep the opening
  `{` on the same line as the first property and keep the closing `}` on the
  same line as the final property. Do not explode them into vertical
  Prettier-style blocks without a concrete readability or line-length reason.
- In TypeScript and TSX, when a compact associative form is broken across two
  lines, keep the opening `{` with the first property and the closing `}` with
  the final property. A shape like `{\n    key: value,\n}` is wrong even when
  line length would permit it.
- In TypeScript and TSX, when a short call such as `apiGet<...> (...)`,
  `apiPatch<...> (...)`, `setState (...)`, `updateClientSettings (...)`, or
  `window.dispatchEvent (...)` already fits within the local style, do not
  add extra vertical layers around the object argument or callback body.
- In TypeScript and TSX, after editing a file, inspect the file tail and
  remove meaningless blank lines before `export default` or the end of file.
  A stray empty line before `export default` is a minor style offence.
- Use correct British English spelling for new identifiers, filenames,
  component names, helper names, comments, and developer-facing prose unless
  editing an already established American-English API that must keep its
  existing spelling for compatibility.
- Prefer British English spellings such as `behaviour`, `colour`, `realise`,
  `theatre`, `centre`, `favourite`, `optimise`, and `catalogue`.
- Avoid American or Canadian spellings such as `behavior`, `color`, `realize`,
  `theater`, `center`, `favorite`, `optimize`, and `catalog`.
- Even when an external library or API uses the wrong spelling, prefer to
  correct it at the local boundary and use proper British English names in this
  codebase. For example, prefer `import { color: colour } from '@external-lib'`
  over spreading `color` through local code.
- Apply the same boundary correction to object destructuring, wrapper helpers,
  adapter layers, and local variable names when doing so does not break the
  external contract.
- For this repository, prefer names such as `BehaviourSettingsSection.tsx`, not
  `BehaviorSettingsSection.tsx`.

### Additional style offences and penalties

Use this section for recurrent failure modes that are easy to miss when
applying the delimiter rules mechanically.

#### Unnecessary `void` before ordinary callbacks

Bad:

```ts
onClick={() => void requestActiveTab ('theme')}
```

Good:

```ts
onClick={() => requestActiveTab ('theme')}
```

Penalty: medium-severity offence. The code still works, but it pollutes the
local style and usually signals that the edit was made mechanically rather than
by reading the surrounding code.

#### Formatting that changes expression structure

Bad:

```ts
const hasUnsavedChanges =
    serialiseThemeDraft (draftThemeSlots)
    !== (serialiseThemeDraft (savedThemeSlots),
	 draftActiveThemeMode !== savedThemeMode)
```

Good:

```ts
const hasUnsavedChanges =
    serialiseThemeDraft (draftThemeSlots)
    !== serialiseThemeDraft (savedThemeSlots)
    || draftThemeMode !== savedThemeMode
```

Penalty: capital offence. Formatting must never corrupt the meaning of an
expression.

#### Mechanical vertical expansion of compact local code

Bad:

```ts
const nextState = {
    open,
    activeTab }

setDirtyStates (
    current => ({
	current,
	key:  value }),
)
```

Good:

```ts
const nextState = { open, activeTab }

setDirtyStates (
    current => ({
	current,
	key:  value }))
```

Penalty: minor offence when semantics stay intact. It is not necessarily
broken, but it ignores the repository's compact associative style.

#### Prettier-style exploded object literals that should stay compact

Bad:

```ts
const next = {
    enabled: keyboard?.enabled !== false,
    bindings: normaliseKeyboardBindings (keyboard?.bindings),
}
```

Good:

```ts
const next = {
    enabled: keyboard?.enabled !== false,
    bindings: normaliseKeyboardBindings (keyboard?.bindings) }
```

Also good when short enough:

```ts
const next = { enabled: keyboard?.enabled !== false,
	       bindings: normaliseKeyboardBindings (keyboard?.bindings) }
```

Rule: for compact associative syntax, keep `{` with the first property and `}`
with the final property unless line length truly forces a different shape.
Do not mechanically apply the common Prettier block form.
Penalty: medium-severity offence. The code still runs, but it violates a core
repository formatting habit that must be preserved consistently.

#### Exploded object arguments inside short calls

Bad:

```ts
updateClientSettings (settings => ({
    ...settings,
    keyboard: next,
}))
```

Good:

```ts
updateClientSettings (settings => ({
    ...settings,
    keyboard: next }))
```

Rule: a call-expression closing `)` must not drift onto its own line, and a
short object argument should not be exploded more than necessary. Keep the
callback's returned object compact when it fits.
Penalty: medium-severity offence. This usually means the edit was formatted
mechanically instead of by reading the local style.

#### Stray blank line before `export default`

Bad:

```ts
const Component: FC = () => {
  return <div/>
}


export default Component
```

Good:

```ts
const Component: FC = () => {
  return <div/>
}

export default Component
```

Rule: do not leave an extra empty line between the last top-level declaration
and `export default` unless some surrounding file structure genuinely requires
visual separation.
Penalty: minor offence. It is small, but it is easy to avoid and should not
recur.

### Frontend delimiter decision table

Use this table before accepting any edited TypeScript or TSX hunk. The table is
more authoritative than formatter habit.

#### Import and export named bindings

Bad:

```ts
import {
    Button,
    Card,
} from '@/components/ui'
```

Good:

```ts
import { Button,
	 Card } from '@/components/ui'
```

Also acceptable when short:

```ts
import { Button, Card } from '@/components/ui'
```

Rule: named-binding `}` is associative-array-style syntax. It must not be alone
at the beginning of a line. Prefer keeping `{` with the first binding and `}`
with the final binding when this fits the line limit.
Penalty: capital offence. This is one of the core associative-brace rules.

#### Type literals

Bad:

```ts
type Props = {
    open: boolean
    onOpenChange: (open: boolean) => void
}
```

Good:

```ts
type Props = {
    open: boolean
    onOpenChange: (open: boolean) => void }
```

Rule: type-literal `}` is not a block close. It must stay on the same line as
the final property unless that would break the hard line limit.
Penalty: capital offence. Mistaking a type-literal `}` for a block `}` is one
of the recurring death-class errors.

#### Object literals

Bad:

```ts
const value = {
    open,
    activeScope,
}
```

Good:

```ts
const value = {
    open,
    activeScope }
```

Bad:

```ts
const value = useMemo (() => ({
    open,
    activeScope,
}), [open, activeScope])
```

Good:

```ts
const value = useMemo (() => ({
    open,
    activeScope }), [open, activeScope])
```

Rule: object-literal `}` is associative-array-style syntax. It must not be on a
line by itself. Keep it with the final property, and keep call `)` off the
beginning of a line.
Penalty: capital offence. Wrong object-literal `}` placement and wrong call
`)` placement are both death-class violations here.

#### Destructuring parameters

Bad:

```tsx
const Component: FC<Props> = ({
    open,
    onOpenChange,
}) => {
  return null
}
```

Good:

```tsx
const Component: FC<Props> = (
    { open,
      onOpenChange },
) => {
  return null
}
```

Rule: when a React component or helper takes a destructured object parameter
that spans multiple lines, do not use the Prettier-style `= ({ ... }) =>`
shape. Put the function parameter list on its own lines. The destructuring `}`
stays with the final binding. The parameter-list `)` is then allowed and
required at the beginning of its own line.
Penalty: capital offence. This pattern is a primary source of repeated TSX
style regressions.

#### Inline typed destructuring parameter

Good:

```ts
const RouteTransitionWrapper = ({ user, setUser }: {
    user:     User | null
    setUser:  Dispatch<SetStateAction<User | null>> }) => {
  return null
}
```

Bad:

```ts
const RouteTransitionWrapper = ({ user, setUser }: {
    user:     User | null
    setUser:  Dispatch<SetStateAction<User | null>>
}) => {
  return null
}
```

Rule: this is not a separately split parameter-list block. The line break is
inside the inline type. Keep the type-literal `}` and parameter-list `)` on the
same line as the final type property.
Penalty: capital offence. Misclassifying this case means the delimiter review
has failed at the syntax-role level.

#### Multi-line normal parameter list

Bad:

```ts
const updateDraft = <Key extends keyof Settings,> (
    key: Key,
    value: Settings[Key]) => {
  return null
}
```

Good:

```ts
const updateDraft = <Key extends keyof Settings,> (
    key:    Key,
    value:  Settings[Key],
) => {
  return null
}
```

Rule: when the parameter list itself is split across multiple parameter lines,
the closing parameter `)` goes at the beginning of its own line before `=>` or
the return type.
Penalty: capital offence. This is the main allowed exception for a leading `)`,
so getting it wrong destroys the whole rule set.

#### Function and callback blocks

Bad:

```ts
const handleSave = () => { save ()
}
```

Bad:

```ts
const handleSave = () => {
  save () }
```

Good:

```ts
const handleSave = () => {
  save ()
}
```

Rule: block `}` closes executable code, not associative data. Multi-line
function, lambda, callback, `if`, `for`, `switch`, and similar block braces
belong on their own line.
Penalty: capital offence. Confusing block `}` with associative `}` is a
death-class delimiter error.

#### Function and method calls

Bad:

```ts
const value = compute (
    first,
    second
)
```

Good:

```ts
const value = compute (
    first,
    second)
```

Rule: call-expression `)` must not be at the beginning of a line. The exception
for leading `)` applies only to function declaration parameter lists, never to
calls.
Penalty: capital offence. A leading call `)` is one of the clearest death-class
violations in this codebase.

#### JSX closing markers

Bad:

```tsx
<Button
    type="button"
    onClick={handleSave}
>
  保存
</Button>
)
```

Good:

```tsx
<Button
    type="button"
    onClick={handleSave}>
  保存
</Button>)
```

Bad:

```tsx
<Input
    value={value}
    onChange={handleChange}
/>
```

Good:

```tsx
<Input
    value={value}
    onChange={handleChange}/>
```

Rule: keep `>` or `/>` with the final prop, and keep JSX closing parentheses in
the local compact form such as `</div>)`.
Penalty: medium-severity offence. It is usually not semantically broken, but it
is a conspicuous TSX-style failure.

#### Arrays and tuples

Bad:

```ts
const items = [
    first,
    second,
]
```

Good:

```ts
const items = [
    first,
    second]
```

Good when short:

```ts
const items = [first, second]
```

Rule: array and tuple `]` must not be at the beginning of a line. Keep it with
the final element unless that would break the hard line limit.
Penalty: capital offence. A leading `]` is forbidden by the same class of hard
delimiter rule as leading associative `}` and leading call `)`.

#### Single-line braces

Good:

```ts
const next = { ...current, enabled: true }
const tag = { id, name }
```

Bad:

```tsx
<Component prop={ value }/>
```

Good:

```tsx
<Component prop={value}/>
```

Rule: JavaScript object braces on one line get one inner space. JSX expression
braces do not get inner spaces.
Penalty: minor offence. This is not normally semantic breakage, but it still
counts as a style miss.

#### Final TypeScript/TSX self-review checklist

Before reporting completion after a TypeScript or TSX edit, check the edited
hunks line by line. Unless a step explicitly mentions JSX, it applies equally
to `.ts` and `.tsx`:

1. No import/export/type/object/destructuring `}` appears alone at the beginning
   of a line.
2. No call-expression `)` appears at the beginning of a line.
3. Any leading `)` is definitely closing a function declaration parameter list,
   not a call.
4. Any parameter-list `)` at the end of a line is valid because the parameter
   list itself was not split, only an inline type or destructuring shape was.
5. No array or tuple `]` appears at the beginning of a line.
6. Multi-line executable block `}` is on its own line.
7. JSX `>` and `/>` stay with the final prop unless nearby code proves
   otherwise.
8. JSX closing parentheses keep the compact local style.
9. Block indentation uses 2 spaces per level, wrapped continuations use the
   repository's 4-space continuation alignment, and every complete run of 8
   spaces used for indentation or alignment has been compressed to tabs.
10. No line has trailing whitespace.

Preferred:

```tsx
const PostFormTagsArea: FC<Props> = ({ tags, setTags, errors, ...rest }) => {
  return (
      <TextArea
	  {...rest}
	  ref={ref}
	  value={tags}
	  invalid={errors && errors.length > 0}
	  onChange={ev => setTags (ev.target.value)}/>)
}
```

Avoid:

```tsx
function PostFormTagsArea ({ tags, setTags }: Props) {
  return (
    <TextArea
      value={tags}
      onChange={ev => setTags (ev.target.value)}
    />
  )
}
```

## Codex workflow

- First inspect existing patterns; do not invent new architecture when a local convention exists.
- Keep changes scoped to the requested issue.
- Do not scan or summarize dependency/generated/runtime directories such as
  `node_modules`, `dist`, `tmp`, `log`, and `storage` unless explicitly needed.
- Before touching wiki, tag, versioning, BAN, IP BAN, or authentication
  behaviour, inspect the related request specs and service objects.
- If frontend code changes, run only non-test verification commands that
  apply, such as `npm run build` and `npm run lint`. Run `npm run test:run`
  only when the user explicitly asks for tests.
- If backend code changes, do not run RSpec unless the user explicitly asks
  for tests.
- If a verification command cannot be run or fails, report the exact command and failure.

## Completion criteria

A task is complete only when:

- implementation is complete,
- relevant non-test verification commands pass, or failures are clearly
  explained,
- unrelated files are not changed,
- migrations and schema are consistent when schema changes are made,
- user-facing behaviour is documented when needed.
