ハルスペース・ラボ
緑色の接続状態は、ユーザーフロー完了の証明ではなかった
外部の市場シグナル、モバイルのローディング、プロジェクトトラフィックをつなぎ、ブラウザからAPI・台帳・外部プロバイダー・画面まで同じ流れで検証するようになった記録です。

Summary
ひと目でわかる要約
- 外部の市場シグナルはグループ管理者が選んだチャットルームだけへ送り、許可フィールド、重複防止、AIコンテキスト分離を適用しました。
- モバイルのローディングは、コンポーネントではなく動的ビューポートとセーフエリアを含む実際の画面全体を基準に修正しました。
- 分析の接続確認と実際のダッシュボードが異なる契約を使っていた問題を見つけ、完全なレポートとキャッシュの経路を一緒に検証するよう補いました。
この記事は、2026年8月24–26日の設計文書、Git履歴、運用課題をもとに書き直したHaru Spaceの開発回顧です。実際のWebhookアドレスや接続キー、チャットルーム・プロジェクトの識別子、認証情報、内部API、プロバイダー応答の原文は公開しません。
開発画面には緑色の状態表示が多い。接続済み、有効、リクエスト成功、準備完了、レスポンス200。
どの表示も必要だ。しかし、それぞれが示すのは一つの層での成功にすぎず、利用者が求めたことが最後まで完了したという意味ではない。
外部Webhookが200を受け取っても、チャットルームにメッセージがないことがある。ローディングカードが画面の高さを満たしていても、モバイルのsafe areaの下に黒い帯が残ることがある。分析の接続確認は成功しても、実際のトラフィックダッシュボードでは確認が必要だと表示され続けることがある。
三つの異なる問題が、同じ教訓にたどり着いた。
「接続済み」は設定の状態である。完了と言うには、ブラウザから始まり、API、保存台帳、外部プロバイダーを経て、利用者が見る最後の画面までを一つの流れとして確認する必要があった。
外部の市場シグナルをどのルームへ送るのか
投資メニューには、外部チャートサービスのWebhookシグナルを受け取る機能があった。当初、シグナルは投資アラート一覧にだけ保存された。利用者が望んだのは、特定のグループチャットでメンバーと一緒に見ることだった。
「チャットルームへ送る」は、単純なメッセージコピーではなかった。外部リクエストがどのグループとルームを選ぶ権限を持つのかから、設計し直す必要があった。
ペイロードがグループコードとルーム識別子を送れるなら、接続キーを知る外部の呼び出し元が送信先を変えられる。元のJSON全体をメッセージとして複製すれば、認証情報や任意の文章がチャットへ入り込む可能性がある。通常のボットメッセージとして保存すれば、外部入力がAIの最近の会話コンテキストに混ざり、指示のように働くこともあり得る。
私たちは、外部ペイロードから送信先を取り除いた。
- グループ管理者がHaru Space内で、自分が参加し、書き込める通常のチャットルームを選ぶ。
- サーバーはその選択を、公開される接続参照と結び付けて保存する。
- 外部リクエストはその接続の秘密を知っていることだけを証明し、グループやルームを指定できない。
- シグナル種別、銘柄、時刻、価格、出来高など、許可したフィールドだけを解析する。
- 任意のメッセージと不明なフィールドは、保存もチャットへの複製もしない。
- 結果は3言語の構造化された通知イベントにし、AIの会話コンテキストから除外する。
同じリクエストが再送されたときにメッセージが複数できないよう、正規化したシグナルの指紋を使って配信台帳を作った。台帳とチャットメッセージは同じトランザクションで確定した。リアルタイム画面更新は既存経路を再利用し、プッシュ通知は付加的なベストエフォート処理として分離した。プッシュが失敗してもWebhook本体を再実行せず、チャットメッセージを重複させなかった。
Webhookの成功基準はHTTPレスポンスではなかった。正しい接続が正しいルームに安全なイベントを一件だけ残し、そのイベントがAIへの指示として再利用されないところまでが成功だった。
コピーできるテンプレートも入力検証の一部だった
最初のシグナルテンプレートには構文エラーがあり、イベントフィールドも実際の分類に使われていなかった。そのため、異なるシグナルが同じ一般通知に見える可能性があった。
サーバーパーサーだけを直しても、利用者がTradingViewへ貼り付ける体験は改善しない。二つのシグナルそれぞれについて完全なJSONをアプリからコピーできるようにし、サーバー側でもeventと状態フラグの整合性を確認した。数値でない値や許可していないイベントは、黙って補正せず拒否した。
UIが提供するテンプレートとサーバー契約を同じテストで確認した。利用者がコピーした例は実際の入力として成功し、手作業で誤って変更したリクエストは安全に失敗する必要があった。
ローディング画面は画面全体ではなかった
同じ週、モバイルSafariで明るいローディング画面の下に黒い領域が現れた。ローディングコンポーネント自体は明るい背景で、画面全体の高さを占める設定にもなっていた。PCや一般的なモバイルプレビューでは、問題が分かりにくかった。
原因はコンポーネントの外側にあった。
文書ルートの標準背景は暗く、モバイルブラウザの動的ビューポートや下部セーフエリアが、固定オーバーレイの外にある文書背景を一時的に見せることがあった。デバイスの上端・下端領域まで使うためのビューポート設定も十分ではなかった。
解決は、ローディングカードにパディングを足すだけではなかった。
- ローディング中は文書ルートと画面の背景色を同じテーマへ同期した。
- 固定ビューポート、小さいビューポート、動的ビューポートの高さをフォールバック順に対応した。
- 上下左右のセーフエリアをローディング画面の実領域に含めた。
- overscrollで別のルート背景が現れないようにした。
- ローディング終了時には一時的なルート状態を片付けた。
検証基準も、コンポーネントのバウンディングボックスではなく、実際のモバイル画面で見えるすべての領域へ変えた。明るいテーマと暗いテーマ、異なるモバイルの高さで、ローディング画面、文書ルート、bodyの背景が一致するか確認した。
利用者が見る不具合は、コンポーネント境界の外で生まれることがある。エンドツーエンド検証にはDOM階層だけでなく、ブラウザが描画するビューポートまで含まれた。
グループ別トラフィックを自分たちで収集しないと決めた
Haru Spaceはグループプロジェクトを管理するため、グループメンバーも自分たちのプロジェクトが実際にどれほど訪問されているかを見たかった。別のプロキシや生ログ収集器を追加する方法もあったが、最初の段階には運用箇所とコストが増えすぎると判断した。
そこで、プロジェクトをホストするプラットフォームがすでに提供しているWeb Analyticsの集計APIを使うことにした。
接続設定はグループ管理にある既存のVercelカード内に置き、実際の指標は別のプロジェクトトラフィックグループメニューで見るようにした。接続を設定する人と、日常的にレポートを見る人を分けた形だ。
ダッシュボードが提供するのは、人による訪問の集計値だけだった。
- 期間別のページビューと訪問者
- 日別推移
- 上位ルートと参照元ホスト名
- 国とデバイス種別の分類
Haru Spaceは、元のIPアドレス、User-Agent、クエリ文字列、個別のリクエスト経路、生の訪問イベントを保存しなかった。ボットとAIクローラーは人の訪問指標に混ぜず、プロバイダーの別の観察画面へ案内した。
ダッシュボードを請求画面やセキュリティログの代わりとしても扱わなかった。月間ページビューの基準はグループへの参考通知であり、Vercelの課金上限を示すものではなかった。
同じ外部プロジェクトを二つのグループの数字として見せなかった
当時、Vercelの公開集計APIには、一つのプロジェクト内にある複数のホスト名を、必要な形でグループごとに分離できる契約がなかった。一つのVercelプロジェクトが複数グループのドメインを一緒に提供している場合、その合計をそれぞれのグループのトラフィックとして表示することはできない。
利便性のためにドメイン文字列から推測する代わりに、外部プロジェクト一つを一つのグループだけへ接続するようにした。プラットフォーム全体で同じプロジェクトの重複接続を防ぎ、所有権を移す場合には、元のグループの台帳と過去の集計を保持する別途承認済みの手順が必要だと決めた。
プラットフォーム管理者であっても、非公開グループのトラフィックを自動的に見られないようにした。インフラ接続を管理する権限とグループ詳細指標を読む権限を分け、現在のグループで有効なメンバーシップを再確認した。
数字は個人の会話ほど機密に見えないが、グループの成長と活動を明らかにする運用データである。集計値もグループ主権の境界内になければならなかった。
外部分析の障害をグループ作業の障害にしなかった
トラフィック画面を開くたびにプロバイダーAPIを呼び直せば、遅く無駄が多くなり、外部障害がそのまま利用者体験へ伝わる。
期間別集計には短いキャッシュを置き、複数のサーバーインスタンスが同時に更新しないようDBリースを使った。プロバイダー呼び出しが失敗した場合は、最後に成功した集計をstaleと表示して提供した。正常データが一度もないときだけ、状態に応じた空画面を表示した。
エラーも、認証が必要、権限不足、分析リソースの確認が必要、リクエスト制限、一時障害に分けた。プロバイダー応答の本文やリクエストURLは保存せず、復旧に必要な限られたコードだけを残した。
外部分析が止まっても、チャット、プロジェクト作業、デプロイは動き続ける必要があった。観察機能の障害を本来の作業失敗と取り違えないようにした。
接続確認は成功したのに、ダッシュボードは失敗した
運用上もっとも重要だった不具合は、利用者が設定を終えた後に現れた。
開発接続画面の接続確認は成功した。APIも正常に応答し、保存された接続と最小範囲の分析用認証情報も有効だった。それでも同じグループのプロジェクトトラフィック画面は、確認が必要だと表示し続けた。
原因は、接続確認と実際のダッシュボードが異なる契約を検証していたことだった。
接続確認は、短い期間の総訪問数一件だけを取得した。実際のダッシュボードは訪問数に加え、日別、ルート、参照元、国、デバイスの集計を一緒に解析した。外部プロバイダーは、直接流入や分類不能など、分類名が空の正常な行を返すことがある。Haru Spaceのパーサーは、その一行のためにレポート全体を不正な応答として破棄した。
二つを一緒に直した。
- 分類名がない行は、その分類表からだけ除外し、全体サマリーとほかの有効な行は維持する。
- 接続確認も実際のダッシュボードと同じ完全なレポートを取得し、キャッシュまで準備して初めて成功とする。
すべての値を緩く受け入れる変更ではなかった。日別時刻、ページビュー、訪問者数は、引き続き安全な整数契約で厳格に検証した。不正な数値を0に見せかけることもしなかった。失敗ログには、どの集計次元が一致しなかったかだけを残し、認証情報、フィルター、応答値は入れなかった。
小さなプローブの緑色を、機能全体の緑色として使ったことが問題だった。
認証情報も「Vercel接続」一つではなかった
当初は、デプロイとドメイン接続に使っていたVercel OAuth認証情報を、分析APIにも再利用しようとした。プロジェクトが正しく、プロバイダーのダッシュボードに訪問データがあっても、分析の照会は期待した結果を返さなかった。
調査すると、二つの機能で公式の権限範囲が異なっていた。デプロイ用の認証情報に分析権限も含まれるとは仮定できなかった。
分析には、現在のチームと単一プロジェクトだけに制限した、別の最小範囲の認証情報を使うよう分離した。保存前に実際に分析を利用できるか確認し、サーバーで暗号化して保管した。クライアントへ返すのは登録済みかどうかと更新時刻だけで、値の一部さえ再表示しなかった。接続プロジェクトが変わっても、認証情報を自動移行しなかった。
「同じプロバイダー」は「同じ権限」を意味しなかった。デプロイ、ドメイン、分析を一つの接続スイッチとして見ると、最小権限が崩れる。
エンドツーエンド検証のチェックリスト
この週の三つの事例から、共通の点検順序が残った。
1. 利用者が始めた入力は正確には何か
コピー用テンプレート、選択したチャットルーム、モバイルテーマ、分析プロジェクトを、実際のUI値で確認する。
2. サーバーが送信先と権限をもう一度決めているか
外部ペイロードやURLの任意識別子を信頼しない。現在のグループ、ルーム、プロジェクト所有権をサーバーで再検証する。
3. 台帳とキャッシュは重複・失敗をどう表現するか
同時再送は一度だけ保存し、失敗を成功で上書きせず、外部障害時には最後に成功した集計を区別して表示する。
4. 外部プロバイダーの実際の契約を最後まで使ったか
接続用プローブだけを見ない。利用者が見る完全なレポートと同じ認証・解析経路を検証する。
5. 最後の画面を実際の環境で見たか
コンポーネントテストだけでなく、モバイルビューポート、選択チャットのリアルタイムメッセージ、トラフィック画面の空・エラー・stale状態を確認する。
6. 付加機能の失敗が本体処理を再実行しないか
プッシュ、分析、進行イベントの失敗によって、Webhookメッセージやプロジェクト作業を重複生成しないようにする。
まだ残っている確認を「完了」とは書かない
この期間中に、外部シグナル配信、モバイルローディングの補完、トラフィックダッシュボードのコードは、検証とマージの段階まで進んだ。トラフィック接続確認と完全なレポートが一致しない原因も見つけ、契約を修正した。
しかしリポジトリの記録では、最終的な運用での実利用や実機確認の一部が、後続の観察項目として残っていた。そのため、この記事でも「すべてのフローを完全に検証した」とは書かない。自動テスト、ビルド、運用API状態が通ったことと、実際の利用者が選んだルーム・デバイス・プロジェクトで最後まで観察したことは分ける。
その区別自体が、この週でもっとも重要な成果だった。
緑色の状態を増やしたときではなく、それぞれの緑色が何を証明し、何をまだ証明できていないかを説明できるようになったとき、運用検証が始まった。
反応を残すと、重複防止のためランダムな識別子がこのブラウザーに保存される場合があります。
