Skip to content

fix(agent): ポートが使われていたら 2 で終わり、選局の途中で閉じられても元の失敗を返す - #2793

Merged
5ym merged 1 commit into
mainfrom
fix/agent-stop-flake
Nov 14, 2026
Merged

5ym merged 1 commit into
mainfrom
fix/agent-stop-flake

Conversation

@5ym

@5ym 5ym commented Nov 14, 2026

Copy link
Copy Markdown
Collaborator

何を直したか

適合テストの「止まれの合図を受けたとき」で、立てたエージェントが応答しない

PR #2785 の CI (run 43565456686) で、:40882 のエージェントが応答しません で 2 件落ちた。同じ失敗は #2768 より前の 2026-11-08 (run 42456322190) にもある。
どちらも落ちたのは、止まれの合図を送る前の spawnAgent の中 (組めるまで待つ ready) で、#2768 の止める手順は通っていない。

  • エージェントは、待ち受けるポートが使われていると例外のまま SIGABRT で落ちる (コンテナで確かめた。確立した接続・TIME_WAIT のどちらがポートを使っていても同じ)
  • Bun の exitCode は、シグナルで落ちたプロセスでは null のまま (signalCode に入る。確かめた)。ready は exitCode しか見ないので、落ちたのに 10 秒待っていた
  • 40882 は Linux のエフェメラルポート (32768〜60999) の中にあり、2 回とも、テストの接続が多い「機材の定義」の直後に起きている。それまでの接続がクライアント側で 40882 を使っていたと推定している (エージェントの出力をテストが捨てていたので、CI には残っていない)

直したこと:

  • エージェントは、待ち受けるポートが使われていたら理由をログに出して終了コード 2 で終わる (AGENT_PORT・CARD_SHARE_PORT)。適合テストを足した
  • 適合テストの ready は signalCode も見る。spawnAgent はエージェントの出力を読み続け、起動できなかったときに添える
  • 適合テストのポートの既定を 28881〜 (エフェメラルポートより下) に移す

選局の途中で止めるために閉じられると、元の失敗が ObjectDisposedException に変わる

It930xDevice.Shutdown は選局が終わるのを待たない。選局の後始末 (給電の取り下げ、CXD2856 の中継を閉じる書き込み、TC90522 の電源を切る書き込み) が閉じた USB に書くと ObjectDisposedException になり、元の失敗が消えていた。

  • 閉じた機材 (Closed) には給電の取り下げを書かない。後始末の ObjectDisposedException は捨てて元の失敗を返す
  • 閉じた USB に書いて選局が失敗したときは「止めるので、選局の途中で <機種> (<ポート>) を閉じました」の IOException にする
  • 偽の IT930x に、閉じたあとの読み書きを ObjectDisposedException にする DisposedThrows を足し、テストを 2 つ足した (直す前のコードでは 2 つとも落ちる)

確かめたこと

  • .NET のテスト: CardLan の TTL の 3 件 (コンテナに IPv6 のループバックが無い) のほかは通る
  • 適合テスト (コンテナで AOT ビルド): [::1] に繋ぐ 2 件 (同じ理由) のほかは通る
  • bun run format・lint・check、単体テスト一式

確かめていないこと

  • CI で 40882 を使っていたのが何の接続か (出力が残っていない)
  • 実機の IT930x で、選局の途中に止めたときの動き

🤖 Generated with Claude Code

https://claude.ai/code/session_01T8TqVbtN2Hp8wNmqBX8pzT

