Repository navigation
Description
何が困るか
実行をまたいで残る状態(投稿の待ち行列・一時の worktree・状態ファイル)に寿命と持ち主が無い。並行する実行が残骸を積み、次の実行を塞ぐ。手で片付ける側が、実行中のものを消すおそれもある。
| 子 | 残るもの | 起きたこと |
|---|---|---|
| #2084 | cross-review の投稿の待ち行列の先頭に残る、前の実行の 422 の指摘 | review が止まり続ける(carmo PR #352、2026-10-21) |
| #2085 | /tmp/ndf-worktrees/ の rf* と状態ファイル |
2026-10-23 にホストのディスクが埋まった。2026-10-31 時点でマージ済みの rf 4 本(2.0G)が残る |
関連: #2221(承認の後の別のセッションのマージで止まる)、#2164・#2165(並行するプランの版数と触るファイル)。
修正レイヤー
実行をまたいで残る状態の寿命と持ち主を持つ層。 一時の置き場は lib/worktree_base.py に掃除を寄せ、merged-steps.py から呼ぶ。待ち行列は lib/post_queue.py の失敗の分類で、前の実行が積んだ項目の扱いを決める。
完了条件
- 状態ごとに持ち主(実行)と寿命が決まり、持ち主の終わった状態は自動で片付く
- 実行中の状態を、別の実行の片付けが消さない
- 前の実行の残りが、次の実行を止めない
要求と受け入れ条件
この節が要求の正である。上の「何が困るか」から「完了条件」までは起票時の文で、条件は下の「起票者の推定」の表で扱いを決め、受け入れ条件は下の AC1〜AC16 で置き換える。
依頼(原文)
- 状態ごとに持ち主(実行)と寿命が決まり、持ち主の終わった状態は自動で片付く
- 実行中の状態を、別の実行の片付けが消さない
- 前の実行の残りが、次の実行を止めない
(書き手: 起票者、日付: 2026-10-31。この課題の「完了条件」。利用者の指示の原文としては扱わない)
- 並列実行に強い作りにする。実行をまたいで残る状態に寿命と持ち主を持たせ、承認の後の別のマージで止まらない(親 C・承認ゲート 2 の後に別のセッションが develop へマージすると、配布物の変わらない文書だけの PR でも承認を取り直す #2221)
(書き手: 不明、日付: 2026-10-31。マイルストーン「26 自律実行の信頼性」の説明の主題 3。2026-10-31 の新方針「並列に強く」を書き写した文と見られるが、利用者の原文を確かめられない)
子の課題の由来: #2085 は「利用者からの依頼(2026-10-23、Mac のディスクの空きが頻繁に無くなる件の調査)」と本文にあるが、依頼の原文は課題に無い。#2084 は carmo-screening #338 の振り返り(2026-10-21)で起票者が書いた。
起票者の推定
題・本文のうち、利用者の指示の原文に無く起票者が推して足した条件。承認ゲート 1 で利用者が確かめるまで、利用者の決定として扱わない。この課題には利用者の原文が無いため、条件はすべてここに並ぶ。
| # | 条件 | 推した出所 | この要求での扱い |
|---|---|---|---|
| S1 | 状態ごとに持ち主(実行)と寿命を決める | 完了条件 1 | AC1 |
| S2 | 持ち主の終わった状態は自動で片付く | 完了条件 1 | AC2〜AC6。人の操作を要さない時点で片付ける(前提 3) |
| S3 | 実行中の状態を、別の実行の片付けが消さない | 完了条件 2 | AC7〜AC10 |
| S4 | 前の実行の残りが、次の実行を止めない | 完了条件 3 | AC11〜AC15 |
| S5 | 子の #2084・#2085 をこの課題の 1 つの設計で直す | 本文の表(子) | 前提 1 |
| S6 | 一時の置き場の掃除を lib/worktree_base.py に寄せ、merged-steps.py から呼ぶ |
修正レイヤー | 採らない(要求としては)。置き場と呼び出し元は設計が決める。merged-steps.py cleanup が片付けの時点の 1 つであることだけを AC2 で求める |
| S7 | 待ち行列は lib/post_queue.py の失敗の分類で、前の実行が積んだ項目の扱いを決める |
修正レイヤー | 採らない(要求としては)。直す場所は設計が決める。振る舞いは AC11〜AC14 |
| S8 | #2221・#2164・#2165 は関連であって、この課題で直さない | 本文の「関連」 | 含まない |
目的
- cross-review・cross-refactoring・merged を並行して回しても、ある実行が残した状態が別の実行を止めず、ディスクを埋めず、作業中の変更を消さない状態にする(Mission の「人の判断が要る所だけで止まる」。手で残骸を片付ける操作を無くす)
前提
- 前提 1: この課題は子の cross-review: 修正前のコミットの行を指す古い指摘が投稿の待ち行列の先頭で 422 になり、review が止まり続ける #2084(投稿キュー)と NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085(一時の置き場)を 1 つの設計で扱う。cross-refactoring の一時の worktree(rf<PR番号>)が merged の後片付けで外されず溜まる #1680(
rf<n>が merged で外れない)は NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085 の受け入れ条件に含まれるため、ここで満たす。子の受け入れ条件は下の AC に取り込み、子の課題の本文は書き換えない - 前提 2: 「実行」は、cross-review・cross-refactoring・merged の後片付け・Claude Code などのセッション(セッション開始 hook が状態ファイルを作るもの)・
worktree-deps.shのプロセス・テストのセッションのどれか 1 回を指す。持ち主はこの単位で記録する - 前提 3: 自動の片付けの時点は「同じリポジトリで NDF の cross-review・cross-refactoring・merged の後片付けのどれかを実行したとき」とする。常駐のプロセスや cron は置かない。設計がこれより早い時点(セッションの終わりなど)を足すのは構わない
- 前提 4: 持ち主が終わったかを判定できない状態は、終わっていないとみなして残す(安全側)。判定の手段(プロセスの生存・PR の状態・記録の時刻)は設計が決める
- 前提 5: 片付けは
git worktree removeとgit branch -dのように git が拒めば止まる操作だけで行い、--force/-Dを使わない(共通原則 C4)。未コミットの変更と未 push のコミットを持つ worktree は外さない(C3。作業を消さない) - 前提 6: 一時の置き場は
NDF_WORKTREE_BASEか${TMPDIR:-/tmp}のままで、場所は変えない(どのプロジェクトでも同じ。Value 5)
対象範囲
含む:
- NDF が実行をまたいで残す状態の一覧と、それぞれの持ち主・寿命の取り決め(AC1)
- レビュー worktree(
<NDF_WORKTREE_BASE>/<slug>/pr<n>・rf<n>)、ndf-worktree-<session>.json、ndf-deps.*、fix-pr<n>-*、ndf-tests-*の片付け - cross-review の投稿キューで、以前の実行が積んだ項目が恒久に送れないときの扱い(cross-review: 修正前のコミットの行を指す古い指摘が投稿の待ち行列の先頭で 422 になり、review が止まり続ける #2084)
- 片付けの結果(外した・残した・理由)を結果の JSON の
itemsへ出すこと
含まない:
- 承認の後の別のセッションのマージで止まる件(承認ゲート 2 の後に別のセッションが develop へマージすると、配布物の変わらない文書だけの PR でも承認を取り直す #2221)、並行するプランの版数と触るファイルの件(スプリントのプランが作った時点の版数を配布と本番のプランへ書き込み、並行する別のスプリントがその版を先に出すとプランを作り直すまで止まる #2164・設計を持たない実装のプランが触るファイルを宣言せず、同時に流す別のプランと重なるとそのファイルを「変えない」と指示されて止まる #2165)
- 開発 worktree(
.worktrees/<ブランチ名>)の片付けの規則の変更(merged の今の扱いのまま) - relay の状態ディレクトリ(
${XDG_STATE_HOME:-~/.local/state}/ndf/relay/)と supervise.py の状態の置き場 - 2026-10-31 時点で手元に残っている残骸を、この変更の外で手で消すこと(AC2〜AC6 の片付けを初めて回したときに片付く)
ドメインイベント
| # | イベント | 引き金 | 失敗したとき | 順序の前提 |
|---|---|---|---|---|
| E1 | 実行が残る状態を作り、持ち主を記録した | cross-review / cross-refactoring の開始、セッションの開始、worktree-deps.sh の起動、テストの開始 |
持ち主を記録できない → 前提 4 で「終わっていない」とみなして残る。作れなければ今までどおりその実行が失敗を返す | — |
| E2 | 実行が終わった | 正常終了・異常終了・プロセスが止められた | trap などの後始末が走らずに終わる → E4 の片付けが拾う(AC5・AC6) |
E1 |
| E3 | Pull Request がマージされた、または閉じられた | 人か conductor の操作(merged を通るとは限らない) | — | E1 |
| E4 | 片付けが、持ち主の終わった状態を外した | 前提 3 の時点 | git が拒む・削除に失敗する → kept と理由を返し、ほかの状態の片付けを続ける |
E2 か E3 |
| E5 | 片付けが、持ち主の生きている状態・作業の残る状態を残した | E4 と同じ時点 | 生きているかを判定できない → 残す(前提 4) | E1 |
| E6 | 次の実行が、以前の実行が積んだ投稿の項目を送ろうとして、GitHub が位置エラー(422 line could not be resolved)で拒んだ | post_review の flush |
— | 以前の実行の E1(積んだ) |
| E7 | その項目を待ち行列から外し、控えか総評に残した | E6 | 外せない → 今回分の送りも止まり failed になる(今の不具合。AC11 で塞ぐ) |
E6 |
| E8 | 次の実行が、以前の実行の残したレビュー worktree を見つけた | cross-review / cross-refactoring の開始 | 使えない形で残っている → 外すか作り直す(AC15) | 以前の実行の E1 |
用語
| 用語 | 意味 |
|---|---|
| 残る状態 | NDF の実行が作り、その実行が終わった後もディスクに残るもの(レビュー worktree・状態ファイル・一時ファイル・投稿キューの項目)(#2229) |
| 状態の持ち主 | 残る状態を作った実行。持ち主が生きている間は、別の実行がその状態を消さない(#2229) |
| 状態の寿命 | 残る状態を片付けてよくなる条件(持ち主のプロセスの終わり・Pull Request が閉じたこと・送り終えたこと など)。状態の種類ごとに 1 つ決める(#2229) |
| 以前の実行 | 今の実行より前に同じ Pull Request・同じリポジトリで回った、終わった実行。supervise.py の run --from の「前の実行」とは別(#2229) |
受け入れ条件
持ち主と寿命:
- AC1: NDF が実行をまたいで残す状態(対象範囲の 5 種と投稿キューの項目)が 1 か所の一覧にあり、各行に置き場・持ち主・寿命・片付ける時点が書かれている。一覧に無い種類の残る状態を NDF のスクリプトが作ると、テストが落ちる
持ち主の終わった状態は自動で片付く:
- AC2: マージ済みの Pull Request
nについてmerged-steps.py cleanupを実行すると、変更も未 push のコミットも持たない<NDF_WORKTREE_BASE>/<slug>/rf<n>(中のworkを含む)がpr<n>と同じく外れ、git worktree listからもディレクトリからも消え、結果のitemsにremovedで出る(NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085・cross-refactoring の一時の worktree(rf<PR番号>)が merged の後片付けで外されず溜まる #1680) - AC3: merged を通らずにマージされた、または閉じられた Pull Request のレビュー worktree(
pr<n>・rf<n>)で、変更も未 push のコミットも持たないものは、前提 3 の時点の後に残らない(NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085) - AC4: 持ち主のセッションが終わった
ndf-worktree-<session>.jsonは、前提 3 の時点の後に残らない(NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085) - AC5: 持ち主のプロセスが終わった
ndf-deps.*は、trapが走らずにプロセスが止められた場合も、前提 3 の時点の後に残らない(NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085) - AC6: 持ち主の実行が終わった
fix-pr<n>-*とndf-tests-*は、AC1 の一覧の寿命を過ぎた後、前提 3 の時点かテストのセッションの終わりの後に残らない
実行中の状態を別の実行が消さない:
- AC7: open の Pull Request のレビュー worktree は、どの片付けを回しても外れない(NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085)
- AC8: 前提: 実行 A が
pr<n>(またはrf<n>)を使って動いている。操作: 別の実行 B が同じリポジトリで片付けを回す。結果: A の worktree と状態ファイルは残り、B の結果のitemsにkeptと理由が出る。Pull Requestnが既にマージ済みでも同じ - AC9: 生きているセッション・プロセスの
ndf-worktree-<session>.jsonとndf-deps.*は、別の実行の片付けで消えない(NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085) - AC10: 未コミットの変更か未 push のコミットを持つレビュー worktree は外れず、結果の
itemsにkeptと理由が出る。片付けは--force/-Dを使わない(NDF の手順が TMPDIR に置く一時の worktree と状態のファイルが消されず溜まる(1 回分で数百 MB) #2085。rf1732/workの 23 件のような作業を消さない)
以前の実行の残りが次の実行を止めない:
- AC11: 投稿キューの先頭に以前の実行が積んだ
review-postの項目があり、GitHub がその行を 422(line could not be resolved)で拒むとき、今回のpost_reviewは今回分の review を送り終え、failedを返さない。先頭の項目は待ち行列から外れ、dropped/の控えか総評のどちらかに残る。テストの偽のghで確かめる(cross-review: 修正前のコミットの行を指す古い指摘が投稿の待ち行列の先頭で 422 になり、review が止まり続ける #2084) - AC12: AC11 の後、新しいラウンドで
post_reviewをもう一度呼ぶと、同じ項目の 422 で止まらない(待ち行列にその項目が積まれていない)(cross-review: 修正前のコミットの行を指す古い指摘が投稿の待ち行列の先頭で 422 になり、review が止まり続ける #2084) - AC13: 先頭の項目が一時の失敗(429・5xx・上限)で送れないときは、今までどおり外さず、今回分も順序を保って待つ(
waitableが真)(cross-review: 修正前のコミットの行を指す古い指摘が投稿の待ち行列の先頭で 422 になり、review が止まり続ける #2084) - AC14: 位置エラーでない恒久の失敗(422 でも line could not be resolved を含まないもの・400・404 など)の
review-postの項目と、別の実行が積んでまだ送りを試みていない項目は、今の扱い(飛ばさない・送る)を変えない。変えるなら設計 PR に理由を書く(cross-review: 修正前のコミットの行を指す古い指摘が投稿の待ち行列の先頭で 422 になり、review が止まり続ける #2084) - AC15: 以前の実行が残したレビュー worktree(同じ
pr<n>・rf<n>)があっても、次の cross-review / cross-refactoring の開始は失敗しない(使い直すか、外して作り直す)。未コミットの変更か未 push のコミットを持つものは消さず、開始を止めて理由を返す
退行しないこと:
- AC16: 既存のテスト(
plugins/ndf一式)が通る。merged-steps.py cleanupの結果の JSON の形(itemsのkind・state・理由)は、足す値を除いて変わらない
非機能の条件
| 大項目 | 条件 |
|---|---|
| 運用・保守性 | 片付けで外したもの・残したものは、1 件ずつ置き場・removed / kept・理由が結果の JSON の items に出る。残したものは、人が手で外す前に理由を読める |
| 性能・拡張性 | 片付けが GitHub へ問い合わせるのは、レビュー worktree 1 本につき Pull Request の状態の照会 1 回まで(一時ファイルの片付けは GitHub を呼ばない) |
| システム環境 | Linux(devbase のコンテナ。/tmp が書き込み層で再起動しても消えない)と macOS の両方で同じに動く。一時の置き場は NDF_WORKTREE_BASE → tempfile.gettempdir() の順のまま |
影響
| 対象 | 影響 |
|---|---|
| 公開インタフェース | 変わる(互換あり)。merged-steps.py cleanup の items に rf<n> などの行が増える。投稿キューは、以前の実行の位置エラーの項目を外すようになる。引数は変わらない |
| データ | 状態に持ち主を記録する形が足される(形は設計が決める)。持ち主の記録が無い既存の残骸は、前提 4 と AC3〜AC6 の判定に従う |
| 既存の振る舞い | merged の後片付けで rf<n> が外れる。cross-review / cross-refactoring / merged の実行の後に、持ち主の終わった一時のものが消える |
検証手段
| 項目 | 手段 |
|---|---|
| テスト | uv run --frozen --project . --all-extras pytest plugins/ndf -q -n 4 |
| 静的解析・型検査 | CI の既存のジョブ(python3 scripts/check-skill-frontmatter.py・python3 plugins/ndf/scripts/instructions-check.py --root .)。新しい検査は足さない |
| 手動確認 | 配布の後、手元の devbase のコンテナで /tmp/ndf-worktrees/devbasex--ai-plugins/ の rf*(2026-10-31 時点で 4 本 2.0G)と ndf-worktree-*.json(79 件)・ndf-deps.*(26 件)が、cross-review か merged を 1 回回した後に、持ち主の生きているもの・作業の残るものを除いて消えたことを数えて確かめる(リリース後テスト) |
前提とする取り決め
| 項目 | 参照先 / 決めたこと |
|---|---|
| プロジェクト構造 | AGENTS.md。NDF の共通のコードは plugins/ndf/scripts/lib/、Skill 固有は各 Skill の scripts/。cross-review と cross-refactoring で同じ役割の関数を分けない(Value 6) |
| コーディング規約 | AGENTS.md の「最小限のコード実装」。結果は統一の JSON レポート(tool・status・items)で返す |
| テスト戦略 | 片付けの判定(持ち主の生存・PR の状態・変更の有無)は、一時の git リポジトリと偽の gh を使う単体テストで確かめる。待ち行列は既存の test_a_position_rejection_of_an_earlier_item_is_not_taken_as_ours を AC11 の形へ書き換える。.md の文言を照合するテストは書かない |
境界
| 区分 | 内容 |
|---|---|
| 常に行う | 既存テストの実行、片付けの前の未コミットの変更・未 push のコミットの確認、残したものの理由の出力 |
| 確認してから行う | 結果の JSON の形の変更、持ち主の記録の形(承認ゲート 1 の設計で人が見る) |
| 行わない | --force / -D での削除、open の Pull Request の worktree と開発 worktree の削除、常駐のプロセス・cron の追加、#2221・#2164・#2165 の修正 |
未決
| 項目 | 誰が決めるか | 期限 |
|---|---|---|
AC11 で外した項目を dropped/ の控えに残すか総評へ移すか |
設計(design) |
設計 PR |
| 持ち主の記録の形と、終わったかの判定の手段(プロセスの生存・PR の状態・時刻) | 設計(design) |
設計 PR |
fix-pr<n>-*・ndf-tests-* の寿命(時刻で切るか、持ち主の終わりで切るか) |
設計(design) |
設計 PR |
| AC1 の一覧の置き場(コードの表か確定仕様か) | 設計(design) |
設計 PR |
進行
モード: — / 進め方: auto
- 要求と受け入れ条件 — 2026-10-31 01:59
- 作業場所の用意 — 2026-10-31 03:59
- 設計 — 2026-10-31 02:02
- 素材の収集と出典の確定
- ドキュメント再構成
- ドキュメントレビュー
- 計画 — 2026-10-31 03:59
- 実装 — 2026-10-31 05:36
- 構造改善 — 2026-10-31 07:37
- 実装レビュー — 2026-10-31 09:12
- 完了判定 — 2026-10-31 06:18
- Pull Request — 2026-10-31 06:41
- 確定仕様化 — 2026-10-31 11:00
- 後片付け — 2026-10-31 10:57
- 配布 — 2026-10-31 10:33
- 体裁レビュー
- リリース後テスト — 2026-10-31 10:56
- 振り返り — 2026-10-31 11:00
- 棚卸し — 2026-10-31 11:21
振り返り: #2282 (comment)
リリース後テスト(v10.27.2)で残ったこと
AC1〜AC16 はすべてテストで合格。AC3〜AC5 の手動確認(手元の残骸が片付けの後に減ること)は保留(記録: #2281 (comment) )。
- 10.27.2 の片付けは、持ち主の記録の無い残骸を最後に動いた時刻から 24 時間(寿命の窓)残す。2026-10-31 20:08 JST の時点の残骸(
/tmp/ndf-worktrees/devbasex--ai-plugins/の rf 6 本・pr 3 本、ndf-worktree-*.json68 件、ndf-deps.*57 件)はすべて窓の中で、削除を止めた判定の再現では全件 kept だった - 確かめ直す時点: 持ち主の記録が残る rf2312 が窓を出る 2026-11-02 02:29 JST より後に、cross-review か merged を 1 回回してから件数を数える。合格の行を同じ記録の PR(Release: ndf v10.27.2 #2281)へ足し、
sprint-close.py --record-pr 2281 --with-verificationで閉じる - 2026-11-01 17:23 JST の時点の残骸は rf 3 本・pr 2 本(1.5G)、
ndf-worktree-*.json30 件、ndf-deps.*5 件。削除を止めた片付けの模擬では、rf2268 は窓を出て removed、pr2277・pr2278 は Pull Request が open のため kept(AC7)、rf2283 は未コミットの変更 1 件のため kept(AC10)、rf2312 はfinished_atが無いまま持ち主の窓の中で kept(cross-refactoring の init が着手前テストの打ち切りで止まると、持ち主の記録が残り次の実行を 24 時間断る #2321 に当たる見込み)
Activity
Docs: #2229 の設計で持ち主の記録の更新を同じ実行に限り、片付けの結果を状態ごとに 1 件へそろえる
docs(#2229): 廃止語「投稿の待ち行列」を「投稿キュー」へ言い換える
Merge pull request #2244 from devbasex/design/issue-2229
Merge: develop を取り込み、用語集の衝突を解く(#2229 の設計の語と #2227 の設計の語を両方残す)
fix(glossary): 片付けの関数名を用語集の正本(glossary.json)へ反映し、glossary.md と揃える(#2229)
Merge pull request #2264 from devbasex/feat/issue-2229-m26-1
takemi-ohama commentedon Nov 1, 2026
スプリントの終わりの工程を通りました