8
実装説明書
みてるぞ が 2026-06-11 21:06:06 +09:00 にこのページを編輯
title, subtitle, date, lang, toc, toc-depth, numbersections
title subtitle date lang toc toc-depth numbersections
BTRC Hub / タグ広場 現行実装仕様書 ソース / Wiki / 旧本番DB / 開発者ヒアリング / Gitea 課題一覧 反映版 2026-06-11 ja-JP true 3 true

本書の位置づけ

本書は、添付された 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 時点のタグ広場の実装事実と確定意図を再構成した仕様書である。

本書は願望ではなく、まずコード・スキーマ・画面・テストに現れてゐる事実を書く。その上で、開発者ヒアリング回答を仕様決定として本文へ反映する。実装が仕様決定に追いついてゐない箇所は 仕様決定済み・実装未反映 として明示する。

確認対象

区分 確認対象
バックエンド Rails 8 API、models、controllers、services、representations、routes、schema、RSpec
フロントエンド React/Vite/TypeScript、routes、pages、components、lib、types、Vitest
Wiki 開発 Wiki、テーブル定義書、環境構築手順
DB 2026-04-25 旧本番 DB SQL ダンプ。現行 schema との差分あり
既存仕様 2026-05-10 版仕様書
課題一覧 添付 Gitea issue JSON 50 件。全件 open。P1/P2/P3・status・area・type を集計して反映

表記ルール

表記 意味
現行仕様 ソース・スキーマ・画面・テストで確認できる仕様
実装あり・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 件を反映する 反映済み

この表と本文が衝突する場合、本文の「確定仕様」節を優先する。現行実装との差分は、修正対象であって仕様の揺れではない。

用語: 公開と公表

現行のタグ広場は URL として到達可能であり、実質的にはすでに 公開 状態である。一方、界隈へ明示的に告知し、利用導線を整へ、人を呼び込む状態を 公表 と呼ぶ。

したがって「一般公開前」という古い表現は、本書では原則として 公表前 と読み替へる。

システム概要

目的

BTRC Hub / タグ広場は、ぼざろクリーチャー関連コンテンツへのリンクを収集し、タグ・Wiki・素材・上映会・推測ゲームを通じて、作品群と関連知識を整理・再発見しやすくする共同編集型基盤である。

開発 Wiki の Home では、目的が次のやぅに整理されてゐる。

  • ぼざろクリーチャーシリーズ関連のあらゆるコンテンツへのリンクを保持する。
  • 各リンクにタグを付け、検索しやすくする。
  • ニコニコのタグ数制限への対応。
  • プラットフォームを超越し、あらゆるユーザによってぼざクリを一元管理できるやぅにする。
  • コンテンツ本体ではなくリンクを保持する。
  • SNS 性はなるべく排除する。

中核ドメイン

ドメイン 内容
投稿 外部 URL によるリンクデータ。タグ・サムネ・親子関係・閲覧済み状態・類似投稿を持つ
タグ カテゴリ、別名、親タグ、外部 Nico タグ連携、ニジラー紐づけ、素材紐づけを持つ分類単位
Wiki タグ名と結びつく説明ページ。行単位ストアと改訂履歴を持つ
素材 キャラクター/素材タグに紐づくファイルまたは URL
上映会 投稿を共同視聴し、在席・ホスト・コメント・番組表・スキップ投票を扱ふ
Gekanator 管理者向けの「投稿当て」推測ゲーム兼質問学習機構

非目的

現行実装は次を主目的にしてゐない。

  • 汎用 SNS。
  • 雑談掲示板。
  • 外部コンテンツ本体の転載保存。
  • 本格的な動画配信基盤。
  • メール/パスワード式の通常アカウント管理。
  • 誰でも自由に高度編集できる完全オープン Wiki。

ただし、上映会コメント・素材投稿・Gekanator 学習など、単なるリンク集より機能はかなり重い。仕様境界を曖昧にすると、SNS 化・荒らし対応・権限崩壊が一気に来る。ここは甘く見ないこと。

技術構成

バックエンド

項目 現行
言語 Ruby
フレームワーク Rails ~> 8.0.2 API
DB MySQL 8 系想定。Gemfile には sqlite3 も残る
ファイル Active Storage。S3/R2 互換想定あり
画像処理 image_processing / MiniMagick
HTML 解析 Nokogiri
Wiki 移行/旧資産 gollum / gollum-lib
差分 diff-lcs
テスト RSpec
BAN/soft delete discard
i18n rails-i18n

フロントエンド

項目 現行
UI React 19.1 + Vite 6.3
言語 TypeScript 5.8
通信 Axios。レスポンスは camelcase-keys で deep camelCase 化
状態/取得 TanStack Query、localStorage、必要箇所で state
スタイル Tailwind CSS、shadcn 風ローカル UI、Framer Motion
Markdown react-markdown、react-markdown-editor-lite、remark-gfm、wiki autolink
テスト Vitest、Testing Library、jsdom

検証コマンド

リポジトリ文書上の推奨コマンドは次である。

cd backend && bundle exec rspec
cd frontend && npm run test:run && npm run build && npm run lint

ルーティング概要

バックエンド API

主な API は次である。

