⚙️ Vault運用

2026-08-04_作業ログ

最終更新 2026年08月04日 / 90_Meta/Claude作業ログ/2026-08-04_作業ログ.md

作業ログ 2026-08-04

13:20 - SCALE CRM v11.6.50: 架電リストの操作品質根治(大串FB・画面収録から・本番LIVE・git 17f7c37)

大串FB: 「架電リストを実際に動かした中で修正して欲しいのが何個もある」「画面が一瞬切り替わったり他の行にいったりカクってしたり。こういうところが品質悪いって感じる所以」+32秒の画面収録

動画の機械解析(推測せず実測した)

  • ffmpeg のシーン検出で画面が大きく変わった瞬間を抽出 → 8.96s / 17.29s / 26.84s にモーダル暗転、10.05s / 18.85s に復帰
  • 前後フレームを切り出して比較した結果:
時刻 状態
10.1s 行が全消え・列見出しも崩壊(従業員規模/架電担当者が消える)
10.6s 復帰(約0.5秒の空白)
19.0〜19.6s 2回目。列見出しごと完全にまっさらが数秒継続
27.0s 3回目も同様

真因(3つとも v11.6.47「常時サーバー駆動」で編集後の経路が旧方式のまま残っていたこと)

  1. 編集後 renderCallList()#mainContent を丸ごと innerHTML で作り直す → 直後の filterCL/calls/query の応答が返るまで tbody が空
  2. renderCallList 末尾の if(calls.length)filterCL() により、_cache が空だとサーバークエリ自体が走らない → 一度空になると数秒戻らない(2回目の症状の主因)
  3. filterCL のサーバー分岐が _clServerQuery(0) 固定 → 編集のたび先頭ジャンプ。v10.78 の作り込んだスクロール復元は「再描画直後に行が存在する」前提なので、行ゼロでは無効化されていた

修正(A/B/C・大串GO)

  • A: _clPatchRowCells(callId) 新設 — 取り消し後は該当行のセル+上部KPIだけ更新(全体再描画ゼロ)
  • B: 応答が返るまで前回結果を即描画(空白ゼロ)/_clKeepOffsetOnce で編集後は表示位置維持/サーバー駆動時は _cache が空でも必ずクエリ実行/「データなし」の誤表示を抑止
  • C: アポ取り消し時、他スロットにアポ系が残っていなければ apoDate/apoGetter もクリア

アポ取り消しの調査結果(大串の懸念「取り消してもアポとして反映されてるかも」)

  • 集計は正しい。全経路(ダッシュ/日報/レポート/メンバー成績/タイマー)が _clApoCountDate(callNStatus 基準)を通っており、取り消せばアポ数・アポ率・API から外れる。動画のKPIでもアポ獲得は1のまま増えていない
  • D1 の残骸(ステータス空 + apoDate 残り)も 0件
  • ただし apoDate/apoGetter がフィールドとして残る穴は実在 → C で修正

D(ステータス選択時の全画面モーダル)= 現状維持で決定(2026-08-04 大串判断)

右サイドパネル化・行内完結の2案を提示したうえで「現状維持でいい」。記載漏れ防止の仕様(閉じられない・毎回まっさら)はこのまま。今後この件を蒸し返さない。

  • 出るのは ST_REQUIRED_FIELDS に項目があるステータスのみ。担当不在(2項目)/失注(3)/育成(5)/資料請求・メール日程調整(6)/アポ獲得(7)。コールのみ・不通・受付ブロック・架電NG・NGリスト・重複はモーダルなし
  • 「閉じられない」「毎回まっさら」は過去の大串FB(v9.184 記載漏れ・誤送信防止)で意図的にそうなっている
  • 提示した選択肢: ①右サイドパネル化(リストが見えたまま=暗転ゼロ)②項目が少ないステータスは行内で完結 ③現状維持

学び

  • 動画FBは推測で読まずフレームを機械抽出して実測する。ffmpeg のシーン検出で「変化の瞬間」を出し、前後を並べると真因の当たりが一発でつく(体感の言語化に頼らない)
  • 新方式に移行したら「旧方式の前提で書かれたガード」を全部洗うif(calls.length) のような一見無害な条件が、新方式では「永久に空」を生む。v11.6.49(案件切替)と全く同じ構図が編集経路にも残っていた
  • スクロール復元のような作り込みは「行が存在する」前提。データ供給が非同期になると静かに無効化される

13:40 - v11.6.51 / v11.6.52(大串FB・本番LIVE・git feaa5fd)

v11.6.51 使い勝手3点

  1. 担当者の自動変更トーストを全廃(「架電担当者を『◯◯』→『◯◯』に自動変更しました」)。大串「どんなときも表示させないでOK」。自動変更の挙動と監査ログは維持(core.js 2箇所・calllist_status.js 1箇所)
  2. time/date のピッカーアイコンが黒地に黒で不可視::-webkit-calendar-picker-indicatorfilter:invert(1) で反転。架電リストだけでなく全画面の日付/時刻入力に横展開
  3. 行ホバー色が「企業名・電話番号」だけ乗らない(色が途中で切れる)。真因=左固定列(チェック/#/企業名/電話番号)が JS 側で backgroundインライン指定しており、CSS の tr:hover td(style.css:144) が負けていた → #clTB tr:not(.cl-vspacer):hover td!important で全セルに適用

v11.6.52 ファビコン透過刷新

  • 指定の翼マーク(~/Downloads/生成画像1 (20).png・1254px)から白背景をアルファ化(輝度→alpha)→ bbox トリム → 8%余白 → 32/64/180px 生成
  • 旧ファビコン(黒背景+白マーク)は ~/scale-lead-backups/favicon-pre-v11651/ に退避
  • 本番 favicon-32.png の md5 一致を確認
  • 注意: マークは黒のまま透過なので、タブがダーク配色の環境では見えにくい可能性あり(未指示のため現状のまま)

13:45 - 【調査】架電リストの読み込み速度(大串「どうしても上げれないもの?」)

実測(D1 で boot クエリを再現)

対象 実測
app_data 全体 839キー / 4.25MB
起動時に実際に運ぶ量(boot=1 除外後) 829キー / 2.02MB

起動ペイロードの約70%が「廃止済み機能のデータ」

キー サイズ
proj_*_voice_mobile_log × 5案件 計 約1.4MB
backup_2026-04-23 / 04-24 計 166KB
  • voice_mobile_logZoomphone連携が2026-05-20に廃止済みで誰も読んでいない。にもかかわらず BOOT_EXCLUDE_LIKE(functions/api/data/index.ts:17)から漏れており、全員が毎回の起動で1.4MBをダウンロードし続けている
  • コメントには「2026-05-20: voice_mobile_log / voice_recordings 削除」とあるが、実際は除外リストから消しただけでD1にはデータが残っていた(=除外されなくなった)

改善余地(未着手・GO待ち)

  1. 即効: voice_mobile_log と backup_ を boot 除外 → 2.02MB → 約0.5MB(70%減)*。データは消さないので安全(D1掃除は別途)
  2. 構造: 起動が「全部取り終えてから画面を出す」直列(await fetch('/api/data?boot=1'))。画面を先に描いてデータを後追いにすれば体感はさらに短縮

学び

  • 「除外リストから消す」と「データを消す」は別物。機能廃止時に除外リストだけ整理すると、残ったデータが除外対象から外れて逆に毎回運ばれる状態になる(今回1.4MB/人/起動)。廃止時は「データ削除」「除外維持」のどちらかを必ず選ぶ

13:50 - SCALE CRM v11.6.53: 起動を速く(大串GO「1.2両方やる」・本番LIVE・git 195dfce)

【1】起動時に運ぶデータを76%カット

  • BOOT_EXCLUDE_LIKE(functions/api/data/index.ts)に %_voice_mobile_log / %_voice_recordings / backup_% を追加
  • 実測: 829キー/2.02MB → 823キー/0.49MB(76%減)
  • 内訳: voice_mobile_log 5案件で約1.4MB=全体の約70%。Zoomphone連携は2026-05-20に廃止済みで、現行コードの参照はコメントのみ(grep で読者ゼロ確認)
  • D1 のデータは残置(復元可)。運ばないだけ

【2】画面の先出し(boot 待ちの解消)

  • 従来: showApp() の後、await _initSupabase()/api/data?boot=1)が終わるまで中央が「読み込み中...」=D1応答 0.5〜0.8秒を丸ごと待っていた
  • 変更: D()_cache 不在時に localStorage(sb_*) を読む性質を使い、前回データで本画面を先に描画。boot は裏で並行取得し、完了後に既存の再描画が最新へ差し替える
  • 架電リストは Phase5 でサーバー駆動=boot(app_data) 非依存なので、boot と行取得が並列になる
  • 前回データが無い(初回ログイン/別PC)ときは先出しをスキップ=従来動作(安全側)

学び(恒久)

  • 機能を廃止したら「データを消す」か「boot除外を維持する」かを必ず選ぶ。除外リストからパターンだけ消すと、残ったデータが除外対象から外れて毎回全員に配信される。今回それで1.4MB/人/起動を2ヶ月半運び続けていた
  • 「重い」の調査はまずペイロードの中身を実測。2.02MBの内訳を見た瞬間に、7割が使われていないデータだと分かった(コードを読む前にデータを見る)
  • 起動シーケンスは「画面を出す」と「データを取る」を直列にしない。ローカルキャッシュで先に描いて、通信は並列にする(架電リストの案件切替 v11.6.49 と同じ設計思想)

14:00 - 【調査】同種の問題の横断洗い出し(大串「同じ問題起きる可能性ない?未然に防ぎたい」)

共通の真因パターン

「_cache = 案件の全行」という前提で書かれたコードが、Phase5(サーバー駆動=_cacheは表示分のみ)に移行した後も残っている。
v11.6.49(案件切替)・v11.6.50(編集後)はこのパターンの2例。他にも残っていないかを機械的に洗った。

影響範囲の前提(D1実測 2026-08-04)

案件 行数 全量ロード
mnpg72otcfqhu(結グループ本体) 17,321 しない(8,000行超)
mr8u1qi3oy490 4,481 する(v11.6.49でアイドル化)
moz6qooo63107 3,348 する
mpgfeuixht619 450 する

8,000行超は1案件のみ。大案件限定の問題は結グループ本体だけで起きる。

見つかったリスク

【A】全案件で発生・明白なバグ — フィルタUIが操作不能になる
- calllist_core.js:906-915 の検索ボックス/全ステータス/全担当者/全地域/全セグメントが ${!calls.length?'disabled':''}
- サーバー駆動で _cache が空の間(案件切替直後・v11.6.49でアイドル化したぶん数秒)、行は表示されているのにフィルタが灰色で押せない
- さらに地域(_clPrefOptions(calls))とセグメントの選択肢を _cache から生成しているため、空だと選択肢もゼロ

【B】大案件(結グループ 17,321行)限定
1. CSV出力が全件出ないcalllist_csv.js:29PD('call_list') 依存=_cacheの200行+今日行のみ
2. CSV取込の重複判定が漏れるcalllist_csv.js:248 existingCalls=PD('call_list')。既存行が_cacheに無いと重複を検知できず二重取込になる(データ品質に直結。v11.6.31で分配経路は「サーバー正本で判定」に直したが、CSV取込は未対応)
3. 分析レポート/ダッシュボード/タイマーKPIが過少集計 — いずれも PD('call_list') 全走査(report:1 / dashboard:3 / timer:2 箇所)。※日報だけは v11.6.48 でガード済み

【C】守られていた点(触ってはいけない)
- calllist_core.js:35,237if(!calls.length)return;空配列を PS すると per-row diffPush が全行 HARD DELETE する事故の防止ガード(v11.5.47)。これは「_cache前提」ではなくデータ保護なので残す

対処案(GO待ち)

  • A: サーバー駆動時は disabled を外す+選択肢はサーバー(facets)or 固定リストから生成 → すぐ直せる
  • B-2: CSV取込の重複判定をサーバー正本へ(v11.6.31 分配と同型)→ データ品質最優先
  • B-1: CSV出力はサーバーから全件取得して書き出し
  • B-3: stats API(/api/calls/stats・v11.6.47で新設済み)をUIに接続 = Phase5.4 の残タスクそのもの

14:55 - SCALE CRM v11.6.54: 洗い出した3つの穴を塞ぐ(大串GO「1〜3やって」・本番LIVE・git 30ea9df)

【B-2 最優先】CSV取込の二重取込を根治

  • 新API functions/api/calls/dedup.ts: 照合キー4項目(SalesNowリンク/会社名/最新ステータス/架電担当者)だけを仮想生成列から返す軽量API。配列の配列で転送量を圧縮・truncated フラグ付き
  • processImportCSV: 大案件 or _cache 空のときサーバー正本と照合してから取り込む
  • _cache 配列そのものに push すると偽行が保存経路に載るので、必ず slice() のコピーに補完(事故防止)
  • 照合失敗時は「二重に入る可能性がある」と confirm で明示(黙って取りこぼさない)+取込結果に照合件数を表示
  • 認証: _middleware.ts/api/calls 前方一致でカバー済み=列挙追加は不要(地雷を踏まずに済んだ)。本番 403 は未認証時の正常応答

【A】フィルタが操作不能になる問題

  • 検索/ステータス/担当者/地域/セグメントの disabled 条件 !calls.length_fdis に集約し、サーバー駆動時は無効化しない(5箇所を一括置換)
  • _clPrefOptions: _cache が空で実在県を拾えないときは全都道府県にフォールバック(選択肢ゼロを作らない)

【B-1】CSV出力が全件出ない問題

  • exportCallCSV を async 化し、大案件はサーバーから全件取得してから書き出し
  • 件数明示の confirm + 進捗トースト + 二重実行ガード(119k分配インシデントの教訓を適用)
  • 取得失敗時は「一部だけになる」と明示して確認

【C】触っていない(役割が真逆のコード)

  • calllist_core.js:35,237if(!calls.length)return;空配列を PS すると per-row diffPush が全行 HARD DELETE する事故の防止(v11.5.47)。見た目は「_cache前提」と同じ形だが役割が正反対なので維持

学び(恒久・横展開)

  • 同じ形のコードでも役割は真逆のことがある!calls.length は「UIを止める誤ったガード」と「データ全消しを防ぐ正しいガード」の両方に使われていた。機械置換の前に1件ずつ役割を確認する
  • 既存配列(_cache)に補完データを push しない。判定用に足すつもりでも保存経路に載れば偽行としてD1に書かれる。必ずコピーを作る
  • 影響範囲は先にデータで確定させる(8,000行超は1案件のみ→「大案件限定の問題」の優先度を正しく判断できた)

16:20 - SCALE CRM v11.6.55(Phase5.4 / B-3): 全期間を走査する画面の取りこぼしを根治(大串GO・本番LIVE・git d3310f2)

調査で当初の見立てを訂正した

14:00 の洗い出しでは「分析レポート・ダッシュボード・タイマーKPIが過少集計」と報告したが、コードを精読したら実態は違った:
- 架電リスト上部KPI(_collectTodayStats)・Slack中断報告・_clTallyByList当日のみの集計 → v11.6.45 の _clServerPrefetchToday(today_called 500件)で大案件でも既に正しかった
- 個人ダッシュボードは daily_teleapo(日報)ベースで、call_list 全走査ではなかった
- 実際に取りこぼすのは「全期間を走査する画面」= アポ獲得リスト(getAutoAppointments)/商談予定/再架電リマインダー(renderReminders)/クロスチャネル重複チェック

方式の選択(ここが肝)

  • 集計ロジックのSQL化は不採用。通電判定が画面ごとに違う(report.js は CONNECTED_ST=担当不在を除くtimer.js は担当不在を含む)。SQLに寄せると数値が変わる=退化リスクが高い
  • D1実測で アポ系9件・再架電予定469件=計478件と少数と判明 → 「重要行だけ先読みして _cache に入れる」方式を採用。既存の集計ロジックを1行も変えずに正しくなる

実装

  • query.ts: preset has_recall 追加(日付を問わず recallDate を持つ行)。JS述語 String(recallDate).trim() と SQL TRIM(COALESCE(c_recall,''))<>''厳密1:1early に登録。期待件数469をD1で照合済み
  • calllist_core.js: _clServerPrefetchKeyRows() 新設。大案件のみ apo / has_recall を先読みし _clServerBridgeRows_cache へ。表示を優先して _clIdle(2秒)で後追い実行
  • 作りかけの tally.ts(集計SQL化API・1.2秒/8万行スキャン)は不採用のため削除(未使用APIを本番に残さない)

学び(恒久)

  • 「集計が合わない」時、集計側を書き換える前に"データ供給側"を直せないか考える。ロジックを触ると画面ごとの微妙な差(通電判定など)を壊すが、データを揃えるだけなら退化ゼロ。今回 478件の先読みで4画面が同時に直った
  • 報告した見立ては実装前に必ず精読で裏を取る。「レポート/ダッシュボード/KPIが過少」は精読すると大半が既に担保済みで、真の対象は別だった(実装してから気づいたら数値を壊していた)
  • 作ってしまった不採用コードは残さず消す(未使用APIは負債)

17:35 - SCALE CRM v11.6.57: 起動時「読み込み中...」を根治(大串FB・本番LIVE・git 8572b06)

真因(推測せず error_log の実データで確定)

ReferenceError: renderManualPage is not defined
  at renderPage → showApp → _appBoot
  • renderPage 冒頭の R マップmanual:renderManualPage / knowledge_hub:renderKnowledgeHubガード無しで直参照していた
  • この2関数は kickoff.js 所属=v11.6.32 の遅延ロード対象なので、起動時点では未定義
  • オブジェクトリテラルを作る時点で落ちるため、開いているページに関係なく renderPage 全体が失敗 → catch(core.js:5843)が _loadingPlaceholderHTML() を表示 → boot 完了まで「読み込み中...」
  • error_log に 大串・山本の両方で同一スタックを記録。2026-07-27(v11.6.32)以降ずっと全員が毎回踏んでいた

なぜ漏れたか

v11.6.32 で「R マップを typeof ガード化」した際、同じ kickoff.js の renderKickoff / renderOnboarding / renderPartnerOnboard はガード+_LAZY_PAGE_MOD 登録をしたのに、renderManualPagerenderKnowledgeHub の2つだけ両方とも漏れた

修正

  1. _LAZY_PAGE_MODmanual:'kickoff' / knowledge_hub:'kickoff' を追加 → 未ロード時は既存機構(renderPage:6578)が kickoff を遅延ロードして再描画する経路に正しく乗る
  2. R マップの未ガード直参照 25個すべて(typeof X==='function'?X:null) に統一(同じ事故の再発防止)

前段(v11.6.56)でやったこと=正直な訂正

  • v11.6.53【2】の「画面の先出し」は showApp() が既に同じことをしていた(core.js:5837-5839)ため完全な重複=効果ゼロ → 撤去
  • 代わりに _reportBootRenderFail() を仕込み、起動時の描画失敗を1セッション1回だけ /api/errorerror_log に自動記録。これで大串に手間をかけずに真因を特定できた
  • boot の D1 クエリは実測 32ms(825キー返却)=待ち時間の主因ではないことも確定

学び(恒久・最重要級)

  • オブジェクトリテラル内の関数直参照は「1個の未定義」で全体を道連れにする。遅延ロードを導入したら、そのファイルが持つ全てのエクスポートを洗い出してガード+登録する(1つでも漏れると全ページが死ぬ)
  • 再現しづらい不具合は「自動記録」を仕込むのが最短。コンソールを見てもらうラリーをせず、error_log に1件送るだけで確定した。しかも他メンバーの発生状況まで分かった
  • 体感の悪さを「遅い」と決めつけない。今回はデータ取得32msに対し、実体は「毎回エラーで描画が止まっていた」。速度改善(v11.6.53)を積む前にエラーを疑うべきだった

21:00 - SCALE CRM v11.6.58: Slack投稿失敗の自動記録+稼働時間の矛盾警告(大串GO・本番LIVE・git 6ecd9c6)

発端: 大串「稼働終了だけSlackで上がってくるのはなんでだろう?」

スレッド(結グループ_エアコン_テレアポ支援_作業報告 / 08-04 山本さん)に 「✅稼働終了」だけ1件で「▶稼働開始」が無かった。

調査で分かったこと(当初の推測を1回訂正した)

  • Slackスレッドに出るのは 稼働開始 / 作業中断 / 稼働終了 の3種類
  • endCallTimer稼働開始を押していなくても実行できる(1分未満なら確認ダイアログのみ)
  • 当初「開始を押していない」と回答したが誤りだった。D1 の daily_teleapo_rows を見ると note:'タイマーから記録' / workHours:0.09_timerState は日付変更時に前日ぶんを timer_missed_end_report へ退避して elapsed=0 から始まる仕様なので、この0.09h(5.4分)は当日ぶん=開始は押されている
  • 開始通知だけが投稿に失敗した可能性が高い(同じ日に前野さんは4件投稿されておりシステム全体の障害ではない)
  • 従来は slackPost の失敗が console 止まりで後から追跡できなかった

実装

【1】Slack投稿失敗の可視化
- _reportSlackFail(where,detail) 新設(pagecore_slack.js)→ /api/errorerror_log に記録。1セッション5件上限
- 設置箇所: 稼働開始(前提なし/スレッド取得失敗/投稿失敗/例外)・作業中断(投稿失敗)・稼働終了(スレッド取得失敗/投稿失敗)
- slackPost は成功時のみ {ok:true} を返す仕様なので !(res&&res.ok) で判定

【2】実態とズレた日報の防止
- endCallTimer に sanity check: 架電5件以上 かつ CPH>60 なら警告して確認
- 警告は clearInterval よりに配置 → キャンセル時はタイマーが止まらず、そのまま作業に戻れる
- 8/4実例: 稼働0.09h(5分)で架電8件=CPH88.9 → 稼働時間・CPH・API・ランク・推定時給が全てズレたままSlack報告されていた

学び

  • 「押していない」と断定する前にデータで裏を取る。タイマーの仕様(日跨ぎで退避してリセット)まで確認して初めて「押している」と分かった。1度誤った回答をしてすぐ訂正した
  • 握りつぶした失敗は必ず後で調査不能になる。Slack投稿は失敗しても業務が止まらないので握りつぶす設計だったが、その結果「なぜ出ていないか」が誰にも分からなかった。握りつぶすなら記録は残す
  • 確認ダイアログは「キャンセルしたときに安全な状態か」まで設計する(今回はタイマー停止前に置くことで、キャンセル=稼働継続にした)