diff --git a/docs/plans/issue-460.md b/docs/plans/issue-460.md new file mode 100644 index 00000000..8b7f1dad --- /dev/null +++ b/docs/plans/issue-460.md @@ -0,0 +1,179 @@ +# Issue #460 — check-all.py が check-mutation-claims.py を既定の並列数で起こし、入れ子で N×N になる + +`check-all.py` は既定で CPU の数(N)だけ並列に回し、その中で `check-mutation-claims.py` を**引数なし**で起こす。 +`check-mutation-claims.py` の `--jobs` の既定も CPU の数なので、その中の `red` は親の残り N-1 本と、 +作業者 N 人ぶんの `red`(陽性対照と変異)と CPU を取り合う。#449 で `red` が 300 秒の打ち切りに届いた(3 回中 2 回)。 + +## 変更範囲 + +触る: + +- `scripts/check-all.py` — `check-mutation-claims.py` を起こすときに `--jobs` を渡す(値は親の `--jobs` から決める)。docstring の並列の節 +- `scripts/test-check-all.py` — 渡した `--jobs` を観測するケースと、その変異 +- `docs/plans/issue-460.md` — この計画 +- `scripts/test-worktree-scripts.py` — 197〜199 行のコメントの 1 文だけ(2 パス目の R5。CR5 と同じ根拠の無い因果が残っていた。 + 広げたのは人間の判断、2026-10-27。挙動は変えない) + +触らない: + +- `scripts/check-mutation-claims.py`(既定の並列数・`TIMEOUT` は変えない。単独で回すときは CPU の数のままでよい) +- `scripts/test-check-mutation-claims.py`(中で `check-mutation-claims.py` を既定の並列で起こすが、当てる木の `red` は軽い試料だけ) +- CI のワークフロー(CI は `check-mutation-claims.py` を単独のステップとして呼ぶ——入れ子にならない) +- `CLAUDE.md`(`check-all.py` の行は並列の細部を docstring に預けている。`check-mutation-claims.py` の行の「既定は CPU の数」は単独で回すときの話で、偽にならない) + +### 同じことを述べている箇所の数 + +入れ子の並列数を述べる箇所: `check-all.py` の docstring(並列の節)と `GUARDS` の注記の 2 箇所。 +`CLAUDE.md` の 2 行は単独のときの既定を言うだけで、入れ子の値は持たない(上)。同じファイルの中なので分けない。 + +## 乗るデプロイ経路 + +`scripts/` だけ。プラグインの中身は変えないので版は上げない。公開サイトにも載らない——**反映経路なし**。 + +## 周の在り処 + +- ブランチ: `issue/460-nested-mutation-jobs` +- worktree: `/Users/hdknr/Projects/hdknr/claude-code-setup/.claude/worktrees/issue-460` +- 起点: `43b5832`(start-point `origin/main`) +- base: `43b58324945badf38de0c481ebc64e62f44e9339` + +## 受入基準 + +不変条件: + +1. **`check-all.py` の中の `check-mutation-claims.py` は、親の `--jobs` より多い作業者を起こさない**—— + 親の作業者と入れ子の作業者の積が N×N にならない。値は起動の引数で確かめる +2. **`check-all.py --jobs 1` は入れ子まで直列**(`check-mutation-claims.py --jobs 1`)——「`--jobs 1` で直列」が入れ子で破れない +3. **`check-all.py` が全部通る**(`N/N 本を回した(失敗 0)`) +4. **`check-all.py` の wall が大きく悪化しない**——同じツリーで前後を交互に測る +5. **`check-all.py` の既存の観測(並列・直列・中断・収録漏れ)が緑のまま**——`test-check-all.py` が見る +6. (2 パス目に範囲を広げた分)**`test-worktree-scripts.py` はコメントの 1 文しか変わらず、挙動が変わらない**—— + `test-worktree-scripts.py` が緑で、差分がコメント行だけ。**直した文は記録(`docs/plans/issue-449.md:46`)にある事実だけを言い、 + `check-all.py` の docstring と食い違わない** + +経路(`check-mutation-claims.py` を起こす入口): + +| 経路 | 入れ子になるか | 守り方 | +| --- | --- | --- | +| `check-all.py` の `GUARDS` | なる | `--jobs` を渡す(この周) | +| CI(`plugins.yml`) | ならない(単独のステップ) | 対象外 | +| 人が直接 | ならない | 既定(CPU の数)のまま | +| `test-check-mutation-claims.py`(`check-all.py` の `TESTS` 経由) | なるが、当てる木は試料だけ | 対象外(上の「触らない」) | +| `check-mutation-claims.py` の `red` が `check-all.py` を起こす | 今の主張には無い | 対象外 | + +検証で証明できるもの: 1・2(`run` に渡る引数を観測するテストと変異)、3・5(`check-all.py` を回す)、 +4(前後を測る。ただし手元の 10 コアの macOS 1 台での値)。 + +この環境では証明できないもの(**未証明**): 遅い機械・コア数の違う機械で打ち切りに届かなくなること +(測れるのはこの 1 台だけ)。 + +生成物: なし(`scripts/` の 2 本は生成物を持たない。図の書き出し・`skill-metrics.py` の生成ブロックは触らない)。 + +## 未解決の判断 + +- 渡す値: **親の半分(少なくとも 1)に決めた**(下の実測)。`1` は wall が延びすぎる + +## 手順 8 + +- この周の起票: 0 件 +- 焼き戻す学び: リポジトリには無し(CR5 の直し残し(R5)は、手順 3 の「同じことを述べている箇所の数」で、 + `check-all.py` の外にある同じ主張を数えていなかったため。規定はもうあるので、足すものは無い) +- 手順 7: PR #467 は CI 緑。マージは人間の承認待ち。反映経路なし + +## 実測(著者。関門ではない) + +10 コアの macOS 1 台、2026-10-27。**計測は scratchpad のスクリプトで、他の `check-all.py` がいなくなってから起動し、 +走っている間の他の `check-all.py` の数と 1 分平均の負荷を 5 秒ごとに記録した。** `red` の所要は、`PATH` の先頭に +記録だけする `python3` を置いて、退避先(worktree の外)で走った `scripts/test-*.py` を拾った。 + +| 入れ子の `--jobs` | wall(他の `check-all.py` が 0 本の回) | 最長の `red` | +| --- | --- | --- | +| 既定(10。変更前) | 227.2 / 217.0 / 232.4 / 224.0 / 262.2 秒 | 90.4 / 99.1 秒 | +| 親の半分(5) | 237.4 / 252.3 秒 | 77.4 / 79.4 秒 | +| 1 | 373.1 / 368.0 / 388.1 秒 | 約 64 秒 | + +- **変更前でも、他の `check-all.py` が重ならなければ打ち切り(300 秒)には届かなかった**(最長 90 秒)。 + **打ち切りに届いた 1 回(wall 678 秒・`test-worktree-scripts.py` の陽性対照が 300 秒)は、負荷の 15 分平均が 50 の時間帯で、 + 別のセッション(`issue-458`)が `check-all.py` を走らせていた**(その回は重なりを記録していないので、重なっていたとまでは言えない) +- **`--jobs 1` は wall が約 +65%** で、受入基準 4 に反する。**親の半分は wall の中央値で約 +8%(227 → 245 秒)で、 + 最長の `red` が 90〜99 → 77〜79 秒**。標本は半分が 2 回・変更前が 5 回で、負荷の揺れ(1 分平均 41〜71)の中にある +- 半分の回がもう 1 回あったが、途中で他の `check-all.py` が 2 本重なった(1 分平均 216・wall 457 秒)ので捨てた + +交互の測り直し(3 セッション目。`/code-review` 1 パス目の CR7 への対応): + +同じツリーで `check-all.py` と `test-check-all.py` の 2 本を**両方とも** base の版と HEAD の版に入れ替えて、順に回した。 +走っている間、**自分の子孫を除いた** `check-all.py` の数と 1 分平均の負荷を 5 秒ごとに記録した +(最初の試行は `test-check-all.py` が起こす `check-all.py` の写しまで「他」に数える形だったので、止めて捨てた)。 + +| 順 | 版 | wall | 他の `check-all.py`(最大) | 1 分平均 | 結果 | +| --- | --- | --- | --- | --- | --- | +| 0 | base | 248.8 秒 | 0 | 11〜47 | 51/51(失敗 0) | +| 1 | HEAD | 243.1 秒 | 0 | 15〜46 | 51/51(失敗 0) | + +- 標本は 1 組だけ(残りの 1 組は人間の判断で止めた)。この 1 組では悪化していない。**「悪化しない」は 1 組と、上の交互でない表からの言い方**である + +捨てた測定(交絡): + +- 入れ子の値だけを変えて、契約(`test-check-all.py`)を新しい版のままにした回——契約が落ちて `test-check-all.py` の主張 4 件が + 陽性対照で止まり、仕事が減った(wall 237 / 227 / 222 秒)。**比べるときは 2 本とも base の版に戻す** +- 最初の数回(678 / 441 / 520 / 459 秒)——他のセッションの `check-all.py` と重なっていたかを記録していなかった +- 1/3 の回——変異の表が式の文字列(`max(1, jobs // 2)`)を名指ししているので契約が崩れ、さらに他の `check-all.py` が 2 本重なった + +## 関門の進捗(再開点) + +- 周の在り処は上の節。肯定的な確認済み(再開した 2 セッション目でも `issue/460-nested-mutation-jobs` の上・`dfa754b`) +- 実装: `4d460c9`(入れ子を `--jobs 1`)→ `dfa754b`(親の半分。契約を性質で見る)→ `f95df7b`(実測を docstring に)。**実装は完了**。 + `f95df7b` で `check-all.py` 51/51 +- 次: 手順 5 の Verifier(1 パス目)から +- 手順 4 の割り目: AskUserQuestion で訊いた → **「ここで割る」が選ばれた**(2026-10-27) +- 手順 5 の Verifier: 1 パス目 **反証 0 件**(3 セッション目・sonnet・`dev-loop:dev-loop-verifier`。`test-check-all.py` 緑・足した変異 3 件は捕まえる・鮮度 2 本緑。 + 基準 3 は回さず、基準 4 は計画の実測を読んで判定)。注意 1 件は下の採否の表 / `/code-review`: 1 パス目 **指摘 9 件**(high)。採否は下の表 +- 1 パス目の指摘対応: CR5 を直した(docstring の 1 文)・CR7 を測り直した。他は残課題。直したあと `check-all.py` 51/51 +- 次: **手順 5 の当て直し(Verifier 2 パス目)**と **`/code-review` 2 パス目**の両方が残っている。base は同じ SHA + (2 パス目で見るのは主に `b99fc7f` の docstring の 1 文と、計画の測り直し) +- 手順 6 の割り目(2 パス目の前): AskUserQuestion で訊いた → **「ここで割る」が選ばれた**(2026-10-27) +- 手順 5 の当て直し(Verifier 2 パス目): **反証 0 件**(4 セッション目・sonnet・`dev-loop:dev-loop-verifier`。注意 2 件は基準 4 の証拠の弱さ) + / `/code-review` 2 パス目: **指摘 10 件**(high)。採否は下の「採否の表(2 パス目)」 +- 2 パス目の指摘対応: R5 だけ直す(人間の判断)。**狭いパスの起点: `f258b08e8fa6690aea3eb5caa6e644aab3d4e82b`**(指摘対応を始める直前の HEAD) +- 範囲を広げた分の受入基準: 6(下) +- R5 の修正 `bba67c9` のあと `check-all.py` 51/51(失敗 0) +- 狭いパス(R5): **反証 0 件**(sonnet・`dev-loop:dev-loop-verifier`。コメント以外の差分が空・`test-worktree-scripts.py` 緑・ + 直した文は #449 の記録と一致)。**Verifier は変異を設計しなかった**(コメントだけの変更なので、変異は前後で同じ結果になるとして)。素通りした変異: なし(当てていない) +- 関門はここで打ち切り(2 パス)。次: PR を作り、残課題・未証明・基準 4 の証拠の弱さを PR コメントに書く +- 交絡を潰した手順: 上の「捨てた測定」。他の `check-all.py` の重なりを記録し、比べる 2 本を base の版に揃えた + +## 既知の限界・決着済みの論点 + +- **他のセッションの `check-all.py` が重なったときの打ち切りは、この周では止められない**——入れ子を絞るのは 1 本の `check-all.py` の中の + 取り合いだけで、セッションをまたぐ取り合いは見えない。決着: 採らない(範囲外)。理由: Issue の受入基準は入れ子の N×N +- **`--jobs 1` は採らない**——wall が約 +65%(上の表)で、受入基準 4 に反する + +### 採否の表(1 パス目) + +| 指摘 | 実物で失敗を示せるか | 破る受入基準 | 採否 | +| --- | --- | --- | --- | +| Verifier: 契約 9 は「親の半分」を固定しない(入れ子を定数 1 にする変異が通るはず。読解のみ) | 示せない(その変異は基準 1・2 を満たし、失敗ではない) | 無し(契約は意図して値ではなく性質を見る——`dfa754b`) | 残課題 | +| CR1: 契約 9 は `--jobs` を明示した起動しか見ず、既定の起動(引数なし)を観測しない(変異 `and "--jobs" in sys.argv` が緑) | 示せない——実物は既定の起動で入れ子に `--jobs 5`(10 コア。scratchpad で `run` を差し替えて記録)。失敗はテストの網の穴で、成果物の失敗ではない | 無し(基準 1 は実物で満たす) | 残課題 | +| CR2: 契約 9 は入れ子以外の歯止めに `--jobs` が渡ることを見ない(変異 `if True:` が緑) | 示せない——実物は他の歯止めに `--jobs` を渡さない(同上の記録で 0 本) | 無し | 残課題 | +| CR3: 契約 9 は親 1 と 4 しか見ない(変異 `1 if jobs < 8 else jobs * 2` が緑) | 示せない——実物は親 1・10・32 で 1・5・16 | 無し | 残課題 | +| CR4: docstring「`--jobs 1` なら入れ子まで直列」は孫(`red` が起こす `test-*.py` の並列)まで直列にしない | 示せない(基準 2 は「`check-mutation-claims.py --jobs 1`」と定義しており、その意味では真) | 無し | 残課題 | +| CR5: docstring「#449 の打ち切りはその時間帯に出た」は根拠が無い | **示せる**——`docs/plans/issue-449.md:46`「#449 の 300 秒超えは 3 回中 2 回で起きた別の日の値」。重なりは記録されていない。この周の 678 秒の回は別の事象で、計画も「重なっていたとまでは言えない」 | 無し(docstring の事実は受入基準に無い) | **直す(人間の判断。2026-10-27)**——根拠の無い因果の 1 文を、記録にある事実だけに直す | +| CR6: 高度——実測では入れ子 N の `red` は打ち切りから遠く(最長 90〜99 秒)、wall +8% を払って原因と裏付けられないものを直している。並列の形も N×N ではなく (N-1)+N | 示せない(打ち切りは重ならない回で再現しない) | 無し(Issue の受入基準は入れ子の並列を絞ること) | 残課題(**決着の理由への反論**なので PR コメントで人間に渡す) | +| CR7: 基準 4 の証拠は「交互に測った」ことを示していない(順序・時刻が無く、変更後は 2 回) | 示せる(計画の表に順序が無い) | 4(証拠の不足) | **測り直した**(コードは変えない)——下の「交互の測り直し」。4 回の予定を 1 組(2 回)で止めた(人間の判断。#464 に CPU を空けるため) | +| CR8: `nested_jobs` を CPU の数で頭打ちにしていない(`--jobs 32` で入れ子 16) | 示せる(親 32 で 16)が、基準 1 は「親より多くない」で、16 < 32 | 無し | 残課題 | +| CR9: FAIL の行・ログの見出しに入れ子に渡した `--jobs` が出ない | 示せない(並列に依る失敗の再現の例は仮定) | 無し | 残課題 | + +### 採否の表(2 パス目) + +| 指摘 | 実物で失敗を示せるか | 破る受入基準 | 採否 | +| --- | --- | --- | --- | +| Verifier 注意 1・R1: 交互の測り直しは 1 組(248.8 / 243.1 秒)で、予想の +8%(約 19 秒)を判別できない。基準 4 には閾値が無く落ちようがない | 示せる(計画の表のばらつき 217〜262 秒) | 4(証拠の不足。成果物の失敗ではない) | 残課題——**PR コメントに「基準 4 は証拠が弱い」と名指し**(人間の判断。2026-10-27) | +| Verifier 注意 2・R2: 1 分平均 11〜47 で、他の `check-all.py` 以外の負荷は統制していない。順序も固定 | 示せる(計画の記録) | 4(同上) | 残課題(R1 と同じ扱い) | +| R3: 経路表の「`red` が `check-all.py` を起こす——今の主張には無い」は偽。`red` が `test-check-all.py` の主張が 4 件ある | 示せる(`grep` で 4 件)。ただし `test-check-all.py` は `run` を偽物に差し替えて `main()` を回す(`test-check-all.py:281`)ので、重い入れ子にはならない | 無し | 残課題(経路表の言い方の誤り) | +| R4: 孫の `test-check-mutation-claims.py` は既定の `--jobs` で `main()` を回すので、`check-all.py --jobs 1` でも孫は直列でない | 示せる(主張 3 件) | 無し(基準 2 は「`check-mutation-claims.py --jobs 1`」と定義。1 パス目の CR4 と同じ) | 残課題 | +| R5: `test-worktree-scripts.py:197-199` に CR5 と同じ根拠の無い因果が残り、`check-all.py` の docstring と食い違う | 示せる(ファイル:行) | 無し(範囲外だった) | **直す(人間の判断。2026-10-27)**——範囲を広げ、基準 6 を足した | +| R6: 「未証明」が狭い——打ち切りはこの機械でも重ならない回で再現しておらず、「打ち切りを防ぐ」はこの機械でも未証明 | 示せる(計画の実測節) | 無し | 残課題——PR コメントの未証明に足す | +| R7: CR8 の理由「16 < 32」は base との比較を見ていない——10 コアで `--jobs 32` なら入れ子は変更前の 10 から 16 に増える | 示せる(式から) | 無し(基準 1 は「親より多くない」) | 残課題(**決着の理由への反論**なので PR コメントで人間に渡す) | +| R8: 捨てた測定(237 / 227 / 222 秒)と残した表の値(227.2・237.4)が近く、混ざったかもしれない | 示せない(1 セッション目の記録は残っていない) | 無し | 残課題 | +| R9: 高度——名前で 1 段だけ絞っており、下の段は既定の並列のまま。環境変数で継ぐ上限なら全段に効く | 示せない(設計の提案) | 無し | 残課題 | +| R10: 半分の固定は、軽い歯止めが終わった後の尾で約 N/2 コアを空ける | 示せない(wall +8% の原因という推測) | 無し | 残課題 | diff --git a/scripts/check-all.py b/scripts/check-all.py index d86dcf77..cd76c0c8 100755 --- a/scripts/check-all.py +++ b/scripts/check-all.py @@ -31,6 +31,14 @@ - **既定は CPU の数だけ並列**(`--jobs`)。**`--jobs 1` で直列**になる。 - **起動は宣言順。** いちばん重い `check-mutation-claims.py` を `GUARDS` の先頭に置いてある ——並列のとき、律速になる 1 本が最後に起動されないように。 +- **入れ子は親の半分**(#460)。`check-mutation-claims.py` は自分も `--jobs`(既定は CPU の数)で並列に当てるので、 + ここからは `--jobs` を親の半分(少なくとも 1)にして起こす——`--jobs 1` なら入れ子まで直列になる。 + 2026-10-27、10 コアの macOS で、他の `check-all.py` が重ならない回の wall と、入れ子の中で最も長かった `red`: + 既定のまま(10)は 217〜262 秒・90〜99 秒、半分(5)は 237・252 秒・77〜79 秒、`1` は 368〜388 秒・約 64 秒。 + **`1` は打ち切りまでの余裕が最も大きいが wall が 6 割延びる**ので、半分にした。 + **別のセッションの `check-all.py` と重なったときの取り合いは、ここでは絞れない**。打ち切りの原因は特定していない + ——#449 の打ち切りは重なりを記録していない日の値で、#460 で届いた 1 回は別のセッションが `check-all.py` を走らせていた + 時間帯だが、重なっていたかは記録していない。 - **出力は宣言順**(終わった順ではない)。先頭が最も重いので、最初の行が出るまで待つ。 - **Ctrl-C で、まだ起動していないものは起動せず、走っているものは止める** (親だけに SIGINT が届いた場合も——子を止めるのは親である)。**孫まで止める**—— @@ -68,6 +76,7 @@ GUARDS: list[tuple[str, list[str], bool]] = [ # **他の歯止めを子プロセスで回すので、1 本だけ重い**(直列で 166 秒。#430 の時点)。 # **起動は宣言順なので先頭に置く**——後ろに置くと、並列のとき律速の 1 本が遅れて起動される。 + # **中の並列数は `NESTED` が決める**(#460)。 ("check-mutation-claims.py", [], False), ("check-plugin-versions.py", [], False), ("check-norm-markers.py", [], False), @@ -82,6 +91,16 @@ ("check-plan-scope.py", [], True), ] +# 中で自分も `--jobs` で並列に回すもの(#460)。親の並列数から決めた `--jobs` を渡す—— +# 渡さないと入れ子の既定(CPU の数)で起き、親の作業者と同じ CPU を取り合う。 +NESTED = "check-mutation-claims.py" + + +def nested_jobs(jobs: int) -> int: + """入れ子に渡す `--jobs`。親の半分で、少なくとも 1(親が 1 なら入れ子も直列)。""" + return max(1, jobs // 2) + + # 回帰テスト(歯止め自体が何も見ていない状態に退化していないかを見る) TESTS: list[str] = [ "test-check-plugin-versions.py", @@ -234,6 +253,8 @@ def main() -> int: guard_runs.append((label, None)) continue args = [*extra, opts.base] if needs_base else extra + if name == NESTED: + args = [*args, "--jobs", str(nested_jobs(opts.jobs))] guard_runs.append((label, pool.submit(run, name, args))) test_runs = ([] if opts.guards_only else [(name, pool.submit(run, name, [])) for name in TESTS]) diff --git a/scripts/test-check-all.py b/scripts/test-check-all.py index 716fc383..3279fc1b 100755 --- a/scripts/test-check-all.py +++ b/scripts/test-check-all.py @@ -386,6 +386,26 @@ def contract(target: Path, root: Path, tag: str) -> list[tuple[str, bool]]: except Exception: # noqa: BLE001 — 断らずに別の形で落ちたら、それも失格 refused = False got.append(("--jobs 0 を断る", refused)) + + # 9) 入れ子の並列を絞る(#460)——`check-mutation-claims.py` に、親より少ない `--jobs` を渡す。 + # **実物の `GUARDS` のまま、起動の引数だけを記録する**(`run` を差し替えるので何も走らない)。 + # **値そのものは見ない**——見るのは「渡すこと」「親が 1 なら 1」「親が 2 以上なら親より少ない」。 + def nested_args(jobs: str) -> list[list[str]]: + mod = load(target, f"{tag}_nested{jobs}") + mod.TESTS = [] + calls: list[tuple[str, list[str]]] = [] + mod.run = lambda name, args: (calls.append((name, list(args))), (True, ""))[1] + run_main(mod, ["--base", "no-such-ref-xyz", "--jobs", jobs]) + return [args for name, args in calls if name == "check-mutation-claims.py"] + + def inner(args: list[list[str]]) -> int | None: + if len(args) != 1 or args[0][:-1] != ["--jobs"] or not args[0][-1].isdigit(): + return None + return int(args[0][-1]) + + one, four = inner(nested_args("1")), inner(nested_args("4")) + got.append(("--jobs 1 なら入れ子も --jobs 1", one == 1)) + got.append(("--jobs 4 なら入れ子は 1 以上 4 未満", four is not None and 1 <= four < 4)) return got @@ -601,6 +621,11 @@ def test_ci_runs_every_registered_script() -> None: "回帰テストを宣言順に出さない": (" for name, fut in test_runs:\n", " for name, fut in reversed(test_runs):\n"), "--jobs 0 を通す": (" if opts.jobs < 1:\n", " if False:\n"), + # 以下は #460(入れ子の並列) + "入れ子に --jobs を渡さない": (' args = [*args, "--jobs", str(nested_jobs(opts.jobs))]\n', + " pass\n"), + "入れ子に親と同じ並列数を渡す": (" return max(1, jobs // 2)\n", " return jobs\n"), + "入れ子を 0 にしうる": (" return max(1, jobs // 2)\n", " return jobs // 2\n"), # **終わった順に出す**本体を写した変異(`as_completed`。#430 の 2 パス目)。`reversed()` だけでは、 # 受入基準 P11 が挙げる「終わった順」を表す変異が無かった。歯止めの側は SKIP(すぐ終わる)が先に出るので # 試料の遅延に依らず崩れる——**遅延を外しても 2 本とも捕まえた**ので、遅延が要るとは言えない。 diff --git a/scripts/test-worktree-scripts.py b/scripts/test-worktree-scripts.py index 4df667d2..1c35d350 100644 --- a/scripts/test-worktree-scripts.py +++ b/scripts/test-worktree-scripts.py @@ -196,8 +196,8 @@ def _build_dangling_origin_head(base: Path) -> Path: # **足場は 1 度だけ作り、使うたびに複製する**(#449)。**`observe()` は 21 回走る** # (正しい版 1 + 変異 20)ので、足場を毎回 `git init`・`commit`・`clone`・`push` で -# 組み直すと、**それだけで子プロセスが 1 回に 50 本ほど増える**——`check-all.py` の並列と -# `check-mutation-claims.py` の並列が重なったとき、この 1 本が打ち切り(300 秒)に届いた。 +# 組み直すと、**それだけで子プロセスが 1 回に 50 本ほど増える**——`check-all.py` の中の +# `check-mutation-claims.py` で、この 1 本が打ち切り(300 秒)に届いたことがある(3 回中 2 回。原因は特定していない)。 # **複製は使い捨てで、観測どうしは今までどおり足場を共有しない。** _templates: dict[str, tuple[Path, object]] = {} _template_root: tempfile.TemporaryDirectory | None = None