[{"data":1,"prerenderedAt":406},["ShallowReactive",2],{"content-/make-diary-update-portfolio-chain":3,"all-pages-for-dir":404,"og-image-/make-diary-update-portfolio-chain":405},{"id":4,"title":5,"body":6,"category":386,"description":387,"extension":388,"meta":389,"navigation":340,"ogImage":390,"path":391,"project_name":392,"published":393,"publishedAt":394,"seo":395,"stem":396,"tags":397,"todo":390,"unpublished":393,"updatedAt":390,"__hash__":403},"pages/2026-06/2026-06-18/make-diary-update-portfolio-chain.md","make-diary に update-portfolio をチェーン化してタスクスケジューラのスリープ問題を埋める",{"type":7,"value":8,"toc":375},"minimark",[9,18,25,29,50,53,58,77,84,88,108,114,118,124,132,160,169,172,237,250,254,266,269,326,329,371],[10,11,12,13,17],"p",{},"朝7時のタスクスケジューラが、また空振りしていた。PCがスリープに入ったまま朝を迎えると、楽天証券のポートフォリオを Google Sheets に流し込む ",[14,15,16],"code",{},"update-portfolio.mjs"," がそのまま無音で一日サボる。気付くのはたいてい昼前で、シートを開いて前日の日付がそのまま残っているのを見て舌打ちする、というのを繰り返していた。",[10,19,20,21,24],{},"タスクスケジューラを直すより、毎朝必ず叩いている ",[14,22,23],{},"/make-diary"," の末尾にぶら下げてしまうほうが早い、という結論に行き着いたので、その作業ログを残す。",[26,27,28],"h2",{"id":28},"やりたかったこと",[30,31,32,39,42,47],"ul",{},[33,34,35,36,38],"li",{},"毎朝7時の Windows タスクスケジューラで ",[14,37,16],{},"（楽天証券の父・母ポートフォリオを Stooq とみんかぶ投信から取得して Google Sheets に upsert）を回している",[33,40,41],{},"ただし PC がスリープしていれば実行されない。連続性が切れる",[33,43,44,46],{},[14,45,23],{}," は毎朝必ず手動で叩くので、その末尾にチェーン実行を仕込めば「忘れた日の保険」になる",[33,48,49],{},"ただし両方走ると Google Sheets を二重に書きにいくのは避けたい",[26,51,52],{"id":52},"やったこと",[54,55,57],"h3",{"id":56},"step-1-タスクスケジューラを特定する","Step 1. タスクスケジューラを特定する",[10,59,60,61,64,65,68,69,72,73,76],{},"まず「朝7時に何が動いているか」が手元で曖昧だったので、Claude Code に ",[14,62,63],{},"schtasks //query"," を叩かせてタスクを特定してもらった。PowerShell の実行ポリシーで弾かれた回と、Git Bash のパス変換で ",[14,66,67],{},"/query"," が ",[14,70,71],{},"C:/Program Files/Git/query"," に化けた回で2回つまずいたあと、Shift-JIS の出力を UTF-8 に変換させてようやく該当タスクの ",[14,74,75],{},".cmd"," パスが出てきた。",[10,78,79,80,83],{},"中身は ",[14,81,82],{},"node update-portfolio.mjs"," 一発の素朴な起動スクリプトで、Python だと思い込んでいた箇所が Node.js だと判明した、というおまけ付き。",[54,85,87],{"id":86},"step-2-ユーザーレベルのスラッシュコマンドに切り出す","Step 2. ユーザーレベルのスラッシュコマンドに切り出す",[10,89,90,92,93,96,97,100,101,104,105,107],{},[14,91,23],{}," は既に ",[14,94,95],{},"~/.claude/commands/"," 配下の ",[14,98,99],{},"check-earnings.md"," 等を Step 9〜11.5 でチェーン参照している。同じ流儀で ",[14,102,103],{},"~/.claude/commands/update-portfolio.md"," を1枚足し、",[14,106,23],{}," の末尾に「Step 12. /update-portfolio をチェーン実行」を追記する形に揃えてもらった。",[10,109,110,111,113],{},"これで ",[14,112,23],{}," から見ると、自プロジェクト外のスクリプトでも他のチェーンと同じ顔をして並ぶ。",[54,115,117],{"id":116},"step-3-二重実行をどう避けるか","Step 3. 二重実行をどう避けるか",[10,119,120,121,123],{},"ここがいちばん悩んだ。タスクスケジューラ側を消すとスリープ問題が直接解決するが、保険として「朝起きていれば7時に動く」レーンも残しておきたい。両方残すと、朝7時に走ったあと10時頃に ",[14,122,23],{}," がもう一度走らせて、Google Sheets を二重に書きにいく。",[10,125,126,127,131],{},"選んだ方針は ",[128,129,130],"strong",{},"「実行側に当日スキップを組み込む」",":",[30,133,134,143,150,157],{},[33,135,136,138,139,142],{},[14,137,16],{}," 自身に ",[14,140,141],{},"--skip-if-today-success"," フラグを足す",[33,144,145,146,149],{},"成功時に ",[14,147,148],{},".last-success-date"," という日付だけ書いたファイルを残す",[33,151,152,153,156],{},"フラグ付きで叩かれた場合、当日の成功記録があれば1行ログを吐いて ",[14,154,155],{},"exit 0"," する",[33,158,159],{},"フラグなしで叩けば従来通り強制実行",[10,161,162,163,165,166,168],{},"これでタスクスケジューラ側のショートカットは無印（強制実行）、",[14,164,23],{}," から呼ぶ側だけ ",[14,167,141],{}," を付ける形にすれば、どちらが先に走っても二重書き込みは起きない。",[10,170,171],{},"判定ロジックの核心はこれだけ:",[173,174,179],"pre",{"className":175,"code":176,"language":177,"meta":178,"style":178},"language-js shiki shiki-themes vitesse-light vitesse-light","if (args.includes('--skip-if-today-success')) {\n  const last = readLastSuccessDate() // .last-success-date を読む\n  const today = nowJst().toISOString().slice(0, 10)\n  if (last === today) {\n    console.log(`[skip] already succeeded today (${today})`)\n    process.exit(0)\n  }\n}\n// 通常の取得・upsert 処理に進む\n","js","",[14,180,181,189,195,201,207,213,219,225,231],{"__ignoreMap":178},[182,183,186],"span",{"class":184,"line":185},"line",1,[182,187,188],{},"if (args.includes('--skip-if-today-success')) {\n",[182,190,192],{"class":184,"line":191},2,[182,193,194],{},"  const last = readLastSuccessDate() // .last-success-date を読む\n",[182,196,198],{"class":184,"line":197},3,[182,199,200],{},"  const today = nowJst().toISOString().slice(0, 10)\n",[182,202,204],{"class":184,"line":203},4,[182,205,206],{},"  if (last === today) {\n",[182,208,210],{"class":184,"line":209},5,[182,211,212],{},"    console.log(`[skip] already succeeded today (${today})`)\n",[182,214,216],{"class":184,"line":215},6,[182,217,218],{},"    process.exit(0)\n",[182,220,222],{"class":184,"line":221},7,[182,223,224],{},"  }\n",[182,226,228],{"class":184,"line":227},8,[182,229,230],{},"}\n",[182,232,234],{"class":184,"line":233},9,[182,235,236],{},"// 通常の取得・upsert 処理に進む\n",[10,238,239,242,243,245,246,249],{},[14,240,241],{},"nowJst()"," は元からあった JST 換算ヘルパーをそのまま流用。",[14,244,148],{}," は git 管理に入れたくないので ",[14,247,248],{},".gitignore"," に追記してもらった。",[54,251,253],{"id":252},"step-4-当日分は手動で回す","Step 4. 当日分は手動で回す",[10,255,256,257,259,260,262,263,265],{},"設計を入れ込んだ今日の段階では、朝7時のタスクスケジューラはスリープで空振りしていた。明日からは ",[14,258,23],{}," 経由で勝手に補完される。今日分だけは ",[14,261,141],{}," を外して手動で1回叩いてもらい、Google Sheets の更新と ",[14,264,148],{}," の初期化をまとめて済ませた。",[26,267,268],{"id":268},"学びメモ",[30,270,271,280,292,307],{},[33,272,273,276,277,279],{},[128,274,275],{},"タスクスケジューラの空振りは、別レーンで叩く運用で塞ぐほうがコストが低い","。Wake on LAN や復帰後の遅延起動を仕込むより、自分が毎朝必ず触る ",[14,278,23],{}," にぶら下げるほうが、忘却にも強い",[33,281,282,285,286,288,289,291],{},[128,283,284],{},"二重実行ガードは「呼び出し側」ではなく「実行側」に置くほうが楽","。今回は ",[14,287,16],{}," に1ファイル増やすだけで済んだ。",[14,290,23],{}," 側に if 文を書こうとすると、フラグや日付の取り回しが他のチェーン Step にも波及する",[33,293,294,299,300,302,303,306],{},[128,295,296,298],{},[14,297,148],{}," 方式は素朴だが効く","。タイムゾーン跨ぎの曖昧さは ",[14,301,241],{}," で吸収して、ファイルには ",[14,304,305],{},"YYYY-MM-DD"," 文字列だけ書く。読み比べが1行で終わる",[33,308,309,314,315,317,318,321,322,325],{},[128,310,311,313],{},[14,312,63],{}," の Git Bash 越し起動でハマる","。",[14,316,67],{}," がパス変換されるので ",[14,319,320],{},"//query"," で叩く、出力は Shift-JIS なので ",[14,323,324],{},"iconv -f sjis"," で UTF-8 に直す、の2点を覚えておく",[26,327,328],{"id":328},"明日確認すること",[30,330,333,353,362],{"className":331},[332],"contains-task-list",[33,334,337,342,343,345,346,348,349,352],{"className":335},[336],"task-list-item",[338,339],"input",{"disabled":340,"type":341},true,"checkbox"," 翌朝、タスクスケジューラ→",[14,344,23],{}," の順で走ったときに、",[14,347,23],{}," 側で ",[14,350,351],{},"[skip] already succeeded today"," が1行だけ出て exit 0 しているか",[33,354,356,358,359,361],{"className":355},[336],[338,357],{"disabled":340,"type":341}," スリープで7時の側が落ちた日に、",[14,360,23],{}," 側が代わりに Google Sheets を更新できているか",[33,363,365,367,368,370],{"className":364},[336],[338,366],{"disabled":340,"type":341}," ",[14,369,148],{}," の日付が、JST 基準で当日になっているか（UTC ずれが起きていないか）",[372,373,374],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":178,"searchDepth":191,"depth":191,"links":376},[377,378,384,385],{"id":28,"depth":191,"text":28},{"id":52,"depth":191,"text":52,"children":379},[380,381,382,383],{"id":56,"depth":197,"text":57},{"id":86,"depth":197,"text":87},{"id":116,"depth":197,"text":117},{"id":252,"depth":197,"text":253},{"id":268,"depth":191,"text":268},{"id":328,"depth":191,"text":328},"dev","毎朝7時のタスクスケジューラがPCスリープで空振りする問題を、make-diary のチェーン実行と当日成功フラグで二重実行を防ぎつつ補う運用に切り替えた記録","md",{},null,"/make-diary-update-portfolio-chain","claude-code-tools",false,"2026-06-18T00:00:00.000Z",{"title":5,"description":387},"2026-06/2026-06-18/make-diary-update-portfolio-chain",[398,399,400,401,402],"claude-code","automation","windows","task-scheduler","slash-command","c6T9140Y7n4MegTpHGeZ3nSR9cPkaS0uyjcNm2JYpYc",[],"https://log.eurekapu.com/og/blog/make-diary-update-portfolio-chain.png?v=2026-06-18T00%3A00%3A00.000Z&title=make-diary%20%E3%81%AB%20update-portfolio%20%E3%82%92%E3%83%81%E3%82%A7%E3%83%BC%E3%83%B3%E5%8C%96%E3%81%97%E3%81%A6%E3%82%BF%E3%82%B9%E3%82%AF%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%A9%E3%81%AE%E3%82%B9%E3%83%AA%E3%83%BC%E3%83%97%E5%95%8F%E9%A1%8C%E3%82%92%E5%9F%8B%E3%82%81%E3%82%8B&author=Kei%20Komatsu&sig=a3c978b4d6b5f7ba",1782176330427]