目次
- 0. 本書の位置づけ
- 1. システム概要
- 2. 技術構成
- 3. 認証・ユーザ・権限
- 3A. ユーザ設定・テーマ仕様
- 4. 公開・除票・抑止
- 4.1 状態の分離
- 4.2 投稿の除票
- 4.3 索引抑止
- 4.4 索引抑止の対象
- 4.5 タグベース索引抑止
- 4.6 タグ名・別名・本文一致ルール
- 4.7 IndexPolicyResolver
- 4.8 推奨データ構造
- 4.9 公開抑止
- 4.10 内部検索抑止
- 5. 投稿仕様
- 6. タグ仕様
- 7. Nico タグ・外部連携
- 8. Wiki 仕様
- 9. 素材仕様
- 9.1 基本
- 9.2 素材タグ体系
- 9.3 権限
- 9.4 作成・更新
- 9.5 一覧・検索
- 9.6 表現
- 9.7 履歴
- 9.8 object storage と thumbnail
- 9.9 export path と ZIP download
- 9.10 同期抑止
- 9.11 取込ブロック
- 9.12 同期元
- 9.13 Git 管理と配布
- 10. 上映会仕様
- 11. Gekanator 仕様
- 11.1 目的
- 11.2 公開段階
- 11.3 質問 kind
- 11.4 回答値
- 11.5 質問数
- 11.6 候補・スコア
- 11.7 候補復旧
- 11.8 ユーザ追加質問
- 11.9 AI 変換
- 11.10 回答例の統計
- 12. Preview API
- 13. 同期仕様
- 14. 検索・sitemap・SEO
- 15. API 仕様概要
- 16. DB ターゲット設計
- 17. Frontend 画面仕様
- 18. 管理・監査
- 19. テスト仕様
- 20. 実装優先順位
- 21. 質問票
- 22. 受入条件チェックリスト
- 23. Codex 作業分解案
- 24. 付録: 現行資材からの主な読み取り
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_ms と post_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 目的
- 外部プラットフォーム上の関連コンテンツを横断的に発見できるようにする。
- ニコニコ等のタグ数制限を超え、より豊かなタグ体系を提供する。
- タグ・親子関係・別名・Wiki・素材を通じて、作品群の文脈を保持する。
- 上映会により共同視聴を支援する。
- Gekanator により投稿の識別質問・類似性・タグ付け知識を蓄積する。
- 問題化し得る語・ページ・投稿について、削除だけでなく索引抑止・公開抑止・除票を選べるようにする。
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 認証方式
通常ログインではなく、引継ぎコードを認証トークンとして用いる。
- 初回アクセス時、フロントは
localStorage.user_codeを確認する。 - 値があれば
POST /users/verifyで検証する。 - 無効または未存在なら
POST /usersで guest user を作成する。 - 以後、API 呼び出しでは
X-Transfer-Codeにコードを入れる。 - バックエンドは
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_at、users.user_agent_hash、users.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
端末依存の動作設定は localStorage の btrc_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.txtのDisallowで塞ぐことは原則禁止する。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 とする。
- 当該タグの詳細ページ。
- 当該タグに対応する Wiki ページ。
- 当該タグに対応する素材ページまたは素材一覧。
- 当該タグが直接付与された投稿。
- 親タグ展開により当該タグを含むとみなされる投稿。
- 当該タグ名または別名がページの文章中に現れる Wiki ページ。
- 当該タグ名または別名がタイトル、説明、タグチップ、親子タグ表示、関連タグ表示に現れるページ。
この仕様は、単に投稿タグを見るだけでは不足する。ページ本文に文字列として現れる場合も検索エンジンには拾われるためである。
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_versions に create snapshot を記録する。
5.4 更新と競合制御
PUT/PATCH /posts/:id は member 以上が実行できる。
base_version_noを要求する。- 現在版と異なる場合は 409 conflict を返す。
forceは admin または明示権限者のみ許可する。mergeは競合のない集合差分のみ自動統合する。forceとmergeの同時指定は禁止する。
競合判定対象:
- 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_names と tags に分離する。
| テーブル | 役割 |
|---|---|
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 正規化
タグ入力は次を通す。
- 空白・空文字除去。
- Unicode 正規化。
tag_name_sanitisation_rules適用。- prefix 解析。
- 別名解決。
- 存在しない tag_name/tag の作成。
- 廃止タグ拒否。
- nico 手入力拒否。
タグ希望/ニジラー情報不詳の自動付与。- 親タグ展開。
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 /materials と PUT/PATCH /materials/:id は次を行う。
- current user が member 以上であることを確認する。
- tag 名を解決し、存在しなければ
materialcategory の tag を作る。 - file がある場合、SHA256 を計算する。
material_import_blocksに該当する SHA256 は拒否する。- Active Storage blob に SHA256 metadata を持たせる。
- material 本体を保存する。
- export path が指定されていれば
material_export_itemsを upsert する。 material_versionsを記録する。- 画像または動画なら 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/versions は material_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_riskcopyright_takedownadult_or_sensitivepersonal_informationmalware_or_dangerous_fileduplicate_or_low_qualitysource_owner_requestother
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_EMAILGOOGLE_DRIVE_PRIVATE_KEYまたはGOOGLE_DRIVE_PRIVATE_KEY_PATHGOOGLE_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 |
いいえ | 強い負 |
unknown を no 側に混ぜてはいけない。ここを間違えると候補集合が壊れる。
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_byfilter。- 未分類素材を目立たせる表示。
- 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 公表前に必須
- Preview API SSRF / resource abuse 防御。
- 素材作成権限を member+ に閉じる。
- 投稿除票と通常フロント 404。
- 索引抑止基盤
index_policiesと route robots meta。 - tag-based noindex propagation。
- sitemap から noindex/除票を除外。
users.inheritance_codeunique index。POST /usersrate limit と bot 対策。- タグ親子循環禁止。
- 投稿検索のページネーション安定化。
P1 中核 UX
- 投稿履歴 parent posts 表示。
post_versions.tags_json移行。- 局所記載。
- 別投稿からタグ import。
- Wiki 本文検索。
- material_versions 完成。
- Wiki asset。
- Gekanator unknown handling / score 引継ぎ / 質問多様化。
- 上映会上位タグ表示。
- bot 操作タグ整理画面。
P2 拡張
- 素材 Git mirror/export/import。
- URL-only 素材 guest 開放検討。
- AI による Gekanator 回答補完。
- 外部タグ連携抑止。
- 個人用メモ。
- 限定公開。
- 申請フォーム。
- 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_atとtag_versions.deprecated_atは schema に入っている。tagsにはdeprecated_at IS NULL OR category <> 'nico'の check constraint がある。posts.video_msとpost_versions.video_msは schema に入っている。post_tag_sectionsは複合主キーpost_id, tag_id, begin_msで存在する。post_versionsはdiscard/restoreevent_type を許容しているが、posts 本体の除票 column は未整備である。materialsはversion_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 の件数が大きく、掃除・履歴・抑止を後回しにすると運用で詰まる。