領域 代表エンドポイント
投稿 GET /posts, GET /posts/:id, POST /posts, PUT/PATCH /posts/:id, GET /posts/random, GET /posts/versions, POST/DELETE /posts/:id/viewed
タグ GET /tags, GET /tags/:id, PUT/PATCH /tags/:id, GET /tags/autocomplete, GET /tags/with-depth, GET /tags/versions
Nico タグ GET /tags/nico, PUT /tags/nico/:id
タグ親子 POST /tags/:parent_id/children/:child_id, DELETE /tags/:parent_id/children/:child_id
Wiki GET/POST /wiki, GET/PUT /wiki/:id, GET /wiki/search, GET /wiki/changes, GET /wiki/:id/diff, GET /wiki/title/:title
素材 GET/POST /materials, GET/PUT/DELETE /materials/:id
ニジラー紐づけ GET/PUT/DELETE /deerjikists/:platform/:code
プレビュー GET /preview/title, GET /preview/thumbnail
ユーザ POST /users, POST /users/verify, POST /users/code/renew, GET /users/me, PUT/PATCH /users/:id
上映会 GET /theatres/:id, PUT /watching, PATCH /next_post, PUT/DELETE /skip_vote, GET /post_selection_weights, comments/programmes/skip_events
Gekanator GET /gekanator/posts, GET /gekanator/questions, POST /gekanator/games, POST /gekanator/question_suggestions, POST /ai_convert

フロントエンド画面

パス 画面
/posts 投稿一覧
/posts/new 投稿作成
/posts/search 投稿検索
/posts/:id 投稿詳細/編集
/posts/changes 投稿履歴
/tags タグ一覧
/tags/:id タグ詳細/編集
/tags/:id/deerjikists ニジラー紐づけ
/tags/nico, /nico/tags Nico タグ一覧/連携編集
/tags/changes タグ履歴
/wiki Wiki 検索
/wiki/:title Wiki 表示
/wiki/new Wiki 新規作成
/wiki/:id/edit Wiki 編集
/wiki/:id/diff Wiki 差分
/wiki/changes Wiki 履歴
/materials 素材一覧
/materials/new 素材作成
/materials/:id 素材詳細
/theatres/:id 上映会
/gekanator 管理者専用 Gekanator
/users/settings, /settings ユーザ設定
/tos 利用規約
/more その他

認証・ユーザ・BAN

認証方式

通常のログインではなく、引継ぎコードによる軽量認証である。

  • users.inheritance_code が認証トークン。
  • フロントは localStorage.user_code に保存。
  • API 呼び出し時、X-Transfer-Code ヘッダに付与。
  • バックエンドは ApplicationController#authenticate_usercurrent_user を設定する。

初回利用フロー

フロント起動時に次を行ふ。

  1. localStorage.user_code があれば POST /users/verify
  2. 有効なら返却ユーザを採用。
  3. 無効またはコードなしなら POST /users で guest ユーザを作成。
  4. 新規作成された inheritance_code を localStorage へ保存。

確定仕様: guest 自動作成

初回閲覧で guest user を作る仕様は継続する。理由は、仕様分岐を増やさず単純に保つためである。

ただし、旧本番 DB で users = 42805 件あるため、bot または cookie/localStorage を保持しないアクセスにより users が肥大化してゐる可能性は高い。これは仕様として許容されたわけではなく、未決定の運用リスクである。

現時点では次を推奨仕様とする。

方針
フロント localStorage が使へない環境では編集系 UI を出さず、POST /users を連打しない
API POST /users に IP 単位・UA 単位の軽い rate limit を置く
DB userslast_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 意味
guest 自動生成される通常閲覧者。閲覧、閲覧済み、上映会在席、コメント、skip 投票など軽い参加行為の主体
member 投稿・タグ・Wiki・素材等の編集者
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

ApplicationControllerbefore_action は次の順序で動く。

  1. reject_banned_ip_address!
  2. authenticate_user
  3. reject_banned_user!

したがって BAN は全 API にかかる。

対象 判定 応答
IP BAN ip_addresses.banned_at が存在 403
User BAN users.banned_at が存在 403

IP は IPAddr.new(request.remote_ip).hton により binary 化して ip_addresses.ip_address に保存する。

注意

users.inheritance_code はモデル上必須・64文字以内だが、現行 schema では一意 index が見当たらない。UUID なので衝突可能性は低いが、認証トークンに DB 一意制約がないのは設計として弱い。DB 一意 index を追加すべきである。

エラー応答

バリデーションエラー

ApplicationController#render_validation_error 系は概ね次の JSON を返す。

{
  "type": "validation_error",
  "message": "入力内容を確認してください.",
  "errors": {},
  "base_errors": []
}

フロントは camelcase-keys により baseErrors などへ変換して扱ふ。

競合

投稿更新では version based optimistic concurrency があり、競合時は 409 を返す。

主なフィールド:

  • error: 'conflict'
  • message
  • post_id
  • base_version_no
  • current_version_no
  • base
  • current
  • mine
  • changes
  • conflicts
  • mergeable

投稿仕様

投稿モデル

posts は外部 URL を中心とするリンク記録である。

属性 仕様
url 必須・一意・HTTP/HTTPS のみ
title NULL 可
thumbnail_base 外部サムネイル URL。長さ 2000
thumbnail Active Storage 添付
uploaded_user_id 投稿者。同期投稿では NULL 可
original_created_from 元コンテンツ作成日時の下限
original_created_before 元コンテンツ作成日時の上限
version_no 投稿内の現行版番号。1 以上

URL 正規化

保存前に次を行ふ。

  • 前後空白の除去。
  • URI として parse できる場合、host を小文字化。
  • path 末尾の / を除去。
  • HTTP/HTTPS 以外は不正。

元コンテンツ日時

