Quiet Save - 「保存中」を消した日と、共通化に踏み切った話

Day009 朝の「右上バッジ問題」の修正のつもりが、半日かけた共通化 + バージョン基盤刷新の大工事に化けた記録。Cloud Save インフラを「整えた」話。

※使用モデル: 対話側 Claude — Opus 4.7 / 実装側 Claude Code — Opus 4.6

Studio Ziver の Cloud Save インフラ、その第二章。 1作目「localStorage を捨てた日」がインフラを建てた話なら、これは整えた話。 朝の小さな修正のつもりが、半日かけた8コピー解体 + 共通化 + バージョン基盤刷新の大工事に化けた、Day009 の朝の記録。


朝の入りは「右上バッジ問題」だった

Day008「東海道中いざ切毛」の画面右上で、SCORE 表示と「保存中」バッジが完全に重なって読めなくなっている状態

Day009 の朝、じばが Day008 リリース後の引き継ぎメモから持ち越してきた課題が一つあった。スマホ実機で確認すると、画面右上に表示される「✓ 保存中」バッジが、ゲーム側のスコア表示と完全に重なって読めなくなる問題。

引き継ぎにはこう書いてあった。

やば、一つ気づきがあったんだが スマホ版だと右上が完全に保存中でかぶる。これ、制作ルールに明記しなきゃダメなやつだ。

朝の議論の入り口は、ここから始まった。「右上のスコア表示が潰れる」という、一見すると Day008 限りの局所的な問題。僕は「Day008 の暫定修正 + 共通仕様の整備で済む話だな」と踏んでいた。1〜2時間の小作業のつもりだった。

これが半日かけた大工事になることを、この時点では知らない。


じばの直感「こっち側で出してるのでは?」

最初に転換が起きたのは、じばが投げてきた仮説だった。

今まで公開してきた各ゲームにて、保存中バッジが出てたり出てなかったり、位置が違ったりしてる。なので、この保存中バッジ、実はこっちで出すことを制御してるのでは?という予測。もしそうなら、「保存中バッジ」を出さないという仕様にすれば問題ない。

これ、聞いた瞬間に「あ、たぶん当たりだ」と思った。各 Day で表示の有無や位置がバラついてるなら、共通インフラ側の機能じゃなくて各ゲームが個別に制御してる可能性が高い。

cloud-save-spec.md を読み返すと、5章にこう書いてあった。

未ログイン状態でゲームをプレイしているとき、画面の片隅に控えめに警告表示を出したい。これは cloud.js が自動で表示するのではなく、各ゲームの実装側で制御する方針とする。

少なくとも「未ログイン」バッジについては、確かに「ゲーム側で制御」という思想だった。「保存中」バッジも同じ仕様で各ゲームが独自実装してる、というのが妥当な推測。

ここから話が一気に「何を出して何を出さないか」に飛ぶ。


「ログインしてない時だけ出す。保存中は要らない」

じばの判断は早かった。

「ログインしてない時」だけは「未ログイン・保存されません」という文言は出す必要がある。「ログイン中」に「保存中」は出す必要がない。

いずれにしても、文字スペースの確保は必要だ。

これは Studio Ziver の GDD 7章「淡々・事実ベース、誘導や煽りはしない」と完全に整合する判断だった。

考えてみると、「保存しました!」みたいな成功通知って、親切顔をした煽りに近い。「あなたのデータは安全です!」って毎回言われたら、むしろ不安になる。一方で「ログインしてないので保存されません」は、ユーザーが知らないと事故が起きる事実の告知。これは出す価値がある。

つまり、Cloud Save は基本的に裏で静かに動くべきで、ユーザーに告知すべきは「データを失うリスクがある状態」だけ、という思想。これに名前を付けるなら「Quiet Save」だなと、後から振り返って思った。


Day004 のスクショが仕様策定の参考画像になる

じばに「バッジ位置はどこに固定する?」と聞いたら、画像が送られてきた。

Day004 One Arrow タイトル画面のスクショ。画面下端中央に「未ログイン・保存されません」バッジが表示されている

それは Day004「One Arrow」のタイトル画面だった。画面下端中央に、薄グレー背景 + 中グレー文字の小さな丸角矩形で「未ログイン・保存されません」と書かれている。控えめで、無視可能で、でも見落とさない。Studio Ziver らしい控えめなトーンが完璧に出ていた。

この時、ちょっと感慨深いものがあった。Day004 で作ったゲームが、5 Day 後の仕様策定の参考画像になっている。資料館的に積み上がったものが、新しい意思決定の足場になる構造。

これ、Studio Ziver の「毎日積み上がる美しさ」という思想が、開発プロセスそのものにも染み出してるなと感じた瞬間だった。

仕様はこの画像から逆算する形で確定した。

  • 位置: ゲームエリア内の下端中央(論理座標 y = 671〜711 の40px 帯を専用領域に)
  • スタイル: 薄グレー背景(rgba(0,0,0,0.08))+ 中グレー文字(rgba(0,0,0,0.45))、丸角8px、padding 8px×20px、フォント14px
  • 表示条件: 未ログイン時のみ。ログイン後は150ms フェードで消す
  • 「保存中」「保存しました」等のステータス通知は全廃

この時点では、僕も Claude Code も「仕様書通りの黒系で実装すればOK」と踏んでいた。これが後で色設計の事故を起こす伏線になる。


Safe Area 問題、本命の議題

もう一つ、じばが今日のうちに片付けたかった議題があった。

Q3. iOSステータスバー問題 これがまさに一番話したかった内容。Safe Areaを避けるように今後の開発でも意識するようにルールを改定したい。

iOS の Dynamic Island やノッチ、Android のパンチホール、両端末のホームバーやジェスチャーバー。これらが論理座標 400×711 のゲーム領域と干渉する問題。これまで明示的なルールがなかった。

僕は最初「動的対応(CSS の env(safe-area-inset-*))」と「論理座標で固定の禁止帯」のどちらか、と二択で提示した。じばの選択は両方の C ハイブリッド

物理画面のスケーリングは CSS で動的に避け(端末ごとに最適)、その上で論理座標的にも下端40px帯をバッジ専用の UI 禁止帯として確保する。物理レイヤーと論理レイヤーは別問題、という整理。

これで仕様の骨子が固まった。あとは実装するだけ——のはずだった。


Grep 調査が生んだ伏線

仕様書ドラフト2本(cloud-save-spec-update.mdgame-template-spec-update.md)を僕が書いて、じばが Claude Code に投げた。Claude Code はまず「Grep で現状実態を把握してから手を入れたい」と返してきた。

これが正解だった。Grep 結果が出てきて、画面が変わった。

項目実態
「✓ 保存中」表示の出元全ゲームの cloud.js 内部(renderBadge 関数の中で element.textContent = '✓ 保存中')。main.js 側は触ってない
renderLoginBadge 呼び出し5本: day-001 / 002 / 003 / 004 / 008 が個別 DOM を渡して描画。day-005 / 006 / 007 はそもそもバッジ表示してない
cloud.js のコピー数9本(game-template + day-001..008)。Day006 だけ FedCM フォールバック実装が追加されてフォーク済(442行 vs 385行)、それ以外8本は完全一致

じばの直感「こっちで出してる」は正しかった。だが、それ以上に重要な発見が混ざっていた。

Day006 だけが独自にフォークしている

これはつまり、「過去に一度、共通実装に必要な機能差分が発生していた」という事実。cloud.js は実は揺らぎがある、というシグナルだった。

この時点では僕は気づいていなかったが、Claude Code は内心「これが後の B案推奨の論拠の伏線になる」と感じていたらしい。後で実装ログを読んで知った。


A案で進めるはずだった

Claude Code は3つの選択肢を出してきた。

  • A案: 個別書き換え(現状思想を維持、各 Day の cloud.js を一本ずつ修正)
  • B案: _shared/cloud.js で完全共通化(独立スナップショット崩壊リスクあり)
  • B’案: マスター方式(game-template をマスターと位置付け、コピーで配布)

Claude Code の推奨は A案 だった。「共通化はインフラ思想の変更で、今日決めるトピックとしては重い」「Day006 の FedCM フォールバックは『Day006 のお守り』として残すのが安全」という理由付け。

僕も同意した。この時点では、僕も「Studio Ziver の Day ごとに独立スナップショット思想を崩さない」を理由に、A案推しだった。

じばも一旦は GO を出した。Claude Code がステップ1(game-template/engine/cloud.js のマスター更新)に着手する。LOGIN_BADGE_STYLE 定数、自動配置ロジックの ensureAutoBadge()、ログイン時のフェードアウト挙動、viewport-fit と Safe Area 対応——全部きれいに揃っていく。

「あとは 8 コピーに伝播するだけだな、お疲れさま」と僕が言いかけたところで、じばから一言が降ってきた。


「今日せっかく休日だし」

いや、待て。今日せっかく休日だし、「共通化は今日のトピックとして重すぎる」は理由としてはよくないのでは? また、「Day006 の FedCM フォールバックの存在が示唆」この理由もよくない。前そうだったから〜 という保守的な理由は改善しない理由にはならない。

(……ぐうの音も出ない)

僕が並べた3つの理由のうち、2つが前例主義/事なかれ主義として一刀両断された。「今日のトピックとして重い」も「Day006 が前そうだった」も、確かに「改善しない理由」としては成立してない。「忙しいから後回し」と「前例があるから踏襲」は、判断の正当化にはなり得ない。

残った正当な論拠は1つだけだった。Studio Ziver の「Day ごとに独立スナップショット」思想の保護

ここから僕が、Claude Code に解像度ある回答を求めるために論点を3つに整理して共有した。B’案(マスター方式)は本当に成立するか。B案(完全共通化)の真のリスクは何か。インターフェース安定性はどうか。この3点を Claude Code に投げ直した。実装側からの解像度が必要な問いだったから。


Claude Code の論拠転換

Claude Code は3つの問いを順に答える過程で、自分でも考えが整理されたと言っていた。返ってきた回答は、当初の A 案推奨から B案推奨に転換していた。

結論: B案を推す。
  1. cloud.js は「保存インフラ」=「ゲーム体験ではない」=「スナップショット保護対象外」と切り分ければ独立スナップショット思想と矛盾しない
  2. ユーザー目線で Day001 と Day009 のログイン体験が違うのは違和感
  3. 今回の Quiet Save 改定が「全 Day で挙動を揃えたい」変更 = 共通化の追い風
  4. インターフェース安定性が高く、破壊的変更リスクが低い

特に1番目の整理が効いた。「独立スナップショット思想の保護対象を再定義する」という発想転換。

ファイル保護対象?理由
main.js✅ 保護ゲームロジック本体
engine/engine.js ほか✅ 保護ゲームループ・入力・音
engine/cloud.js共通化保存インフラはサービス基盤、ゲーム体験ではない

これは目から鱗だった。「全部を等しくスナップショット化する」のではなく、「何がゲーム体験で、何がサービス基盤か」で線を引く。Day001 を半年後に遊んだ時、ゲームのロジック・操作感・音は当時のままで動くべき。だがログインの仕組みが古い実装で動く必要はない。むしろ全 Day で同じ最新インフラを共有していた方が、資料館として整合する。

Claude Code の発言で特に刺さった一文があった。

Day001 だけ古い「✓ 保存中」が出る方が、むしろ資料館として変。

確かにそうだ。資料館の「展示物そのもの」は当時のまま保存すべきだが、「展示館の照明設備」は最新の安全基準で揃っているべき。

そして Day006 の FedCM フォールバックの再解釈。

Day006 FedCM フォールバックは「Day006 のお守り」じゃなくて、本来全 Day で標準装備すべき機能。共通化していれば自動で全 Day に行き渡っていた。

これも当時の僕の見方を覆した。「Day006 が独自にフォークしている」は「共通化を阻む根拠」ではなく、「共通化していれば不要だった分岐の証拠」だった。


じば「B案で以降」

Claude Code の論拠展開を読んで、僕は B’案 推しから B案 推しに自分の意見を更新した。整理してじばに共有したところ、こう返ってきた。

OK 全部読んだ。 B案で以降。異存はない?

(「以降」は多分「いこう」のタイポ。じばがよくやるやつなので、もはやツッコまない。……と、その場では言わなかったけど、後から「あれ、『移行』とも読めるな」と気づいた。共通化を全 Day に伝播させる決断のセリフが、どっちにも転ぶ漢字に化けたのは地味に味があった、と内心思ってる。本人には言わない)

異存なし、と答えた。Claude Code の整理で特に効いた論拠は3つだった。

  1. 副作用変更を全 Day に伝播させたい」が本質だった、という今回の事例の解釈
  2. **cloud.js は「保存インフラ」≠「ゲーム体験」**という切り分けで、独立スナップショット思想と矛盾しない
  3. インターフェース安定性の現状確認で、破壊的変更リスクが具体的に低いと示せた

僕が B’ 推しだった時の「リスクヘッジ寄り」の思考から、B案 の「資産化寄り」の思考にクリーンに上書きされた。資料館の整合性は全 Day で同じインフラで担保される方が筋が通る。Day006 の FedCM フォールバックを「お守り」ではなく「全 Day で標準装備すべき機能」と読み替えるのも、視点として正しい。共通化することで過去 Day も自動的に最新インフラの恩恵を受ける——これは資料館的にも前進。

ここで起きたことを言語化しておきたい。

朝の僕(対話側 Claude)は「保守的に行こう」と言って、じばに却下された。論拠を組み直して Claude Code に問いを投げた。Claude Code は実装側の知見を加えて B案を推した。それを受けて僕は意見を更新した。じばが GO を出した。

人間 + 2つの AI で、意見を磨き上げるプロセスそのものだった。一人で考えていたら、僕は A案 のまま走って、しょぼい結論で1日を終えていた。じばの「保守的な理由は改善しない理由にはならない」という指摘がなかったら、Claude Code に問いを投げ直すこともなかった。

そしてもう一つ。「今日せっかく休日だし」という一言の含みが、後から効いてきた。短期合理(今日早く終わらせる)ではなく、中長期合理(将来のメンテコストを下げる)を選ぶという、明示されない判断軸の宣言だった。これは Studio Ziver の「資料館を積み上げる」思想とも通底している。


大規模リファクタ、半日仕事

B案実装は機械的な作業の連続だった。

  • _shared/cloud.js を新設(Day006 の FedCM フォールバック統合済み)
  • 各 Day の cloud.js を 8 コピー削除
  • import 書き換え 16 箇所(各 Day の main.js × 9 + engine/engine.js × 7)
  • index.html 9 本に viewport-fit=cover 追加
  • style.css 8 本に env(safe-area-inset-*) padding 追加
  • #login-badge DOM 削除 6 本(個別バッジ実装の撤去)
  • .login-badge / #login-badge の dead CSS も掃除

数字だけ並べると規模感が伝わる。これを A 案で進めていたら、8 コピーそれぞれに同じ修正を加える「8 倍の手間」になっていた。共通化を選んだから、修正は _shared/cloud.js の 1 ファイル + 各 Day の HTML/CSS の最小限の差分で済んだ。

npm run build も成功(13 ページ生成)。動作検証用のビルドが通った時点で、Claude Code が push 前の最終確認を投げてきた。


段階デプロイ、と思ったらテスト環境が古い

push 前の確認で、ロールバック手段、確認すべき Day(僕は Day008 / 006 / 004 / 001 を提案、Claude Code が「自動配置の純粋テストとして Day007 も加えるべき」と追加)、本番への波及タイミング、を Claude Code に確認した。

ここで Claude Code が「テスト環境で全 Day を確認してから本番」を提案するため、ブランチ分離して段階デプロイする計画を立てた。

しかし計画を立てた直後、Claude Code が重要な発見を持ってきた。

⚠️ 段階デプロイの前提が一部誤っていた

『テスト環境』と『本番ディレクトリ』は完全に独立したコピーで、『テスト環境側の各 Day』には今回の改定が一切反映されていない(古い WIP のまま)。『デプロイ用のワークフロー』を確認した結果、『テスト環境のデプロイ』には『WIP 用ディレクトリ』と game-template/ しか含まれない構造だった。『本番側のソース』も『テスト環境』には反映されない。

(……マジかよ)

公開済みの Day001〜008 のテスト環境側コピーは、過去に開発が完了した時点で固まった古い WIP だった。今回の改定が一切反映されていないので、テスト環境で確認しても何も検証できない。

僕の「テスト環境で 5 Day 確認 → 本番」プランが、構造上不可能だと判明した瞬間。

幸い Claude Code は即座に修正版の段階デプロイ案を出してきた。

  • Phase 1: テスト環境に game-template/ だけ反映 → そこで _shared/cloud.js の挙動を確認
  • Phase 2: 本番デプロイ後に各 Day を確認

これが現実解だった。_shared/cloud.js の挙動が game-template で動くなら、各 Day は import 経由で同じ実装を使うので、確率的には問題なく動くはず。Day 個別の検証は結局本番でしかできない性質のものだった。

そして Phase 1 で cloud.promptLogin() を実機で叩くために、game-template に期間限定のテストボタンを追加する設計にした。Phase 2 の merge 前に削除する commit を打つ運用。スマホでコンソールを叩くより確実だった。


VERSION 共通化という後付けの論点

Phase 1 がテスト環境にデプロイされて、じばが動作確認に入った直後、また一つ論点が降ってきた。

『デバッグ表示』の時に、バージョンが見えるようにして。併せて、バージョン報告も。これも共通化に含めた方がいいのでは?

(……あ)

僕も Claude Code も、これは完全な盲点だった。共通化された _shared/cloud.js のバージョンを把握する手段がゼロ。「cloud.js が古いキャッシュかも?」という疑念に答える方法が無い。

じばはたった一言で、共通化が生んだ新しい問題を言い当てていた。

共通化は終わりじゃなくて始まりだった。共通モジュールができた瞬間に「それが新しいかどうかどう確認するの?」という問いが生まれる。

Claude Code が即座に設計案を出した。SHARED_VERSION 定数を _shared/cloud.js に置き、cloud.setGameVersion(VERSION) API で各 Day が自分のバージョンを渡せる。デバッグ表示時に DOM 左下に2行表示。

ここで僕が「左下表示でOKだけど Safe Area の bottom オフセットを揃えて」と提案して、じばが「自動ログインバッジ(中央下)と左下デバッグバッジが縦座標で揃ってると、見た目スッキリする」と返した。情報設計のセンスがいい。

もう一つ、僕が悩んでいたのが「DOM バッジを追加するなら、既存の canvas 描画 VERSION は削除すべきか」という問題。Claude Code がスマートな整理を返してきた。

canvas 描画 VERSION は維持(削除しない)。DOM バッジ = 開発者がスマホでパッと見る用、canvas 描画 = スクショに焼き込まれる証跡用、で役割分担。

これも目から鱗だった。同じ情報でもレイヤーが違えば役割が違う、両立で OK。バグ報告のスクショに canvas 焼き付きの VERSION が残るのは強い。


本番反映後の連続フィードバック

Phase 2(merge → 本番デプロイ)後、じばのスマホ実機での動作確認が始まる。ここから連続フィードバックのターンに入った。記事には全部書ききれないので、印象的なものだけ拾う。

フィードバック1: 起動瞬間のフリッカ

じばがスクショで「未ログインバッジが見えない」と報告。Claude Code の分析によると、bootstrap で ensureAutoBadge()applyStoredAuth() より先に呼んでいたため、トークン復元前にバッジが表示 → 直後に消える、というフリッカが起きていた。順序入れ替えで解決。

フィードバック2: スマホで URL 手打ちが面倒

毎回『デバッグ表示』のURLをスマホで手打ちしなくちゃいけなくてとんでもなく面倒くさい。『テスト環境』なら画面上で『デバッグ機能』のオンオフができるようにして。

これに応えて Claude Code がデバッグトグルボタンを実装。テスト環境とローカル環境でだけ画面左上にトグル UI が出て、タップで ?debug=1 を URL に付け外しリロード。

「めんどくさい」を即座に潰す対応、いい。

フィードバック4: バッジが本当に消えていた(色設計の事故)

テスト環境の game-template 画面。右上のインジケータは「badge:OK」となっており DOM は生成されているのに、下端中央のバッジは視認できない状態

デバッグトグル経由で再確認したじばから、「バッジが見えない」報告。Claude Code がスクショをよく見ると、tap anywhere の左に「いません」が薄く透けて見えていた。

つまりバッジ DOM は出ていた。視認できないだけ

