robot_error_reports から読み、編集モードで対応状況と備考を更新できるようにする(+清掃詳細ログの 11px 横スクロールを消す)日付: 2026-10-19 / 担当: Cursor(または監督が指名する実装担当)/ 基点: develop 64c4079 前提: TeamWorks 第11回 P-8・P-10(docs/handoff/TeamWorks回答_第11回_20261005.md:45-60。D-158 docs/decisions.md:12523 以降)/ 第10回 N-5(docs/handoff/TeamWorks回答_第10回_20261002.md:100-112。error_type の値)/ D-091 項3(セキュリティ無効はアラートではない。docs/decisions.md:3510-3551)/ D-105(error_id は string・Number() に通さない。:5442 以降)/ D-160(エラー一覧。:12612)/ D-161・D-165(対応状況はプルダウン・編集モードでまとめて保存。:12647 / :12706)/ D-172(serial_number の拡張待ち → 当面 db pull しない。:12807)/ D-178(詳細ログの事前チェック2列。:12901)/ 指示書AT(4カラム UPDATE)/ 指示書AU 段階0-4(robot_error_reports は select で読む)/ 指示書AW(エラー一覧)/ CLAUDE.md §3・§4.7・§4.8・§6・§7・§9.3・§9.4・§9.6
★ユーザー決定(2026-10-19。§2 の U1〜U8)と監督の設計判断(§2 の D1〜)は確定仕様。変えないこと★(CLAUDE.md §0-1)。 ★期待値表(§3)の表1〜表4 は正。変えないこと★(CLAUDE.md §0-2)。表1 の R1〜R7・R11 は監督確定、R8〜R10・R12・R13・表2・表3 は本書で導き ★監督が正とした(監督判断 2026-10-19。§11 Q5)★。 ★表5(削除済みの施設・清掃エリア。U8)は本書で U8 から導いたもの。監督が確認した(§11 Q9。監督判断 2026-10-19)★。 ★§11 のユーザーへの質問(Q1〜Q4)はすべて確定し、本文に反映済み★(Q4 は 2026-10-19 のユーザー決定 U8 で確定。暫定だった D12 は書き直した)。残るのは監督の確認 Q9(表5)だけ。
★行番号はすべて 2026-10-19 の develop 64c4079 で確かめたもの★。進んでいたら関数名・文で探し直すこと(特に lib/db/index.ts・lib/types/index.ts・d-watch-api.yaml・scripts/seed-local-db.ts)。
★docs/decisions.md と CLAUDE.md は、本書の作成時点で別のセッションが未コミットの編集をしている(D-179)★。 本書の実装で docs/decisions.md に足すのは ★D-180 の追記だけ★(§13 の下書き)。★D-179 の部分・CLAUDE.md には触らない★(CLAUDE.md は監督が直す)。
| # | やること | 段階 |
|---|---|---|
| 1 | ★D-Sec アラート(GET /ds-alerts・/ds-alerts 画面・ダッシュボードのセキュリティバナー)の取得元を control_error_histories から robot_error_reports に切り替える★。対象は ★error_type = 'D-S_CONTROL_FAILURE' の行だけ★ |
段階2〜4 |
| 2 | ★DsAlert の契約を直す★: id を string(error_id)に、resolution_status を ★エラー一覧と同じ unresolved / in_progress / resolved★ に、memo を note に(★YAML を先に直す★) |
段階4 |
| 3 | ★/ds-alerts 画面に編集モードを付ける★(エラー一覧と同じ操作。D-161 / D-165)。★書き込みはエラー一覧の PATCH /errors/{error_id}/status をそのまま使う★(新しい書き込みルートを作らない) |
段階5 |
| 4 | ★YAML だけにあって未実装の PATCH /ds-alerts/{id}/status を削除する★ |
段階4 |
| 5 | fixtures を作り直す(D-Sec アラートは errorReportRows の D-S_CONTROL_FAILURE 行から作る。セキュリティ・清掃開始失敗のシリアルを実データどおり --- に) |
段階3 |
| 6 | ローカルDB のシードを追従させる(control_error_histories への投入をやめ、robot_error_reports に D-S_CONTROL_FAILURE 行を入れる) |
段階6 |
| 7 | ★(別件・独立)清掃詳細ログの表が画面幅 1440px で 11px 横スクロールするのを消す★(施設名の列 126px → 110px) | 段階7 |
| 8 | ★削除済み(論理削除。facilities.deleted_at / sections.deleted_at)の施設・清掃エリアの行を、★エラー一覧(GET /errors・meta.total)と D-Sec アラート(GET /ds-alerts・summary・ダッシュボード)の両方から外す★。★その行への PATCH /errors/{error_id}/status は404★(U8・D12) |
段階2〜4 |
★変えないもの★: D-Sec ログ(/ds-logs。dsDevices()=controls + control_reports)/エラー一覧(/errors)の挙動・PATCH /errors/{error_id}/status の挙動(★U8 の「削除済みの施設・清掃エリアの行を外す・404 にする」を除く。ルートのコードは変えない — 404 は db.errorReportById() が null を返すことで出る★)/施設名・分類の解決が deleted_at で絞らないこと(★罠7★)/ アラート(/alerts)・PATCH /prechecks/{id}/status/lib/db/robot-error-write.ts(書いてよい列)/lib/prechecks/edit-drafts.ts の中身/ ダッシュボードのセキュリティバナーのコード(computeSecurityBanner()。§2 D4)/prisma/schema.prisma(db pull しない。D-172)。
prismaSource.dsAlerts()(lib/db/index.ts:1967-1996)は ★control_error_histories(+ controls / facilities / sections)を読む★。 alert_type は固定で "disconnected"、device_serial = controls.serial_number、detected_at = occurred_at、 resolution_status は resolutionStatusFromControlErrorStatus()(lib/db/ds-status.ts:36-49。0/1/2 → pending / in_progress / done、他は例外)、memo = comment。 ★アプリで control_error_histories を読むのはここだけ★(書くのは scripts/seed-local-db.ts:1160-1182 のシードだけ)。robot_error_reports を正とする。D-Security のアラートも対応状況の更新もこちら。control_error_histories は読まない★(D-158 の表の P-10 行)。robot_id NULL・serial_number が制御盤の SN/セキュリティと清掃開始失敗=robot_id NULL・serial_number が 0(error_type で見分ける)/施設は行の facility_id・section_id(d = A)。error_type は D-S_CONTROL_FAILURE(error_detail "no regular report from the D-S control.")と SECURITY_ON("security is enabled.")。robot_error_reports 16行。うち robot_id NULL が9行 = D-S_CONTROL_FAILURE 3行(シリアル DFAS010001 / DFAS010005 / DFAS010002。status 0 が2行・2 が1行)、SECURITY_ON 5行(シリアル ---)、NOT_STARTED 1行(シリアル ---)。
errored_at が NULL★ → 検知時刻は ★errored_at ?? created_at★ でないと出せない(いまの D-Sec は occurred_at を読むので、切り替え後は既存の detectedAtFromError() を使う)。error_id 1000004 は、シリアル DFAS010001 が施設2の制御盤なのに、行の facility_id / section_id は 3★(§12 申し送り1)。---★(P-8 の回答の 0 ではない)。fixtures(lib/fixtures/error-reports.ts:179-215)と docs/fixtures.md:514-534 は '0' のまま。components/features/ds-alerts/DsAlertsScreen.tsx)は表示だけ。PATCH /ds-alerts/{id}/status は ★YAML(d-watch-api.yaml:1486-1511)にだけあり、実装されていない★(app/api/ds-alerts/route.ts:7-9)。t("未対応") は、EN で ★"Unsupported"★ と出る(lib/i18n.ts:182 は対応機種バッジ用の訳。キーが衝突している)。§2 U5(文言をエラー一覧に揃える。ユーザー確定)で消える。| # | 決定 | 帰結 |
|---|---|---|
| U1 | ★SECURITY_ON は D-Sec アラートに出さない★(エラー一覧には今までどおり出る) |
D-091 項3「セキュリティが無効であっても異常ではない」「D-sec アラートは…生死報告が途切れたなどのエラーのみ」と一致 |
| U2 | ★NOT_STARTED(清掃開始失敗)は D-Sec アラートに出さない★(ロボット側の出発失敗。エラー一覧には今までどおり出る) |
— |
| U3 | ★施設は行の facility_id(と section_id)★(P-8 d = A)。シリアルが別の施設の制御盤でも、行の施設で出す |
1000004 は施設3で出る。食い違いは TeamWorks に申し送る(§12) |
| U4 | ★D-Sec アラート画面に編集モードを今回付ける★。エラー一覧と同じ操作(通常モードはバッジ表示/編集 → 保存(N件)・キャンセル/まとめて保存/下書きがあるあいだ beforeunload で確認/変えた行の目印/編集中は絞り込み・ページ送りを無効)。★書くのは同じ robot_error_reports の4カラム★。★同じ行をどちらの画面からも編集できる★ |
D-161 / D-165。書き込み経路は1本(D6) |
| U5 | ★D-Sec 画面の対応状況の文言をエラー一覧に揃える★: 未対応 → ★未解消★/対応中 → 対応中/対応済み → ★解消★(数値カード・状態の絞り込みボタン・バッジ・編集のプルダウン)。★ユーザー確定 2026-10-19(§11 Q1)★ | 同じ行が2画面で同じ状態なのに文言が違うと混乱する。★EN の "Unsupported" 衝突(lib/i18n.ts:182)も消える★。モック(d-watch-mock.jsx:38-42 の DSA_INIT)の「未対応 / 対応済み」とは食い違う(承知のうえ。モックは書き換えない) |
| U6 | ★D-Sec 画面の種別のセレクト(全種別 / セキュリティ無効 / 通信断。DsAlertsScreen.tsx:217-228)と副題(「セキュリティ無効・通信断のアラート管理」)は ★そのまま残す★★。★「セキュリティ無効」を選ぶと必ず0件になることは承知のうえ★。★ユーザー判断 2026-10-19(§11 Q2)★ |
本書の旧暫定(セレクトを外し副題を変える)は ★撤回★。API の type パラメータと enum も残る(D4) |
| U7 | ★ダッシュボードのセキュリティバナーのコードは残す★(DB のデータでは出ない)。★ユーザー確定 2026-10-19(§11 Q3)★ | D4 |
| U8 | ★削除済みの施設・清掃エリアの行は、エラー一覧からも D-Sec アラートからも出さない★。ユーザー原文(2026-10-19):「削除済みのものは表示する必要がないので、エラー一覧からもD-sec一覧からも消してください」(§11 Q4) | D12。★本書の範囲がエラー一覧(errorReports() / errorReportById())に広がる★。/alerts は範囲外(§10・§12-2) |
| # | 決定 | 理由 |
|---|---|---|
| D1 | ★D-Sec アラート = robot_error_reports のうち error_type が ★D_SEC_ALERT_ERROR_TYPES(lib/config/index.ts に新設。初期値 ["D-S_CONTROL_FAILURE"])★ に ★完全一致(JS の ===)★ する行★。alert_type は "disconnected" |
監督判断。値は設定に外出しし、配列にして後から足せるようにする。★robot_id IS NULL やシリアルの形で見分けない★(P-8 の規則はシリアルの実値が回答と違う=---。形で見分けると壊れる) |
| D2 | ★SQL の error_type IN (...) は「写し」、正は純粋関数の ===★。Prisma 版も読んだ後に純粋関数で絞り直す |
★DB の照合順序は大小文字を区別せず(ci)、末尾の空白も無視する(PAD SPACE)★。SQL だけに任せると d-s_control_failure や D-S_CONTROL_FAILURE␣ が当たり、fixtures と件数が割れる(D-4 の === と同じ考え方。表1 R8・R9) |
| D3 | ★status は 0/1/2 の行だけ(SQL の status IN と純粋関数の両方で絞る)★。3 以上は ★出さない・例外にしない★ |
★db.dsAlerts() はダッシュボードも呼ぶ(app/api/dashboard/route.ts:152)★。1行の想定外の値でダッシュボード全体が500になるのを防ぐ(エラー一覧の表2 F12 と同じ扱い)。コードは robotErrorStatusFromResolutionStatus() から作る(0/1/2 を直書きしない) |
| D4 | ★API の alert_type の enum に security_off は残す(互換)。ただし DB からは出ない★。★ダッシュボードのセキュリティバナー(computeSecurityBanner()。lib/dashboard/aggregate.ts:130-140)は DB のデータでは ★もう出ない★★。★コードと aggregate.test.ts のテスト(テスト内で作った DsAlert を使う)は残す★ |
監督判断。YAML の security_banner と /ds-alerts の description にこのことを書く。★バナーのコードを残すことはユーザー確定(2026-10-19。U7・§11 Q3)★。廃止・転用は範囲外(§10) |
| D5 | ★DsAlert の項目★: id = error_id(★string。Number() に通さない★。YAML を先に直す。前例 D-105)/device_serial = 行の serial_number(★生値。D-077★)/facility_id・facility_name = 行の facility_id とその施設名(U3)/section_name = 行の section_id の清掃エリア名/detected_at = ★jstIsoFromDb(detectedAtFromError(row))★(errored_at ?? created_at。lib/db/precheck-mapping.ts:228。エラー一覧と同じ)/responded_at・resolved_at = jstIsoFromDb()/★note★ = comment |
監督判断(note への改名は本書の判断。D9) |
| D6 | ★書き込みはエラー一覧の PATCH /errors/{error_id}/status(app/api/errors/[error_id]/status/route.ts)と lib/db/robot-error-write.ts をそのまま使う★。★新しい書き込みルート・DataSource の書き込みメソッドを作らない★ |
監督判断。同じ列を2つの規則で書かない |
| D7 | ★YAML の PATCH /ds-alerts/{id}/status(d-watch-api.yaml:1486-1511)は ★削除する★(deprecated で残さない)。/ds-alerts の description に「対応状況と備考の更新は PATCH /errors/{error_id}/status(同じ robot_error_reports の行)」と書く |
残すと「実装すべきもの」に見え、2本目の書き込み経路が生まれる。実装が一度も無いので消しても壊れる呼び出し元が無い |
| D8 | ★resolution_status の語彙はエラー一覧と同じ unresolved / in_progress / resolved(PrecheckStatus)に統一する★。DsAlertStatus(pending / in_progress / done)は ★削除★。変換は ★既存の resolutionStatusFromRobotErrorStatus()(lib/db/precheck-mapping.ts:41)の1本★ |
下の「D8 の理由」 |
| D9 | ★DsAlert.memo を note に改名する★(YAML を先に)。★監督判断 2026-10-19(§11 Q6)★ |
★lib/prechecks/edit-drafts.ts:19 の EditableRow は id / resolution_status / note を要求する★。D5・D8 と合わせると DsAlert がそのまま EditableRow になり、saveDrafts<DsAlert>() を ★無改造で使える★。PrecheckItem / ErrorReportItem も note。契約は D5・D8 で既に変わるので、変えるなら同時に変える(呼び出し元は自前の画面だけ) |
| D10 | ★GET /ds-alerts の status パラメータは open / all / ★resolved★(旧 done は ★422★)★。★summary のキーは unresolved / in_progress / resolved★。★監督判断 2026-10-19(§11 Q7)★ |
D8 の帰結。done という状態が無くなるので、パラメータとキーも揃える(別名で受けない=黙って倒さない) |
| D11 | ★並びは detected_at の降順、同値は id の昇順(★文字列の辞書順。localeCompare を使わない★)★(sortDsAlerts() lib/ds-alerts/query.ts:113-120 の a.id - b.id を直す) |
id が string になるため。エラー一覧の compareErrorReportKeys() と同じ規則("E-10" が "E-9" より先) |
| D12 | ★robot_error_reports の行のうち、★施設(facilities.deleted_at)か清掃エリア(sections.deleted_at)が論理削除されている行は「見えない行」★とし、★エラー一覧(errorReports()。meta.total も)・errorReportById()(= PATCH /errors/{error_id}/status の実在確認 → 404)・D-Sec アラート(dsAlerts()。summary・ダッシュボードも)のすべてで外す★。★deleted_at が NULL でなければ(日時の値によらず)削除済み★。★正は純粋関数 isVisibleErrorRow()、SQL のリレーション条件 ERROR_ROW_VISIBLE_WHERE は写し★(段階2-7)。★U8 で確定(ユーザー 2026-10-19)。旧暫定「絞らない」は破棄★ |
U8。施設・清掃エリア・制御盤は ★論理削除なので行は残り、絞らなければエラー行も返ってくる★(§11 Q4 の事実)。★旧 D-Sec 実装の controls.deleted_at の絞りは復活させない★(controls を結合しない。行の施設・清掃エリアで決める — U3)。★施設名・分類 → 施設ID の解決は従来どおり deleted_at で絞らない(罠7。lib/facilities/resolve-by-name.ts:74 / resolve-by-master.ts:84)★ — 解決された削除済みの施設の行は、この可視性の条件で外れる |
| D13 | ★fixtures の D-Sec アラートは lib/fixtures/error-reports.ts の errorReportRows から、Prisma 版と ★同じ純粋関数★ で作る★。★lib/fixtures/ds-alerts.ts は削除★ |
CLAUDE.md §9.5(判定ロジックは切替の外側)。★fixtures でも、エラー一覧で書いた対応状況が D-Sec に出る(上書き表を共有)★ — U4「同じ行をどちらの画面からも」を fixtures でも確かめられる |
| D14 | ★lib/db/ds-status.ts の resolutionStatusFromControlErrorStatus() / controlErrorStatusFromResolutionStatus() は削除★(テストも)。connectionStatusFromControlStatus() / controlStatusFromConnectionStatus() は残す(dsDevices() とシードが使う) |
段階6 でシードが control_error_histories に書かなくなると、使う場所が無くなる |
| D15 | ★画面の保存後、summary(数値カード)は ★取り直さずに手元で付け替える★(変わった行ごとに旧状態 −1・新状態 +1)。★「対応済み」にした行は、status=open の表示でも次に取り直すまで消さない★ |
エラー一覧(ErrorsScreen.tsx の saveEdit())と同じ振る舞い(押し間違いをその場で戻せる)。取り直すと解消した行が消え、summary だけ正しくて一覧と食い違う |
案A: 揃える(unresolved / in_progress / resolved)★採用★ |
案B: 残す(pending / in_progress / done)+ 専用の変換 |
|
|---|---|---|
| 変換 | ★既存の resolutionStatusFromRobotErrorStatus() の1本★ |
★同じ列に2つ目の変換★(robot_error_reports.status → DsAlertStatus)。どちらかだけ直す事故の余地 |
| 書き込み | PATCH /errors/... の本文・応答の語彙と同じ。変換なし |
画面で DsAlertStatus ⇄ PrecheckStatus を往復変換(送るとき・応答を戻すとき) |
| 編集モード | edit-drafts.ts を無改造で使える(D9 と合わせて) |
EditableRow が PrecheckStatus 固定なので、ジェネリクス化(/alerts・/errors を巻き込む)か、画面側の詰め替えが要る |
| 書き換え量 | 型・YAML・lib/ds-alerts/query.ts・DsAlertsScreen.tsx・aggregate.ts 1行・テスト(query.test.ts 約10か所・aggregate.test.ts 約7か所・master-filter.test.ts 1か所)。★tsc が全部指す★ |
型はそのままだが、変換・詰め替えの新しいコードとそのテストが要る |
| 同じ行の見え方 | 2画面で同じ値 | API では別の値・画面の文言も別になりやすい |
★書き換えは型エラーで漏れなく見つかる。一方、案B の「2つ目の変換」は型では見つからない食い違いの温床★なので、案A を採る。 ★画面の文言(「未対応」か「未解消」か)は語彙とは別の問題★ — こちらもエラー一覧に揃えることでユーザー確定(§2 U5)。
★ユーザー確認待ちの暫定は無い★。旧 2-3 の U5 は確定、U6 は撤回(そのまま残す)、D12 は U8 で確定して書き直した。 ★表5(U8 から導いた期待値)も監督が確認済み(§11 Q9。2026-10-19)★。
dsAlertsFromErrorRows())★R1〜R7・R11 は §2 のユーザー決定・監督判断から直接決まる(監督確定)★。★R8〜R10・R12・R13 は本書で導き、監督が正とした(監督判断 2026-10-19。§11 Q5)★。
| # | robot_error_reports の行 |
D-Sec に出るか | 出たときの値 | 根拠 |
|---|---|---|---|---|
| R1 | error_type D-S_CONTROL_FAILURE・status 0・errored_at あり・robot_id NULL・シリアル DFAS010001 |
出る | alert_type disconnected/device_serial DFAS010001/resolution_status unresolved/detected_at = errored_at(+09:00)/note null |
D1・D5 |
| R2 | 同上・status 1・responded_at あり・comment 「回線業者に確認中」 |
出る | in_progress/responded_at あり/note 「回線業者に確認中」 |
D5・D8 |
| R3 | 同上・status 2・responded_at・resolved_at あり |
出る(status=open の一覧には出ない。all・resolved で出る。summary.resolved に数える) |
resolved |
D8・D10 |
| R4 | 同上・status 0・★errored_at NULL★ |
出る | ★detected_at = created_at★ |
D5(ステージングの16行すべてがこの形) |
| R5 | error_type SECURITY_ON・robot_id NULL・シリアル --- |
★出ない★ | — | U1 |
| R6 | error_type NOT_STARTED・robot_id NULL・シリアル --- |
★出ない★ | — | U2 |
| R7 | ロボットのエラー(error_type EmergencyStop・robot_id あり) |
★出ない★ | — | D1 |
| R8 | error_type ★d-s_control_failure★(小文字) |
★出ない★(SQL の ci では当たるが、純粋関数の === で外れる) |
— | D2 |
| R9 | error_type ★D-S_CONTROL_FAILURE␣★(末尾に空白1つ) |
★出ない★(SQL の PAD SPACE では当たるが、=== で外れる) |
— | D2 |
| R10 | D-S_CONTROL_FAILURE・★status 3★ |
★出ない・例外にならない★(ダッシュボードも500にならない) | — | D3 |
| R11 | D-S_CONTROL_FAILURE・シリアル DFAS010001(施設2の制御盤)・★行の facility_id / section_id は施設3★ |
出る | ★facility_id 3・施設3の名前・section_name は行の section_id の名前★/device_serial DFAS010001(そのまま) |
U3(ステージング 1000004 の形) |
| R12 | D-S_CONTROL_FAILURE・R1 と同じ検知時刻・error_id ★"E-10"★(R1 は "E-9") |
出る | 並びで ★"E-10" が "E-9" より先★(辞書順。数値順なら逆) |
D11 |
| R13 | D-S_CONTROL_FAILURE・★clean_timetable_id NULL★ |
出る | 予定の有無で絞らない | D1(P-9 で制御盤の行にも予定 id が入る予定だが、入らない行も出す) |
並び順: detected_at の降順、同値は id の昇順(文字列)。
★テストの書き方の注意★:
findMany が「SQL のように ci・PAD SPACE で当ててしまう」★形にして返す(= where を無視して全行返してよい)。そのうえで ★応答に出ない★ことを確かめる。純粋関数の単体テストでも同じ行を入れる/api/ds-alerts も /api/dashboard も200」を確かめるrobot_id NULL の行は ★guardNullRobotId()(lib/db/__tests__/robot-error-null-guard.ts)をスタブに入れる★(robot_id / robot を読むと落ちる)+09:00 付きの ISO 文字列から toDbJst(new Date(...)) で DB の壁時計値を作る(エラー一覧のテストと同じ)D_SEC_ALERT_ERROR_TYPES[0] から読む★。R8・R9 の「わざと違う綴り」は直書きする(コメントで理由を書く)GET /ds-alerts のパラメータ・summary。★本書で導き監督が正とした(監督判断 2026-10-19。§11 Q5)★)入力は表1 の R1〜R4・R11〜R13(出る7行。status は R1・R4・R11・R12・R13 が 0、R2 が 1、R3 が 2)と R5〜R10(出ない6行)。
| # | 入力 | 期待 | 確かめること |
|---|---|---|---|
| A1 | 既定(パラメータなし = type=all&status=open) |
出る7行のうち R3 以外の6行・meta.total = 6 |
D10 |
| A2 | status=resolved |
R3 だけ | D10 |
| A3 | status=all |
7行 | D10 |
| A4 | ★status=done(旧の値)★ |
★422★。details.status が open / all / resolved のいずれかを指定してください |
D10(黙って倒さない) |
| A5 | type=security_off |
★200・0件★(422 にしない。enum は残す) | D4。★画面の種別セレクトで「セキュリティ無効」を選んだときもこれ(ユーザー承知。U6)★ |
| A6 | type=disconnected |
A1 と同じ | D1 |
| A7 | summary(どの type / status でも) |
★{ unresolved: 5, in_progress: 1, resolved: 1 }★(出る7行に対して数える。R5〜R10 は数えない) |
D10・D-054(summary は type / status の前) |
| A8 | customer_id で絞る |
summary も一覧も同じ施設の集合に絞られる(app/api/__tests__/master-filter.test.ts:364-380 の形) |
分類は母集団(既存の規則) |
| A9 | per_page=2&page=2(既定の6行) |
並び順の3〜4行目・meta.total = 6 |
既存のページ分け |
dsAlertWithResolution(row, updated)(lib/ds-alerts/edit.ts。新規)と adjustDsAlertSummary(summary, before, updated)(同):
| # | 入力 | 期待 |
|---|---|---|
| S1 | row(unresolved・note null)、updated(ErrorReportItem。同じ id・resolved・responded_at / resolved_at あり・note 「確認済み」) |
row の ★4項目だけ★ 差し替え(resolution_status / responded_at / resolved_at / note)。alert_type・device_serial・facility_name・section_name・detected_at は row のまま |
| S2 | updated.id !== row.id |
★例外★(呼び出しの誤り) |
| S3 | summary {2,1,1}(unresolved, in_progress, resolved)、1行が unresolved → resolved |
{1,1,2} |
| S4 | 備考だけ変えた(状態は同じ) | summary は変わらない |
| S5 | 2行(unresolved → in_progress、in_progress → unresolved) |
{2,1,1} のまま |
| S6 | updated が空の Map |
入力と同じ値(★入力のオブジェクトを書き換えない★) |
| 箇所 | 今 | 本書の後 | EN |
|---|---|---|---|
| 数値カード1 | 未対応 / 即時対応 | ★未解消★ / 即時対応 | Unresolved / (既存の訳) |
| 数値カード3 | 対応済み / 完了 | ★解消★ / 完了 | Resolved |
| 状態ボタン | 未対応・対応中 / 全件 / 対応済み | ★未解消・対応中★ / 全件 / ★解消★ | 新キー「未解消・対応中」→ "Unresolved / In progress"(暫定) |
| バッジ・プルダウン | 未対応 / 対応中 / 対応済み | 未解消 / 対応中 / 解消(エラー一覧の STATUS_OPTIONS と同じ) |
既存(lib/i18n.ts:161-163) |
| 副題 | セキュリティ無効・通信断のアラート管理 | ★変えない★(U6) | — |
| 種別セレクト | 全種別 / セキュリティ無効 / 通信断 | ★変えない(残す)★。「セキュリティ無効」を選ぶと0件(ユーザー承知。U6・表2 A5) | — |
「施設」「清掃エリア」は行の facility_id / section_id の先(facilities / sections)。「削除」は deleted_at が NULL でないこと。
| # | 行 | エラー一覧(GET /errors) |
D-Sec アラート(GET /ds-alerts) |
PATCH /errors/{error_id}/status |
|---|---|---|---|---|
| V1 | 施設・清掃エリアとも生きている・ロボットのエラー(EmergencyStop)・status 0 |
出る(meta.total に数える) |
対象外(種類が違う) | 200 |
| V2 | ★施設が削除済み★(清掃エリアは生きている)・ロボットのエラー・status 0 |
★出ない★(既定でも「解消済みも表示」でも。meta.total に数えない) |
対象外 | ★404★(updateMany を呼ばない) |
| V3 | ★清掃エリアが削除済み★(施設は生きている)・ロボットのエラー・status 0 |
★出ない★ | 対象外 | ★404★ |
| V4 | 施設・清掃エリアとも生きている・D-S_CONTROL_FAILURE・status 0 |
出る | 出る(summary.unresolved に数える) |
200 |
| V5 | ★施設が削除済み★・D-S_CONTROL_FAILURE・status 0 |
★出ない★ | ★出ない★(summary にも数えない。ダッシュボードにも渡らない) |
★404★ |
| V6 | ★清掃エリアが削除済み★・D-S_CONTROL_FAILURE・status 1 |
★出ない★ | ★出ない★ | ★404★ |
| V7 | ★施設・清掃エリアとも削除済み★・D-S_CONTROL_FAILURE・status 2 |
★出ない★(「解消済みも表示」でも) | ★出ない★(status=all / resolved でも) |
★404★ |
| V8 | V2 の施設名で facility_name を絞る(名前の解決は deleted_at で絞らない=罠7 なので施設IDは解決される) |
★0件・200★(422 / 500 にしない) | — | — |
| V9 | V1 と同じ施設で、別の清掃エリアが削除済み(V3)の場合の V1 | 出る(★清掃エリア単位で決まる。同じ施設の生きている清掃エリアの行は消えない★) | — | 200 |
★テストの書き方の注意★:
where の facility.deleted_at / section.deleted_at の条件も評価する★形と、★where を無視して全行返す★形の両方で確かめる(後者で ★純粋関数側の isVisibleErrorRow() が外す★ことを見る。D2 と同じ考え方)db.errorReportById() が null を返すことで出る★。★updateMany が呼ばれないこと・db.write が出ないこと★を確かめる(存在しない id の404 と同じ扱い)toDbJst(new Date("2026-10-01T10:00:00+09:00")) 等)。★「未来の日時なら生きている」とはしない★(D12。NULL か否かだけ)| # | 罠 | どう避けるか |
|---|---|---|
| 4-1 | ★robot_error_reports を select なし・include で読む/robot_id・robot を選ぶ★ |
★D-Sec の行は全部 robot_id NULL★ — 読み取りが落ちる(指示書AU 段階0-4)。★新しい定数 ROBOT_ERROR_DS_ALERT_SELECT だけを使う★ |
| 4-2 | ★新しい select 定数に clean_timetable_id を入れ忘れる★ |
lib/db/__tests__/robot-error-select.test.ts:35-37 が「全定数が clean_timetable_id を含む」を見ている。★使わなくても clean_timetable_id: true を入れる★(ROBOT_ERROR_LIST_KEY_SELECT と同じ扱い) |
| 4-3 | ★SQL の error_type IN の結果をそのまま採る★ |
D2。★読んだ後に純粋関数で === で絞り直す★(表1 R8・R9) |
| 4-4 | ★resolutionStatusFromRobotErrorStatus() に 3 以上を通す★ |
例外 → ★ダッシュボード全体が500★。★SQL の status IN と純粋関数の両方で 0/1/2 に絞ってから変換する★(D3・表1 R10) |
| 4-5 | ★detected_at を errored_at だけから作る/created_at を new Date() で作り直す★ |
★detectedAtFromError() → jstIsoFromDb()★。DB の値は JST の壁時計値(CLAUDE.md §3)。★9時間を足さない・引かない★ |
| 4-6 | ★error_id を Number() に通す/a.id - b.id が残る★ |
★文字列のまま★(D-105)。sortDsAlerts() の同値比較は ★< / > の辞書順★(D11)。React の key も文字列 |
| 4-7 | ★施設をシリアル(制御盤)から引く★ | ★行の facility_id / section_id★(U3。表1 R11)。★controls を結合しない★ |
| 4-8 | ★書き込みのために新しいルート・DataSource メソッドを足す★ |
★PATCH /api/errors/${encodeURIComponent(id)}/status を呼ぶだけ★(D6)。応答は ErrorReportItem なので dsAlertWithResolution() で DsAlert に戻す |
| 4-9 | ★保存後に一覧を取り直す★ | ★取り直さない★。成功した行だけ差し替え、summary は adjustDsAlertSummary() で付け替える(D15) |
| 4-10 | ★summary を一覧の件数(meta.total)の代わりに使う/逆★ |
既存の注記(DsAlertsScreen.tsx の total の docstring。D-054)のまま |
| 4-11 | ★fixtures の D-Sec を別の配列で持つ★ | ★errorReportRows から作る★(D13)。別にすると、エラー一覧で書いた状態が D-Sec に出ない(fixtures だけ U4 を満たさない) |
| 4-12 | ★fixtures の件数を変えたのに、エラー一覧の件数のテスト・資料を直し忘れる★ | lib/db/__tests__/fixture-error-reports.test.ts:49-53 / :96-97 / :102-108 / :120、app/api/errors/__tests__/route.fixtures.test.ts、docs/fixtures.md §13 の件数と並び(段階3-4) |
| 4-13 | ★security_off を enum から消す★ |
★消さない★(D4)。lib/ds-alerts/query.ts:10-14 の TYPE_FILTERS もそのまま |
| 4-14 | ★computeSecurityBanner() を消す/DB から security_off を作ろうとする★ |
★どちらもしない★(D4・U1)。バナーが出ないのは意図した挙動。YAML に書く |
| 4-15 | ★シードを dwatch_local 以外に向ける★ |
★lib/db/local-guard.ts を触らない★。シードはテーブルを消してから入れ直す(ステージングに向けると実データが消える。CLAUDE.md §3) |
| 4-16 | ★t() に生値(シリアル・施設名・清掃エリア名・備考)を通す★ |
通さない(辞書の対訳に誤って当たる。ErrorsScreen.tsx の冒頭コメント)。★「読み込み中…」は t() に通す★(いま DsAlertsScreen.tsx:263 は通していない) |
| 4-17 | ★編集中に一覧が取り直されて下書きが消える★ | エラー一覧と同じ: ★編集モードに入れるのは一覧が今の絞り込みの結果と一致しているときだけ★、★編集中は絞り込みとページ送りを <fieldset disabled> で無効★(ErrorsScreen.tsx の canStartEdit / filtersDisabled) |
| 4-18 | ★eslint-disable を足す★ |
★足さない★。いまの DsAlertsScreen.tsx:127 に react-hooks/set-state-in-effect の抑止が1つある。★書き直すなら ErrorsScreen.tsx の「result.query !== queryString で読み込み中を判定する」形に揃えて抑止を消してよい★(増やさないことが条件。消せなければ既存の1つは残してよい) |
| 4-19 | ★生SQL($queryRaw / $executeRaw)★ |
禁止(CLAUDE.md §3) |
| 4-21 | ★findUnique の where に施設・清掃エリアの条件を足そうとする★ |
★errorReportById() は select で facility.deleted_at / section.deleted_at を読み、isVisibleErrorRow() で null を返す★(error_id !== id の null と同じ並び。4-8 の前例)。findUnique の一意条件はいじらない |
| 4-22 | ★可視性の条件を SQL だけに書く/純粋関数だけに書く★ | ★両方★: SQL(ERROR_ROW_VISIBLE_WHERE を errorReportListWhere() と dsAlertWhere() に混ぜる)で読む量を減らし、★正は isVisibleErrorRow()★(fixtures はこちらしか通らない。USE_FIXTURES で件数を変えない) |
| 4-23 | ★施設名・分類の解決(resolveFacilityIdsByName() / resolveFacilityIdsByMasters())に deleted_at の条件を足す★ |
★足さない★(罠7。過去の実績・履歴の検索が静かに消える)。U8 の範囲は robot_error_reports の行の可視性だけ |
| 4-24 | ★/alerts(事前チェック。prechecks() / precheckItemById())にも同じ条件を入れる★ |
★入れない★(範囲外。§10)。削除済みの施設の行が /alerts に出るかは監督が確かめる(§12-2) |
| 4-20 | ★git add -A★ |
禁止(CLAUDE.md §7)。★作業ツリーにある他人の変更(CLAUDE.md・docs/decisions.md の D-179・docs/handoff/ の未追跡ファイル)をコミットに含めない★ |
develop で作業する★(.claude/worktrees/ の中ではない。ワークツリーは main 基点で古い)。git pull で最新にし、64c4079 以降であることを確かめる。git status を控える。★CLAUDE.md・docs/decisions.md(D-179)・docs/handoff/TeamWorks確認事項_第13回_*・docs/handoff/回答待ち一覧_*・docs/handoff/pdf/*.py は他のセッションの作業★。触らない・コミットに含めない。npx prisma generate → npx tsc --noEmit -p . → npm test が通ることを確かめ、★テストの件数を記録する★。通らなければ着手前に報告。node_modules/next/dist/docs/ の該当の項を読む(CLAUDE.md 末尾)。★この時点ではテストが落ちる(関数・型が無い)のが正しい★。コミットはテストだけで1つ(「テストだけ。この時点では落ちる」と明記)。
lib/db/__tests__/ds-alert-mapping.test.ts(新規)★: 冒頭コメントに表1 を写し「R1〜R7・R11 は監督確定、R8〜R10・R12・R13 は指示書AX で導き監督が正としたもの(2026-10-19)」と書く。
isDsAlertRow(): R1〜R13 の出る/出ない(it.each)dsAlertFromErrorRow(): R1〜R4・R11 の各値(alert_type / device_serial / facility_id / facility_name / section_name / detected_at / resolution_status / responded_at / resolved_at / note)。★R4 は created_at が detected_at に入る★dsAlertsFromErrorRows(): 13行を入れて7行が出る・並びは呼び出し側(sortDsAlerts())に任せるので ★順は問わない★(toEqual(expect.arrayContaining(...)) と件数)dsAlertWhere(): { error_type: { in: [...D_SEC_ALERT_ERROR_TYPES] }, status: { in: [0,1,2 を robotErrorStatusFromResolutionStatus から作ったもの] }, ...ERROR_ROW_VISIBLE_WHERE } と toEqual(★期待値も変換関数から作る。直書きしない★。可視性は U8・段階2-7)lib/ds-alerts/__tests__/query.test.ts(直す)★:
row()(:11-25)を新しい型に(id: "1"・resolution_status: "unresolved"・note: null)status の選択肢の422の文言を open / all / resolved に(:67-77)。★status=done は422★(表2 A4)filterDsAlertsByStatus(:168-197): open = 未解消+対応中/resolved = 解消のみ/all。「type=disconnected & status=done で0件」のテスト(:187)は status=resolved に読み替え、★「fixtures の再現条件」という説明は消す★(fixtures の意図的な0件は無くなる。段階3)sortDsAlerts(:199-217): 同値のときの ★文字列の辞書順★("10" が "9" より先)を足すsummarizeDsAlerts(:219-233): キーを unresolved / in_progress / resolved にlib/ds-alerts/__tests__/edit.test.ts(新規)★: 表3 S1〜S6lib/dashboard/__tests__/aggregate.test.ts(直す)★: alert()(:72-91)を新しい型に(id: String(seq)・resolution_status は PrecheckStatus・resolved のとき resolved_at・note: null)。computeSecurityBanner のテスト(:300-340)は "pending" → "unresolved"、"done" → "resolved"。not.toHaveProperty("memo") は ★"note" にも当てる★(両方書いてよい)lib/db/__tests__/robot-error-select.test.ts★: SELECTS(:19-27)に ROBOT_ERROR_DS_ALERT_SELECT を足すlib/db/__tests__/ds-status.test.ts★: resolutionStatusFromControlErrorStatus / controlErrorStatusFromResolutionStatus / 往復の describe(:33-70)を削る(D14)。connectionStatus* の2つは残すlib/db/__tests__/error-report-visibility.test.ts(新規)★: isVisibleErrorRow() — 両方生きている → true/施設だけ削除 → false/清掃エリアだけ削除 → false/両方削除 → false(表5 V1〜V7 の可視性の列)。ERROR_ROW_VISIBLE_WHERE が { facility: { deleted_at: null }, section: { deleted_at: null } } と toEquallib/db/__tests__/error-report-mapping.test.ts(直す)★: ★行の組み立てに facility.deleted_at / section.deleted_at(既定 null)を足す★(型が要求する)。errorReportKeyFromRow() の visible が表5 どおり。★errorReportListWhere() の期待値に ERROR_ROW_VISIBLE_WHERE の2キーが入る★(旧「既定 → status だけ」は「status と施設・清掃エリアの可視性」に直す。facility_ids / 期間のケースも同じ2キーを含む)。★AW の表1 E1〜E9 の期待値そのものは変えない★(全行が生きている)lib/error-reports/__tests__/query.test.ts(直す)★: キーの組み立てに visible: true を足す(型)。★visible: false のキーは既定でも include_resolved=true でも外れ、total に数えない★(表5 V2・V3・V7)lib/db/__tests__/ds-alert-mapping.test.ts(1 の続き): 表5 V4〜V7 の D-Sec 列。dsAlertWhere() の期待値に ERROR_ROW_VISIBLE_WHERE の2キーlib/db/__tests__/robot-error-select.test.ts: ★ROBOT_ERROR_LIST_KEY_SELECT / ROBOT_ERROR_LIST_ROW_SELECT / ROBOT_ERROR_DS_ALERT_SELECT が facility.select.deleted_at と section.select.deleted_at を含む★ことを足す(可視性の判定に要る列の取り忘れを防ぐ)lib/config/index.ts — 設定値を1つ足すPRECHECK_NOTE_MAX_LENGTH(:628)の近くに:
/** * D-Sec アラートとして出す `robot_error_reports.error_type` の値(指示書AX D1。D-180)。 * ★完全一致(JS の `===`)で比べる★ — SQL の `IN` は写し(DB の照合順序は大小文字・末尾の空白を区別しないため)。 * ★`SECURITY_ON` は入れない★(ユーザー判断 2026-10-19。D-091 項3「セキュリティが無効であっても異常ではない」)。 * ★`NOT_STARTED`(清掃開始失敗)も入れない★(ロボット側の出発失敗。同日のユーザー判断)。 * 値の出所: TeamWorks 第10回 N-5(`D-S_CONTROL_FAILURE` = "no regular report from the D-S control.")。 */ export const D_SEC_ALERT_ERROR_TYPES: readonly string[] = ["D-S_CONTROL_FAILURE"];
★配列にする★(後から値を足せる)。★as const にしない★(includes(row.error_type) で string を渡すため)。
lib/types/index.ts(:1033-1058)DsAlertStatus を削除★。DsAlert.resolution_status: PrecheckStatus(D8)DsAlertStatusFilter = "open" | "all" | "resolved"(D10)。docstring「open = unresolved + in_progress」DsAlert:
id: string — docstring「robot_error_reports.error_id(★string のまま。Number() を通さない。D-105★)」device_serial — 「robot_error_reports.serial_number の生値(D-077)。制御盤の SN」section_name — ★旧 docstring の「実DB上の取得元が不明なため fixtures では暫定のダミー値」を消し★、「行の section_id の清掃エリア名」にdetected_at — 「errored_at ?? created_at(detectedAtFromError())」memo → note★(D9)alert_type — 「★DB からは disconnected しか出ない★(security_off は互換のため残す。D-180)」DsAlertType / DsAlertTypeFilter は変えないlib/db/robot-error-select.ts — 定数を1つ足す(末尾)/**
* D-Sec アラート(`dsAlertFromErrorRow()`。`dsAlerts()`)に要る列(指示書AX)。
* ★`section` は清掃エリア名だけ★(D-Sec のカードに出す)。★`robot_id` / `robot` を足さない★(D-Sec の行は全部 `robot_id` NULL)。
* ★`clean_timetable_id` は使わないが、select の共通テスト(紐づけのキーを含む)のために読む★
*/
export const ROBOT_ERROR_DS_ALERT_SELECT = {
error_id: true,
clean_timetable_id: true,
serial_number: true,
facility_id: true,
facility: { select: { facility_name: true, deleted_at: true } }, // deleted_at は可視性(U8・段階2-7)
section: { select: { section_name: true, deleted_at: true } },
error_type: true,
errored_at: true,
created_at: true,
status: true,
responded_at: true,
resolved_at: true,
comment: true,
} as const satisfies Prisma.RobotErrorReportSelect;
★ROBOT_ERROR_LIST_ROW_SELECT の docstring「section を足さない」はその定数についての話なので、そちらは変えない★。
lib/db/ds-alert-mapping.ts(新規)— ★純粋関数のみ★(lib/db/error-report-mapping.ts と同じ作法)/** `ROBOT_ERROR_DS_ALERT_SELECT` の結果がそのまま入る形 */
export interface DsAlertRowSource extends PrecheckDetectedAtSource {
error_id: string;
serial_number: string;
facility_id: bigint;
facility: { facility_name: string; deleted_at: Date | null };
section: { section_name: string; deleted_at: Date | null };
error_type: string;
status: number;
responded_at: Date | null;
resolved_at: Date | null;
comment: string | null;
}
/** ★D-Sec アラートの対象か(正)★: `error_type` が `D_SEC_ALERT_ERROR_TYPES` に `===` で一致し、`status` が 0/1/2 */
export function isDsAlertRow(row: Pick<DsAlertRowSource, "error_type" | "status">): boolean
/** 1行 → `DsAlert`(★`isDsAlertRow()` を通った行だけを渡す★。`status` が 0/1/2 以外なら変換関数が例外) */
export function dsAlertFromErrorRow(row: DsAlertRowSource): DsAlert
/** `rows.filter((r) => isDsAlertRow(r) && isVisibleErrorRow(r)).map(dsAlertFromErrorRow)`(fixtures 版・Prisma 版が共有する1本。可視性は U8) */
export function dsAlertsFromErrorRows(rows: readonly DsAlertRowSource[]): DsAlert[]
/** SQL 側の写し: `{ error_type: { in: [...D_SEC_ALERT_ERROR_TYPES] }, status: { in: dsAlertStatusCodes() }, ...ERROR_ROW_VISIBLE_WHERE }` */
export function dsAlertWhere(): Prisma.RobotErrorReportWhereInput
isDsAlertRow(): ★D_SEC_ALERT_ERROR_TYPES.includes(row.error_type)★(Array.prototype.includes は === 相当。★toUpperCase() / trim() で緩めない★)かつ ★dsAlertStatusCodes().includes(row.status)★(["unresolved", "in_progress", "resolved"] as const).map(robotErrorStatusFromResolutionStatus)(★0/1/2 を直書きしない★。非公開の関数 dsAlertStatusCodes() にまとめてよい)dsAlertFromErrorRow():
id: row.error_id(★string のまま★)/alert_type: "disconnected"/device_serial: row.serial_number(★そのまま★)facility_id: Number(row.facility_id)/facility_name: row.facility.facility_name/section_name: row.section.section_namedetected_at: jstIsoFromDb(detectedAtFromError(row))resolution_status: resolutionStatusFromRobotErrorStatus(row.status)responded_at / resolved_at: null はそのまま、それ以外 jstIsoFromDb()/note: row.commenterrorReportItemFromRow()(lib/db/error-report-mapping.ts:136)と同じ書き方で揃える★(日時・BigInt の扱い)control_error_histories は読まない★、★robot_id を読まない★、★security_off はここからは出ない(D4)★lib/ds-alerts/query.tsSTATUS_FILTERS(:15)を ["open", "all", "resolved"] にfilterDsAlertsByStatus()(:96-111): open → resolution_status !== "resolved"/resolved → === "resolved"/allsortDsAlerts()(:113-120): 同値は ★a.id < b.id ? -1 : a.id > b.id ? 1 : 0★(D11)。docstring に「id は string。辞書順」DsAlertSummary(:122-126): { unresolved; in_progress; resolved }/summarizeDsAlerts()(:136-)も同じキーで数えるTYPE_FILTERS と filterDsAlertsByType() は変えない★(D4)lib/ds-alerts/edit.ts(新規)— ★純粋関数のみ★(画面の保存で使う。表3)/** PATCH /errors/{error_id}/status の応答(`ErrorReportItem`)を `DsAlert` に戻す。★4項目だけ差し替え★。id が違えば例外 */
export function dsAlertWithResolution(row: DsAlert, updated: ErrorReportItem): DsAlert
/** 保存で状態が変わった行の分だけ `summary` を付け替える(旧状態 −1・新状態 +1)。★入力を書き換えない★ */
export function adjustDsAlertSummary(
summary: DsAlertSummary,
before: readonly DsAlert[],
updated: ReadonlyMap<string, DsAlert>
): DsAlertSummary
before は保存前の画面の行(id で updated と突き合わせる)。updated に無い行・状態が同じ行は数えない(表3 S4・S6)。
段階1 のテストのうち純粋関数のものが通ること。コミット。
lib/db/error-report-mapping.ts(★純粋関数のみのまま★)に足す:
/** 可視性の判定に要る列(`facility` / `section` の `deleted_at`) */
export interface ErrorRowVisibilitySource {
facility: { deleted_at: Date | null };
section: { deleted_at: Date | null };
}
/**
* ★削除済み(論理削除)の施設・清掃エリアの行は見せない★(ユーザー決定 2026-10-19「削除済みのものは表示する必要がないので、
* エラー一覧からもD-sec一覧からも消してください」。指示書AX U8・D12。D-180)。★`deleted_at` が NULL でなければ削除済み(日時によらない)★。
* ★これが正★。SQL の `ERROR_ROW_VISIBLE_WHERE` は写し(fixtures はこちらしか通らない)。
* ★施設名・分類の解決は `deleted_at` で絞らない(罠7)★ — この関数は `robot_error_reports` の行の可視性だけを決める
*/
export function isVisibleErrorRow(row: ErrorRowVisibilitySource): boolean {
return row.facility.deleted_at === null && row.section.deleted_at === null;
}
/** SQL 側の写し(`errorReportListWhere()` と `dsAlertWhere()` に混ぜる) */
export const ERROR_ROW_VISIBLE_WHERE = {
facility: { deleted_at: null },
section: { deleted_at: null },
} as const satisfies Prisma.RobotErrorReportWhereInput;
ErrorReportKeySource★ に ErrorRowVisibilitySource を足し(extends)、★ErrorReportKey に visible: boolean★ を足す。errorReportKeyFromRow() が visible: isVisibleErrorRow(row) を入れるErrorReportRowSource★ の facility を { facility_name: string; deleted_at: Date | null } に、★section: { deleted_at: Date | null } を足す★errorReportListWhere()★ の戻りに ★...ERROR_ROW_VISIBLE_WHERE★ を混ぜる(status の次。docstring に U8)filterSortErrorReportKeys()(lib/error-reports/query.ts:151-167)★ の filter の先頭に ★if (!key.visible) return false;★(★絞り込みの正★。meta.total もこれで正しくなる)lib/db/robot-error-select.ts★:
ROBOT_ERROR_LIST_KEY_SELECT に ★facility: { select: { deleted_at: true } }・section: { select: { deleted_at: true } }★ROBOT_ERROR_LIST_ROW_SELECT の facility を ★{ select: { facility_name: true, deleted_at: true } }★ に、★section: { select: { deleted_at: true } } を足す★。docstring の「★section を足さない★」は ★「section は deleted_at だけ(U8)。robot_id / robot は足さない」★ に書き換える(元の理由は robot_id が NULL の行で落ちることで、section は無関係)ROBOT_ERROR_DS_ALERT_SELECT は 2-3 のとおり(deleted_at 入り)lib/db/ds-alert-mapping.ts★: dsAlertsFromErrorRows() は isDsAlertRow(r) && isVisibleErrorRow(r)、dsAlertWhere() は ...ERROR_ROW_VISIBLE_WHERE を混ぜる(2-4 のとおり)PATCH /errors/{error_id}/status のルート(app/api/errors/[error_id]/status/route.ts)は変えない★ — 実在確認(:126 の db.errorReportById())が null を返せば既存の404の分岐に入るlib/fixtures/error-reports.tsFixtureErrorReportRow(:34-36)に ★section: { section_name: string; deleted_at: Date | null }★ を足す(★全行に持たせる★。実DBでも section_id は NOT NULL)。robotError()(:85-106)の既定は { section_name: "1F売り場", deleted_at: null }。★facilityOf()(:64-68)が返す facility にも deleted_at: null を足す★(U8)FX-ERR-08(:179-188)と FX-ERR-11(:206-215)の serial_number: "0" → ★"---"★。 ★定数 SERIAL_NOT_APPLICABLE = "---" を置き、コメントに「ステージングの実データ(2026-10-19 監督の SELECT・先方投入)。P-8 の回答は 0 だが実値は ---(§12 申し送り2)」★FX-ERR-12〜FX-ERR-17。robot_id: null・error_type: D_SEC_ALERT_ERROR_TYPES[0]・error_level: "CRITICAL"(N-5))。★シリアルは controlPanelSerialOf() で引く★(直書きしない):| id | 施設(顧客) | シリアル | status | errored_at / created_at | その他 | 表1 | |---|---|---|---|---|---|---| | FX-ERR-10(既存) | 1(1) | 施設1の制御盤(DS-001) | 0 | 前日 22:45 / 22:46 | section 1F売り場 | R1 | | FX-ERR-12 | 11(2) | 施設11の制御盤 | 0 | ★errored_at NULL★ / 当日 02:55 | ★clean_timetable_id NULL★・section 2F衣料品 | R4・R13 | | FX-ERR-13 | 5(1) | 施設5の制御盤 | 1 | 前日 23:10 / 23:11 | responded_at 当日 09:05・備考「回線業者に確認中」・section 1Fレジ前 | R2 | | FX-ERR-14 | 4(2) | 施設4の制御盤 | 2 | 前日 03:55 / 03:56 | responded_at 前日 08:40・resolved_at 前日 09:15・備考「正常復帰を確認」・section B1食品 | R3 | | FX-ERR-15 | 10(3) | 施設10の制御盤 | 2 | 2日前 02:00 / 02:01 | responded_at / resolved_at あり・備考「正常復帰を確認」 | R3 | | FX-ERR-16 | ★3(1)★ | ★施設2の制御盤★ | 0 | 当日 01:30 / 01:31 | ★シリアルと施設が食い違う★(ステージング 1000004 の形) | R11 | | FX-ERR-17 | 8(1) | 施設8の制御盤 | 1 | ★当日 01:30(FX-ERR-16 と同時刻)★ / 01:32 | responded_at あり | R12("FX-ERR-16" が先) |
clean_timetable_id は FX-ERR-12 以外 TT_MAIN_A / TT_MAIN_B のどちらか(P-9: 制御盤の行にも予定 id が入る)app/api/__tests__/master-filter.test.ts:194-201 / :364-380 が「customer_id=1 で真に減る」を見ている)status 3)は fixtures に入れない★(単体テストとルートテストのスタブにだけ置く。fixtures はデモにも使うので)status 0 — ★可視性が無ければ既定で出るはずの行★にして、外れることを確かめる):| id | 内容 | 施設 | 清掃エリア | 表5 | |---|---|---|---|---| | FX-ERR-18 | ロボットのエラー(EmergencyStop・robot_id あり) | ★削除済みのダミー施設★(id 99・名前「削除済みテスト施設(ダミー)」・deleted_at 過去の日時)。★facilities(lib/fixtures/facilities.ts)には足さない★(施設一覧・分類・他の画面に出さないため)。専用の小さな関数で作る(facilityOf() を通さない) | 生きている | V2 | | FX-ERR-19 | D-S_CONTROL_FAILURE(robot_id NULL・施設2の制御盤のシリアル) | 施設2(生きている) | ★削除済み★(section_name 「旧2F売り場(削除済み)」・deleted_at 過去の日時) | V6(status は 0 にする) |
CLAUDE.md §7)errorReportRows は19行。そのうち見える行は17行(既定(未解消・対応中)14行・「解消済みも表示」17行)★、D-Sec は7行(FX-ERR-19 は削除済みの清掃エリアなので出ない))lib/fixtures/ds-alerts.ts を削除し、lib/fixtures/index.ts:19 の export * from "./ds-alerts"; を消すDataSource と2つの実装 — lib/db/index.tsDataSource.dsAlerts()(:371)の契約は変えない★(引数なし・Promise<DsAlert[]>。/dashboard も呼ぶ)。docstring に「robot_error_reports の D_SEC_ALERT_ERROR_TYPES の行(指示書AX)。★並べ替え・絞り込み(type / status / 分類)はルート側★」:683-685):
async dsAlerts() {
// ★Prisma 版と同じ純粋関数★(D13)。★エラー一覧の上書き表を反映してから作る★ —
// `PATCH /errors/{error_id}/status` で書いた対応状況が D-Sec にも出る(U4)
return dsAlertsFromErrorRows(errorReportRows.map(withFixtureErrorReportOverride));
},:1954-1996。docstring ごと置き換える):
async dsAlerts() {
const { getPrisma } = await import("./prisma");
const prisma = await getPrisma();
// ★SQL は写し(`error_type IN`・`status IN`)。正は `dsAlertsFromErrorRows()` の `===`(D2・D3)★
const rows = await prisma.robotErrorReport.findMany({
where: dsAlertWhere(),
select: ROBOT_ERROR_DS_ALERT_SELECT,
});
return dsAlertsFromErrorRows(rows);
},★旧 docstring の「security_off は返さない理由(security_statuses に列が無い)」は消し★、新しい理由(U1。D-091 項3)に書き換える。dsAlerts(fixtures。:60)と resolutionStatusFromControlErrorStatus(:168)を消す。ROBOT_ERROR_DS_ALERT_SELECT / dsAlertWhere / dsAlertsFromErrorRows を足すlib/db/index.ts:15 付近の冒頭コメント「書き込んでよいのは robot_error_reports / control_error_histories の…」は変えない★(権限の説明として正しい)lib/db/index.tserrorReports()(:647-656)★: 変えなくてよい(errorReportKeyFromRow() が visible を入れ、filterSortErrorReportKeys() が外す)。★確かめるだけ★errorReportById()(:658-667)★: 行が見つかっても ★!isVisibleErrorRow(row) なら null★(= PATCH は404)errorReports()(:1743-1783)★: 1段目の where は errorReportListWhere()(2-7 で可視性入り)・select は ROBOT_ERROR_LIST_KEY_SELECT(deleted_at 入り)のまま。コードの変更は無いはずerrorReportById()(:1785-1806)★: report.error_id !== id の null の直後に ★if (!isVisibleErrorRow(report)) return null;★(4-21)。コメントに U8errorReportItemsInPageOrder()(:540-556)★: ★見えない行も「消えた行」と同じく飛ばす★(1段目と2段目の間に施設が削除された場合。meta.total は1段目のまま — 4-15 と同じ扱い)updateErrorReportColumns() は変えない★(存在・可視性はルートが書く前に errorReportById() で確かめる)lib/db/__tests__/fixture-error-reports.test.ts: :49-53(17行・FX-ERR-01〜17)/:88・:92(シリアル ---)/:96-97(robot_id NULL は 08・10〜17)/:102-108(既定14・全17)/:120 付近(ページの total)/並びの期待(降順の列)を ★テストを走らせて得た順ではなく、表の時刻から手で導いて★ 書くlib/db/__tests__/fixture-error-reports.test.ts の続き: ★errorReportRows は19行(FX-ERR-01〜19)★・robot_id NULL は 08・10〜17・19・★既定14・全17(FX-ERR-18・19 は出ない)★・★db.errorReportById("FX-ERR-18") / ("FX-ERR-19") は null★app/api/errors/__tests__/route.fixtures.test.ts: 件数(既定14・全17)。★PATCH /api/errors/FX-ERR-18/status・FX-ERR-19 は404★(表5 V2・V6)app/api/ds-alerts/__tests__/route.fixtures.test.ts★: fixtures で GET /api/ds-alerts(既定5件・status=all で7件・summary {unresolved:3, in_progress:2, resolved:2}・FX-ERR-08 / FX-ERR-11 / ★FX-ERR-19(削除済みの清掃エリア)★ が出ない)→ ★PATCH /api/errors/FX-ERR-10/status(resolved)→ GET /api/ds-alerts に反映(status=open から消え、summary.resolved が 3)★。beforeEach で resetFixtureErrorReportOverrides()段階1 の残りのテストが通ること。コミット。
d-watch-api.yaml(★先に直す。CLAUDE.md §6-1★)/ds-alerts(:1452-1484)の description に:
robot_error_reports のうち error_type が D-S_CONTROL_FAILURE の行(完全一致。D-180)★。SECURITY_ON・NOT_STARTED は含まない(エラー一覧 /errors には出る)alert_type は DB からは disconnected だけ★。security_off は互換のため enum に残すが出ないfacility_id(制御盤のシリアルの所属ではない)/detected_at は errored_at(無ければ created_at)detected_at の降順、同値は ★id(error_id)の昇順(文字列の辞書順)★PATCH /errors/{error_id}/status(同じ行)★status パラメータ(:1469-1475): enum: [open, all, resolved]/description: open=未解消・対応中 / resolved=解消/ds-alerts/{id}/status(:1486-1511)を削除★(D7)DsAlert(:2647-2678): id: { type: string, maxLength: 50, example: "1000001", description: robot_error_reports.error_id }/alert_type の description に D4/device_serial の example を "DFAS010001"・description「生値(D-077)」/ section_name の description を「行の section_id の清掃エリア名」(★「取得元が未確定で fixtures では暫定のダミー値」を消す★)/detected_at に description/ resolution_status: enum [unresolved, in_progress, resolved]・description「未解消 / 対応中 / 解消」/★memo → note★DsAlertListResponse.summary(:2683-2688): unresolved / in_progress / resolvedsecurity_banner(:308-316)の description に「★/ds-alerts が DB から security_off を返さないため、DB 接続時は常に空配列(D-180)★」を足すapp/api/ds-alerts/route.ts:1-10)の「★対応状況の更新(PATCH /ds-alerts/{id}/status)は今回作らない…★」を ★「対応状況・備考の更新は PATCH /errors/{error_id}/status(robot_error_reports の同じ行。指示書AX D6)。このルートは読むだけ」★ に書き換えるparseDsAlertQuery() → 分類の解決と db.dsAlerts() を同じ try → filterRowsByFacilityIds() → summary → type / status → 並べ替え → ページ)。型の変更に追従するだけapp/api/dashboard/route.ts★コードは変えない★(db.dsAlerts() → computeSecurityBanner() のまま。D4)。:189-211 付近に「★DB のデータでは security_off が出ないのでバナーは常に空(指示書AX D4)★」のコメントを1つ足してよい。
app/api/ds-alerts/__tests__/route.test.ts(新規。Prisma 経路)★ — app/api/errors/__tests__/route.test.ts の作法(vi.mock("@/lib/db/use-fixtures", () => ({ USE_FIXTURES: false }))・@/lib/db/prisma のスタブ):
robotErrorReport.findMany は ★guardNullRobotId() を通し★、★where を無視して全行(表1 の13行)を返す★(= SQL が ci・PAD SPACE で余分に当てた状態を再現。D2)findMany の引数の where が dsAlertWhere() と toEqual、select が ROBOT_ERROR_DS_ALERT_SELECT★(ルートが別の条件を組み立てていない)controlErrorHistory のスタブを置かない(呼ばれたら落ちる形にする)★ — もう読まないことの証拠master-filter.test.ts に任せてよい(fixtures 経路)。★Prisma 経路で施設名・分類を同時に試すと、モックした @/lib/db/prisma の動的 import の罠を踏む★(指示書AW §13-4 #2)。要るなら db.facilityGroupIndex() 等を vi.spyOn で差し替える 1b. ★app/api/errors/__tests__/route.test.ts(直す。Prisma 経路)★: スタブの行に facility.deleted_at / section.deleted_at を持たせ(既存の行は null)、★スタブの where の評価に facility.deleted_at / section.deleted_at の条件を足す★。表5 V1〜V3・V9 の行を足し、★V2・V3 が既定でも include_resolved=true でも出ず meta.total に数えない★、★V8(V2 の施設名で絞る)が0件・200★。★where を無視して全行返すスタブでも V2・V3 が出ない★(純粋関数側で外れる)ことを1件確かめる。★1段目の where が errorReportListWhere() と toEqual★ は既存のまま(可視性が入った戻りと一致) 1c. ★app/api/errors/[error_id]/status/__tests__/route.test.ts(足す)★: findUnique のスタブが ★施設が削除済みの行(V2)・清掃エリアが削除済みの行(V3)・D-Sec の V5★ を返す → ★404(error.code は既存の404と同じ)・updateMany を呼ばない・db.write を出さない★。生きている行(V1・V4)は200(既存) 1d. app/api/ds-alerts/__tests__/route.test.ts(1 の続き): 表5 V4〜V7。★V5〜V7 は一覧にも summary にも出ない★(where を無視して全行返すスタブで)app/api/dashboard/__tests__/route.test.ts★: スタブの controlErrorHistory(:174)を消す(読まないので)。★robotErrorReport.findMany のスタブ(:172)は [] のまま★でよい。★表1 R10(status 3)の行を返しても200★のテストを1つ足す(dsAlerts() がダッシュボードを落とさない証拠。D3)app/api/__tests__/master-filter.test.ts: :364-380 の summary の型とキーを unresolved / in_progress / resolved に。:194-201 は status=all のまま(all は残る)app/api/errors/** と lib/db/__tests__/error-report-mapping.test.ts・lib/error-reports/__tests__/query.test.ts は行の型の追従と表5 の追加が要るので、このリストから外れる★): app/api/prechecks/**・lib/prechecks/__tests__/*・lib/db/__tests__/robot-error-write-sql.test.ts・lib/db/__tests__/error-report-mapping.test.ts(★AW の表1 E8 のシリアル '0' はテスト内の行なので変えない★)YAML → ルート → テストの順でコミット。
/ds-alerts に編集モード)★見た目と操作はエラー一覧(components/features/errors/ErrorsScreen.tsx・.module.css)から借りる。新しい共通部品は作らない★(指示書AW と同じ考え方)。 ★D-Sec 画面はカードの一覧(表ではない)★なので、カードの中で同じ部品を使う。
components/features/ds-alerts/DsAlertsScreen.tsxDsAlertStatusFilter は "open" | "all" | "resolved"、EMPTY_SUMMARY は { unresolved: 0, in_progress: 0, resolved: 0 }、バッジの分岐(:294-308)は resolved / in_progress / unresolved:141-143)・状態ボタン(:229-261)・バッジを表4 のとおり。★副題(:136)と種別のセレクト(:217-228)・typeFilter の state は ★変えない★(U6。ユーザー判断で残す。「セキュリティ無効」を選ぶと0件になるのは承知のうえ)★。★編集中は種別のセレクトも他の絞り込みと同じく無効にする★(<fieldset disabled> の中に入れる)。カードの「通信断」Pill(:281)は残すErrorsScreen.tsx の editing / drafts / rowErrors / saving / beforeunload / canStartEdit / filtersDisabled / startEdit / cancelEdit / saveEdit をそのまま写す):
編集 / 編集中は 保存(N件)(N=0 なら 保存、保存中は 保存中…)・キャンセル★(ボタンは固定幅。ErrorsScreen.module.css:53- の .editButton 等を写す)lib/prechecks/edit-drafts.ts の draftStatus / draftNote / setDraftStatus / setDraftNote / saveDrafts★(DsAlert がそのまま EditableRow になる。D9)/** ★書き込みはエラー一覧と同じルート(指示書AX D6)★。応答は ErrorReportItem なので DsAlert に戻す */
async function sendDsAlertUpdate(row: DsAlert, request: PrecheckStatusUpdateRequest): Promise<DsAlert> {
const updated = await apiFetch<ErrorReportItem>(`/api/errors/${encodeURIComponent(row.id)}/status`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
return dsAlertWithResolution(row, updated);
}summary は adjustDsAlertSummary(summary, 保存前の items, updated)★(D15)。★1件でも失敗したら編集モードのまま★、失敗したカードの直下に文言(t(rowError)。サーバーの文言は辞書に無ければ原文)。全部成功なら通常モードへSTATUS_OPTIONS と同じ並び・文言。.statusSelect を写す)と ★備考の textarea★(2行・resize: none・maxLength={PRECHECK_NOTE_MAX_LENGTH}・placeholder t("備考を入力"))。 ★通常モードの備考は今の memoLine の位置に item.note を出す★(空なら出さない=今と同じ)item.resolution_status === "resolved" かつ ★保存済みの値で判定★。エラー一覧と同じ)ErrorsScreen.module.css の .rowChanged と同じ色をカードの背景に)<fieldset disabled> で無効★、案内「編集中は絞り込みとページ送りを使えません」と失敗件数({n}件を保存できませんでした)を出すwindow.confirm(t("変更を破棄しますか?"))★t("読み込み中…") に直す(4-16)。effect の書き方は 4-18item.id(文字列)のまま★components/features/ds-alerts/DsAlertsScreen.module.css編集ボタン・プルダウン・textarea・目印・案内・失敗の文言のクラスを ErrorsScreen.module.css から写す(★JP/EN で位置が動かないよう固定幅★)。
lib/i18n.ts"未解消・対応中": "Unresolved / In progress"(★副題は変えないので副題のキーは足さない★。U6)未解消・対応中・解消・編集・保存・保存({n}件)・キャンセル・保存中…・変更を破棄しますか?・編集中は絞り込みとページ送りを使えません・{n}件を保存できませんでした・備考を入力・読み込み中…未対応: "Unsupported"(:182)は変えない★(対応機種バッジの訳。U5 で D-Sec 画面がこのキーを使わなくなるだけ)画面のテストは vitest で走らない(vitest.config.ts)ので段階10 で見る。保存の規則は純粋関数(表3)と edit-drafts.test.ts で固定してある。
コミット。
scripts/seed-local-db.ts)★ローカル(dwatch_local)の /ds-alerts を空にしないため★。★lib/db/local-guard.ts は触らない★。
control_error_histories への投入をやめる★: import(:65 の dsAlerts、:44 の controlErrorStatusFromResolutionStatus)と、:812-830(security_off を弾く・件数7の検査)・:1160-1182(controlErrorHistory.create)を消す。 ★:900 の controlErrorHistory.deleteMany({}) は残す★(前回のシードの行を消すため)。:2238 の件数のログから control_error_histories を外すrobot_error_reports に D-Sec の行を入れる★: errorReportRows(lib/fixtures/error-reports.ts)のうち ★isDsAlertRow() と isVisibleErrorRow() の両方を通る行★(★FX-ERR-19 は入れない★ — ローカルでは施設の生きている清掃エリアに付け替わって出てしまうため)を、次の値で robotErrorReport.create:
id = error_id = fixtures の error_id/facility_id = fixtures の値/section_id = ★facilityIdToSectionId.get(facility_id)★(無ければ例外。:1094 と同じ)robot_id = ★section_id と同じ値★(★ローカルのスキーマは robot_id が NOT NULL★。シードの既存の採番規則「sections.id とロボットの id は同値」に合わせたダミー。コメントで理由を書く)clean_timetable_id = ★null★(fixtures の予定 id 9001 等はローカルに無い。予定不明として出さないため)serial_number / error_type / error_level / status / comment / errored_at / created_at / responded_at / resolved_at は fixtures の値そのまま(日時は ★既に toDbJst() 済み★。もう一度通さない)error_detail = 既存の ERROR_DETAIL_PLACEHOLDERsection.section_name ではなく、ローカルの sections の名前で出る★(USE_FIXTURES の true/false で清掃エリア名だけ違う。承知のうえ。コメントで書く)コミット(シードだけで1つ)。
★段階1〜6 と独立★。先にやってもよい。★コミットは別にする★。
画面幅 1440px のとき、表の入れ物の幅 = 1440 − サイドバー 220 − .main の左右 padding 64 − カードの枠 2 = ★1154px★。 行が多くて .main に縦スクロールバー(15px)が出ると ★1139px★。表の最小幅は ★126 + 1024 = 1150px★ なので、★11px 横スクロールする★。
components/features/detail-log/CleanLogTable.module.css:38 の ★--col-facility: 126px; → 110px;★ → 最小幅 ★110 + 1024 = 1134px★。 縦スクロールバーが 17px でも入れ物は 1137px なので収まる。★画面幅 1435px 以上で横スクロールが出ない★(スクロールバー 15px のとき).compact の 240px も変えない)overflow-wrap: anywhere で折り返す(★長い施設名は1行増えることがある。承知のうえ★)| 場所 | 今 | 直す |
|---|---|---|
CleanLogTable.module.css:10 |
施設名 126 は「画面幅 1440px で表 1151px」に収まる値(126 + 1024 = 1150。…) | ★施設名 110 は、画面幅 1440px で縦スクロールバー(15〜17px)が出ても収まる値(110 + 1024 = 1134。入れ物は 1154px、スクロールバーが出ると 1137〜1139px)★。2026-10-19 に 126 → 110(11px の横スクロールを消すため。指示書AX 段階7) |
CleanLogTable.module.css:26 |
表の最小幅 1150px を変えないため | ★表の最小幅 1134px を増やさないため★ |
CleanLogTable.module.css:33-34 |
通常表示 = 126 + 1024 = 1150px(画面幅 1440px のとき表は 1151px なので横スクロールは出ない…) | ★通常表示 = 110 + 1024 = 1134px(画面幅 1440px のとき入れ物は 1154px、縦スクロールバーが出ると 1137〜1139px なので横スクロールは出ない…)★ |
FacilitiesScreen.module.css:43 |
親の幅 1151px を既に超えて… | ★親の幅 1154px(縦スクロールバーが出ると約 1139px)を既に超えて…★(★値の min-width: 1300px は変えない★) |
CleanAreaScreen.module.css:209 |
切替前の実測(画面幅1440px・表の幅1151px) | ★過去の実測なので数値は変えない★。直後に「(縦スクロールバーが出ると約 1139px)」を足すだけ |
DsLogScreen.module.css:83 |
画面幅1440px 時の親の幅は約1150px なので | 「約1150px(縦スクロールバーが出ると約1139px)」に(最小幅 800px なので結論は同じ) |
★D-178 の決定文(docs/decisions.md:12901 以降)にある「最小幅 1150 を変えないため」は本書では直さない★(docs/decisions.md は D-180 の追記だけ。§13 の下書きに「D-178 の 1150 は 1134 に読み替える」と書いてある)。
施設ロボットマスタ(FacilitiesScreen)の表は min-width: 1300px で、画面幅 1440px では約 161px はみ出す(既知。コメント :43 のとおり)。★本書では直さない★。
docs/fixtures.md:
:21)の ds-alerts.ts を消す:293-305)を書き換える: ★D-Sec アラートは errorReportRows の D-S_CONTROL_FAILURE 行から作る(7行。§13 の FX-ERR-10・12〜17)★/状態3つ・備考あり/なし・errored_at NULL・シリアルと施設の食い違い・同時刻/★security_off は fixtures にも無い(D-180)★/★「disconnected × done で0件」の意図的な0件は無くなった★(0件を見たいときは分類で絞る):444-451): security_banner の「複数件」の行に「★fixtures・DB とも security_off が無いので出ない(D-180)。描画は aggregate.test.ts で検証★」、:451 の section_name の注記を消す:503-): ★19行(見える行17)★・既定14・全17、FX-ERR-12〜19 の行(★FX-ERR-18・19 は削除済みの施設・清掃エリアなので、エラー一覧にも D-Sec にも出ない。U8★)、FX-ERR-08・11 のシリアル ---、既定の並び(★表の時刻から手で導く★)d-watch-api.yaml / lib/types/index.ts / ルートの冒頭コメント(段階2・4 で書いたもの)docs/decisions.md に D-180 を追記★(§13 の下書き。★追記直前に grep -n "^### D-1[78][0-9]" docs/decisions.md で最終番号を確かめる★。D-179 の本文は触らない。★他のセッションの未コミットの差分を自分のコミットに含めない — git add -p で D-180 の塊だけを選ぶか、D-179 がコミットされるのを待つ★)★CLAUDE.md / docs/code-map.md / docs/spec.md / docs/handoff.md は監督が直す★(§12 の監督向けの申し送り)。
npx tsc --noEmit -p . — 0件npm run lint — 0件(★eslint-disable を足していない★・i18n の重複キー・$queryRaw・any・未使用の import が無い)npm test — ★全件通過★(段階0 の件数から増えている)USE_FIXTURES=true で npm run build★ — 成功(★ワークツリーではなく本体で★。Turbopack が node_modules のジャンクションを拒否する)$queryRaw は既存の「使わない」と書いたコメントだけ):
grep -rn "controlErrorHistory" app lib components --include=*.ts --include=*.tsx | grep -v generated | grep -v __tests__
grep -rn "resolutionStatusFromControlErrorStatus\|controlErrorStatusFromResolutionStatus\|DsAlertStatus\b" app lib components scripts
grep -rn "\"pending\"\|\"done\"" lib/ds-alerts components/features/ds-alerts lib/dashboard
grep -rn "memo" lib/types/index.ts lib/ds-alerts components/features/ds-alerts lib/db/ds-alert-mapping.ts
grep -rn "robot_id: true\|robot: true\|robot: {" lib/db/robot-error-select.ts
grep -rn "ds-alerts/{id}/status" d-watch-api.yaml
grep -rn "\$queryRaw\|\$executeRaw" app lib components★アプリ内ブラウザ(mcp__Claude_Browser__*)を使う。Edge(ヘッドレス・CDP を含む)は使わない★(ユーザー指示 2026-10-16)。 ★ユーザーの :3000(next dev。ステージング向け)は止めない★。
:3100)USE_FIXTURES=true npm run build(PowerShell: $env:USE_FIXTURES='true'; npm run build)AUTH_TOKEN をその場で生成★(例: Node の crypto.randomBytes(24).toString("hex"))し、★環境変数で渡して★ USE_FIXTURES=true npx next start -p 3100 で起動(process.env は .env.local より優先)。★トークンを文書・コミット・報告に書かない★preview_start の url で http://localhost:3100 を開き、ログイン画面にそのトークンを入れるread_page / get_page_text / javascript_tool / スクリーンショット):
FX-ERR-16 が施設3の名前で出る。FX-ERR-12 の検知時刻が created_at(当日 02:55)。★「セキュリティ無効」のカードが無い★。★種別のセレクトは残っていて、「セキュリティ無効」を選ぶと0件・「通信断」で5件★(U6・表2 A5)/errors(「解消済みも表示」オンでも)と /ds-alerts(「全件」でも)に ★FX-ERR-18・19 が出ない★。javascript_tool で fetch("/api/errors/FX-ERR-18/status", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status: "in_progress" }) }) → ★404★(fixtures の上書き表だけなので実害なし)編集 → FX-ERR-10 を「解消」・FX-ERR-12 に備考 → 保存(2件) → 通常モードに戻り、★FX-ERR-10 は「未解消・対応中」の表示のまま残る★(取り直すまで)・数値カードが ★未解消2・対応中2・解消3★ に変わる(保存前は 3・2・2。FX-ERR-12 は備考だけなので数は変わらない)/errors で FX-ERR-10 が「解消」・FX-ERR-12 の備考が出る。★逆に /errors で FX-ERR-13 を「未解消」にすると /ds-alerts に反映★beforeunload)、キャンセルで確認 → 下書きが消えるresize_window で 1440×900 にし、★行が多くて縦スクロールが出ている状態★(期間を広げる・per_page を最大に)で、JP と EN の両方で:
const w = document.querySelector('[class*="tableWrap"]');
const main = document.querySelector('main') ?? document.querySelector('[class*="main"]');
({ scroll: w.scrollWidth, client: w.clientWidth, scrollbar: main.offsetWidth - main.clientWidth })★scroll === client★ であること。★scrollbar が 0 なら縦スクロールバーが出ていない(オーバーレイ型)ので、そのことを報告する★(それでも scroll === client を確かめる)。終わったら resize_window を desktop に戻すdwatch_local。条件付き)npm run seed:local(★ローカル以外を拒否する★)→ USE_FIXTURES=false で ★ホストが localhost・DB 名が dwatch_local であることを検査してから★ next start -p 3100:
/ds-alerts に7件(既定5件)。1行を「対応中 → 解消 → 未解消」・備考を入れて空に → サーバーのログに db.write が success: true・route が /api/errors/[error_id]/status・備考の中身なし。最後は元の値に戻す着手の条件(すべて): ステージングに接続できる(★別の認証情報を探したり試したりしない★)/D-S_CONTROL_FAILURE の行が3行ある(2026-10-19 時点。★件数が違えば、書かずに件数だけ報告★)。
手順(★ステージングへの4カラムの書き込みはユーザーが許可済み。D-152 / CLAUDE.md §3★):
robot_error_reports を facilities / sections と結合し、どちらかの deleted_at が NULL でない行の件数と error_type を記録する。★0件でなければ、下の「3行」等の期待値はその分減る★(減った数と一致すれば合格。食い違えば書かずに報告):3000 経由(止めない)。★/ds-alerts が3行(既定で未解消の2行、「全件」で3行・解消1行)★、★1000004 が施設3で出る★、検知時刻が created_at(16行すべて errored_at NULL)、★SECURITY_ON・NOT_STARTED が出ない★responded_at / resolved_at / comment が空の D-Sec の行を選ぶerror_id と4列。★mysql で SELECT するだけ★(UPDATE / DELETE / INSERT を手で打たない。パスワードは表示しない)/errors)にも同じ状態が出ること★を見るupdated_at は DB が更新するので戻らない — 値を記録して報告。AT §10 #8 で受け入れ済み)npm run seed:local をステージングに向けない★| ファイル | 内容 | 段階 |
|---|---|---|
lib/db/__tests__/ds-alert-mapping.test.ts(新規) |
表1・dsAlertWhere() |
1 |
lib/ds-alerts/__tests__/query.test.ts |
語彙・status=resolved・done は422・文字列の並び・summary のキー |
1 |
lib/ds-alerts/__tests__/edit.test.ts(新規) |
表3 | 1 |
lib/dashboard/__tests__/aggregate.test.ts |
型の追従(バナーのテストは残す) | 1 |
lib/db/__tests__/robot-error-select.test.ts |
ROBOT_ERROR_DS_ALERT_SELECT を足す |
1 |
lib/db/__tests__/ds-status.test.ts |
制御盤エラーの変換のテストを削る | 1 |
lib/db/__tests__/fixture-error-reports.test.ts |
17行・既定14・シリアル ---・robot_id NULL の行 |
3 |
app/api/errors/__tests__/route.fixtures.test.ts |
件数 | 3 |
app/api/ds-alerts/__tests__/route.fixtures.test.ts(新規) |
fixtures で GET → PATCH /errors → GET に反映 |
3 |
app/api/ds-alerts/__tests__/route.test.ts(新規) |
Prisma 経路(表1・表2・where / select・controlErrorHistory を呼ばない) |
4 |
app/api/dashboard/__tests__/route.test.ts |
controlErrorHistory のスタブを消す・status 3 でも200 |
4 |
app/api/__tests__/master-filter.test.ts |
summary のキー |
4 |
lib/db/__tests__/error-report-visibility.test.ts(新規) |
isVisibleErrorRow()・ERROR_ROW_VISIBLE_WHERE(表5) |
1 |
lib/db/__tests__/error-report-mapping.test.ts |
行の型の追従・visible・errorReportListWhere() に可視性の2キー |
1 |
lib/error-reports/__tests__/query.test.ts |
visible: false のキーが外れ total に数えない |
1 |
app/api/errors/__tests__/route.test.ts |
Prisma 経路で表5 V1〜V3・V8・V9 | 4 |
app/api/errors/[error_id]/status/__tests__/route.test.ts |
削除済みの施設・清掃エリアの行は404・updateMany を呼ばない |
4 |
| ★無修正で通ること★ | app/api/prechecks/**・lib/prechecks/__tests__/*・lib/db/__tests__/robot-error-write-sql.test.ts・lib/db/__tests__/precheck-mapping.test.ts |
— |
lib/config/index.ts(定数1つ)・lib/types/index.ts(DsAlert まわり)・lib/db/robot-error-select.ts(定数1つと、既存2定数への deleted_at の追加)・lib/db/ds-alert-mapping.ts(新規)・lib/db/index.ts(dsAlerts() 2本と import、★U8 の errorReportById() 2本・errorReportItemsInPageOrder()★)・ ★lib/db/error-report-mapping.ts(2-7 の可視性の型・isVisibleErrorRow()・ERROR_ROW_VISIBLE_WHERE・errorReportListWhere() だけ)★・★lib/error-reports/query.ts(filterSortErrorReportKeys() の visible の1行だけ)★・ lib/db/ds-status.ts(2関数の削除)・lib/ds-alerts/query.ts・lib/ds-alerts/edit.ts(新規)・lib/dashboard/aggregate.ts(★!== "done" → !== "resolved" の1か所とコメントだけ★)・ lib/fixtures/error-reports.ts・lib/fixtures/ds-alerts.ts(削除)・lib/fixtures/index.ts(1行)・app/api/ds-alerts/route.ts・app/api/dashboard/route.ts(コメントだけ)・ components/features/ds-alerts/DsAlertsScreen.tsx・.module.css・lib/i18n.ts・scripts/seed-local-db.ts(段階6 の範囲だけ)・ components/features/detail-log/CleanLogTable.module.css(段階7)・FacilitiesScreen.module.css / CleanAreaScreen.module.css / DsLogScreen.module.css(★コメントだけ★)・ d-watch-api.yaml・docs/fixtures.md・docs/decisions.md(★D-180 の追記だけ★)・§8 のテスト
CLAUDE.md★(監督が直す。いま別のセッションが編集中)・★docs/decisions.md の D-179 とそれ以前の本文★・docs/code-map.md・docs/spec.md・docs/handoff.mddocs/handoff/TeamWorks確認事項_第13回_*・docs/handoff/回答待ち一覧_*・docs/handoff/pdf/*★(他のセッションの作業)d-watch-mock.jsx(モックは書き換えない。U5 の文言の食い違いは承知のうえ。ユーザー確定)prisma/schema.prisma / lib/generated/prisma/**★(db pull しない。D-172。RobotErrorReport.updated_at に @updatedAt を付けない)lib/db/robot-error-write.ts・app/api/errors/** のルート・lib/prechecks/status-update.ts・lib/prechecks/edit-drafts.ts の中身★(書き込みの規則は1本。D6)lib/db/precheck-mapping.ts の中身(resolutionStatusFromRobotErrorStatus() 等は使うだけ)・lib/db/error-report-mapping.ts の ★2-7 以外★(errorScheduleLink() / errorReportItemFromRow() の規則)lib/facilities/resolve-by-name.ts / resolve-by-master.ts / lib/db/facility-name-source.ts / facility-group-source.ts)★(deleted_at で絞らない=罠7 を変えない)/alerts の読み取り(prechecks() / precheckItemById())★(U8 は範囲外。§12-2)components/features/errors/**・components/features/alerts/**(写すだけ。変えない)dsDevices() / /ds-logs / lib/fixtures/ds-devices.ts(D-Sec ログは影響なし)computeSecurityBanner() の中身("done" → "resolved" の1か所以外)・components/features/dashboard/**lib/db/local-guard.tseslint-disable の追加★git add -A★facilities.contract による D-Sec メニュー・データの出し分け★(CLAUDE.md §4.6。P2)prisma db pull(robot_id NULL 可・serial_number の拡張の取り込み。D-172)PrecheckStatus → ResolutionStatus 等。指示書AW §10 と同じ)/alerts(事前チェック+出発失敗)で削除済みの施設・清掃エリアの行を外すこと★(U8 はエラー一覧と D-Sec だけ。§12-2 で監督が確かめる)deleted_at の規則(罠7 を含む)★Q1〜Q3・Q5〜Q8 は確定し、本文に反映済み。未確定は Q4 だけ★。
| # | 論点 | 具体例 | 回答 | 反映した場所 |
|---|---|---|---|---|
| Q1 | D-Sec 画面の対応状況の文言をエラー一覧に揃えるか、モックの文言(未対応 / 対応中 / 対応済み)を残すか | ステージングの同じ行(例: 1000004)が、エラー一覧では「未解消」、D-Sec 画面では「未対応」と出る。★今の D-Sec 画面は EN で「未対応」が "Unsupported" と出ている★ | ★確定: 揃える(未解消 / 対応中 / 解消)★(ユーザー 2026-10-19) | §2 U5 / 表4 / 段階5-1-2 / 5-3 / 段階10 |
| Q2 | D-Sec 画面の種別セレクトと副題 | DB からは「通信断」しか出ないので、「セキュリティ無効」を選ぶと必ず0件 | ★確定: そのまま残す(0件になるのは承知のうえ)★(ユーザー 2026-10-19)。★本書の旧暫定(外す・副題を変える)は撤回★ | §2 U6 / 表2 A5 / 表4 / 段階5-1-2 / 5-3 / 段階10 / §13 |
| Q3 | ダッシュボードのセキュリティバナー(赤い警告帯)と「D-Sec N件」の Pill | U1 により DB のデータでは ★二度と出ない★。docs/spec.md:34 は F-01 の機能として挙げている |
★確定: コードは残す★(ユーザー 2026-10-19)。廃止・転用は範囲外 | §2 D4・U7 / 段階4-1-6 / §10 |
| Q4 | 削除済み(deleted_at)の施設・清掃エリアのアラートを出すか |
旧 D-Sec 実装は controls.deleted_at で絞っていた。★facilities / sections / controls は deleted_at の論理削除なので行は残り、絞らなければそこに紐づくエラー行も返ってくる★(ユーザーの「削除済みなら DB にいないので返ってこないのでは」への答え) |
★確定(ユーザー 2026-10-19)★:「削除済みのものは表示する必要がないので、エラー一覧からもD-sec一覧からも消してください」→ ★エラー一覧と D-Sec の両方で外す。PATCH は404★(U8)。旧暫定「絞らない」は破棄 | §0 #8 / §2 U8・D12 / 表5 / 4-21〜4-24 / 段階1-7〜11 / 2-7 / 3-1-4 / 3-3b / 3-4 / 4-4 / 段階6 / 段階8 / 段階10・11 / §8 / §9 / §10 / §13 / §15 |
| # | 論点 | 決定 | 反映した場所 |
|---|---|---|---|
| Q5 | 表1 の R8〜R10・R12・R13、表2(A1〜A9)、表3(S1〜S6)、fixtures の件数(エラー一覧 17行・既定14/D-Sec 7行・既定5・summary 3/2/2) |
★正とする★ | §3 / 3-1 / 3-4 / 段階8 / 段階10 |
| Q6 | DsAlert.memo → note の改名(D9) |
★改名する★ | §2 D5・D9 / 2-2 / 4-1-4 / 段階5 |
| Q7 | status パラメータの done → resolved・summary のキーの改名(D10) |
★改名する。旧 done は422★ |
§2 D10 / 2-5 / 4-1-2・5 / 表2 A4 |
| Q8 | シード(段階6)を本書に含めるか | ★含める★(robot_error_reports に D-Sec 7行を入れる) |
段階6 / 段階10-2 |
| # | 論点 | 本書の暫定 | 直す場所 |
|---|---|---|---|
| Q9 | ★表5(V1〜V9)と fixtures の FX-ERR-18・19(errorReportRows 19行・見える行17・D-Sec 7行は変わらず)を正にしてよいか★。特に ★V9(清掃エリア単位で決まる)★・★deleted_at は NULL か否かだけで決める(未来の日時でも削除済み)★・★FX-ERR-18 の施設は facilities の fixture に足さないダミー(id 99)★ |
本書で U8 から導いた値。★本書のとおりで確定(監督判断 2026-10-19)★ | 表5 / 3-1-4・5 / 3-4 / 段階8 / 段階10 |
docs/handoff/TeamWorks確認事項_第13回_* は触らない★)error_id 1000004(D-S_CONTROL_FAILURE)★: シリアル DFAS010001 は施設2の制御盤だが、行の facility_id / section_id は 3(2026-10-19 監督の SELECT・先方投入)。 当方は P-8 d = A のとおり ★行の施設(3)で出す★。★どちらが正しいか(行の施設の誤りか、制御盤の付け替えか)★を確かめたい。SECURITY_ON)・清掃開始失敗(NOT_STARTED)の serial_number★: 第11回 P-8 の回答は 0 だが、ステージングの実値は ★---★(6行すべて)。当方は値で見分けないので影響は無いが、★今後どちらに揃うのか★を確かめたい(第10回 N-5 では「serial_number が null」という案もあった)。errored_at がすべて NULL★(16行。2026-10-19)。当方は created_at で代用している。★errored_at は今後入るのか★(入らないなら「エラーが起きた時刻」ではなく「記録した時刻」が画面に出ている)。CLAUDE.md 等、本書の実装では直さないもの)CLAUDE.md §4.7 の取得元の表の「エラー」の行 — 「★3区分の見分けと D-Sec アラートの取得元の切り替えは次フェーズ★」を、本書の実装後に「D-Sec アラートは robot_error_reports の D-S_CONTROL_FAILURE(D-180)」に。§3 の接続先の注記の P-8 のシリアル '0' も実値 --- の注記をdocs/decisions.md の D-091 項3 の「確認が必要なこと(バナーをどうするか)」は、★コードを残す★ことでユーザー確定(§11 Q3)。廃止・転用するかは未決のままdocs/code-map.md / docs/spec.md(/ds-alerts の取得元・編集モード)'0')は単体テストの行なので変えていない。fixtures と資料だけ --- にした/alerts(事前チェック+出発失敗。prechecks() / precheckItemById() / PATCH /prechecks/{id}/status)は、削除済みの施設・清掃エリアの行を出しているか確かめてほしい★(本書の範囲外。U8 の「エラー一覧からもD-sec一覧からも」に /alerts は入っていない)。出しているなら、同じ規則(isVisibleErrorRow())に揃えるかをユーザーに確かめるCLAUDE.md に U8(削除済みの施設・清掃エリアの行はエラー一覧・D-Sec に出さない。罠7 の解決は別)を足すかどうかdocs/decisions.md の末尾に追記する)### D-180 `[決定]` D-Sec アラートを `robot_error_reports`(`D-S_CONTROL_FAILURE`)から読み、D-Sec 画面に編集モードを付ける
★根拠★: TeamWorks 第11回 P-10 = A(`robot_error_reports` を正とする。D-Sec のアラートも対応状況の更新もこちら。`control_error_histories` は読まない)/
P-8(制御盤のエラーは `robot_id` NULL・シリアルは制御盤の SN。施設は行の `facility_id`・`section_id`)/第10回 N-5(`error_type` の値)/
ユーザー判断 2026-10-19(下の1〜4・7・7b・11)。指示書AX。
1. ★`SECURITY_ON` は D-Sec アラートに出さない★(エラー一覧には出る)。D-091 項3「セキュリティが無効であっても異常ではない」と一致
2. ★`NOT_STARTED`(清掃開始失敗)も D-Sec アラートに出さない★(ロボット側の出発失敗)
3. ★施設は行の `facility_id`★(P-8 d = A)。ステージングの 1000004 はシリアルが施設2の制御盤なのに行は施設3 → ★施設3で出す★(TeamWorks に申し送り)
4. ★D-Sec アラート画面に編集モードを付ける★(エラー一覧と同じ操作。D-161 / D-165)。書くのは同じ行の4カラム。★書き込みは `PATCH /errors/{error_id}/status` の1本★(新しいルートを作らない。YAML の未実装の `PATCH /ds-alerts/{id}/status` は削除)
5. ★対象は `error_type` が `D_SEC_ALERT_ERROR_TYPES`(`lib/config/index.ts`。初期値 `["D-S_CONTROL_FAILURE"]`)に完全一致する行★。★SQL の `IN` は写し、正は JS の `===`★(照合順序が大小文字・末尾の空白を区別しないため)。★`robot_id IS NULL` やシリアルの形では見分けない★(実データのシリアルは回答の `0` ではなく `---`)。`status` は 0/1/2 の行だけ(3以上は出さず例外にもしない — ダッシュボードを落とさない)
6. ★`DsAlert` の契約★: `id` は string(`error_id`)/`resolution_status` はエラー一覧と同じ `unresolved` / `in_progress` / `resolved`(`DsAlertStatus` は削除。変換は `resolutionStatusFromRobotErrorStatus()` の1本)/`memo` → `note`/`status` パラメータは `open` / `all` / `resolved`(旧 `done` は422)/`summary` のキーも同じ/`detected_at` は `errored_at ?? created_at`/`section_name` は行の `section_id` の清掃エリア名(暫定のダミーではなくなった)
7. ★`alert_type` の `security_off` は互換のため enum に残すが、DB からは出ない★。★ダッシュボードのセキュリティバナーは DB のデータでは常に空★(★コードとテストは残す — ユーザー確定 2026-10-19★。廃止・転用は未決 — D-091 項3 の「確認が必要なこと」)
7b. ★D-Sec 画面の対応状況の文言はエラー一覧に揃える(未解消 / 対応中 / 解消)★(ユーザー確定 2026-10-19。モックの「未対応 / 対応済み」とは食い違う)。★種別のセレクト(全種別 / セキュリティ無効 / 通信断)と副題はそのまま残す★(ユーザー判断 2026-10-19。「セキュリティ無効」を選ぶと0件になることは承知のうえ)
8. `control_error_histories` はアプリから読まない(シードも書かない。`deleteMany` だけ残す)。`lib/db/ds-status.ts` の制御盤エラーの変換2関数は削除
9. fixtures: D-Sec アラートは `errorReportRows` の `D-S_CONTROL_FAILURE` 行(7行)から同じ純粋関数で作る(`lib/fixtures/ds-alerts.ts` は削除)。セキュリティ・清掃開始失敗のシリアルを実データどおり `---` に
10. (別件)清掃詳細ログの施設名の列を 126px → 110px(表の最小幅 1150 → 1134px)。画面幅 1440px で縦スクロールバーが出ると入れ物が 1139px になり、11px 横スクロールしていた。★D-178 の「最小幅 1150 を変えないため」は 1134 に読み替える★
11. ★削除済み(論理削除)の施設・清掃エリアの行は、エラー一覧(`GET /errors`・`meta.total`)からも D-Sec アラート(`GET /ds-alerts`・`summary`・ダッシュボード)からも外す。その行への `PATCH /errors/{error_id}/status` は404★(ユーザー決定 2026-10-19「削除済みのものは表示する必要がないので、エラー一覧からもD-sec一覧からも消してください」)。★`deleted_at` が NULL でなければ削除済み★。正は `isVisibleErrorRow()`、SQL の `ERROR_ROW_VISIBLE_WHERE` は写し。★施設名・分類の解決は従来どおり `deleted_at` で絞らない(罠7)★。★`/alerts` は範囲外(出しているかは要確認)★
★`memo` → `note`・`done` → `resolved`(旧 `done` は422)・期待値表(指示書AX §3)・シードの手当ては監督判断 2026-10-19★。
★ユーザー回答待ちの暫定は無い★(指示書AX §11 Q1〜Q4 はすべて確定)。
(★実装の記録(コミット・確認の結果)は、実装後に D-180 の末尾に「実装の記録」として足す★)
段階ごとに分ける(段階1 / 段階2 / 段階3 / 段階4 / 段階5 / 段階6 / ★段階7(別件)★ / 段階8)。★git add はファイルパスを明示する★(git add -A 禁止)。 メッセージには ★何を変えたか★ と ★なぜ(P-10 = A。D-Sec アラートを robot_error_reports から読む。D-180)★ を書く。★段階1 は「テストだけ(この時点では落ちる)」と明記★。 ★docs/decisions.md は D-180 の塊だけをコミットする(D-179 の未コミットの差分を巻き込まない)★。 ★PR の作成とマージは daichi がレビューしてから★(D-154)。
meta.total を含む)にも D-Sec アラート(summary を含む)にも出ない。PATCH は404で updateMany を呼ばない★(fixtures の FX-ERR-18・19 で画面と API の両方を確かめた)/alerts の読み取りは変えていないtsc 0件・lint 0件・npm test 全件通過(件数が増えている)・USE_FIXTURES=true npm run build 成功$queryRaw は既存のコメントだけ)control_error_histories を読まない。robot_error_reports の読み取りは定数の select だけ(robot_id / robot を含まない)PATCH /errors/{error_id}/status の1本(新しいルート・DataSource の書き込みメソッドが無い)。YAML に /ds-alerts/{id}/status が無いtableWrap.scrollWidth === clientWidthdocs/fixtures.md・YAML・型のコメントを直し、docs/decisions.md に D-180 を追記した(D-179・CLAUDE.md には触っていない)CLAUDE.md §6-6)/本書と違う判断をしたところ