2026-08 レビューの残件トリアージ (#243 ) で #237 (タブ凍結時の二重インスタンス) を検証した際に見つかった、その前提となる潜在バグ 。#237 の段階 2 を設計するには先にこちらを決める必要があるので分離して起票する。
事実
packages/editor/src/services/SessionStorageService.ts の次の 3 メソッドは、リポジトリ全体で呼び出し元がゼロ (grep 確認済み):
メソッド
位置
本来の役割
updateSessionActivity()
:289-297
lastActiveAt の heartbeat
markSessionInactive()
:302-316
isActive を false に戻す
getInstanceId()
:386-390
インスタンス同定
結果として session.isActive は createSession / resumeSession で true にされるだけで、false に戻る経路が存在しません 。
さらに resumeSession (:250-268) は:
session . isActive = true ;
session . instanceId = this . instanceId ; // :259-260 — 既存値を無条件で上書き
既存の instanceId との照合を一切せずに上書きします。
なぜ問題か
「このセッションを今このタブが握っているか」を判定する材料が実質死んでいます。#237 の本質的な修正 (二重インスタンスの検出) は「元のタブがまだ生きているか」の判定を必要としますが、その判定基準となる isActive / lastActiveAt が機能していないため、#237 の段階 2 だけを先に実装できません 。
あわせて確認された関連事実 (#237 側)
SingleInstanceGuard.instanceId (:29) と SessionStorageService.instanceId (:48) は別々の crypto.randomUUID() 。修正時にどちらを権威にするか設計判断が要る
appendEvent の ConstraintError は :535-545 で preventDefault() + stopPropagation() してログも警告も出さずに握り潰される 。事故が起きても完全に不可視
対応案
可視化だけ先に (安価): ConstraintError の握り潰しに console.warn + 既存イベントとの hash 比較を足す。1 ファイル ~20 行。今は完全に無言なので、これだけでも価値がある
ライフサイクルの復活 : updateSessionActivity を実際に配線 (heartbeat) し、markSessionInactive を beforeunload / タブクローズで呼ぶ。instanceId の権威を 1 つに寄せる
2 の上で [editor] タブ凍結時に SingleInstanceGuard をすり抜けた二重インスタンスがチェーンを壊しうる #237 段階 2 (resume 時の照合と read-only フォールバック) を実装
死んだ API を消す 選択肢もありますが、#237 の解決に必要なのでまず配線を検討すべきです。
関連
2026-08 レビューの残件トリアージ (#243) で #237 (タブ凍結時の二重インスタンス) を検証した際に見つかった、その前提となる潜在バグ。#237 の段階 2 を設計するには先にこちらを決める必要があるので分離して起票する。
事実
packages/editor/src/services/SessionStorageService.tsの次の 3 メソッドは、リポジトリ全体で呼び出し元がゼロ (grep 確認済み):updateSessionActivity():289-297lastActiveAtの heartbeatmarkSessionInactive():302-316isActiveを false に戻すgetInstanceId():386-390結果として
session.isActiveはcreateSession/resumeSessionでtrueにされるだけで、false に戻る経路が存在しません。さらに
resumeSession(:250-268) は:既存の
instanceIdとの照合を一切せずに上書きします。なぜ問題か
「このセッションを今このタブが握っているか」を判定する材料が実質死んでいます。#237 の本質的な修正 (二重インスタンスの検出) は「元のタブがまだ生きているか」の判定を必要としますが、その判定基準となる
isActive/lastActiveAtが機能していないため、#237 の段階 2 だけを先に実装できません。あわせて確認された関連事実 (#237 側)
SingleInstanceGuard.instanceId(:29) とSessionStorageService.instanceId(:48) は別々のcrypto.randomUUID()。修正時にどちらを権威にするか設計判断が要るappendEventの ConstraintError は:535-545でpreventDefault()+stopPropagation()してログも警告も出さずに握り潰される。事故が起きても完全に不可視対応案
console.warn+ 既存イベントとの hash 比較を足す。1 ファイル ~20 行。今は完全に無言なので、これだけでも価値があるupdateSessionActivityを実際に配線 (heartbeat) し、markSessionInactiveをbeforeunload/ タブクローズで呼ぶ。instanceIdの権威を 1 つに寄せる死んだ API を消す選択肢もありますが、#237 の解決に必要なのでまず配線を検討すべきです。
関連