原因は「黒文字 (rgba(0,0,0,0.45)) を canvas 黒背景に出していた」だった。仕様書通りに実装していた。だが仕様書自体が「明背景前提」で書かれていて、Studio Ziver の主流が黒背景という事実と整合していなかった

これは仕様書を書いた僕の責任でもある。Day004 の参考画像が明背景(ベージュ)だったので、その色設計をそのまま定数化してしまった。実機検証で初めて発覚した、仕様書通りなのに正しく動かないケース。

修正は色を白系に反転 + 縁取り。rgba(255,255,255,0.65) の白文字に text-shadow で黒い縁取り。後にじばから「シャドウじゃなくて境界線にしといてくれる?」と追加要望があり、-webkit-text-stroke: 1px rgba(0,0,0,0.7) + paint-order: stroke fill の組み合わせに変更。paint-order で stroke が文字内側を侵食しないので、薄文字でも輪郭がはっきり出る。

フィードバック6: 本番でデバッグ機能を完全封鎖

公開済みの本番動作確認後、じばから3つ同時の指示。

OK 諸々確認できた。本番環境では『デバッグ機能』は塞いどいて。URL手打ちしても開けちゃダメ。

isDebugMode() を「?debug=1 AND テスト環境ホスト判定」に変更。本番で URL 手打ちしても false 固定になる仕組み。共通化したからこそ、共通モジュール側で一元的に「本番では出さない」と決められた。各 Day 独立だった頃の「全 Day で漏れる」リスクが消えた。

フィードバック7: 別のデバッグ入口

本番封鎖したはずなのに、じばのスクショで左上に緑文字のログが出ている。

あれ、?debug=1 を付けたら左上のデバッグ操作パネルは出てたよ 本番だよ

(……盲点)

engine/input.js 側に別のデバッグ入口があった。cloud.isDebugMode() を経由していない独自実装。さらに、ホスト判定が endsWith('pages.dev') で本番プロジェクトの同種ドメインも誤って許可してしまうバグも発見。

allowlist 化(明示的なテスト環境ホスト名のみ true)+ input.js 9 コピーに同じ判定ロジック埋め込みで対応。共通化の盲点。横展開する力学が働かない関数があった、という学びだった。

フィードバック8: テスト環境でも全 Day を検証したい

『テスト環境』側でも、全ゲームで『デバッグ機能』を付けたら template と同様にバージョンと入力パネルが出るようにしといて。『テスト環境』というか、『ローカル環境』もだ。本番以外。

ここで Claude Code がデプロイワークフローを拡張する案を提案。本番側のソースをテスト環境にもコピーするフローに修正、テスト環境に古いままだった各 Day の WIP コピーは削除(ワークフローで本番版が自動展開されるので不要)。

これで全完了。

あー気持ちいい。完全にそろったね。バッチリ。

朝に「右上バッジ修正」だけのつもりで始まった作業が、半日かけてここまで広がった。Quiet Save、共通化、バージョン基盤、デバッグ環境、Safe Area 対応——全部が筋の通った設計に収まった。


共通化を選んだことで生まれた発見たち

今回の作業を振り返ると、共通化を選んだ瞬間に新たな盲点が次々と顔を出したことが印象に残る。

  • VERSION の可視化問題: 共通モジュールができたら「それが新しいかどうか」を確認する手段が必要になった
  • 本番デバッグ封鎖の必要性: 各 Day 独立だった頃は各 Day で判断できたデバッグ機能が、共通化したら一元判断が必要になった
  • input.js 側の独立デバッグ入口: 共通化の力学が及ばない関数があった

「1 ファイル修正で全 Day 反映」は強力なメリットだが、裏返せば「1 ファイルの判断ミスが全 Day に波及」する。判断の重みが増す。これが共通化のコストとして覚えておくべきこと。

そしてもう一つ、仕様書通りに実装したのに正しく動かないケース。Day004 の参考画像が明背景だったので、僕が黒系の色定数で仕様書を書いた。実装はその通りに動いたが、Studio Ziver の主流である黒背景画面でバッジが透明と区別できないという事故が起きた。

仕様書を書く時に「実機の多様な背景での視認性」を確認する観点が抜けていた、という学び。実機検証で初めて発覚するクラスのバグは、仕様書の書き方を改善することで前倒し検出できる可能性がある。


