From 87276c30a457fe21b2443135088b79c1427efd81 Mon Sep 17 00:00:00 2001 From: tkgstrator <29420801+tkgstrator@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:29:04 +0000 Subject: [PATCH] feat(scenario): add investigable places and death-time disclosure routes Backfill 22 reviewed scenarios with place-based findings alongside character routes, mark death-time evidences with dual disclosure paths, and add migrations 0021/0022 plus authoring and public-layer test coverage for the new invariants. Co-Authored-By: Claude --- .../db/scenario-current-authoring.test.ts | 125 +- __tests__/db/scenario-public-layer.test.ts | 10 + .../0021_scenario-investigation-upgrade.sql | 381 ++++++ ...0022_scenario-place-public-copy-safety.sql | 47 + db/migrations/meta/0021_snapshot.json | 1190 +++++++++++++++++ db/migrations/meta/0022_snapshot.json | 1190 +++++++++++++++++ db/migrations/meta/_journal.json | 14 + .../avalanche-monastery-bell-window.yaml | 28 +- db/scenarios/cave-lab-locator-cart.yaml | 14 +- .../coldcase-lodge-borrowed-memory.yaml | 37 +- .../flood-archive-self-locking-vault.yaml | 14 +- db/scenarios/gallery-blue-frame.yaml | 14 +- .../generation-ship-staggered-dawn.yaml | 13 + db/scenarios/ink-stained-contract.yaml | 14 + ...landslide-clock-museum-eleven-minutes.yaml | 28 +- .../landslide-hotel-frosted-silhouette.yaml | 13 + db/scenarios/midnight-radio-rerun.yaml | 27 +- .../quarantine-clinic-borrowed-badge.yaml | 28 +- .../shanghai-warehouse-carbon-copy.yaml | 13 + .../snowbound-farm-morning-chores.yaml | 13 + .../snowbound-gallery-wrong-crime-scene.yaml | 14 +- .../snowbound-studio-roomtone-loop.yaml | 14 +- .../storm-hydropower-rising-walkway.yaml | 14 +- db/scenarios/storm-island-scheduled-mail.yaml | 13 + .../storm-mountain-inn-echoed-cane.yaml | 27 +- .../storm-planetarium-reflected-witness.yaml | 28 +- .../thunder-cablecar-occupied-cabin.yaml | 14 +- .../victorian-underground-last-telegram.yaml | 33 + 28 files changed, 3344 insertions(+), 26 deletions(-) create mode 100644 db/migrations/0021_scenario-investigation-upgrade.sql create mode 100644 db/migrations/0022_scenario-place-public-copy-safety.sql create mode 100644 db/migrations/meta/0021_snapshot.json create mode 100644 db/migrations/meta/0022_snapshot.json diff --git a/__tests__/db/scenario-current-authoring.test.ts b/__tests__/db/scenario-current-authoring.test.ts index 157fb95..1d25a59 100644 --- a/__tests__/db/scenario-current-authoring.test.ts +++ b/__tests__/db/scenario-current-authoring.test.ts @@ -112,6 +112,46 @@ const LEGACY_TITLES = new Set([ const TIME_DECEPTION_PATTERN = /(死亡時刻|生存時刻|事件時刻|死亡後)/ +// 43本を真相・時系列・嘘・証拠まで読み直したうえで、 +// 「死後の出来事を生存証明に見せる/時計の基準をずらす」こと自体が解法の軸になる事件。 +// 文言だけの正規表現では取りこぼすので、レビュー結果を明示しておく。 +const REVIEWED_DEATH_TIME_CASES = new Set([ + 'avalanche-monastery-bell-window.yaml', + 'coldcase-lodge-borrowed-memory.yaml', + 'generation-ship-staggered-dawn.yaml', + 'ink-stained-contract.yaml', + 'landslide-clock-museum-eleven-minutes.yaml', + 'landslide-hotel-frosted-silhouette.yaml', + 'midnight-radio-rerun.yaml', + 'quarantine-clinic-borrowed-badge.yaml', + 'shanghai-warehouse-carbon-copy.yaml', + 'snowbound-farm-morning-chores.yaml', + 'storm-island-scheduled-mail.yaml', + 'storm-mountain-inn-echoed-cane.yaml', + 'storm-planetarium-reflected-witness.yaml', + 'victorian-underground-last-telegram.yaml', +]) + +// 場所そのものの設備・構造を確かめる行為が、人物への聞き込みとは別の推理になる事件。 +// 「places が新機能だから」ではなく、全件レビューで一手を使う価値があると判断したものだけ。 +const REVIEWED_PLACE_CASES = new Set([ + 'avalanche-monastery-bell-window.yaml', + 'cave-lab-locator-cart.yaml', + 'coldcase-lodge-borrowed-memory.yaml', + 'flood-archive-self-locking-vault.yaml', + 'gallery-blue-frame.yaml', + 'landslide-clock-museum-eleven-minutes.yaml', + 'midnight-radio-rerun.yaml', + 'quarantine-clinic-borrowed-badge.yaml', + 'snowbound-gallery-wrong-crime-scene.yaml', + 'snowbound-studio-roomtone-loop.yaml', + 'storm-hydropower-rising-walkway.yaml', + 'storm-mountain-inn-echoed-cane.yaml', + 'storm-planetarium-reflected-witness.yaml', + 'thunder-cablecar-occupied-cabin.yaml', + 'victorian-underground-last-telegram.yaml', +]) + const isRecord = (value: unknown): value is Record => typeof value === 'object' && value !== null && !Array.isArray(value) @@ -131,13 +171,27 @@ describe('scenario current authoring guide', () => { if (victim.foundAt === undefined) violations.push(`${file}: victim.foundAt`) if (victim.foundIn === undefined) violations.push(`${file}: victim.foundIn`) - if (victim.causeOfDeath === undefined) violations.push(`${file}: victim.causeOfDeath`) - if (victim.findings.length < 2) violations.push(`${file}: victim.findings < 2`) + if (file !== 'coldcase-lodge-borrowed-memory.yaml') { + if (victim.causeOfDeath === undefined) violations.push(`${file}: victim.causeOfDeath`) + if (victim.findings.length < 2) violations.push(`${file}: victim.findings < 2`) + } } expect(violations).toEqual([]) }) + test('過去の未解決事件は現在の遺体ではなく旧捜査資料を調べる', async () => { + const item = (await scenarios()).find( + ({ file }) => file === 'coldcase-lodge-borrowed-memory.yaml', + ) + expect(item).toBeDefined() + if (item === undefined) return + + expect(item.scenario.victim?.causeOfDeath).toBeUndefined() + expect(item.scenario.victim?.findings).toEqual([]) + expect(item.scenario.places.length).toBeGreaterThan(0) + }) + test('動機へ繋がる手掛かりが最低1つ victim source から取れる', async () => { const violations: string[] = [] @@ -156,8 +210,18 @@ describe('scenario current authoring guide', () => { revelation.sources.some((source) => source.type === 'victim'), ) - if (!evidenceHasMotiveClue && !revelationHasMotiveClue) { - violations.push(`${file}: no victim-sourced motive clue`) + const locationHasMotiveClue = scenario.evidences.some( + (evidence) => + evidence.sources.some((source) => source.type === 'location') && + evidence.supports.some((factId) => motiveFacts.has(factId)), + ) + + if ( + !evidenceHasMotiveClue && + !revelationHasMotiveClue && + !(file === 'coldcase-lodge-borrowed-memory.yaml' && locationHasMotiveClue) + ) { + violations.push(`${file}: no stable motive clue`) } } @@ -228,8 +292,9 @@ describe('scenario current authoring guide', () => { const violations: string[] = [] for (const { file, scenario } of await scenarios()) { + const reviewedAsTimeCase = REVIEWED_DEATH_TIME_CASES.has(file) if ( - TIME_DECEPTION_PATTERN.test(scenario.solution.method) && + (reviewedAsTimeCase || TIME_DECEPTION_PATTERN.test(scenario.solution.method)) && scenario.victim?.estimatedDeathAt === undefined ) { violations.push(`${file}: victim.estimatedDeathAt`) @@ -239,6 +304,56 @@ describe('scenario current authoring guide', () => { expect(violations).toEqual([]) }) + test('死亡推定時刻を置いた事件には、それを手掛かりから開ける経路がある', async () => { + const violations: string[] = [] + + for (const { file, scenario } of await scenarios()) { + if (scenario.victim?.estimatedDeathAt === undefined) continue + + const marked = scenario.evidences.filter((evidence) => evidence.revealsDeathTime) + if (marked.length === 0) { + violations.push(`${file}: no revealsDeathTime evidence`) + continue + } + + const routes = new Set( + marked.flatMap((evidence) => + evidence.sources.map((source) => `${source.type}:${source.id}`), + ), + ) + if (routes.size < 2) { + violations.push(`${file}: death-time routes < 2`) + } + } + + expect(violations).toEqual([]) + }) + + test('精読で場所調査が有効と判断した事件には、空振りしない調査場所を置く', async () => { + const violations: string[] = [] + + for (const { file, scenario } of await scenarios()) { + if (REVIEWED_PLACE_CASES.has(file) && scenario.places.length === 0) { + violations.push(`${file}: places missing`) + } + + for (const place of scenario.places) { + const hasSource = + scenario.evidences.some((evidence) => + evidence.sources.some((source) => source.type === 'location' && source.id === place.id), + ) || + scenario.revelations.some((revelation) => + revelation.sources.some( + (source) => source.type === 'location' && source.id === place.id, + ), + ) + if (!hasSource) violations.push(`${file}: place:${place.id}: no source route`) + } + } + + expect(violations).toEqual([]) + }) + test('物証で時刻が裏付けられる出来事は record を持ち、record は物証か第三者観察に基づく', async () => { const violations: string[] = [] diff --git a/__tests__/db/scenario-public-layer.test.ts b/__tests__/db/scenario-public-layer.test.ts index 51f00b6..a5b78cb 100644 --- a/__tests__/db/scenario-public-layer.test.ts +++ b/__tests__/db/scenario-public-layer.test.ts @@ -15,6 +15,9 @@ const titleSpoilerPattern = const publicIntroductionSpoilerPattern = /(アリバイ|秘密|隠し|不正|証拠として|人物識別|思い込み|標準時|時系列|休憩延長|記録から外|機器表示|杖の音|絶対視|バッジ)/ +const placePublicSpoilerPattern = + /(自動施錠|閉じるだけで|反射|映り込|重量センサー|人物を識別|送信者を識別|十一分|時刻のずれ|自律飛行|繰り返す経路|実行ログ|電源履歴|通過履歴|新しい打痕|台紙片)/ + const scenarioFiles = async (): Promise => (await readdir(SCENARIO_DIR)).filter((name) => name.endsWith('.yaml')).sort() @@ -44,6 +47,13 @@ describe('scenario public layer', () => { violations.push(`${file}: publicIntroduction:${character.name}`) } } + + for (const place of scenario.places) { + const publicPlaceText = `${place.name}\n${place.shortName}\n${place.introduction}\n${place.situation}` + if (placePublicSpoilerPattern.test(publicPlaceText)) { + violations.push(`${file}: place:${place.id}`) + } + } } expect(violations).toEqual([]) diff --git a/db/migrations/0021_scenario-investigation-upgrade.sql b/db/migrations/0021_scenario-investigation-upgrade.sql new file mode 100644 index 0000000..07ece88 --- /dev/null +++ b/db/migrations/0021_scenario-investigation-upgrade.sql @@ -0,0 +1,381 @@ +-- Upgrade the reviewed scenarios for investigable places and disclosed death estimates. + +-- Existing scenario, character, and evidence UUIDs are preserved whenever rows already exist. + +UPDATE scenarios SET places = '[{"id":"choir-access","name":"聖歌席裏の点検口","shortName":"点検口","introduction":"鐘の修復作業で使われた、聖歌席裏の設備点検口","situation":"修復用の資材札が残る、小さな点検区画"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"choir-access","findings":[{"id":"test-line-still-present","statement":"点検口の奥に仮設試験線が残り、工事タグも撤去済みの状態にはなっていない。","requires":{"revelations":[],"evidences":[]}},{"id":"test-line-reaches-bell","statement":"仮設試験線は、鐘塔へ入らず一階側から鐘の作動確認を行える配線になっている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一'); + +UPDATE scenarios SET places = '[{"id":"survey-zone","name":"自動測量区画","shortName":"測量区画","introduction":"測量カートの校正と位置タグ確認を行う研究区画","situation":"カートと位置タグの充電台が壁沿いに並んでいる"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '岩代圭吾'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"survey-zone","findings":[{"id":"cart-tag-fastener","statement":"測量カートの収納ベルトには、小型の位置タグを固定できる留め具と新しい擦れ跡がある。","requires":{"revelations":[],"evidences":[]}},{"id":"cart-track-overlay","statement":"端末に残るカートの走行軌跡と位置タグの軌跡は、同じ時間帯に同じ経路を通っている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '岩代圭吾'); + +UPDATE scenarios SET places = '[{"id":"case-archive","name":"旧捜査資料箱","shortName":"旧捜査資料","introduction":"1979年事件の調書・検視記録・現場写真をまとめた保管箱","situation":"黄ばんだ封筒と写真袋が、作成日順に綴じ直されている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '野上修一'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"case-archive","findings":[{"id":"original-autopsy-time","statement":"当時の検視記録には、死亡は22時05分ごろと見積もられた旨が記されている。","requires":{"revelations":[],"evidences":[]}},{"id":"original-sighting-source","statement":"事件直後の供述調書で22時30分の暖炉前目撃を自分の体験として述べているのは、一人だけである。","requires":{"revelations":[],"evidences":[]}},{"id":"old-expense-ledger","statement":"押収資料の仕入れ帳には、倉田の担当欄の金額を野上が事件当日に再確認した印が残っている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '野上修一'); + +UPDATE scenarios SET places = '[{"id":"rare-vault","name":"希少資料庫","shortName":"資料庫","introduction":"貴重資料を保管する、自動施錠式の資料庫","situation":"重い扉が閉じ、廊下側には開錠用の鍵穴がある"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '今泉孝臣'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"rare-vault","findings":[{"id":"self-locking-latch","statement":"扉は廊下側から開けるときだけ鍵を使い、外へ出て閉じればラッチが自動で掛かる構造になっている。","requires":{"revelations":[],"evidences":[]}},{"id":"door-contact-window","statement":"扉センサーには21時09分から21時22分まで開放が続き、その後に閉じた記録が残っている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '今泉孝臣'); + +UPDATE scenarios SET places = '[{"id":"restoration-room","name":"修復室","shortName":"修復室","introduction":"紫外線検査装置と修復用の作業台がある部屋","situation":"検査装置は停止し、作業台だけが照明に浮かんでいる"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '鳥羽薫'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"restoration-room","findings":[{"id":"uv-power-gap","statement":"紫外線検査装置の履歴には、18時37分から18時51分まで電源が切れていた空白がある。","requires":{"revelations":[],"evidences":[]}},{"id":"uv-resume-time","statement":"装置は18時51分に再起動しており、その前の検査状態が連続していたわけではない。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '鳥羽薫'); + +UPDATE scenarios SET places = '[{"id":"master-clock","name":"親時計盤","shortName":"親時計","introduction":"館内の展示時計と時報へ基準時刻を配る同期盤","situation":"保守扉に夕方の同期試験票が挟まれたままになっている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"master-clock","findings":[{"id":"master-clock-offset","statement":"親時計盤の補正値は正しい時刻より十一分進む設定になっている。","requires":{"revelations":[],"evidences":[]}},{"id":"independent-security-clock","statement":"防災端末の時刻は親時計盤と別系統で、同じ瞬間を十一分早い数字で記録している。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一'); + +UPDATE scenarios SET places = '[{"id":"playout-room","name":"自動送出室","shortName":"送出室","introduction":"収録音源と深夜番組の放送順を管理する送出卓","situation":"深夜番組の送出キューが画面に残っている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '大門修一'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"playout-room","findings":[{"id":"midnight-queued-audio","statement":"午前零時の番組冒頭には、事前収録された音声ファイルが自動送出対象として登録されている。","requires":{"revelations":[],"evidences":[]}},{"id":"playout-execution-log","statement":"実行ログでは、午前零時の音声は人のマイク操作ではなく送出卓から自動再生されている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '大門修一'); + +UPDATE scenarios SET places = '[{"id":"badge-reader","name":"検査廊下の認証端末","shortName":"認証端末","introduction":"防護区画を通る管理バッジの認証端末","situation":"通過履歴が時刻とバッジ番号の順で表示されている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '星名悟'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"badge-reader","findings":[{"id":"reader-records-badge","statement":"端末が保存しているのは通過したバッジ番号で、装着者の顔や氏名を記録する機能はない。","requires":{"revelations":[],"evidences":[]}},{"id":"orange-badge-entry","statement":"21時18分には橙色の管理バッジが検査廊下を通過した記録が残っている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '星名悟'); + +UPDATE scenarios SET places = '[{"id":"framing-room","name":"額装作業室","shortName":"額装室","introduction":"作品台紙の加工と搬送準備を行う作業室","situation":"清掃後の床に、細かな紙粉がまだ残っている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '荻原直哉'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"framing-room","findings":[{"id":"framing-paper-traces","statement":"床に散った紙片は、その夜に荻原が確認していた作品台紙と同じ材質である。","requires":{"revelations":[],"evidences":[]}},{"id":"cart-used-again","statement":"清掃後に戻された作品搬送台車には、その後もう一度動かされた車輪跡が残っている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '荻原直哉'); + +UPDATE scenarios SET places = '[{"id":"patch-bay","name":"第2ブース監視席","shortName":"監視席","introduction":"録音入力を切り替えるパッチ盤と収録端末の席","situation":"直前の収録セッションの配線がそのまま残っている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '冬木圭介'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"patch-bay","findings":[{"id":"recorder-loop-route","statement":"第2ブースの録音入力は生マイクではなく、四十七秒の音声素材を繰り返す経路へ切り替えられている。","requires":{"revelations":[],"evidences":[]}},{"id":"waveform-identical","statement":"収録波形は空調音や小さな物音まで四十七秒ごとに同じ形を繰り返している。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '冬木圭介'); + +UPDATE scenarios SET places = '[{"id":"old-walkway","name":"旧保守歩廊","shortName":"旧歩廊","introduction":"排水区画を通り、旧制御室の接続口へ続く古い通路","situation":"現在は増水で水に覆われ、入口から先へ進めない"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '峰岸達也'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"old-walkway","findings":[{"id":"walkway-connects-control","statement":"設備図と入口の表示から、この歩廊は正面扉を通らず旧制御室へ入れる接続口まで続いている。","requires":{"revelations":[],"evidences":[]}},{"id":"reddish-silt-floor","statement":"水際より手前の床には、この歩廊周辺に特有の赤褐色の堆積泥が残っている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '峰岸達也'); + +UPDATE scenarios SET places = '[{"id":"old-stairs","name":"厨房脇の旧階段","shortName":"旧階段","introduction":"二階廊下へ木の隔壁一枚で接する、普段使われない階段","situation":"古い木造の隔壁と手すりがそのまま残されている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"old-stairs","findings":[{"id":"partition-carries-taps","statement":"手すりを軽く叩くと、木の中空隔壁を通って二階廊下側へ乾いた音がよく響く。","requires":{"revelations":[],"evidences":[]}},{"id":"cane-height-marks","statement":"隔壁には、桐谷の杖の金属部分と高さの合う新しい打痕が残っている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介'); + +UPDATE scenarios SET places = '[{"id":"observation-glass","name":"投影室の観察ガラス","shortName":"観察ガラス","introduction":"投影室と廊下を隔てる、大型の観察窓","situation":"閉館後の点検で投影室側の主照明が落とされている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"observation-glass","findings":[{"id":"corridor-reflection","statement":"投影室を暗くして廊下側を明るくすると、ガラスには廊下側に立つ人物の像が強く映り込む。","requires":{"revelations":[],"evidences":[]}},{"id":"witness-position-replay","statement":"21時35分の立ち位置を再現すると、廊下にいた人物の像が投影室内の人影のように重なる。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠'); + +UPDATE scenarios SET places = '[{"id":"emergency-cabin","name":"非常用搬器","shortName":"非常搬器","introduction":"点検席と乗員表示センサーを備えた非常用の搬器","situation":"営業終了後の点検位置で停止している"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '高瀬修司'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"emergency-cabin","findings":[{"id":"seat-senses-weight","statement":"点検席のセンサーは人物を識別せず、一定以上の重量が掛かると「乗員1」を表示する。","requires":{"revelations":[],"evidences":[]}},{"id":"tool-case-triggers-seat","statement":"保守用工具ケースだけを点検席へ置いても、監視盤の表示は「乗員1」へ切り替わる。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬修司'); + +UPDATE scenarios SET places = '[{"id":"shaft-two-telegraph","name":"第二立坑の電信機","shortName":"第2電信","introduction":"二つの立坑を結ぶ、工事連絡用の電信機","situation":"送信キーと符号表が作業机の上に残されている"}]' WHERE id = (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル'); + +UPDATE scenario_truths SET place_findings = '[{"placeId":"shaft-two-telegraph","findings":[{"id":"sender-not-recorded","statement":"電信機と受信簿が残すのは電文と受信時刻だけで、第二立坑で誰が送信キーを操作したかは記録されない。","requires":{"revelations":[],"evidences":[]}},{"id":"mark-can-be-copied","statement":"過去の工程電文にはヘイルが使う短い末尾符号が何度も残り、作業関係者が見られる状態になっている。","requires":{"revelations":[],"evidences":[]}}]}]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル'); + +UPDATE scenarios SET victim_estimated_death_at = '21:05' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一'); + +UPDATE scenarios SET victim_estimated_death_at = '22:05' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '野上修一'); + +UPDATE scenarios SET victim_estimated_death_at = '05:57' WHERE id = (SELECT id FROM scenarios WHERE victim_name = 'ミラ・ヴォス'); + +UPDATE scenarios SET victim_estimated_death_at = '20:41' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '石橋礼司'); + +UPDATE scenarios SET victim_estimated_death_at = '20:28' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一'); + +UPDATE scenarios SET victim_estimated_death_at = '21:05' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '長峰宗一'); + +UPDATE scenarios SET victim_estimated_death_at = '23:48' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '大門修一'); + +UPDATE scenarios SET victim_estimated_death_at = '21:08' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '星名悟'); + +UPDATE scenarios SET victim_estimated_death_at = '21:52' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '周文海'); + +UPDATE scenarios SET victim_estimated_death_at = '2026-01-15T22:10:00+09:00' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '佐久間隆志'); + +UPDATE scenarios SET victim_estimated_death_at = '21:28' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '榊原宗一'); + +UPDATE scenarios SET victim_estimated_death_at = '21:08' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介'); + +UPDATE scenarios SET victim_estimated_death_at = '21:24' WHERE id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠'); + +UPDATE scenarios SET victim_estimated_death_at = '22:03' WHERE id = (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル'); + +UPDATE scenarios SET victim_investigable = 0 WHERE id = (SELECT id FROM scenarios WHERE victim_name = '野上修一'); + +UPDATE scenario_truths SET victim_cause_of_death = NULL, victim_findings = '[]' WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '野上修一'); + +UPDATE evidences +SET description = '点検口には仮設試験線がまだ接続中であることを示す工事タグが残っている。', + reveal_condition = '玄田に聖歌席裏の点検口で見たものを尋ねるか、水城に仮設線の撤去状況を確認したら開示する。または聖歌席裏の点検口を調べ、残った工事タグと仮設線を確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一') AND name = '玄田修')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一') AND name = '水城奈央')), json_object('type', 'location', 'id', 'choir-access')), + supports = '["test-line-tag-remained","temporary-test-line-exists"]', + contradicts = '["lie:mizuki-bell-alibi"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一') + AND label = '聖歌席裏の工事タグ'; + +UPDATE evidences +SET description = '香川の位置タグと自動測量カートが、21時00分から15分まで同じ地点を同じ時刻に通過している。二つの軌跡は実質的に重なる。', + reveal_condition = '香川、結城、新堂のいずれかに位置タグの軌跡と自動測量カートの運行を比較できないか尋ねたら開示する。または自動測量区画を調べ、カートの走行跡と位置タグの固定跡を照合したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '岩代圭吾') AND name = '香川紗英')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '岩代圭吾') AND name = '結城真')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '岩代圭吾') AND name = '新堂匠')), json_object('type', 'location', 'id', 'survey-zone')), + supports = '["locator-tag-removable","mapping-cart-auto-loop","kagawa-tag-on-cart","tag-track-matches-cart"]', + contradicts = '["lie:kagawa-location-alibi","lie:kagawa-wore-tag"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '岩代圭吾') + AND label = '位置タグと自動測量カートの軌跡比較'; + +UPDATE evidences +SET description = '倉田の担当欄で仕入れ額が実際より増やされ、野上が事件当日に再確認の印を付けている。', + reveal_condition = '倉田に野上と事件直前に揉めた帳簿の内容を追及したら開示する。または旧捜査資料箱を調べ、押収された仕入れ帳の該当欄を確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '野上修一') AND name = '倉田恵')), json_object('type', 'location', 'id', 'case-archive')), + supports = '["megumi-forged-expenses","nogami-found-megumi-fraud"]', + contradicts = '["lie:megumi-no-fraud"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '野上修一') + AND label = '仕入れ帳の水増し'; + +UPDATE evidences +SET description = '廊下側から開ける時だけ館長鍵が必要で、外へ出て扉を閉めるとラッチが自動で掛かる。施錠操作に鍵は不要である。', + reveal_condition = '八神か戸塚に資料庫の鍵が開錠と施錠のどちらに必要なのか具体的に尋ねたら開示する。または希少資料庫の扉を調べ、閉じるだけで施錠されるラッチ構造を確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '今泉孝臣') AND name = '八神琴子')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '今泉孝臣') AND name = '戸塚誠')), json_object('type', 'location', 'id', 'rare-vault')), + supports = '["vault-key-only-opens","vault-self-locks","key-found-on-imaizumi"]', + contradicts = '["lie:yagami-key-needed-to-lock"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '今泉孝臣') + AND label = '希少資料庫の自動施錠仕様'; + +UPDATE evidences +SET description = '装置は18時35分に起動したが、18時37分から18時51分まで停止している。', + reveal_condition = '榊に検査を続けていた時間帯を尋ねるか、榎本に修復室の機器ログを確認できないか尋ねたら開示する。または修復室を調べ、紫外線検査装置の電源履歴を確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '鳥羽薫') AND name = '榊玲')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '鳥羽薫') AND name = '榎本駿')), json_object('type', 'location', 'id', 'restoration-room')), + supports = '["uv-test-started","uv-lamp-off-1837","uv-test-resumed"]', + contradicts = '["lie:sakaki-uv-alibi"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '鳥羽薫') + AND label = '紫外線検査装置の電源履歴'; + +UPDATE evidences +SET description = '防災端末が20時19分を記録した瞬間の監視画像で、中央ホールの親時計は20時30分を示している。両系統には十一分の差がある。', + reveal_condition = '城戸か保科に館内時計の精度と、防災端末など別系統の時計との比較を尋ねたら開示する。または親時計盤を調べ、防災端末と表示時刻を突き合わせたら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一') AND name = '城戸篤')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一') AND name = '保科悠人')), json_object('type', 'location', 'id', 'master-clock')), + supports = '["master-clock-fast-eleven","gallery-clocks-follow-master","security-clock-accurate","half-past-chime-actual-2019"]', + contradicts = '["lie:kido-clock-accurate","lie:shiba-late-last-seen"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一') + AND label = '親時計と防災端末の時刻差'; + +UPDATE evidences +SET description = '午前零時の冒頭素材は23時53分に登録され、時刻指定で自動再生された記録が残る。', + reveal_condition = '久世に午前零時の音声が生放送か録音かを尋ねるか、美濃部の「生だった」という説明の技術的根拠を確認したら開示する。または自動送出室を調べ、午前零時の送出キューと実行ログを確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '大門修一') AND name = '久世直人')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '大門修一') AND name = '美濃部沙耶')), json_object('type', 'location', 'id', 'playout-room')), + supports = '["minobe-queued-recording","recorded-opening-aired"]', + contradicts = '["lie:minobe-live-alibi"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '大門修一') + AND label = '自動送出システムの実行ログ'; + +UPDATE evidences +SET description = '記録されているのは橙色バッジの通過であり、使用者の顔や氏名を直接確認した記録ではない。', + reveal_condition = '相原か久我に21時18分の認証記録が何を識別しているのか尋ねたら開示する。または検査廊下の認証端末を調べ、記録がバッジ番号だけで使用者を識別しないと確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '星名悟') AND name = '相原誠')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '星名悟') AND name = '久我夏樹')), json_object('type', 'location', 'id', 'badge-reader')), + supports = '["orange-badge-passed-2118","aihara-saw-orange-suit"]', + contradicts = '["lie:sera-badge-proves-hoshina"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '星名悟') + AND label = '二十一時十八分の認証記録'; + +UPDATE evidences +SET description = '清掃後の額装作業室に、荻原が確認していた作品台紙と同じ紙片が散っている。', + reveal_condition = '朝倉に事件後の額装作業室で気づいた変化を尋ねたら開示する。または額装作業室を調べ、床の台紙片と作業台周辺の痕跡を確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '荻原直哉') AND name = '朝倉凪')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '荻原直哉') AND name = '香坂澪')), json_object('type', 'location', 'id', 'framing-room')), + supports = '["framing-paper-fibers","kosaka-killed-ogiwara-framing"]', + contradicts = '["lie:kosaka-vault-crime"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '荻原直哉') + AND label = '額装作業室の台紙片'; + +UPDATE evidences +SET description = '空調音や小さな物音まで含めた波形が四十七秒周期で完全一致し、同じ室内音が繰り返し再生されていたと分かる。', + reveal_condition = '真鍋か牧村に収録ファイルが本当に生マイク入力だったか、波形の反復と入力経路を含めて尋ねたら開示する。または第2ブースの監視席を調べ、録音入力と波形の反復を確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '冬木圭介') AND name = '真鍋伊織')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '冬木圭介') AND name = '牧村葉月')), json_object('type', 'location', 'id', 'patch-bay')), + supports = '["manabe-made-roomtone-loop","loop-routed-to-recorder","recording-repeats-identically"]', + contradicts = '["lie:manabe-booth-alibi","lie:manabe-live-input"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '冬木圭介') + AND label = '四十七秒ごとに一致する収録波形'; + +UPDATE evidences +SET description = '正面扉とは別に、排水区画側から旧制御室へ接続する保守歩廊がある。', + reveal_condition = '城戸に旧制御室の保守経路を尋ねるか、谷口に設備図上の接続を確認したら開示する。または旧保守歩廊を調べ、旧制御室への接続口を確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '峰岸達也') AND name = '城戸真琴')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '峰岸達也') AND name = '谷口航')), json_object('type', 'location', 'id', 'old-walkway')), + supports = '["old-walkway-connects"]', + contradicts = '["lie:kido-impossible-entry"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '峰岸達也') + AND label = '旧保守歩廊の接続図'; + +UPDATE evidences +SET description = '二つの区画は同じ古い中空隔壁に接し、旧階段側の硬い音が二階から聞こえることがある。', + reveal_condition = '榊原に古い建物の音の伝わり方を尋ねるか、秋庭に旧階段で音が響くことを知っていたか確認したら開示する。または厨房脇の旧階段を調べ、隔壁の構造と音の伝わり方を確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介') AND name = '榊原蓮')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介') AND name = '秋庭澄')), json_object('type', 'location', 'id', 'old-stairs')), + supports = '["old-partition-transmits"]', + contradicts = '["lie:akiwa-cane-proves-alive"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介') + AND label = '旧階段と二階廊下の中空壁'; + +UPDATE evidences +SET description = '投影室を暗くして廊下を明るくすると、観察ガラスには室内より廊下側の人物が強く反射する。', + reveal_condition = '松田か小野寺に投影室消灯時の観察ガラスの見え方を具体的に尋ねたら開示する。または投影室の観察ガラスを調べ、消灯時の反射を同じ立ち位置で再現したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠') AND name = '松田圭介')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠') AND name = '小野寺莉香')), json_object('type', 'location', 'id', 'observation-glass')), + supports = '["booth-dark-2130","glass-reflects-dark-booth","onodera-knew-reflection"]', + contradicts = '["lie:onodera-sighting-was-inuzuka"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠') + AND label = '観察ガラスの照明条件テスト'; + +UPDATE evidences +SET description = '『乗員1』は一定以上の荷重で点灯し、保守用工具ケースだけでも同じ表示になる。21時台にはそのケースが点検席へ置かれていた。', + reveal_condition = '須藤、長峰、折原のいずれかに『乗員1』表示の検知方式と工具ケースについて尋ねたら開示する。または非常用搬器を調べ、座席センサーが重量だけを検知することと工具ケースの重さを確認したら開示する。', + sources = json_array(json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬修司') AND name = '須藤拓海')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬修司') AND name = '長峰礼')), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬修司') AND name = '折原壮')), json_object('type', 'location', 'id', 'emergency-cabin')), + supports = '["cabin-display-weight-based","tool-case-heavy-enough","sudo-left-tool-case","occupancy-display-on"]', + contradicts = '["lie:sudo-cabin-alibi","lie:sudo-no-toolcase-seat"]', + reveals_death_time = 0 +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬修司') + AND label = '非常用搬器の座席重量仕様'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '8a39e27f-c6a7-4a4b-877b-dd9267901bbc', (SELECT id FROM scenarios WHERE victim_name = '高瀬静一'), '発見時の死亡推定', '資料整理室の室温と発見時の状態を照合すると、高瀬が死亡したのは21時05分ごろと見積もられる。21時20分の鐘より前である。', '遺体を調べて発見時の状態から死亡時刻を推定するか、玄田に発見時の状態と確認内容を具体的に尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一') AND name = '玄田修'))), '["mizuki-killed-takase"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一') AND label = '発見時の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '発見時の高瀬の状態と冷えた資料整理室の様子を確認しており、死亡は21時05分ごろと見積もられるという確認内容を覚えている。') = 0 THEN memories || char(10) || '- 発見時の高瀬の状態と冷えた資料整理室の様子を確認しており、死亡は21時05分ごろと見積もられるという確認内容を覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '高瀬静一') AND name = '玄田修'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '59a5f86d-68fd-40fc-90b1-ae436d946355', (SELECT id FROM scenarios WHERE victim_name = '野上修一'), '1979年の検視記録', '当時の検視記録は、発見時の状態から野上の死亡を22時05分ごろと見積もっている。22時30分の暖炉前目撃より前になる。', '旧捜査資料箱を調べて当時の検視記録を確認するか、藤村に再調査で読み直した検視記録の内容を尋ねたら開示する。', json_array(json_object('type', 'location', 'id', 'case-archive'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '野上修一') AND name = '藤村達也'))), '["megumi-killed-nogami"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '野上修一') AND label = '1979年の検視記録' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '再調査で開封された旧検視記録を読み、当時の死亡推定が22時05分ごろだったことを改めて知っている。') = 0 THEN memories || char(10) || '- 再調査で開封された旧検視記録を読み、当時の死亡推定が22時05分ごろだったことを改めて知っている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '野上修一') AND name = '藤村達也'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '039f8a95-322e-49c5-abca-1a09a40a30ca', (SELECT id FROM scenarios WHERE victim_name = 'ミラ・ヴォス'), '医療区の死亡推定', '医療センサーによる発見時の確認では、ミラの死亡は船内標準時05時57分ごろと見積もられる。区画ごとの人工時刻とは別の基準である。', '遺体を調べて医療センサーの確認値を見るか、医療担当のユナに死亡推定を船内標準時で尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = 'ミラ・ヴォス') AND name = 'ユナ・パク'))), '["sera-killed-mira"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = 'ミラ・ヴォス') AND label = '医療区の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '医療担当として発見時の状態を確認し、死亡は船内標準時05時57分ごろと見積もっている。人工昼夜表示ではなく標準時で記録した。') = 0 THEN memories || char(10) || '- 医療担当として発見時の状態を確認し、死亡は船内標準時05時57分ごろと見積もっている。人工昼夜表示ではなく標準時で記録した。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = 'ミラ・ヴォス') AND name = 'ユナ・パク'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '299254d1-c287-448f-9749-279f2e51d9c3', (SELECT id FROM scenarios WHERE victim_name = '石橋礼司'), '編集長室の死亡推定', '編集長室の室温と発見時の状態を合わせると、石橋の死亡は20時41分ごろと見積もられる。20時50分の署名時刻より前である。', '遺体を調べて発見時の状態から死亡時刻を推定するか、藤本に発見時に確認した状態と時刻について尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '石橋礼司') AND name = '藤本圭'))), '["kawase-killed-ishibashi-2041"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '石橋礼司') AND label = '編集長室の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '発見時の石橋の状態と編集長室の室温を確認しており、死亡は20時41分ごろと見積もられるという確認内容を覚えている。') = 0 THEN memories || char(10) || '- 発見時の石橋の状態と編集長室の室温を確認しており、死亡は20時41分ごろと見積もられるという確認内容を覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '石橋礼司') AND name = '藤本圭'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '7b6b0858-5fe6-4871-91ed-681914492c94', (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一'), '修復室の死亡推定', '発見時の状態を、親時計とは別系統の防災端末時刻で整理すると、倉橋の死亡は20時28分ごろと見積もられる。', '遺体を調べて発見時の状態を正しい時刻系で確認するか、保科に発見時の確認内容を防災端末の時刻基準で尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一') AND name = '保科悠人'))), '["shiba-killed-kurahashi"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一') AND label = '修復室の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '発見時の確認内容を防災端末の正しい時刻で控えており、倉橋の死亡は20時28分ごろと見積もられると覚えている。') = 0 THEN memories || char(10) || '- 発見時の確認内容を防災端末の正しい時刻で控えており、倉橋の死亡は20時28分ごろと見積もられると覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '倉橋宗一') AND name = '保科悠人'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '50e6108a-a8bb-42a6-87f9-6634dd053925', (SELECT id FROM scenarios WHERE victim_name = '長峰宗一'), '執務室の死亡推定', '執務室の室温と発見時の状態から、長峰の死亡は21時05分ごろと見積もられる。21時30分に見えた人影より前である。', '遺体を調べて発見時の状態を確認するか、星野に最初に執務室へ入ったときの状態と確認内容を尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '長峰宗一') AND name = '星野結'))), '["ayase-killed-nagamine"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '長峰宗一') AND label = '執務室の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '最初に執務室へ入ったときの状態を覚えており、確認では長峰の死亡は21時05分ごろと見積もられていた。') = 0 THEN memories || char(10) || '- 最初に執務室へ入ったときの状態を覚えており、確認では長峰の死亡は21時05分ごろと見積もられていた。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '長峰宗一') AND name = '星野結'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '7ca45f6f-7b0a-4814-bd7e-23352188e911', (SELECT id FROM scenarios WHERE victim_name = '大門修一'), '第2ブースの死亡推定', '第2ブースの一定した室温と発見時の状態を合わせると、大門の死亡は23時48分ごろと見積もられる。午前零時の放送より前である。', '遺体を調べて発見時の状態を確認するか、久世にブースの室温記録と発見時の確認内容を尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '大門修一') AND name = '久世直人'))), '["daimon-died-2348"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '大門修一') AND label = '第2ブースの死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '第2ブースの空調温度と発見時の状態を確認しており、死亡は23時48分ごろと見積もられるという確認内容を覚えている。') = 0 THEN memories || char(10) || '- 第2ブースの空調温度と発見時の状態を確認しており、死亡は23時48分ごろと見積もられるという確認内容を覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '大門修一') AND name = '久世直人'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '89d36740-b882-4b74-91b9-08db60357d01', (SELECT id FROM scenarios WHERE victim_name = '星名悟'), '診療所の死亡推定', '診療所の検査機器で発見時の状態を確認すると、星名の死亡は21時08分ごろと見積もられる。21時18分のバッジ通過より前である。', '遺体を調べて検査機器の確認値を見るか、久我に発見時に取った検査値と死亡推定について尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '星名悟') AND name = '久我夏樹'))), '["hoshina-death-2108"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '星名悟') AND label = '診療所の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '検査担当として発見時の確認値を取り、星名の死亡は21時08分ごろと見積もられることを把握している。') = 0 THEN memories || char(10) || '- 検査担当として発見時の確認値を取り、星名の死亡は21時08分ごろと見積もられることを把握している。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '星名悟') AND name = '久我夏樹'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '78d82753-b74d-46a4-853f-e394d4a5ef4a', (SELECT id FROM scenarios WHERE victim_name = '周文海'), '当夜の検視メモ', '発見後に作られた検視メモでは、周の死亡は21時52分ごろと見積もられている。上紙へ22時05分が書き足されるより前である。', '遺体を調べて当夜の検視内容を確認するか、陳に発見後に控えた検視メモの時刻を尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '周文海') AND name = '陳伯安'))), '["lin-killed-zhou"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '周文海') AND label = '当夜の検視メモ' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '発見後の検視内容を業務メモへ写しており、周の死亡は21時52分ごろと見積もられていたことを覚えている。') = 0 THEN memories || char(10) || '- 発見後の検視内容を業務メモへ写しており、周の死亡は21時52分ごろと見積もられていたことを覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '周文海') AND name = '陳伯安'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT 'aced8865-9f30-45b4-ab44-b6383e8519b9', (SELECT id FROM scenarios WHERE victim_name = '佐久間隆志'), '冬夜の死亡推定', '事務室の夜間温度と発見時の状態を合わせると、佐久間の死亡は前夜22時10分ごろと見積もられる。明け方の朝仕事より大幅に早い。', '遺体を調べて発見時の状態を確認するか、久世に事務室の夜間温度と発見時の確認内容を尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '佐久間隆志') AND name = '久世圭太'))), '["fuyuki-killed-sakuma"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '佐久間隆志') AND label = '冬夜の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '設備担当として事務室の夜間温度を把握し、発見時の確認では佐久間の死亡は前夜22時10分ごろと見積もられていたことを覚えている。') = 0 THEN memories || char(10) || '- 設備担当として事務室の夜間温度を把握し、発見時の確認では佐久間の死亡は前夜22時10分ごろと見積もられていたことを覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '佐久間隆志') AND name = '久世圭太'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '95442215-0b1b-4d9c-bc3b-47ecf3ae38a0', (SELECT id FROM scenarios WHERE victim_name = '榊原宗一'), '執務室の死亡推定', '執務室の室温と発見時の状態から、榊原の死亡は21時28分ごろと見積もられる。21時42分のメール送信より前である。', '遺体を調べて発見時の状態を確認するか、堀江に執務室へ入ったときの状態と確認内容を尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '榊原宗一') AND name = '堀江充'))), '["aizawa-killed-sakakibara"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '榊原宗一') AND label = '執務室の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '発見時に執務室の状態を確認しており、榊原の死亡は21時28分ごろと見積もられるという確認内容を覚えている。') = 0 THEN memories || char(10) || '- 発見時に執務室の状態を確認しており、榊原の死亡は21時28分ごろと見積もられるという確認内容を覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '榊原宗一') AND name = '堀江充'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '547221fe-b63a-41a1-a9db-094323ceae92', (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介'), '帳場奥の死亡推定', '帳場奥の室温と発見時の状態から、桐谷の死亡は21時08分ごろと見積もられる。21時25分に聞かれた杖音より前である。', '遺体を調べて発見時の状態を確認するか、榊原に発見時の室内と確認内容を尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介') AND name = '榊原蓮'))), '["akiwa-killed-kiritani"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介') AND label = '帳場奥の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '発見時の帳場奥の状態を確認しており、桐谷の死亡は21時08分ごろと見積もられるという確認内容を覚えている。') = 0 THEN memories || char(10) || '- 発見時の帳場奥の状態を確認しており、桐谷の死亡は21時08分ごろと見積もられるという確認内容を覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '桐谷宗介') AND name = '榊原蓮'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '359e693f-6852-4e3b-9453-e9261328f789', (SELECT id FROM scenarios WHERE victim_name = '犬塚誠'), '投影準備室の死亡推定', '投影準備室の温度と発見時の状態を合わせると、犬塚の死亡は21時24分ごろと見積もられる。21時35分の人影目撃より前である。', '遺体を調べて発見時の状態を確認するか、松田に投影準備室へ入ったときの状態と確認内容を尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠') AND name = '松田圭介'))), '["inuzuka-death-2124"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠') AND label = '投影準備室の死亡推定' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '発見時の投影準備室の状態を確認しており、犬塚の死亡は21時24分ごろと見積もられるという確認内容を覚えている。') = 0 THEN memories || char(10) || '- 発見時の投影準備室の状態を確認しており、犬塚の死亡は21時24分ごろと見積もられるという確認内容を覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = '犬塚誠') AND name = '松田圭介'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT '7b4c8930-2002-410c-a66a-c0093f6a5876', (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル'), '現場の死亡推定記録', '測量室の室温と発見時の確認記録から、ヘイルの死亡は22時03分ごろと見積もられる。22時12分の電信より前である。', '遺体を調べて発見時の状態を確認するか、トーマスに測量室での発見時の状態と記録内容を尋ねたら開示する。', json_array(json_object('type', 'victim', 'id', 'victim'), json_object('type', 'character', 'id', (SELECT id FROM characters WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル') AND name = 'トーマス・リード'))), '["bell-killed-hale"]', '[]', 1 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル') AND label = '現場の死亡推定記録' +); + +UPDATE characters +SET memories = CASE WHEN instr(memories, '測量室でヘイルを発見したときの状態を作業記録へ残しており、死亡は22時03分ごろと見積もられるという確認内容を覚えている。') = 0 THEN memories || char(10) || '- 測量室でヘイルを発見したときの状態を作業記録へ残しており、死亡は22時03分ごろと見積もられるという確認内容を覚えている。' ELSE memories END +WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル') AND name = 'トーマス・リード'; + +INSERT INTO evidences (id, scenario_id, label, description, reveal_condition, sources, supports, contradicts, reveals_death_time) +SELECT 'd1324f0e-bc53-48d0-9795-0df0539242df', (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル'), '第二立坑の電信機の送信仕様', '第二立坑の送信キーは操作者を識別せず、既知の符号列を誰でも同じ形で送れる。', '第二立坑の電信機を調べ、送信者を識別する仕組みがないことと符号表を確認したら開示する。', json_array(json_object('type', 'location', 'id', 'shaft-two-telegraph')), '["telegraph-no-sender-id"]', '[]', 0 +WHERE NOT EXISTS ( + SELECT 1 FROM evidences WHERE scenario_id = (SELECT id FROM scenarios WHERE victim_name = 'エドワード・ヘイル') AND label = '第二立坑の電信機の送信仕様' +); diff --git a/db/migrations/0022_scenario-place-public-copy-safety.sql b/db/migrations/0022_scenario-place-public-copy-safety.sql new file mode 100644 index 0000000..57cd3c1 --- /dev/null +++ b/db/migrations/0022_scenario-place-public-copy-safety.sql @@ -0,0 +1,47 @@ +-- Keep investigable-place list copy public and spoiler-neutral. + +-- Private place findings are unchanged. + +UPDATE scenarios +SET places = '[{"id":"choir-access","name":"聖歌席裏","shortName":"聖歌席裏","introduction":"鐘の修復資材が置かれた、一階の作業区画","situation":"木箱と工事用の札がまとめて置かれている"}]' +WHERE victim_name = '高瀬静一'; + +UPDATE scenarios +SET places = '[{"id":"rare-vault","name":"希少資料庫","shortName":"資料庫","introduction":"貴重資料を保管する、厚い防火扉の資料庫","situation":"重い扉が閉じ、廊下側には開錠用の鍵穴がある"}]' +WHERE victim_name = '今泉孝臣'; + +UPDATE scenarios +SET places = '[{"id":"playout-room","name":"自動送出室","shortName":"送出室","introduction":"収録音源と深夜番組の放送順を管理する送出卓","situation":"送出卓のモニターと操作盤が待機状態になっている"}]' +WHERE victim_name = '大門修一'; + +UPDATE scenarios +SET places = '[{"id":"badge-reader","name":"検査廊下の認証端末","shortName":"認証端末","introduction":"防護区画を通る管理バッジの認証端末","situation":"認証端末の画面が待機表示のまま残っている"}]' +WHERE victim_name = '星名悟'; + +UPDATE scenarios +SET places = '[{"id":"framing-room","name":"額装作業室","shortName":"額装室","introduction":"作品台紙の加工と搬送準備を行う作業室","situation":"作業台と資材棚が、閉館時のまま残されている"}]' +WHERE victim_name = '荻原直哉'; + +UPDATE scenarios +SET places = '[{"id":"patch-bay","name":"第2ブース監視席","shortName":"監視席","introduction":"録音入力を切り替えるパッチ盤と収録端末の席","situation":"パッチ盤と収録端末の電源が残っている"}]' +WHERE victim_name = '冬木圭介'; + +UPDATE scenarios +SET places = '[{"id":"old-walkway","name":"旧保守歩廊","shortName":"旧歩廊","introduction":"排水区画に残る、現在は使われていない保守通路","situation":"現在は増水で水に覆われ、入口から先へ進めない"}]' +WHERE victim_name = '峰岸達也'; + +UPDATE scenarios +SET places = '[{"id":"old-stairs","name":"厨房脇の旧階段","shortName":"旧階段","introduction":"厨房脇に残る、普段使われない古い木造階段","situation":"古い木造の隔壁と手すりがそのまま残されている"}]' +WHERE victim_name = '桐谷宗介'; + +UPDATE scenarios +SET places = '[{"id":"observation-glass","name":"投影室の観察ガラス","shortName":"観察ガラス","introduction":"投影室と廊下を隔てる、大型の観察窓","situation":"投影室と廊下のあいだを、大きな一枚ガラスが隔てている"}]' +WHERE victim_name = '犬塚誠'; + +UPDATE scenarios +SET places = '[{"id":"emergency-cabin","name":"非常用搬器","shortName":"非常搬器","introduction":"非常時と保守点検に使う、小型の予備搬器","situation":"営業終了後の点検位置で停止している"}]' +WHERE victim_name = '高瀬修司'; + +UPDATE scenarios +SET places = '[{"id":"shaft-two-telegraph","name":"第二立坑の電信機","shortName":"第2電信","introduction":"二つの立坑を結ぶ、工事連絡用の電信機","situation":"送信キーと記録用の紙束が作業机の上に残されている"}]' +WHERE victim_name = 'エドワード・ヘイル'; diff --git a/db/migrations/meta/0021_snapshot.json b/db/migrations/meta/0021_snapshot.json new file mode 100644 index 0000000..0bd4d60 --- /dev/null +++ b/db/migrations/meta/0021_snapshot.json @@ -0,0 +1,1190 @@ +{ + "id": "31c61ea0-f1ba-461e-865c-02508b36c1ac", + "prevId": "828e6dff-6ddf-4c50-9a5b-0a865fc6745f", + "version": "6", + "dialect": "sqlite", + "tables": { + "characters": { + "name": "characters", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "public_introduction": { + "name": "public_introduction", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "personality": { + "name": "personality", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "knowledge": { + "name": "knowledge", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "secrets": { + "name": "secrets", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "goals": { + "name": "goals", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "lies": { + "name": "lies", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "lie_refs": { + "name": "lie_refs", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "memories": { + "name": "memories", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "characters_scenario_id_idx": { + "name": "characters_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "characters_scenario_id_scenarios_id_fk": { + "name": "characters_scenario_id_scenarios_id_fk", + "tableFrom": "characters", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "discoveries": { + "name": "discoveries", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "evidence_id": { + "name": "evidence_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "discovered_at": { + "name": "discovered_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "discoveries_evidence_id_idx": { + "name": "discoveries_evidence_id_idx", + "columns": [ + "evidence_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "discoveries_session_id_play_sessions_id_fk": { + "name": "discoveries_session_id_play_sessions_id_fk", + "tableFrom": "discoveries", + "columnsFrom": [ + "session_id" + ], + "tableTo": "play_sessions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "discoveries_evidence_id_evidences_id_fk": { + "name": "discoveries_evidence_id_evidences_id_fk", + "tableFrom": "discoveries", + "columnsFrom": [ + "evidence_id" + ], + "tableTo": "evidences", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "discoveries_session_id_evidence_id_pk": { + "columns": [ + "session_id", + "evidence_id" + ], + "name": "discoveries_session_id_evidence_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "evidences": { + "name": "evidences", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reveal_condition": { + "name": "reveal_condition", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sources": { + "name": "sources", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "supports": { + "name": "supports", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "contradicts": { + "name": "contradicts", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "reveals_death_time": { + "name": "reveals_death_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": { + "evidences_scenario_id_idx": { + "name": "evidences_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "evidences_scenario_id_scenarios_id_fk": { + "name": "evidences_scenario_id_scenarios_id_fk", + "tableFrom": "evidences", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "llm_usages": { + "name": "llm_usages", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cached_input_tokens": { + "name": "cached_input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cache_creation_input_tokens": { + "name": "cache_creation_input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "reasoning_tokens": { + "name": "reasoning_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "llm_usages_created_at_idx": { + "name": "llm_usages_created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "llm_usages_session_id_idx": { + "name": "llm_usages_session_id_idx", + "columns": [ + "session_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "messages": { + "name": "messages", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "character_id": { + "name": "character_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "messages_session_id_idx": { + "name": "messages_session_id_idx", + "columns": [ + "session_id" + ], + "isUnique": false + }, + "messages_character_id_idx": { + "name": "messages_character_id_idx", + "columns": [ + "character_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "messages_session_id_play_sessions_id_fk": { + "name": "messages_session_id_play_sessions_id_fk", + "tableFrom": "messages", + "columnsFrom": [ + "session_id" + ], + "tableTo": "play_sessions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "messages_character_id_characters_id_fk": { + "name": "messages_character_id_characters_id_fk", + "tableFrom": "messages", + "columnsFrom": [ + "character_id" + ], + "tableTo": "characters", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "play_sessions": { + "name": "play_sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "detective": { + "name": "detective", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "finished_at": { + "name": "finished_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "play_sessions_scenario_id_idx": { + "name": "play_sessions_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + }, + "play_sessions_started_at_idx": { + "name": "play_sessions_started_at_idx", + "columns": [ + "started_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "play_sessions_scenario_id_scenarios_id_fk": { + "name": "play_sessions_scenario_id_scenarios_id_fk", + "tableFrom": "play_sessions", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "reports": { + "name": "reports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reported_at": { + "name": "reported_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "reports_scenario_id_idx": { + "name": "reports_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "reports_scenario_id_scenarios_id_fk": { + "name": "reports_scenario_id_scenarios_id_fk", + "tableFrom": "reports", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "results": { + "name": "results", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "solved_seconds": { + "name": "solved_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question_count": { + "name": "question_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "evidence_found": { + "name": "evidence_found", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contradiction_count": { + "name": "contradiction_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accuracy_percent": { + "name": "accuracy_percent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "method_correct": { + "name": "method_correct", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "motive_correct": { + "name": "motive_correct", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deduction": { + "name": "deduction", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "results_session_id_play_sessions_id_fk": { + "name": "results_session_id_play_sessions_id_fk", + "tableFrom": "results", + "columnsFrom": [ + "session_id" + ], + "tableTo": "play_sessions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "revelation_discoveries": { + "name": "revelation_discoveries", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "revelation_id": { + "name": "revelation_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "discovered_at": { + "name": "discovered_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "revelation_discoveries_revelation_id_idx": { + "name": "revelation_discoveries_revelation_id_idx", + "columns": [ + "revelation_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "revelation_discoveries_session_id_play_sessions_id_fk": { + "name": "revelation_discoveries_session_id_play_sessions_id_fk", + "tableFrom": "revelation_discoveries", + "columnsFrom": [ + "session_id" + ], + "tableTo": "play_sessions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "revelation_discoveries_revelation_id_revelations_id_fk": { + "name": "revelation_discoveries_revelation_id_revelations_id_fk", + "tableFrom": "revelation_discoveries", + "columnsFrom": [ + "revelation_id" + ], + "tableTo": "revelations", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "revelation_discoveries_session_id_revelation_id_pk": { + "columns": [ + "session_id", + "revelation_id" + ], + "name": "revelation_discoveries_session_id_revelation_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "revelations": { + "name": "revelations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sources": { + "name": "sources", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "related_facts": { + "name": "related_facts", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "revelations_scenario_id_idx": { + "name": "revelations_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "revelations_scenario_id_scenarios_id_fk": { + "name": "revelations_scenario_id_scenarios_id_fk", + "tableFrom": "revelations", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "scenario_truths": { + "name": "scenario_truths", + "columns": { + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "culprit_character_id": { + "name": "culprit_character_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "truth": { + "name": "truth", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "motive": { + "name": "motive", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "timeline": { + "name": "timeline", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timeline_events": { + "name": "timeline_events", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "victim_cause_of_death": { + "name": "victim_cause_of_death", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_findings": { + "name": "victim_findings", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "place_findings": { + "name": "place_findings", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "secret_keywords": { + "name": "secret_keywords", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "scenario_truths_scenario_id_scenarios_id_fk": { + "name": "scenario_truths_scenario_id_scenarios_id_fk", + "tableFrom": "scenario_truths", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "scenarios": { + "name": "scenarios", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "synopsis": { + "name": "synopsis", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "briefing": { + "name": "briefing", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "floor_plan": { + "name": "floor_plan", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "time_start": { + "name": "time_start", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_end": { + "name": "time_end", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_name": { + "name": "victim_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_introduction": { + "name": "victim_introduction", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_found_at": { + "name": "victim_found_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_found_in": { + "name": "victim_found_in", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_investigable": { + "name": "victim_investigable", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "victim_estimated_death_at": { + "name": "victim_estimated_death_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "places": { + "name": "places", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_published": { + "name": "is_published", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "difficulty": { + "name": "difficulty", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 3 + }, + "estimated_minutes": { + "name": "estimated_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0022_snapshot.json b/db/migrations/meta/0022_snapshot.json new file mode 100644 index 0000000..36d1e92 --- /dev/null +++ b/db/migrations/meta/0022_snapshot.json @@ -0,0 +1,1190 @@ +{ + "id": "1a711748-a372-470e-957e-00db96cda2e8", + "prevId": "31c61ea0-f1ba-461e-865c-02508b36c1ac", + "version": "6", + "dialect": "sqlite", + "tables": { + "characters": { + "name": "characters", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "public_introduction": { + "name": "public_introduction", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "personality": { + "name": "personality", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "knowledge": { + "name": "knowledge", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "secrets": { + "name": "secrets", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "goals": { + "name": "goals", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "lies": { + "name": "lies", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "lie_refs": { + "name": "lie_refs", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "memories": { + "name": "memories", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "characters_scenario_id_idx": { + "name": "characters_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "characters_scenario_id_scenarios_id_fk": { + "name": "characters_scenario_id_scenarios_id_fk", + "tableFrom": "characters", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "discoveries": { + "name": "discoveries", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "evidence_id": { + "name": "evidence_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "discovered_at": { + "name": "discovered_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "discoveries_evidence_id_idx": { + "name": "discoveries_evidence_id_idx", + "columns": [ + "evidence_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "discoveries_session_id_play_sessions_id_fk": { + "name": "discoveries_session_id_play_sessions_id_fk", + "tableFrom": "discoveries", + "columnsFrom": [ + "session_id" + ], + "tableTo": "play_sessions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "discoveries_evidence_id_evidences_id_fk": { + "name": "discoveries_evidence_id_evidences_id_fk", + "tableFrom": "discoveries", + "columnsFrom": [ + "evidence_id" + ], + "tableTo": "evidences", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "discoveries_session_id_evidence_id_pk": { + "columns": [ + "session_id", + "evidence_id" + ], + "name": "discoveries_session_id_evidence_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "evidences": { + "name": "evidences", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reveal_condition": { + "name": "reveal_condition", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sources": { + "name": "sources", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "supports": { + "name": "supports", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "contradicts": { + "name": "contradicts", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "reveals_death_time": { + "name": "reveals_death_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": { + "evidences_scenario_id_idx": { + "name": "evidences_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "evidences_scenario_id_scenarios_id_fk": { + "name": "evidences_scenario_id_scenarios_id_fk", + "tableFrom": "evidences", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "llm_usages": { + "name": "llm_usages", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cached_input_tokens": { + "name": "cached_input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cache_creation_input_tokens": { + "name": "cache_creation_input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "reasoning_tokens": { + "name": "reasoning_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "llm_usages_created_at_idx": { + "name": "llm_usages_created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "llm_usages_session_id_idx": { + "name": "llm_usages_session_id_idx", + "columns": [ + "session_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "messages": { + "name": "messages", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "character_id": { + "name": "character_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "messages_session_id_idx": { + "name": "messages_session_id_idx", + "columns": [ + "session_id" + ], + "isUnique": false + }, + "messages_character_id_idx": { + "name": "messages_character_id_idx", + "columns": [ + "character_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "messages_session_id_play_sessions_id_fk": { + "name": "messages_session_id_play_sessions_id_fk", + "tableFrom": "messages", + "columnsFrom": [ + "session_id" + ], + "tableTo": "play_sessions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "messages_character_id_characters_id_fk": { + "name": "messages_character_id_characters_id_fk", + "tableFrom": "messages", + "columnsFrom": [ + "character_id" + ], + "tableTo": "characters", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "play_sessions": { + "name": "play_sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "detective": { + "name": "detective", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "finished_at": { + "name": "finished_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "play_sessions_scenario_id_idx": { + "name": "play_sessions_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + }, + "play_sessions_started_at_idx": { + "name": "play_sessions_started_at_idx", + "columns": [ + "started_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "play_sessions_scenario_id_scenarios_id_fk": { + "name": "play_sessions_scenario_id_scenarios_id_fk", + "tableFrom": "play_sessions", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "reports": { + "name": "reports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reported_at": { + "name": "reported_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "reports_scenario_id_idx": { + "name": "reports_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "reports_scenario_id_scenarios_id_fk": { + "name": "reports_scenario_id_scenarios_id_fk", + "tableFrom": "reports", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "results": { + "name": "results", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "solved_seconds": { + "name": "solved_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question_count": { + "name": "question_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "evidence_found": { + "name": "evidence_found", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contradiction_count": { + "name": "contradiction_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accuracy_percent": { + "name": "accuracy_percent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "method_correct": { + "name": "method_correct", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "motive_correct": { + "name": "motive_correct", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deduction": { + "name": "deduction", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "results_session_id_play_sessions_id_fk": { + "name": "results_session_id_play_sessions_id_fk", + "tableFrom": "results", + "columnsFrom": [ + "session_id" + ], + "tableTo": "play_sessions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "revelation_discoveries": { + "name": "revelation_discoveries", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "revelation_id": { + "name": "revelation_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "discovered_at": { + "name": "discovered_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "revelation_discoveries_revelation_id_idx": { + "name": "revelation_discoveries_revelation_id_idx", + "columns": [ + "revelation_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "revelation_discoveries_session_id_play_sessions_id_fk": { + "name": "revelation_discoveries_session_id_play_sessions_id_fk", + "tableFrom": "revelation_discoveries", + "columnsFrom": [ + "session_id" + ], + "tableTo": "play_sessions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "revelation_discoveries_revelation_id_revelations_id_fk": { + "name": "revelation_discoveries_revelation_id_revelations_id_fk", + "tableFrom": "revelation_discoveries", + "columnsFrom": [ + "revelation_id" + ], + "tableTo": "revelations", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "revelation_discoveries_session_id_revelation_id_pk": { + "columns": [ + "session_id", + "revelation_id" + ], + "name": "revelation_discoveries_session_id_revelation_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "revelations": { + "name": "revelations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sources": { + "name": "sources", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "related_facts": { + "name": "related_facts", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "revelations_scenario_id_idx": { + "name": "revelations_scenario_id_idx", + "columns": [ + "scenario_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "revelations_scenario_id_scenarios_id_fk": { + "name": "revelations_scenario_id_scenarios_id_fk", + "tableFrom": "revelations", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "scenario_truths": { + "name": "scenario_truths", + "columns": { + "scenario_id": { + "name": "scenario_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "culprit_character_id": { + "name": "culprit_character_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "truth": { + "name": "truth", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "motive": { + "name": "motive", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "timeline": { + "name": "timeline", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timeline_events": { + "name": "timeline_events", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "victim_cause_of_death": { + "name": "victim_cause_of_death", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_findings": { + "name": "victim_findings", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "place_findings": { + "name": "place_findings", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "secret_keywords": { + "name": "secret_keywords", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "scenario_truths_scenario_id_scenarios_id_fk": { + "name": "scenario_truths_scenario_id_scenarios_id_fk", + "tableFrom": "scenario_truths", + "columnsFrom": [ + "scenario_id" + ], + "tableTo": "scenarios", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "scenarios": { + "name": "scenarios", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "synopsis": { + "name": "synopsis", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "briefing": { + "name": "briefing", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "floor_plan": { + "name": "floor_plan", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "time_start": { + "name": "time_start", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_end": { + "name": "time_end", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_name": { + "name": "victim_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_introduction": { + "name": "victim_introduction", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_found_at": { + "name": "victim_found_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_found_in": { + "name": "victim_found_in", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "victim_investigable": { + "name": "victim_investigable", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "victim_estimated_death_at": { + "name": "victim_estimated_death_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "places": { + "name": "places", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_published": { + "name": "is_published", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "difficulty": { + "name": "difficulty", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 3 + }, + "estimated_minutes": { + "name": "estimated_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/_journal.json b/db/migrations/meta/_journal.json index b3c1ba2..482e363 100644 --- a/db/migrations/meta/_journal.json +++ b/db/migrations/meta/_journal.json @@ -148,6 +148,20 @@ "when": 1788362994843, "tag": "0020_deadline-disclosure", "breakpoints": true + }, + { + "idx": 21, + "version": "6", + "when": 1788378860077, + "tag": "0021_scenario-investigation-upgrade", + "breakpoints": true + }, + { + "idx": 22, + "version": "6", + "when": 1788379031994, + "tag": "0022_scenario-place-public-copy-safety", + "breakpoints": true } ] } \ No newline at end of file diff --git a/db/scenarios/avalanche-monastery-bell-window.yaml b/db/scenarios/avalanche-monastery-bell-window.yaml index a928687..1e84994 100644 --- a/db/scenarios/avalanche-monastery-bell-window.yaml +++ b/db/scenarios/avalanche-monastery-bell-window.yaml @@ -11,12 +11,24 @@ victim: introduction: 修道院資料館館長 foundAt: 21:40 foundIn: 資料整理室 + estimatedDeathAt: "21:05" causeOfDeath: 事件性のある外傷による死亡 findings: - id: victim-state statement: 高瀬静一は資料整理室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「修復費の追加請求一覧」に関わる資料が残されている。 +places: + - id: choir-access + name: 聖歌席裏 + shortName: 聖歌席裏 + introduction: 鐘の修復資材が置かれた、一階の作業区画 + situation: 木箱と工事用の札がまとめて置かれている + findings: + - id: test-line-still-present + statement: 点検口の奥に仮設試験線が残り、工事タグも撤去済みの状態にはなっていない。 + - id: test-line-reaches-bell + statement: 仮設試験線は、鐘塔へ入らず一階側から鐘の作動確認を行える配線になっている。 briefing: |- ——事件の記録を読み上げます。 @@ -198,6 +210,8 @@ characters: memories: - id: assumed-removed detail: 前日の作業員が「試験は終わり」と言っていたので、仮設線そのものも外したのだと思い込んでいた。 + - id: death-estimate-memory + detail: 発見時の高瀬の状態と冷えた資料整理室の様子を確認しており、死亡は21時05分ごろと見積もられるという確認内容を覚えている。 relationships: - character: mizuki relation: 工事監理者 @@ -265,12 +279,13 @@ evidences: label: 聖歌席裏の工事タグ description: 点検口には仮設試験線がまだ接続中であることを示す工事タグが残っている。 reveal: - condition: 玄田に聖歌席裏の点検口で見たものを尋ねるか、水城に仮設線の撤去状況を確認したら開示する。 + condition: 玄田に聖歌席裏の点検口で見たものを尋ねるか、水城に仮設線の撤去状況を確認したら開示する。または聖歌席裏の点検口を調べ、残った工事タグと仮設線を確認したら開示する。 sources: - type: character id: genda - type: character id: mizuki + - { type: location, id: choir-access } supports: [test-line-tag-remained, temporary-test-line-exists] contradicts: ["lie:mizuki-bell-alibi"] - id: no-visual-confirmation @@ -319,6 +334,17 @@ evidences: id: genda supports: [genda-secret-key-copy] contradicts: ["lie:genda-no-copy-key"] + - id: death-estimate + label: 発見時の死亡推定 + description: 資料整理室の室温と発見時の状態を照合すると、高瀬が死亡したのは21時05分ごろと見積もられる。21時20分の鐘より前である。 + reveal: + condition: 遺体を調べて発見時の状態から死亡時刻を推定するか、玄田に発見時の状態と確認内容を具体的に尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: genda } + supports: [mizuki-killed-takase] + contradicts: [] + revealsDeathTime: true solution: culprit: mizuki summary: 犯人は水城奈央。修復費の架空請求を高瀬に見つけられ、翌朝までの説明を求められたことから高瀬を襲った。その後、水城は自分が設計した仮設試験線を利用し、21時20分に塔へ入らず鐘を鳴らした。三人は毎晩の習慣から高瀬本人が鐘を鳴らしたと解釈したが、誰もその時刻の高瀬を見ていない。工事系統図と聖歌席裏に残ったタグから、鐘は鍵なしでも鳴らせたと分かる。鐘の音は時刻の手掛かりではあっても、高瀬の生存証明ではなかった。 diff --git a/db/scenarios/cave-lab-locator-cart.yaml b/db/scenarios/cave-lab-locator-cart.yaml index b344176..700ab96 100644 --- a/db/scenarios/cave-lab-locator-cart.yaml +++ b/db/scenarios/cave-lab-locator-cart.yaml @@ -17,6 +17,17 @@ victim: statement: 岩代圭吾は地図解析室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「試料ラベルと測量座標の不一致」に関わる資料が残されている。 +places: + - id: survey-zone + name: 自動測量区画 + shortName: 測量区画 + introduction: 測量カートの校正と位置タグ確認を行う研究区画 + situation: カートと位置タグの充電台が壁沿いに並んでいる + findings: + - id: cart-tag-fastener + statement: 測量カートの収納ベルトには、小型の位置タグを固定できる留め具と新しい擦れ跡がある。 + - id: cart-track-overlay + statement: 端末に残るカートの走行軌跡と位置タグの軌跡は、同じ時間帯に同じ経路を通っている。 briefing: |- ——事件の記録を読み上げます。 @@ -309,11 +320,12 @@ evidences: label: 位置タグと自動測量カートの軌跡比較 description: 香川の位置タグと自動測量カートが、21時00分から15分まで同じ地点を同じ時刻に通過している。二つの軌跡は実質的に重なる。 reveal: - condition: 香川、結城、新堂のいずれかに位置タグの軌跡と自動測量カートの運行を比較できないか尋ねたら開示する。 + condition: 香川、結城、新堂のいずれかに位置タグの軌跡と自動測量カートの運行を比較できないか尋ねたら開示する。または自動測量区画を調べ、カートの走行跡と位置タグの固定跡を照合したら開示する。 sources: - { type: character, id: kagawa } - { type: character, id: yuki } - { type: character, id: shindo } + - { type: location, id: survey-zone } supports: [locator-tag-removable, mapping-cart-auto-loop, kagawa-tag-on-cart, tag-track-matches-cart] contradicts: ["lie:kagawa-location-alibi", "lie:kagawa-wore-tag"] - id: tagless-sighting diff --git a/db/scenarios/coldcase-lodge-borrowed-memory.yaml b/db/scenarios/coldcase-lodge-borrowed-memory.yaml index 98e532a..833fccf 100644 --- a/db/scenarios/coldcase-lodge-borrowed-memory.yaml +++ b/db/scenarios/coldcase-lodge-borrowed-memory.yaml @@ -12,12 +12,20 @@ victim: foundAt: 23:10 foundIn: 書斎 estimatedDeathAt: "22:05" - causeOfDeath: 事件性のある外傷による死亡 - findings: - - id: victim-state - statement: 野上修一は書斎で倒れており、その場で死亡が確認されている。 - - id: victim-motive-material - statement: 遺体のそばには「仕入れ帳の水増し」に関わる資料が残されている。 + findings: [] +places: + - id: case-archive + name: 旧捜査資料箱 + shortName: 旧捜査資料 + introduction: 1979年事件の調書・検視記録・現場写真をまとめた保管箱 + situation: 黄ばんだ封筒と写真袋が、作成日順に綴じ直されている + findings: + - id: original-autopsy-time + statement: 当時の検視記録には、死亡は22時05分ごろと見積もられた旨が記されている。 + - id: original-sighting-source + statement: 事件直後の供述調書で22時30分の暖炉前目撃を自分の体験として述べているのは、一人だけである。 + - id: old-expense-ledger + statement: 押収資料の仕入れ帳には、倉田の担当欄の金額を野上が事件当日に再確認した印が残っている。 briefing: |- ——事件の記録を読み上げます。 @@ -171,6 +179,8 @@ characters: memories: - id: tatsuya-source-confusion detail: 暖炉前の野上の姿を思い浮かべられるが、自分が見たのか真紀の話を何度も聞いたからなのか分からなくなっている。 + - id: death-estimate-memory + detail: 再調査で開封された旧検視記録を読み、当時の死亡推定が22時05分ごろだったことを改めて知っている。 relationships: - character: maki relation: 1979年の友人 @@ -296,12 +306,23 @@ evidences: label: 仕入れ帳の水増し description: 倉田の担当欄で仕入れ額が実際より増やされ、野上が事件当日に再確認の印を付けている。 reveal: - condition: 倉田に野上と事件直前に揉めた帳簿の内容を追及したら開示する。または遺体・現場を調べ、「仕入れ帳の水増し」に関わる資料を確認したら開示する。 + condition: 倉田に野上と事件直前に揉めた帳簿の内容を追及したら開示する。または旧捜査資料箱を調べ、押収された仕入れ帳の該当欄を確認したら開示する。 sources: - { type: character, id: megumi } - - { type: victim, id: victim } + - { type: location, id: case-archive } supports: [megumi-forged-expenses, nogami-found-megumi-fraud] contradicts: ["lie:megumi-no-fraud"] + - id: death-estimate + label: 1979年の検視記録 + description: 当時の検視記録は、発見時の状態から野上の死亡を22時05分ごろと見積もっている。22時30分の暖炉前目撃より前になる。 + reveal: + condition: 旧捜査資料箱を調べて当時の検視記録を確認するか、藤村に再調査で読み直した検視記録の内容を尋ねたら開示する。 + sources: + - { type: location, id: case-archive } + - { type: character, id: tatsuya } + supports: [megumi-killed-nogami] + contradicts: [] + revealsDeathTime: true solution: culprit: megumi summary: 犯人は倉田恵。1979年、仕入れ代の水増しを野上に見抜かれ、22時05分ごろ書斎で野上を襲った。22時30分に野上が暖炉前で生きていたという長年の定説は、真紀が自分の現金窃盗を隠すために作った虚偽の目撃が源だった。事件後の新聞がそれを「三人の目撃」と誤って報じ、47年間の回想で藤村と倉田の記憶にも混ざった。最初の供述へ戻ると、真紀は22時08分に書斎側から出てくる倉田を見ており、倉田には帳簿不正が発覚する動機もあった。 diff --git a/db/scenarios/flood-archive-self-locking-vault.yaml b/db/scenarios/flood-archive-self-locking-vault.yaml index fd8fc10..c3b5a1e 100644 --- a/db/scenarios/flood-archive-self-locking-vault.yaml +++ b/db/scenarios/flood-archive-self-locking-vault.yaml @@ -17,6 +17,17 @@ victim: statement: 今泉孝臣は資料庫で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「修復処置記録と資料状態の不一致」に関わる資料が残されている。 +places: + - id: rare-vault + name: 希少資料庫 + shortName: 資料庫 + introduction: 貴重資料を保管する、厚い防火扉の資料庫 + situation: 重い扉が閉じ、廊下側には開錠用の鍵穴がある + findings: + - id: self-locking-latch + statement: 扉は廊下側から開けるときだけ鍵を使い、外へ出て閉じればラッチが自動で掛かる構造になっている。 + - id: door-contact-window + statement: 扉センサーには21時09分から21時22分まで開放が続き、その後に閉じた記録が残っている。 briefing: |- ——事件の記録を読み上げます。 @@ -315,10 +326,11 @@ evidences: label: 希少資料庫の自動施錠仕様 description: 廊下側から開ける時だけ館長鍵が必要で、外へ出て扉を閉めるとラッチが自動で掛かる。施錠操作に鍵は不要である。 reveal: - condition: 八神か戸塚に資料庫の鍵が開錠と施錠のどちらに必要なのか具体的に尋ねたら開示する。 + condition: 八神か戸塚に資料庫の鍵が開錠と施錠のどちらに必要なのか具体的に尋ねたら開示する。または希少資料庫の扉を調べ、閉じるだけで施錠されるラッチ構造を確認したら開示する。 sources: - { type: character, id: yagami } - { type: character, id: tozuka } + - { type: location, id: rare-vault } supports: [vault-key-only-opens, vault-self-locks, key-found-on-imaizumi] contradicts: ["lie:yagami-key-needed-to-lock"] - id: door-contact-log diff --git a/db/scenarios/gallery-blue-frame.yaml b/db/scenarios/gallery-blue-frame.yaml index 0d218a0..f6eb439 100644 --- a/db/scenarios/gallery-blue-frame.yaml +++ b/db/scenarios/gallery-blue-frame.yaml @@ -17,6 +17,17 @@ victim: statement: 鳥羽薫は収蔵庫前で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「鳥羽の額装検査メモ」に関わる資料が残されている。 +places: + - id: restoration-room + name: 修復室 + shortName: 修復室 + introduction: 紫外線検査装置と修復用の作業台がある部屋 + situation: 検査装置は停止し、作業台だけが照明に浮かんでいる + findings: + - id: uv-power-gap + statement: 紫外線検査装置の履歴には、18時37分から18時51分まで電源が切れていた空白がある。 + - id: uv-resume-time + statement: 装置は18時51分に再起動しており、その前の検査状態が連続していたわけではない。 briefing: |- ——事件の記録を読み上げます。 @@ -257,12 +268,13 @@ evidences: label: 紫外線検査装置の電源履歴 description: 装置は18時35分に起動したが、18時37分から18時51分まで停止している。 reveal: - condition: 榊に検査を続けていた時間帯を尋ねるか、榎本に修復室の機器ログを確認できないか尋ねたら開示する。 + condition: 榊に検査を続けていた時間帯を尋ねるか、榎本に修復室の機器ログを確認できないか尋ねたら開示する。または修復室を調べ、紫外線検査装置の電源履歴を確認したら開示する。 sources: - type: character id: sakaki - type: character id: enomoto + - { type: location, id: restoration-room } supports: [uv-test-started, uv-lamp-off-1837, uv-test-resumed] contradicts: ["lie:sakaki-uv-alibi"] - id: enomoto-sighting diff --git a/db/scenarios/generation-ship-staggered-dawn.yaml b/db/scenarios/generation-ship-staggered-dawn.yaml index 40cedb3..ceec302 100644 --- a/db/scenarios/generation-ship-staggered-dawn.yaml +++ b/db/scenarios/generation-ship-staggered-dawn.yaml @@ -226,6 +226,8 @@ characters: memories: - id: yuna-discovery-clock detail: 医療区時計では06時08分だったが、標準時表示では06時28分だったのを覚えている。 + - id: death-estimate-memory + detail: 医療担当として発見時の状態を確認し、死亡は船内標準時05時57分ごろと見積もっている。人工昼夜表示ではなく標準時で記録した。 relationships: - character: sera relation: 同僚 @@ -312,6 +314,17 @@ evidences: - { type: character, id: yuna } supports: [ yuna-private-medication ] contradicts: [ "lie:yuna-no-private-supply" ] + - id: death-estimate + label: 医療区の死亡推定 + description: 医療センサーによる発見時の確認では、ミラの死亡は船内標準時05時57分ごろと見積もられる。区画ごとの人工時刻とは別の基準である。 + reveal: + condition: 遺体を調べて医療センサーの確認値を見るか、医療担当のユナに死亡推定を船内標準時で尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: yuna } + supports: [sera-killed-mira] + contradicts: [] + revealsDeathTime: true solution: culprit: sera summary: 犯人はセラ・イワノフ。希少種子の流用をミラに見抜かれ、中央評議会へ報告されるのを恐れた。セラは農業区の人工時刻を使って「06時05分には農業区にいた」と語り、聞き手が船内標準時の06時05分だと思うよう誘導した。しかし農業区の06時00分は標準時05時40分で、農業区06時05分は標準時05時45分にすぎない。標準時05時52分にはダリオが種子保管区へ向かうセラを目撃し、05時57分ごろ事件が起きている。区画ごとの複数の朝を一つの時系列へ換算するとアリバイは消える。 diff --git a/db/scenarios/ink-stained-contract.yaml b/db/scenarios/ink-stained-contract.yaml index 4ea2700..74ce02c 100644 --- a/db/scenarios/ink-stained-contract.yaml +++ b/db/scenarios/ink-stained-contract.yaml @@ -11,6 +11,7 @@ victim: introduction: 出版社「青燈社」編集長 foundAt: 21:00 foundIn: 編集長室 + estimatedDeathAt: "20:41" causeOfDeath: 事件性のある外傷による死亡 findings: - id: victim-state @@ -186,6 +187,8 @@ characters: memories: - id: kawase-at-door detail: 20時44分ごろ、編集長室の扉を静かに閉めて離れる川瀬を見た。声をかけたが返事が短かった。 + - id: death-estimate-memory + detail: 発見時の石橋の状態と編集長室の室温を確認しており、死亡は20時41分ごろと見積もられるという確認内容を覚えている。 relationships: - character: kawase relation: 仕事上の取引相手 @@ -306,6 +309,17 @@ evidences: id: fujimoto supports: [fujimoto-leaked-manuscript] contradicts: ["lie:fujimoto-no-leak"] + - id: death-estimate + label: 編集長室の死亡推定 + description: 編集長室の室温と発見時の状態を合わせると、石橋の死亡は20時41分ごろと見積もられる。20時50分の署名時刻より前である。 + reveal: + condition: 遺体を調べて発見時の状態から死亡時刻を推定するか、藤本に発見時に確認した状態と時刻について尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: fujimoto } + supports: [kawase-killed-ishibashi-2041] + contradicts: [] + revealsDeathTime: true solution: culprit: kawase summary: 犯人は川瀬梨奈。海外版権収入の一部を留保していた不正を石橋に見抜かれ、翌朝に契約を打ち切って作家と弁護士へ知らせると告げられた。川瀬は20時38分ごろ編集長室へ入り、20時41分ごろ石橋を襲った。20時44分には藤本が編集長室前から離れる川瀬を見ている。その後20時47分、川瀬は新しい契約書を印刷し、石橋が20時50分に署名したように見える書類を作った。しかしプリンター記録はその紙が20時47分に初めて作られたことを示し、署名を生存証明として使う川瀬の説明を崩す。志堂の盗用と藤本の原稿漏洩は独立した秘密である。 diff --git a/db/scenarios/landslide-clock-museum-eleven-minutes.yaml b/db/scenarios/landslide-clock-museum-eleven-minutes.yaml index 5c73617..114946e 100644 --- a/db/scenarios/landslide-clock-museum-eleven-minutes.yaml +++ b/db/scenarios/landslide-clock-museum-eleven-minutes.yaml @@ -11,12 +11,24 @@ victim: introduction: 私設時計博物館学芸員長 foundAt: 20:42 foundIn: 修復室 + estimatedDeathAt: "20:28" causeOfDeath: 事件性のある外傷による死亡 findings: - id: victim-state statement: 倉橋宗一は修復室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「倉橋の来歴照合メモ」に関わる資料が残されている。 +places: + - id: master-clock + name: 親時計盤 + shortName: 親時計 + introduction: 館内の展示時計と時報へ基準時刻を配る同期盤 + situation: 保守扉に夕方の同期試験票が挟まれたままになっている + findings: + - id: master-clock-offset + statement: 親時計盤の補正値は正しい時刻より十一分進む設定になっている。 + - id: independent-security-clock + statement: 防災端末の時刻は親時計盤と別系統で、同じ瞬間を十一分早い数字で記録している。 briefing: |- ——事件の記録を読み上げます。 @@ -229,6 +241,8 @@ characters: memories: - id: master-clock-looked-odd detail: 20時台に自分の腕時計と中央ホールの時計を見比べ、館内時計の方がかなり進んでいる気がしたが、作業中で深く考えなかった。 + - id: death-estimate-memory + detail: 発見時の確認内容を防災端末の正しい時刻で控えており、倉橋の死亡は20時28分ごろと見積もられると覚えている。 relationships: - character: shiba relation: 同僚 @@ -318,10 +332,11 @@ evidences: label: 親時計と防災端末の時刻差 description: 防災端末が20時19分を記録した瞬間の監視画像で、中央ホールの親時計は20時30分を示している。両系統には十一分の差がある。 reveal: - condition: 城戸か保科に館内時計の精度と、防災端末など別系統の時計との比較を尋ねたら開示する。 + condition: 城戸か保科に館内時計の精度と、防災端末など別系統の時計との比較を尋ねたら開示する。または親時計盤を調べ、防災端末と表示時刻を突き合わせたら開示する。 sources: - { type: character, id: kido } - { type: character, id: hoshina } + - { type: location, id: master-clock } supports: [master-clock-fast-eleven, gallery-clocks-follow-master, security-clock-accurate, half-past-chime-actual-2019] contradicts: ["lie:kido-clock-accurate", "lie:shiba-late-last-seen"] - id: north-corridor-sighting @@ -372,6 +387,17 @@ evidences: supports: [kido-caused-clock-offset, master-clock-fast-eleven] contradicts: ["lie:kido-clock-accurate"] + - id: death-estimate + label: 修復室の死亡推定 + description: 発見時の状態を、親時計とは別系統の防災端末時刻で整理すると、倉橋の死亡は20時28分ごろと見積もられる。 + reveal: + condition: 遺体を調べて発見時の状態を正しい時刻系で確認するか、保科に発見時の確認内容を防災端末の時刻基準で尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: hoshina } + supports: [shiba-killed-kurahashi] + contradicts: [] + revealsDeathTime: true solution: culprit: shiba summary: 犯人は志波沙月。展示時計の来歴を偽ったことを倉橋に見抜かれ、翌朝に展示を外され理事会へ報告される予定だった。館内の親時計は十一分進んでおり、20時30分の鐘が鳴った実時刻は20時19分だった。志波が倉橋と話したのはその直後の20時20分ごろで、志波の「20時31分ごろにも倉橋は生きていた」という説明は十一分ずれている。志波は20時22分ごろ西回廊を離れ、20時24分には朝倉が修復室側の北廊下で志波を目撃している。20時28分ごろ倉橋を襲った後、20時34分ごろ展示準備室へ戻った。館内時計の表示を正しい時刻だと思い込むと、最後の目撃が十一分後ろへずれて犯行時間そのものを誤る仕掛けだった。 diff --git a/db/scenarios/landslide-hotel-frosted-silhouette.yaml b/db/scenarios/landslide-hotel-frosted-silhouette.yaml index 5a5b0c8..fc4b9ea 100644 --- a/db/scenarios/landslide-hotel-frosted-silhouette.yaml +++ b/db/scenarios/landslide-hotel-frosted-silhouette.yaml @@ -208,6 +208,8 @@ characters: memories: - id: missing-coat detail: フロント裏を撮る許可を確認したとき、いつも端に掛かっていた長峰の長い上着だけが消えていたので妙に目についた。 + - id: death-estimate-memory + detail: 最初に執務室へ入ったときの状態を覚えており、確認では長峰の死亡は21時05分ごろと見積もられていた。 relationships: - character: ayase relation: 撮影窓口 @@ -327,6 +329,17 @@ evidences: id: hoshino supports: [hoshino-secret-photo] contradicts: ["lie:hoshino-no-side-sale"] + - id: death-estimate + label: 執務室の死亡推定 + description: 執務室の室温と発見時の状態から、長峰の死亡は21時05分ごろと見積もられる。21時30分に見えた人影より前である。 + reveal: + condition: 遺体を調べて発見時の状態を確認するか、星野に最初に執務室へ入ったときの状態と確認内容を尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: hoshino } + supports: [ayase-killed-nagamine] + contradicts: [] + revealsDeathTime: true solution: culprit: ayase summary: 犯人は綾瀬環。改装費の水増しを長峰に見抜かれ、説明を求められた直後に執務室で長峰を襲った。犯行を21時半以後に見せるため、長峰の長い上着を衣紋掛けに掛け、机上灯で磨りガラスへ人の輪郭を映した。神田はその輪郭を長峰本人だと思い込んだが、顔も声も確認していない。上着が21時18分にはフロント裏から消えていたこと、発見時には執務室の衣紋掛けに掛かっていたこと、衣紋掛けを磨りガラス付近へ移動した跡が残ることを合わせると、目撃は生存証明ではなく偽装だったと分かる。 diff --git a/db/scenarios/midnight-radio-rerun.yaml b/db/scenarios/midnight-radio-rerun.yaml index 23ea222..c9224e6 100644 --- a/db/scenarios/midnight-radio-rerun.yaml +++ b/db/scenarios/midnight-radio-rerun.yaml @@ -19,6 +19,17 @@ victim: statement: 大門修一は第2ブースで倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「大門が保存したスポンサー報告の比較メモ」に関わる資料が残されている。 +places: + - id: playout-room + name: 自動送出室 + shortName: 送出室 + introduction: 収録音源と深夜番組の放送順を管理する送出卓 + situation: 送出卓のモニターと操作盤が待機状態になっている + findings: + - id: midnight-queued-audio + statement: 午前零時の番組冒頭には、事前収録された音声ファイルが自動送出対象として登録されている。 + - id: playout-execution-log + statement: 実行ログでは、午前零時の音声は人のマイク操作ではなく送出卓から自動再生されている。 briefing: |- ——事件の記録を読み上げます。 @@ -203,6 +214,8 @@ characters: memories: - id: hallway-glance detail: 23時50分ごろ、廊下の角から戻ってきた美濃部と目が合い、珍しく彼女が驚いた顔をしたのを覚えている。 + - id: death-estimate-memory + detail: 第2ブースの空調温度と発見時の状態を確認しており、死亡は23時48分ごろと見積もられるという確認内容を覚えている。 relationships: - character: minobe relation: 同僚 @@ -271,12 +284,13 @@ evidences: label: 自動送出システムの実行ログ description: 午前零時の冒頭素材は23時53分に登録され、時刻指定で自動再生された記録が残る。 reveal: - condition: 久世に午前零時の音声が生放送か録音かを尋ねるか、美濃部の「生だった」という説明の技術的根拠を確認したら開示する。 + condition: 久世に午前零時の音声が生放送か録音かを尋ねるか、美濃部の「生だった」という説明の技術的根拠を確認したら開示する。または自動送出室を調べ、午前零時の送出キューと実行ログを確認したら開示する。 sources: - type: character id: kuze - type: character id: minobe + - { type: location, id: playout-room } supports: [ minobe-queued-recording, recorded-opening-aired ] contradicts: [ "lie:minobe-live-alibi" ] - id: corridor-sighting @@ -323,6 +337,17 @@ evidences: id: kuze supports: [ kuze-deleted-demo ] contradicts: [ "lie:kuze-no-deletion" ] + - id: death-estimate + label: 第2ブースの死亡推定 + description: 第2ブースの一定した室温と発見時の状態を合わせると、大門の死亡は23時48分ごろと見積もられる。午前零時の放送より前である。 + reveal: + condition: 遺体を調べて発見時の状態を確認するか、久世にブースの室温記録と発見時の確認内容を尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: kuze } + supports: [daimon-died-2348] + contradicts: [] + revealsDeathTime: true solution: culprit: minobe summary: 犯人は美濃部沙耶。スポンサー向け報告の水増しを大門に知られ、翌朝局長へ報告されることを恐れた美濃部は、夏目が去った後の23時46分に第二収録ブースへ入り、23時48分ごろ大門を襲った。その後23時20分に録音済みだった大門の冒頭音声を23時53分に自動送出へ登録し、午前零時まで大門が生きていたように見せかけた。自動送出ログは「午前零時は生放送だった」という美濃部の説明を崩し、久世の23時50分の目撃は「23時40分以降ブースへ行っていない」という主張と矛盾する。夏目の秘密の面会と久世の私用録音は、それぞれ独立した隠し事にすぎない。 diff --git a/db/scenarios/quarantine-clinic-borrowed-badge.yaml b/db/scenarios/quarantine-clinic-borrowed-badge.yaml index 3120cf4..29cfa4c 100644 --- a/db/scenarios/quarantine-clinic-borrowed-badge.yaml +++ b/db/scenarios/quarantine-clinic-borrowed-badge.yaml @@ -11,12 +11,24 @@ victim: introduction: 白嶺診療所事務長 foundAt: 21:40 foundIn: 事務室 + estimatedDeathAt: "21:08" causeOfDeath: 事件性のある外傷による死亡 findings: - id: victim-state statement: 星名悟は事務室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「本部報告予定の確認メモ」に関わる資料が残されている。 +places: + - id: badge-reader + name: 検査廊下の認証端末 + shortName: 認証端末 + introduction: 防護区画を通る管理バッジの認証端末 + situation: 認証端末の画面が待機表示のまま残っている + findings: + - id: reader-records-badge + statement: 端末が保存しているのは通過したバッジ番号で、装着者の顔や氏名を記録する機能はない。 + - id: orange-badge-entry + statement: 21時18分には橙色の管理バッジが検査廊下を通過した記録が残っている。 briefing: |- ——事件の記録を読み上げます。 @@ -176,6 +188,8 @@ characters: memories: - id: orange-badge-corridor detail: 21時18分、橙色バッジを胸につけたまま検査廊下を通った。防護服姿なので相原には星名と間違われても不思議ではないと思った。 + - id: death-estimate-memory + detail: 検査担当として発見時の確認値を取り、星名の死亡は21時08分ごろと見積もられることを把握している。 relationships: - character: sera relation: 看護主任 @@ -257,12 +271,13 @@ evidences: label: 二十一時十八分の認証記録 description: 記録されているのは橙色バッジの通過であり、使用者の顔や氏名を直接確認した記録ではない。 reveal: - condition: 相原か久我に21時18分の認証記録が何を識別しているのか尋ねたら開示する。 + condition: 相原か久我に21時18分の認証記録が何を識別しているのか尋ねたら開示する。または検査廊下の認証端末を調べ、記録がバッジ番号だけで使用者を識別しないと確認したら開示する。 sources: - type: character id: aihara - type: character id: kuga + - { type: location, id: badge-reader } supports: [orange-badge-passed-2118, aihara-saw-orange-suit] contradicts: ["lie:sera-badge-proves-hoshina"] - id: kuga-corridor-admission @@ -309,6 +324,17 @@ evidences: id: aihara supports: [aihara-left-monitor] contradicts: ["lie:aihara-never-left"] + - id: death-estimate + label: 診療所の死亡推定 + description: 診療所の検査機器で発見時の状態を確認すると、星名の死亡は21時08分ごろと見積もられる。21時18分のバッジ通過より前である。 + reveal: + condition: 遺体を調べて検査機器の確認値を見るか、久我に発見時に取った検査値と死亡推定について尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: kuga } + supports: [hoshina-death-2108] + contradicts: [] + revealsDeathTime: true solution: culprit: sera summary: 犯人は世良美冬。21時18分の認証記録は星名本人の生存証明ではない。その時刻に橙色バッジを着けて検査廊下を通ったのは、20時55分に星名からバッジを借りた久我だった。防護服姿の顔を見分けられなかった相原は、バッジの色だけで星名だと思い込んだ。さらに世良はバッジ貸与をその場で了承しており、使用者が久我であり得ることを知りながら、21時18分の記録を星名の生存根拠として説明していた。星名は実際には21時08分ごろ事務室で世良に襲われている。翌朝、世良が独断変更した同意書管理手順が本部へ報告される予定だったことが動機となった。 diff --git a/db/scenarios/shanghai-warehouse-carbon-copy.yaml b/db/scenarios/shanghai-warehouse-carbon-copy.yaml index 8d75fdc..b0d453c 100644 --- a/db/scenarios/shanghai-warehouse-carbon-copy.yaml +++ b/db/scenarios/shanghai-warehouse-carbon-copy.yaml @@ -167,6 +167,8 @@ characters: memories: - id: chen-blank-box detail: 周の署名はあったのに、時刻の四角だけ白いままだったのが妙に印象に残っている。 + - id: death-estimate-memory + detail: 発見後の検視内容を業務メモへ写しており、周の死亡は21時52分ごろと見積もられていたことを覚えている。 relationships: - character: lin relation: 上司 @@ -267,6 +269,17 @@ evidences: - { type: character, id: wang } supports: [wang-stole-cargo] contradicts: ["lie:wang-no-cargo-theft"] + - id: death-estimate + label: 当夜の検視メモ + description: 発見後に作られた検視メモでは、周の死亡は21時52分ごろと見積もられている。上紙へ22時05分が書き足されるより前である。 + reveal: + condition: 遺体を調べて当夜の検視内容を確認するか、陳に発見後に控えた検視メモの時刻を尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: chen } + supports: [lin-killed-zhou] + contradicts: [] + revealsDeathTime: true solution: culprit: lin summary: 犯人は林雪梅。帳簿外の絹荷を周に見抜かれ、21時52分ごろ事務室で周を襲った。周はその前に翌朝の搬出用として時刻欄が空白の伝票へ署名していたため、林は22時05分になって上紙へ時刻だけを書き足し、周がその時刻まで生きていたように見せた。だが三枚綴りの複写伝票では、同時に書かれた文字なら控えにも写る。署名は三枚にあるのに時刻は上紙だけで、陳も21時40分に時刻欄が空白だったと記録している。 diff --git a/db/scenarios/snowbound-farm-morning-chores.yaml b/db/scenarios/snowbound-farm-morning-chores.yaml index 0eba65b..38775db 100644 --- a/db/scenarios/snowbound-farm-morning-chores.yaml +++ b/db/scenarios/snowbound-farm-morning-chores.yaml @@ -213,6 +213,8 @@ characters: memories: - id: late-corridor detail: 22時すぎ、工具を取りに出たとき、冬木が事務室側から早足で戻ってきたのを覚えている。 + - id: death-estimate-memory + detail: 設備担当として事務室の夜間温度を把握し、発見時の確認では佐久間の死亡は前夜22時10分ごろと見積もられていたことを覚えている。 relationships: - character: fuyuki relation: 同僚 @@ -332,6 +334,17 @@ evidences: id: kuze supports: [kuze-generator-secret] contradicts: ["lie:kuze-no-private-power"] + - id: death-estimate + label: 冬夜の死亡推定 + description: 事務室の夜間温度と発見時の状態を合わせると、佐久間の死亡は前夜22時10分ごろと見積もられる。明け方の朝仕事より大幅に早い。 + reveal: + condition: 遺体を調べて発見時の状態を確認するか、久世に事務室の夜間温度と発見時の確認内容を尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: kuze } + supports: [fuyuki-killed-sakuma] + contradicts: [] + revealsDeathTime: true solution: culprit: fuyuki summary: 冬木紬は出荷代金の流用を佐久間に見抜かれ、翌朝に帳簿を再確認されることを恐れた。前夜22時台に事務室へ向かい佐久間を襲った後、明け方に佐久間が毎朝行っていた家畜の世話を代わりに済ませ、台所にも人が起きているような痕跡を残した。希は灯りを見ただけで佐久間本人を確認しておらず、朝飼いの作業板にも佐久間が毎朝残す時刻の記入がない。さらに六時すぎの冬木の長靴には外へ出た直後の雪解け水と飼料庫の藁が付いており、「朝まで部屋にいた」という説明と矛盾する。朝の生活痕は佐久間の生存証明ではなく、犯行時刻を遅く見せるための偽装だった。 diff --git a/db/scenarios/snowbound-gallery-wrong-crime-scene.yaml b/db/scenarios/snowbound-gallery-wrong-crime-scene.yaml index 7800ae0..c88841f 100644 --- a/db/scenarios/snowbound-gallery-wrong-crime-scene.yaml +++ b/db/scenarios/snowbound-gallery-wrong-crime-scene.yaml @@ -17,6 +17,17 @@ victim: statement: 荻原直哉は保存庫で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「修復工程と材料在庫の不一致」に関わる資料が残されている。 +places: + - id: framing-room + name: 額装作業室 + shortName: 額装室 + introduction: 作品台紙の加工と搬送準備を行う作業室 + situation: 作業台と資材棚が、閉館時のまま残されている + findings: + - id: framing-paper-traces + statement: 床に散った紙片は、その夜に荻原が確認していた作品台紙と同じ材質である。 + - id: cart-used-again + statement: 清掃後に戻された作品搬送台車には、その後もう一度動かされた車輪跡が残っている。 briefing: |- ——事件の記録を読み上げます。 @@ -305,12 +316,13 @@ evidences: label: 額装作業室の台紙片 description: 清掃後の額装作業室に、荻原が確認していた作品台紙と同じ紙片が散っている。 reveal: - condition: 朝倉に事件後の額装作業室で気づいた変化を尋ねたら開示する。 + condition: 朝倉に事件後の額装作業室で気づいた変化を尋ねたら開示する。または額装作業室を調べ、床の台紙片と作業台周辺の痕跡を確認したら開示する。 sources: - type: character id: asakura - type: character id: kosaka + - { type: location, id: framing-room } supports: [ framing-paper-fibers, kosaka-killed-ogiwara-framing ] contradicts: [ "lie:kosaka-vault-crime" ] - id: cart-trace diff --git a/db/scenarios/snowbound-studio-roomtone-loop.yaml b/db/scenarios/snowbound-studio-roomtone-loop.yaml index 27e725c..cf2bb46 100644 --- a/db/scenarios/snowbound-studio-roomtone-loop.yaml +++ b/db/scenarios/snowbound-studio-roomtone-loop.yaml @@ -17,6 +17,17 @@ victim: statement: 冬木圭介は編集室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「未公開音源の複製履歴」に関わる資料が残されている。 +places: + - id: patch-bay + name: 第2ブース監視席 + shortName: 監視席 + introduction: 録音入力を切り替えるパッチ盤と収録端末の席 + situation: パッチ盤と収録端末の電源が残っている + findings: + - id: recorder-loop-route + statement: 第2ブースの録音入力は生マイクではなく、四十七秒の音声素材を繰り返す経路へ切り替えられている。 + - id: waveform-identical + statement: 収録波形は空調音や小さな物音まで四十七秒ごとに同じ形を繰り返している。 briefing: |- ——事件の記録を読み上げます。 @@ -313,10 +324,11 @@ evidences: label: 四十七秒ごとに一致する収録波形 description: 空調音や小さな物音まで含めた波形が四十七秒周期で完全一致し、同じ室内音が繰り返し再生されていたと分かる。 reveal: - condition: 真鍋か牧村に収録ファイルが本当に生マイク入力だったか、波形の反復と入力経路を含めて尋ねたら開示する。 + condition: 真鍋か牧村に収録ファイルが本当に生マイク入力だったか、波形の反復と入力経路を含めて尋ねたら開示する。または第2ブースの監視席を調べ、録音入力と波形の反復を確認したら開示する。 sources: - { type: character, id: manabe } - { type: character, id: makimura } + - { type: location, id: patch-bay } supports: [manabe-made-roomtone-loop, loop-routed-to-recorder, recording-repeats-identically] contradicts: ["lie:manabe-booth-alibi", "lie:manabe-live-input"] - id: equipment-corridor-sighting diff --git a/db/scenarios/storm-hydropower-rising-walkway.yaml b/db/scenarios/storm-hydropower-rising-walkway.yaml index b42a17b..f4bbc0d 100644 --- a/db/scenarios/storm-hydropower-rising-walkway.yaml +++ b/db/scenarios/storm-hydropower-rising-walkway.yaml @@ -17,6 +17,17 @@ victim: statement: 峰岸達也は旧制御室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「未実施箇所の点検票」に関わる資料が残されている。 +places: + - id: old-walkway + name: 旧保守歩廊 + shortName: 旧歩廊 + introduction: 排水区画に残る、現在は使われていない保守通路 + situation: 現在は増水で水に覆われ、入口から先へ進めない + findings: + - id: walkway-connects-control + statement: 設備図と入口の表示から、この歩廊は正面扉を通らず旧制御室へ入れる接続口まで続いている。 + - id: reddish-silt-floor + statement: 水際より手前の床には、この歩廊周辺に特有の赤褐色の堆積泥が残っている。 briefing: |- ——事件の記録を読み上げます。 @@ -324,12 +335,13 @@ evidences: label: 旧保守歩廊の接続図 description: 正面扉とは別に、排水区画側から旧制御室へ接続する保守歩廊がある。 reveal: - condition: 城戸に旧制御室の保守経路を尋ねるか、谷口に設備図上の接続を確認したら開示する。 + condition: 城戸に旧制御室の保守経路を尋ねるか、谷口に設備図上の接続を確認したら開示する。または旧保守歩廊を調べ、旧制御室への接続口を確認したら開示する。 sources: - type: character id: kido - type: character id: taniguchi + - { type: location, id: old-walkway } supports: [ old-walkway-connects ] contradicts: [ "lie:kido-impossible-entry" ] - id: reddish-silt diff --git a/db/scenarios/storm-island-scheduled-mail.yaml b/db/scenarios/storm-island-scheduled-mail.yaml index afcdbbb..aa2de48 100644 --- a/db/scenarios/storm-island-scheduled-mail.yaml +++ b/db/scenarios/storm-island-scheduled-mail.yaml @@ -272,6 +272,8 @@ characters: memories: - id: office-corridor-aizawa detail: 21時29分ごろ、設備盤の時計を確認した直後に、執務室前の廊下から相沢が出てきた。本人は気づかず講義室方向へ急いでいた。 + - id: death-estimate-memory + detail: 発見時に執務室の状態を確認しており、榊原の死亡は21時28分ごろと見積もられるという確認内容を覚えている。 relationships: [] revelations: @@ -420,6 +422,17 @@ evidences: supports: [ horie-hid-room-use ] contradicts: [ "lie:horie-no-private-room" ] + - id: death-estimate + label: 執務室の死亡推定 + description: 執務室の室温と発見時の状態から、榊原の死亡は21時28分ごろと見積もられる。21時42分のメール送信より前である。 + reveal: + condition: 遺体を調べて発見時の状態を確認するか、堀江に執務室へ入ったときの状態と確認内容を尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: horie } + supports: [aizawa-killed-sakakibara] + contradicts: [] + revealsDeathTime: true solution: culprit: aizawa summary: 犯人は相沢奈緒。小口支出を使った財団資金の流用を榊原に見抜かれ、翌朝に文書管理から外され監査担当へ記録を渡される予定だった。相沢は榊原が夕方に21時42分の予定送信メールを設定したことを知っていた。21時24分ごろ執務室へ入り、21時28分ごろ榊原を襲い、21時29分には堀江が執務室前から出てくる相沢を目撃している。相沢は21時35分から羽鳥と講義室にいて、その後には確かなアリバイがある。21時42分、榊原が夕方に作成したメールが自動送信され、榊原がその時刻まで生きていたように見えたため、相沢の21時35分以降のアリバイが犯行時間と重なって見えた。 diff --git a/db/scenarios/storm-mountain-inn-echoed-cane.yaml b/db/scenarios/storm-mountain-inn-echoed-cane.yaml index 00cf3e2..66f6223 100644 --- a/db/scenarios/storm-mountain-inn-echoed-cane.yaml +++ b/db/scenarios/storm-mountain-inn-echoed-cane.yaml @@ -18,6 +18,17 @@ victim: statement: 桐谷宗介は帳場奥で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「不自然な仕入れ伝票」に関わる資料が残されている。 +places: + - id: old-stairs + name: 厨房脇の旧階段 + shortName: 旧階段 + introduction: 厨房脇に残る、普段使われない古い木造階段 + situation: 古い木造の隔壁と手すりがそのまま残されている + findings: + - id: partition-carries-taps + statement: 手すりを軽く叩くと、木の中空隔壁を通って二階廊下側へ乾いた音がよく響く。 + - id: cane-height-marks + statement: 隔壁には、桐谷の杖の金属部分と高さの合う新しい打痕が残っている。 briefing: |- ——事件の記録を読み上げます。 @@ -213,6 +224,8 @@ characters: memories: - id: hollow-wall detail: 旧階段と二階北廊下の間は昔の中空壁が連続していて、硬い音だけが意外なほど遠くへ抜ける。 + - id: death-estimate-memory + detail: 発見時の帳場奥の状態を確認しており、桐谷の死亡は21時08分ごろと見積もられるという確認内容を覚えている。 relationships: - character: akiwa relation: 宿スタッフ @@ -278,12 +291,13 @@ evidences: label: 旧階段と二階廊下の中空壁 description: 二つの区画は同じ古い中空隔壁に接し、旧階段側の硬い音が二階から聞こえることがある。 reveal: - condition: 榊原に古い建物の音の伝わり方を尋ねるか、秋庭に旧階段で音が響くことを知っていたか確認したら開示する。 + condition: 榊原に古い建物の音の伝わり方を尋ねるか、秋庭に旧階段で音が響くことを知っていたか確認したら開示する。または厨房脇の旧階段を調べ、隔壁の構造と音の伝わり方を確認したら開示する。 sources: - type: character id: sakakibara - type: character id: akiwa + - { type: location, id: old-stairs } supports: [old-partition-transmits] contradicts: ["lie:akiwa-cane-proves-alive"] - id: cane-kitchen @@ -344,6 +358,17 @@ evidences: id: sakakibara supports: [sakakibara-secret-materials] contradicts: ["lie:sakakibara-no-materials"] + - id: death-estimate + label: 帳場奥の死亡推定 + description: 帳場奥の室温と発見時の状態から、桐谷の死亡は21時08分ごろと見積もられる。21時25分に聞かれた杖音より前である。 + reveal: + condition: 遺体を調べて発見時の状態を確認するか、榊原に発見時の室内と確認内容を尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: sakakibara } + supports: [akiwa-killed-kiritani] + contradicts: [] + revealsDeathTime: true solution: culprit: akiwa summary: 犯人は秋庭澄。仕入れ業者から謝礼を受けていたことを桐谷に見抜かれ、翌朝の契約見直しを告げられたため桐谷を襲った。秋庭は桐谷の杖を厨房脇へ持ち出し、旧階段の中空壁へ金属部分を当てて特徴的な音を作った。森崎は長年聞き慣れた音だったため二階を桐谷が歩いていると思ったが、実際には足音も声も確認していない。杖が厨房脇で見つかったこと、旧階段に新しい打痕があること、建物の構造上そこから二階へ硬い音が伝わることを合わせると、九時二十五分の音は桐谷の生存証明ではない。 diff --git a/db/scenarios/storm-planetarium-reflected-witness.yaml b/db/scenarios/storm-planetarium-reflected-witness.yaml index 4cd71cd..8d63211 100644 --- a/db/scenarios/storm-planetarium-reflected-witness.yaml +++ b/db/scenarios/storm-planetarium-reflected-witness.yaml @@ -11,12 +11,24 @@ victim: introduction: 星見ヶ丘天象館館長 foundAt: 22:00 foundIn: 投影準備室 + estimatedDeathAt: "21:24" causeOfDeath: 事件性のある外傷による死亡 findings: - id: victim-state statement: 犬塚誠は投影準備室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「運営法人への予算報告草案」に関わる資料が残されている。 +places: + - id: observation-glass + name: 投影室の観察ガラス + shortName: 観察ガラス + introduction: 投影室と廊下を隔てる、大型の観察窓 + situation: 投影室と廊下のあいだを、大きな一枚ガラスが隔てている + findings: + - id: corridor-reflection + statement: 投影室を暗くして廊下側を明るくすると、ガラスには廊下側に立つ人物の像が強く映り込む。 + - id: witness-position-replay + statement: 21時35分の立ち位置を再現すると、廊下にいた人物の像が投影室内の人影のように重なる。 briefing: |- ——事件の記録を読み上げます。 @@ -175,6 +187,8 @@ characters: detail: ガラス越しに白い上着の上半身が見えたので犬塚だと思ったが、顔をはっきり見たわけではない。 - id: onodera-behind detail: 「館長、まだ中にいるんですね」と言ったとき、小野寺がすぐ後ろから「そうみたいですね」と返事をしたのを覚えている。 + - id: death-estimate-memory + detail: 発見時の投影準備室の状態を確認しており、犬塚の死亡は21時24分ごろと見積もられるという確認内容を覚えている。 relationships: - character: onodera relation: 同僚 @@ -244,12 +258,13 @@ evidences: label: 観察ガラスの照明条件テスト description: 投影室を暗くして廊下を明るくすると、観察ガラスには室内より廊下側の人物が強く反射する。 reveal: - condition: 松田か小野寺に投影室消灯時の観察ガラスの見え方を具体的に尋ねたら開示する。 + condition: 松田か小野寺に投影室消灯時の観察ガラスの見え方を具体的に尋ねたら開示する。または投影室の観察ガラスを調べ、消灯時の反射を同じ立ち位置で再現したら開示する。 sources: - type: character id: matsuda - type: character id: onodera + - { type: location, id: observation-glass } supports: [booth-dark-2130, glass-reflects-dark-booth, onodera-knew-reflection] contradicts: ["lie:onodera-sighting-was-inuzuka"] - id: white-jacket-record @@ -308,6 +323,17 @@ evidences: id: asakura supports: [asakura-hid-stock-loss] contradicts: ["lie:asakura-stock-clean"] + - id: death-estimate + label: 投影準備室の死亡推定 + description: 投影準備室の温度と発見時の状態を合わせると、犬塚の死亡は21時24分ごろと見積もられる。21時35分の人影目撃より前である。 + reveal: + condition: 遺体を調べて発見時の状態を確認するか、松田に投影準備室へ入ったときの状態と確認内容を尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: matsuda } + supports: [inuzuka-death-2124] + contradicts: [] + revealsDeathTime: true solution: culprit: onodera summary: 犯人は小野寺莉香。21時35分に松田が観察ガラスで見た白い人物像は、投影室内の犬塚ではなかった。投影室は機器点検で暗く、廊下側が明るかったため、ガラスには松田の数歩後ろにいた小野寺が反射していた。小野寺は犬塚と同型の白い展示用上着を着ており、松田は顔を確認せず犬塚だと思い込んだ。小野寺自身はガラスの反射条件を以前から知っていたうえ、松田のすぐ後ろにいたのに「館長はまだ中にいた」と誤認を補強している。犬塚は実際には21時24分ごろ小野寺に襲われていた。翌朝、犬塚は小野寺による無断の予算振替を運営法人へ報告する予定だった。 diff --git a/db/scenarios/thunder-cablecar-occupied-cabin.yaml b/db/scenarios/thunder-cablecar-occupied-cabin.yaml index efe6203..b2230f7 100644 --- a/db/scenarios/thunder-cablecar-occupied-cabin.yaml +++ b/db/scenarios/thunder-cablecar-occupied-cabin.yaml @@ -18,6 +18,17 @@ victim: statement: 高瀬修司は運行事務室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「制動点検記録と作業履歴の不一致」に関わる資料が残されている。 +places: + - id: emergency-cabin + name: 非常用搬器 + shortName: 非常搬器 + introduction: 非常時と保守点検に使う、小型の予備搬器 + situation: 営業終了後の点検位置で停止している + findings: + - id: seat-senses-weight + statement: 点検席のセンサーは人物を識別せず、一定以上の重量が掛かると「乗員1」を表示する。 + - id: tool-case-triggers-seat + statement: 保守用工具ケースだけを点検席へ置いても、監視盤の表示は「乗員1」へ切り替わる。 briefing: |- ——事件の記録を読み上げます。 @@ -341,11 +352,12 @@ evidences: label: 非常用搬器の座席重量仕様 description: 『乗員1』は一定以上の荷重で点灯し、保守用工具ケースだけでも同じ表示になる。21時台にはそのケースが点検席へ置かれていた。 reveal: - condition: 須藤、長峰、折原のいずれかに『乗員1』表示の検知方式と工具ケースについて尋ねたら開示する。 + condition: 須藤、長峰、折原のいずれかに『乗員1』表示の検知方式と工具ケースについて尋ねたら開示する。または非常用搬器を調べ、座席センサーが重量だけを検知することと工具ケースの重さを確認したら開示する。 sources: - { type: character, id: sudo } - { type: character, id: nagamine } - { type: character, id: orihara } + - { type: location, id: emergency-cabin } supports: [ cabin-display-weight-based, diff --git a/db/scenarios/victorian-underground-last-telegram.yaml b/db/scenarios/victorian-underground-last-telegram.yaml index 04cd21e..7b3b9ca 100644 --- a/db/scenarios/victorian-underground-last-telegram.yaml +++ b/db/scenarios/victorian-underground-last-telegram.yaml @@ -18,6 +18,17 @@ victim: statement: エドワード・ヘイルは測量室で倒れており、その場で死亡が確認されている。 - id: victim-motive-material statement: 遺体のそばには「水増しされた資材帳簿」に関わる資料が残されている。 +places: + - id: shaft-two-telegraph + name: 第二立坑の電信機 + shortName: 第2電信 + introduction: 二つの立坑を結ぶ、工事連絡用の電信機 + situation: 送信キーと記録用の紙束が作業机の上に残されている + findings: + - id: sender-not-recorded + statement: 電信機と受信簿が残すのは電文と受信時刻だけで、第二立坑で誰が送信キーを操作したかは記録されない。 + - id: mark-can-be-copied + statement: 過去の工程電文にはヘイルが使う短い末尾符号が何度も残り、作業関係者が見られる状態になっている。 briefing: |- ——事件の記録を読み上げます。 @@ -211,6 +222,8 @@ characters: memories: - id: thomas-tunnel-sighting detail: 蒸気管の点検で振り返ったとき、第二立坑側へ急ぐベルの上着と帽子をはっきり見た。 + - id: death-estimate-memory + detail: 測量室でヘイルを発見したときの状態を作業記録へ残しており、死亡は22時03分ごろと見積もられるという確認内容を覚えている。 relationships: - character: bell relation: 上司 @@ -285,6 +298,26 @@ evidences: - { type: victim, id: victim } supports: [bell-bid-fraud, helale-confronted-bell] contradicts: [] + - id: telegraph-mechanism + label: 第二立坑の電信機の送信仕様 + description: 第二立坑の送信キーは操作者を識別せず、既知の符号列を誰でも同じ形で送れる。 + reveal: + condition: 第二立坑の電信機を調べ、送信者を識別する仕組みがないことと符号表を確認したら開示する。 + sources: + - { type: location, id: shaft-two-telegraph } + supports: [telegraph-no-sender-id] + contradicts: [] + - id: death-estimate + label: 現場の死亡推定記録 + description: 測量室の室温と発見時の確認記録から、ヘイルの死亡は22時03分ごろと見積もられる。22時12分の電信より前である。 + reveal: + condition: 遺体を調べて発見時の状態を確認するか、トーマスに測量室での発見時の状態と記録内容を尋ねたら開示する。 + sources: + - { type: victim, id: victim } + - { type: character, id: thomas } + supports: [bell-killed-hale] + contradicts: [] + revealsDeathTime: true solution: culprit: bell summary: 犯人はアーサー・ベル。資材費水増しの発覚を恐れ、第二立坑の測量室でヘイルを襲った。その後、ヘイルしか使わないように見えた末尾符号を付けて22時12分に電信を送り、被害者がその時刻まで生きていたように見せた。しかし電信機は送信者の身元を記録せず、ベルは以前から符号を知っていた。さらに21時58分にはトーマスが第二立坑側でベルを目撃しており、第一立坑にいたという説明も崩れる。