#346 Co-authored-by: miteruzo <miteruzo@naver.com> Reviewed-on: https://git.miteruzo.com/miteruzo/btrc-hub/pulls/347main
| @@ -0,0 +1,43 @@ | |||
| --- | |||
| name: 'Codex task' | |||
| about: 'Codex に実装させるための課題' | |||
| title: '' | |||
| labels: | |||
| - codex-ready | |||
| --- | |||
| ## 背景 | |||
| なぜ必要か。 | |||
| ## 対象範囲 | |||
| - backend: | |||
| - frontend: | |||
| - docs: | |||
| - migration: | |||
| ## やること | |||
| - [ ] | |||
| ## 受け入れ条件 | |||
| - [ ] | |||
| ## 実行すべき確認 | |||
| - [ ] `cd backend && bundle exec rspec` | |||
| - [ ] `cd frontend && npm run build` | |||
| - [ ] `cd frontend && npm run lint` | |||
| ## 禁止事項 | |||
| - unrelated refactor はしない | |||
| - 既存 API response shape を壊さない | |||
| - 認証・認可・BAN を弱めない | |||
| ## Codex への指示 | |||
| この issue を読んで実装してください。 | |||
| 不明点があれば、実装前に調査結果と選択肢を提示してください。 | |||
| @@ -0,0 +1,143 @@ | |||
| # 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 preview | |||
| ``` | |||
| `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`. | |||
| ## 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. | |||
| - Ruby: never put a space before method-call parentheses. | |||
| - Ruby: do not use `%w` or `%i`. | |||
| - TypeScript and Python: use GNU-style spacing before parentheses where syntactically valid. | |||
| - Do not add production dependencies without explicit approval. | |||
| ## 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. | |||
| - 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. | |||
| - Be careful with tag names, tag normalization, implications, similarities, and discard behavior. | |||
| - 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. | |||
| ## 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 behavior, inspect the related request specs and service objects. | |||
| - If frontend code changes, run the existing frontend verification commands that apply: `npm run build` and `npm run lint`. | |||
| - If backend code changes, run the relevant RSpec command; for broad backend changes, run `bundle exec rspec`. | |||
| - 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 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 behavior is documented when needed. | |||
| @@ -0,0 +1,147 @@ | |||
| # 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: | |||
| ```sh | |||
| bin/setup | |||
| bin/dev | |||
| bin/rails | |||
| bin/rake | |||
| bin/rubocop | |||
| bin/brakeman | |||
| bundle exec rspec | |||
| ``` | |||
| Common checks: | |||
| ```sh | |||
| bundle exec rspec | |||
| bin/rubocop | |||
| bin/brakeman | |||
| ``` | |||
| Common Rails commands: | |||
| ```sh | |||
| 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: | |||
| ```sh | |||
| 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. | |||
| - Do not use `%w` or `%i` in new Ruby code. | |||
| - 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-Code` header in `ApplicationController#authenticate_user`. | |||
| - `current_user` is set by looking up `User.inheritance_code`. | |||
| - Do not bypass or weaken the `X-Transfer-Code` flow unless the task explicitly changes authentication. | |||
| - Unauthenticated write actions should return `:unauthorized` consistently with existing controllers. | |||
| - Role checks use `User` enum roles: `guest`, `member`, and `admin`. | |||
| - 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 | |||
| - `ApplicationController` runs these before actions in order: | |||
| - `reject_banned_ip_address!` | |||
| - `authenticate_user` | |||
| - `reject_banned_user!` | |||
| - User and IP bans use `banned_at`, not a boolean `banned` column. | |||
| - `User#banned?` and `IpAddress#banned?` check `banned_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.rb` loads `spec/support/**/*.rb`. | |||
| - Request specs include `AuthHelper` and `JsonHelper`. | |||
| - `AuthHelper#sign_in_as(user)` stubs `ApplicationController#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.rb` consistent. | |||
| - Use reversible migrations where practical; otherwise define explicit `up` and `down`. | |||
| - For data backfills inside migrations, follow the existing pattern of defining migration-local `ActiveRecord::Base` classes with `self.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 positive `version_no` with unique indexes scoped to the parent record. | |||
| - Version event types are `create`, `update`, `discard`, and `restore`. | |||
| - Version rows are readonly through the `VersionRecord` concern. | |||
| - Use the existing recorder services instead of manually inserting version rows in application code: | |||
| - `PostVersionRecorder` | |||
| - `TagVersionRecorder` | |||
| - `NicoTagVersionRecorder` | |||
| - `WikiVersionRecorder` | |||
| - `TagVersioning` | |||
| - `VersionRecorder` locks the current record, validates sequence consistency, skips unchanged update snapshots, creates the next version row, and updates the record `version_no`. | |||
| - Do not update versioned records without considering whether a version snapshot must be created. | |||
| - For optimistic concurrency paths, preserve `base_version_no`, `force`, and `merge` semantics 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/representations` when 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: `:unauthorized` for no user, `:forbidden` for insufficient role or banned user, `:not_found` for missing records, and `:unprocessable_entity` for validation failures. | |||
| ## 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. | |||
| @@ -0,0 +1,646 @@ | |||
| # Codex handoff for BTRC Hub / タグ広場 | |||
| This document transfers project-specific context from prior ChatGPT-assisted design and review work to Codex. | |||
| Use this file as project background. | |||
| Use `AGENTS.md`, `backend/AGENTS.md`, and `frontend/AGENTS.md` for concrete coding rules and verification commands. | |||
| ## Project identity | |||
| BTRC Hub / タグ広場 is a collaborative knowledge base for collecting, tagging, explaining, and rediscovering Bocchi the Rock creature-related works. | |||
| It is not a generic SNS. | |||
| It is not a comment board. | |||
| It is not a service for rehosting external content. | |||
| It is primarily a structured link, tag, wiki, material, and viewing-party system. | |||
| Core domains: | |||
| 1. Posts | |||
| 2. Tags | |||
| 3. Wiki pages | |||
| 4. Materials | |||
| 5. Theatre / watch-party features | |||
| The project is already publicly accessible and indexed by search engines, but it has not been broadly announced. Treat it as a small public production system, not a private prototype. | |||
| ## Current stack | |||
| Backend: | |||
| - Ruby 3.2.2 | |||
| - Rails 8.0.2 API | |||
| - MySQL 8 | |||
| - Active Storage | |||
| - Cloudflare R2 / S3-compatible storage is expected for uploaded files | |||
| - RSpec | |||
| Frontend: | |||
| - React 19.1 | |||
| - Vite 6.3 | |||
| - TypeScript 5.8 | |||
| - Axios | |||
| - TanStack Query | |||
| - Tailwind CSS | |||
| - Framer Motion | |||
| - shadcn-like local components | |||
| - react-markdown | |||
| - react-markdown-editor-lite | |||
| - remark-wiki-autolink | |||
| Batch / background-like tasks: | |||
| - Rake tasks | |||
| - Nico sync | |||
| - YouTube sync | |||
| - Similarity calculation tasks | |||
| ## Repository working principle | |||
| Before editing, inspect the existing implementation. | |||
| Do not invent a new architecture when the current repo already has an established convention. | |||
| Keep changes scoped to the requested issue. | |||
| Prefer small, reviewable changes over broad rewrites. | |||
| Do not perform unrelated cleanup in the same patch. | |||
| When a task has design ambiguity, first produce a short investigation and recommended plan. Do not silently choose a risky design. | |||
| ## User coding preferences | |||
| General: | |||
| - Prefer single quotes for strings unless interpolation, escaping, or framework convention makes double quotes better. | |||
| - Do not add production dependencies without explicit approval. | |||
| - Do not perform broad formatting churn. | |||
| - Do not convert unrelated files to a different style. | |||
| Ruby: | |||
| - Do not put a space before method-call parentheses. | |||
| - Do not use `%w`. | |||
| - Do not use `%i`. | |||
| - Keep Rails code idiomatic, but preserve the user's style where the repo already uses it. | |||
| TypeScript / Python: | |||
| - The user prefers GNU-style spacing before parentheses where syntactically valid. | |||
| - Preserve existing project formatting if a formatter or nearby code dictates otherwise. | |||
| ## Current authentication model | |||
| The system does not use normal email/password authentication. | |||
| Users are authenticated by inheritance code. | |||
| Frontend: | |||
| - Stores the code in `localStorage.user_code`. | |||
| - Sends it as the `X-Transfer-Code` header. | |||
| Backend: | |||
| - Looks up `users.inheritance_code`. | |||
| - Sets `current_user`. | |||
| Roles: | |||
| - `guest` | |||
| - `member` | |||
| - `admin` | |||
| Important helper: | |||
| - `User#gte_member?` returns true for `member` and `admin`. | |||
| Never introduce a conventional login assumption unless the issue explicitly asks for it. | |||
| ## BAN / abuse-control model | |||
| The backend currently enforces BAN at API level. | |||
| The relevant before_action order is conceptually: | |||
| 1. Reject banned IP address. | |||
| 2. Authenticate user if transfer code exists. | |||
| 3. Reject banned user. | |||
| Entities: | |||
| - `users.banned_at` | |||
| - `ip_addresses.banned_at` | |||
| - `user_ips` | |||
| IP addresses are stored as binary values using `IPAddr#hton`. | |||
| Do not weaken BAN behavior. | |||
| Do not move BAN checks behind optional authentication. | |||
| Do not make preview, theatre, verify, user creation, or public-looking endpoints bypass BAN without an explicit design decision. | |||
| ## Public-operation assumptions | |||
| Current practical operation: | |||
| - A few editor accounts exist. | |||
| - Meaningful editing is mostly done by the owner. | |||
| - Read access is already public. | |||
| - Search engines have indexed the site. | |||
| - Future editor applications are expected through Discord. | |||
| - Prospective editors are likely people known in the Bocchi creature community. | |||
| This means security and moderation issues matter even if traffic is still small. | |||
| ## Core domain summary | |||
| ### Posts | |||
| Posts are external URL-based link records. | |||
| Important properties: | |||
| - `url` is required and unique. | |||
| - URLs are normalized. | |||
| - Only HTTP / HTTPS are allowed. | |||
| - Posts can have thumbnails through Active Storage. | |||
| - `uploaded_user_id` may be NULL for synced or bot-created posts. | |||
| - `original_created_from` and `original_created_before` represent a time range for original content creation. | |||
| - When both original time bounds exist, `from < before` is required. | |||
| Parent/child posts: | |||
| - Current implementation uses `post_implications`. | |||
| - It is many-to-many. | |||
| - Do not assume `posts.parent_id`. | |||
| - Frontend/API clients must send `parent_post_ids`, even when empty. | |||
| - `parent_post_ids` is parsed as a space-separated ID string. | |||
| - Self-parenting is invalid. | |||
| - Missing parent IDs are invalid. | |||
| Versions: | |||
| - `post_versions` stores snapshots. | |||
| - `version_no` is a per-post sequence. | |||
| - Snapshot includes title, URL, thumbnail base, tags, parent post IDs, original time bounds, event type, and actor. | |||
| - Optimistic locking for posts is planned / important, but do not assume it is fully implemented unless the code proves it. | |||
| ### Tags | |||
| Tags are central. | |||
| There is separation between tag names and tag entities: | |||
| - `tag_names` | |||
| - `tags` | |||
| Categories: | |||
| - `deerjikist` | |||
| - `meme` | |||
| - `character` | |||
| - `general` | |||
| - `material` | |||
| - `nico` | |||
| - `meta` | |||
| Alias model: | |||
| - `tag_names.canonical_id` expresses aliases. | |||
| - `canonical_id = NULL` means canonical name. | |||
| - `canonical_id != NULL` means alias. | |||
| - An alias must not point to another alias. | |||
| - A tag name that already has a tag or wiki page generally must not be aliasified. | |||
| Tag normalization: | |||
| - User-entered tags are normalized through existing backend logic. | |||
| - Known aliases are canonicalized. | |||
| - Parent tags are expanded recursively. | |||
| - `nico:` is normally rejected for manual entry. | |||
| - Special tags such as tag-request / bot / unknown-deerjikist / video / niconico / youtube must be protected. | |||
| Do not casually change tag normalization, alias resolution, or parent expansion. These affect search, wiki, sync, and historical data. | |||
| ### Nico tags | |||
| Nico tags use the `nico` category and have separate versioning. | |||
| Important relation: | |||
| - `nico_tag_relations` maps external Nico tags to internal tags. | |||
| - `nico_tag_id` must be a Nico category tag. | |||
| - `tag_id` must not be Nico category. | |||
| Do not allow ordinary manual tag editing to create or corrupt Nico tags. | |||
| ### Deerjikists | |||
| Deerjikists map external platform identities to internal `deerjikist` tags. | |||
| Known platforms include: | |||
| - `nico` | |||
| - `youtube` | |||
| YouTube handles may be normalized to `UC...` channel IDs. | |||
| Do not treat user-facing handles and canonical channel IDs as interchangeable without checking existing code. | |||
| ### Wiki | |||
| Wiki pages are a major knowledge layer. | |||
| Important points: | |||
| - Wiki pages are tied to tag-like titles. | |||
| - Title handling, aliases, and canonical tag names matter. | |||
| - There is line-level storage / revision-oriented behavior in the current implementation. | |||
| - There has been design tension between wiki revisions and wiki versions. | |||
| - Wiki conflict detection using `base_revision_id` exists on the backend side. | |||
| - Frontend support for conflict detection must be verified before assuming it is complete. | |||
| Do not redesign Wiki storage casually. | |||
| Do not add a second competing history system. | |||
| Do not break existing wiki URLs. | |||
| ### Materials | |||
| Materials connect files or reference URLs to `material` or `character` tags. | |||
| Important properties: | |||
| - A material has a `tag_id`. | |||
| - The tag must be `material` or `character`. | |||
| - A material requires either `url` or attached `file`. | |||
| - Active Storage is involved. | |||
| - Upload/security policy matters more than plain link posting. | |||
| Important unresolved/risky area: | |||
| - Material creation permissions have historically been risky because upload endpoints can be abused. | |||
| - Prefer `member` or higher for material creation unless the issue explicitly says otherwise. | |||
| ### Theatre | |||
| Theatre is an experimental watch-party style feature. | |||
| Known pieces include: | |||
| - Display | |||
| - Presence | |||
| - Next post | |||
| - Comments | |||
| - Host-like control | |||
| Do not assume theatre has complete CRUD/admin support unless the code proves it. | |||
| Theatre may become expensive if next-item selection uses random DB ordering. | |||
| ## Current high-risk areas | |||
| Treat these areas with extra care. | |||
| ### Security | |||
| - Preview API SSRF protection. | |||
| - External iframe / embed CSP. | |||
| - Markdown link safety. | |||
| - BAN / IP BAN bypass. | |||
| - Transfer-code leakage. | |||
| - Guest write access. | |||
| - Upload endpoints. | |||
| - Admin-only tag operations. | |||
| - System tag mutation. | |||
| ### Data integrity | |||
| - Tag alias canonicalization. | |||
| - Tag parent expansion. | |||
| - Post parent many-to-many relationships. | |||
| - Version tables. | |||
| - `version_no` synchronization. | |||
| - Schema drift from branch migration contamination. | |||
| - Wiki revision/version split. | |||
| - Material version recording. | |||
| ### Frontend correctness | |||
| - React Hooks must not be called conditionally. | |||
| - Role guards are currently spread across components/pages. | |||
| - TanStack Query keys must not collide between ID/name or ID/title variants. | |||
| - URL path segments containing tag names or wiki titles must use `encodeURIComponent`. | |||
| - API response types may allow `null` users for bot or migration data. | |||
| - Tag autocomplete has had duplicated logic and stale state hazards. | |||
| ### Performance | |||
| - Avoid unbounded `limit`. | |||
| - Avoid `order('RAND()')` for growing tables. | |||
| - Avoid loading full relations just to count. | |||
| - Avoid Ruby-side sorting/paging for large histories. | |||
| - Tag sidebar client-side aggregation can become expensive. | |||
| - Wiki full-text search needs deliberate indexing/design. | |||
| ## Current priority order | |||
| Use this as the default priority unless an issue says otherwise. | |||
| ### P0: Safety before broad announcement | |||
| 1. Preview API SSRF hardening. | |||
| 2. Material creation permission tightening. | |||
| 3. System tag mutation holes. | |||
| 4. `GET /users/me` transfer-code leakage through query params. | |||
| 5. Limit caps for index/history/comment APIs. | |||
| 6. CSP / iframe sandbox policy. | |||
| 7. Confirm BAN enforcement remains global. | |||
| ### P1: Core correctness | |||
| 1. Post optimistic locking with `version_no`. | |||
| 2. Wiki edit conflict handling. | |||
| 3. Wiki history/revision model clarification. | |||
| 4. Wiki search truthfulness: implement body search or remove false UI. | |||
| 5. Tag alias/canonical/wiki interaction. | |||
| 6. Tag URL encoding. | |||
| 7. TanStack Query key separation. | |||
| 8. Frontend null-user handling. | |||
| 9. React Hooks rule fixes. | |||
| 10. Material version policy. | |||
| ### P2: Operational/admin usability | |||
| 1. Admin screens for users, IPs, bans, aliases, and settings. | |||
| 2. Settings table and user settings usage. | |||
| 3. Better tag sidebar. | |||
| 4. Better role guard helpers. | |||
| 5. Better frontend tests. | |||
| 6. Better issue triage and closure of already-implemented issues. | |||
| ### P3: Future features | |||
| 1. Theatre list/create/edit/admin flow. | |||
| 2. Muted/hidden tags. | |||
| 3. Tag category custom colors. | |||
| 4. Responsive refinements. | |||
| 5. Watch-party improvements. | |||
| 6. Broader embed support. | |||
| ## Known issue triage notes | |||
| Some existing issues may already be partially or mostly implemented. | |||
| Before implementing an issue, check code first. | |||
| Examples: | |||
| - Tag search and OR/NOT search may already be mostly implemented. | |||
| - BAN enforcement may have been implemented after earlier issue drafts. | |||
| - YouTube sync exists and should not be treated as purely planned. | |||
| - Parent posts are many-to-many in current schema, even if older issues mention one-to-many. | |||
| - Some issues may reflect old schema or old branch state. | |||
| When in doubt: | |||
| 1. Inspect current code. | |||
| 2. Inspect schema. | |||
| 3. Inspect routes. | |||
| 4. Inspect frontend usage. | |||
| 5. Report whether the issue is implemented, partially implemented, not implemented, or obsolete. | |||
| 6. Only then edit. | |||
| ## Verification expectations | |||
| Backend changes: | |||
| - Run RSpec when possible. | |||
| - Add request specs for API behavior changes. | |||
| - Add model specs for validation / normalization changes. | |||
| - Check migrations and schema consistency. | |||
| - Do not silently ignore pending migrations. | |||
| Frontend changes: | |||
| - Run build. | |||
| - Run lint if configured. | |||
| - Run tests if configured. | |||
| - Add tests for important behavior when the test framework exists. | |||
| - If frontend tests are not yet installed, state that clearly. | |||
| Full-stack changes: | |||
| - Verify both backend and frontend compile/test paths where possible. | |||
| - Confirm API response shapes match TypeScript types. | |||
| - Confirm authorization behavior on both server and UI. | |||
| If commands cannot be run because dependencies are missing, report that explicitly. Do not pretend verification passed. | |||
| ## Branch / migration caution | |||
| The project has previously suffered from schema contamination caused by running migrations from another branch. | |||
| Be careful when touching: | |||
| - `db/schema.rb` | |||
| - migration files | |||
| - parent post schema | |||
| - banned / banned_at schema | |||
| - version_no migrations | |||
| - wiki asset schema | |||
| Before changing migrations: | |||
| 1. Inspect current schema. | |||
| 2. Inspect existing migrations. | |||
| 3. Confirm whether the intended branch already includes related migrations. | |||
| 4. Prefer additive migrations for shared branches. | |||
| 5. Do not edit already-applied production migrations unless explicitly instructed. | |||
| ## API design principles | |||
| Prefer explicit server-side authorization. | |||
| Do not rely only on frontend hiding. | |||
| Do not return sensitive codes unnecessarily. | |||
| Use 403 for authorization failures. | |||
| Use 422 for validation failures. | |||
| Use 409 for edit conflicts. | |||
| Do not expose internal exception messages to users. | |||
| Clamp or reject abusive limits consistently. | |||
| Keep response shape stable unless the issue explicitly includes a breaking API change. | |||
| ## Frontend design principles | |||
| Use existing route and query-key conventions. | |||
| Use TanStack Query `enabled` rather than conditional hook calls. | |||
| Do not let role-based early returns change hook order. | |||
| Centralize repeated tag autocomplete logic when touching it. | |||
| Use `encodeURIComponent` for tag names and wiki titles in URL path segments. | |||
| Prefer graceful fallback for nullable actors: | |||
| - bot operation | |||
| - deleted user | |||
| - migration-created data | |||
| - external sync | |||
| Do not assume all API user fields are non-null. | |||
| ## Testing priorities to add over time | |||
| Frontend tests are especially important because the backend already has more mature RSpec coverage. | |||
| Suggested first frontend tests: | |||
| 1. Tag autocomplete. | |||
| 2. Post form tag editing. | |||
| 3. Tag URL encoding. | |||
| 4. Wiki edit conflict UI. | |||
| 5. Role guard behavior. | |||
| 6. Null-user history rendering. | |||
| 7. Dialog behavior. | |||
| 8. Top navigation responsive behavior. | |||
| Backend test priorities: | |||
| 1. BAN enforcement across public-looking endpoints. | |||
| 2. Material permissions. | |||
| 3. Preview SSRF rejection. | |||
| 4. System tag protection. | |||
| 5. Post optimistic locking. | |||
| 6. Wiki conflict detection. | |||
| 7. Tag alias/canonical behavior. | |||
| 8. Limit caps. | |||
| 9. Parent post parsing. | |||
| 10. Version recorder behavior. | |||
| ## What Codex should not do without explicit approval | |||
| Do not: | |||
| - Replace Rails. | |||
| - Replace React. | |||
| - Replace TanStack Query. | |||
| - Redesign the database. | |||
| - Rewrite Wiki storage. | |||
| - Remove version tables. | |||
| - Change authentication model. | |||
| - Change role names. | |||
| - Change tag category names. | |||
| - Add background job infrastructure. | |||
| - Add a new UI framework. | |||
| - Add a new test framework if one already exists. | |||
| - Add major dependencies. | |||
| - Change public URL design. | |||
| - Change production storage configuration. | |||
| - Remove historical data behavior. | |||
| - Simplify BAN/security checks. | |||
| - Treat the site as private-only. | |||
| ## Good first Codex tasks | |||
| Start with investigation-only tasks. | |||
| Example: | |||
| ```txt | |||
| Inspect the repository and summarize the Rails, React, TypeScript, and test setup. | |||
| Do not modify files. | |||
| List commands that actually exist in this repository. | |||
| List risks Codex should know before editing. | |||
| ``` | |||
| Then small safe patches: | |||
| ```txt | |||
| Fix a React Hooks rule violation in one file. | |||
| Keep behavior unchanged. | |||
| Run the relevant frontend verification commands. | |||
| ``` | |||
| ```txt | |||
| Add encodeURIComponent around one tag-name URL path segment. | |||
| Add or update a test if the project has a frontend test setup. | |||
| Run build/lint. | |||
| ``` | |||
| ```txt | |||
| Add a request spec for a known authorization rule. | |||
| Do not change implementation unless the spec fails for the expected reason. | |||
| ``` | |||
| Avoid starting with: | |||
| - Wiki history redesign. | |||
| - Post versioning redesign. | |||
| - Full admin screen suite. | |||
| - Broad frontend refactor. | |||
| - Database cleanup. | |||
| - Authentication rewrite. | |||
| ## Relationship with ChatGPT | |||
| ChatGPT has been used for: | |||
| - Design review. | |||
| - Risk analysis. | |||
| - Prioritization. | |||
| - Specification reconstruction. | |||
| - Migration/locking discussions. | |||
| - Codex migration planning. | |||
| Codex should be used mainly for: | |||
| - Repository inspection. | |||
| - Localized implementation. | |||
| - Test addition. | |||
| - Running verification commands. | |||
| - Producing small reviewable diffs. | |||
| For ambiguous architecture, Codex should stop and present options rather than implement a guessed design. | |||
| ## Current strategic stance | |||
| The project should not be rewritten from scratch. | |||
| The current Rails + React system is acceptable. | |||
| The immediate goal is not elegance. | |||
| The immediate goal is safe public operation, data integrity, and maintainable incremental improvement. | |||
| Priority is: | |||
| 1. Prevent abuse/security incidents. | |||
| 2. Preserve data correctness. | |||
| 3. Make editing safe for multiple users. | |||
| 4. Add tests around fragile frontend behavior. | |||
| 5. Improve admin/operation workflows. | |||
| 6. Optimize performance after obvious dangerous patterns are removed. | |||
| ## Final rule | |||
| When current code, old specs, issue drafts, and memory disagree, current code wins. | |||
| When current code is unsafe, write that explicitly and propose a small safe fix. | |||
| When the task is too broad, split it. | |||
| When verification cannot be performed, say exactly what was not verified. | |||
| @@ -0,0 +1,29 @@ | |||
| # Commands | |||
| ## Backend | |||
| ```sh | |||
| cd backend | |||
| bundle install | |||
| bundle exec rails db:migrate | |||
| bundle exec rspec | |||
| bundle exec rails routes | |||
| ``` | |||
| ## Frontend | |||
| ```sh | |||
| cd frontend | |||
| npm install | |||
| npm run dev | |||
| npm run build | |||
| npm run lint | |||
| npm test | |||
| ``` | |||
| ### Full verification | |||
| ```sh | |||
| cd backend && bundle exec rspec | |||
| cd ../frontend && npm run build && npm run lint | |||
| ``` | |||
| @@ -0,0 +1,80 @@ | |||
| # Issue workflow | |||
| ## Source of truth | |||
| Gitea Issues are the source of truth for tasks, discussions, labels, milestones, and status. | |||
| Do not copy the full backlog into git. | |||
| Repository documents may define: | |||
| - issue templates | |||
| - triage rules | |||
| - Codex task format | |||
| - verification rules | |||
| - release checklist | |||
| ## Labels | |||
| Recommended labels: | |||
| - `P0` | |||
| - `P1` | |||
| - `P2` | |||
| - `P3` | |||
| - `security` | |||
| - `data-integrity` | |||
| - `backend` | |||
| - `frontend` | |||
| - `wiki` | |||
| - `tags` | |||
| - `materials` | |||
| - `theatre` | |||
| - `codex-ready` | |||
| - `needs-design` | |||
| - `blocked` | |||
| - `good-first-codex-task` | |||
| ## Codex-ready criteria | |||
| An issue can be labeled `codex-ready` only when it has: | |||
| - clear background | |||
| - target area | |||
| - concrete tasks | |||
| - acceptance criteria | |||
| - verification commands | |||
| - explicit non-goals | |||
| - no unresolved architecture decision | |||
| ## Workflow | |||
| 1. Create or refine the issue in Gitea. | |||
| 2. Add labels and milestone. | |||
| 3. If design is unclear, label `needs-design`. | |||
| 4. Discuss design before implementation. | |||
| 5. When scoped enough, label `codex-ready`. | |||
| 6. Give Codex the issue URL or copied issue body. | |||
| 7. Codex creates a branch. | |||
| 8. Codex implements a small patch. | |||
| 9. Codex runs verification commands. | |||
| 10. Human reviews the diff. | |||
| 11. Merge. | |||
| 12. Close the issue from the PR/commit message. | |||
| ## Commit message | |||
| Use issue references when possible: | |||
| ```txt | |||
| fix: prevent preview SSRF | |||
| Refs: #123 | |||
| ``` | |||
| or | |||
| ``` | |||
| fix: prevent preview SSRF | |||
| Closes: #123 | |||
| ``` | |||
| depending on whether the change fully resolves the issue. | |||
| @@ -0,0 +1,8 @@ | |||
| # Release checklist | |||
| - [ ] Backend specs pass | |||
| - [ ] Frontend build passes | |||
| - [ ] No pending migrations | |||
| - [ ] Preview API SSRF checked | |||
| - [ ] BAN behavior checked | |||
| - [ ] CSP checked | |||
| @@ -0,0 +1,8 @@ | |||
| # Roadmap | |||
| ## Public announcement readiness | |||
| - Harden preview API | |||
| - Tighten material creation permission | |||
| - Add admin MVP | |||
| - Improve frontend tests | |||
| @@ -0,0 +1,95 @@ | |||
| # frontend/AGENTS.md | |||
| ## Scope | |||
| These rules apply to work under `frontend/`. | |||
| This is a Vite + React + TypeScript app using TanStack Query, Tailwind CSS, Framer Motion, Radix UI-style components, MDX, and Zustand. | |||
| ## Commands | |||
| Use only scripts that exist in `package.json`: | |||
| ```sh | |||
| npm run dev | |||
| npm run build | |||
| npm run lint | |||
| npm run preview | |||
| ``` | |||
| `npm run build` runs `tsc -b && vite build`, and `postbuild` runs `node scripts/generate-sitemap.js`. | |||
| There is currently no `test` script in `package.json`. Do not run or report `npm test` unless a test script is added. | |||
| After frontend changes, run: | |||
| ```sh | |||
| npm run build | |||
| npm run lint | |||
| ``` | |||
| If either command cannot be run or fails, report the exact command and failure. | |||
| ## TypeScript | |||
| - TypeScript is strict. `tsconfig.app.json` enables `strict`, `noUnusedLocals`, `noUnusedParameters`, `erasableSyntaxOnly`, `noFallthroughCasesInSwitch`, and `noUncheckedSideEffectImports`. | |||
| - Keep types explicit at module boundaries, API helpers, and exported utilities. | |||
| - Use `import type` for type-only imports. | |||
| - Prefer existing shared types from `src/types.ts` before adding local duplicate types. | |||
| - Preserve the repository's existing spacing style in TypeScript, including GNU-style spacing before call parentheses where it is already used. | |||
| - Prefer single quotes for strings unless interpolation or escaping makes double quotes better. | |||
| ## React | |||
| - Use function components. | |||
| - Existing page components commonly export an anonymous function satisfying `FC`; match nearby file style when editing. | |||
| - React hooks must be called unconditionally and at the top level of components or custom hooks. | |||
| - Keep page-level components under `src/pages`. | |||
| - Keep shared and feature components under `src/components`. | |||
| - Use `react-router-dom` route params and navigation patterns already present in `src/App.tsx`. | |||
| - Encode URL path-segment values with `encodeURIComponent`. | |||
| ## TanStack Query | |||
| - Use `@tanstack/react-query` for server state. | |||
| - Query keys should come from `src/lib/queryKeys.ts`; add key builders there instead of using ad hoc arrays in components. | |||
| - Fetch functions should live in domain helpers under `src/lib`, such as `posts.ts`, `tags.ts`, or `wiki.ts`. | |||
| - Use `useQueryClient().invalidateQueries` with the shared root keys when mutations affect cached lists or detail views. | |||
| - The app-wide `QueryClient` is configured in `src/main.tsx`; do not create additional clients in feature code. | |||
| ## API calls | |||
| - Use `src/lib/api.ts` for HTTP calls. | |||
| - The API wrapper attaches `X-Transfer-Code` from `localStorage` and converts non-blob responses to camelCase. | |||
| - Send Rails snake_case params and request body keys where the backend expects them. | |||
| - Do not bypass the API wrapper unless there is a specific reason, such as a third-party request outside the Rails API. | |||
| - For blob responses, pass `responseType: 'blob'` so the wrapper does not camelCase the body. | |||
| ## Imports and aliases | |||
| - The `@` alias points to `frontend/src`. | |||
| - Prefer `@/...` imports for app code instead of long relative paths. | |||
| - Keep type imports separate with `import type`. | |||
| - Match existing import grouping: external packages, app modules, then type imports. | |||
| ## Tailwind and UI | |||
| - Tailwind scans `src/**/*.{html,js,ts,jsx,tsx,mdx}`. | |||
| - Use `cn` from `src/lib/utils.ts` for conditional class names and class merging. | |||
| - Reuse components from `src/components/common`, `src/components/layout`, and `src/components/ui` before adding new primitives. | |||
| - Keep Tailwind classes consistent with nearby components. | |||
| - When adding dynamic tag color classes, update `tailwind.config.js` safelist if the class cannot be statically detected. | |||
| - Do not introduce new UI libraries or production dependencies without approval. | |||
| ## Lint and build constraints | |||
| - ESLint uses `@eslint/js`, `typescript-eslint`, `eslint-plugin-react-hooks`, and `eslint-plugin-react-refresh`. | |||
| - The hooks rules are enforced; fix hook ordering instead of disabling the rule. | |||
| - `react-refresh/only-export-components` is enabled as a warning with `allowConstantExport`. | |||
| - Build failures from unused locals or unused parameters are TypeScript errors, not lint-only issues. | |||
| ## Files to avoid in routine work | |||
| - Do not edit `dist/` output directly. | |||
| - Do not inspect or modify `node_modules/` unless explicitly needed. | |||
| - Keep generated build artifacts out of source changes unless the user asks for them. | |||