original_created_fromoriginal_created_before の両方がある場合、from < before が必須である。

同期系では動画公開時刻を from = 公開時刻の秒切捨て, before = from + 1分 として持つ。

投稿作成

POST /postsmember 以上が必要。

主要入力:

入力 仕様
title 任意
url 必須
thumbnail 任意。添付時は 180x180 JPEG へ変換
thumbnail_base 任意
tags 空白区切りタグ文字列
parent_post_ids 必須。空でも送る必要あり
original_created_from / before 任意

タグは Tag.normalise_tags! で正規化され、別名解決、カテゴリ prefix 解釈、タグ希望/ニジラー不詳の自動付与、親タグ展開が行はれる。

投稿更新

PUT/PATCH /posts/:idmember 以上が必要。

現行では version based optimistic concurrency が実装されてゐる。

パラメータ 仕様
base_version_no force でない限り必須。正整数
force 強制上書き
merge 競合がなければ自動マージ

forcemerge の同時指定は禁止。

競合判定対象:

  • title
  • original_created_from
  • original_created_before
  • tag_names
  • parent_post_ids

スカラー値は、base から current と mine が別々に変更され、かつ値が違ふ場合に競合する。集合値は、同じ要素について片方が追加し片方が削除してゐる場合に競合する。

merge 可能なら、現在状態と自分の変更を統合する。競合があれば 409。

タグ処理

投稿タグは post_tags に保持される。

  • 物理削除ではなく discarded_at による論理削除。
  • 同一投稿・同一タグの active 重複は禁止。
  • PostTag#destroy は読み取り専用例外を投げる。
  • discard_by! により discarded_atdeleted_user を設定し、tags.post_count を減算する。

投稿に手動で nico: タグを入れることは通常禁止される。Nico タグは同期/連携経由で扱ふ。

親子投稿

post_implications は投稿間の多対多親子関係である。

カラム 意味
post_id 子投稿
parent_post_id 親投稿

制約:

  • 複合主キー。
  • 自己親は禁止。
  • 存在しない親 ID はエラー。

フロントの PostList は親子投稿の存在に応じてカード枠を変へる。

関連投稿

post_similarities は、投稿同士の類似度を保持する。

  • Similarity::Calc.call(Post, :tags) により生成。
  • 各投稿につき上位 20 件を保存。
  • 類似度はタグ集合の cosine similarity。
  • Post#related(limit:)cos DESC で取得する。

投稿一覧検索

GET /posts は次の絞り込みを持つ。

パラメータ 仕様
url URL 部分一致
title タイトル部分一致
tags 空白区切りタグ。別名 canonicalise あり
match all または any
not: prefix 除外タグ指定
original_created_from/to 元作成日時範囲
created_from/to 作成日時範囲
updated_from/to 更新日時範囲。タグ更新も考慮
order title, url, original_created_at, created_at, updated_at + asc/desc
page, limit 1 未満は 1 へ補正

updated_at sort は、投稿本体の updated_atpost_tags.updated_at の最大値を使ふ。

投稿履歴

post_versions は immutable snapshot である。

属性 内容
post_id 対象投稿
version_no 投稿内連番
event_type create, update, discard, restore
title, url, thumbnail_base 投稿本体
tags タグ名の空白区切り snapshot。将来 tags_json へ移行予定
parent_post_ids 親投稿 ID の空白区切り snapshot
original_created_from/before 元日時範囲
created_by_user_id 操作者

VersionRecorder は次を保証する。

  • 永続化済み version は readonly。
  • 初回イベントは create でなければならない。
  • 同一 snapshot の update は版を増やさない。
  • 作成後、対象 record の version_no を更新する。

確定仕様: 親投稿変更履歴

投稿履歴画面では、親投稿の追加・削除を必ず表示する。現行 API が返してゐないのはバグである。

GET /posts/versions は各 version に少なくとも次を返すべきである。

parentPosts: Array<{
  id: number
  title: string | null
  historicalTitle?: string | null
}>

API の snake_case 原型は次を想定する。

