第6章:過去から届く観測

ALT+TABをしただけで、日本語入力が消える
2026年7月4日の朝、奇妙な報告がメモに書き付けられました。ALT+TABでウィンドウを 切り替えただけで、日本語入力がオフになっている。キーを押し間違えたわけではありません。 何も入力していないのに、IMEが勝手に閉じているのです。
原因はすぐに見当がつきました。ALT+TABのタスクスイッチャーが表示されている間、OSは
選択用のUIウィンドウ(XamlExplorerHostIslandWindowのような一時的な窓)へ、ほんの一瞬
フォーカスを移します。ちょうどそのタイミングで起動していた観測処理(ImmCrossProbe)が、
この一時ウィンドウを対象にIME状態を読み取り、「オフ」という値を返しました。当時のこの
観測は「高信頼度」の扱いを受けていたため、現在の信念(desired_open)へそのまま採用され、
IMEエンジンをオフにするカスケードが走りました。
奇妙なのはここからです。この観測自体は、読み取った瞬間には嘘をついていませんでした。 一時ウィンドウのIME状態は、確かにオフだったのです。問題は、その観測が届いた ときには、フォーカスはもう別のウィンドウへ移っていたということでした。過去のある時点 では正しかった観測が、届いた時点ではもう別の文脈の下にいるのに、時刻だけを頼りに現在の 状態として採用されてしまう。
時系列に並べると、観測の対象と、その結果が使われる時点のずれが見えます。

最初は、見たことのない新種のバグに見えました。ALT+TABという操作自体は何百回、何千回と 繰り返されてきた日常的な操作であり、そこに潜む競合が今になって表面化したことも不思議 でした。しかし調べていくうちに、既視感がありました。似た構造の問題には、すでに5月の 時点で一度向き合っていたはずだったのです。当時のコミットログを掘り返すと、確かに同じ 主題を扱ったリファクタリングが4回も連なっていました。しかも、そのうち3回はわずか1週間の うちに集中していました。「解決した」はずだった問題は、実は解決していなかったのです。
この章は、なぜこの問題が2026年7月まで生き延びたのかを辿る章です。答えを先に 言うと、同じ問題に対する手当ては、5月から3か月近くにわたって何度も打たれていました。 にもかかわらず、7月4日にまた同じ顔で現れたのです。
信念を一か所に集めれば解決すると思っていた
時間を5月まで巻き戻します。当時向き合っていたのは、もう少し素朴な症状でした。
非同期に完了するIME操作(ImmCross経由のSendMessageTimeoutWなど)の完了通知が、
発行から数十〜数百ミリ秒遅れて届くことがあり、その間にユーザーが新しい意図を出していると、
古い応答が新しい意図を上書きしてしまうのです。当時の記録にはこうあります。
async ImmCross apply は SendMessageTimeoutW を含み数十〜数百 ms かかることがある。 その間にユーザーが新しい IME 操作を行うと、古い apply の async 完了 event が遅れて 到着し、新しい意図を上書きしてしまう。
SendMessageTimeoutWは、別プロセスのウィンドウへメッセージを送り、相手が応答するか
タイムアウトするまで待つWindows APIです。相手プロセスが忙しければ、応答は簡単に
数百ミリ秒遅れます。この遅れそのものを止めることはできません。止められるのは、
遅れて届いた応答をどう扱うかだけです。
このとき立てられた仮説は、単純でした。IME状態についての「信念」があちこちに分散して いるから、上書き事故が起きるのだ。ならば、信念を一か所に集めてSSOT(Single Source of Truth、単一の真実源)にすればよい。分散していた場所を数え上げると、確かに多すぎました。 フォーカス変更を検知するコード、非同期apply完了を受け取るコード、GJI(Google Japanese Input、Google 日本語入力)のポーリング結果を受け取るコード——それぞれが自分の判断で 「今のIME状態」を書き換えていました。IMM32(Input Method Manager)は、Windowsが長く 提供してきたIME連携の旧来APIです。TSF(Text Services Framework)は、その後継として Windowsが提供するIME連携の仕組みです。IMM32によるクロスプロセス検出、TSFの通知、GJI のI/O観測という複数の経路が、それぞれ別のタイミングで「今のIME状態はこうだ」と申告 してきます。どの申告を信じるかという判断が、コードベースのあちこちに散らばって しまうのは当然でした。この仮説のもとで、同じ構造物に 4回名前を変えながら実装をやり直すことになります。
改名遍歴——名前を変えるたびに、答えようとした問いが変わっていた
以下の4つの名前は、単なる呼び方の変遷ではありません。それぞれが「この構造物は何に 答えるべきか」という問いの立て直しでした。
| 名前 | 答えようとしていた問い | 隠れていた仮定 |
|---|---|---|
BeliefStore | 現在何を信じるか | 信念は一つに集めればよい |
ImeApplyLatch | 適用済みか | 適用履歴が次の判断を制御できる |
LastAppliedImeState | 最後に何を送ったか | 最後の命令が現在状態を代表する |
shadow_model | システムの現在状態は何か | SSOTなら競合はなくなる |
こうして並べてみると、4つの名前は退化しているようにも、進化しているようにも見えます。
最初のBeliefStoreは「信じる」という認識的な言葉を使い、次のImeApplyLatchは
「適用する」という行為の言葉に変わり、LastAppliedImeStateは「最後に何を送ったか」
という履歴の言葉に変わり、shadow_modelは「システムの現在状態」という存在論的な言葉に
戻ります。振り返ってみれば、毎回「この構造物の役割は何か」が問い直され、
少しずつ違う答えにたどり着いていたことになります。しかもこの問い直しは、思いつきで
起きたわけではありません。それぞれの改名の直前には、GJIがポーリング方式から
observer_poll経由の通知方式へ切り替わったことや、「適用ログ」と「真実源」を混同していた
ことへの気づきといった、具体的なコード側の変化がありました。以下の4つのコミットは、
その変化のたびに何が引き金になったかを示しています。
日付を並べると、最初の2つの改名がいかに急いで行われたかが分かります。

