dsDevices / dsAlerts(disconnected) を実DB経由にする)| 項目 | 内容 |
|---|---|
| 実行環境 | ★Cursor 内の Claude が実装します★(.claude/commands/d-watch.md の役割分担表を参照) |
| 実行状態 | ★実行済み★(D-097。段階0〜5すべて完了。origin/develop へ push 済み。着手前確認1件はA案でユーザー回答済みのうえで着手した。着手途中で dwatch_local のスキーマ再同期(指示書Q・D-096)を挟んでいる) |
| 実行順 | ★指示書M(D-086)の続き★。★指示書M が作った scripts/seed-local-db.ts / lib/db/prisma.ts / lib/db/local-guard.ts / lib/db/use-fixtures.ts の上に積む作業★です。★指示書M が実装した prismaSource.facilities() / schedules() は1文字も変えません★。★触るコードが重なる未実行の指示書はありません★(未実行は ★指示書I(lib/auth/ / proxy.ts)/指示書J(lib/settings/)★ の2本 — ★着手直前に自分で確認すること★)が、★docs/decisions.md / docs/handoff.md は共通で触ります★。★同時に走らせないこと★ |
| 取得する決定番号 | ★着手直前に grep -n "^### D-0" docs/decisions.md で最終番号を確認すること★。★作成時点の最終は D-090(docs/decisions.md:3325)なので、最初の空き番号は D-091 の見込み★。★2026-09-07 に D-068 の二重採番が実際に起きています(D-070 冒頭)。2026-09-09 にも D-089 の取り合いが起きかけました(D-090 冒頭)。必ず自分で確認してください★ |
| 前提 | ★D-090★(docs/decisions.md:3325。本指示書の設計根拠。★未決20件の一覧★) / ★D-086★(:2814。第1歩の実行記録。★Edge Runtime バンドル破壊の地雷を含む★) / ★D-065★(:474。Q1-1 備考 :482 / Q1-2 controls.status :483 / Q1-3 control_error_histories.status :484) / ★D-067 項1★(:604。★2系統の取得元は完全に別★) / ★D-070 D-3★(:1133。controls.status を読むだけでよい。訂正節は :1161) / D-042(:226。書き込みは4カラムのみ) / D-050(:308。通ってからコミット) / D-044(:230。fixtures で開発する) / D-056(:353。schema.prisma を手で書き換えない) |
| 段階数 | ★6段階★(段階0〜段階5) |
| 着手前の基準 | ★着手直前に自分で npm test / npx tsc --noEmit / npm run lint を実行し、その実測値を基準にすること★(作成時点の参考値は D-089 の「31ファイル / 610テスト / tsc 0 / lint 0」だが、★鵜呑みにしない★) |
| 作成 | 2026-09-09(作成時点の決定事項最終番号 D-090 / docs/handoff.md の最新は 追記25(docs/handoff.md:2061)) |
実行後はこの表と、文書末尾の「実行状態」表を更新してください。
★この指示書を読んでいるあなたへ★
.claude/commands/d-watch.mdに「実装は Cursor 側、Claude Code は指示書作成まで」とありますが、 これは2つの環境の役割分担です。Cursor の中で動いている Claude は実装担当です。 この指示書を渡されたなら、調査や改訂ではなく実装を行ってください。★★これは「第2歩」です。うまくいかなければ途中で止めて構いません。★★ ★
.env.localのUSE_FIXTURESをtrueに戻すだけで、 完全に元の状態(fixtures で全画面が動く状態)に戻ります★。 ★DB に書いたデータもdwatch_localの中にしかありません。★
security_off を含めないのか★★★dsAlerts() は disconnected だけを返します。security_off は返しません。★
★注記(2026-09-09 追記。D-091 項3)★: ★ユーザー判断により、
security_offは そもそもアラートから外す方向で内部調整中です★(「セキュリティが無効であっても異常ではない」)。 ★したがって本指示書の「disconnectedのみ」という範囲は、結果的に最終形に近い可能性があります★。 ★ただし確定ではないので、本指示書の方針(第3歩でsecurity_offを扱う)はそのままにしておきます★。
DsAlert(lib/types/index.ts:312-330)が要求する ★id / device_serial / resolution_status / responded_at / resolved_at / memo の6フィールド★に ★対応するカラムが security_statuses(prisma/schema.prisma:362-377)にも security_status_histories(:379-392)にも存在しません★(D-090 項3-9)。 両テーブルが持つのは id / facility_id / section_id / status / reported_at / created_at だけです。
さらに ★security_statuses.reported_at は @updatedAt★(prisma/schema.prisma:368)なので、 ★detected_at(「いつからセキュリティが解除されたままか」)が取れません★(D-090 項3-10)。
★この6つを埋めるには推測が要ります。それは CLAUDE.md §7「仕様が不明な箇所を推測で実装すること」の 禁止に真正面から当たります。★
一方 disconnected 側は ★取得元が完全に確定しています★:
| 種別 | 取得元 | 根拠 |
|---|---|---|
★disconnected★ |
★control_error_histories★(制御盤異常のエラー) |
★D-067 項1(docs/decisions.md:604)★ |
security_off |
security_statuses(状態から導出。エラーではない) |
同上。★この2つを同じテーブルから取ろうとしないこと★ |
★USE_FIXTURES=false にすると、以下が起こります。これは想定内であり、バグではありません★:
| 画面 | 変化 | 理由 |
|---|---|---|
★/ds-alerts★ |
★12件 → 5件 に減る★ | ★security_off 7件が返らなくなる★ |
★/dashboard のセキュリティバナー★ |
★出なくなる(空配列)★ | ★computeSecurityBanner()(lib/dashboard/aggregate.ts:113-125)が alert_type === "security_off" で絞っているため★ |
★★「静かに減る」ので、段階4 の確認項目に件数の突き合わせを必ず入れてあります。飛ばさないでください。★★ ★差がちょうど7件であることを数えること★(fixtures の内訳は security_off 7件 / disconnected 5件 — lib/fixtures/ds-alerts.ts を実測して確かめること)。
★security_off は第3歩で扱います★(D-090 項2 の表)。
DS-012 をどう扱うか★事実(★すべて現物で裏を取ってから使うこと★):
lib/fixtures/facilities.ts:213 の robots: []) → ★シードは「ロボット1台につき section を1件」作る★(scripts/seed-local-db.ts:207-224)ので ★施設12 には sections が0件★になるd_security_enabled: false★(lib/fixtures/facilities.ts:212)DS-012(lib/fixtures/ds-devices.ts:109-115)と 対応するアラート(lib/fixtures/ds-alerts.ts:140-152。id: 10)★ があるcontrols.section_id は NOT NULL の sections への FK★ (prisma/schema.prisma:182 / :193)。★そのままではシードできず、FK 違反で止まる★選択肢:
| 案 | 内容 | 帰結 |
|---|---|---|
| ★A(推奨)★ | ★DS-012 とそのアラートをシードから除外する★ |
★指示書M で施設12 のスケジュールを除外したのと同じ扱い★(D-086 / scripts/seed-local-db.ts:128-149)。★d_security_enabled: false の施設が D-Sec デバイスを持つのは、そもそも fixtures 側の矛盾★。★/ds-logs が 12台 → 11台 になります★(異常5→4) |
| B | 施設12 に sections を1件作る(ロボットは無いまま) |
★「1清掃エリア = 1ロボット」(CLAUDE.md §8)の前提に反する section が DB に生まれる★ |
| C | DS-012 を別の施設に付け替える |
★fixtures と DB で施設が変わる。段階4 の突き合わせが読みにくくなる★ |
★A を採る場合、/ds-logs の台数が 12 → 11 に変わります。段階4 の期待値を書き換えること。★ ★回答を得るまで段階1 に入らないこと★(CLAUDE.md §0-1「仕様が不明なら止まる」)。
lib/db/index.ts の prismaSource(:149-257)は、指示書M で facilities()(:162-198)と schedules()(:199-238)の2本が実装済みです。★残り10本は notImplemented() のまま★。
本指示書で実装するのは ★そのうち2本だけ★:
| メソッド | 現在地 | 使われる画面 |
|---|---|---|
★dsDevices()★ |
lib/db/index.ts:156-158 |
★F-06 D-Secログ★(/ds-logs)。app/api/ds-devices/route.ts:42 / app/api/ds-devices/[serial_number]/note/route.ts |
★dsAlerts()★(★disconnected のみ★) |
lib/db/index.ts:159-161 |
★F-07 D-Secアラート★(/ds-alerts)+ ★F-01 ダッシュボード★。app/api/ds-alerts/route.ts:43 / app/api/dashboard/route.ts:59 |
★この2本を選んだ理由★(D-090 項2):
★fixtures の施設名の不整合が、DB 経由にすることで構造的に消えます★(D-090 項4):
| # | fixtures の値 | 施設マスタの実名 | 該当箇所 |
|---|---|---|---|
| 1 | ★「メガバリューマート緑野第二店」★(facility_id: 12) |
★フレッシュタウン湖畔店★(lib/fixtures/facilities.ts:204) |
lib/fixtures/ds-devices.ts:111 / lib/fixtures/ds-alerts.ts:145 |
| 2 | ★「バリューマート城南店」★(facility_id: 10) |
★フレッシュタウン大和店★(lib/fixtures/facilities.ts:169) |
lib/fixtures/ds-devices.ts:103 / lib/fixtures/ds-alerts.ts:132 |
★#1 は既知(D-086 項1 / 項15)、#2 は D-090 で初めて記録された新発見★です。
★なぜ DB にすると消えるのか★: controls / control_reports / control_error_histories は いずれも facilities への FK を持つ(prisma/schema.prisma:192 / :333 / :353)ので、 ★施設名は結合で取ることになり、手書きの文字列を入れる余地が構造的に無くなる★からです。 ★段階1 で facility_name をシードに渡さないこと(後述 1-6)が、この成果の前提です。★
prisma/schema.prisma の実測)★| テーブル | model | 行 | ★deleted_at★ |
備考 |
|---|---|---|---|---|
controls |
Control |
:174-198 |
★ある★(:188) |
serial_number(:176)/ facility_id(:180)/ ★section_id NOT NULL FK★(:182, :193)/ status(:183)/ inactive(:184) |
control_reports |
ControlReport |
:323-340 |
★無い★ | control_id(:327)/ serial_number(:328)/ ★reported_at★(:329) |
control_error_histories |
ControlErrorHistory |
:342-360 |
★無い★ | control_id(:346)/ ★status★(:347)/ ★comment★(:348)/ ★occurred_at★(:349)/ responded_at(:350)/ resolved_at(:351)。★created_at / updated_at も無い★ |
sections |
Section |
:142-172 |
ある(:151) |
★section_name★(:145) |
★deleted_at が無いテーブルに where: { deleted_at: null } を書くと型エラーになります(=安全側)★。 ★型エラーが出たら「このテーブルには論理削除が無い」というサインです。無理に回避しないこと。★
| 変換元 | 変換先 | 根拠 |
|---|---|---|
★controls.status = 0★ |
★DsDevice.connection_status = "ok"★ |
★D-065 Q1-2(docs/decisions.md:483)★ |
★controls.status = 1★ |
★DsDevice.connection_status = "error"★ |
同上。★3状態目は存在しない★(lib/types/index.ts:284-290) |
★control_error_histories.status = 0★ |
★DsAlert.resolution_status = "pending"★ |
★D-065 Q1-3(docs/decisions.md:484)★ |
| ★= 1★ | ★"in_progress"★ |
同上 |
| ★= 2★ | ★"done"★ |
同上 |
★判定の再現はしないこと★: 通信断のしきい値(link_schedules.intervals)から自分で 異常を計算し直す必要はありません。★TeamWorks 側のサーバーが判定結果を controls.status に 格納するので、DFA は読むだけです★(D-070「★通信断判定についての訂正★」。docs/decisions.md:1161)。
★既存のシード(scripts/seed-local-db.ts:152-158)が入れている7テーブルの後に足します★:
(既存)groups → customers → devices → facilities → sections → robots → clean_schedules (追加) → controls → control_reports → control_error_histories
★削除は挿入の逆順★(scripts/seed-local-db.ts:151-158 と同じ流儀)。
note)はメインDBに無い★★DsDevice.note に対応するカラムは controls にありません★(D-065 Q1-1。docs/decisions.md:482)。 ★実値は app/api/ds-devices/route.ts:75-78 が JSON ストア(data/settings/ds-device-notes.json)から マージします★。★prismaSource 側は何もしません(常に null を返す)★。
| # | ★してはいけないこと★ | 理由 |
|---|---|---|
| 1 | ★★security_off 側を推測で実装すること★★ |
★対応カラムが存在しない★(D-090 項3-9 / 3-10)。★CLAUDE.md §7「仕様が不明な箇所を推測で実装すること」の禁止★。★件数が減るのは想定内。埋めようとしないこと★ |
| 2 | ★★メインDB(TeamWorks 側)に接続・書き込みすること★★ | CLAUDE.md §2 / §7 / D-042。★作業は dwatch_local の中だけで完結します★。★接続先ホスト名は未受領なので、そもそも繋がりません★ |
| 3 | ★dwatch_ddl に書き込むこと★ |
★参照専用★(受領した実DDL の姿を保つため) |
| 4 | ★prisma migrate / prisma db push / prisma db pull を使うこと★ |
★CLAUDE.md §7 / D-056 / D-059★。★dwatch_local には余分な4列があり db push は DROP しようとします★(D-086 項3)。★テーブルは既にあるので不要★ |
| 5 | ★prisma/schema.prisma を手で書き換えること★ |
★D-056★。★db pull の生成物が正★ |
| 6 | ★lib/db/prisma.ts / lib/db/local-guard.ts / lib/db/use-fixtures.ts を変更すること★ |
★指示書M で完成しており、そのまま効きます★。★特に use-fixtures.ts の切り出しは Edge Runtime バンドル破壊の修正そのものです★(D-086。553b3cc)。★戻すと /api/reports/* が404になります★ |
| 7 | ★prismaSource.facilities() / schedules() を変更すること★ |
★指示書M の成果物★。★本作業は「その隣に2本足す」だけです★ |
| 8 | ★lib/ds-devices/query.ts / lib/ds-alerts/query.ts / API ルート2本 / 画面2枚を変更すること★ |
★本作業はデータ層の差し替えです★。★これらが1文字も変わらないまま画面が実DBで動くことが、変換が正しいことの証明になります★ |
| 9 | ★lib/db/ にフィルタ・ページング・ソートを持ち込むこと★ |
★lib/db/index.ts:59-63 の設計方針★(「フィルタや集計はこの層では行わず、APIルート側で組み立てます」)。★DataSource は引数を取らない契約(:74-82)★ |
| 10 | ★inactive で絞り込むこと★ |
★第1歩と同じ方針(D-086 / lib/db/index.ts:165-167)★。★inactive は「無効化」であって削除ではなく、画面が見せ分ける可能性がある。仕様が未確定なので絞らない側に倒す★ |
| 11 | ★lib/fixtures/ を削除・改変すること★ |
★CLAUDE.md §9.5★(DB接続後も残す)。★施設名の不整合も「直さない」— DB 経由で消えることを実測するのが今回の成果です★ |
| 12 | ★テストを DB 依存にすること★ | ★遅く・不安定になり、DB が空の環境で落ちます★。★テストは fixtures と純粋関数だけで書く★ |
| 13 | ★シードに実在の住所・電話番号・社名・人名を入れること★ | CLAUDE.md §7 / D-043。★fixtures の架空値をそのまま使えば安全★ |
| 14 | ★git add -A を使うこと★ |
CLAUDE.md §7。★過去に別作業の途中ファイルを巻き込む事故が起きています。必ずファイルパスを明示★ |
| 15 | ★.env.local をコミットすること★ |
CLAUDE.md §9.3。★git add でパスを明示すれば入ってしまいます★。★確認後は USE_FIXTURES=true に戻すこと★ |
| 16 | ★DB のユーザー名・パスワードを git 管理下の文書やコードに書くこと★ | CLAUDE.md §9.3。★.env.local にのみ置く★ |
| 段階 | 内容 | コミット |
|---|---|---|
| 段階0 | 前提の確認(★読むだけ★) | コミットなし |
| 段階1 | シードに D-Sec の3テーブルを足す | 段階1で1コミット |
| 段階2 | prismaSource.dsDevices() を実装する |
段階2で1コミット |
| 段階3 | prismaSource.dsAlerts()(disconnected のみ)を実装する |
段階3で1コミット |
| 段階4 | ★動作確認(ここが本題)★ | コミットなし |
| 段階5 | 記録と push | 段階5で1コミット + push |
★段階ごとに npx tsc --noEmit / npm run lint / npm test を実行し、通ってからコミットすること★ (D-050。docs/decisions.md:308)。
| # | 対象 | 見る点 |
|---|---|---|
| 1 | ★D-065 Q1-2 / Q1-3★(docs/decisions.md:483-484) |
★controls.status は 0/1 の2値★ / ★control_error_histories.status は 0/1/2★ |
| 2 | ★D-067 項1★(docs/decisions.md:604) |
★2系統の取得元は完全に別★。★同じテーブルから取ろうとしないこと★ |
| 3 | ★D-070 D-3★(docs/decisions.md:1133)+ ★訂正節★(:1161) |
★controls.status を読むだけでよい★。★intervals から判定を再現する必要はない★ |
| 4 | ★D-090★(docs/decisions.md:3325) |
★本指示書の設計根拠と未決20件★ |
| 5 | ★指示書M の facilities() / schedules()★(docs/handoff/指示書M_ローカルDBで動かす第1歩.md:529-720) |
★雛形として読む★。★遅延 import・変換の書き方・テストの作り方をそのまま踏襲する★ |
| 6 | ★lib/db/prisma.ts★(全40行) |
★シングルトン・★遅延 import が必須★な理由(:4-8)★ |
| 7 | ★lib/db/local-guard.ts★(全31行) |
★dwatch_local 以外を弾く仕組み。★そのまま効きます★★ |
| 8 | ★scripts/seed-local-db.ts★(全276行) |
★冪等の作り(:152-158)★ / ★スキップの作法(:128-149)★ / ★id を明示する方針(:14-16)★ |
| 9 | ★D-086 の「段階5で発見した想定外の不具合」★(docs/decisions.md:2912-2930) |
★Edge Runtime バンドル破壊★。★404 が出たらこれを疑う★ |
★.env.local の DATABASE_URL が dwatch_local を指していることを確認してください。★ ★値そのものを画面・ログ・文書に出さないこと★(CLAUDE.md §9.3)。 ★確認は「ローカルを向いているか」だけで十分です★(lib/db/local-guard.ts:10-20 と同じ判定)。
DS-012)の回答を得た★npm test のファイル数・テスト数 / tsc / lint)を自分で実測して控えた★★scripts/seed-local-db.ts に追記する★(新規ファイルを作らない)。 ★ガード(assertLocalDwatchDatabaseUrl。:40)はそのまま効きます★。
★新たにシードするテーブル(挿入順)★: controls → control_reports → control_error_histories
controls★lib/fixtures/ds-devices.ts の dsDevices を import して変換する★section_id は sections に実在する id を渡すこと★(prisma/schema.prisma:182 / :193 の NOT NULL FK)
sections.id をロボットの id と同じにしている★(scripts/seed-local-db.ts:217-224)facility_id → その施設の section の1つ★)sections が0件なので、DS-012 をそのままシードすると FK 違反で止まります★★ → ★着手前確認の回答に従うこと★
scripts/seed-local-db.ts:128-149)と同じ書き方にすること★: ★facility_id: 12 を名指ししない★。★「section が存在しない施設のデバイス」を構造的に弾き、 スキップ件数が想定と違ったら例外で止める★status は connection_status から逆変換★("ok" → 0 / "error" → 1)password は NOT NULL(prisma/schema.prisma:177)★ — ★fixtures に無いのでダミー値を入れる★。 ★実在しそうな文字列を入れないこと★(CLAUDE.md §7 / D-043)scripts/seed-local-db.ts:14-16 の方針。AUTO_INCREMENT に任せない)control_reports★DsDevice.last_reported_at が reported_at の元になります★reported_at は last_reported_at より確実に前にすること★facility_id / section_id / control_id / serial_number はすべて NOT NULL★ (prisma/schema.prisma:325-328)control_error_histories★lib/fixtures/ds-alerts.ts の dsAlerts のうち ★alert_type === "disconnected" の行だけ★ を変換する★
security_off の行はシードしない★(DB に置き場所が無い。禁止事項1)security_off の件数と一致するはず★ — 自分で数えること)status は resolution_status から逆変換★("pending" → 0 / "in_progress" → 1 / "done" → 2)comment ← memo★ / ★occurred_at ← detected_at★ / responded_at / resolved_at はそのままcontrol_id は serial_number から引くこと★(device_serial → controls.id の対応表)★deleteMany を挿入の逆順で先頭に足す★(scripts/seed-local-db.ts:151-158 の既存ブロックに、 ★D-Sec の3本を先頭側(= clean_schedules より前)に★ 追加する):
control_error_histories → control_reports → controls → (既存)clean_schedules → robots → sections → ...
★2回続けて実行しても結果が同じであることを実際に確かめること★。
facility_name はシードに渡さないこと★★★fixtures の facility_name(ds-devices.ts / ds-alerts.ts の文字列)を使ってはいけません。★ ★シードに渡すのは facility_id だけ★です。施設名は ★段階2 / 段階3 で facilities を結合して取ります★。
★これが「城南店」「緑野第二店」の不整合が構造的に消える仕組みそのものです★(D-090 項4)。
★同様に section_name(ds-alerts.ts のダミー値。lib/fixtures/ds-alerts.ts:15-17 に 「暫定のダミー値」と明記)も渡しません。★sections.section_name を結合で取ります★。
★シード後にテーブル件数を数えること★。
# ★mysql.exe は PATH に無い。フルパスで呼ぶこと★
# ★接続アカウントは `.env.local` の DATABASE_URL に書いてあるものを使う★
# (★この文書にユーザー名・パスワードを書かないこと★。CLAUDE.md §9.3)
SELECT (SELECT COUNT(*) FROM controls) c,
(SELECT COUNT(*) FROM control_reports) cr,
(SELECT COUNT(*) FROM control_error_histories) ceh;
★期待値は自分で fixtures を数えて立てること★(★作成時点の参考: controls は fixtures の デバイス数から施設12 の分(A案なら1件)を引いた数 / control_error_histories は disconnected の件数★ — ★鵜呑みにせず実測すること★)。
DATABASE_URL を一時的に別の値にして中断を1回だけ確かめ、必ず元に戻す★)prisma/schema.prisma に差分が無いこと★(git diff --stat prisma/ が空)lib/fixtures/ に差分が無いこと★(git diff --stat lib/fixtures/ が空)npx tsc --noEmit エラー0 / npm run lint 0 / npm test 全パスprismaSource.dsDevices() を実装する★lib/db/index.ts:156-158 の notImplemented("dsDevices") を実装に置き換える★。 ★facilities()(:162-198)と同じ形にすること★(遅延 import → findMany → map で変換)。
controls + control_reports(★最新1件★)+ facilities
facilities は facility_name を取るため★(★fixtures の手書き文字列は使わない★)control_reports は last_reported_at を取るため★。 ★デバイスごとに reported_at が最大の1件を選ぶこと★ (Prisma の include で orderBy + take: 1 を使うのが素直。★段階1-3 で古い行を入れたのは ここを検証するためです★)出力(DsDevice。lib/types/index.ts:295-302) |
入力 |
|---|---|
serial_number |
controls.serial_number |
facility_id |
controls.facility_id(★Number() で変換。BigInt のまま返さない★ — facilities() の :180 と同じ) |
★facility_name★ |
★facilities.facility_name(結合)★ |
connection_status |
★controls.status 0 → "ok" / 1 → "error"★(D-065 Q1-2) |
last_reported_at |
control_reports.reported_at(最新1件)。★ISO 8601(JST, +09:00)★(CLAUDE.md §9.3) |
★note★ |
★常に null★(下記 2-4) |
note は常に null を返すこと★★実値は app/api/ds-devices/route.ts:75-78 が JSON ストアからマージします★(D-065 Q1-1)。 ★prismaSource 側では何もしません。★ ★理由をコードコメントに残すこと★(後任が「実装漏れ」と誤解して埋めにいかないように)。
deleted_at の扱い★controls には deleted_at があります★(prisma/schema.prisma:188)→ ★where: { deleted_at: null } で絞る★ (facilities() の lib/db/index.ts:169 と同じ)control_reports には deleted_at がありません★ → ★書くと型エラーになります(=安全側)★。 ★型エラーが出たら「このテーブルには無い」というサインなので、無理に回避しないこと★inactive では絞らないこと★★禁止事項10★。第1歩と同じ方針(D-086 / lib/db/index.ts:165-167)。 ★理由をコメントに残すこと★。
★DB 依存のテストは書かないこと★(禁止事項12)。 ★変換の中に純粋関数として切り出せる部分(コード値 → 文字列)があれば、そこにテストを書く★。 ★期待値表を先に作ること★(CLAUDE.md §9.4)。
npx tsc --noEmit エラー0 / npm run lint 0 / npm test 全パスlib/ds-devices/query.ts / app/api/ds-devices/ / 画面に差分が無いこと★(禁止事項8)prismaSource.dsAlerts() を実装する(★disconnected のみ★)★lib/db/index.ts:159-161 の notImplemented("dsAlerts") を実装に置き換える★。
control_error_histories + controls + sections + facilities
controls は device_serial を取るため★sections は section_name を取るため★(★fixtures ではダミー値だった。 lib/fixtures/ds-alerts.ts:15-17★)facilities は facility_name を取るため★出力(DsAlert。lib/types/index.ts:312-330) |
入力 |
|---|---|
id |
control_error_histories.id(★Number() で変換★) |
★alert_type★ |
★固定で "disconnected"★(control_error_histories は制御盤異常しか持たない。D-067 項1) |
device_serial |
controls.serial_number(結合) |
facility_id |
control_error_histories.facility_id(★Number()★) |
★facility_name★ |
★facilities.facility_name(結合)★ |
★section_name★ |
★sections.section_name(結合)★ |
detected_at |
control_error_histories.occurred_at(★ISO 8601 / +09:00★) |
★resolution_status★ |
★status 0 → "pending" / 1 → "in_progress" / 2 → "done"★(D-065 Q1-3) |
responded_at / resolved_at |
同名カラム(nullable のまま) |
★memo★ |
★control_error_histories.comment★(★書き込み許可4カラムの1つ。D-042★) |
security_off を返さないことを docstring に明記すること★★★メソッドの直上に、次の3点をコメントで残すこと★:
disconnected しか返さないこと★security_statuses / security_status_histories に id / device_serial / resolution_status / responded_at / resolved_at / memo に 対応するカラムが無い(D-090 項3-9)。detected_at も @updatedAt のため取れない(同 3-10)★/ds-alerts の件数が減り、/dashboard のセキュリティバナーが出なくなる。 これは意図した挙動であり、実装漏れではない。第3歩で扱う★★★「実装漏れ」ではなく「意図的な未実装」だと後任が一目で分かるようにすること★★。
deleted_at / inactive★control_error_histories には deleted_at がありません★(prisma/schema.prisma:342-360)→ ★絞らない★controls の deleted_at は結合側で絞ってよい★inactive では絞らない★(禁止事項10)npx tsc --noEmit エラー0 / npm run lint 0 / npm test 全パスsecurity_off を返さない」と理由が書かれていること★lib/ds-alerts/query.ts / app/api/ds-alerts/ / lib/dashboard/ / 画面に差分が無いこと★(禁止事項8)★ここまでの3段階は準備です。この段階で「本当に DB から出ているのか」を確かめます。★
.env.local の USE_FIXTURES を false にする★ (★lib/db/use-fixtures.ts は process.env.USE_FIXTURES !== "false" なので、 ★文字列 false 以外はすべて fixtures 扱い★です。FALSE や 0 では切り替わりません)npm run dev を Bash で起動しないこと。Browser pane の preview_start を使う★ (★.claude/launch.json に d-watch(npm run dev / port 3000)があります★ — ★着手時に確認すること★)preview_start に url を渡して開き直す★ (指示書O :183 の前例)/ds-logs(F-06)★★| # | 項目 |
|---|---|
| 1 | ★台数が期待どおりであること★ — ★fixtures は 12台(正常7 / 異常5)★。★A案(施設12 除外)を採った場合は 11台(正常7 / 異常4)★。★着手前確認の回答に応じて期待値を確定させること★ |
| 2 | ★★施設名が「バリューマート城南店」ではなく「フレッシュタウン大和店」で出ること★★(DS-011 / 施設10) ← ★これが今回の成果★ |
| 3 | ★★施設12 を残した場合、「メガバリューマート緑野第二店」ではなく「フレッシュタウン湖畔店」で出ること★★(DS-012) |
| 4 | ★last_reported_at が正しい(最新の control_reports の値である)こと★ — ★段階1-3 で古い行を足したデバイスで、古いほうが出ていないことを確認する★ |
| 5 | ★備考が消えていないこと★ — ★JSON ストアに保存済みの値があれば、それが表示されること★(app/api/ds-devices/route.ts:75-78 のマージが効いている証拠) |
| 6 | ★ソートが「異常 → 正常」であること★(サーバ側ソート。lib/ds-devices/query.ts) |
/ds-alerts(F-07)★★ ← ★件数の突き合わせを飛ばさないこと★| # | 項目 |
|---|---|
| 1 | ★★件数を数えること★★ — ★fixtures は 12件(security_off 7 + disconnected 5)★、★DB 経由では 5件★(disconnected のみ)。★A案で DS-012 を除外しても、DS-012 のアラートは security_off なので disconnected の5件は変わらない★ — ★自分で fixtures を数えて確かめること★ |
| 2 | ★★差がちょうど7件(= security_off の件数)であること★★。★これ以外の差が出たら変換のバグです★ |
| 3 | ★section_name が実データで出ること★ — ★fixtures のダミー値(「1F売り場」「2F衣料品」「1Fレジ前」「B1食品」)ではなく、シードが入れた sections.section_name が出ること★ |
| 4 | ★施設名が施設マスタの実名で出ること★(確認1-2 と同じ趣旨) |
| 5 | ★resolution_status の3値がすべて出ること★(pending / in_progress / done — ★fixtures の disconnected には done が無い(lib/fixtures/ds-alerts.ts:182-185 に「意図的に作っていない」と明記)ので、done が0件なのが正しい★) |
| 6 | ★memo が comment から出ていること★ |
/dashboard★★| # | 項目 |
|---|---|
| 1 | ★★セキュリティバナーが出なくなること★★(★security_off が返らないため。lib/dashboard/aggregate.ts:113-125★)。★これは想定内。記録すること★ |
| 2 | ★ただし cleanLogs が未実装なので /api/dashboard 自体が 500 になるはず★(app/api/dashboard/route.ts:57 が db.cleanLogs() を呼ぶ)。★バナーの確認は 500 の内側なので目視できない可能性が高い★ → ★その場合は「確認できなかった」と正直に記録すること★ |
★これは失敗ではありません。第2歩の想定どおりの状態です。★
| 画面 | 呼んでいる未実装メソッド | 期待 |
|---|---|---|
/dashboard |
cleanLogs / prechecks |
★500★ |
/detail-log |
cleanLogs |
★500★ |
/alerts |
prechecks / cleanLogs |
★500★ |
/reports |
reportKpis / facilityPerformance / continuousProblems / errorBreakdown |
★500★ |
★/facilities / /schedules★ |
★(実装済み。指示書M)★ | ★200。回帰していないこと★ |
★/ds-logs / /ds-alerts★ |
★(本作業で実装)★ | ★200★ |
[db] xxx のPrisma実装はまだありません。USE_FIXTURES=true で開発してください。 であること★(lib/db/index.ts:143-147)docs/decisions.md:2912-2930)。 ★500 と 404 は意味が違います★ — ★500 = 想定どおりの未実装 / 404 = ビルドが壊れている★USE_FIXTURES=true に戻す★.env.local を true に戻し、dev サーバを再起動する★/ds-logs 12台 / /ds-alerts 12件 / /dashboard のセキュリティバナーが出ること★)read_console_messages でブラウザ側のエラー0件★preview_logs でサーバ側のエラーを確認★(★A Node.js module is loaded が出ていないこと★ ← 4-5 の404対策)docs/decisions.md に ★D-091★ を追記★着手直前に grep -n "^### D-0" docs/decisions.md で最終番号を確認すること★。 ★D-089 は指示書O が、D-090 は本指示書の設計根拠が既に取得済みです★。★空き番号は自分で確かめること★。
★記録すること★:
security_off を含めなかった理由★★(対応カラムが無い。D-090 項3-9 / 3-10)と、 ★その帰結(/ds-alerts が5件になり、/dashboard のバナーが出なくなる)★section_name がダミー値でなくなったか★DS-012 をどう扱ったか★★(着手前確認の回答と、その結果の台数)dsAlerts の security_off / cleanLogs ほか。D-090 項2 の表を引くこと)npm test のファイル数・テスト数 / tsc / lint。★着手前の基準値との差★)docs/handoff.md に追記★最新は 追記25(docs/handoff.md:2061)★。次は ★追記26★ の見込み。★着手直前に自分で確認すること★。
git add は必ずファイルパスを明示。git add -A は禁止★(禁止事項14).env.local をコミットしないこと★(禁止事項15)。★USE_FIXTURES=true に戻したことを確認してから★git push origin develop(★main へは push しない★)docs/decisions.md に決定番号を追記した(★上記7項目すべて記録★)docs/handoff.md に追記したorigin/develop へ push 済み★★本指示書の範囲外です。手を出さないこと★(D-090 項2 の分割表):
| 対象 | いつ |
|---|---|
★dsAlerts の security_off 側★ |
第3歩 |
cleanLogs |
第3歩 |
prechecks |
第4歩 |
reportKpis / facilityPerformance / errorBreakdown |
第4歩 |
continuousProblems |
第5歩 |
★点検記録2本(inspections / inspectionDetail)★ |
★最後★(D-072 項7。docs/decisions.md:1052) |
★D-090 項3 の未決20件も本指示書では解決しません★。★「未決である」と分かっている状態を保つのが正しい★。
DS-012)の回答を得てから段階1 に入った★npx tsc --noEmit エラー0 / npm run lint 0 / npm test 全パスUSE_FIXTURES=false で /ds-logs と /ds-alerts が 200 で表示されること★/ds-alerts の件数が fixtures より7件(= security_off の件数)少ないことを実測で確認したこと★★section_name が実データ(sections.section_name)で出ること★dsAlerts() の docstring に「security_off を返さない」と理由が書かれていること★USE_FIXTURES=true に戻して全画面が従来どおり動くこと★prisma/schema.prisma / lib/fixtures/ / lib/ds-devices/query.ts / lib/ds-alerts/query.ts / API ルート / 画面 に差分が無いこと★lib/db/prisma.ts / lib/db/local-guard.ts / lib/db/use-fixtures.ts / prismaSource.facilities() / prismaSource.schedules() に差分が無いこと★docs/decisions.md / docs/handoff.md の更新origin/develop へ push 済み★| 段階 | 状態 | コミット |
|---|---|---|
| ★着手前の確認★ | ★回答済み(A案。施設12のDS-012をシードから除外)★ |
— |
| 段階0 前提の確認 | 完了 | コミットなし |
| 段階1 シードに D-Sec の3テーブルを足す | 完了 | 9b39847 |
段階2 prismaSource.dsDevices() |
完了 | 7811572 |
段階3 prismaSource.dsAlerts()(disconnected のみ) |
完了 | f05b522 |
| 段階4 動作確認 | 完了(Browser pane無し。curlで代替。限界は D-097 §5 参照) | コミットなし |
| 段階5 記録と push | 完了 | 本コミット |
実行日: 2026-09-09 取得した決定番号: D-097(docs/decisions.md:4223)。途中の寄り道で D-095(dwatch_local作り直し)・ D-096(指示書Q。prisma/schema.prisma再同期)も取得 着手前の実測値: 33ファイル / 631テスト、tsc 0、lint 0 施設12 の DS-012 の扱い: A案採用(シードから除外)。ユーザー事前回答どおり /ds-logs の台数: 12台 → 11台(正常7 / 異常4) /ds-alerts の件数(fixtures / DB): 12件 → 5件(disconnectedのみ。差7件 = security_off件数と一致) 完了後の実測値: 33ファイル / 631テスト(変化なし)、tsc 0、lint 0 詳細: docs/decisions.md D-097 を参照