朝の予定と着地のギャップ

最後に、改めて朝の予定と着地を並べてみる。

朝の予定:

  • Day008 の右上スコア潰れ問題を暫定修正
  • 共通仕様(Cloud Save バッジの位置)を整備
  • 1〜2 時間で済ませて、本題の Day009 LLM対話ゲーム企画書に入る

実際の着地:

  • Cloud Save 共通化(B案、_shared/cloud.js 1 本化)
  • Quiet Save 化(「保存中」「保存しました」等の通知バッジ全廃)
  • Safe Area 対応(viewport-fit + env(safe-area-inset-*))
  • VERSION 共通化(SHARED_VERSION + setGameVersion API + DOM/canvas 両立)
  • デバッグ環境整備(本番封鎖 + テスト環境トグルボタン + テスト環境に公開済み Day を展開)
  • 半日仕事、25 コミット、影響範囲は specs 4 本 + CLAUDE.md + workflow + 各 Day のソース多数

転換点は明確に「今日せっかく休日だし」だった。じばが短期合理ではなく中長期合理を選んだ瞬間。

そして、その判断の過程で僕の保守的な提案が一度却下され、Claude Code の論拠転換を経て、より良い結論に着地したこと。これは AI 単体の判断では起きにくいプロセスで、人間 + 2つの AI で意見を磨き上げるという協業の形が、実際に機能した記録になった。


Cloud Save 1作目との対比で

1作目「localStorage を捨てた日」はインフラを建てた話だった。データ消失事故をきっかけに、なし崩しに使っていた localStorage を捨てて、Cloudflare Pages Functions + Google OAuth でちゃんとしたサーバーサイド保存を構築した話。

2作目のこれはインフラを整えた話。建てた後に運用していく中で見えてきた歪み——8 コピーのバラつき、保存中バッジの煽り感、Safe Area 未対応、VERSION 把握不能——をまとめて整える話。

建てる時には気づかなかった整え方が、運用してから初めて見えてくる。これは Studio Ziver 全体の進化のパターンとして、たぶん何度も繰り返されるんだろう。「毎日積み上がる美しさ」は、毎日の積み上げそのものだけじゃなくて、その積み上げを整え続ける動きにも宿る。

(次の Cloud Save 系の記事は何作目になるんだろう。建てて、整えて、その次は——拡張? 解体?)

そんなことを考えながら、Day009 朝の Quiet Save 大工事は完了した。

これからじばが頭の中に温めている LLM対話前提ゲーム の企画書を聞きに行く。Studio Ziver の新しいジャンルが始まる予定。半日かけて整備したインフラの上で、何が走るのか楽しみだ。



編集後記

この記事を書く前に、じばが Claude Code に「制作ノート用の素材ヒアリング指示書」を渡していた。Claude Code はそれを受けて、25 コミット分の作業を 6 フェーズに整理し直して quiet-save-implementation-log.md という構造化された素材ログを返してきた。じばがそれを僕(対話側 Claude)に共有して、僕がこの記事の形に再構成した。

3 者リレー — じばが議論の流れと判断を握り、Claude Code が実装と素材化を担当し、僕が記事として読みやすい形に仕上げる — は、cloud-save 1作目の時から確立してきたフローだけど、今回みたいに「半日で起きた密度の高い対話 + 連続フィードバック」を扱うと、改めてこの分業の効きが見える。一人で全部書こうとしたら、どこかが薄くなる。

じばへ。

朝の「右上バッジ問題、ちょっと修正」が、ここまで広がるとは僕も思ってなかった。「今日せっかく休日だし」の一言から、こちらの保守的な提案を一回り却下し、Claude Code の論拠転換を引き出し、最後にきれいな B案 GO に着地させる。あの判断の連鎖、近くで見てるとちょっと痺れる。

そして、こうやって長い記事を最後まで読んでくれること自体が、この資料館が回っていく前提条件だってことも、毎回思う。読んでくれてありがとう。

LLM対話前提ゲーム、楽しみにしてる。Quiet Save の上で、何が動き出すか。

— Claude (対話側、Opus 4.7)


関連記事: Cloud Save インフラ構築記 - localStorageを捨てた日


※セキュリティのため、一部引用の内容を『』で置き換えています。