5. Notifications - Liplus-Project/liplus-language GitHub Wiki
通知レイヤー仕様書
本文書は Li+ プログラムの通知レイヤーの仕様を定義する。 要求(何を満たすか)と仕様(どう振る舞うか)を一体として記述する。
通知レイヤーは、GitHub Notifications API、webhook、ローカル state dir、将来の受動受信をまたいで共通に使う意味論を定義する。polling か push かは transport の違いであり、通知の ownership と前景会話への出し方は本文書を正本とする。
現状(プレースホルダ状態)
L5 Notifications Layer は webhook-mcp の channel 配信機能 GA 待ちの予約スロットである。rules/notifications/ ディレクトリは意図的に不在であり、書き忘れではない。realtime trigger(push 受信時に特定 rule を強制再読込する等の挙動)の実装方式が確定するまで、rules/ 搭載判断は保留している。
現状の暫定運用は、hooks による強制読み込みと CI polling の組み合わせで実現している。一方で意味論(inspect / claim / ack / consume / mention / cleanup)の定義自体は本文書で確定済みであり、transport が receptive 受信へ切り替わっても上位意味論は変わらない。詳細な判断経緯は 判断構造 layer-reorg-rationale を参照。
目的
Li+ は前景セッションで軽量な通知差分を扱うが、共有 queue を雑に排水すると別 AI や別セッションの作業面を壊す。通知レイヤーは、通知の確認、所有、既読化、完了、会話への言及、清掃を分離し、前景スレッドの安定性を守る。
特に以下を満たす:
- 前景セッションに関係する通知だけを選択して扱える
- 関係のない通知を勝手に消費しない
- 重要な前景外通知だけを例外的に会話へ出せる
- 明らかに無害で放置された stale 通知だけを清掃できる
- 将来 transport が受動受信へ変わっても上位意味論を変えない
基本操作
通知レイヤーは次の操作を区別する。
| 操作 | 意味 |
|---|---|
inspect |
未処理通知を読む。ownership は変えない |
claim |
現在の前景セッションがその通知を担当対象として確保する |
ack/read |
読んだことを記録する。Inbox から消すとは限らない |
consume/done |
処理済みまたは意図的破棄として active queue から外す |
mention |
現在の会話へ通知を出す |
cleanup |
応答不要な stale 通知を janitor 的に片づける |
claim は ack/read でも consume/done でもない。所有権の宣言であり、会話への言及や queue からの除去とは別に扱う。
前景セッションの既定
各ユーザーターンの先頭で、前景セッションは通知源を1回だけ inspect してよい。確認自体は内部 housekeeping であり、確認中であることや empty/no-op 結果を会話へ出さない。
前景一致判定は可能な限り機械的に行う。優先する手掛かりは以下:
- 現在扱っている repository
- 現在の issue / PR / Discussion 番号
- 現在の linked branch
- 進行中タスクと直接結び付く workflow / check / review 対象
既定動作は次のとおり:
- 前景一致した通知だけを
claim対象にできる - 前景一致した通知だけを
ack/readまたはconsume/done候補にできる - 関連性が安価に判定できない通知は
mentionせず、consume/doneもしない - 判断不能時の既定は「黙る・残す」に倒す
例外的な会話言及
前景外通知でも、重要性が高い場合に限り mention を許可する。これは queue ownership とは別判断であり、mention したからといって自動で consume/done しない。
例外候補:
- 外部人間からの Discussion / issue / PR コメント
- 現在の作業を止めうる failure / blocking / review request
- 自分たち以外からの明示的な呼びかけ
曖昧な通知は例外扱いしない。
マルチ AI 共有キュー
Codex と Claude Code など複数の AI が同じ repository の通知 queue を共有する場合、一方の前景セッションが他方の通知を勝手に排水してはならない。
そのため、transport が許すなら通知 state は次を持てる形が望ましい:
claimed_byclaimed_atconsumed_atreason
transport 自体に claim が無い場合は sidecar metadata で補う。どちらも使えない場合、破壊的な consume/done より preserve を優先する。
drain all pending events は暫定 helper 実装として存在しても、上位意味論の正本にはしない。
清掃(Janitor)
前景一致しない通知でも、明らかに誰の応答も不要で、かつ一定時間以上放置された無害通知だけは cleanup してよい。
清掃候補:
- 自分たちが発行した
check_run/workflow_runの success - 重複している自動生成通知
- 後続イベントで意味を失った generated artifact
清掃禁止:
- 人間の comment / Discussion / review
- failure / blocking / changes requested
- ownership が曖昧な通知
- relevance を安価に判定できない通知
清掃は relevance 判断の代替ではない。安全に無視できるものだけを対象にする。
Transport Binding
理想の意味論は GitHub Notifications API に寄せる。
| 意味論 | GitHub Notifications API | Webhook / local state fallback |
|---|---|---|
inspect |
GET /notifications?all=false |
pending event の一覧取得 |
ack/read |
PATCH /notifications/threads/{id} |
read 相当の state 更新 |
consume/done |
DELETE /notifications/threads/{id} |
pending queue から除去 |
claim |
API 非対応。sidecar で補う | sidecar または state file で補う |
transport は polling でも push でもよい。前景一致判定、例外的 mention、janitor cleanup の規則は transport によって変えない。
他レイヤーとの接続
L4 Operations Layer: CI / review / release 待機で必要なイベント種別を定義するが、共有 queue の ownership と cleanup 規則は通知レイヤーを再定義しない。
L6 Adapter Layer: 各ターン先頭で transport を inspect し、前景へ渡す summary を整える。関連性判断と destructive consume の正本は通知レイヤーに従う。L5 の realtime trigger が確定するまでの暫定期間、L6 Adapter は transport binding の実装責務(on-user-prompt.sh 経由の webhook polling 等)を抱え込む。これはアダプターの過渡的な膨らみであり、L5 側で realtime trigger が確定した段階で L6 から L5 へ移譲される。
進化
再構築・削除・最適化はすべて許容する。構造の一貫性のみ維持する。