適合テストの「止まれの合図を受けたとき」で、立てたエージェント (:40882) が
応答しないまま 10 秒で落ちることがあった (CI の 2026-11-08 と 11-14。
#2768 より前から起きている)。待ち受けるポートが使われていると、
エージェントは例外のまま SIGABRT で落ちる。Bun の exitCode はシグナルで
落ちると null のままなので、テストは落ちたことに気づかず応答を待っていた。

- 待ち受けるポートが使われていたら、理由をログに出して終了コード 2 で終わる
- 適合テストは signalCode も見て、起動できなかったときはエージェントの出力を添える
- 適合テストのポートを Linux のエフェメラルポートより下 (28881〜) に移す

止めるときに It930xDevice.Shutdown が選局の途中で USB を閉じると、
選局の失敗の後始末 (給電の取り下げ・中継を閉じる書き込み) が閉じた USB に
書き、ObjectDisposedException で元の失敗が消えていた。

- 閉じた機材には給電の取り下げを書かず、後始末の ObjectDisposedException は捨てて元の失敗を返す
- 閉じた USB に書いて失敗した選局は、止めたことが分かる IOException にする

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8TqVbtN2Hp8wNmqBX8pzT
@claude

claude Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

Claude finished @5ym's task in 35s —— View job


レビュー結果

指摘する不具合はありません。要確認が 2 点、軽い指摘が 1 点あります。

確認したこと

  • 選局の後始末: Close() は _closed = true を先に立ててから _usb.Dispose() を呼びます (It930xDevice.cs:1118-1119、1147)。catch (ObjectDisposedException) when (_device is { Closed: true }) は、USB を閉じる前に Closed が立つので取りこぼしません。
  • catch の順序: ObjectDisposedException は IOException を継承していないので、2 つの catch が重なりません。Closed でない ObjectDisposedException はそのまま外へ出ます。これは本物の不具合を隠さないので妥当です。
  • 後始末の握りつぶし: Cxd2856・Tc90522・It930xTuner の 3 か所は、IOException or ObjectDisposedException だけを捨てます。範囲が狭く、元の例外を throw; で返しています。
  • Program.cs: AddressInUseException を内側に持つ IOException だけを拾い、終了コード 2 を返します。設定を読めないときと同じ扱いです。
  • テスト: 偽の IT930x の DisposedThrows は既定で false なので、既存のテストに影響しません。新しい 2 件は「直す前のコードで落ちる」と PR 本文にあり、検証の筋が通っています。
  • ready: signalCode を見るので、SIGABRT で落ちたときに 10 秒待たなくなります。spawnAgent が出力を読み続けるので、パイプも詰まりません。
  • 文章: docs・コメント・文言は docs/writing.md の決まりに反する所を見つけませんでした。

要確認

  1. conformance.test.ts の「ポートが使われていれば 2 で終わる」テストが、前のテストで立てたエージェントの生存を当てにしています。
    • PORT と SHARE_PORT (= PORT + 3) を「上で立てたエージェントが使っている」前提にしています。上のテストが先に止めると、このテストはエージェントが立ち上がってしまい、exited を待ち続けて 30 秒で落ちます。
    • 定義を書き換えるテストより前に置く、という既存のコメントの並びとも合わせて、前提が崩れないか確認してください。必要なら、このテスト自身が net.createServer などでポートを塞ぐほうが独立します。
  2. CARD_SHARE_PORT 側のケースが、AGENT_PORT: PORT + 2 を自分で空いている前提にしています。
    • ほかのテストが PORT + 2 を使ったまま残っていると、:${busyPort} の検証が AGENT_PORT 側の失敗に当たって紛らわしくなります。落ちたときの原因が分かる程度の確認で足ります。

軽い指摘

  • spawnAgent の失敗時のエラーは、${message}:\n${output} で文末にコロンが付きます。writing.md の「文末のコロンを使わない」は文の書き方の決まりです。テストが出すエラーの文字列なので、改行で区切るだけにするか、出力: のようなラベルにすると決まりに合います。

確かめていないこと

  • ビルドとテストは実行していません。PR 本文のとおり、実機の IT930x で選局の途中に止めたときの動きと、CI で 40882 を使っていた接続の正体は未確認です。ポートを 28881 に移す対処は理屈が通っており、確認できるまでは推定として書かれています。
    | ブランチ: fix/agent-stop-flake

@5ym
5ym merged commit c6d0005 into main Nov 14, 2026
46 checks passed
@5ym
5ym deleted the fix/agent-stop-flake branch November 14, 2026 07:55
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.

1 participant