9
実装説明書
みてるぞ が 2026-07-09 21:28:43 +09:00 にこのページを編輯
title, subtitle, date, lang, toc, toc-depth, numbersections
title subtitle date lang toc toc-depth numbersections
BTRC Hub / タグ広場 製造仕様書 2026-07-09 差替資材反映版: 現行資材・Wiki・DB・未実装方針を統合した将来実装基準 2026-07-09 ja-JP true 3 false

0. 本書の位置づけ

本書は、BTRC Hub / タグ広場を ゼロから製造開始できる水準 まで仕様化するための基準文書である。

既存実装の説明書ではない。現行ソース、Wiki、DB ダンプ、既存仕様書、直近の設計会話、未起票の構想を統合し、実装済みか未実装かを本文上では区別せず、今後作るべき完成形を仕様として定義する。

ただし、製造判断に必要な危険点や未決定点は隠さない。未決定事項は「質問票」として末尾に隔離する。本文の仕様は、質問への回答が来るまでの暫定既定値である。

0.1 確認した資材

区分 内容
現行ソース btrc-hub-main.zip。2026-07-09 差替版。Rails API、React/Vite frontend、RSpec/Vitest、routes、schema、services
Wiki btrc-hub.wiki.zip。Home、実装説明書、テーブル定義書、環境構築手順、移行方針、グカネータ要件
DB btrc_hub_20260617時点本番DBデータ.zip。本番 SQL dump
既存仕様書 BTRC_Hub_現行実装仕様書_2026-06-11_完成版.md
直近設計 タグ廃止、Gekanator、局所記載、素材管理、Git 管理、オブジェクト・ストレージ、索引抑止、除票
課題管理 Gitea Issues を源泉とする。今回の生成環境では API JSON の全件抽出は完全にはできなかったため、既存仕様書内の issue 反映分とリポジトリ文書を併用した

0.2 2026-06-17 本番 DB 規模

差替後の btrc_hub_20260617時点本番DBデータ.zip から確認した主要件数は次の通りである。小規模メモ帳ではなく、すでに履歴・類似度・同期・上映会・Gekanator のデータを持つ運用系である。

テーブル 件数 読み取り
users 15,620 guest 自動作成の影響を受ける。bot・cookie なしアクセス対策が必要
posts 1,167 投稿は千件規模。履歴・類似度・同期の影響がすでに無視できない
tags 6,243 タグ体系は大きく、廃止・抑止・親子循環対策が必須
tag_names 6,296 別名・Wiki 結合点まで含めた名称空間が大きい
post_tags 48,294 投稿あたり多数タグ。タグベース制御の影響範囲が広い
post_versions 15,822 履歴が中核データになっている
tag_versions 2,385 タグ履歴も運用済み
nico_tag_versions 3,957 Nico 連携の変更履歴も大きい
nico_tag_relations 317 外部タグ連携は仕様対象として扱う必要がある
deerjikists 190 ニジラー紐づけは同期の基礎データ
post_similarities 48,900 投稿類似度は計算済みキャッシュとして利用
tag_similarities 122,560 タグ類似度も同様
materials 37 素材機能は初期規模だが、storage 圧迫リスクが強い
wiki_pages 51 Wiki は既に実データを持つ
wiki_revisions 201 Wiki 履歴は運用済み
theatre_programmes 320 上映会は再生履歴を持つ運用機能
theatre_comments 98 コメントは既存データあり。SNS 化しない境界設計が必要
gekanator_games 202 Gekanator は試行データが蓄積開始済み
gekanator_questions 73 質問カタログは実体化済み
gekanator_question_examples 1,514 学習データはすでに仕様対象
gekanator_question_suggestions 77 追加質問の承認/昇格フローが必要

総行数は約 31 万件である。DB 規模を見れば、履歴・検索抑止・素材同期・設定移行を「後で何とかする」扱いにするのは危険である。

0.5 2026-07-09 差替資材で追加反映した事項

今回の差替資材では、前回版で記述が薄かった次を仕様本文へ追加した。

領域 追加・補正内容
投稿 posts.video_mspost_versions.video_ms、局所記載の DB 形状、時間記法、動画長との整合性
タグ deprecated_at の API/UI 反映、autocomplete・投稿表示・Gekanator からの除外、Nico タグ廃止禁止
素材 member+ 作成、素材履歴 snapshot、export items、ZIP download、同期抑止、同期元、Google Drive 同期、thumbnail 生成
設定 typed user settings、端末 localStorage 設定、ライト/ダーク各 3 個のユーザテーマスロット
API /materials/download.zip, /materials/versions, /materials/suppressions, /users/theme_slots, tag name 系 endpoint
DB material_sync_sources, material_sync_suppressions, material_export_items, material_import_blocks, user_theme_slots
Frontend 素材履歴・素材抑止・設定タブ・テーマ編集・ショートカット表・ローカル動作設定

特に素材まわりは、単なる添付ファイル管理ではなく「外部同期、抑止、export path、ZIP 配布、履歴、thumbnail 生成」を含む標準素材基盤として扱う。

0.3 仕様語彙

用語 意味
公開 URL を知っていれば到達できる状態
公表 界隈へ明示的に案内し、利用者を呼び込む状態
投稿 外部コンテンツへのリンク単位。本文コンテンツは原則として保持しない
タグ 投稿・Wiki・素材・Gekanator を結ぶ分類単位
Wiki タグ名に結びつく説明ページ
素材 タグに紐づくファイルまたは外部 URL。キャラクター立ち絵、差分、部品等を含む
除票 一般公開面から投稿を外す。通常フロントでは admin にも詳細を見せず、履歴・監査のみ残す状態
索引抑止 外部検索エンジンに載せないため noindex 等を出す状態。閲覧禁止とは別
公開抑止 一般ユーザから本文・詳細を見せない状態
内部検索抑止 タグ広場内検索・一覧・サジェストから外す状態
廃止タグ 通常タグとしての新規付与を禁止するタグ。外部検索 noindex とは別概念

0.4 規範語

表記 意味
必須 実装しなければならない
禁止 実装してはならない
推奨 原則として実装する。外す場合は理由を issue に残す
任意 実装してよいが必須ではない

1. システム概要

BTRC Hub / タグ広場は、ぼざろクリーチャー関連コンテンツへのリンク、タグ、Wiki、素材、上映会、Gekanator を統合する共同編集型知識基盤である。

1.1 目的

  1. 外部プラットフォーム上の関連コンテンツを横断的に発見できるようにする。
  2. ニコニコ等のタグ数制限を超え、より豊かなタグ体系を提供する。
  3. タグ・親子関係・別名・Wiki・素材を通じて、作品群の文脈を保持する。
  4. 上映会により共同視聴を支援する。
  5. Gekanator により投稿の識別質問・類似性・タグ付け知識を蓄積する。
  6. 問題化し得る語・ページ・投稿について、削除だけでなく索引抑止・公開抑止・除票を選べるようにする。

1.2 非目的

次は主目的ではない。

  • 汎用 SNS。
  • 雑談掲示板。
  • 外部コンテンツ本体の無制限な再配布。
  • 動画配信サービス。
  • メール/パスワード前提の一般的ログイン基盤。
  • 誰でも無制限に編集できる匿名 Wiki。

ただし、コメント、素材、Gekanator、編集履歴を持つ以上、荒らし・ストレージ圧迫・検索流入・権利対応は避けられない。ここを軽視すると、仕様全体が砂上の楼閣になる。

1.3 中核ドメイン

ドメイン 概要
Users 引継ぎコード認証、guest/member/admin、BAN、設定
Posts 外部 URL、タイトル、サムネイル、タグ、親投稿、元日時、閲覧済、除票、索引抑止
Tags 名前、カテゴリ、別名、親子、廃止、索引抑止、Nico 連携、素材、Wiki
Wiki タグ名と結びつく説明ページ、行単位履歴、差分、本文検索、索引抑止
Materials タグに紐づく素材、URL、ファイル、履歴、Git 配布、object storage
Theatre 共同視聴、ホスト、番組表、コメント、skip 投票、重み付け
Gekanator 投稿当てゲーム、質問、回答例、AI 変換、学習データ
Sync Nico/YouTube 等からの投稿同期、外部タグ連携
Moderation BAN、除票、公開抑止、索引抑止、申請フォーム、操作履歴

2. 技術構成

2.1 Backend

項目 仕様
言語 Ruby
Framework Rails 8 API
DB MySQL 8 系を標準とする
File Active Storage。production は S3 互換 object storage、Cloudflare R2 を想定
画像処理 image_processing / MiniMagick
HTML 解析 Nokogiri
差分 diff-lcs
Test RSpec
Soft delete discarded_at 系の論理削除を標準とする
API 認証 X-Transfer-Code による引継ぎコード認証

2.2 Frontend

項目 仕様
UI React 19 + Vite
言語 TypeScript
API Axios。レスポンスは deep camelCase 化
State/Fetch TanStack Query、必要に応じて localStorage
Style Tailwind CSS、ローカル UI components、Framer Motion
Markdown react-markdown、remark-gfm、Wiki autolink
Head react-helmet-async により title / meta robots を route ごとに制御
Test Vitest、Testing Library

2.3 製造時の標準検証

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

マイグレーションを追加した場合は、少なくとも次を確認する。

cd backend && bin/rails db:migrate RAILS_ENV=test
cd backend && bin/rails db:schema:load RAILS_ENV=test

3. 認証・ユーザ・権限

3.1 認証方式

通常ログインではなく、引継ぎコードを認証トークンとして用いる。

  1. 初回アクセス時、フロントは localStorage.user_code を確認する。
  2. 値があれば POST /users/verify で検証する。
  3. 無効または未存在なら POST /users で guest user を作成する。
  4. 以後、API 呼び出しでは X-Transfer-Code にコードを入れる。
  5. バックエンドは users.inheritance_code から current_user を設定する。

users.inheritance_code には DB 一意制約を必須とする。認証トークンに一意制約がない設計は弱い。

3.2 role

role 権限
guest 閲覧、閲覧済み、上映会参加、コメント、skip 投票、限定的な利用
member 投稿・タグ・Wiki・素材の編集
admin 管理、BAN、抑止、Gekanator 調整、危険操作

member 以上を判定する共通述語を用意する。実装上は gte_member? のような名前でよい。

3.3 権限表

操作 guest member admin
投稿閲覧
投稿作成/更新 不可
投稿除票/復帰 不可 不可
投稿索引抑止 不可 申請可
タグ編集 不可
タグ親子編集 表示のみ
タグ廃止 不可
タグ索引抑止 不可 申請可
Wiki 作成/更新 不可
Wiki 索引抑止 不可 申請可
素材作成 原則不可
URL-only 素材作成 将来検討
file 素材作成 不可
上映会コメント
skip 投票
Gekanator 一般プレイ 将来可
Gekanator 調整 不可 不可
Preview API 可。ただし rate limit・SSRF 防御必須

3.4 BAN

BAN は全 API の入口で判定する。

対象 判定 応答
IP ip_addresses.banned_at IS NOT NULL 403
User users.banned_at IS NOT NULL 403

IP は binary 16 bytes で保存する。IPv4/IPv6 を同一設計で扱う。

3.5 guest 自動作成の制御

guest 自動作成は継続する。ただし、bot がアクセスするだけで users が増える状態は避ける。

必須対策:

  • POST /users に IP + UA + subnet 単位の rate limit を設ける。
  • localStorage が使えない環境では POST /users を連打しない。
  • bot UA には user を発行しない。
  • users.last_seen_atusers.user_agent_hashusers.created_ip_address_id を追加し、掃除 task を可能にする。
  • 編集・投票・コメント等の実績がない古い guest は削除または圧縮できるようにする。

3A. ユーザ設定・テーマ仕様

設定は、DB に保存する portable settings と、端末ごとに保存する client settings に分ける。

3A.1 DB 保存設定

settings は user ごとに 1 row とする。GET /users/settings は存在しなければ default を作って返す。PATCH /users/settings は指定された editable attribute だけ更新する。

属性 既定
theme string system, light, dark system
auto_fetch_title string auto, manual, off manual
auto_fetch_thumbnail string auto, manual, off manual
wiki_editor_mode string split, write, preview split

画面に露出していない typed settings も schema には残す。画面に出すかどうかと DB schema は分けて考える。

3A.2 client settings

端末依存の動作設定は localStoragebtrc_hub.client_settings に保存する。

設定 用途
animationMode off, reduced, normal 画面遷移・タグ移動 animation
linkPreloadMode off, intent link hover/touch 時の prefetch
tagRelationDisplay flat, grouped タグ親子の表示方式
embedAutoLoad off, manual, auto 外部 embed の自動読込
thumbnailMode 実装定義 thumbnail 表示挙動
activeLightThemeSlotNo 1, 2, 3 light theme の使用 slot
activeDarkThemeSlotNo 1, 2, 3 dark theme の使用 slot
keyboard bindings action => key ショートカット

sidebar 幅、一覧件数、並び順など端末・画面ごとに意味が変わるものも原則 localStorage とする。

3A.3 ユーザテーマスロット

user_theme_slots は user ごとのテーマ token 保存枠である。

属性 仕様
base_theme light または dark
slot_no 1 から 3
tokens JSON object。色 token、tagColours 等

API:

endpoint 仕様
GET /users/theme_slots current user の slot を base_theme, slot_no 順で返す
PUT /users/theme_slots/:base_theme/:slot_no 指定 slot を upsert する

UI 仕様:

  • light 3 枠、dark 3 枠を持つ。
  • 選択中 slot へ上書き保存する。
  • 既定に戻す操作を持つ。
  • TopNav、背景、リンク、タグ色など主要 token を編集対象にする。
  • 未保存変更がある状態で離脱しようとした場合は確認する。

3A.4 設定画面

設定画面は次の tab を持つ。

tab 内容
アカウント user code、role、引継ぎ情報
動作 animation、prefetch、embed、自動取得、親子 grouping
テーマ system/light/dark、theme slot、色 token
キーボード shortcut 表示と編集

PC は左縦 tab、SP は横 tab を標準とする。

4. 公開・除票・抑止

この章が今後の製造で最も重要である。投稿やタグは、単に「存在する/消す」では足りない。外部検索、内部検索、一覧、詳細、履歴、サムネイル、sitemap、Wiki 本文への波及まで制御する必要がある。

4.1 状態の分離

状態 意味 閲覧 内部検索 外部検索 履歴 代表用途
通常 通常公開 出る index 可 残る 一般投稿
索引抑止 noindex 対象 原則出る noindex 残る 検索流入だけ避けたい
内部検索抑止 広場内検索から外す 直リンク可 出ない 設定次第 残る ノイズ・暫定非表示
公開抑止 一般閲覧不可 不可 出ない noindex 残る 権利・個人情報・問題語
除票 投稿を一般面から外す 不可 出ない noindex 残る 誤登録・リンク先問題・削除相当
廃止タグ タグ新規付与不可 タグページ可 設定次第 設定次第 残る 分類体系の整理

deprecated_at はタグ分類上の廃止であって、noindex ではない。ここを混ぜると仕様が壊れる。

4.2 投稿の除票

投稿は物理削除しない。投稿除票は次を意味する。

  • 一般の投稿一覧から除外する。
  • 投稿検索から除外する。
  • 通常フロントの GET /posts/:id は role にかかわらず 404 を返す。
  • admin フロントでも除票済み投稿の通常詳細は表示しない。削除と同等に扱う。
  • sitemap から除外する。
  • HTML 詳細ページを返す場合は必ず noindex を付ける。
  • 類似度計算、上映会抽選、Gekanator 候補から除外する。
  • post_versions には discard / restore を記録する。
  • post_tags は削除しない。復帰時の再構築と監査のため保持する。

推奨カラム:

テーブル カラム 内容
posts discarded_at 除票日時
posts discarded_by_user_id 除票者
posts discard_reason 管理用理由
posts restored_at 任意。最後の復帰日時
post_versions event_type = discard/restore 履歴

public 応答は 410 Gone が意味としては正しい。ただし、URL 存在推測を避けたい場合は 404 に寄せる。既定は 404 とする。admin フロントでも通常詳細は非表示にし、監査・復帰が必要な場合だけ専用の管理導線で扱う。

4.3 索引抑止

索引抑止は「ページは見られるが、外部検索に載せない」ための状態である。

必須動作:

  • HTML には <meta name="robots" content="noindex"> を出す。
  • sitemap から除外する。
  • 必要に応じて HTTP header X-Robots-Tag: noindex を出す。
  • robots.txtDisallow で塞ぐことは原則禁止する。crawler がページを読めないと noindex を認識できないためである。
  • route 遷移後も React Helmet で robots meta を更新する。
  • noindex 判定は API の payload に含め、frontend だけの推測にしない。

nofollow は既定では付けない。リンク先への評価伝搬を止めたい場合だけ個別指定する。

4.4 索引抑止の対象

対象 noindex 対象ページ
投稿 /posts/:id、投稿を含む静的 detail route
タグ /tags/:id/tags/name/:name、関連する Wiki、素材、タグ検索結果
Wiki /wiki/:title、diff、history
素材 /materials/:id、タグ素材一覧
route /theatres/:id、settings、エラー画面等
phrase 本文・タイトル・タグリスト等に指定語が現れる任意ページ

4.5 タグベース索引抑止