{
  "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#indexparent_post_ids を select/serialize しておらず、parentPosts を返してゐない。これは仕様決定済みの実装バグである。

タグ仕様

タグ名とタグ実体

タグは tag_namestags に分離される。

テーブル 役割
tag_names 名前文字列、canonical/alias 関係、Wiki ページとの結合点
tags カテゴリ、投稿件数、実体 ID、version_no

Tag#nametag_name.name の delegate である。

タグカテゴリ

category 表示/用途
deerjikist ニジラー
meme 原作・ネタ元・ミーム等
character キャラクター
general 一般
material 素材
meta メタタグ
nico ニコニコタグ

nico タグは名前が必ず nico: で始まる必要があり、非 nico タグが nico: で始まることも禁止される。

システムタグ

Tag には次の作成ヘルパがある。

メソッド タグ名 用途
Tag.tagme タグ希望 タグ不足の印
Tag.bot bot操作 bot/sync 由来の印
Tag.no_deerjikist ニジラー情報不詳 ニジラー不明
Tag.video 動画 動画投稿
Tag.niconico ニコニコ ニコニコ由来
Tag.youtube YouTube YouTube 由来

タグ希望, bot操作, ニジラー情報不詳, 動画, ニコニコ は名称変更が明示的に禁止される。

タグ正規化

Tag.normalise_tags! の仕様:

  • 入力はタグ名配列。
  • 空白・空文字を除去。
  • カテゴリ prefix を解釈する。
  • TagName.canonicalise により別名を正規名へ解決する。
  • 存在しない tag_name/tag は作成する。
  • with_tagme が true かつタグ数が 10 未満で タグ希望 がなければ追加。
  • with_no_deerjikist が true かつ deerjikist タグがなければ ニジラー情報不詳 を追加。
  • deny_nico が true なら nico: prefix はエラー。

対応 prefix:

prefix category
general:, gen: general
deerjikist:, djk: deerjikist
meme: meme
character:, chr: character
material:, mtr: material
meta: meta

別名

tag_names.canonical_id により別名を表現する。

  • canonical_id = NULL: 正規名。
  • canonical_id != NULL: 別名。
  • 別名の参照先は正規名でなければならない。
  • 別名名に : は含められない。
  • タグまたは Wiki ページを持つ tag_name は別名化できない。

タグ詳細の full update では、名称変更時に旧名が aliases に追加される。

タグ親子

tag_implications は子タグから親タグへの関係を表す。

カラム 意味
tag_id 子タグ
parent_tag_id 親タグ
  • 同一組合せは一意。
  • 自己親は禁止。
  • 投稿側では、付与タグの親タグが再帰的に展開される。

確定仕様: タグ親子編集権限

タグ親子関係の編集は member 以上 が可能である。

UI 仕様:

ユーザ 親タグ欄の表示 変更
guest 表示する 不可
member 表示する
admin 表示する

現行の D&D 操作が admin 限定だった理由は、履歴管理がなかった時期に誤操作を避けるための安全策である。現在は履歴管理があるため、admin 限定を続ける必然性は薄い。

実装差分

TagChildrenController の親子追加/削除 API は admin 限定である。一方、TagsController#update_all は member 以上で parent_tags を更新できる。正しい仕様は member+ なので、専用 API 側の権限を揃へる必要がある。

追加制約

添付 issue #332 により、上位タグの循環登録禁止が P1 バグとして課題化されてゐる。タグ親子編集を member+ に開くなら、循環検出は必須である。

タグ一覧/検索

GET /tags は次を持つ。

パラメータ 仕様
post 投稿 ID で絞り込み
name 名前部分一致
category カテゴリ
post_count_gte/lte 投稿件数範囲
created_from/to 作成日時
updated_from/to 更新日時
order name, category, post_count, created_at, updated_at

カテゴリ順は独自順で、deerjikist, meme, character, general, material, meta, nico の順を使ふ。

タグ autocomplete

GET /tags/autocomplete:

パラメータ 仕様
q 検索語
nico nico タグを含めるか。既定 true
present 投稿件数 > 0 のものに絞るか。既定 true

別名 hit も見て、matched_alias を返す。

タグ履歴

tag_versions は immutable snapshot。

属性 内容
tag_id 対象タグ
version_no タグ内連番
event_type create/update/discard/restore
name タグ名 snapshot
category カテゴリ snapshot
aliases 別名空白区切り snapshot
parent_tag_ids 親タグ ID 空白区切り snapshot
created_by_user_id 操作者

TagVersioning は更新前 snapshot と更新後記録を制御する。

Nico タグ仕様

概要

ニコニコ由来のタグは内部タグとは別性質である。

  • tags.category = 'nico'
  • tag_names.name は必ず nico: で始まる。
  • 通常の手動入力では nico: は拒否される。
  • 内部タグとの連携は nico_tag_relations で行ふ。

Nico タグ連携

nico_tag_relations:

カラム 意味
nico_tag_id nico カテゴリタグ
tag_id 内部タグ

NicoTagsController#update は member 以上が利用可能。

  • 対象 tag は nico でなければならない。
  • 連携先は通常タグで、nico から nico への連携は禁止。
  • 連携先タグも version snapshot される。
  • nico_tag_versions へ連携状態が記録される。

Nico 同期

backend/lib/tasks/sync_nico.rake は外部 NIZIKA_NICO_PATH の Python スクリプトから動画情報を取得する。

処理概要:

  1. 動画 code/title/uploaded_at/tags を取得。
  2. 既存投稿を nicovideo.jp/watch/<code> で検索。
  3. なければ投稿作成。
  4. サムネイルを HTML meta から取得して Active Storage 添付。
  5. タグ希望, bot操作, ニコニコ, 動画 を付与。
  6. ニコニコの生タグを nico:<raw> として nico タグ化。
  7. Nico タグに連携された内部タグも付与。
  8. 投稿版/Nico タグ版を記録。

Nico 逆連携

backend/lib/tasks/export_nico.rake は、タグ広場上のニコニコ動画 ID を外部 tracked_videos.put_bulk_upsert へ渡す。

YouTube 同期仕様

backend/lib/tasks/sync_posts.rakeYoutube::Sync.new.sync! を呼ぶ。

検出対象

  • 検索語:
    • ぼざろクリーチャーシリーズ
    • 伊地知ニジカ
    • 伊地知虹鹿
  • プレイリスト ID 3 件。
  • 検索対象は直近 14 日。

同期処理

  1. YouTube Data API で動画 ID を取得。
  2. videos API で snippet/status/contentDetails を取得。
  3. youtube.com/watch?v=<id> または youtu.be/<id> として既存投稿検索。
  4. なければ投稿作成。
  5. サムネイルを添付。
  6. 新規時 タグ希望, bot操作, YouTube, 動画 を付与。
  7. チャンネル ID が deerjikists に登録済みなら該当 deerjikist タグを付与。
  8. 未登録かつ deerjikist タグなしなら ニジラー情報不詳 を付与。
  9. 投稿版を記録。

ニジラー紐づけ仕様

deerjikists

deerjikists は外部プラットフォーム上のユーザ/チャンネルとタグを紐づける。

カラム 意味
platform nico または youtube
code 外部 ID
tag_id deerjikist カテゴリのタグ

複合主キーは (platform, code)

Deerjikist モデルは紐づけ先タグが deerjikist カテゴリであることを要求する。

API

API 権限 内容
GET /deerjikists/:platform/:code public 紐づけ取得
PUT /deerjikists/:platform/:code member+ 紐づけ作成/更新
DELETE /deerjikists/:platform/:code member+ 紐づけ削除
PUT /tags/:id/deerjikists member+ タグ側から複数紐づけ更新

YouTube では @handle が渡された場合、YouTube ページを取得して UC... channel ID へ正規化する処理がある。

注意

YouTube handle 正規化は外部 HTML 取得に依存してゐる。失敗時は nil になり得るため、UI 側のエラー誘導が重要。

Wiki 仕様

Wiki ページとタグ名

wiki_pagestag_name_id と一対一に紐づく。

  • Wiki のタイトルは tag_name.name
  • タグ実体ではなく tag_name に紐づくため、タグがなくても Wiki ページは存在し得る。
  • タイトル変更時は tag_name を変更する。

現行ストレージ

Wiki は本文を wiki_pages.body に持つだけではない。現行は行単位の改訂履歴を持つ。

テーブル 役割
wiki_pages ページ本体/現在情報
wiki_revisions 改訂ヘッダ。kind/content/redirect、tree_sha 等
wiki_lines 行本文を SHA256 で重複排除
wiki_revision_lines 改訂と行の順序
wiki_versions ページ title/body の version snapshot

作成/更新

Wiki 作成/更新は member 以上。

Wiki::Commit.create_content! は次を行ふ。

  • CRLF を LF へ統一。
  • 不正 UTF-8 を置換文字で救済。
  • 末尾改行を strip。
  • 行ごとに SHA256 を計算。
  • 未登録行だけ wiki_lines に upsert。
  • wiki_revision_lines に行順を保存。
  • wiki_versions を記録。
  • base_revision_id が渡された場合、現在 revision と一致しなければ conflict。

差分

GET /wiki/:id/diffdiff-lcs による行差分を返す。

返却 type:

  • context
  • added
  • removed

content revision 同士のみ差分対象。

redirect

Wiki::Commit.redirect! は現行では raise '廃止しました.' で無効化されてゐる。ただし schema と controller には redirect revision の痕跡がある。

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 は将来機能として正当化されたが、実装は未完である。

素材仕様

Material

materials はタグに紐づく素材である。

属性 仕様
tag_id 必須・一意
url 任意。file がなければ必須
file Active Storage 添付。url がなければ必須
parent_id 親素材。任意だが廃止予定
created_by_user_id 作成者
updated_by_user_id 更新者
discarded_at 論理削除

MaterialMyDiscard ではなく独自に default_scope -> { kept } を持つ。

タグ制約

素材に紐づくタグは character または material カテゴリでなければならない。

注意: バリデーション文言は「素材カテゴリのタグ」と言ってゐるが、実装上は character も許可してゐる。仕様としては character も素材を持てる で確定し、文言を直すべきである。

API

API 現行権限 確定仕様 内容
GET /materials public public 素材一覧
GET /materials/:id public public 素材詳細。関連 Wiki 本文も返す
POST /materials current_user 必須 member+ 素材作成
PUT/PATCH /materials/:id member+ member+ 素材更新
DELETE /materials/:id member+ member+ 論理削除

確定仕様: 素材作成権限

素材作成は member 以上を原則とする。現行の guest 作成可能状態は設計意図ではなく実装バグとして扱ふ。

ただし、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 返却形:

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 だけでなく履歴まで含めて完了とみなすべきである。

上映会仕様

概要

theatres は共同視聴部屋である。

属性 意味
name 部屋名
opens_at / closes_at 開始/終了
kind 種別
current_post_id 現在再生中投稿
current_post_started_at 再生開始時刻
host_user_id 現在ホスト
next_comment_no コメント採番

在席とホスト

PUT /theatres/:id/watching はログイン済みユーザを在席として更新する。

  • theatre_watching_users.expires_at は 30 秒後。
  • 現在 host がいない、または host が active でなければ、呼び出しユーザが host になる。
  • フロントは約 1.5 秒ごとに watching を送る。

返却:

  • host_flg
  • post_id
  • post_started_at
  • post_elapsed_ms
  • watching_users
  • skip_vote

確定仕様: 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 のみ実行可能。

TheatrePostSelector は次の投稿を候補にする。

  • urlnicovideo.jp, youtube.com/watch, youtu.be を含む投稿。
  • 現在投稿は除外。

重みは 1.0 / (1.0 + penalty)

penalty は、active user が過去に skip した投稿のタグに基づく。つまり、視聴者が嫌がったタグを持つ投稿ほど選ばれにくい。

添付 issue #360 により、上映会で上位タグを持つタグが表示されないバグが P1 として課題化されてゐる。上映会はタグ継承・親タグ展開と強く結びつくため、タグ表示は単純な直接付与タグだけでは不足する。

番組表

theatre_programmes は再生履歴/番組表である。

カラム 意味
theatre_id 部屋
position 連番位置
post_id 投稿
created_at 追加時刻

TheatrePostAdvancer は次投稿へ進むたびに position を加算して programme を作る。

コメント

theatre_comments は部屋内コメントである。

  • 主キーは (theatre_id, no)
  • 投稿時に theatre を lock し、next_comment_no を採番する。
  • 削除は投稿者本人のみ。
  • 削除後は discarded_at を設定し、一覧では content: null, deleted: true として返す。
  • フロントは最新 20 件を中心に取得する。

スキップ投票

theatre_skip_votes(theatre_id, post_id, user_id) 複合主キー。

PUT /skip_vote:

  1. ログイン必須。
  2. post_id 必須。
  3. watching を更新。
  4. 現在投稿と request post_id が違へば 409。
  5. vote 作成。
  6. active users の過半数に達したら skip 確定。

必要票数:

required_count = floor(active_watching_users_count / 2) + 1

スキップ確定時:

  • TheatreSkipFinalizertheatre_skip_events を作る。
  • voter 一覧を theatre_skip_event_voters に保存。
  • skip 時点の投稿タグを theatre_skip_event_tags に保存。
  • 当該投稿の skip vote を削除。
  • TheatrePostAdvancer で次投稿へ進む。

DELETE /skip_vote で自分の投票を取り消せる。

再生同期

フロント TheatreDetailPage は server elapsed と player current time の差が 5 秒を超えた場合に seek する。

  • YouTube は react-youtube
  • ニコニコは NicoViewer による JS API postMessage
  • duration が 0 以下など再生不能っぽい場合、現行では host が次投稿へ進める。

埋め込み

PostEmbed の対応:

URL 表示
NicoVideo NicoViewer iframe + postMessage
YouTube react-youtube
Twitter/X status blockquote + widgets script
その他 確認後 iframe

ニコニコ iframe の loadComplete timeout は 8000 ms。

Gekanator 仕様

概要

Gekanator は投稿当てゲームである。投稿群に関する質問を出し、回答から候補投稿を絞り、最終的に投稿を推測する。

用途はゲームだけではない。終了後の質問追加・回答保存により、投稿間類似や識別質問を蓄積する学習機構でもある。

確定仕様: 公開範囲

Gekanator は恒久的な admin-only ツールではない。

段階:

段階 公開範囲 目的
現行 admin のみ 学習データ作成、質問品質確認、パラメータ調整
調整後 member または限定ユーザ 追加学習、UX 確認
公表版 一般ユーザ おたのしみゲームとして公開

公表版では、内部パラメータ・候補スコア・調整用情報は非表示にする。添付 issue #361 により「グカネータ公開」が課題化されてゐる。

現行権限

バックエンド API は current_user&.admin? でなければ 404 を返す。フロントも /gekanator は admin 以外 NotFound を表示する。

これは現行実装であって最終仕様ではない。

投稿カタログ

GET /gekanator/posts は現行 admin 専用。

返却投稿:

  • 全投稿。
  • tags preload。
  • サムネイル付き。
  • 並びは COALESCE(original_created_before - 1分, original_created_from, created_at) DESC, id DESC

質問カタログ

GET /gekanator/questions は現行 admin 専用。

質問 kind:

kind 内容
tag 特定タグを含むか
source URL host/source
title タイトル性質
original_date 年/月/月日
post_similarity 特定投稿との近さ/例示回答

質問 source:

  • user_suggested
  • ai_generated
  • admin_curated

status:

  • pending
  • accepted
  • rejected
  • disabled

現行 API は accepted questions を返す。

回答値

GekanatorQuestionSuggestion::ANSWERS:

意味
yes はい
no いいえ
partial 部分的にそう
probably_no たぶん違ふ
unknown わからない

フロント推測ロジック

frontend/src/lib/gekanator.tsGekanatorPage.tsx による。

主な定数:

定数 意味
questionsBetweenGuesses 25 通常推測までの質問数
minQuestionsBeforeCertainGuess 5 確信時の最低質問数
certainGuessPercent 99.5 ほぼ確定判定
runnerUpMaxPercent 0.5 2位候補の上限
hardMaxQuestions 80 最大質問数
softenedAnswerWeight 0.35 答え緩和重み
confidenceTemperature 6 確率化温度
maxQuestionSuggestionsPerGame 3 追加質問上限

質問選択は、候補分割力、冗長性、排他条件、優先度、seed による決定性を組み合はせる。

候補が潰れる場合、高難度・非 unknown の過去回答を 0.35 に緩和して復旧を試みる。

確定仕様: 質問数ルール

通常は 25 問を経過するまで推測しない。「続けますか → はい」の流れでも 25 問ルールを維持する。

例外は、100% 近似に近い確信状態で、候補が実質的に一意になった場合のみである。ただし、この例外も最低質問数・2 位候補との差・誤答時の UX を見て慎重に扱ふ。

ゲーム保存

POST /gekanator/games は現行 admin 専用。

入力:

  • guessed_post_id
  • correct_post_id
  • answers JSON

保存値:

  • won = guessed_post_id == correct_post_id
  • question_count = answers.length

質問追加

終了後、ユーザは質問を最大 3 件追加できる。

POST /gekanator/question_suggestions:

  • 現行 admin 専用。
  • game_id
  • question_text 1000 文字以内。
  • answer は enum。
  • unknown は promoter で質問化されない。

Gekanator::QuestionSuggestionPromoter は unknown 以外を accepted な post_similarity question として即昇格させ、correct_post に対する example を作る。

追加質問回答

GET /gekanator/games/:id/extra_questions は、ゲーム後に correct_post へ未回答の post_similarity question を最大 2 件返す。

POST /extra_question_answers は、それらへの回答を GekanatorQuestionExample として保存する。

確定仕様: AI 変換

POST /gekanator/question_suggestions/:id/ai_convert は存在するが、Gekanator::QuestionSuggestionAiConverter は現行 NotImplementedError を投げる。

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 予算:

項目
月上限 450 円
1 run 見積 5 円
超過見込み 402 blocked_budget

この見積は仮置きであり、確定予算ではない。現在の希望は「半年で 500 円くらゐ」であるため、1 run 5 円の設計は高すぎる。AI 補完は per game 即時実行ではなく、batch 化・cache 化・差分実行に寄せるべきである。

プレビュー API

タイトル取得

GET /preview/title:

  • current_user 必須。
  • URL 必須。
  • scheme がなければ http:// を補完。
  • URI.open で HTML 取得。
  • Nokogiri で <title> を返す。
  • open/read timeout は 5 秒。

サムネイル生成

GET /preview/thumbnail:

  • current_user 必須。
  • URL 必須。
  • scheme がなければ http:// を補完。
  • node lib/screenshot.js <url> <path> を実行。
  • 生成画像を MiniMagick で 180x180 に resize。
  • PNG inline で返す。

確定仕様: セキュリティ方針

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/内部ネットワーク到達/リソース消費のリスクがある。これは公表前の赤信号である。

類似度計算

Similarity::Calc は汎用化されてゐる。

task 対象 関係
post_similarity:calc Post tags による投稿類似
tag_similarity:calc Tag posts によるタグ類似

計算仕様:

  • 対象集合 ID を sort。
  • 2集合の intersection size を計算。
  • cos = intersection / sqrt(|a| * |b|)
  • 各 record につき上位 20 件を保存。
  • similarity table は全削除後に insert_all。

データベース仕様

現行 schema の主要テーブル

2026-06-10 migration まで反映された schema では、主に次のテーブルが存在する。

領域 テーブル
Active Storage active_storage_blobs, active_storage_attachments, active_storage_variant_records
ユーザ/BAN users, ip_addresses, user_ips, settings, user_post_views
投稿 posts, post_tags, post_implications, post_versions, post_similarities
タグ tags, tag_names, tag_implications, tag_versions, tag_similarities, tag_name_sanitisation_rules
Nico nico_tag_relations, nico_tag_versions
ニジラー deerjikists
Wiki wiki_pages, wiki_revisions, wiki_lines, wiki_revision_lines, wiki_versions, wiki_assets
素材 materials, material_versions
上映会 theatres, theatre_comments, theatre_watching_users, theatre_programmes, theatre_skip_votes, theatre_skip_events, theatre_skip_event_tags, theatre_skip_event_voters
Gekanator gekanator_games, gekanator_questions, gekanator_question_suggestions, gekanator_question_examples, gekanator_ai_runs

旧本番 DB ダンプの利用実態

2026-04-25 ダンプは現行 schema より古い。Gekanator、上映会スキップ、投稿親子など一部は含まれてゐない。とはいへ、当時の利用規模を把握するには有用である。

主な行数:

テーブル 行数
users 42805
posts 893
tags 5961
tag_names 5996
post_tags 45345
post_versions 14351
tag_versions 2026
nico_tag_versions 3842
nico_tag_relations 314
deerjikists 99
materials 37
wiki_pages 51
wiki_revisions 197
theatres 1
theatre_comments 98
post_similarities 43140
tag_similarities 116760

解釈:

  • users が極端に多い。自動 guest 生成と bot/巡回アクセスの影響が濃い。
  • post_tags が 4.5 万件あり、投稿あたりタグ量はかなり多い。
  • post_versions が 1.4 万件あり、同期/編集履歴が大量に蓄積されてゐる。
  • 類似度テーブルは posts * 20 / tags * 20 に近い規模で、静的計算済みキャッシュとして動いてゐる。

フロントエンド仕様

API 通信

frontend/src/lib/api.ts:

  • Axios base URL は API_BASE_URL
  • 全リクエストに X-Transfer-Code を付与。
  • JSON レスポンスは deep camelCase 化。
  • responseType: 'blob' の場合は変換しない。

型定義

frontend/src/types.ts は API レスポンスの期待形を定義する。ただし、前述の PostVersion.parentPosts のやぅに、実 API とズレてゐる箇所がある。

編集権限

canEditContentadmin または member を true とする。

画面設計上の特徴

  • App.tsx 初期化時に guest 自動作成。
  • route transition に framer-motion
  • 上部ナビ TopNav
  • MDX 利用規約。
  • Gekanator は admin only route。
  • Post detail は pathname を key にして再 mount。

テスト状況

バックエンド

RSpec が整備されてゐる。確認できる主要テスト領域:

  • users / auth / BAN 系。
  • posts / post implications / versions / conflict。
  • tags / aliases / nico tags / tag children / deerjikists。
  • wiki / commit / diff / conflict / title collision / history integrity。
  • materials。
  • theatres / comments / programmes。
  • Gekanator games / learning。
  • YouTube/Nico sync tasks。
  • similarity calculation。

フロントエンド

Vitest が導入され、かなり広範囲にテストがある。

確認できる主な領域:

  • Post 系 components/pages。
  • Tag 系 components/pages。
  • Wiki pages。
  • Materials pages。
  • Theatre page。
  • Gekanator scoring lib。
  • API utility / error handling。
  • common UI components。
  • NicoViewer / PostEmbed / TwitterEmbed。

2026-05-10 時点の「フロントテストなし」状態からは明確に改善してゐる。

実装上の不整合・危険点

この節は、仕様決定後も実装が追いついてゐない箇所を列挙する。ここを曖昧にしたまま公表すると、設計負債が増える。

P0: Preview API の SSRF/リソース消費リスク

/preview/title/preview/thumbnail はサーバから任意 URL へアクセスする。guest 自動生成により、事実上かなり広い入口である。

仕様としては guest 提供の余地を残すが、次は必須である。

  • private IP / localhost / link-local / metadata IP の拒否。
  • scheme allowlist を http/https に固定。
  • redirect 先検査。
  • content length 上限。
  • screenshot queue/timeout/concurrency 制限。
  • IP + user + host rate limit。

守れないなら機能ごと廃止を検討する。便利さよりサーバ防衛が優先である。

P0: guest 自動作成による users 肥大化

旧 DB で users = 42805。実利用規模に比して異様に多い。bot が来るたび user を発行してゐる可能性が高い。

guest 自動作成は継続するが、以下は未実装リスクとして残る。

  • POST /users rate limit。
  • bot UA / localStorage 不可環境への抑制。
  • 編集実績のない guest 掃除 task。
  • user 分析用の last_seen_at 等。

P0: 素材作成権限が緩い

POST /materials は current_user だけを要求する。guest 自動作成と合はせると、匿名同然で素材作成できる。

確定仕様は member+ 作成である。file 素材は storage を食ふため、ここは早めに閉じるべきである。

P1: タグ親子編集権限が二重化

  • TagChildrenController: admin のみ。
  • TagsController#update_all: member 以上で parent_tags 更新可。

確定仕様は member+ なので、専用 API 側を修正する。あはせて issue #332 の循環登録禁止を入れないと、member 開放は危ない。

P1: post_versions の parent post 履歴 API が不足

schema と snapshot には parent_post_ids があるが、PostVersionsController が返してゐない。フロント型には parentPosts がある。

確定仕様として、親投稿変更履歴は表示必須である。parent_posts の返却を追加する。

P1: material_versions, wiki_assets は必要だが未実装

開発者回答により、どちらも将来機能として必要と確定した。

  • material_versions: tag, URL, file blob, 更新者の履歴。
  • wiki_assets: Wiki 内画像/添付、next_asset_no、sha256 連動。

したがって「残骸として削除」ではなく「仕様化して実装」が正しい。

P1: inheritance_code の一意制約なし

認証トークンである以上、DB 一意 index を張るべき。UUID 衝突は現実的に低くても、仕様としては弱い。

P1: 上映会 host 乗っ取り耐性

active host が切れたら次の watching ユーザが host になる。小規模内輪ではよいが、公表後は荒らしが再生制御を握る可能性がある。

長期方針はサーバ主導進行である。現行 host は暫定制御者と見るべきで、連続 next、短時間 host 交替、再生不能偽装への防御が必要である。

P2: Material のバリデーション文言と許可カテゴリのズレ

文言は「素材カテゴリ」だが、実装は character も許可する。仕様として character も素材を持てるため、文言を直すべきである。

P2: Gekanator AI 変換 API は未実装

API と予算 model はあるが converter は NotImplemented。公表版では質問構成 AI を稼動させる予定なので、未実装表示ではなく実装対象である。

P2: issue 管理との接続が手動

今回の issue 一覧は添付 JSON で取り込んだ。継続的な同期手段は未確定である。全件共有方針は妥当だが、手動添付だけだと仕様書がすぐ腐る。

開発者ヒアリング反映結果

本章は、前版で未確定だった H-001 から H-011 への回答を記録する。回答内容は本文へ反映済みである。

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 課題一覧反映

残る未決定事項

回答後も、次はまだ設計決定が必要である。

項目 未決定内容 推奨
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

Gitea 課題一覧反映

添付された Gitea issue JSON を取り込み、2026-06-11 時点の仕様書へ反映した。今回の添付には 50 件が含まれ、全件 open である。

集計

Priority

priority 件数
P1 8
P2 26
P3 15
優先度なし 1

Status

status 件数
status/blocked 8
status/in-progress 1
status/ready 39
status/review 1
statusなし 1

Area

area 件数
area/backend 26
area/frontend 25

Type

type 件数
type/bug 8
type/enhancement 24
type/task 17
typeなし 1

P1 課題

P1 は公表前または主要 UX に直撃するものとして扱ふ。

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

issue 仕様上の扱ひ
#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 同定文字 タグ正規化/検索同定の将来仕様として扱ふ

全 issue 一覧

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. 本書を実装作業用に分解し、P0/P1 から Codex 向け issue prompt を作る。
  2. POST /materials 権限修正、PostVersionsControllerparent_posts 返却、Preview API 防御を先に潰す。
  3. RSpec/Vitest/build/lint を実行し、仕様ではなく検証報告として別章へ追記する。
  4. API レスポンス例を主要 endpoint ごとに追加する。
  5. DB ER 図またはテーブル関係図を別紙化する。
  6. Gitea issue の次回添付または export script により、仕様書を腐らせない運用を決める。

外部参照

Gekanator AI モデル選定では、2026-06-11 時点の OpenAI 公式ドキュメントを参照した。モデル ID は変動し得るため、仕様では GEKANATOR_AI_MODEL による差し替へを必須とする。