実装説明書 を更新

2026-06-11 21:06:06 +09:00
コミット 3946724d3d
+501 -152
@@ -1,6 +1,6 @@
--- ---
title: 'BTRC Hub / タグ広場 現行実装仕様書' title: 'BTRC Hub / タグ広場 現行実装仕様書'
subtitle: 'btrc-hub-main.zip / btrc-hub.wiki.zip / 2026-04-25 旧本番DBダンプ確認版' subtitle: 'ソース / Wiki / 旧本番DB / 開発者ヒアリング / Gitea 課題一覧 反映版'
date: '2026-06-11' date: '2026-06-11'
lang: ja-JP lang: ja-JP
toc: true toc: true
@@ -10,9 +10,9 @@ numbersections: true
# 本書の位置づけ # 本書の位置づけ
本書は、添付された `btrc-hub-main.zip` の現行ソース、`btrc-hub.wiki.zip` の Wiki、`btrc_hub_20260425時点本番DBデータ.zip` の旧本番 DB ダンプ、既存仕様書 `BTRC_Hub_現行実装仕様書_2026-05-10_ヒアリング反映版.md` を確認し、2026-06-11 時点のタグ広場の実装事実を再構成した仕様書である。 本書は、添付された `btrc-hub-main.zip` の現行ソース、`btrc-hub.wiki.zip` の Wiki、`btrc_hub_20260425時点本番DBデータ.zip` の旧本番 DB ダンプ、既存仕様書 `BTRC_Hub_現行実装仕様書_2026-05-10_ヒアリング反映版.md`、開発者ヒアリング回答、添付 Gitea 課題一覧 JSON を確認し、2026-06-11 時点のタグ広場の実装事実と確定意図を再構成した仕様書である。
本書は願望ではなく、まずコード・スキーマ・画面・テストに現れてゐる事実を書く。未確定の設計意図は `開発者ヒアリング` として分離し、回答後に仕様へ反映する。 本書は願望ではなく、まずコード・スキーマ・画面・テストに現れてゐる事実を書く。その上で、開発者ヒアリング回答を仕様決定として本文へ反映する。実装が仕様決定に追いついてゐない箇所は `仕様決定済み・実装未反映` として明示する。
## 確認対象 ## 確認対象
@@ -23,7 +23,7 @@ numbersections: true
| Wiki | 開発 Wiki、テーブル定義書、環境構築手順 | | Wiki | 開発 Wiki、テーブル定義書、環境構築手順 |
| DB | 2026-04-25 旧本番 DB SQL ダンプ。現行 schema との差分あり | | DB | 2026-04-25 旧本番 DB SQL ダンプ。現行 schema との差分あり |
| 既存仕様 | 2026-05-10 版仕様書 | | 既存仕様 | 2026-05-10 版仕様書 |
| 課題一覧 | Gitea API 取得を試行したが、この実行環境では取得失敗。未反映 | | 課題一覧 | 添付 Gitea issue JSON 50 件。全件 open。P1/P2/P3・status・area・type を集計して反映 |
## 表記ルール ## 表記ルール
@@ -33,7 +33,27 @@ numbersections: true
| 実装あり・UI薄い | API/モデルはあるが画面導線が弱い、または管理導線が薄い | | 実装あり・UI薄い | API/モデルはあるが画面導線が弱い、または管理導線が薄い |
| 未実装・残骸候補 | スキーマだけある、または途中の設計痕跡はあるが呼び出し実装が薄いもの | | 未実装・残骸候補 | スキーマだけある、または途中の設計痕跡はあるが呼び出し実装が薄いもの |
| 注意 | 仕様として固定するには危険な実装差・不整合・セキュリティ上の穴 | | 注意 | 仕様として固定するには危険な実装差・不整合・セキュリティ上の穴 |
| 開発者ヒアリング | コードだけでは判断不能。開発者確認が必要な事項 | | 開発者ヒアリング | コードだけでは判断不能だったが、開発者回答により仕様確定した事項 |
## 本版で確定した設計判断
開発者ヒアリングにより、次を仕様として確定する。
| ID | 決定事項 | 実装状態 |
| --- | --- | --- |
| H-001 | タグ親子関係は member 以上が編集可能。UI の親タグ欄は全員に表示し、変更操作は member 以上に限る。D&D だけ admin 専用だったのは履歴管理前の安全策であり、恒久方針ではない | 一部未反映。`TagChildrenController` は admin 限定のまま |
| H-002 | 素材作成は member 以上を原則とする。URL-only 素材だけ guest 許可の余地あり。file 素材は storage 圧迫防止のため guest 不可 | 未反映。現行 `POST /materials` は current_user のみ |
| H-003 | 初回閲覧で guest user を作る単純設計は継続。ただし bot/cookie なしアクセス対策は未定 | 現行通り。ただし users 肥大化が残る |
| H-004 | 投稿履歴には親投稿変更を必ず表示する。API は `parent_posts: [{ id, title }]` を返し、表示は現在 title に加へて当時 title を注記する方向 | 未反映。API が返してゐない |
| H-005 | 素材履歴は必要。snapshot 対象は tag、URL、file blob、更新者。parent は廃止予定 | テーブルあり、実装薄い |
| H-006 | Wiki asset は Wiki 内画像/添付として実装予定。`wiki_pages.next_asset_no` と連動し、バイト列 SHA256 をキー情報に使ふ | テーブルあり、導線未確認 |
| H-007 | Gekanator は恒久 admin 専用ではない。管理者側で学習・調整後、一般ユーザ向けに公表する | 現行は admin 専用 |
| H-008 | Gekanator AI は質問分類だけでなく、既存投稿への回答補完まで行ふ。初期モデルは低コスト structured output 対応モデルを環境変数で差し替へる | converter 未実装 |
| H-009 | 上映会 host 制御は、本来サーバ側で担ふ方向。現行の active watching user 自動 host は暫定色が強い | 現行は自動 host |
| H-010 | Preview API は guest 利用を直ちに禁止はしない。ただし private IP 拒否、redirect 検査、content length 上限等は必須。クリーンに守れないなら機能廃止も検討 | 未反映 |
| H-011 | issue は全件共有が望ましい。今回は添付 JSON 50 件を反映する | 反映済み |
この表と本文が衝突する場合、本文の「確定仕様」節を優先する。現行実装との差分は、修正対象であって仕様の揺れではない。
## 用語: 公開と公表 ## 用語: 公開と公表
@@ -168,6 +188,7 @@ cd frontend && npm run test:run && npm run build && npm run lint
| `/tos` | 利用規約 | | `/tos` | 利用規約 |
| `/more` | その他 | | `/more` | その他 |
# 認証・ユーザ・BAN # 認証・ユーザ・BAN
## 認証方式 ## 認証方式
@@ -188,16 +209,50 @@ cd frontend && npm run test:run && npm run build && npm run lint
3. 無効またはコードなしなら `POST /users` で guest ユーザを作成。 3. 無効またはコードなしなら `POST /users` で guest ユーザを作成。
4. 新規作成された `inheritance_code` を localStorage へ保存。 4. 新規作成された `inheritance_code` を localStorage へ保存。
## 確定仕様: guest 自動作成
初回閲覧で guest user を作る仕様は継続する。理由は、仕様分岐を増やさず単純に保つためである。
ただし、旧本番 DB で `users = 42805` 件あるため、bot または cookie/localStorage を保持しないアクセスにより users が肥大化してゐる可能性は高い。これは仕様として許容されたわけではなく、未決定の運用リスクである。
### bot / cookie なしアクセスへの推奨方針
現時点では次を推奨仕様とする。
| 層 | 方針 |
| --- | --- |
| フロント | localStorage が使へない環境では編集系 UI を出さず、`POST /users` を連打しない |
| API | `POST /users` に IP 単位・UA 単位の軽い rate limit を置く |
| DB | `users``last_seen_at`, `created_ip_address_id`, `user_agent_hash` の追加を検討する |
| 運用 | 一定期間一度も編集/投票/コメントしてゐない guest を掃除できる Rake task を用意する |
| bot 対策 | 明確な bot UA は user を作らず 403/204 に寄せる。検索エンジン等に guest ID を発行しない |
初回閲覧 guest 自動作成を維持するなら、最低限 `POST /users` の rate limit と掃除 task は必要である。ここを放置すると、BAN・分析・バックアップが砂嵐になる。
## ロール ## ロール
| role | 意味 | | role | 意味 |
| --- | --- | | --- | --- |
| `guest` | 自動生成される通常閲覧者。閲覧中心 | | `guest` | 自動生成される通常閲覧者。閲覧、閲覧済み、上映会在席、コメント、skip 投票など軽い参加行為の主体 |
| `member` | 投稿・タグ・Wiki・素材等の編集者 | | `member` | 投稿・タグ・Wiki・素材等の編集者 |
| `admin` | 管理者。Gekanator、タグ親子 API 等も利用可能 | | `admin` | 管理者。管理者専用調整機能や未公開ツールを利用可能 |
`User#gte_member?``member` または `admin` を許可する。 `User#gte_member?``member` または `admin` を許可する。
## 権限原則
| 操作 | 権限 |
| --- | --- |
| 投稿作成/更新 | member+ |
| タグ編集 | member+ |
| タグ親子編集 | member+。現行の D&D/admin 限定は暫定 |
| Wiki 作成/更新 | member+ |
| 素材作成 | member+。URL-only guest 許可は将来検討 |
| file 素材作成 | member+ 必須 |
| Gekanator 調整 | admin |
| Gekanator 公開プレイ | 将来 public 化予定 |
| Preview API | public/guest 相当を維持する可能性あり。ただし SSRF 対策必須 |
## BAN ## BAN
`ApplicationController``before_action` は次の順序で動く。 `ApplicationController``before_action` は次の順序で動く。
@@ -217,7 +272,7 @@ IP は `IPAddr.new(request.remote_ip).hton` により binary 化して `ip_addre
### 注意 ### 注意
`users.inheritance_code` はモデル上必須・64文字以内だが、現行 schema では一意 index が見当たらない。UUID なので衝突可能性は低いが、認証トークンに DB 一意制約がないのは設計として弱い。 `users.inheritance_code` はモデル上必須・64文字以内だが、現行 schema では一意 index が見当たらない。UUID なので衝突可能性は低いが、認証トークンに DB 一意制約がないのは設計として弱い。DB 一意 index を追加すべきである。
# エラー応答 # エラー応答
@@ -386,6 +441,7 @@ IP は `IPAddr.new(request.remote_ip).hton` により binary 化して `ip_addre
`updated_at` sort は、投稿本体の `updated_at``post_tags.updated_at` の最大値を使ふ。 `updated_at` sort は、投稿本体の `updated_at``post_tags.updated_at` の最大値を使ふ。
## 投稿履歴 ## 投稿履歴
`post_versions` は immutable snapshot である。 `post_versions` は immutable snapshot である。
@@ -396,7 +452,7 @@ IP は `IPAddr.new(request.remote_ip).hton` により binary 化して `ip_addre
| `version_no` | 投稿内連番 | | `version_no` | 投稿内連番 |
| `event_type` | `create`, `update`, `discard`, `restore` | | `event_type` | `create`, `update`, `discard`, `restore` |
| `title`, `url`, `thumbnail_base` | 投稿本体 | | `title`, `url`, `thumbnail_base` | 投稿本体 |
| `tags` | タグ名の空白区切り snapshot | | `tags` | タグ名の空白区切り snapshot。将来 `tags_json` へ移行予定 |
| `parent_post_ids` | 親投稿 ID の空白区切り snapshot | | `parent_post_ids` | 親投稿 ID の空白区切り snapshot |
| `original_created_from/before` | 元日時範囲 | | `original_created_from/before` | 元日時範囲 |
| `created_by_user_id` | 操作者 | | `created_by_user_id` | 操作者 |
@@ -408,9 +464,45 @@ IP は `IPAddr.new(request.remote_ip).hton` により binary 化して `ip_addre
- 同一 snapshot の update は版を増やさない。 - 同一 snapshot の update は版を増やさない。
- 作成後、対象 record の `version_no` を更新する。 - 作成後、対象 record の `version_no` を更新する。
### 注意: frontend 型と API のズレ ## 確定仕様: 親投稿変更履歴
フロント `PostVersion` 型には `parentPosts` がある。しかし現行 `PostVersionsController#index``parent_post_ids` を select/serialize しておらず、`parentPosts` を返してゐない。これは履歴 UI の仕様不整合である。親投稿変更履歴を画面に出すつもりなら、API 実装が不足してゐる。 投稿履歴画面では、親投稿の追加・削除を必ず表示する。現行 API が返してゐないのはバグである。
`GET /posts/versions` は各 version に少なくとも次を返すべきである。
```ts
parentPosts: Array<{
id: number
title: string | null
historicalTitle?: string | null
}>
```
API の snake_case 原型は次を想定する。
```json
{
"parent_posts": [
{
"id": 123,
"title": "現在のタイトル",
"historical_title": "履歴作成当時のタイトル"
}
]
}
```
表示仕様:
- 通常表示は現在 title を使ふ。
- 当時 title が現在 title と異なる場合、`(当時タイトル:{historical_title})` を付記する。
- 対象投稿が削除済みまたは参照不能なら、`#123(現在参照不可)` のやぅに ID を残す。
タグ履歴についても同様に、現在名だけでなく当時の名前・カテゴリを表示できる形が望ましい。添付 issue #354 により、`post_versions.tags_json` を作り、旧 `post_versions.tags` を廃止する方向が課題化されてゐる。
### 実装差分
フロント `PostVersion` 型には `parentPosts` がある。しかし現行 `PostVersionsController#index``parent_post_ids` を select/serialize しておらず、`parentPosts` を返してゐない。これは仕様決定済みの実装バグである。
# タグ仕様 # タグ仕様
@@ -490,6 +582,7 @@ IP は `IPAddr.new(request.remote_ip).hton` により binary 化して `ip_addre
タグ詳細の full update では、名称変更時に旧名が aliases に追加される。 タグ詳細の full update では、名称変更時に旧名が aliases に追加される。
## タグ親子 ## タグ親子
`tag_implications` は子タグから親タグへの関係を表す。 `tag_implications` は子タグから親タグへの関係を表す。
@@ -503,11 +596,27 @@ IP は `IPAddr.new(request.remote_ip).hton` により binary 化して `ip_addre
- 自己親は禁止。 - 自己親は禁止。
- 投稿側では、付与タグの親タグが再帰的に展開される。 - 投稿側では、付与タグの親タグが再帰的に展開される。
### 注意: 権限仕様が二重化してゐる ## 確定仕様: タグ親子編集権限
`TagChildrenController` の親子追加/削除 API は admin 限定である。一方、`TagsController#update_all` は member 以上で `parent_tags` を更新できる。つまり、API とタグ編集フォームの設計意図がズレてゐる可能性が高い タグ親子関係の編集は **member 以上** が可能である
ここは仕様として固定する前に開発者ヒアリングが必要である。 UI 仕様:
| ユーザ | 親タグ欄の表示 | 変更 |
| --- | --- | --- |
| guest | 表示する | 不可 |
| member | 表示する | 可 |
| admin | 表示する | 可 |
現行の D&D 操作が admin 限定だった理由は、履歴管理がなかった時期に誤操作を避けるための安全策である。現在は履歴管理があるため、admin 限定を続ける必然性は薄い。
### 実装差分
`TagChildrenController` の親子追加/削除 API は admin 限定である。一方、`TagsController#update_all` は member 以上で `parent_tags` を更新できる。正しい仕様は member+ なので、専用 API 側の権限を揃へる必要がある。
### 追加制約
添付 issue #332 により、上位タグの循環登録禁止が P1 バグとして課題化されてゐる。タグ親子編集を member+ に開くなら、循環検出は必須である。
## タグ一覧/検索 ## タグ一覧/検索
@@ -709,9 +818,33 @@ content revision 同士のみ差分対象。
`Wiki::Commit.redirect!` は現行では `raise '廃止しました.'` で無効化されてゐる。ただし schema と controller には redirect revision の痕跡がある。 `Wiki::Commit.redirect!` は現行では `raise '廃止しました.'` で無効化されてゐる。ただし schema と controller には redirect revision の痕跡がある。
## 注意: wiki_assets
`wiki_assets` テーブルは schema に存在するが、現行ソースで明確な model/controller/画面導線は確認できない。Wiki 内画像/添付の将来設計または残骸候補である。 ## Wiki asset
`wiki_assets` は Wiki 内画像/添付ファイルのための将来機能である。
確定仕様:
- Wiki ページ内に画像または添付ファイルを持たせる。
- `wiki_pages.next_asset_no` と連動し、ページ内 asset 番号を採番する。
- `wiki_assets.sha256` は添付バイト列を SHA256 に通した値を保持する。
- 同一バイト列の重複検出・再利用・整合性確認に `sha256` を使ふ。
- 本体保存は Active Storage を前提にする。
推奨カラム意味:
| カラム | 意味 |
| --- | --- |
| `wiki_page_id` | 所属 Wiki ページ |
| `no` | ページ内 asset 番号 |
| `sha256` | 添付バイト列の SHA256 |
| `filename` | 表示/ダウンロード用ファイル名 |
| `content_type` | MIME type |
| `byte_size` | サイズ |
| `created_by_user_id` | 追加者 |
現行ソースでは明確な model/controller/画面導線は確認できない。したがって、schema は将来機能として正当化されたが、実装は未完である。
# 素材仕様 # 素材仕様
@@ -724,7 +857,7 @@ content revision 同士のみ差分対象。
| `tag_id` | 必須・一意 | | `tag_id` | 必須・一意 |
| `url` | 任意。file がなければ必須 | | `url` | 任意。file がなければ必須 |
| `file` | Active Storage 添付。url がなければ必須 | | `file` | Active Storage 添付。url がなければ必須 |
| `parent_id` | 親素材。任意 | | `parent_id` | 親素材。任意だが廃止予定 |
| `created_by_user_id` | 作成者 | | `created_by_user_id` | 作成者 |
| `updated_by_user_id` | 更新者 | | `updated_by_user_id` | 更新者 |
| `discarded_at` | 論理削除 | | `discarded_at` | 論理削除 |
@@ -735,25 +868,67 @@ content revision 同士のみ差分対象。
素材に紐づくタグは `character` または `material` カテゴリでなければならない。 素材に紐づくタグは `character` または `material` カテゴリでなければならない。
注意: バリデーション文言は「素材カテゴリのタグ」と言ってゐるが、実装上は `character` も許可してゐる。 注意: バリデーション文言は「素材カテゴリのタグ」と言ってゐるが、実装上は `character` も許可してゐる。仕様としては **character も素材を持てる** で確定し、文言を直すべきである。
## API ## API
| API | 権限 | 内容 | | API | 現行権限 | 確定仕様 | 内容 |
| --- | --- | --- | | --- | --- | --- | --- |
| `GET /materials` | public | 素材一覧 | | `GET /materials` | public | public | 素材一覧 |
| `GET /materials/:id` | public | 素材詳細。関連 Wiki 本文も返す | | `GET /materials/:id` | public | public | 素材詳細。関連 Wiki 本文も返す |
| `POST /materials` | current_user 必須 | 素材作成 | | `POST /materials` | current_user 必須 | member+ | 素材作成 |
| `PUT/PATCH /materials/:id` | member+ | 素材更新 | | `PUT/PATCH /materials/:id` | member+ | member+ | 素材更新 |
| `DELETE /materials/:id` | member+ | 論理削除 | | `DELETE /materials/:id` | member+ | member+ | 論理削除 |
### 注意: 作成権限が緩い ## 確定仕様: 素材作成権限
`POST /materials``current_user` があれば通る。フロントは初回訪問者に guest を自動作成するため、実質的に guest でも素材作成可能である。`docs/roadmap.md` にも `Tighten material creation permission` があるため、ここは既知の弱点と見るべきである 素材作成は member 以上を原則とする。現行の guest 作成可能状態は設計意図ではなく実装バグとして扱ふ
### 注意: material_versions ただし、URL-only 素材については guest への開放余地がある。理由は、file 付き素材と違ひ、オブジェクト・ストレージを直接圧迫しないためである。
推奨仕様:
| 入力 | guest | member+ | 理由 |
| --- | --- | --- | --- |
| URL-only | 将来検討 | 可 | storage 圧迫がない。荒らし URL への moderation は別途必要 |
| file-only | 不可 | 可 | storage 圧迫・危険ファイル・著作権対応が重い |
| URL + file | 不可 | 可 | file を含むため member+ |
現時点では単純に `POST /materials` を member+ へ制限するのが最優先である。URL-only guest 開放は、rate limit、通報/削除導線、URL preview の安全化後に検討する。
## 確定仕様: material_versions
`material_versions` は必要なテーブルであり、残骸ではない。素材にも履歴を持たせる。
snapshot 対象:
| 対象 | 必要性 |
| --- | --- |
| tag | 素材が何に紐づいてゐたかを復元するため必要 |
| URL | URL-only / 参照先変更履歴として必要 |
| file blob | 添付ファイル差替え履歴として必要 |
| 更新者 | 誰が変更したかを追跡するため必要 |
| parent | 廃止予定のため新仕様では対象外 |
推奨 `material_versions` 返却形:
```ts
interface MaterialVersion {
id: number
materialId: number
versionNo: number
eventType: 'create' | 'update' | 'discard' | 'restore'
tag: { id: number, name: string | null, category: string | null } | null
url: string | null
fileBlobId: number | null
fileUrl: string | null
createdByUser: { id: number, name: string | null } | null
createdAt: string
}
```
添付 issue #306 にも `material_versions` 実装が明記されてゐる。素材管理は UI だけでなく履歴まで含めて完了とみなすべきである。
`material_versions` テーブルは schema にあるが、現行ソースで MaterialVersion model / recorder / controller 利用は確認できない。素材履歴は未実装または残骸候補である。
# 上映会仕様 # 上映会仕様
@@ -788,13 +963,28 @@ content revision 同士のみ差分対象。
- `watching_users` - `watching_users`
- `skip_vote` - `skip_vote`
### 注意 ## 確定仕様: host 制御の方向性
ホスト選出はかなり自動的で、悪意あるユーザが active host になる余地がある。公表後に荒らし耐性を考へるなら、host 権限の条件を仕様化する必要がある 現行の active watching user から自動で host を選ぶ仕様は暫定である。開発者意図としては、上映会の進行制御はできるだけサーバ側で担ひたい
したがって、長期仕様では次を目標にする。
| 領域 | 方針 |
| --- | --- |
| 次投稿決定 | サーバが theatre 状態・番組表・skip イベント・重みを見て決定 |
| 再生不能検知 | クライアント通知は使ふが、最終判断はサーバ状態に集約 |
| host | 手動操作が必要な暫定制御者。恒久的な絶対権限者ではない |
| 荒らし耐性 | 公表前に host 乗っ取り・連続 next・skip 連打への制限を入れる |
`skip 投票と next_post の権限を分離するか` という問いは、現時点では仕様語彙が曖昧だった。ここでは次の意味に定義する。
- skip 投票: 視聴者全員ができる意思表示。
- next_post: 現行では host だけが実行できる即時進行操作。
- 将来仕様: next_post も単なる host 命令ではなく、サーバの進行判断 API に寄せる。
## 次投稿選択 ## 次投稿選択
`PATCH /theatres/:id/next_post` は host のみ実行可能。 `PATCH /theatres/:id/next_post`現行 host のみ実行可能。
`TheatrePostSelector` は次の投稿を候補にする。 `TheatrePostSelector` は次の投稿を候補にする。
@@ -805,6 +995,8 @@ content revision 同士のみ差分対象。
`penalty` は、active user が過去に skip した投稿のタグに基づく。つまり、視聴者が嫌がったタグを持つ投稿ほど選ばれにくい。 `penalty` は、active user が過去に skip した投稿のタグに基づく。つまり、視聴者が嫌がったタグを持つ投稿ほど選ばれにくい。
添付 issue #360 により、上映会で上位タグを持つタグが表示されないバグが P1 として課題化されてゐる。上映会はタグ継承・親タグ展開と強く結びつくため、タグ表示は単純な直接付与タグだけでは不足する。
## 番組表 ## 番組表
`theatre_programmes` は再生履歴/番組表である。 `theatre_programmes` は再生履歴/番組表である。
@@ -863,7 +1055,7 @@ required_count = floor(active_watching_users_count / 2) + 1
- YouTube は `react-youtube` - YouTube は `react-youtube`
- ニコニコは `NicoViewer` による JS API `postMessage` - ニコニコは `NicoViewer` による JS API `postMessage`
- duration が 0 以下など再生不能っぽい場合、host が次投稿へ進める。 - duration が 0 以下など再生不能っぽい場合、現行では host が次投稿へ進める。
## 埋め込み ## 埋め込み
@@ -878,21 +1070,38 @@ required_count = floor(active_watching_users_count / 2) + 1
ニコニコ iframe の `loadComplete` timeout は 8000 ms。 ニコニコ iframe の `loadComplete` timeout は 8000 ms。
# Gekanator 仕様 # Gekanator 仕様
## 概要 ## 概要
Gekanator は管理者専用の投稿当てゲームである。投稿群に関する質問を出し、回答から候補投稿を絞り、最終的に投稿を推測する。 Gekanator は投稿当てゲームである。投稿群に関する質問を出し、回答から候補投稿を絞り、最終的に投稿を推測する。
用途はゲームだけではない。終了後の質問追加・回答保存により、投稿間類似や識別質問を蓄積する学習機構でもある。 用途はゲームだけではない。終了後の質問追加・回答保存により、投稿間類似や識別質問を蓄積する学習機構でもある。
## 権限 ## 確定仕様: 公開範囲
Gekanator は恒久的な admin-only ツールではない。
段階:
| 段階 | 公開範囲 | 目的 |
| --- | --- | --- |
| 現行 | admin のみ | 学習データ作成、質問品質確認、パラメータ調整 |
| 調整後 | member または限定ユーザ | 追加学習、UX 確認 |
| 公表版 | 一般ユーザ | おたのしみゲームとして公開 |
公表版では、内部パラメータ・候補スコア・調整用情報は非表示にする。添付 issue #361 により「グカネータ公開」が課題化されてゐる。
## 現行権限
バックエンド API は `current_user&.admin?` でなければ 404 を返す。フロントも `/gekanator` は admin 以外 `NotFound` を表示する。 バックエンド API は `current_user&.admin?` でなければ 404 を返す。フロントも `/gekanator` は admin 以外 `NotFound` を表示する。
これは現行実装であって最終仕様ではない。
## 投稿カタログ ## 投稿カタログ
`GET /gekanator/posts` は admin 専用。 `GET /gekanator/posts`現行 admin 専用。
返却投稿: 返却投稿:
@@ -903,7 +1112,7 @@ Gekanator は管理者専用の投稿当てゲームである。投稿群に関
## 質問カタログ ## 質問カタログ
`GET /gekanator/questions` は admin 専用。 `GET /gekanator/questions`現行 admin 専用。
質問 kind: 質問 kind:
@@ -963,9 +1172,15 @@ status:
候補が潰れる場合、高難度・非 unknown の過去回答を `0.35` に緩和して復旧を試みる。 候補が潰れる場合、高難度・非 unknown の過去回答を `0.35` に緩和して復旧を試みる。
## 確定仕様: 質問数ルール
通常は 25 問を経過するまで推測しない。「続けますか → はい」の流れでも 25 問ルールを維持する。
例外は、100% 近似に近い確信状態で、候補が実質的に一意になった場合のみである。ただし、この例外も最低質問数・2 位候補との差・誤答時の UX を見て慎重に扱ふ。
## ゲーム保存 ## ゲーム保存
`POST /gekanator/games` は admin 専用。 `POST /gekanator/games`現行 admin 専用。
入力: 入力:
@@ -984,7 +1199,7 @@ status:
`POST /gekanator/question_suggestions`: `POST /gekanator/question_suggestions`:
- admin 専用。 - 現行 admin 専用。
- `game_id` - `game_id`
- `question_text` 1000 文字以内。 - `question_text` 1000 文字以内。
- `answer` は enum。 - `answer` は enum。
@@ -998,11 +1213,40 @@ status:
`POST /extra_question_answers` は、それらへの回答を `GekanatorQuestionExample` として保存する。 `POST /extra_question_answers` は、それらへの回答を `GekanatorQuestionExample` として保存する。
## AI 変換 ## 確定仕様: AI 変換
`POST /gekanator/question_suggestions/:id/ai_convert` は存在するが、`Gekanator::QuestionSuggestionAiConverter` は現行 `NotImplementedError` を投げる。 `POST /gekanator/question_suggestions/:id/ai_convert` は存在するが、`Gekanator::QuestionSuggestionAiConverter` は現行 `NotImplementedError` を投げる。
AI 予算: AI は質問分類だけでなく、既存投稿への回答補完も行ふ。
担当範囲:
| 処理 | 内容 |
| --- | --- |
| 質問分類 | ユーザ文を `tag`, `source`, `title`, `original_date`, `post_similarity` 等へ分類 |
| 正規化 | 曖昧な質問文をゲームで扱へる構造へ変換 |
| 回答補完 | 既存投稿群に対して yes/no/partial/probably_no/unknown を推定 |
| 信頼度 | AI 推定には confidence を持たせ、低信頼は pending に留める |
| 監査 | AI が作った質問・回答は `ai_generated` として追跡可能にする |
## AI モデル選定
初期実装は OpenAI Responses API + Structured Outputs を前提とする。
推奨初期値:
| 項目 | 値 |
| --- | --- |
| 環境変数 | `GEKANATOR_AI_MODEL` |
| 初期モデル | `gpt-5.4-mini` |
| 出力形式 | JSON Schema strict structured output |
| reasoning | `none` または `low` から開始 |
| 大量補完 | Batch API または夜間 Rake task |
| 予算 | 仮置き。半年 500 円程度を目標に、run 数・token 数・batch 割引で調整 |
`gpt-5.4-mini` は価格と品質の妥協点としての初期値であり、固定ではない。分類だけなら将来 `gpt-5.4-nano` 等のさらに低価格モデルへ落とせるやぅに、モデル ID は必ず環境変数化する。
旧メモの AI 予算:
| 項目 | 値 | | 項目 | 値 |
| --- | --- | | --- | --- |
@@ -1010,7 +1254,8 @@ AI 予算:
| 1 run 見積 | 5 円 | | 1 run 見積 | 5 円 |
| 超過見込み | 402 `blocked_budget` | | 超過見込み | 402 `blocked_budget` |
現行では API 枠だけが先行してゐる。 この見積は仮置きであり、確定予算ではない。現在の希望は「半年で 500 円くらゐ」であるため、1 run 5 円の設計は高すぎる。AI 補完は per game 即時実行ではなく、batch 化・cache 化・差分実行に寄せるべきである。
# プレビュー API # プレビュー API
@@ -1036,9 +1281,41 @@ AI 予算:
- 生成画像を MiniMagick で 180x180 に resize。 - 生成画像を MiniMagick で 180x180 に resize。
- PNG inline で返す。 - PNG inline で返す。
### 注意: SSRF/任意URLアクセスリスク ## 確定仕様: セキュリティ方針
この API は current_user 必須だが、guest は自動作成されるため実質的に広く使へる。任意 URL をサーバから取得・Node screenshot するので、SSRF/内部ネットワーク到達/リソース消費のリスクがある。roadmap に `Harden preview API` があるのは妥当で、これは公表前の赤信号である。 Preview API は guest 相当にも提供したい機能である。ただし、危険性は高い。クリーンに守れないなら機能廃止も検討対象である。
最低必須対策:
| 対策 | 必須度 | 内容 |
| --- | --- | --- |
| scheme allowlist | 必須 | `http`, `https` のみ |
| DNS 解決後 IP 検査 | 必須 | private, loopback, link-local, multicast, metadata IP を拒否 |
| redirect 検査 | 必須 | redirect 先 URL も同じ検査を繰り返す |
| content length 上限 | 必須 | title HTML 取得、画像、screenshot 出力に上限 |
| timeout | 必須 | connect/read/browser 全て短めに制限 |
| concurrency 制限 | 必須 | screenshot は特に重いので同時実行数を絞る |
| rate limit | 必須 | IP + user + URL host 単位で制限 |
| user agent | 推奨 | 明示 UA を使ひ、必要なら deny されても落ちない UX にする |
| cache | 推奨 | 同一 URL の title/thumbnail を短時間 cache |
## rate limit 推奨仕様
Preview API を guest に残すなら、Rails 単体の before_action ではなく、Rack middleware または reverse proxy で一次防衛するのがよい。
推奨値の初期案:
| 単位 | `/preview/title` | `/preview/thumbnail` |
| --- | --- | --- |
| IP | 60 req / 10 min | 10 req / 10 min |
| user | 120 req / 10 min | 20 req / 10 min |
| URL host | 30 req / 10 min | 10 req / 10 min |
thumbnail は headless browser を起動するため、title より厳しくする。
## 実装差分
現行 API は current_user 必須だが、guest は自動作成されるため実質的に広く使へる。任意 URL をサーバから取得・Node screenshot するので、SSRF/内部ネットワーク到達/リソース消費のリスクがある。これは公表前の赤信号である。
# 類似度計算 # 類似度計算
@@ -1171,190 +1448,262 @@ Vitest が導入され、かなり広範囲にテストがある。
2026-05-10 時点の「フロントテストなし」状態からは明確に改善してゐる。 2026-05-10 時点の「フロントテストなし」状態からは明確に改善してゐる。
# 実装上の不整合・危険点 # 実装上の不整合・危険点
この節は慰めではなく、仕様確定前に潰すべき箇所である。ここを曖昧にしたまま公表すると、後で設計負債が増える。 この節は、仕様決定後も実装が追いついてゐない箇所を列挙する。ここを曖昧にしたまま公表すると、設計負債が増える。
## P0: Preview API の SSRF/リソース消費リスク ## P0: Preview API の SSRF/リソース消費リスク
`/preview/title``/preview/thumbnail` はサーバから任意 URL へアクセスする。guest 自動生成により、事実上かなり広い入口である。 `/preview/title``/preview/thumbnail` はサーバから任意 URL へアクセスする。guest 自動生成により、事実上かなり広い入口である。
必要な対策候補: 仕様としては guest 提供の余地を残すが、次は必須である。
- private IP / localhost / link-local / metadata IP の拒否。 - private IP / localhost / link-local / metadata IP の拒否。
- scheme allowlist を http/https に固定。 - scheme allowlist を http/https に固定。
- redirect 先検査。 - redirect 先検査。
- content length 上限。 - content length 上限。
- screenshot queue/timeout/concurrency 制限。 - screenshot queue/timeout/concurrency 制限。
- member 以上へ制限するか、rate limit を置く - IP + user + host rate limit。
守れないなら機能ごと廃止を検討する。便利さよりサーバ防衛が優先である。
## P0: guest 自動作成による users 肥大化 ## P0: guest 自動作成による users 肥大化
旧 DB で `users = 42805`。実利用規模に比して異様に多い。bot が来るたび user を発行してゐる可能性が高い。 旧 DB で `users = 42805`。実利用規模に比して異様に多い。bot が来るたび user を発行してゐる可能性が高い。
機会費用: guest 自動作成は継続するが、以下は未実装リスクとして残る。
- BAN/権限/ユーザ管理がノイズで死ぬ - `POST /users` rate limit
- 本物ユーザの分析ができない - bot UA / localStorage 不可環境への抑制
- DB とバックアップが無駄に膨らむ - 編集実績のない guest 掃除 task
- user 分析用の `last_seen_at` 等。
少なくとも「閲覧だけの初回アクセスで user を作る」設計は再検討対象。
## P0: 素材作成権限が緩い ## P0: 素材作成権限が緩い
`POST /materials` は current_user だけを要求する。guest 自動作成と合はせると、匿名同然で素材作成できる。 `POST /materials` は current_user だけを要求する。guest 自動作成と合はせると、匿名同然で素材作成できる。
roadmap にも `Tighten material creation permission` がある。これは既知の穴として扱ふべき 確定仕様は member+ 作成である。file 素材は storage を食ふため、ここは早めに閉じるべきである
## P1: タグ親子編集権限が二重化 ## P1: タグ親子編集権限が二重化
- `TagChildrenController`: admin のみ。 - `TagChildrenController`: admin のみ。
- `TagsController#update_all`: member 以上で `parent_tags` 更新可。 - `TagsController#update_all`: member 以上で `parent_tags` 更新可。
どちらを正とするか決める必要がある。現状は仕様が二枚舌 確定仕様は member+ なので、専用 API 側を修正する。あはせて issue #332 の循環登録禁止を入れないと、member 開放は危ない
## P1: `post_versions` の parent post 履歴 API が不足 ## P1: `post_versions` の parent post 履歴 API が不足
schema と snapshot には `parent_post_ids` があるが、`PostVersionsController` が返してゐない。フロント型には `parentPosts` がある。履歴画面で親投稿変更を出すなら API 追加が必要。 schema と snapshot には `parent_post_ids` があるが、`PostVersionsController` が返してゐない。フロント型には `parentPosts` がある。
## P1: `material_versions`, `wiki_assets` の幽霊テーブル 確定仕様として、親投稿変更履歴は表示必須である。`parent_posts`返却を追加する。
schema にあるが、現行導線が薄い/見当たらない。将来機能なら仕様化、残骸なら削除か凍結判断が必要。 ## P1: `material_versions`, `wiki_assets` は必要だが未実装
開発者回答により、どちらも将来機能として必要と確定した。
- `material_versions`: tag, URL, file blob, 更新者の履歴。
- `wiki_assets`: Wiki 内画像/添付、`next_asset_no`、sha256 連動。
したがって「残骸として削除」ではなく「仕様化して実装」が正しい。
## P1: inheritance_code の一意制約なし ## P1: inheritance_code の一意制約なし
認証トークンである以上、DB 一意 index を張るべき。UUID 衝突は現実的に低くても、仕様としては弱い。 認証トークンである以上、DB 一意 index を張るべき。UUID 衝突は現実的に低くても、仕様としては弱い。
## P2: Material のバリデーション文言と許可カテゴリのズレ ## P1: 上映会 host 乗っ取り耐性
文言は「素材カテゴリ」だが、実装は `character` も許可する。仕様として `character` も素材を持てるなら文言を直すべき。
## P2: Gekanator AI 変換 API は未実装
API と予算 model はあるが converter は NotImplemented。画面から到達できるなら UX 上は「未実装」と明示した方がよい。
## P2: 上映会 host 乗っ取り耐性
active host が切れたら次の watching ユーザが host になる。小規模内輪ではよいが、公表後は荒らしが再生制御を握る可能性がある。 active host が切れたら次の watching ユーザが host になる。小規模内輪ではよいが、公表後は荒らしが再生制御を握る可能性がある。
# 開発者ヒアリング 長期方針はサーバ主導進行である。現行 host は暫定制御者と見るべきで、連続 next、短時間 host 交替、再生不能偽装への防御が必要である。
以下は、仕様書へ確定反映する前に開発者確認が必要な事項である。回答を受けたら「回答済み」として本書の該当節へ反映する。 ## P2: Material のバリデーション文言と許可カテゴリのズレ
## H-001: タグ親子編集の正しい権限 文言は「素材カテゴリ」だが、実装は `character` も許可する。仕様として `character` も素材を持てるため、文言を直すべきである。
現状、タグ親子 API は admin 限定だが、タグ full update は member でも `parent_tags` を更新できる。 ## P2: Gekanator AI 変換 API は未実装
質問: API と予算 model はあるが converter は NotImplemented。公表版では質問構成 AI を稼動させる予定なので、未実装表示ではなく実装対象である。
1. タグ親子関係の編集は admin 限定にしたいのか。 ## P2: issue 管理との接続が手動
2. それとも member にも許可したいのか。
3. UI のタグ編集フォームで親タグ欄を出す対象は誰か。
## H-002: 素材作成権限 今回の issue 一覧は添付 JSON で取り込んだ。継続的な同期手段は未確定である。全件共有方針は妥当だが、手動添付だけだと仕様書がすぐ腐る。
`POST /materials` は current_user のみ要求してゐる。
質問: # 開発者ヒアリング反映結果
1. guest に素材作成を許す意図か 本章は、前版で未確定だった H-001 から H-011 への回答を記録する。回答内容は本文へ反映済みである
2. member 以上に制限すべきか。
3. URL-only 素材と file 素材で権限を分ける必要はあるか。
## H-003: guest 自動作成の継続可否 | ID | 状態 | 反映先 |
| --- | --- | --- |
| H-001 | 回答済み | タグ仕様 / タグ親子 |
| H-002 | 回答済み | 素材仕様 / 素材作成権限 |
| H-003 | 回答済み | 認証・ユーザ・BAN / guest 自動作成 |
| H-004 | 回答済み | 投稿仕様 / 投稿履歴 |
| H-005 | 回答済み | 素材仕様 / material_versions |
| H-006 | 回答済み | Wiki 仕様 / Wiki asset |
| H-007 | 回答済み | Gekanator 仕様 / 公開範囲 |
| H-008 | 回答済み | Gekanator 仕様 / AI 変換 |
| H-009 | 回答済み | 上映会仕様 / host 制御 |
| H-010 | 回答済み | Preview API / セキュリティ方針 |
| H-011 | 回答済み | Gitea 課題一覧反映 |
旧 DB で users が 42805 件ある。 ## 残る未決定事項
質問: 回答後も、次はまだ設計決定が必要である。
1. 初回閲覧だけで guest user を作る仕様を継続するか。 | 項目 | 未決定内容 | 推奨 |
2. 閲覧者には user を作らず、編集/投票/コメント時に初めて作る形へ変更するか。 | --- | --- | --- |
3. bot 判定や cookie/localStorage なしアクセスへの扱ひをどうするか。 | guest bot 対策 | cookie/localStorage なしアクセス、bot UA、掃除 task の詳細 | `POST /users` rate limit と未使用 guest 掃除 task から着手 |
| URL-only 素材の guest 開放 | いつ・どの条件で許可するか | 当面は member+ に閉じる |
| Gekanator AI 予算 | 半年 500 円程度をどう守るか | batch 化、cache 化、差分実行、model env 化 |
| 上映会 host | 完全サーバ主導へどう移行するか | host を暫定権限に格下げし、server advancer を育てる |
| Preview API | 防御実装不能な場合の廃止判断 | SSRF 防御を実装できなければ thumbnail 生成から止める |
| issue 同期 | 今後どう ChatGPT と共有し続けるか | 当面は JSON 添付。将来は read-only token + 手元 export script |
## H-004: post_versions の親投稿履歴 # Gitea 課題一覧反映
`post_versions.parent_post_ids` は保存されるが、履歴 API が返してゐない 添付された Gitea issue JSON を取り込み、2026-06-11 時点の仕様書へ反映した。今回の添付には 50 件が含まれ、全件 `open` である
質問: ## 集計
1. 投稿履歴画面で親投稿追加/削除を表示したいか。 ### Priority
2. 表示するなら、当時の title snapshot が必要か、現在 title 参照でよいか。
3. `parent_post_ids` だけでよいか、version API で `parentPosts` を返すべきか。
## H-005: material_versions の扱ひ | priority | 件数 |
| --- | ---: |
| `P1` | 8 |
| `P2` | 26 |
| `P3` | 15 |
| `優先度なし` | 1 |
`material_versions` が schema に存在するが、利用実装が薄い。 ### Status
質問: | status | 件数 |
| --- | ---: |
| `status/blocked` | 8 |
| `status/in-progress` | 1 |
| `status/ready` | 39 |
| `status/review` | 1 |
| `statusなし` | 1 |
1. 素材にも履歴を持たせる予定か。 ### Area
2. 予定があるなら snapshot 対象は URL、tag、parent、file blob、作成/更新者のどれか。
3. 不要なら migration 残骸として扱ふか。
## H-006: wiki_assets の扱ひ | area | 件数 |
| --- | ---: |
| `area/backend` | 26 |
| `area/frontend` | 25 |
`wiki_assets` が存在する。 ### Type
質問: | type | 件数 |
| --- | ---: |
| `type/bug` | 8 |
| `type/enhancement` | 24 |
| `type/task` | 17 |
| `typeなし` | 1 |
1. Wiki 内画像/添付ファイル機能を実装する予定か。 ## P1 課題
2. `wiki_pages.next_asset_no` と連動する想定か。
3. Active Storage と `wiki_assets.sha256` の関係はどう設計するか。
## H-007: Gekanator の公開範囲 P1 は公表前または主要 UX に直撃するものとして扱ふ。
Gekanator は admin-only かつ非 admin には 404。 | issue | title | status | area | type |
| --- | --- | --- | --- | --- |
| #360 | 上映会で上位タグを持つタグが表示されないバグ | status/ready | area/backend, area/frontend | type/bug |
| #356 | 天保暦対応 | status/ready | area/frontend | type/enhancement |
| #353 | 局所記載 (#351) | | | |
| #344 | 今後の課題整理 | status/ready | | type/task |
| #334 | posts.thumbnail_base を後からでも変更できるやぅにする | status/ready | area/backend, area/frontend | type/enhancement |
| #332 | 上位タグの循環を登録できないやぅにする | status/ready | area/backend | type/bug |
| #306 | 素材管理(#99 の続き) | status/ready | area/backend, area/frontend | type/enhancement |
| #170 | 別の投稿からタグ・インポート | status/ready | area/backend, area/frontend | type/enhancement |
質問: ## 仕様へ反映した主な issue
1. 今後も完全 admin ツールか。 | issue | 仕様上の扱ひ |
2. member へ開放する予定はあるか。 | --- | --- |
3. 一般ユーザ向けゲームとして公表する可能性はあるか。 | #361 グカネータ公開 | Gekanator は恒久 admin-only ではなく、公表予定機能として定義した |
| #360 上映会で上位タグを持つタグが表示されないバグ | 上映会のタグ表示は親タグ/継承タグを含むべき既知バグとして記載した |
| #356 天保暦対応 | 日付表示/旧暦対応の未実装拡張として課題一覧に反映した |
| #354 `post_versions.tags_json` | 投稿履歴タグ snapshot の構造化予定として記載した |
| #353/#351 局所記載 | 投稿タグに対する sections 構造の未完機能として扱ふ |
| #337 Wiki 履歴 | Wiki 履歴は `wiki_versions` を根拠にする方針として課題一覧に反映した |
| #336 Wiki 本文検索 | Wiki 検索仕様の既知バグとして扱ふ |
| #334 `posts.thumbnail_base` 変更 | 投稿更新対象の拡張課題として扱ふ |
| #332 タグ親子循環禁止 | タグ親子 member 開放前の必須制約として記載した |
| #306 素材管理 | `material_versions` を必要機能として確定した根拠に含めた |
| #301 備考欄 | `post_remarks`, `post_remark_versions` の将来拡張として扱ふ |
| #227 限定公開 | 投稿閲覧権限モデルの将来拡張として扱ふ |
| #123 同定文字 | タグ正規化/検索同定の将来仕様として扱ふ |
## H-008: Gekanator AI 変換 ## 全 issue 一覧
AI converter は未実装。 | issue | kind | title | priority | status | type | area | milestone | due |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| #361 | Issue | 【おたのしみ】グカネータ公開 | `P3` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #360 | Issue | 上映会で上位タグを持つタグが表示されないバグ | `P1` | `status/ready` | `type/bug` | area/backend, area/frontend | | |
| #356 | Issue | 天保暦対応 | `P1` | `status/ready` | `type/enhancement` | area/frontend | | |
| #354 | Issue | `post_versions.tags_json` の作成 → `post_versions.tags` を廃止 | `P2` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #353 | PR | 局所記載 (#351) | `P1` | `` | `` | | | |
| #352 | Issue | サムネつきで手動投稿した際にエラーとなる | `P2` | `status/ready` | `type/bug` | area/backend | | |
| #351 | Issue | 局所記載のしくみ作り | `` | `status/review` | `type/enhancement` | area/backend, area/frontend | | |
| #344 | Issue | 今後の課題整理 | `P1` | `status/ready` | `type/task` | | | |
| #337 | Issue | Wiki 履歴,wiki_versions を根拠にする | `P2` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #336 | Issue | Wiki 検索で本文が検索できないバグ | `P2` | `status/ready` | `type/bug` | area/backend | | |
| #335 | Issue | API の直発行をやめる | `P2` | `status/ready` | `type/task` | area/frontend | | |
| #334 | Issue | posts.thumbnail_base を後からでも変更できるやぅにする | `P1` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #332 | Issue | 上位タグの循環を登録できないやぅにする | `P1` | `status/ready` | `type/bug` | area/backend | | |
| #322 | Issue | ニジラー情報の履歴管理 | `P2` | `status/ready` | `type/enhancement` | area/backend | | |
| #320 | Issue | ニコニコ連携画面の TextArea に対するタグ補完 | `P2` | `status/ready` | `type/enhancement` | area/frontend | | |
| #306 | Issue | 素材管理(#99 の続き) | `P1` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #301 | Issue | 備考欄の作成 | `P2` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #291 | Issue | “閲覧済” を押しても反映されないバグ | `P2` | `status/ready` | `type/bug` | area/frontend | | |
| #285 | Issue | モデルに対する Spec | `P3` | `status/ready` | `type/task` | area/backend | | |
| #283 | Issue | `deerjikists.tag_id` に外部キー制約 | `P2` | `status/ready` | `type/bug` | area/backend | | |
| #279 | Issue | 検索のワイルドカード | `P2` | `status/in-progress` | `type/enhancement` | area/backend | | |
| #273 | Issue | タグ補完コンポーネント共通化 | `P2` | `status/ready` | `type/task` | area/frontend | | |
| #272 | Issue | タグ補完,前方検索を優先表示するが,後方検索も行ふやぅにする(末尾に追記) | `P2` | `status/ready` | `type/enhancement` | area/backend | | |
| #270 | Issue | 個人用メモを作成可能に | `P2` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #268 | Issue | タグのドラッグがスマホのスクロールと干渉する問題 | `P2` | `status/ready` | `type/bug` | area/frontend | | |
| #266 | Issue | Wiki 新規作成直後に内容表示されないバグ対応 | `P2` | `status/ready` | `type/bug` | area/frontend | | |
| #242 | Issue | `tags.post_count` にインデクス | `P2` | `status/ready` | `type/task` | area/backend | | |
| #235 | Issue | フロントのビルド軽量化 | `P2` | `status/ready` | `type/task` | area/frontend | | |
| #227 | Issue | 限定公開用のしくみ作り | `P3` | `status/ready` | `type/enhancement` | area/backend | | |
| #225 | Issue | GET /tag_names/name/:name により,タグや Wiki を含む情報を返す | `P2` | `status/ready` | `type/enhancement` | area/backend | | |
| #221 | Issue | Wiki 排他 | `P2` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #213 | Issue | 広場追加の自動チェックボックス,ボタンにして押したタイミングでの取得にする | `P2` | `status/ready` | `type/enhancement` | area/frontend | | |
| #205 | Issue | 申請フォーム | `P2` | `status/ready` | `type/enhancement` | area/backend | | |
| #172 | Issue | 上位タグ設定画面 | `P3` | `status/ready` | `type/enhancement` | area/backend, area/frontend | | |
| #170 | Issue | 別の投稿からタグ・インポート | `P1` | `status/ready` | `type/enhancement` | area/backend, area/frontend | 一般公開 | |
| #164 | Issue | 【運用】bot 操作タグの投稿を 0 件までに減らす | `P3` | `status/blocked` | `type/task` | | 一般公開 | 2026-06-30 |
| #163 | Issue | 【運用】bot 操作タグの投稿を 100 件までに減らす | `P3` | `status/blocked` | `type/task` | | | 2026-06-15 |
| #162 | Issue | 【運用】bot 操作タグの投稿を 200 件までに減らす | `P3` | `status/blocked` | `type/task` | | | 2026-05-31 |
| #161 | Issue | 【運用】bot 操作タグの投稿を 300 件までに減らす | `P3` | `status/blocked` | `type/task` | | | 2026-05-15 |
| #160 | Issue | 【運用】bot 操作タグの投稿を 400 件までに減らす | `P3` | `status/blocked` | `type/task` | | | 2026-04-30 |
| #159 | Issue | 【運用】bot 操作タグの投稿を 500 件までに減らす | `P3` | `status/blocked` | `type/task` | | | 2026-04-15 |
| #158 | Issue | 【運用】bot 操作タグの投稿を 600 件までに減らす | `P3` | `status/blocked` | `type/task` | | | 2026-03-31 |
| #152 | Issue | Pixiv の埋込み | `P2` | `status/ready` | `type/enhancement` | area/frontend | | |
| #151 | Issue | ニジカ投稿局の埋込み | `P2` | `status/ready` | `type/enhancement` | area/frontend | | |
| #150 | Issue | bilibili の埋込み | `P2` | `status/ready` | `type/enhancement` | area/frontend | | |
| #149 | Issue | Tiktok の埋込み | `P2` | `status/ready` | `type/enhancement` | area/frontend | | |
| #147 | Issue | コントローラをサービスに分離 | `P3` | `status/ready` | `type/task` | area/backend | | |
| #138 | Issue | bot 操作タグのみに限定し,自動で楽曲に関するタグを付与するバッチ作成 | `P3` | `status/ready` | `type/task` | area/backend | | |
| #123 | Issue | 同定文字の制定 | `P3` | `status/ready` | `type/task` | | 一般公開 | |
| #122 | Issue | 【運用】bot 操作タグの投稿を 700 件までに減らす | `P3` | `status/blocked` | `type/task` | | | 2026-03-15 |
質問:
1. 使用モデル/API は何を想定してゐるか。
2. 月 450 円/1 run 5 円という予算は確定か仮置きか。
3. AI は質問分類だけするのか、既存投稿への回答補完もするのか。
## H-009: 上映会 host 権限
host は active watching user から自動選出される。
質問:
1. 公表後も誰でも host になれる仕様でよいか。
2. theatre ごとに host 候補を member/admin に限定するか。
3. skip 投票と next_post の権限を分離するか。
## H-010: Preview API の制限方針
Preview API は危険度が高い。
質問:
1. preview は member 以上に制限してよいか。
2. guest に許すなら rate limit はどこで実装するか。
3. SSRF 対策として private IP 拒否・redirect 検査・content length 上限は必須でよいか。
## H-011: 課題一覧の取得方法
指定された Gitea API は、この実行環境では取得に失敗した。
質問:
1. issue JSON をファイルとして渡す運用にするか。
2. read:issue token 付きの安全な取得方法を用意するか。
3. ChatGPT へ渡すべき issue は全件か、open + P0/P1 のみか。
# 次回更新方針 # 次回更新方針
1. 上記ヒアリング回答を反映する。 1. 本書を実装作業用に分解し、P0/P1 から Codex 向け issue prompt を作る。
2. Gitea issue 一覧を取得でき次第、仕様上の未実装/既知不具合/優先度と照合する 2. `POST /materials` 権限修正、`PostVersionsController` の `parent_posts` 返却、Preview API 防御を先に潰す
3. RSpec/Vitest/build/lint 実行結果を追記し、仕様ではなく検証報告として分離する。 3. RSpec/Vitest/build/lint 実行し、仕様ではなく検証報告として別章へ追記する。
4. API レスポンス例を主要 endpoint ごとに追加する。 4. API レスポンス例を主要 endpoint ごとに追加する。
5. DB ER 図またはテーブル関係図を別紙化する。 5. DB ER 図またはテーブル関係図を別紙化する。
6. Gitea issue の次回添付または export script により、仕様書を腐らせない運用を決める。
# 外部参照
Gekanator AI モデル選定では、2026-06-11 時点の OpenAI 公式ドキュメントを参照した。モデル ID は変動し得るため、仕様では `GEKANATOR_AI_MODEL` による差し替へを必須とする。
- OpenAI Models: https://developers.openai.com/api/docs/models
- OpenAI Structured Outputs: https://developers.openai.com/api/docs/guides/structured-outputs
- OpenAI API Pricing: https://openai.com/api/pricing/