Newer
Older
client_dwatch / docs / handoff / 指示書G_D-Secログの備考入力と2状態化.md

指示書G — D-Secログ「備考」の入力欄と、通信状態の2状態化

実行状態

項目 値
状態 ★完了(2026-09-08)★。段階0〜7 すべて実施。ブラウザでの目視確認も実施済み
★実行順★ ★docs/handoff/指示書J_JSONストアの共通化と安全化.md を先に実行すること★(2026-09-07 追加)。J が作る lib/settings/json-store.ts を段階2-1 で使います。J を飛ばすと、この指示書は advice.ts の3つの欠陥をコピーします → 完了済み(2026-09-08)
作成 2026-09-07(作成時点の決定事項最終番号 D-067)
実行環境 Cursor の中で動く Claude が実装する。★この指示書を渡されたあなたが実装者です。実装してください★ → 実際にはユーザーの直接指示によりClaude Code(Sonnet 5。監督役+opusサブエージェント委譲)が実行した(2026-09-08)
実行日 2026-09-08
実行コミット b275392(段階0)/ 5f23723(段階1)/ 22a58b3(段階2)/ 9efba0c(段階6)/

段階3・4は並行していた別セッションのコミット b564574 に巻き込まれた(内容は正常。詳細はD-076参照)/ 52f93e1(段階5) | | 取得した決定番号 | D-076(D-075 まで使用済みであることを着手直前に確認済み) | | ★前提★ | ★指示書J は完了済み★(2026-09-08。lib/settings/json-store.ts が存在します)。第5回の回答待ちには依存しません。今すぐ着手できます → 満たされていたため着手した |

実行後はこの表を更新してください。

★この指示書を読んでいるあなたへ★ .claude/commands/d-watch.md に「実装は Cursor 側、Claude Code は指示書作成まで」とありますが、 これは2つの環境の役割分担です。Cursor の中で動いている Claude は実装担当です。 この指示書を渡されたなら、調査や改訂ではなく実装を行ってください。


この指示書の位置づけ

TeamWorks 第2回回答(D-065)で、F-06 D-Secログに2つの変更が必要になりました。

# 変更 性質
A 通信状態を 3状態(正常 / 通信断 / 異常)→ 2状態(正常 / 異常) に 機械的。型変更が起点で、コンパイルエラーが漏れを教えてくれる
B 「備考」を表示専用から、運用担当が入力できる欄に 設計判断が多い。独自ストア・保存API・入力UI が要る

D-065 は B を「独立した作業として扱うこと」としています。 本指示書では A と B を段階で分け、別コミットにしますが、1つの指示書にまとめました。 理由は、両者が同じ4ファイルを触るためです。

components/features/ds-logs/DsLogScreen.tsx
lib/types/index.ts
lib/fixtures/ds-devices.ts
d-watch-api.yaml

別々に走らせると確実に衝突します。段階0(A)を先に完了させてから、段階1以降(B)に進んでください。

★指示書F(F-04 レポート)とは触るファイルが重なりません。並行して進められます。★ ただし docs/decisions.md の決定番号は取り合うので、番号を取る直前に最終番号を確認すること。


★先に決まっていること(読み飛ばさないこと)★

次の10点は判断済みです。実装時に蒸し返さないでください。

1. 保存先は JSON ファイル(★2026-09-07 決定★)

data/settings/ds-device-notes.json に Record<serial_number, string> の形で保存します。

  • 却下した案: SQLite(点検記録と同じストア)。同時書き込みに強く件数にも耐えますが、 Prisma のデータソースをもう1つ持つ必要があり、マイグレーション・バックアップの対象が増えます
  • JSON を選んだ根拠(実データ): 制御盤は 20施設弱(TeamWorks 回答)= 20件程度。 1件あたり短いテキスト1本。運用は「翌朝 DFA 担当が確認する」体制で同時編集はまず起きません
  • 前例をそのまま使えます: lib/settings/advice.ts が path.join(process.cwd(), "data", "settings", "advice.json") の形で実装済み
  • .gitignore に /data があるのでコミットされません(確認済み)。 裏返すとgit のバックアップ対象外なので、その旨をファイル冒頭コメントに書くこと
  • 将来 SQLite に移すときは、読み書きを lib/settings/ds-device-notes.ts に閉じ込めてあるので そのファイルだけ差し替えれば済みます。呼び出し側がファイル形式を知らない形にすること

2. メインDBには絶対に書かない

CLAUDE.md §7 の禁止事項。更新可は robot_error_reports / control_error_histories の 4カラムのみ(D-042)。備考はそこに含まれません。 lib/settings/advice.ts の冒頭コメントと同じ趣旨の注記を、新しいファイルにも書くこと。

3. control_error_histories.comment は使わない

あれはアラート1件ごとに運用担当が対応時に書き込む欄で、デバイス単位の備考とは粒度が違います。 第3回の共有文書でも TeamWorks へその認識を伝えています。

4. キーは serial_number

DsDevice に id はありません(lib/types/index.ts:190-197)。 serial_number は既に一意キーとして使われています。

  • 画面の行キー: DsLogScreen.tsx:156 の <tr key={item.serial_number}>
  • ソートの安定化キー: lib/ds-devices/query.ts:106-110(D-055)
  • DsAlert.device_serial からの参照もシリアル文字列

5. API は PATCH /ds-devices/{serial_number}/note

  • パス方式にします。前例: /clean-logs/{id}/advice、/clean-logs/{id}/areas
  • 却下した案: /settings/ds-device-notes にボディで serial を入れる(advice と同じ方式)。 advice は「設定」ですが備考はデバイスという資源に属する値なので、パスに出すほうが自然です
  • メソッドは PATCH。リポジトリで唯一の書き込み前例(app/api/settings/advice/route.ts:50)に合わせます

6. ★実在するデバイスかを必ず検証する★

db.dsDevices() に存在しない serial_number は 404 を返します。

これは2つの問題を同時に潰すためです。

  1. 存在しないデバイスの備考がストアに溜まる(孤児レコード)
  2. ★プロトタイプ汚染★ — serial_number は任意文字列なので、 __proto__ や constructor が JSON のキーとして飛んできます。 実在チェックで弾けば、そもそも到達しません

あわせて、JSON を読み込んだオブジェクトは Object.create(null) を土台にするか、 キーの取り出しに Object.prototype.hasOwnProperty.call() を使うこと。 advice は「閉じた union のキー」だったのでこの問題がありませんでした。前例が使えない箇所です。

7. 空文字は削除、レスポンスは null

  • 空文字(またはトリム後に空)が来たらキーごと削除します
  • API が返す note は string | null。"" を返さないこと
  • 根拠: lib/fixtures/ds-devices.ts のファイル冒頭コメントに 「備考あり / なし(空文字ではなく null で持つ)」と明記されています

8. 文字数上限は 200 文字

短いメモの想定です。超えたら 422 validation_failed、 details に { note: ["200文字以内で入力してください"] } を載せること。 上限値は lib/config/ に定数として置く(ハードコード禁止。CLAUDE.md §7)。

9. 書き込みはアトミックにする(★advice の作りを真似しないこと★)

lib/settings/advice.ts には次の弱点があります。備考では直してください。

advice の作り 備考ではどうするか
fs.writeFile を直接呼ぶ(非アトミック)。書き込み中にクラッシュするとファイルが壊れる 一時ファイルに書いてから fs.rename でアトミックに差し替える
JSON が壊れていると catch で {} を返し、次の書き込みで丸ごと上書き消滅。ログも出ない .bak に退避してから {} で継続し、logEvent("warn", ...) を出す。 備考は運用担当が書いた文章なので、黙って消してはいけません
テストが1本もない(lib/settings/__tests__/ が存在しない) テストを書く(下記 段階2)

同時書き込みのロックは入れません。 20件・実質1人の運用で、rename によるアトミック差し替えで 「壊れたファイルが残る」ことは防げるためです。理由をコメントに残すこと。

10. 備考のマージは API ルート側で行う

lib/db/index.ts:44-46 が 「フィルタや集計はこの層では行わず、APIルート側で組み立てます」と定めています。 db.dsDevices() は fixtures / Prisma の値をそのまま返し、 app/api/ds-devices/route.ts で備考ストアの値を上書きしてください。


着手前の確認

git status                 # クリーンであること
npm test                   # 全パス(★267は古い基準値。着手直前に実行して実測値を基準にすること★)
npx tsc --noEmit           # エラー0

★基準値267・決定番号D-067はどちらも作成時点(2026-09-07)のものです。古くなっています★ (指示書Jの段階3完了時点で実測 21ファイル/447テスト。指示書Jの段階4でさらに決定番号が 1件以上進みます。指示書G自身の実行時にはこれよりさらに進んでいる可能性があります)。 npm test と grep -n "^### D-0" docs/decisions.md を着手直前に必ず実行し、その実測値を基準にすること (grep で確認するのはファイル末尾ではなく決定番号そのもの。D-070 のように追記位置が 時系列順でないことがあるため)。


段階0. 通信状態を2状態にする(★先にこれを完了させてコミット★)

D-065 の決定です。controls.status は 0(正常)/ 1(異常)の2値で、 かつ「異常=生死報告が来ていない」なので、通信断と異常は同じものでした。

型を先に変えてください。 CONNECTION_STATUS_ORDER が Record<DsDevice["connection_status"], number> なので、型を変えると必ずコンパイルエラーになり、 直す場所を機械が教えてくれます。

0-1. d-watch-api.yaml(★D-040。YAML が先★)

行 現状 変更
:596 並び順は 異常→通信断→正常 異常→正常
:609 description: error=異常・通信断 / ok=正常 error=異常 / ok=正常
:1125-1127 enum: [ok, disconnected, error] / description: 正常 / 通信断 / 異常 enum: [ok, error] / 正常 / 異常
:1140 error: { description: 異常+通信断の合計 } 異常の件数

status クエリパラメータの enum([all, error, ok])は変更不要です。元々 disconnected を含みません。

0-2. lib/types/index.ts

  • :186 DsConnectionStatus = "ok" | "disconnected" | "error" → "ok" | "error"
  • :189 のコメント「error = "error" + "disconnected"」を修正
  • DsDeviceStatusFilter(:189)の値は変更不要

★lib/types/index.ts:200 の DsAlertType = "security_off" | "disconnected" は別物です。触らないこと。★ F-07 D-Secアラートの種別で、DsConnectionStatus とは無関係です。 "disconnected" を一括置換すると壊れます。

0-3. lib/ds-devices/query.ts

行 変更
:70 コメント「error = "error" または "disconnected"」を修正
:75-88 filterDsDevicesByStatus の case "error" を === "error" だけに
:90-94 CONNECTION_STATUS_ORDER を { error: 0, ok: 1 } に
:96-99 ソートのコメント「異常→通信断→正常の順(D-055)」を修正
:129-137 summarizeDsDevices の error の数え方を === "error" だけに

0-4. components/features/ds-logs/DsLogScreen.tsx

行 変更
:23-27 connectionStatusLabel() から disconnected → "通信断" の分岐を削除
:29-33 connectionStatusColor() から disconnected → c.wn(黄)の分岐を削除
:126 フィルタボタンのラベル {t("異常・通信断")} → {t("異常")}

lib/i18n.ts の対訳も追随させること(「異常・通信断」を消し、「異常」があるか確認)。

0-5. lib/fixtures/ds-devices.ts

  • :46(DS-005)と :96(DS-010)の connection_status: "disconnected" を "error" に
  • ファイル冒頭コメント(:4-15)の「3状態すべて(正常 / 通信断 / 異常)」「異常 → 通信断 → 正常」を修正

0-6. lib/ds-devices/__tests__/query.test.ts

disconnected を使っているケースを2状態に直します。

行 内容
:119-121 フィルタのテスト用3件(ok / error / disconnected)
:144-148 ソートの期待値配列
:157-159 / :172-175 サマリのテスト
:197-199 summary 独立性テストの3件

★:187-193 に重要なコメントがあります。 「DsConnectionStatus は閉じた union で、4つ目の値を持つ DsDevice は any キャスト無しでは作れない。 状態が増える時は、型を先に拡張してからこのテストにケースを足すこと」。 今回は逆に減らすので、このコメントも2状態に合わせて更新すること。

段階0の完了条件

  • npx tsc --noEmit エラー0
  • npm test 全パス(テスト数は減らないこと。ケースの中身は変わるが件数は維持)
  • ここで一度コミットする(D-050)

段階1. YAML に備考の保存エンドポイントを足す(★D-040★)

1-1. DsDevice.note の description を書く

現状は note: { type: string, nullable: true, example: "セキュリティ復帰失敗" } で説明がありません。 次の趣旨を書くこと。

運用担当がビューアーから入力するメモ。DB由来ではなく DFA 側の独自ストアに保存される。 検知側が生成する値ではない。

example も差し替えること。 "セキュリティ復帰失敗" は検知側の文言に見えるため誤解を招きます (D-065 でこれが誤りだと判明した経緯があります)。"定期点検実施済み" のような運用メモらしい例に。

1-2. PATCH /ds-devices/{serial_number}/note を追加

  /ds-devices/{serial_number}/note:
    patch:
      tags: [ds-devices]
      summary: D-S制御盤の備考を保存する
      description: |
        運用担当が入力するメモ。DFA側の独自ストアに保存し、メインDBには書き込まない。
        空文字を送ると備考を削除する(レスポンスの note は null になる)。
      parameters:
        - name: serial_number
          in: path
          required: true
          schema: { type: string }
      requestBody: ...  # { note: string }
      responses:
        "200": ...      # { serial_number, note: string | null }
        "404": ...      # 存在しない serial_number
        "422": ...      # $ref: "#/components/responses/ValidationError"
        "500": ...
  • 200 のレスポンスは保存後の値をエコーバックする(advice の PATCH と同じ形)
  • 404 を必ず定義すること(実在チェックで返します)
  • 422 は既存の components/responses/ValidationError(:920-924)を $ref する

段階2. 独自ストア lib/settings/ds-device-notes.ts を作る

2-1. 実装

★lib/settings/json-store.ts(指示書J で作成)を使ってください。★ アトミック書き込み・.bak 退避・プロトタイプ汚染対策は共通部品側で済んでいます。 このファイルは createJsonStore を呼んで薄く包むだけにしてください。 ★advice.ts を写して自前で fs.writeFile を書かないこと★(advice.ts も指示書J で同じ部品に載せ替え済みです)。

/** 保存先。lib/settings/advice.ts と同じ決め方(process.cwd() 起点) */
const NOTES_PATH = path.join(process.cwd(), "data", "settings", "ds-device-notes.json");

/** serial_number → 備考。空文字は保存しない(キーごと削除する) */
type DeviceNotes = Record<string, string>;

export async function readDeviceNotes(): Promise<DeviceNotes>;
/** note が空文字なら削除。戻り値は保存後の値(削除なら null) */
export async function saveDeviceNote(serial: string, note: string): Promise<string | null>;

ファイル冒頭のコメントに必ず書くこと:

  1. なぜメインDBに書かないか(CLAUDE.md §7 / D-042。advice.ts の書き方に倣う)
  2. .gitignore の /data によりコミットされない = git のバックアップ対象外であること
  3. 将来 SQLite へ移すときはこのファイルだけ差し替えれば済む設計であること

実装の要点:

  • 読み込み: ファイル不在は {}(正常系)。JSON が壊れていたら .bak に退避してから {}、 かつ logEvent("warn", ...) を出す。★黙って捨てないこと★
  • 書き込み: 一時ファイル(ds-device-notes.json.tmp 等)に書いてから fs.rename。 fs.mkdir(recursive: true) は advice と同じ
  • プロトタイプ汚染対策: 読み込んだ JSON をそのまま使わず、 Object.create(null) に hasOwnProperty で確認しながら移し替える
  • 同時書き込みのロックは入れない。理由をコメントに書く(★先に決まっていること 9★)

2-2. テスト lib/settings/__tests__/ds-device-notes.test.ts(★新規★)

lib/settings/ には現在テストが1本もありません。 ここで作ります。 一時ディレクトリを使い、実際のファイル読み書きを検証すること。

必ず含めるケース:

  • ファイル不在 → {} が返る(例外にならない)
  • 保存 → 読み込みで同じ値が返る
  • 空文字で保存 → キーが消え、戻り値が null
  • 前後の空白のみの文字列も削除扱いになる
  • 既存の別デバイスの備考が消えないこと(部分更新)
  • JSON が壊れている → .bak が作られ、{} で継続する
  • __proto__ をキーにしても Object.prototype が汚染されない

段階3. API ルート app/api/ds-devices/[serial_number]/note/route.ts

app/api/settings/advice/route.ts の PATCH(:50-110)が唯一の前例です。 骨格を踏襲すること。

const ROUTE = "/api/ds-devices/[serial_number]/note";

export async function PATCH(request: Request, { params }: { params: ... }) {
  return withRequestId(request, async () => {
    // 1) serial_number をパスから取得
    // 2) body = await request.json() を try/catch → 失敗は 422「JSONの形式が不正です」
    // 3) typeof body.note === "string" を検査 → 違えば 422
    // 4) 文字数上限(lib/config の定数)を超えたら 422 + details
    // 5) ★db.dsDevices() に存在するか確認 → 無ければ 404★
    // 6) try { saveDeviceNote(...) } catch → 500(errorPayload を logApiError と両方に渡す)
    // 7) 200 で { serial_number, note } を返す
  });
}

★守ること★

  • 500 の errorPayload は1つのオブジェクトを作り、logApiError と NextResponse.json の 両方に同じものを渡す(lib/logger/api.ts:4-6 の設計意図。ログとレスポンスがズレるのを防ぐ)
  • withRequestId で全体を包む(lib/logger から import)
  • バリデーションは typeof による手動チェック(zod は未導入。前例に合わせる)
  • 404 のメッセージは「デバイスが見つかりません」。code は not_found (lib/types の ErrorCode に既にあるか確認し、無ければ追加してから YAML と合わせる)

Next.js のルーティング: 動的セグメントは [serial_number] です。 app/api/clean-logs/[id]/advice/route.ts が同じ構造なので、params の受け取り方はそれを見ること (このバージョンの Next.js では params が Promise の可能性があります。必ず既存ルートに合わせる)。


段階4. 一覧に備考をマージする

app/api/ds-devices/route.ts を変更します。lib/db/index.ts は触りません(★先に決まっていること 10★)。

rows = await db.dsDevices()          ← try の中はこれだけ(既存の形を維持)
notes = await readDeviceNotes()      ← 同じ try の中で取得してよい
rows = rows.map(d => ({ ...d, note: notes[d.serial_number] ?? d.note }))

マージの規則: 独自ストアに値があればそちらを優先し、無ければ元の値(fixtures の note)。 理由をコメントに残すこと。

フィルタ・ソート・サマリより前にマージすること(将来 q の部分一致に備考を含める可能性があるため。 ただし今回は備考を検索対象にしません。q は施設名とシリアルのみ = 現状維持)。


段階5. 画面に入力欄を作る

components/features/ds-logs/DsLogScreen.tsx と DsLogScreen.module.css。

5-1. 現状

  • :151 に <th>{t("備考")}</th>、:166 に <td className={styles.tdMuted}>{item.note ?? "-"}</td>
  • 表示専用。画面全体で <input> は検索ボックス(:107)の1つだけ
  • .tdMuted(CSS :119-123)は font-size: 12px; color: var(--tx-m);

5-2. 入力の形

セル内インライン編集にします。

  • 通常は今までどおりテキスト表示(未入力は -)
  • クリックすると <textarea> に変わる
  • 確定はフォーカスを外したとき(blur)。Enter は改行として使えるようにする
  • 保存中は入力を無効化し、失敗したらセルの下に赤字でメッセージを出す (ApiFetchError.serverMessage を使う。lib/api/client.ts)

却下した案: モーダル(20件の一覧で1つずつ開くのは手数が多い)/保存ボタンを常時表示 (列が広がり、他の列が潰れる)。

5-3. 通信

★fetch を直書きしないこと。必ず apiFetch を通す★(lib/api/client.ts の冒頭コメント)。 第2引数に RequestInit を渡せます(lib/api/__tests__/client.test.ts:52 で透過がテスト済み)。

await apiFetch<{ serial_number: string; note: string | null }>(
  `/api/ds-devices/${encodeURIComponent(serial)}/note`,
  { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ note }) }
);

encodeURIComponent を必ず使うこと。

5-4. 保存後の反映

★全件再取得(fetchDevices())はしないこと。★ fetchDevices は setLoading(true) を伴う(:67-86)ため、表がローディング表示に切り替わり、 編集していた位置を見失います。

保存レスポンスの値で setItems を部分更新してください。

setItems(prev => prev.map(d => d.serial_number === serial ? { ...d, note: saved.note } : d));

5-5. CSS

DsLogScreen.module.css に入力欄用のクラスを新規追加します。 既存の .searchInput(:36-44)が入力系スタイルの参考になります。

色は必ず CSS 変数を使うこと(--tx / --tx-m / --bd / --sf-h / --ng)。 このファイルは全て CSS 変数で書かれています。THEME_TOKENS を混ぜないこと。

5-6. i18n

lib/i18n.ts に対訳を追加すること(「備考を入力」「保存に失敗しました」など、新しく出す文言)。


段階6. fixtures の note を運用メモらしい文言に直す

D-065 で「モックの値は検知側の情報ではなく運用担当のメモ」と判明しました。 ところが lib/fixtures/ds-devices.ts には検知側の文言に見えるものが残っています。

行 現状 問題
:32(DS-001)/ :114(DS-012) "セキュリティ復帰失敗" 検知側が出す文言に見える
:48(DS-005) "6時間以上通信なし" 同上。しかも「6時間」は根拠のない仮の値(第2回で TeamWorks へその旨を伝えている)
:90(DS-009) "通信不安定" 同上
:98(DS-010) "長時間通信なし" 同上
:64 / :106 "定期点検実施済み" ★これだけが運用メモらしい★

運用担当が書きそうな文言に差し替えてください(例:「現地確認済み。電源要交換」「9/5 に再起動対応」など)。 ★実在の店舗名・住所・電話番号を書かないこと★(CLAUDE.md §7)。

null の件数(6件)と非 null の件数(6件)の比率は維持すること。 ファイル冒頭コメントの「備考あり / なし(空文字ではなく null で持つ)」はそのまま残します。


docs/decisions.md への記録

★着手直前に最終番号を確認してから番号を振ること★(2026-09-07 時点の最終は D-067。 指示書F と取り合うので必ず再確認)。

記録すべきもの:

内容 段階
D-Secログの通信状態を2状態にした(controls.status が 0/1 の2値で、通信断と異常は同じもの) 段階0
備考の保存先は JSON(data/settings/ds-device-notes.json)。SQLite を却下した理由と、将来移す場合の道筋 先に決まっていること 1
API は PATCH /ds-devices/{serial_number}/note。実在チェックで 404 を返す(孤児レコードとプロトタイプ汚染を同時に防ぐ) 先に決まっていること 5・6
空文字は削除し null を返す 先に決まっていること 7
備考の文字数上限(200文字) 先に決まっていること 8
書き込みは一時ファイル + rename。JSON 破損時は .bak へ退避して warn(advice の黙殺は真似しない) 先に決まっていること 9

★D-065 に「保存先の具体(SQLite か JSON か)は未決」と書いてあります。決まったので その記述を更新すること。★


★やらないこと★

対象 理由
lib/types/index.ts:200 の DsAlertType の "disconnected" ★F-07 の種別で別物★。"disconnected" の一括置換は禁止
メインDBへの書き込み CLAUDE.md §7 / D-042
control_error_histories.comment の利用 粒度が違う(★先に決まっていること 3★)
F-07 D-Secアラートの「種別」列・「詳細」列の削除 D-065 の別項目。この指示書の範囲外。別途
備考を検索(q)の対象に含めること 現状は施設名とシリアルのみ。変更しない
同時書き込みのロック機構 ★先に決まっていること 9★
lib/settings/advice.ts のリファクタ ★2026-09-07 撤回。指示書J で実施します★。理由: この指示書が advice.ts を「ひな型にする」と指示しているため、直さないと同じ欠陥が2つ目のストアにコピーされる。また advice.ts はパスをモジュール先頭で固定していてテストが書けず、段階2-2 の「一時ディレクトリで実際のファイル読み書きを検証」を満たすには部品化が必要
lib/db/index.ts の変更 マージは API ルート側(★先に決まっていること 10★)
SQLite への移行 JSON で確定

段階7 — ★origin/develop へ push する★

段階0〜6 がすべて緑でコミット済みになってから行ってください。

git push origin develop
  • ★main へは push しないこと★。CLAUDE.md §6-5 のとおり main へのマージは PR 経由で、もう1人のレビューを通してから行います
  • --force は使わないこと
  • ★403 が出たら、無理に回避せずユーザーへ報告してください★ (認証情報を画面に出すコマンドを実行しないこと。git credential fill は禁止)

push 後に確認すること:

git log --oneline -1 origin/develop   # ローカルの HEAD と同じコミットか
git status -sb                        # 1行目に ahead/behind が無いか

★この2つを段階6の記録に実測値として書き足すこと。★


完了条件

  1. 段階0〜6 をすべて実施
  2. npx tsc --noEmit がエラー0
  3. npm test のテスト数が減っていないこと。 ★基準値は 2026-09-08 実測で「21ファイル / 447テスト」★(指示書H・指示書J 完了後)。 文中に残っている 267 / 389 はどちらも古い値です。 ★着手直前に npm test を実行して実測値を基準にすること★。 段階2 の lib/settings/__tests__/ds-device-notes.test.ts の分が増えるのが期待値
  4. USE_FIXTURES=true で実際に画面から備考を入力・保存し、リロード後も残ることを確認する (npm run dev → /ds-logs)。data/settings/ds-device-notes.json が生成されることも確認
  5. 空文字で保存して備考が消えることも実際に確認する
  6. 上の表の決定事項を docs/decisions.md に記録する。 ★D-065 の「保存先は未決」は指示書J(D-075)で解消済みです★。二重に直さないこと
  7. 段階0 と段階1以降は別コミットにする(段階0 は独立して意味を持つため)。 検証が通ったら即コミット(D-050)
  8. 実行後、この指示書の冒頭「実行状態」表を更新する
  9. ★origin/develop へ push 済みで、git status -sb に ahead/behind が無いこと★(段階7)