順番に見ていきます。
最初の一歩はd2e183f(5月22日18:43)でした。コミットメッセージにはこうあります。
refactor(Phase C): ImeBeliefStore で shadow_ime_on を Confirmed/Intended/Unknown に 型区別
- tsf/belief.rs: ImeOpenBelief enum と ImeBeliefStore struct を新規追加
- on_focus_changed() で belief.invalidate_on_focus_change() を呼び Unknown に降格
- notify_ime_open → record_intent (意図)、新規 record_observation (実測値) を追加
この時点で、すでに「意図」と「実測値」を型で分けるという発想に到達していたことが
わかります。ImeOpenBeliefという列挙型に、Confirmed(確認済み)・Intended(意図済みだが
未確認)・Unknown(不明)の3つの状態を持たせ、フォーカスが変わった瞬間には無条件で
Unknownへ降格させる。これは第5章で扱った「わからない」を型として持つという発想の、
IME状態版の応用でもありました。方向性としては悪くありません。ところが、この改良には
わずか42分しか寿命がありませんでした。同日19:25、f8dd8d4で次の変更が入ります。
refactor(Pass B): ImeBeliefStore → ImeApplyLatch、shadow_on の役割を明確化 Confirmed/Intended/Unknown の3値は GJI が observer_poll 経由になった後は役割が 重複していた。ラッチ(最後に apply_ime_open に渡した値)としての役割だけが残るため
Cell<Option<bool>>に単純化。
3値モデルを導入したその同じ日のうちに、それは「重複していた」として単純なラッチへ 後退させられています。ここで問いが変わりました。「現在何を信じるか」ではなく「最後に何を 適用したか」を答える構造物になったのです。
2日後の6baabf9(5月24日01:35)では、さらに問いが動きます。
refactor: ImeApplyLatch → LastAppliedImeState、shadow_ime_on → last_applied_ime_on 「最後に apply_ime_open で送った値のログ」と SSOT を区別するためリネーム。 Preconditions.ime_on が唯一の SSOT であることを doc comment で明示。
ここで、「適用ログ」と「真実源」は別物だと気づかれ、名前でその違いを明示する試みが
なされました。興味深いのは、このときのdocコメントが「唯一のSSOTは
Preconditions.ime_onである」と明記していたことです。つまりこの時点で、3回改名されてきた
当の構造物(shadow_ime_on→last_applied_ime_on)は、SSOTそのものではなく、SSOTの隣に
置かれた副産物にすぎませんでした。真実源はまったく別の場所に存在していたのです。この段階
では、「集める」という作業が、まだ本当に集め切れていませんでした。
この後退には、当時なりの理由がありました。GJIというコンポーネントがobserver_poll 経由で状態を渡すように変わったことで、3値のうち「Intended」が実質的に使われなくなって いたのです。使われない区分を残すより、実際に使われている「最後に何を送ったか」だけを 正確に保つほうが健全に見えました。しかしこの判断は、後から振り返ると別の意味を持ちます。 3値モデルは「複雑すぎたから」捨てられたのではなく、「まだ何を表現すべきかを決め切れて いなかったから」捨てられたのです。
最後の到達点はa4db93e(5月29日00:21)です。
refactor(state): Phase 3e — shadow_model を IME SSOT に昇格し belief.ime_on / ImeObservations を撤去 ImeBelief.ime_on・ShadowSource・ImeObservations・apply_ime_observations() は shadow_model.effective_open() に完全移行済みのため削除する。
ここでようやく、shadow_modelへの一本化が完了しました。分散していた信念の置き場所は、
たった一つの構造物に集約されたのです。Preconditions.ime_onという副次的なSSOTと、
last_applied_ime_onという適用ログという、2つに分かれていた真実は、この一回のリファ
クタリングでようやく1つに統合されました。実に7日間、4つの名前、2段階の権限移動を経て、
ようやく「信念の置き場所は1つである」という当初の目標そのものは達成されたことになります。
42分後の再改名は、単体で見れば笑い話に見えるかもしれません。しかしここで見るべき だったのは、名前を決め切れなかったのは、対象の責務を決め切れていなかったからだということ です。「信じる」「適用する」「最後に送る」「現在の状態を表す」は、どれも似て聞こえますが 別の約束です。3回作り直すまで、その違いには気づかれていませんでした。
いま振り返ると、この改名の連鎖は無駄な回り道ではなく、責務を少しずつ絞り込んでいく 過程そのものだったとも言えます。ただし、絞り込みの先に見つけた答え(shadow_modelへの 一本化)は、後で述べるように、本当に必要だった答えとは、まだ半分しか一致していません でした。コードの中で頻繁に名前が変わる構造物を見つけたら、それは実装が下手なのではなく、 その構造物の責務そのものがまだ固まっていない徴候だと考えてよいでしょう。
shadow modelは何を解決したか
ここで一度、この一本化の成果を正当に認めておく必要があります。shadow_modelへの
統合は失敗ではありませんでした。統合前は、IME状態を知りたいときにImeBeliefStoreの
フィールド、Preconditions.ime_on、ImeApplyLatchの残骸のうちどれを見ればよいか、
コードを読むたびに追い直す必要がありました。統合後は、見るべき場所はshadow_model
ただ一つになりました。信念の置き場所が一つに定まったことで、「どのフィールドを
見れば今の状態が分かるか」というコードレビュー時の混乱は明確に解消しました。この直後、
ADR-032(Architecture Decision Record、設計判断とその理由を記録しておく文書)の
4層分離(Intent/Observation/Transition/Barrier)が確立し、「observationが
intentを直接書き換えない」という構造的な保証が生まれます。当時のメモにはこう記録されて
います。
IME 状態モデル
shadow_modelにおいて、Observer 系の event (ObserverReported) はdesired_openを直接書き換えてはいけない。observationsに記録するのみで、desired を変えられるのは UserIntent のみ。
つまり、「誰が何を書き換えてよいか」という権限の問題は、この時点で確かに解けていました。 これは本書全体を通じて何度も参照される、重要な前進です。統合前後で、コード上の問いかけ方 も変わりました。
// 統合前: どのフィールドを見ればよいか自体が曖昧だった
belief.confirmed_open.unwrap_or(latch.last_value)
// 統合後: 問い合わせ先は一つだけ
shadow_model.effective_open()
問い合わせる側が「どこを見ればよいか」を判断する必要がなくなったこと自体が、統合の
価値でした。バグ報告を受けたときの調査の速さにも、この統合は直接効いています。統合前で
あれば、まず疑うべきフィールドの候補を複数思い浮かべ、一つずつ当たっていく必要が
ありました。統合後は、まずshadow_modelの状態遷移だけを追えばよく、調査の入口が
一つに絞られました。
問い合わせ先が一つになったことは、テストの書き方にも影響しました。shadow_modelが
受け取るのはIntent・Observationという値であり、返すのはeffective_open()が示す一つの
状態です。入力の列を与えて出力を確かめるという形にまとまったことで、実際のIMM32やTSFを
呼び出さずに、観測の順序を変えたときの挙動をテストコードの中だけで再現できるように
なりました。統合前のように、複数のフィールドを手で読み比べて確認する必要がなくなったのは、
コードレビューだけでなく、実機なしで書けるテストの範囲にも同じ効果をもたらしています。
同じ時期、もう一つの小さな防御も別の場所に用意されていました。非同期apply完了イベントが 古い場合に、それを黙って採用しないための仕組みです。窓口の整理券を思い浮かべてください。 番号を発行した時点と、あとで呼び出される時点とで、同じ番号かどうかを確かめれば、 別の用件の順番を誤って処理することはありません。各apply処理にも発行した瞬間の通し番号 (世代番号)を持たせておき、完了イベントが届いたときに、それが今も保留中の番号と 一致するかどうかを確認します。
fn on_apply_succeeded(&mut self, generation: u64, target: bool) {
if self.pending.as_ref().map(|p| p.generation) != Some(generation) {
log::debug!("stale apply result ignored generation={generation}");
return;
}
self.applied_open = Some(target);
self.pending = None;
}
連打的な操作、たとえばIME切り替えキーを短時間に2回押した場合の上書き事故には、この 確認だけで十分でした。ただしこの世代番号が数えていたのは 「何回目のapply命令か」であって、「どのフォーカス下で発行されたapply命令か」ではありません。 apply命令の世代とフォーカスの世代は、別の軸でした。したがって、フォーカスが変わらないまま apply命令だけが連打される場面には効きますが、ALT+TABのようにフォーカス自体が入れ替わる 場面には、そのままでは効きませんでした。世代を数えるという発想はすでにここにあったのに、 何の世代を数えるべきかが、まだ取り違えられていたのです。
それでも5週間後、同じ問題が別の顔で現れた
しかしshadow_modelへの統合から5週間以上が経過した7月2日〜4日、冒頭で述べたALT+TABの
バグが報告されました。最初にログを見たときは、shadow_modelのどこかにまた新しい
書き込み経路が紛れ込んだのだろうと疑われました。しかし探しても、権限違反は見当たりません
でした。desired_openを書き換えていたのは、確かに正規のUserIntent経路でした。書き換えた
値そのものが、間違っていたのです。ログを遡ると、その値の出所はImmCrossProbeという
観測でした。観測の内容自体は誤っていません。誤っていたのは、その観測が届いた時点で、
もうその観測が指していたウィンドウにフォーカスが残っていなかったという事実のほうでした。
最初に打たれた対策(以下、Fix Aと呼びます)は、時間による猶予期間でした。「直前まで
shadow_onがオン相当で、かつ観測が届いてから200ミリ秒未満なら、その観測は疑わしいと
みなす」という条件です。この対策は機能しました。しかし、当時のADRに残された「残存する
制約」の記述は、この対策が抱える構造的な問題を率直に認めています。
Fix A は機能したが、時間ベース競合という構造的な問題を抱えていた:
- CPU 負荷次第で 200ms を超えてしまう可能性
- 3 箇所に同じ
shadow_on && probe_age_ms < SHADOW_GRACE_MSが複製された- 「この観測は信用できるか」という判断がコードベースに分散した
この3行を読んだとき、既視感で背筋が寒くなりました。「3箇所に複製された」「判断が
コードベースに分散した」という言い回しは、5月にBeliefStoreを作った動機とまったく
同じ言葉でした。今度は信念の置き場所ではなく、信念の鮮度をどう判定するかという
ロジックが、同じように複製され、分散していたのです。5月には「今のIME状態はこうだ」と
申告する経路が複数箇所に散らばっていたのに対し、7月には「この観測は今も信じてよいか」を
判定するロジックが複数箇所に散らばっていました。対象が変わっただけで、構造は同じ形を
していたのです。
ここで露わになったのは、痛烈な事実でした。3回の改名を重ねて解決したはずの 「信念の分散」という問題が、形を変えて戻ってきていたのです。今度は信念の置き場所ではなく、 「この観測を信じてよいかという判定」が3箇所に複製されていました。集約すればSSOT問題は 解決するという当初の仮説は、不十分だったのです。単一の場所に集めることと、古い観測を 弾くことは、まったく別の問題でした。前者を3回やり直しても、後者は5月29日の時点でまだ 何一つ解決していなかったのです。
Fix Aが動いていた数日間は、一度「これで直った」ように見えました。ALT+TABを繰り返しても 再現しなくなったからです。しかし、この安心は正しくありませんでした。直ったのは 「200ミリ秒以内に古い観測が届く場合」だけであり、それより遅れて届く場合や、逆に本当に 新しい観測を誤って疑わしいと判定してしまう場合は、まだ手つかずのままでした。Fix Aは 症状を隠しただけで、原理を説明していなかったのです。
正直なところ、このときの落胆は小さくありませんでした。shadow_modelへの統合を終えた
時点では、信念の管理はこれで整理し切ったと考えられていたからです。しかし5週間後に
現れたバグは、整理が終わっていたのは「信念をどこに置くか」だけであり、「その信念を
いつまで信じてよいか」は最初から範囲の外にあったことを教えてくれました。
SSOTが答えられなかった問い
shadow_modelは「システムの現在状態は何か」という問いには、確かに答えられるように
なりました。しかし、そのために必要なもう一つの問いには、まだ答えられていませんでした。
その信念はどのフォーカス世界で得られたものなのか。そして、そのフォーカス世界は、いまも
まだ続いているのか。
probe_age_ms < 200msという条件は、この問いに対する近似的な答えでした。「最近届いた
観測なら、たぶん同じ世界のものだろう」という賭けです。ADR-077に残された比較の記述は、
この近似がなぜ壊れやすいかを言い当てています。
| 旧 shadow grace | 新 epoch 照合 | |
|---|---|---|
| 判定基準 | probe_age < 200ms(近似) | フォーカスが「同じ」か(正確) |
| CPU 負荷時 | 200ms 超で素通りのリスク | 時間に無関係 |
| 重複コード | 3 箇所にコピー | 1 箇所に集約 |
| 診断 | なし | 棄却理由を記録するカウンタあり |
時間は連続量であり、CPU負荷やメッセージキューの遅延によっていくらでも伸び縮みします。 「200ミリ秒未満」という境界線は、忙しいマシンの上ではあっという間に踏み越えられて しまいます。
7月4日06:58、604cf99で決定打が投入されました。
feat(probe-admission): FocusEpoch + ImmLikeTicket で観測受理層を新設 probe の spawn 時にフォーカスエポック(u64)をキャプチャし、完了時に照合することで 「spawn 後にフォーカスが変わったか」を時間ベースの競合なしに正確に判定できる probe_admission モジュールを追加。
ここで発想が変わりました。「鮮度」は経過時間の近似ではなく、同一の文脈(フォーカス)の下で
発行されたかという、離散的な世代の一致として扱われるようになったのです。時間を数えるのを
やめて世代(整数)を照合するように変えた瞬間、問題は近似ではなく正確な判定になりました。
ただし、この受理層がすべての観測経路を覆ったわけではありません。TSF・GJI・HwndCache
経由で届く一部の観測は、この時点でもエポック照合の対象外のまま残され、代わりに
3000ミリ秒という時間ベースの鮮度ウィンドウで足切りする設計にとどまっています。
「FocusEpochによって鮮度の問題はすべて解けた」と言い切るのは、まだ早いのです。
この転換の実装は次章以降で扱いますが、ここで確認しておくべきなのは、shadow_modelが
本当に足りなかったものの正体です。
SSOTは「現在何を信じるか」には答えます。しかし「その信念はどのフォーカス世界で得たものか」 「まだその世界が続いているか」には、そもそも答える設計になっていませんでした。信念を 一か所へ集めることは、文脈の世代を記録することの代わりにはならなかったのです。
この問いが見落とされやすいのには理由があります。コードレビューの場でshadow_modelを
見せられれば、誰もが「信念は一箇所に集まっている」ことを確認できます。しかし「この信念が
どのフォーカス世界の産物か」は、shadow_modelの型定義を眺めるだけでは分かりません。
欠けているものは、フィールドの不足としては見えず、フィールドが記録していない時間軸として
しか現れないからです。存在しないフィールドの不在に気づくのは、存在するフィールドの誤りに
気づくよりも、はるかに難しいことでした。
具体的に考えると分かりやすくなります。エディタAにフォーカスがある間に観測処理を起動し、 その処理がまだ完了しないうちに、ユーザーがALT+TABでエディタBへ切り替えたとします。 時間ベースの判定は「観測が届くまでに何ミリ秒かかったか」しか見ません。届くのが速ければ、 それがエディタAの世界のものであってもエディタBの世界のものであっても、区別せずに採用 します。しかし本来知りたいのは経過時間ではなく、「その観測はエディタAの世界とエディタB の世界のどちらに属するか」という、離散的な所属の問題でした。時間はこの所属を表現する ための、不完全な代役に過ぎなかったのです。
振り返ると、非同期apply完了のための世代照合(前節のコード例)も、同じ不足を抱えていました。 あの世代番号は「何回目の命令か」を正確に数えていましたが、「どの文脈で発行された命令か」 は数えていませんでした。命令の世代とフォーカスの世代は別の数え方であり、片方を照合できて いるからといって、もう片方が照合できているとは限りません。集約と改名が繰り返された間、 本当に足りなかったのは置き場所ではなく、この「どの世代を数えるべきか」という設計判断 そのものだったのです。
Physical AIへの接続
鮮度は一度の検査で終わる属性ではない
shadow_modelが答えられなかった問いは、ロボティクスの世界では以前から知られている
問題と同じ形をしています。ロボットのセンサーが返す値も、届いた時点ではすでに過去の
ものです。遅れて届く観測(delayed measurement)や、順序が入れ替わって届く観測
(out-of-sequence measurement)を扱う議論は、この分野で以前から蓄積されています。センサー
融合の実装で「信念(belief)を1つの状態推定器へ集約する」設計自体は、ロボティクスでも
古くから行われてきました。しかしそこでも、集約した状態推定器が「いつの観測に基づいて
いるか」を保持できていなければ、古いセンサー値が新しい状況を上書きする同じ事故が起こり
えます。信念を集約することと、集約した信念の鮮度を管理することは、ロボティクスにおいても
別の設計判断として扱われるべきものです。
したがって、本書がここで主張したいのは「遅れて届く観測がある」という事実そのものでは ありません。それは既知の問題です。本書が示したいのは、「この観測は今の文脈でまだ有効か」 という判定を、時間の近似ではなく、世代の照合という離散的で正確な仕組みに置き換えた という、実装レベルの規律です。信念を一か所に集めることと、観測の所属世代を照合すること は、似た言葉で語られがちですが、まったく別の設計判断です。この2つを混同したまま3回の 改名を重ねたことが、本章で辿った回り道の正体でした。
もう一点、注意すべき限界があります。観測を受理する層で世代を照合できても、受理した観測を 実際に不可逆な作用へつなげるまでの間に、もう一度世代が変わってしまう可能性は、この設計 だけでは塞がれていません。鮮度は一度検査すれば終わる属性ではなく、不可逆な作用へ進む すべての境界で、繰り返し再検証されるべき契約です。この規律は、観測を受け取る側だけでなく、 観測に基づいて実際に何かを実行する側にも同じ強度で適用されて初めて意味を持ちます。信念を 集約する設計だけを見て「もう鮮度の問題は解決した」と判断してしまうと、この2つ目の適用を 見落としたまま次の作業に進んでしまいます。この急所については、後の章であらためて扱います。
設計原則
観測には「いつのものか」が付いていないと、過去が現在を書き換える。
この原則が適用できるのは、非同期に届く観測や応答が、発行から到着までの間に、判断の 前提となる文脈(フォーカス・セッション・接続先など)が変わりうる場面です。単一プロセス内の 同期処理のように、発行と適用の間に文脈が変わりえない場面では、この原則を適用する必要は ありません。逆に、文脈が変わりうるのに「めったに変わらないから」という理由でこの原則を 省略すると、本章で見たように、発生頻度が低いぶん原因の特定がかえって遅れます。
実装の形としては、観測を発行した時点で文脈を表す世代番号を記録し、観測を採用する時点で 現在の世代番号と照合します。一致しなければ棄却します。BeliefStore→ImeApplyLatch→ LastAppliedImeStateという3回の改名は、「どこに集約するか」を探す試みでした。しかし本当に 必要だったのは集約場所ではなく、この観測は今の文脈でまだ有効かを照合する仕組みでした。 時間(ミリ秒)で近似すると、負荷や遅延で簡単に破れます。世代(整数)で照合すると、時間に 依存せず正確に判定できます。
ただしこの原則は、観測を受け取る場面だけを保証します。観測を受理した後、実際に不可逆な 作用へ進むまでの間に、文脈がもう一度変わってしまう場合があります。その間隙をこの原則は まだ塞いでいません。