Skip to content

StopFailure の hook で利用上限の停止をその場で記録・通知する(#456 の陽性対照を取りこぼさない) #468

Closed

Description

@hdknr

何が困るか

#456(利用上限の自動継続の陽性対照)は、上限を狙って起こせないので、実際の作業で自然に当たるのを待つしかない。いまは「上限に当たったら利用者が気づいて知らせる」に頼っていて、気づかなければ記録は埋もれ、止まったときに人間が「継続」と打てば陽性対照にならない(#454 の実データでは、10 月の停止の後は人間の「継続」が 23 件)。

count-usage-limit-resumes.py は後から全プロジェクトのトランスクリプトを走査できるが、いつ走らせればよいかが分からない(2026-10-27 13:36 JST に true を明示してから、2 回走らせてどちらも停止 0 件)。

検出の手段(本体 2.1.311 の文字列から。動かしていない)

  • hook のイベント StopFailure: 「Fires instead of Stop when an API error (rate limit, auth failure, etc.) ended the turn. Fire-and-forget — hook output and exit codes are ignored.」
  • 入力: hook_event_name: "StopFailure"・error・error_details(任意)・last_assistant_message(任意)
  • matcher は error に当たり、値に rate_limit がある(ほかに overloaded・authentication_failed・billing_error など)
  • user の settings に書けば、全プロジェクトのセッションで、止まったその場で発火する(はず)

案

  1. hook 用のスクリプト(scripts/ に置く。標準ライブラリのみ): StopFailure の入力(stdin の JSON)を読み、手元のファイル(例: ~/.claude/usage-limit-stops.jsonl)に 1 行追記し、macOS の通知を出す
    • 書くのは時刻・session_id・cwd のハッシュ(伏せる)・error・error_details の先頭だけ。last_assistant_message は書かない(案件の中身が入る)
  2. 設定の手順(docs/): user の settings に StopFailure(matcher rate_limit)の hook を足す形。登録は利用者が行う(リポジトリから利用者の settings を書き換えない)
  3. 数えるのは後から: hook は止まった時点しか知らない——自動継続が起きたかは count-usage-limit-resumes.py で数える。手元のファイルに記録があることが「数える時が来た」の合図になる
  4. 通知には「止まっても『継続』と打たずにリセットまで待つ(利用上限の自動継続の陽性対照(true を明示した状態)を取る手段が無い(#454 の BLOCKED) #456 の陽性対照のため)」を添える

しないこと

受入基準

  1. hook 用スクリプトと、その回帰テスト(変異テストを含む)。テストは偽の stdin と一時 HOME で回し、実環境の ~/.claude を対象にしないことをアサートで担保する
    • last_assistant_message・cwd の生の値を書かない
    • 入力が壊れていても(JSON でない・キーが無い)例外で落ちず、読めなかったことを 1 行で残す(fire-and-forget なので、落ちても誰も気づかない)
  2. check-all.py と CI に登録する
  3. 設定の手順を docs/ に置く(dev-loop-cruise.md の環境の点検の節から指す)
  4. 陽性対照: hook を登録した状態で、実際に StopFailure が発火した記録を 1 件取る。上限は狙って起こせないので、上限以外で起こせる StopFailure(例: 認証エラーなど)で発火の経路だけでも確かめられるかを先に調べる。確かめられなければ BLOCKED として残す
    • 記録が出ないことは「発火しない」の証拠にならない(hook が壊れていても出ない)——陽性対照が取れるまでこの仕組みの効き目を言わない

確かめていないこと

  • StopFailure が利用上限(claude.ai のセッションの上限)で実際に発火するか、error が rate_limit になるか
  • 自動継続の待ちの間に hook が何回発火するか(停止 1 回に 1 回か)

関連: #456(この仕組みで待つ相手)・#454・#457

Activity

  1. added a commit that references this issue on Oct 27, 2026
  2. hdknr commented on Oct 27, 2026

    @hdknr
    OwnerAuthor

    追記:認証切れ(OAuth のアクセストークンの期限切れ)も、cruise の停止として記録・通知する

    不在の cruise(/dev-loop:cruise <親> --absent)を回している最中に、次のエラーでセッションが止まった:

    Please run /login · API Error: 401 OAuth access token has expired. Re-authenticate to continue.
    (error type authentication_failed, HTTP 401)
    

    足したいこと:

    1. StopFailure の hook の対象に、認証切れ(authentication_failed・HTTP 401)を含める。 利用上限と分けて記録し(停止の種類・時刻・止まっていたもの)、通知の本文に「/login が要る」と書く——利用者が Remote Control から打てる。
    2. cruise の起動時の環境点検(check-unattended-env.py)に、認証の方式を足す。 対話ログインの OAuth で起動しているなら「途中で期限が切れうる。長期トークン(claude setup-token)か API キーで起動すると止まらない」と出す。
    3. 子のエージェントが 401 で落ちたときの扱いを references/ledger.md(再開)に書く。欠票は「反証なし」ではないので、認証が戻った後にやり直す(いまも手順 5 の「Verifier が報告を返さなかったら」で拾えるが、/code-review の分岐した実行は通知の status でしか分からない)。

    関連: #454・#466・#472

  3. added a commit that references this issue on Oct 27, 2026
  4. hdknr commented on Oct 27, 2026

    @hdknr
    OwnerAuthor

    追記は #473 に分けました(#471 は関門を通した範囲のまま)。なお認証切れの記録そのものは、#471 の登録(matcher なし)で error: authentication_failed として残ります。足りないのは通知の「/login が要る」と、plugins/ 側の 2 件です。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions