← 実証デモ集トップ

実証 #03

障害ログを渡したら、93秒でファイル・行番号・修正案まで返ってきた——実物

診断所要実測93秒
対象規模app.js 3,569行+HTML/CSS
方式ブラインドテスト
人の稼働0分
診断役AIへの指示文(要約・原文の趣旨ママ)ユーザーから『保存表示が動かない』という報告と、アプリ一式・エラーログを渡された。原因を特定し、最小の修正案を提示して(アプリは読み取り専用・レポートのみ出力)
公平性の担保: 実証用に、自作の公開アプリ「support」のコピーへ意図的にtypoを1箇所仕込みました。仕込み役と診断役は別のAIセッションで完全分離——診断役が受け取ったのは下の「ユーザー報告+エラーログ」とアプリ一式だけで、仕込み内容は知りません。診断結果は原文ママです(ファイル・行番号・原因・修正diffまで、仕込みと完全一致の正解でした)。
診断役AIに渡した全情報(これがすべて):
【報告】supportアプリの調子がおかしいです。 「別の一言」ボタンを押したり、日記を入力したりすると、右上の保存表示(「保存中」→「保存済み」)が出なくなりました。データが保存されているのか不安です。 ブラウザのコンソールに以下のエラーが出ています。 ---- console ---- Uncaught TypeError: Cannot set properties of null (setting 'textContent') at queueSave (http://localhost:4180/app.js:732:29) at HTMLButtonElement.<anonymous> (http://localhost:4180/app.js:456:5) ----------------- 環境: Mac / Chrome / localhost:4180 で起動(app_broken フォルダ一式)

AIの出力(原文ママ・93秒後)


1. 根本原因

app.js 378行目の querySelector のセレクタ文字列が、HTML側の実際の id と1語ずれている。

場所記述判定
app.js 378行目saveState: document.querySelector("#saveStatus"),誤り(唯一の不一致)
index.html 63行目<span class="save-state" id="saveState">保存済み</span>実際の id は saveState
styles.css 299行目.save-state { ... }class基準なので表示崩れなし

なぜこのエラーになるか

  1. HTML に id="saveStatus" の要素は存在しない(フォルダ全体を検索して saveStatus の出現は app.js:378 の1箇所のみ)。
  2. そのため document.querySelector("#saveStatus")null を返し、els.saveState が null になる。
  3. 保存表示を更新する queueSave() の先頭(732行目29桁)で els.saveState.textContent = "保存中" を実行した瞬間、null にプロパティを設定しようとして TypeError: Cannot set properties of null (setting 'textContent') が発生する。
  4. スタックの HTMLButtonElement.<anonymous> (app.js:456:5) は「別の一言」ボタンのクリックハンドラ(450〜457行目)からの呼び出しで、ログの再現手順と一致する。

このコードベースは「els のプロパティ名 = HTML の id」という命名規約で統一されており(例: progressRate#progressRatesyncState#syncState)、#saveStatus だけが規約から外れた typo。


2. 影響範囲

(a) queueSave() 経由 — 全30箇所が失敗。データが保存されない(実害あり)

queueSave() は1行目(732行目)で即例外になるため、その先の setTimeout が予約されず saveState()(localStorage保存)が一度も実行されない。さらに進捗・週チャート・週次ふりかえり・カレンダーの再描画、クラウド同期(queueCloudSync)・Google同期(queueGoogleSync)もすべてスキップされる。

該当する操作(いずれも変更はメモリ上だけで、リロードすると消える):

→ 報告者の「データが保存されているのか不安」は的中。上記の操作は保存されていない。

(b) saveState() を直接呼ぶ9箇所 — 保存はされるが直後にクラッシュ

saveState() 内は 718行目 localStorage.setItem(...) が先に実行され、719行目 els.saveState.textContent で例外になる。よって起動時の正規化処理や CNP 関連(2444・2455・2471・2480行目など)は localStorage への保存自体は成功するが、呼び出し元の後続処理(render() / renderCnp() など)が中断される。


3. 最小修正案(1行・1語の修正)

app.js 378行目のセレクタを HTML の実 id に合わせる。app_broken 内のファイルは変更していません。以下は適用すべき差分の提示のみです。

--- a/app.js
+++ b/app.js
@@ -378 +378 @@
-  saveState: document.querySelector("#saveStatus"),
+  saveState: document.querySelector("#saveState"),

4. 再発防止の一言

els を組み立てた直後に null チェックを1つ入れると、同種の typo が「操作時のクラッシュ」ではなく「起動時のコンソール警告」で即座に見つかります。
例: Object.entries(els).forEach(([k, v]) => { if (!v) console.warn("要素が見つかりません:", k); });


5. 調査の所要時間

項目時刻
開始2026-07-24 12:15:09
終了2026-07-24 12:16:42
実測約1.6分(93秒)