doeff-adr: 外部 process 起動の待ちに構造的な上限を与える(semgrep / pytest の hang で ADR 検査の門が無期限に停まるのを根治) - #600
Open
proboscis wants to merge 1 commit into
Open
doeff-adr: 外部 process 起動の待ちに構造的な上限を与える(semgrep / pytest の hang で ADR 検査の門が無期限に停まるのを根治)#600proboscis wants to merge 1 commit into
proboscis wants to merge 1 commit into
Conversation
doeff-adr は semgrep と pytest を子 process として起動するが、外部 process 接触を 所有する層が無く、待ちの上限は起動地点ごとの任意 keyword だった。結果、package 全域の起動 2 箇所(registry.py の semgrep / cli.py の pytest --collect-only)ともに timeout なし = 0/2 で、子が返らなければ門も返らず、門を待つ CI / land 列 / agent 席 がまとめて止まる。 - 新設 doeff_adr/process.py = package が外部 process と接する唯一の層。 run_external_process が常に上限を適用し(既定 600 秒・環境変数 DOEFF_ADR_EXTERNAL_PROCESS_TIMEOUT_SECONDS で上書き、読めない値は黙って 無期限に落とさず ValueError)、subprocess.TimeoutExpired を command と秒数を 名指す ExternalProcessTimeoutError へ変換する。 - registry.py: 打ち切りを既存作法どおり AssertionError へ変換 — 「findings なし」 「検査通過」と読ませない(走らなかった scan を発火なしと黙読する失敗と同型)。 - cli.py: 打ち切りは「wiring verified」ではなく exit 124(pytest の 0..5 と非衝突) + stderr に診断。 - .semgrep.yaml: doeff-adr-external-process-must-be-bounded — doeff_adr 配下で process.py 以外の raw な subprocess / os.system 起動を ERROR で禁止。新しい起動 地点で上限を書き忘れる経路自体を閉じる。台帳 semgrep_rules 251→252。 - tests/test_external_process_bounds.py: 固まる子(sleep 30)を短い上限で起動し、 無期限に待たず所定の例外/非ゼロ終了になることを helper・semgrep runner・ verify-wiring の 3 経路で検査。test_wiring.py の CLI テストは seam を doeff_adr.process 側へ移し、timeout が起動へ渡ることも併せて検査する。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
診断した根本原因(fix_class: root-cause)
doeff-adr は外部 process(semgrep / pytest)を起動するが、外部 process 接触を所有する層が無かった。待ちの上限は各起動地点が任意に書く keyword 引数でしかなく、package 全域の起動 2 箇所とも
timeout=を渡していなかった(2026-08-18 実測 0/2)。registry.py:434_run_semgrep— semgrep 起動cli.py:36verify-wiring—pytest --collect-only起動registry.py の既存コメントは「オフライン機で hang、飽和網で timeout」と hang の危険を自ら名指ししながら、その hang への上限を持っていなかった。この待ちは ADR 検査の門の中にあるため、子が返らなければ門も返らず、門を待つ CI / land 列 / agent 席がまとめて止まる。
除去した設計上の原因 = 「上限は起動地点ごとの任意項目」という状態そのもの。上限を持つ層を新設して全起動地点をそこへ通し、raw な起動経路を lint で閉じたため、片方だけ直す対症修正(同じ欠陥が他方と将来の 3 箇所目に残る)にはなっていない。
変更
doeff_adr/process.pyrun_external_processが常に上限を適用(既定 600 秒 / 環境変数DOEFF_ADR_EXTERNAL_PROCESS_TIMEOUT_SECONDSで上書き)。既定値の根拠は module 定数の直上に 1 行コメント。読めない値・非正・非有限は黙って無期限に落とさずValueError。registry.pyAssertionErrorへ変換し、command と秒数を名指す。「findings なし」「検査通過」と読ませない。cli.pywiring verifiedではなくexit 124(GNU timeout 慣行・pytest の 0..5 と非衝突)+ stderr に診断行。.semgrep.yamldoeff-adr-external-process-must-be-bounded(ERROR)—**/doeff_adr/**でprocess.py以外の raw なsubprocess/os.system系起動を禁止。台帳semgrep_rules251→252。packages/doeff-adr/tests/test_external_process_bounds.py新設(6 本)+test_wiring.pyの CLI テストを新しい seam へ移設・強化。Verification(受け入れ条件 1:1 対応)
process.py:19-23(DEFAULT_EXTERNAL_PROCESS_TIMEOUT_SECONDS/EXTERNAL_PROCESS_TIMEOUT_ENV+ 根拠コメント)、registry.py:_run_semgrep、cli.py:main。packages/doeff-adr/tests/test_external_process_bounds.py::test_external_process_timeout_seconds_is_overridable_by_env/::test_default_bound_is_finite_and_reaches_subprocess/packages/doeff-adr/tests/test_wiring.py::test_verify_wiring_cli_runs_strict_collection(起動へ timeout が渡ることを seam で検査)TimeoutExpiredを握りつぶさず診断可能な例外へ(registry は AssertionError 作法)process.py:ExternalProcessTimeoutError(command / 秒数 / 部分 stderr)、registry.pyのexcept ExternalProcessTimeoutError→AssertionError。::test_run_semgrep_timeout_is_loud_not_empty_findings(秒数・command が message にあり、「発火なし」に化けない)/::test_verify_wiring_timeout_exits_nonzero_with_diagnosis(wiring verifiedを出さず exit 124)doeff_adr/process.pyへ集約 + semgrepdoeff-adr-external-process-must-be-bounded。実測: 同 rule をdoeff_adr配下の raw 起動 probe(import subprocess+subprocess.run(...))へ当てて 2 hit を確認し、process.pyと修正後の 2 箇所には 0 hit(probe は検証後に削除)。::test_run_external_process_stops_waiting_at_the_bound(sleep 30を 0.5 秒上限で起動 →ExternalProcessTimeoutError、経過 < 20 秒)/::test_run_external_process_applies_the_bound_without_being_asked/::test_run_semgrep_timeout_is_loud_not_empty_findings/::test_verify_wiring_timeout_exits_nonzero_with_diagnosis実行したコマンド
uv run pytest packages/doeff-adr -q→ 27 passed(既存 3 本test_defadr_macros.py/test_semgrep_runner_tree_only.py/test_wiring.py+ 追加分)uv run pytest tests/test_adr_wiring_gate.py tests/test_enforcement_ledger.py tests/test_semgrep_gate.py tests/test_semgrep_dependency_config.py -q→ 7 passed(semgrep baseline ratchet 0 findings・enforcement 台帳 252 一致)uv run semgrep --metrics=off --disable-version-check --config .semgrep.yaml doeff/ packages/ --error→ 0 findings(201 rules / 903 files)uv run ruff check packages/doeff-adr→ clean、uv run pyright packages/doeff-adr/src→ 0 errorsVerification deviations
なし(受け入れ条件の代替・弱化はしていない)。
備考
test_wiring.py::test_verify_wiring_cli_runs_strict_collectionはdoeff_adr.cli.subprocessを patch していたため、起動が helper へ移った時点で seam が消える。同テストをdoeff_adr.process.subprocessに付け替え、command の形に加えて timeout が起動へ渡ること も検査するよう強化した(検査の弱化ではなく追加)。出所
sandbox:responsibility:host-load-steward掲出 gen 55 に対する operator 裁定withdraw-and-route-via-repair-ticket-v189(delivered 2026-08-18T04:49:20Z / requestId dav-h558ce886bcfb9d5b)の実施。ACP issuedoeff-adr-2-semgrep-pytest-adr-967704。🤖 Generated with Claude Code