Skip to content

v0.29.2

Choose a tag to compare

@Kewton Kewton released this 31 Aug 12:12
· 27 commits to main since this release
3b2b0e5

[0.29.2] - 2026-08-31

Highlight: 2026-08-25 の利用者報告 4 件(リロード時のサイドバー空表示・Verification の使い方が解らない・既定エージェントが固定・codex が更新できない)を Epic #2071 として一括で解消したリリース。あわせて、全テスト green のまま Unit Tests を exit 1 にしていた未回収 setTimeout を 8 コンポーネントぶん回収した(無関係な PR の CI を 2 回赤にした実績があり、押下フィードバックとコピー確認の両方を共有フックへ一本化して再発経路を塞いだ)。DB マイグレーションなし。

Added

  • feat(ui): Verification のゲート選択再実行・実行中ランの中止・履歴拡張・ゲートログ全文表示を追加 (#2063): 検証ペインは #1816 以来「全ゲートを実行する」以外に何もできず、lint の 1 行修正を確かめるためだけに 1800 秒の build と mutex 付きの unit / integration を道連れにするしかなかった。(1) ゲート選択: POST …/verify は #1543 から gateIds(1..32)を受け付けていたのに verification-api.ts の呼び出しが {} しか送っておらず、型定義だけが在って死んでいた。サーバの plannedGateIds(組み込みゲート込み)をチェックボックスで選び、「不合格だったゲートだけ」ショートカットで直前ランの失敗集合(FAILING_GATE_STATUSES。skipped は含めない=断られたゲートはもう一度断られるだけ)をそのまま送れるようにした。全件選択は gateIds を送らないことで表現する — 省略時の scope ゲートは implicit で契約なしランの skip が赦されるのに対し、scope を名指しすると explicit になって skip が集計に効くため、両者は別のリクエストであり既定経路の挙動は 1 bit も変えていない。(2) 中止 API: POST /api/worktrees/:id/verify/runs/:runId/cancel を新設。gate-runner に run 単位のキャンセルスイッチ(RunCancellation / cancelVerification())を入れ、実行中ゲートのプロセスグループへ SIGTERM→5 秒→SIGKILL を送ってから run を cancelled で閉じる(status 列だけ書き換えると npm run build が worktree に居座ったまま UI だけ終わったことになる。ゲートは元から detached で spawn されているのでグループに届く)。中断されたゲートと未到達のゲートは理由つきの skipped 行として記録し、aggregateRunStatus を通さず直接 cancelled を返す(操作者の意図的な停止を「判定不能」と読ませない)。#2062 が残していた verification.runStatus.cancelled の生成元がこれで、verifying → cancel → cancelled という到達不能だった task 遷移もつながった。応答は 200(閉じた)/ 202(シグナル済みで収束待ち)/ 409(既に判定済み、またはこのプロセスが実行していない孤児)に分けている。(3) 履歴: DEFAULT_RUN_LIST_LIMIT = 10 固定だったランキャッシュに「もっと見る」(route の上限 100 まで 10 件ずつ)を付け、#1593 から Web が一度も読んでいなかった GET /api/verification/runs(worktree 横断履歴)を折りたたみブロックとして読むようにした(開いたときだけ取得。1 ブランチで赤いゲートは作業への判定だが 6 ブランチで赤いゲートはゲートへの判定、という比較が verify.yaml 調整の実際の問い)。(4) ログ全文: 行の末尾 40 行表示に加えて、保存されている log_tail 全量をモーダルで表示しコピーできるようにした(「全文は commandmate verify show」という端末への送り返しが、判定をスマホで読むための画面から出ていた)。保存されているのは options.maxLogTailBytes で切られた末尾だけであることをモーダル自身が明示する。実プロセスを使う統合テスト(tests/integration/verification-cancel-2063.test.ts)でゲートの shell とその背景子プロセス双方の pid が消えることを実測し、プロセスグループへ送らない変異では赤になることを確認済み。
  • feat(ui): エージェントペインに「この構成を既定にする」と「未変更のブランチにも適用する」を追加 (#2067): #2065 は新規ブランチの既定エージェントを設定可能にし、#2066 はリポジトリ単位の宣言を足したが、どちらも「すでにあるブランチ」には触らない設計だったため、1 か月動かした環境では全ブランチが古いタブ順のまま残り、直す手段は 1 本ずつ開いて並べ替えることしか無かった(CHANGELOG #1516 が「既存分を揃えるマイグレーションは別 Issue」と書いたその Issue が未作成だった)。アクティビティバー「エージェント」のペイン末尾に開閉式パネルを追加し、いま並んでいるロスターのツール順(同一ツールの複数インスタンスは 1 つに畳む)を #2065 の PUT /api/settings/default-agents にそのまま書き込む「既定にする」と、GET/POST /api/worktrees/apply-default-agents で未変更のブランチにだけ書き込む一括適用を置いた。「未変更」は worktrees.selected_agents IS NULL かつ agent_instances の行が無いことで、これは #2066 が repo 宣言を差し止めるときに見るのと同じ 2 条件・同じヘルパ(getUnchangedAgentWorktreeIds())であり、手で並べ替えたブランチも別名を付けたブランチも書き換わらない。適用は確認ダイアログの手前で件数を表示し、クリック時点で件数を読み直してからダイアログに出すので、表示件数と実際の更新件数(サーバが返す updated)が食い違わない。パネルは開くまで 1 リクエストも出さない(#2054 の「claude / codex だけのロスターはゼロリクエスト」条件を維持)。あわせて ActivityPane の ErrorBoundary 名が agent に対して実体と違う 'AgentSettingsPane'(モバイルの checkbox ペイン)を報告していたのを 'AgentInstancesPane' に修正した。
  • feat(repo-config): リポジトリ単位の既定エージェントを .commandmate/agents.yaml で宣言できるようにする (#2066): #2065 が入れた resolveSelectedAgents() の SELECTED_AGENTS_LAYERS には repo 層が宣言だけされていて値が来ていなかった(['worktree','repo','appSettings'])。#2066 はその 1 層に値を供給しただけで、解決関数の本体も層の順序も組み替えていない — 優先順位は worktree -> repo ファイル -> app_settings -> 定数 になり、値を渡すのは src/lib/db/worktree-db.ts の getWorktrees() / getWorktreeById() の 2 箇所だけ(getWorktrees() は repository_path ごとに 1 回だけ解決する)。宣言は agents: [codex, claude](任意で primary:)で、検証は validateAgentsPair() を共用(2〜6 個・重複なし・CLI_TOOL_IDS のいずれか=app_settings と同一の制約)。壊れている/値が不正なときは例外を投げず repo-agents:* の警告ログを出して次の層に落ちる(fail-open。空ファイルとコメントのみのファイルは「宣言していない」と読んで無音)。ファイルは scanWorktrees() が git worktree list の成功後に refreshRepoAgentsConfig() を呼ぶ形で同期のたびに読み直し、getWorktrees() 自身はメモリ(TTL 60 秒)を見るだけにした — サイドバーのポーリング経路にファイルシステムプローブを生やさないという #1913 の規則を守るためで、否定的な答えもキャッシュするので壊れた宣言の警告はポーリングごとではなく同期ごとに 1 回になる。agent_instances を既に持つ worktree には宣言を適用しない(ロスターだけでなく selectedAgents にも): /sessions と Review タブは wt.selectedAgents を読んで agentInstances を見ないため、これが無いと「タブは変わらないのにチップだけ宣言どおりに描き変わり、実際に動いているエージェントが稼働中一覧から消える」。PATCH /api/worktrees/[id] は agentInstances と selectedAgents を独立した分岐で書くので、この状態は日常的に発生する。CMATE.md にキーを置く案は採らなかった(CMATE.md は worktree ルートから読む=リポジトリのロスターが存在する前に読めない/Markdown の表なので順序つきリスト+primary を 1 セルに押し込むことになる/parseCmateConfig() はスケジュール実行経路なので巻き添えが大きい)。設計は docs/design/repo-agents-config.md。
  • feat(agents): エージェント CLI の版表示と、pane の外で走る codex 更新導線を追加 (#2069): CommandMate には CLI の版表示も更新導線も無く、src/cli/config/cli-dependencies.ts には codex の行すら無かった。commandmate agents versions / agents update <tool>・More 画面の「設定」・エージェント一覧ペインの 3 面から更新できるようにし、更新はエージェントの tmux pane ではなく CommandMate 自身の子プロセスで実行して出力をそのままストリームする(codex の「Update now」は codex を終了させてからインストーラを動かし自動再起動しないため、pane 内で走らせると pane が素のシェルに落ちる=#2070 の状態になる)。実行コマンドは codex 0.149.0 以降なら codex update、未満または codex が PATH に無ければ npm install -g @openai/codex@latest で、いずれも execFile に argv 配列を渡し(シェル文字列連結なし)、findExecutableOnPath() で絶対パスへ解決してから起動し、env は sanitizeEnvForChildProcess() を通す(PATH は起動シェル由来のまま)。リクエスト本文が触れるのはツール ID 1 個だけで、それは UPDATABLE_AGENT_TOOLS で検証したあと捨てられる(argv はモジュール内のリテラル)。「更新あり」はネットワークを使わず codex 自身が書く ~/.codex/version.json(latest_version / dismissed_version)を読み、他ツールは --version の installed 版のみを表示する。ファイルが無い/壊れている/型が違う場合は fail-open で「更新情報なし」になる。対象ツールのセッションが稼働中なら「再起動が必要」を警告し、インスタンス単位の再起動ボタン(kill-session)を出す。cli-dependencies.ts に Codex CLI(codex --version, required: false)を追加したので commandmate init / status が codex の版を表示する。隔離環境実測(2026-08-31, 隔離 HOME + 隔離 npm prefix + tmux -L cm2069): agents update codex --yes で codex update が走り 0.149.0 → 0.151.0、その間に走らせていた codex TUI の pane は pane_pid 38581 のまま無傷、利用者のグローバル codex(Homebrew 0.149.1)は未変更。
  • feat(settings): 新規ブランチの既定エージェントを More 画面から設定できるようにした (#2065): 新しく発見された worktree のタブは DEFAULT_SELECTED_AGENTS = ['claude','codex','antigravity'] というコンパイル時定数からしか作られなかった(upsertWorktree は selected_agents を書かず、scan/sync は agent_instances も作らないので、新規 worktree は必ず定数の順序・定数の primary になる)。app_settings.default_selected_agents(2〜6 件・CLI_TOOL_IDS 内・重複なし・先頭が primary)と GET/PUT /api/settings/default-agents を新設し、More 画面に順序変更つきの設定カード(インストール済み CLI を併記。isInstalled() の子プロセス起動は ?include=installed を付けたこの画面だけが払い、30 秒 TTL + single-flight でキャッシュする=#1913 の「ホットパスで await しない」規約)を追加した。フォールバックの順序は resolveSelectedAgents() 1 箇所に集約し、worktree -> repo -> app_settings -> 定数 の層を配列で宣言する(repo 層は #2066 が値を渡すだけで挿さるよう宣言だけ済ませ、本 Issue では実装しない)。サーバ側は parseSelectedAgents(raw, appSettingsDefault) と resolveAgentInstances() の 2 経路が、クライアント側は 8 箇所のフォールバック(src/types/sidebar.ts ×2 / src/app/sessions/page.tsx / src/components/review/ReviewTab.tsx / src/hooks/useWorktreeDetailController.ts ×4)が同じ解決関数を通る。設定が無い環境の挙動は定数のままで不変、既存 worktree の agent_instances 行も書き換えない(agent_instances があればそれが権威で、早期 return はそのまま)。GET /api/worktrees は defaultSelectedAgents を無条件で載せ、/api/capabilities にはトークン default-selected-agents のみを足す(値は載せない — この応答は認証前に読めうる面なので、#1925 / DR4-008 のとおりインストール構成を一切反映しないコンパイル時固定リストのまま)。
  • feat(verification): Verification ペインに 4 状態のオンボーディングと verify.yaml 起案導線を追加 (#2061): ペインは .commandmate/verify.yaml を読んでいなかったため、ゲートを 1 件も宣言していないリポジトリと「宣言はあるが 1 度も走らせていない」リポジトリが画面上で同一で、未作成のまま「再検証」を押した唯一の手がかりは失敗した run の config ゲートに入る英文 1 行(.commandmate/verify.yaml not found in …)だけだった。読み取り API GET /api/worktrees/:id/verify/config(exists と error は独立 —— 在って壊れている状態は 200 + exists:true + error で返し、「宣言してください」と言われた操作者が 2 本目のファイルを書きに行くのを防ぐ)を新設し、ペイン先頭に 2 行の説明と 未作成 / 宣言済み・未実行 / 実行中 / 結果 の 4 状態(+読み取り未着の unknown)を別の文言・別の CTA で描画する(tests/unit/components/worktree/VerificationPaneOnboarding.test.tsx がスナップショットで固定)。起案は commandmate verify init(--dry-run / --json / --cwd)と Web の「CI から起案する」ボタンが src/lib/verification/verify-draft.ts の同一実装を呼ぶ —— .github/workflows/*.yml の各 run: と package.json の scripts を読み、何度でも安全に再実行できるコマンドだけをゲートにして、拒否したものは 14 種の理由つきで報告する(本リポジトリの CI からは lint / typecheck / unit / build を含む 11 ゲートを起案し、npm ci / npm audit / npm publish / test:e2e / watch モードの npm test は拒否する)。既存ファイルは決して上書きしない(--force は用意せず、書き込みは flag:'wx')。verify init は verify 系で唯一サーバを必要としない —— verify.yaml がまだ無い段階で走らせるコマンドなので、commandmate start を前提にすると起動の前提がその起動自身になる。空状態の CTA は commandmate verify <worktree-id> という補間されない literal だったので、実際の worktree id を補間するようにした。

Changed

  • fix(ui): 検証ペインの判定語彙を日本語化し、判定の意味・CLI exit code・実行されなかった理由を画面に出す (#2062): verification.runStatus.* / verification.gateStatus.* は DB の生トークン(passed / not_started / SKIP / TIMEOUT)を ja と en で同一値のまま表示しており、日本語表示は事実上未翻訳、かつどちらの言語でも語の意味が画面のどこにも無かった。判定語を各言語の語(合格 / 不合格 / 未着手 / 判定不能、Passed / Failed / Not started / Could not judge)に置き換え、判定ごとに 1 行の gloss(verification.runStatusGloss.* / gateStatusGloss.*)を追加。not_started は「作業証跡ゼロ: コミットも未コミット変更も無い」= CLI が exit 21 を返す判定であって不合格ではないことを明示する。CLI との対応は綴りの一致ではなく明示された exit code で示す方式に変更し、検証ラン節に RUN_STATUS_EXIT_CODE から組み立てた凡例(合格 = exit 0 / 不合格 = exit 20 / 未着手 = exit 21 / 判定不能 = exit 99)を、各ラン行に CLI exit=<code> を出す。aggregateRunStatus が skipped を 1 件でも含むランを error に昇格させる仕様のため、options.skipInPrimaryCheckout(サーバープロセスの作業ディレクトリでコマンド系ゲートを断る保護)が原因不明の赤いラン=製品の不具合に見えていたので、選択中ランの判定バナーが実行されなかったゲート名と理由(primary-checkout / work-evidence 落ち / mutex 待ち / 契約未接続 / 契約なし / requireScopeClean:false / 不明)を列挙する。組み込みゲート work-evidence / scope / env-clean / config は生 id のままだったのでゲート行に説明文を、[contract] マーカーには意味の注記を追加。verification.runStatus.cancelled はキーを残したまま文言だけ整えた(生成元は #2063 のキャンセル API が入るまで存在しない)。判定語の合意はテストで固定(生トークンの再発と ja==en の再発を tests/unit/i18n/verification-vocabulary-2062.test.ts が、CLI exit code の写しの drift を tests/unit/verification/run-verdict-vocabulary-2062.test.ts が、docs/UI_UX_GUIDE.md の Activity 一覧と ACTIVITIES の一致を tests/unit/docs/ui-ux-guide-activities-2062.test.ts が禁じる)。あわせて docs/UI_UX_GUIDE.md の Activity 一覧を 6 項目から src/config/activity-bar-config.ts の 10 項目へ更新(todo #1015 / skills #1441 / verification #1816 / env #1968 が未反映だった)。
  • feat(cli-tools): codex の更新ダイアログへの応答を方針化し、既定を「次の版まで出さない」に変更 (#2068): CommandMate は codex の Update available! ダイアログに Issue #890 以来つねに 2(Skip)を送っていたが、2 は何も記録しない — codex-cli 0.149.1 を隔離 CODEX_HOME で実測したところ(2026-08-31)、2 は version.json の dismissed_version を null のまま残し、次回起動でまた同じダイアログが出る。結果として毎回の起動でダイアログが出て毎回サーバーが勝手に閉じるため、利用者は CommandMate 経由で更新を選べなかった。応答方針を CM_CODEX_UPDATE_DIALOG で選べるようにし(skip=2 / skip-until-next-version=3(既定) / update=1 / ask=送らない)、既定を 3 に変更した(実測: dismissed_version: "0.151.0" が書かれ、次回起動でダイアログは出ない)。update は codex が npm install -g @openai/codex に置き換わって終了したあと、同じ pane へ起動コマンドを送り直す(#2070 の「シェルに落ちた pane」機構に乗るが、判定は judgeToolLiveness ではなく新設の findShellPromptTail — npm install の出力が 3 行しかないため、死んだ › 1. Update now 行が #2070 の 12 行 alive 窓に残って exit を打ち消すことを実測したため)。ask は waitForReady が自動応答せずポーリングを続け、ダイアログは detectPrompt が 3 択の multiple_choice として報告しているので PromptPanel でそのまま人が選べる。Auto-Yes は全方針で従来どおりこのダイアログに答えない(Issue #1829 のガードを維持、ask を含めて回帰テスト済み)。認識できない値は既定にフォールバックする(updates が update として解釈されることはない)。claude / opencode / copilot / gemini / antigravity の挙動は不変。

Fixed

  • fix(ui): コピー確認フィードバックの未回収タイマーを共有フックに一本化して回収する (#2180): コピー確認(COPY_FEEDBACK_RESET_MS=2000ms)の setTimeout を id を持たないまま張っていた 3 ファイル(MarkdownEditor / FileViewer の content・path 2 系統 / review/ReportTab)で、コピー直後 2 秒以内に unmount するとコールバックが消えたツリーへ setCopied(false) を書きに行っていた。ブラウザでは無害(React が torn-down root への更新を捨てる)だが jsdom では window 撤去後に発火し、全テスト green のまま Unit Tests が exit 1 になる — 落ちるのは当該ファイルを差分に含まない無関係な PR で、押下フィードバック側の同型(#2174)は実際に PR #2170 / PR #2173 を赤にしている。窓が 150ms ではなく 2000ms のぶんコピー側の方が踏まれやすい。#2176 の useKeyPressFeedback にならって src/hooks/useCopyFeedback.ts を新設し(id を useRef に持ち、次のコピー(前回の残り時間を引き継がず張り直す)・reset()・unmount の 3 箇所で clearTimeout)、既に pathTimerRef / contentTimerRef で自前回収していた WorktreeDetailSubComponents(path / repo path)と FilePanelContent(path / content)も含む 5 ファイル 8 ボタンすべてを移行した。1 コンポーネントに 2 系統ある画面(FileViewer / WorktreeInfoFields / FileToolbar)はフックをボタンごとに 1 インスタンス呼ぶので、片方を押しても他方の確認表示は自分の 2 秒で消える。見た目は不変(クリップボード書き込み解決と同時にチェックマークが出て COPY_FEEDBACK_RESET_MS 後に戻る)。変異注入で検証済み(unmount の clearTimeout 削除で 8 テスト・再 arm の clearTimeout 削除で 6 テスト・reset の clearTimeout 削除で 1 テスト・FileViewer の 2 インスタンスを 1 本に畳むと 2 テストが赤)。併せて ui-feedback-config.ts の陳腐化した Site: コメント 3 件(KEY_PRESS_FEEDBACK_RESET_MS は #2176 以降 useKeyPressFeedback が唯一の import 元、NAV_KEY_REFRESH_DELAY_MS は useSpecialKeys / UnsentComposerBar)を実測に合わせた。
  • fix(ui): 押下フィードバックの未回収タイマーを 3 コンポーネントぶん回収し、共有フック useKeyPressFeedback に一本化 (#2176): TerminalEscapeHatch / NavigationButtons に、#2174(PR #2175)が OpencodeQuickKeys で直したのと同型の未回収 setTimeout が残っていた(どちらも clearTimeout も useRef も持たず、押下から KEY_PRESS_FEEDBACK_RESET_MS=150ms 以内に unmount するとコールバックが消えたツリーへ setActiveKey(null) を書きに行く)。3 つとも useSpecialKeys transport は共有するがタイマーは各ファイルが個別に持っていたため #2175 の修正は届いていない。ブラウザでは無害(React が torn-down root への更新を捨てる)だが jsdom では window 撤去後に発火し、全テスト green のまま Unit Tests が exit 1 になる — しかも落ちるのは当該ファイルを差分に含まない無関係な PR(#2174 は実際に PR #2170 / PR #2173 を赤にした)。修正を 2 回コピーする代わりに、タイマー id を useRef に持ち次の押下(前回の残り時間を引き継がず張り直す)と unmount の両方で clearTimeout する実装を src/hooks/useKeyPressFeedback.ts へ抽出し、OpencodeQuickKeys を含む 3 ファイルすべてを移行した(4 つ目のキー列を足す人が同じ穴を開けないため)。フックは押下ハイライトだけを所有しキー送出には触れないので useSpecialKeys の挙動は不変。見た目も不変(押下で同期的に点灯し KEY_PRESS_FEEDBACK_RESET_MS 後にきっかり消灯)。押下直後 unmount で Unhandled Error が出ないことを 2 ファイル+フック単体で固定し、clearTimeout 2 箇所それぞれの除去で赤くなることを変異注入で確認済み(unmount 側の除去で 9 件、再 arm 側の除去で 8 件が赤)。
  • fix(ui): opencode クイックキーの押下フィードバックタイマーを回収する (#2174): OpencodeQuickKeys の handleClick が setTimeout(() => setActiveId(null), KEY_PRESS_FEEDBACK_RESET_MS) を id を持たずに撃っており、次の押下でも unmount でも clearTimeout されなかった。押下から 150ms 以内に pane が消えると、コールバックは消えた React ツリーへ setActiveId(null) を書きに行く。ブラウザでは無害(React が捨てる)だが jsdom では違い、タイマーが自分を仕込んだテストより長生きすると window を撤去済みの環境で発火し、たまたま走っている別テストの Unhandled Error として現れる。タイマー id を useRef に持たせ、次の押下(前回の残り時間を引き継がず 0 から張り直す)と unmount の両方で clearTimeout する形に変更した。作法は同じツリーの他の一時フィードバックタイマー(CopyButton / TruncationTooltip / MemoCard)に揃えてあり、見た目は不変 — ハイライトは押下と同時に付き、KEY_PRESS_FEEDBACK_RESET_MS(150ms)後にちょうど消える。混入元は Issue 本文が挙げる #2131 (PR #2160) ではなく、strip 自体を追加した #2046 (ceb1059d):git log -1 -- <file> はファイルの最終コミットを返すため #2160 に見えるが、git blame を当該行に打つと ceb1059d が出る。同型の未回収タイマーは TerminalEscapeHatch と NavigationButtons にも残っているが、いずれも #2174 の scope 外のため本 PR では触っていない。
  • fix(session): tmux セッションだけが残りツールが終了した pane を「稼働中」と報告し、次の send が復旧不能になる問題を修正 (#2070): 「セッションは在るがツールが落ちた」検出は worktree-status-helper.ts の cliToolId === 'claude' 分岐 1 箇所に閉じていたため、codex の「Update now」(codex を npm install に置き換えて終了する)・Ctrl+C 二度押し・クラッシュで pane が素のシェルに戻っても has-session は yes を返し続け、サイドバーは緑のまま・startSession は既存セッションを見て何もせず・send は waitForPrompt がタイムアウトして手動 kill-session 以外に復旧手段が無かった。判定を ICLITool.livenessSpec()(ToolLivenessSpec:ツール自身の prompt-ready パターンの否定+シェルプロンプトの肯定という連言)へ移し、共有ルール judgeToolLiveness() を claude / codex / copilot / opencode / gemini / antigravity / vibe-local の 7 ツールに広げた上で、各ツールの launchSession() 再利用経路が「ツールが動いていない」と 2 回続けて読めたときに同じ pane へ起動コマンドを再送し、sendMessage() 冒頭でも同じ復旧を行うようにした(opencode は死んだ port と subscription を releaseOpencodeEventStream() で手放してから再割当てする)。claude の既存判定は値・分岐順序・reason 文字列まで不変で、isSessionHealthy() はその spec への委譲になっている。aliveTailLines(末尾 12 行に限定)と shellPromptPatterns(user@host … % の肯定形)が両方必要なことは実測に基づく: codex 0.149.1 の終了済み pane には trust ダイアログの › 1. Yes, continue が約 1000 行上に残っており全画面検査では永久に alive になり、その zsh プロンプトはちょうど 40 文字=claude の MAX_SHELL_PROMPT_LENGTH と同値で長さゲートも素通りする(2026-08-31 / 専用 tmux socket / 200x1000 実測、tests/fixtures/tool-liveness-2070/)。サイドバーと commandmate ls には理由コード exited を additive に追加し(既存 reason の値・意味は不変)、idle(起動していない)と idle (exited)(落ちた)を区別できるようにした。
  • fix(ui): Verification の入口を契約なしのブランチにも出し、理由をタッチで読めるようにした (#2064): ヘッダの VerificationStatusChip が if (!task) return null で契約を送っていないブランチでは丸ごと消えていたため、Verification を知らない人ほど入口に辿り着けなかった問題を修正。task 行が無いときはペイン名(verification.title)+RESULT バッジ「未検証」を描き、クリックでこれまで通りペインを開く(run が無いときのバッジも従来の — から「未検証」に変更)。判定理由はこれまで aria-label / title にしか無くタッチ端末からは読めなかったので、チップ右端に ⓘ トグルを足して同じ文言をポップオーバーに出す(Escape / 外側タップ / ペインを開く操作で閉じる。hover-reveal は使っていないのでマウスとタッチで見えるものが一致する)。不合格ゲート名は両シェルとも「ペインで選択中の run が最新 run と同じとき」だけ渡される値だったため古い run を選ぶとヘッダから消えていたが、チップ側で run id をキーに直前の行を保持するようにして解消(新しい run が最新になったら破棄するので、前 run の失敗を新 run の verdict の下に出すことはない)。あわせてモバイル Tools タブの verification サブタブが verification state 未指定時に消える条件を撤去し、NotesAndLogsPane / MobileContent の verification prop を必須化して、常時表示の PC Activity Bar と到達可否が型で一致するようにした。