Skip to content

perf(autopilot): daemon の read-GraphQL 予算枯渇を緩和(listItems 重複排除 / Bot 分散 / 未観測時抑制)#1128

Open
smalruby3-editor-bot[bot] wants to merge 1 commit into
developfrom
fix/autopilot-graphql-budget
Open

perf(autopilot): daemon の read-GraphQL 予算枯渇を緩和(listItems 重複排除 / Bot 分散 / 未観測時抑制)#1128
smalruby3-editor-bot[bot] wants to merge 1 commit into
developfrom
fix/autopilot-graphql-budget

Conversation

@smalruby3-editor-bot

Copy link
Copy Markdown

Summary

autopilot daemon の GitHub API レート枯渇対策。read(個人)トークンの GraphQL 予算だけ
枯渇(実測 166/5000skipLowPriority 発動中)する一方、Bot の GraphQL と両トークンの REST(core) は
ほぼ満タン、という一点集中を緩和する。

調査で判明した消費源(1 interval サイクル ≈ 300 秒あたり、すべて read トークン):

呼び出し 概算 GraphQL pt 備考
tick()listItemsgh project item-list ~100 dispatch 判断用
tick()getIssueStates(closed 確認・バッチ) ~10
refreshBoardlistItems再取得 ~100 モニタ表示用・tick と重複
refreshBoardgetBoardEnrichment ~30–50 sub-issue + PR

→ ~240 pt/サイクル ≈ 2,880 pt/時(5000 の約 58%)を daemon 自身の定期 read が占め、POST /refresh /
POST /tick や人間の素の gh(read バケットは個人トークン=共有)で容易に枯渇していた。

Changes Made

  • B: listItems の重複排除tick が取得した item スナップショットを state.itemsCache に保存し、
    直後に走る refreshBoard は十分新しければ(既定 interval の半分以内)gh project item-list
    (~100 GraphQL pt)を撃ち直さず再利用する。POST /refreshforceFetch=true で最新を取り直す。
  • C: board 読み取りを Bot 予算へ振り分けrefreshBoard の読み取り(listItems キャッシュミス時 /
    enrichment / head-PR 補完)を project.boardToken()(既定 Bot)に回し、遊んでいる Bot GraphQL 予算と
    併用して実効予算を分散する。dispatch 判断の tick 系 read は従来どおり readToken
    AUTOPILOT_BOARD_READS=read で従来へ戻せる。
  • D: 未観測時の定期 refreshBoard を抑制 — 直近に GET /board が読まれていない間は定期の board 追従で
    read を撃たない(maybeRefreshBoardPeriodic)。トラッカー sticky が古くなり過ぎないよう、アップキープ
    間隔(既定 30 分)超過時のみ 1 度走らせる。POST /refreshPOST /tick 直後・起動時は常に実行。

判断ロジックは phases.js の純粋関数(shouldReuseItemsCache / shouldRefreshBoardPeriodic)に集約し、
I/O は daemon.js・トークン選択は project.js に限定(レイヤリング不変条件を維持)。

効果(概算)

  • 観測中(モニタを開いている): B により board の listItems が消える → read GraphQL が 約半減
    残る board read(enrichment)は C で Bot 側へ。
  • 未観測: D で定期 board read がほぼゼロ(30 分ごとのアップキープのみ)。read バケットは tick の
    listItems + getIssueStates(~110 pt/サイクル)だけになる。
  • read と Bot の 2 つの 5000/時バケットを併用するため、実効予算が実質倍増。

Test Coverage

  • 新規 tools/autopilot/test/board-budget.test.jsnode:test): shouldReuseItemsCache /
    shouldRefreshBoardPeriodic の境界(新鮮/期限切れ/forceFetch/未観測/アップキープ/ISO 時刻)を網羅。
  • tools/autopilot/test/daemon.test.js の GET /board テストに now を追加(新依存に追随)。
  • cd tools/autopilot && node --test: 419 pass / 1 fail。fail は packages/admin/.gitignore
    node_modules symlink 無視チェックで、本 PR と無関係・develop 既存(本 PR は tools/autopilot
    .claude/rules のみ変更)。

Docs

  • .claude/rules/autopilot/github-api.md を同期(board 読み取り = Bot の例外、listItems 重複排除、
    未観測時抑制の規約を追記)。

注意

daemon はコード・settings を起動時スナップショットするため、反映には daemon 再起動が必要
update autopilot)。

🤖 Generated with Claude Code

… refresh

read (personal) トークンの GraphQL 予算が枯渇(実測 166/5000・skipLowPriority 発動)する一方
Bot GraphQL と両者の REST(core) は健全という一点集中を緩和する。

- B: listItems の重複排除。tick が取得した item スナップショットを state.itemsCache に保存し、
  直後の refreshBoard は十分新しければ gh project item-list(~100 GraphQL pt)を撃ち直さず
  再利用する。POST /refresh は forceFetch で最新を取り直す。
- C: 俯瞰ボードの読み取り(listItems キャッシュミス時 / enrichment / head-PR 補完)を
  project.boardToken()(既定 Bot)へ振り分け、遊んでいる Bot GraphQL 予算と併用して実効予算を
  分散する。dispatch 判断の tick 系 read は従来どおり readToken。AUTOPILOT_BOARD_READS=read で復帰。
- D: 未観測時の定期 refreshBoard を抑制。直近 GET /board が無い間は board の read を撃たず、
  トラッカー sticky 維持のためアップキープ間隔(既定 30 分)超過時のみ 1 度走らせる。

判断ロジックは phases.js の純粋関数(shouldReuseItemsCache / shouldRefreshBoardPeriodic)に集約し
unit テストを追加。I/O は daemon.js に限定。.claude/rules/autopilot/github-api.md を同期。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants