| @@ -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. | |||||