feat: 非同期送信モード(send_mode: async)を追加 - #7
Open
y-oga-819 wants to merge 10 commits into
Open
Conversation
非同期送信モード(send_mode: async)の土台として、config に
send_mode フィールドと getSendMode() を追加する。未設定・不正値は
"sync" にフォールバックし、既存ユーザーの同期挙動を後方互換で維持する(HC-1)。
sendIngest の fetch に AbortSignal.timeout(SEND_TIMEOUT_MS≈30s) を付与し、
ハングした送信が後続 Phase の per-session ロックを長時間保持するのを防ぐ
(HC-6 の前提)。MAX_LOCK_MS(60s) > SEND_TIMEOUT_MS の不等式の土台となる。
タイムアウト/Abort は既存の catch で {ok:false} に正規化される。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
既存実装(cursor.ts 等)のコメント密度に合わせ、設計書を読まないと わからない情報やコードから自明な説明をコメントから外す。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
非同期送信で同一セッションの送信を直列化するため、ディレクトリ存在を ロック実体とする per-session ロックを実装する。保持1+待機1 の上限で worker の積み上がりを防ぎ、超過分は待機者の cursor→末尾読込が回収する。 ロックは pid 死亡(即時)または経過時間(MAX_LOCK_MS=60s)で stale と 判定して自己回復する。MAX_LOCK_MS は送信タイムアウト(30s)より大きく取り、 送信中の生存 holder を誤って奪わない。 stale 奪取は取得ごとに一意な instanceId を持たせ、退避直前に同一インスタンス かを再検証することで、別 worker が再取得した新 holder を誤って奪う二重保持を 防ぐ。再検証と rename の間に残る極小窓で万一二重保持が起きても、欠落は カーソル+サーバ冪等が防ぐ(送信2本に留まりデッドロックしない)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
非同期送信の本体を追加する。send.ts の送信ロジックを process.exit に 依存しない純送信部 runSend に分離し、sync(hook)/manual は wrapper が 従来どおりの exit code とログを担う(後方互換を維持)。 worker はロック取得後に read→送信→saveCursor を行い、解放は saveCursor 後の finally で実施する(read はロック取得後・解放は cursor 前進後、の 順序不変条件を担保)。ロックが取れなければ送信せず終了し、待機者の cursor→末尾読込が取りこぼし分を回収する。 ロック解放は自分が保持している場合のみ削除するよう変更した。送信が長時間 ハングして別プロセスにロックを奪取・再取得された後でも、新しい保持者の ロックを誤って削除しない。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
send_mode が async のとき、hook 経由の send は payload を env に載せた detached worker(隠し __send-worker サブコマンド)を spawn して即 exit する。 HTTPS 送信を hook の critical path から外し、Claude Code の応答性を 外部サーバのレイテンシから切り離す。 worker 起動は process.execPath + process.execArgv + process.argv[1] で 構成し、dev(tsx loader を execArgv が保持)/ 本番(dist の素の node)を 分岐なく解決する。env キーは WORKER_ENV 定数で spawn 側と read 側を共有する。 async では UserPromptSubmit の 10 秒待機を行わない(未書き込み分は次の 発火がカーソルから拾う)。sync・手動送信は従来どおり同期送信のまま。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
async 送信で spawn が失敗(同期 throw / 非同期 error)しても hook が クラッシュしないよう、try/catch で sync 送信へフォールバックし、子プロセスに error ハンドラを付ける。fork 資源枯渇などの異常時でも hook 契約(落とさない・ バッチを捨てない)を守る。 並行 stale 奪取テストは固定 sleep で待機者の生存を仮定していたため、高負荷で spawn 間隔が広がると勝者が先に exit して別プロセスが stale 再取得し稀に複数 勝者になっていた。sentinel ファイルで全 racer の取得試行が出揃うまで全員を 生存させ、holder 一意性を atomic mkdir のみに依存させて決定論化した。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
init / on に --async を追加し send_mode を config に保存、doctor で現在の send_mode を表示する。送信モードの切替を CLI から行えるようにする。 send_mode の保存は persistSendMode で実効 config(tree 内の local を優先、 無ければ global)に書き込む。これは送信時に loadConfigWithFallback が読む ファイルと一致するため、project-local 設定のみの環境でも切替が確実に効く。 cli/CLAUDE.md に send_mode(sync/async)と --async 導線の説明を追記。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
spawn() の launch 失敗は throw ではなく非同期の 'error' イベントで 報告されるため、try/catch では捕捉されず「同期フォールバックする」 というコメントは誤り。実際は cursor が HTTP 200 でのみ進むため、 worker が起動失敗しても次回発火でリトライされ無害である旨に修正。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
初回送信時の `git remote get-url` / `git branch --show-current` は timeout なしで実行しており、git がハング(stuck .git/index.lock 等) すると holder ロックを MAX_LOCK_MS 超で保持し、後続発火が stale 判定で 二重 holder 化する恐れがあった。5s の timeout を付与。超過時は既存の catch→null に流れ、git 情報なし(非 git リポジトリと同等)として送信する。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
並行ロックテストが生成する __lock_racer__.ts を cli/src/send/ 配下に 書き込んでいた(gitignore 対象外で、SIGKILL 時に残留・read-only チェックアウトで失敗しうる)。スイート他箇所と同様 tmpHome に出力し、 lock.ts を絶対 file URL で import するよう変更。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
y-oga-819
marked this pull request as ready for review
June 15, 2026 07:15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
opt-in の非同期送信モード(
send_mode: async)を追加します。async では hook 経由のsendが detached worker を spawn して即 return し、HTTPS 送信を Claude Code の critical path から外します。worker は per-session ロックで同一セッションの送信を直列化し(サーバ負荷 ≤ sync)、ロックは pid / 経過時間で stale 自己回復します。デフォルトは sync のままで既存挙動は不変です。使い方
agentrace init --url <url> --asyncagentrace on --asyncagentrace doctorのSend mode行--claude-session-id)は常に同期フロー(async)
graph LR CC[Claude Code hook] -->|同期・高速| S["npx agentrace send"] S --> CFG{config.send_mode} CFG -->|sync| SYNC["従来どおり同期送信"] CFG -->|async| SP["spawn detached worker → exit 0"] SP -.->|待たない| CC SP ==> W["__send-worker (短命・per-fire)"] W --> LK["per-session lock 取得"] LK --> ST["sendTranscript: getNewLines→sendIngest→saveCursor"] ST --> SV[(AgenTrace Server)]正常系シーケンス(同一セッションに発火が連続したとき)
保持1+待機1 の上限で worker の積み上がりを防ぎ、drop された分は待機者の cursor→末尾読込が回収する。
検証
cd cli && npm test→ 76 passednpx tsc --noEmit→ clean