タグに索引抑止を設定した場合、次を noindex とする。

  1. 当該タグの詳細ページ。
  2. 当該タグに対応する Wiki ページ。
  3. 当該タグに対応する素材ページまたは素材一覧。
  4. 当該タグが直接付与された投稿。
  5. 親タグ展開により当該タグを含むとみなされる投稿。
  6. 当該タグ名または別名がページの文章中に現れる Wiki ページ。
  7. 当該タグ名または別名がタイトル、説明、タグチップ、親子タグ表示、関連タグ表示に現れるページ。

この仕様は、単に投稿タグを見るだけでは不足する。ページ本文に文字列として現れる場合も検索エンジンには拾われるためである。

4.6 タグ名・別名・本文一致ルール

索引抑止の文字列判定は次を使う。

  • tag_name の canonical name。
  • aliases。
  • tag_name_sanitisation_rules 適用後の同定形。
  • Unicode 正規化後の表記。
  • 完全一致を既定とする。
  • 部分一致は phrase rule として明示された場合だけ行う。

禁止:

  • 何でも substring match にすること。誤爆が増えすぎる。
  • HTML 文字列へ正規表現を直接かけること。Markdown/HTML/React 表示単位の token 化後に判定する。

4.7 IndexPolicyResolver

索引抑止は各画面が勝手に判断しない。backend に IndexPolicyResolver を置き、API payload と sitemap 生成で共通利用する。

入力例:

route: posts/show
subject: post_id = 123
rendered_text_sources:
  - title
  - tag_names
  - parent_post_titles
  - remarks
  - wiki_excerpt

出力例:

{
  "robots": "noindex",
  "sitemap": false,
  "reasons": [
    { "type": "tag", "id": 456, "name": "センシティブタグ" },
    { "type": "phrase", "phrase": "センシティブ語" }
  ],
  "internal_search_hidden": false,
  "public_hidden": false
}

frontend はこの結果を受け取り、Helmet に反映する。frontend 側だけで推測しない。

4.8 推奨データ構造

抑止は今後増えるため、単純な boolean 列だけでなく policy table を持つ。

index_policies
- id
- target_type          post / tag / wiki_page / material / route / phrase
- target_id            nullable
- phrase               nullable
- noindex              boolean
- nofollow             boolean
- sitemap_excluded     boolean
- internal_search_hidden boolean
- public_hidden        boolean
- propagation          json
- reason               text
- created_by_user_id
- created_at
- updated_at
- discarded_at

頻繁に絞り込む対象には materialized column を持ってよい。

posts.index_suppressed_at
tags.index_suppressed_at
wiki_pages.index_suppressed_at
materials.index_suppressed_at

ただし、materialized column は cache であり、真の根拠は index_policies とする。

4.9 公開抑止

公開抑止は、一般ユーザに内容を見せない状態である。索引抑止より強い。

  • guest/member の詳細 API は 404 を返す。
  • admin は理由付きで閲覧できる。
  • 一覧・内部検索・Gekanator・上映会・類似度から除外する。
  • sitemap から除外する。
  • HTML を返す場合は noindex。
  • 履歴は admin のみ参照可能。

公開抑止は法務・個人情報・権利・荒らし対応に使う。単なる検索流入避けに使うとデータが死ぬので、通常は索引抑止を使う。

4.10 内部検索抑止

内部検索抑止は、広場内の通常検索・一覧から外すが、直リンク閲覧は許可する状態である。

用途:

  • 重複候補。
  • bot 操作タグ付きで未整理の投稿。
  • 一時的に検索ノイズとなる同期投稿。
  • 人手確認待ち。

内部検索抑止の対象は、投稿、タグ、Wiki、素材ごとに個別に持つ。

5. 投稿仕様

5.1 投稿の基本属性

投稿は外部 URL を中心に保持するリンクレコードである。

属性 仕様
url 必須。一意。HTTP/HTTPS のみ
title 任意。外部タイトルまたは手動タイトル
thumbnail_base 外部サムネイル URL。後から変更可能
thumbnail Active Storage 添付。手動アップロード可
uploaded_user_id 作成者。同期投稿では bot/admin を使うか null 許容
original_created_from 元コンテンツ作成日時の下限
original_created_before 元コンテンツ作成日時の上限。from < before 必須
version_no 投稿内版番号。1 以上
video_ms 動画長。ミリ秒単位。動画 タグを持つ投稿で意味を持つ
discarded_at 除票日時
index_suppressed_at 索引抑止 cache

5.2 URL 正規化

保存前に次を行う。

  • 前後空白を除去する。
  • scheme は http / https のみ許可する。
  • host は小文字化する。
  • path 末尾の / は原則除去する。
  • URL 全体は DB index 長に収まるよう制限する。
  • 同一動画の URL 揺れは platform normalizer で正規化する。

YouTube と Nico は専用 normalizer を持つ。

5.3 作成

POST /posts は member 以上が実行できる。

入力:

入力 必須 説明
url 必須 外部 URL
title 任意 タイトル
thumbnail_base 任意 外部サムネイル URL
thumbnail 任意 添付サムネイル
tags 必須 空白区切り。空の場合も文字列として扱う
parent_post_ids 必須 空配列可
original_created_from 任意 元作成日時下限
original_created_before 任意 元作成日時上限
video_ms / duration 任意 動画長。video_ms はミリ秒、duration は時刻文字列

作成時は post_versionscreate snapshot を記録する。

5.4 更新と競合制御

PUT/PATCH /posts/:id は member 以上が実行できる。

  • base_version_no を要求する。
  • 現在版と異なる場合は 409 conflict を返す。
  • force は admin または明示権限者のみ許可する。
  • merge は競合のない集合差分のみ自動統合する。
  • forcemerge の同時指定は禁止する。

競合判定対象:

  • title
  • thumbnail_base
  • original_created_from
  • original_created_before
  • tag_names
  • parent_post_ids
  • video_ms
  • local tag sections
  • remarks

5.5 タグ付与

投稿タグは post_tags で保持する。

仕様:

  • 論理削除で履歴を残す。
  • 同一投稿・同一タグの active 重複は禁止する。
  • タグ名は別名解決・カテゴリ prefix 解釈・sanitisation を通す。
  • nico タグは通常手入力禁止。
  • 廃止タグは新規付与禁止。
  • 親タグは再帰的に展開する。
  • 展開後に廃止タグは除外する。

5.6 局所記載

局所記載は、投稿全体ではなく動画・音声・長尺コンテンツの一部にタグを付ける機能である。投稿全体タグと同じタグ体系を使うが、範囲情報を post_tag_sections に分離して保持する。

5.6.1 動画長

posts.video_ms は動画長をミリ秒で保持する。post_versions.video_ms にも snapshot する。

  • video_ms は正の整数または NULL とする。
  • UI では 動画 タグが付いた場合に動画時間欄を表示する。
  • 動画 タグを削除して再入力した場合でも、編集中の video_ms は可能な限り保持する。
  • API は video_ms の直接指定と、duration 文字列指定の双方を受け付ける。
  • video_ms は optimistic concurrency の競合判定対象である。
  • 動画 タグを持たない投稿では UI に動画時間欄を表示しない。
  • 動画 タグを持たない投稿に video_ms / duration が渡された場合、API は DB に保存しない。
  • 動画属性は将来的に video_posts 等の別テーブルへ切り出す余地がある。

5.6.2 時刻文字列

局所範囲と duration は次の形式を受け付ける。

12
12.345
1:23
1:23.456
1:02:03
1:02:03.456
  • 単独数値は秒として解釈する。
  • 分:秒時:分:秒 を許可する。
  • 秒・分の範囲は 0 から 59 を原則とする。
  • 内部保存はミリ秒に正規化する。

5.6.3 タグ記法

記法:

タグ名[開始-終了]
タグ名[開始-]
タグ名[-終了]
タグ名[-]
タグ名[0:00-0:12]
タグ名[1:23-2:34]

解釈:

  • タグ名[-] は局所記載なしとして タグ名 と等価に扱う。
  • タグ名[0-]タグ名[0:00-] は全範囲指定であり、通常タグと等価に扱う。
  • 開始省略は 0 とみなす。
  • 終了省略は NULL とし、動画末尾までを表す。
  • 開始と終了が逆の場合は並べ替える。
  • 開始と終了が同値の場合は、最小 1 ms の範囲として扱う。
  • 同一タグの重複・隣接・包含する section は merge する。
  • section が全範囲に merge された場合、その tag の section は保存せず通常タグとして扱う。

5.6.4 DB 形状

post_tag_sections は次の複合主キーで保持する。

post_tag_sections
- post_id
- tag_id
- begin_ms
- end_ms
- created_at
- updated_at
primary key: post_id, tag_id, begin_ms
check: begin_ms >= 0
check: end_ms is null or begin_ms < end_ms

現行実装は投稿タグ同期時に対象投稿の section を削除して再作成する。したがって、section 単体の soft delete や作成者履歴は持たない。監査が必要な場合は post_versions.tags または将来の post_versions.tags_json により復元する。

5.6.5 整合性

  • video_ms がある場合、begin_ms < video_ms を必須とする。
  • end_ms がある場合、end_ms <= video_ms を必須とする。
  • video_ms が未設定の場合、section の上限検査は行わない。
  • 局所タグの親タグ展開は、同一 section に対して行う。
  • 投稿表示では、通常タグと局所タグを視覚的に混ぜない。混ぜると「動画全体に出る」のか「数秒だけ出る」のかが壊れる。

5.6.6 履歴表示

投稿履歴では、タグ名だけでなく局所範囲も表示対象にする。

  • 旧: 伊地知ニジカ
  • 新: 伊地知ニジカ[0:12-0:18]

post_versions.tags_json を導入する場合、局所範囲は文字列ではなく次の構造で保持する。

{
  "tag_id": 123,
  "name": "伊地知ニジカ",
  "sections": [
    { "begin_ms": 12000, "end_ms": 18000 }
  ]
}

5.7 親子投稿

post_implications は投稿間の親子関係を表す。

  • post_id から親 parent_post_id への関係。
  • 自己親は禁止。
  • 存在しない親は禁止。
  • 可能なら循環も禁止する。
  • 投稿履歴には親投稿変更を必ず記録・表示する。

表示:

  • 子投稿は親投稿を参照表示する。
  • 親投稿のタイトルは現在タイトルを表示する。
  • 履歴画面では当時タイトルも表示できるようにする。

5.8 投稿検索

GET /posts は次の条件を持つ。

条件 仕様
url 部分一致
title 部分一致
tags 空白区切り。別名解決あり
match all / any
not: 除外タグ
original_created_from/to 元作成日時
created_from/to 作成日時
updated_from/to 更新日時。タグ更新も考慮
visibility 通常/索引抑止/内部検索抑止/公開抑止。除票は通常検索対象外
order title/url/original_created_at/created_at/updated_at
page, limit ページネーション。無限ローディングに依存しない

通常検索は除票・公開抑止・内部検索抑止を除外する。索引抑止・内部検索抑止・公開抑止は admin の明示 filter で確認できるが、除票済み投稿は通常検索には role にかかわらず出さない。

5.9 投稿履歴

post_versions は immutable snapshot である。

必須 snapshot:

  • title
  • url
  • thumbnail_base
  • tags_json
  • parent_posts_json
  • local_sections_json
  • remarks_json
  • original_created_from
  • original_created_before
  • visibility state
  • created_by_user_id

post_versions.tags の空白区切り形式は将来的に tags_json へ移行する。タグ名・カテゴリ・当時名・現在 ID を持てる構造にする。

6. タグ仕様

6.1 タグ構造

タグは tag_namestags に分離する。

テーブル 役割
tag_names 名前文字列、canonical/alias、Wiki との結合点
tags カテゴリ、投稿件数、廃止、除票、索引抑止、version

tag_names.name は全体で一意とする。

6.2 カテゴリ

category 意味
deerjikist ニジラー
meme 原作・ネタ元・ミーム
character キャラクター
general 一般
material 素材タグ
meta メタタグ
nico ニコニコ外部タグ

nico は外部情報なので、内部タグと同じ廃止・除票運用に混ぜない。

6.3 システムタグ

タグ 用途
タグ希望 タグ不足
bot操作 bot/sync 由来
ニジラー情報不詳 投稿者/作者情報不足
動画 動画投稿
ニコニコ ニコニコ由来
YouTube YouTube 由来

システムタグの名称変更は禁止する。別名追加は可能だが、canonical name は固定する。

6.4 正規化

タグ入力は次を通す。

  1. 空白・空文字除去。
  2. Unicode 正規化。
  3. tag_name_sanitisation_rules 適用。
  4. prefix 解析。
  5. 別名解決。
  6. 存在しない tag_name/tag の作成。
  7. 廃止タグ拒否。
  8. nico 手入力拒否。
  9. タグ希望 / ニジラー情報不詳 の自動付与。
  10. 親タグ展開。

prefix:

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

6.5 別名

  • alias は tag_names.canonical_id で表す。
  • alias の参照先は canonical tag_name でなければならない。
  • alias 名に : は含めない。
  • タグ実体または Wiki を持つ tag_name は alias 化できない。
  • タグ名変更時、旧名は alias として残す。

6.6 親子タグ

tag_implications は子タグから親タグへの関係である。

仕様:

  • member 以上が編集可能。
  • UI では guest にも親タグ欄を表示するが編集不可。
  • 自己親は禁止。
  • 循環は禁止。
  • 親タグ展開は再帰的に行う。
  • 廃止タグが中間に挟まる場合、表示上は貫通する。

例:

A -> B -> C
B が廃止済み
表示: A -> C

6.7 タグ廃止

タグ廃止は tags.deprecated_at で表す。

  • 廃止タグは新規投稿に付与できない。
  • 既存投稿から即時剥がさない。
  • 投稿表示では廃止タグを通常タグ一覧から除外する。
  • 投稿検索では、廃止タグを理由に既存投稿を除外しない。検索条件として廃止タグを指定した場合も、既存投稿との対応は保持する。
  • タグ検索では廃止状態で絞り込める。
  • タグ履歴には状態差分を表示する。
  • Gekanator のタグ質問・特徴量には廃止タグを使わない。廃止タグを含むだけで投稿候補そのものを除外しない。
  • 類似度計算では廃止タグを除外する。
  • nico タグは廃止不可。

廃止と索引抑止は別である。廃止しても noindex にはならない。noindex が必要な場合は別途 index policy を設定する。

6.7.1 API/UI 反映

差替後の資材では、タグ廃止は単なる DB カラムではなく API と画面に反映する対象である。

場所 仕様
GET /tags deprecated=1 / deprecated=0 による絞り込みを持つ
GET /tags/autocomplete 廃止タグを候補から除外する
PUT /tags/:id full update では deprecated を必須入力として扱う
PATCH /tags/:id deprecated が指定された場合だけ廃止状態を更新する
投稿作成/更新 deny_deprecated: true により廃止タグの新規付与を拒否する
投稿表示 廃止タグを通常タグ一覧から除外する
Wiki 表示 対応タグが廃止済みの場合、タイトル付近に (廃止) を表示する
Gekanator tag question と特徴量から廃止タグを除外する。廃止タグを含むだけで投稿候補そのものは除外しない
素材一覧 グルーピングやタグ候補で廃止タグを通常候補にしない

Nico タグは外部情報の mirror であるため、廃止は禁止する。tags.category = nico かつ deprecated_at IS NOT NULL は DB check constraint でも拒否する。

7. Nico タグ・外部連携

7.1 Nico タグ

Nico タグは tags.category = 'nico' とし、名前は必ず nico: で始める。

  • 通常入力からは拒否する。
  • Nico 同期または管理 UI でのみ作成する。
  • nico タグ同士の連携は禁止する。
  • nico タグは外部情報であり廃止不可。
  • nico_tag_versions には連携状態を記録する。

7.2 Nico タグ連携

nico_tag_relations は nico タグと内部タグを結びつける。

  • member 以上が編集可能。
  • 連携先は非 nico タグのみ。
  • 連携先タグの廃止状態を見て警告する。
  • 連携変更は nico_tag_versions と tag_versions の双方に必要な履歴を残す。

7.3 連携抑止

外部タグから内部タグへの自動付与を止めたい場合、連携抑止ルールを持つ。

用途:

  • 外部タグが荒れている。
  • 同名だが意味が違う。
  • 特定投稿だけ連携したくない。
  • 権利・検索流入上の懸念がある。

推奨構造:

external_tag_suppression_rules
- id
- platform          nico / youtube / pixiv / etc
- external_key
- target_type       tag / post / source / global
- target_id
- reason
- created_by_user_id
- discarded_at

8. Wiki 仕様

8.1 基本

Wiki ページは tag_names に一対一で紐づく。

  • タイトルは tag_name.name
  • タグ実体がなくても Wiki は存在し得る。
  • タグが廃止されている場合、Wiki 表示上も廃止状態を示す。
  • Wiki 本文が空の場合は noindex とする。

8.2 作成・更新

Wiki 作成/更新は member 以上。

保存時:

  • CRLF を LF へ統一する。
  • 不正 UTF-8 は置換して保存する。
  • 行単位で SHA256 を計算し、wiki_lines に dedup 保存する。
  • wiki_revision_lines に順序を保存する。
  • wiki_versions に title/body snapshot を保存する。
  • base_revision_id が現在 revision と違う場合は conflict とする。

8.3 本文検索

Wiki 検索は title だけでなく body も検索対象にする。

必須:

  • title 完全一致を最優先。
  • title 前方一致。
  • title 部分一致。
  • body 部分一致。
  • 廃止タグ Wiki は検索結果に表示するが状態表示する。
  • index policy により内部検索抑止されている Wiki は通常検索から除外する。

8.4 自動リンク

Wiki 本文中のタグ名は自動リンクできる。ただし次は禁止する。

  • Markdown のリンク先 URL 部分を勝手に autolink すること。
  • code span / code block 内を autolink すること。
  • 既存リンクの label 内を破壊すること。

8.5 Wiki asset

Wiki には画像・添付 asset を持てる。

仕様:

  • wiki_pages.next_asset_no によりページ内番号を採番する。
  • wiki_assets.sha256 は添付バイト列の SHA256。
  • 同一ページ内の同一 SHA256 重複は禁止する。
  • Active Storage に実体を保存する。
  • alt_text を必須または強く推奨する。
  • asset 追加も revision/version に残す。
  • noindex 対象 Wiki の asset には X-Robots-Tag: noindex を返す。

9. 素材仕様

9.1 基本

素材は tag に紐づく URL または file である。標準素材管理基盤として、単なる添付ファイルではなく、同期元、同期抑止、履歴、export path、ZIP 配布、thumbnail 生成を含めて扱う。

属性 仕様
tag_id 任意。ただし手動作成では必須。character または material のタグだけ許可
url 任意。file がなければ必須
file 任意。url がなければ必須。Active Storage 添付
thumbnail 任意。画像・動画素材から生成した 180px 級 thumbnail
version_no 素材内版番号。1 以上
source_kind uri, google_drive_path, google_drive_file, legacy_drive_path
source_uri URI 同期元
source_path Drive/legacy drive 内の相対 path
source_file_id Google Drive file id
normalized_source_key 同期元を一意化した key
created_by_user_id 作成者
updated_by_user_id 更新者
discarded_at 論理削除

character タグも素材を持てる。文言上「素材カテゴリのみ」としない。

手動作成ではタグ必須とする。同期取り込みでは未分類素材を許容し、耕作員が後から分類する。

9.2 素材タグ体系

派生差分は親タグを使って表す。

伊地知ニジカ          category: character
伊地知ニジカ_泣き     category: material, parent: 伊地知ニジカ
伊地知ニジカ_笑顔     category: material, parent: 伊地知ニジカ

この設計により「キャラクター本体」と「素材差分」を同じタグ親子体系で扱える。

ただし、同期時の自動分類は行わない。ファイル名や path がニジカらしく見えても、勝手に 伊地知ニジカ タグを付けない。material_sync_sources.default_tag_name は互換・将来検討用の項目であり、標準同期仕様では使わない。

9.3 権限

入力 guest member/admin
URL-only 当面不可。将来検討
file-only 不可
URL + file 不可
同期抑止登録 不可
同期元登録/編集 不可 admin 推奨
ZIP download 可。ただし rate limit

file 素材は object storage を直接圧迫するため guest に開けない。差替後の実装では POST /materials も member 以上に閉じる。

9.4 作成・更新

POST /materialsPUT/PATCH /materials/:id は次を行う。

  1. current user が member 以上であることを確認する。
  2. tag 名を解決し、存在しなければ material category の tag を作る。
  3. file がある場合、SHA256 を計算する。
  4. material_import_blocks に該当する SHA256 は拒否する。
  5. Active Storage blob に SHA256 metadata を持たせる。
  6. material 本体を保存する。
  7. export path が指定されていれば material_export_items を upsert する。
  8. material_versions を記録する。
  9. 画像または動画なら thumbnail を生成する。

更新時に URL だけへ切り替える場合、file は detach できる。

9.5 一覧・検索

GET /materials は次を持つ。

パラメータ 仕様
q タグ名、URL、ファイル名の部分一致
tag_state all, tagged, untagged
unclassified legacy alias。true の場合 tag_state=untagged
media_kind all, image, video, audio, file_other, url_only
tag_id 指定タグで絞り込み
include_descendants 指定タグの子孫タグも含める
group_by none, parent_tag
created_from/to 作成日時範囲
updated_from/to 更新日時範囲
sort created_at, updated_at, tag_name, media_kind, file_byte_size, version_no, id
direction asc, desc
page, limit ページネーション

group_by=parent_tag の場合は親タグ単位の group 情報を返す。廃止タグは通常の group 候補にしない。

9.6 表現

素材レスポンスは少なくとも次を返す。

フィールド 内容
id material id
version_no material version
tag nullable。id, name, category, deprecated state
url 外部 URL
file_url 添付 file の URL
thumbnail_url thumbnail URL
media_kind image/video/audio/file_other/url_only
content_type file MIME type
file_byte_size file size
source_kind/source_uri/source_path/source_file_id/normalized_source_key 同期元情報
export_paths profile ごとの export path
created_by_user/updated_by_user 操作者
wiki_page_body 詳細画面用。対応タグの Wiki 本文

タグがない素材は、同期取り込み直後の未分類素材として UI に表示する。

9.7 履歴

material_versions は必須である。

snapshot 対象:

対象 内容
tag tag_id, tag_name, tag_category
URL url
file file_blob_id, filename, content_type, byte_size, checksum, sha256
source source_kind, source_uri, source_path, source_file_id, normalized_source_key
export export_paths_json
state discarded_at
actor created_by_user_id, updated_by_user_id
parent 現行互換の snapshot として残す。新仕様では親素材構造を拡張しない

GET /materials/versionsmaterial_id, tag, event_type で絞り込める。並びは created_at DESC, id DESC とする。

9.8 object storage と thumbnail

素材ファイルの本体は object storage に置く。Cloudflare R2 など S3 互換 service を想定する。

必須:

  • sha256 を保存する。
  • byte_size を保存する。
  • content_type を保存する。
  • original filename を保存する。
  • 同一 sha256 の重複アップロードは blob 再利用を検討する。
  • public URL は policy により制御する。
  • noindex/公開抑止対象素材には header または署名 URL で制御する。

Thumbnail 生成:

対象 処理
image/* MiniMagick で 180px 級 JPEG へ変換
video/* ffmpeg で 1 秒地点、失敗時 0 秒地点を抽出し JPEG 化
audio/* thumbnail は生成しない
URL-only thumbnail は生成しない

thumbnail 生成失敗は material 保存失敗にしない。ログに残し、UI は fallback 表示を行う。

9.9 export path と ZIP download

material_export_items は、素材を旧素材集互換の ZIP へ出すための path を管理する。

カラム 仕様
material_id 素材
profile 初期値 legacy_drive
export_path ZIP 内の相対 path
enabled false の場合 export しない
created_by_user_id 登録者

制約:

  • 1 material につき profile ごとに 1 export item。
  • profile + export_path は一意。
  • absolute path、drive path、backslash、NUL、...、連続 slash、末尾 slash は禁止。

GET /materials/download.zip は有効な export item をもとに ZIP を生成する。

  • file 添付がない素材は ZIP に入れない。
  • export 対象が空なら validation error を返す。
  • export path 重複は validation error を返す。
  • file が Active Storage 上で失われている場合は missing file error を返す。
  • tag subtree 単位で export できる。
  • 公開抑止・除票素材は ZIP から除外する。
  • ダウンロードには rate limit を設ける。

9.10 同期抑止

material_sync_suppressions は、外部同期から特定素材を取り込まないためのルールである。

source_kind 意味
uri source_uri 完全一致
google_drive_path Google Drive 相対 path 完全一致
google_drive_path_prefix Google Drive 相対 path prefix
google_drive_file Google Drive file id
legacy_drive_path legacy drive 相対 path 完全一致
legacy_drive_path_prefix legacy drive 相対 path prefix

理由:

  • copyright_high_risk
  • copyright_takedown
  • adult_or_sensitive
  • personal_information
  • malware_or_dangerous_file
  • duplicate_or_low_quality
  • source_owner_request
  • other

path は同期元フォルダからの相対 path だけ許可する。My Drive/マイドライブ/、absolute path、...、NUL、連続 slash、末尾 slash は禁止する。

GET/POST /materials/suppressions は member 以上が使える。

9.11 取込ブロック

material_import_blocks は、アップロードまたは同期取込自体を拒否するためのルールである。

match_kind 仕様
sha256 file hash 一致で拒否
exact_path 外部 path 完全一致で拒否
path_prefix 外部 path prefix 一致で拒否
manual 管理者の手動記録

手動アップロード時は SHA256 block を確認する。同期時も SHA256 block と同期抑止を確認する。

9.12 同期元

material_sync_sources は外部素材集の同期元を管理する。

カラム 仕様
name 表示名
source_kind uri, google_drive_path, google_drive_file, legacy_drive_path
source_uri URI 同期元
source_path Drive/legacy drive の相対 path
source_file_id Google Drive file id
profile 初期値 legacy_drive
enabled 同期対象か
default_tag_name 標準仕様では使わない。既定タグ自動付与は禁止
export_path_prefix export path 生成時の prefix
last_synced_at 最終同期日時

Google Drive 同期は service account を用いる。必要な環境変数は次を標準とする。

  • GOOGLE_DRIVE_SERVICE_ACCOUNT_EMAIL
  • GOOGLE_DRIVE_PRIVATE_KEY または GOOGLE_DRIVE_PRIVATE_KEY_PATH
  • GOOGLE_DRIVE_SUBJECT は必要時のみ

Drive API scope は readonly に限定する。Google Docs などの native document は file として直接取り込まない。

legacy_drive_path は DB 上の source_kind として保持できるが、runner 実装は段階的対応とする。未対応 source_kind は同期失敗として明示する。

9.13 Git 管理と配布

素材は ZIP ダウンロードだけでなく、知識のある利用者が Git で最新化できるようにする。

ただし、大容量ファイルを通常 Git に直接入れると repository が死ぬ。Git は索引と manifest を管理し、実体は object storage に置く。

生成される repository 例:

materials.git
- README.md
- manifest.json
- tags/
  - 伊地知ニジカ.json
  - 伊地知ニジカ_泣き.json
- files/
  - ab/cd/<sha256>.asset.json
- scripts/
  - sync-assets

git pull で manifest と差分情報を取得する。実体ファイルは script が object storage から取得する。

禁止:

  • 任意の Git push をそのまま production storage に反映すること。
  • MIME/type/size/sha256 検証なしで取り込むこと。
  • Git repository に巨大 binary を無制限に保持すること。

10. 上映会仕様

10.1 基本

上映会は共同視聴 room である。

属性 仕様
name 部屋名
opens_at / closes_at 開始/終了
kind 種別
current_post_id 現在投稿
current_post_started_at 再生開始時刻
host_user_id 暫定制御者
next_comment_no コメント採番

10.2 在席

PUT /theatres/:id/watching は現在ユーザを在席として更新する。

  • expires_at は短時間、現行目安 30 秒。
  • active user だけを人数・skip 判定に使う。
  • watching は荒らし対策の rate limit 対象にする。

10.3 host

host は暫定制御者であり、長期的にはサーバ主導進行へ寄せる。

必須:

  • host 交替は監査対象。
  • 連続 next に rate limit を設ける。
  • 再生不能申告は複数条件で検証する。
  • admin は host を移譲または解除できる。

10.4 次投稿選択

候補:

  • YouTube/Nico 等、埋め込み再生可能な投稿。
  • 除票・公開抑止・内部検索抑止投稿は除外。
  • noindex のみなら上映可能。ただし room policy で除外可能。
  • 現在投稿は除外。

重み:

  • active user が skip した投稿のタグに基づき penalty を増やす。
  • 上位タグ・親タグも重みに反映する。
  • 同じ投稿が短期間に再選出されないよう cooldown を持つ。

10.5 skip 投票

  • active users の過半数で skip 確定。
  • 必要票数は floor(active_count / 2) + 1
  • 投票は (theatre_id, post_id, user_id) で一意。
  • skip event には voters と当時タグ snapshot を保存する。

10.6 コメント

  • guest も投稿可能。
  • 削除は投稿者本人または admin。
  • 削除済みコメントは no と時刻を残し、本文は null にする。
  • comment spam rate limit を設ける。

11. Gekanator 仕様

11.1 目的

Gekanator は投稿当てゲームである。同時に、質問・回答例・類似投稿補正を蓄積する学習機構である。

11.2 公開段階

段階 対象 目的
調整段階 admin データ作成、質問品質確認、パラメータ調整
試験公開 member または限定 guest UX と負荷確認
公表版 一般 ゲームとして公開

公表版ではスコアや内部候補を見せない。

11.3 質問 kind

kind 条件
tag タグを含むか
source URL host/source
title タイトル文字数・構造
original_year 元投稿年
original_month 元投稿月
original_month_day 月日
title_length タイトル長
post_similarity 特定投稿との類似
material 素材・キャラクター差分

曖昧表現は禁止する。「タイトルが長め」ではなく「タイトルが N 文字以上」とする。

11.4 回答値

意味 score
yes はい 強い正
partial 部分的にそう 弱い正
unknown わからない スコアに混ぜない
probably_no たぶん違う 弱い負
no いいえ 強い負

unknownno 側に混ぜてはいけない。ここを間違えると候補集合が壊れる。

11.5 質問数

  • 原則 25 問までは推測しない。
  • 「続けますか → はい」後も 25 問ルールを維持する。
  • 例外は、1 投稿がほぼ 100% で、2 位以下がほぼ 0% の場合のみ。
  • 例外でも最低数問は出す。
  • 最大 80 問程度で打ち切りを設ける。

11.6 候補・スコア

  • 全投稿スコアは常に保持する。
  • 候補から drop しても score は捨てない。
  • maxScore >= 20 かつ score < 0 の投稿は候補から hard drop してよい。
  • post_similarities.cos は類似投稿補正に必ず乗算する。
  • metadata 条件には類似度補正を掛けない。年・月・タイトル長は直接一致のみ。
  • tag_similarities はタグ質問の類似補助に使うが、確定回答を上書きしない。

11.7 候補復旧

矛盾で候補が 0 になる場合、暗黙に復旧してよい。

  • 次質問の全回答選択肢が 0 件になる場合だけ事前に広げる。
  • ある選択肢だけ 0 件なら、実際に選ばれるまで広げない。
  • 復旧は 6 件、12 件、24 件のように段階的に行う。
  • 復旧したことをユーザに大きく表示しない。

11.8 ユーザ追加質問

  • 追加学習は 2 件単位。
  • 1 play 中、有効なユーザ追加質問を 2/3 程度出す。
  • 無効質問も 1/3 程度は出題し、次学習に使う。
  • unknown の追加質問は即昇格しない。
  • 回答例には user/game/source/weight/answer_counts を保存する。

11.9 AI 変換

AI は質問分類だけでなく、既存投稿への回答補完も行う。

必須:

  • model は GEKANATOR_AI_MODEL 環境変数で差し替え可能。
  • structured output を使う。
  • 低 confidence は pending に留める。
  • AI 生成結果は ai_generated として監査可能にする。
  • 予算上限を持つ。
  • 大量補完は batch または夜間 rake task に寄せる。

半年 500 円程度を守るなら、play ごと即時 AI 実行は高すぎる。cache と差分処理が必須である。

11.10 回答例の統計

gekanator_question_examples は単一回答だけでなく、回答統計を持つ。

属性 仕様
answer_counts answer ごとの集計 JSON
sample_count 集計対象 sample 数。既定 1

同じ question/post/user の回答は一意とし、後続の回答で統計を更新できる。Gekanator の scoring は、単発回答だけでなく集計済み回答の偏りを使えるようにする。

12. Preview API

Preview API は便利だが、サーバから任意 URL にアクセスするため危険である。公表前 P0 として扱う。

12.1 /preview/title

  • URL を受け取り title を取得する。
  • scheme は http/https のみ。
  • redirect 先も検査する。
  • content length 上限を設ける。
  • timeout を短くする。
  • HTML 以外は拒否する。
  • cache を使う。

12.2 /preview/thumbnail

  • headless browser による screenshot を生成する。
  • /preview/title より厳しい rate limit と concurrency 制限を設ける。
  • private IP、loopback、link-local、metadata IP を拒否する。
  • browser context は sandbox 的に隔離する。
  • 出力画像サイズ・生成時間・メモリ使用を制限する。

12.3 SSRF 防御

必須拒否:

  • localhost / loopback
  • private address
  • link-local
  • multicast
  • cloud metadata IP
  • file scheme
  • ftp scheme
  • gopher 等の非 http(s)
  • redirect 後に上記へ到達する URL

12.4 rate limit 初期値

単位 title thumbnail
IP 60 / 10 min 10 / 10 min
user 120 / 10 min 20 / 10 min
URL host 30 / 10 min 10 / 10 min

13. 同期仕様

13.1 Nico sync

  • 外部スクリプトから動画情報を取得する。
  • 投稿がなければ作成する。
  • タグ希望, bot操作, ニコニコ, 動画 を付与する。
  • 生 Nico タグは nico:<raw> で nico tag 化する。
  • nico tag relation に基づき内部タグを付与する。
  • 投稿版と nico tag 版を記録する。
  • 除票済み投稿は自動復帰しない。

13.2 YouTube sync

  • 検索語と playlist から候補動画を取得する。
  • 既存 URL normalizer で重複検出する。
  • 新規投稿に タグ希望, bot操作, YouTube, 動画 を付与する。
  • channel ID が deerjikists にあれば deerjikist tag を付与する。
  • 不明なら ニジラー情報不詳 を付与する。

13.3 bot 操作タグ整理

bot操作 のみ、または未整理の bot 投稿を人手で整理する運用を持つ。

改善仕様:

  • bot操作 投稿の一覧に専用作業画面を持つ。
  • 別投稿からタグ import できる。
  • AI/ルールで楽曲・投稿者・source タグ候補を出す。
  • 作業済みで bot操作 を外す。
  • 進捗件数を dashboard に出す。

14. 検索・sitemap・SEO

14.1 sitemap

sitemap は frontend build 後に生成する。

含める:

  • 通常投稿詳細。
  • 通常タグ詳細。
  • 通常 Wiki。
  • 通常素材。

除外する:

  • 除票。
  • 公開抑止。
  • 索引抑止。
  • 空本文 Wiki。
  • settings、theatre、Gekanator、error pages。
  • query search pages。

14.2 robots meta

route ごとの既定:

route 既定
/posts/:id index。ただし policy により noindex
/tags/:id index。ただし policy により noindex
/wiki/:title index。ただし空本文・policy により noindex
/materials/:id index。ただし policy により noindex
/theatres/:id noindex
/settings noindex
/gekanator noindex または公開後 index 判断
error pages noindex

14.3 内部検索

内部検索と外部検索は分ける。

  • noindex だけなら内部検索には出してよい。
  • 内部検索抑止なら出さない。
  • 公開抑止・除票は出さない。
  • admin は明示 filter で抑止対象を確認できる。

15. API 仕様概要

15.1 共通

  • JSON は snake_case を返す。frontend は camelCase に変換してよい。
  • validation error は type, message, errors, base_errors を返す。
  • conflict は 409 とし、base/current/mine/conflicts を返す。
  • forbidden は 403。
  • not found は 404。
  • 除票・公開抑止は public には 404 を既定とする。

15.2 代表 endpoint

領域 endpoint
posts GET /posts, GET /posts/:id, POST /posts, PUT/PATCH /posts/:id, GET /posts/random, GET /posts/versions, POST/DELETE /posts/:id/viewed
post moderation POST /posts/:id/discard, POST /posts/:id/restore, PUT /posts/:id/index_policy
tags GET /tags, GET /tags/:id, GET /tags/name/:name, GET /tags/name/:name/deerjikists, GET /tags/name/:name/materials, PUT/PATCH /tags/:id, GET /tags/autocomplete, GET /tags/with-depth, GET /tags/versions
tag moderation PUT /tags/:id/index_policy, POST /tags/:id/deprecate, POST /tags/:id/undeprecate
nico tags GET /tags/nico, PUT /tags/nico/:id
wiki GET/POST /wiki, GET/PUT /wiki/:id, GET /wiki/search, GET /wiki/changes, GET /wiki/:id/diff, GET /wiki/title/:title, GET /wiki/title/:title/exists
materials GET /materials, GET /materials/:id, POST /materials, PUT/PATCH /materials/:id, DELETE /materials/:id, GET /materials/download.zip, GET /materials/versions
material sync GET /materials/suppressions, POST /materials/suppressions, sync source 管理 API, sync runner job
material git POST /materials/imports, GET /materials/git/manifest
deerjikists GET/PUT/DELETE /deerjikists/:platform/:code
preview GET /preview/title, GET /preview/thumbnail
users POST /users, POST /users/verify, POST /users/code/renew, GET /users/me, PUT/PATCH /users/:id, GET/PATCH /users/settings, GET /users/theme_slots, PUT /users/theme_slots/:base_theme/:slot_no
theatres 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, question suggestions, ai convert

16. DB ターゲット設計

16.1 追加が必要な主要テーブル/カラム

対象 追加 理由
users last_seen_at, created_ip_address_id, user_agent_hash guest 掃除・bot 対策
users unique index on inheritance_code 認証トークン一意性
posts discarded_at, discarded_by_user_id, discard_reason 除票
posts index_suppressed_at, public_suppressed_at, internal_search_suppressed_at policy cache
tags index_suppressed_at, public_suppressed_at, internal_search_suppressed_at tag policy cache
wiki_pages index_suppressed_at, public_suppressed_at, internal_search_suppressed_at wiki policy cache
materials index_suppressed_at, sha256, byte_size, content_type 素材管理
materials version_no, source fields, normalized_source_key 同期・履歴・復元
material_versions file/source/export snapshot 素材履歴
material_export_items profile, export_path, enabled ZIP/Git export
material_sync_suppressions source_kind, normalized_source_key, reason 同期抑止
material_sync_sources source_kind, source info, enabled 外部素材集同期
material_import_blocks sha256/path block 取込拒否
settings typed settings user portable settings
user_theme_slots base_theme, slot_no, tokens user theme slots
posts, post_versions video_ms 動画長と局所記載履歴
index_policies 新規 noindex/抑止の根拠
post_tag_sections 新規 局所記載
post_remarks 新規 備考欄
post_remark_versions 新規 備考履歴
external_tag_suppression_rules 新規 連携抑止
material_imports 新規 Git import 監査
material_manifest_entries 任意 Git/export manifest cache

16.2 index policy propagation JSON

例:

{
  "tag": {
    "posts": "direct_and_expanded",
    "wiki_page": true,
    "materials": true,
    "text_mentions": true,
    "aliases": true
  },
  "phrase": {
    "match": "exact_token",
    "targets": ["wiki_body", "post_title", "tag_name", "material_title"]
  }
}

17. Frontend 画面仕様

17.1 route

path 仕様
/posts 投稿一覧。ページネーション
/posts/new 投稿作成
/posts/search 投稿検索
/posts/:id 投稿詳細・編集。policy により noindex
/posts/changes 投稿履歴
/tags タグ一覧。廃止・抑止 filter
/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 素材一覧。grid/list、tag filter、media filter、grouping
/materials/new 素材作成
/materials/:id 素材詳細
/materials/changes 素材全体履歴
/materials/:id/changes 素材別履歴
/materials/suppressions 素材同期抑止
/theatres/:id 上映会。noindex
/gekanator Gekanator
/users/settings, /settings 設定。noindex

17.2 UI 共通要件

  • 抑止状態・廃止状態は本文タイトルとは別表示する。
  • noindex は UI 上にも管理者向けに理由を表示する。
  • guest に編集欄は見せても disabled にするか、説明付きで隠す。
  • validation error は field ごとに表示する。
  • 競合時は current/mine/base の差分を表示する。
  • 「保存できたように見えて失敗」は禁止。

17.3 設定 UI

設定画面は保存対象を明確に分ける。

対象 保存先
portable DB settings theme, auto fetch, wiki editor mode
theme slot DB user_theme_slots light/dark 各 3 slot の tokens
device-local localStorage animation, prefetch, tag grouping, active slot, shortcut

未保存変更がある場合、別 tab や別 route への移動前に確認する。保存ボタンが必要な設定は、変更即反映と保存済み状態を UI 上で区別する。

17.4 素材 UI

素材画面は次を持つ。

  • 一覧/追加/抑止/全体履歴/素材別履歴への導線。
  • grid/list 切替。
  • tag_state, media_kind, tag_id, include_descendants, group_by filter。
  • 未分類素材を目立たせる表示。
  • export path の編集。
  • file missing、thumbnail 生成失敗、同期抑止適用を区別して表示。

17.5 スマホタグ D&D

スマホのタグ D&D は縦スクロールと競合しやすい。

必須:

  • touch では長押し後に drag 準備へ入る。
  • 縦方向移動が強い場合は drag 準備をキャンセルし、スクロールを優先する。
  • draggable node と droppable node を分け、横幅・baseline 表示を壊さない。
  • 空カテゴリにも drop slot を出す。
  • animation off の場合、drop animation も出さない。
  • drag 中は元タグを非表示にし、drag 解除後に再表示する。

18. 管理・監査

18.1 管理画面 MVP

公表前に最低限必要:

  • users 一覧、BAN、role 変更。
  • IP BAN。
  • index policies 管理。
  • 除票/復帰。
  • 公開抑止。
  • bot 操作投稿一覧。
  • material uploads 使用量。
  • preview API rate limit 状況。
  • 最近の version/activity。

18.2 監査ログ

履歴テーブルがあるものは version でよい。version がない管理操作は audit log を持つ。

audit_logs
- id
- actor_user_id
- action
- target_type
- target_id
- before_json
- after_json
- reason
- ip_address_id
- created_at

対象:

  • BAN。
  • role 変更。
  • index policy。
  • 除票/復帰。
  • 公開抑止。
  • material import。
  • AI batch 実行。

19. テスト仕様

19.1 Backend 必須テスト

  • 認証・guest 作成・inheritance_code unique。
  • BAN by IP/user。
  • posts CRUD、競合、除票、復帰、index policy。
  • post tag 正規化、廃止タグ拒否、局所記載 parse。
  • tags CRUD、alias、親子循環禁止、廃止、noindex propagation。
  • wiki conflict、body search、autolink、noindex text mention。
  • materials permission、file/url validation、version、Git import。
  • theatre watching、host、skip、comment spam limit。
  • Gekanator score、unknown handling、candidate recovery、similarity cos multiplier。
  • preview SSRF、redirect、private IP、content length、rate limit。
  • sitemap exclusion。

19.2 Frontend 必須テスト

  • route robots meta。
  • post search pagination。
  • conflict UI。
  • tag deprecated display。
  • noindex reason display。
  • local section input and duration number field。
  • material upload validation。
  • Gekanator 25 問 rule。
  • theatre sync and skip UI。
  • validation error rendering。

20. 実装優先順位

P0 公表前に必須

  1. Preview API SSRF / resource abuse 防御。
  2. 素材作成権限を member+ に閉じる。
  3. 投稿除票と通常フロント 404。
  4. 索引抑止基盤 index_policies と route robots meta。
  5. tag-based noindex propagation。
  6. sitemap から noindex/除票を除外。
  7. users.inheritance_code unique index。
  8. POST /users rate limit と bot 対策。
  9. タグ親子循環禁止。
  10. 投稿検索のページネーション安定化。

P1 中核 UX

  1. 投稿履歴 parent posts 表示。
  2. post_versions.tags_json 移行。
  3. 局所記載。
  4. 別投稿からタグ import。
  5. Wiki 本文検索。
  6. material_versions 完成。
  7. Wiki asset。
  8. Gekanator unknown handling / score 引継ぎ / 質問多様化。
  9. 上映会上位タグ表示。
  10. bot 操作タグ整理画面。

P2 拡張

  1. 素材 Git mirror/export/import。
  2. URL-only 素材 guest 開放検討。
  3. AI による Gekanator 回答補完。
  4. 外部タグ連携抑止。
  5. 個人用メモ。
  6. 限定公開。
  7. 申請フォーム。
  8. Pixiv / bilibili / TikTok / ニジカ投稿局埋め込み。

21. 質問票

この章は、実装前に確認したい事項である。未回答でも本文の暫定仕様で製造は始められる。

21.1 除票・抑止

ID 質問 暫定仕様
Q-DEL-001 除票済み投稿の public 詳細は 404 と 410 のどちらにするか 404
Q-DEL-002 member は除票済み投稿を見られるべきか 不可。admin もフロント上は非表示。削除と同等
Q-DEL-003 除票理由を一般に表示するか 表示しない
Q-IDX-001 noindex 対象ページを内部検索には出すか 出す。内部検索抑止は別指定
Q-IDX-002 tag noindex は子タグ・親タグへ伝播するか 投稿判定では direct + expanded。タグ自体の親子ページへは伝播しない
Q-IDX-003 tag 名の本文一致は alias も対象か 対象
Q-IDX-004 phrase rule は substring も許可するか admin 明示時のみ
Q-IDX-005 noindex 理由を admin UI 以外に表示するか admin UI のみ

21.2 投稿

ID 質問 暫定仕様
Q-POST-001 thumbnail_base と添付 thumbnail の優先順位 添付 thumbnail 優先、なければ thumbnail_base
Q-POST-002 親投稿の循環禁止を入れるか 入れる
Q-POST-003 局所記載の時刻単位 API は ms、UI は m:ss 入力可
Q-POST-004 動画 タグ以外でも duration を持てるか UI 非表示、API は保存しない。ただね、かぅいふのは video_posts みたいな別テーブル切りたいとこなんだよなぁ……検討中

21.3 タグ

ID 質問 暫定仕様
Q-TAG-001 廃止タグを既存投稿から自動除去するか しない。表示で除外(検索は除外しない)
Q-TAG-002 nico タグに索引抑止は可能か 可能。廃止とは別
Q-TAG-003 tag_name_sanitisation_rules は nico タグにも適用するか 適用する。ただし nico: prefix 構造は保つ

21.4 素材

ID 質問 暫定仕様
Q-MAT-001 Git mirror は public にするか public。ただし公開抑止素材は含めない
Q-MAT-002 Git に実体ファイルを含めるか 原則含めない。manifest + sync script
Q-MAT-003 contributor push の権限は member 全員か member 以上。ただし import job で検証
Q-MAT-004 ZIP には noindex 素材を含めるか 含める。noindex は検索制御であり閲覧禁止ではない

21.5 Gekanator

ID 質問 暫定仕様
Q-GEK-001 公表版で質問追加を guest に許すか 許すが rate limit と moderation 後昇格
Q-GEK-002 AI 補完を即時実行するか しない。batch/cached
Q-GEK-003 noindex 投稿を Gekanator 候補に含めるか 含める。公開抑止・除票は除外

21.6 上映会

ID 質問 暫定仕様
Q-TH-001 noindex 投稿を上映会候補に含めるか 含める
Q-TH-002 公開抑止投稿を上映会候補に含めるか 含めない
Q-TH-003 host を完全廃止するか すぐには廃止しない。サーバ主導へ漸進

22. 受入条件チェックリスト

公表可能と判断するには、最低限次を満たす。

  • Preview API が private IP / redirect / large response / browser abuse を防ぐ。
  • 投稿除票が一覧・検索・詳細・sitemap・Gekanator・上映会から一貫して除外される。
  • noindex が post/tag/wiki/material に対して API と frontend の両方で効く。
  • tag-based noindex が本文中の tag name/alias 出現にも効く。
  • robots.txt で noindex 対象を塞いでいない。
  • sitemap から noindex 対象が消える。
  • タグ廃止と索引抑止が別状態として扱われる。
  • nico タグは廃止できない。
  • 素材 file upload は member+ 限定。
  • material_versions が tag/url/blob/user を記録する。
  • 局所記載 parse が仕様通り。
  • Gekanator で unknown が no 扱いにならない。
  • Gekanator 類似補正に post_similarities.cos が乗る。
  • users.inheritance_code が unique。
  • タグ親子循環禁止。
  • RSpec / Vitest / build / lint が通る。

23. Codex 作業分解案

23.1 P0-1: IndexPolicyResolver

目的:

  • index_policies を導入し、post/tag/wiki/material/phrase の noindex 判定を backend で一元化する。

成果物:

  • migration。
  • model。
  • service IndexPolicyResolver
  • post/tag/wiki/material repr に index_policy を追加。
  • sitemap generator が API または manifest から除外できるようにする。
  • frontend Helmet が robots を反映。
  • tests。

23.2 P0-2: 投稿除票

目的:

  • posts を論理削除できるようにし、通常面から除外する。

成果物:

  • migration。
  • routes/controller actions。
  • admin UI。
  • post_versions discard/restore。
  • search/list/detail/similarity/theatre/gekanator/sitemap 除外。
  • tests。

23.3 P0-3: Preview API hardening

目的:

  • SSRF と resource abuse を潰す。

成果物:

  • URL validator。
  • DNS resolve 後 IP allow/deny。
  • redirect chain validation。
  • timeout/size/concurrency/rate limit。
  • tests。

23.4 P1: 局所記載

目的:

  • タグ[開始-終了] を保存・表示・履歴化する。

成果物:

  • parser。
  • table。
  • post edit UI。
  • duration field。
  • post version snapshot。
  • tests。

24. 付録: 現行資材からの主な読み取り

この章は本文仕様の根拠メモである。本文は実装済み/未実装を区別しないが、製造時の迷子防止のため現行傾向を残す。

  • Rails routes には posts, tags, wiki, materials, theatres, gekanator, preview, users, deerjikists が揃っている。
  • schema version は 2026_07_05_000000 相当まで進んでいる。
  • tags.deprecated_attag_versions.deprecated_at は schema に入っている。
  • tags には deprecated_at IS NULL OR category <> 'nico' の check constraint がある。
  • posts.video_mspost_versions.video_ms は schema に入っている。
  • post_tag_sections は複合主キー post_id, tag_id, begin_ms で存在する。
  • post_versionsdiscard / restore event_type を許容しているが、posts 本体の除票 column は未整備である。
  • materialsversion_no, source fields, normalized_source_key を持つ。
  • material_versions は file/source/export snapshot を持つ。
  • material_sync_sources, material_sync_suppressions, material_export_items, material_import_blocks は schema 上存在する。
  • settings は typed user settings として再構築されている。
  • user_theme_slots は light/dark 各 3 slot の JSON token 保存枠として存在する。
  • wiki_assets は schema 上存在する。
  • frontend は react-helmet-async を使うため route ごとの meta robots 制御が可能である。
  • ErrorScreen, theatre, settings, 空 Wiki など一部 route は既に noindex を出す傾向がある。
  • frontend sitemap generator が存在するため、index policy と接続すべきである。
  • DB 上、users と versions と similarities の件数が大きく、掃除・履歴・抑止を後回しにすると運用で詰まる。