Repository navigation
v0.42.0
[0.42.0] - 2026-09-27
Highlight: 同じマシンで複数の CommandMate サーバを動かすと、同名の tmux セッションを奪い合い、別サーバのエージェントの画面が見えて送信・停止までできていた問題を直した(#2865・#2866・#2867。セッション名にサーバごとの名前空間を入れ、旧名のセッションは作業ディレクトリが一致するものだけ旧名のまま引き継ぐ)。codex 0.157 への追従として、
/modelと trust の画面の判定(#2868・#2884)と、共有デーモン経由で hook の送信元インスタンスが取り違えられる問題(#2874・#2891、実機でcodex-3の返答がcodex-3に入ることを確認)を直した。送れない選択ウインドウでは Send を出さずに直接入力モードへ案内する(#2869・#2870・#2887・#2888)。
Added
-
feat(verify): env-clean が
$HOME直下の無関係なエントリで不合格にならないよう、verify.yaml のoptions.envCleanIgnoreHomeEntriesで名前ごとに除外できるようにする (#2890): 2026-09-27 の/orchestraterun で、ワーカーのコマンドと結び付かない.semgrep(並行ワーカー 3 本すべてに出た)と.commandagent(別プロジェクトのツールが作った)がhome-entriesのunattributedな追加として 7 本中 5 本の検証を exit 20 にしていた。除外できるのはVOLATILE_ENTRY_PATTERNSの固定リストだけで、リポジトリごとに足す手段が無かった。options.envCleanIgnoreHomeEntries(文字列の配列、既定[]、最大 32 件)を足し、$HOME直下のエントリ名との完全一致(glob・正規表現なし。.semgrep-xは.semgrepに一致しない)でhome-entriesprobe の追加・削除の両方を数えないようにした(commandmate-entriesなどほかの probe には効かない)。除外は比較の段階(diffEnvSnapshotsの 4 つ目の引数{ ignoreHomeEntries })だけで、採取済みのベースラインは書き換えないので、後からリストに足した名前は既存 task にも効く。除外した項目は黙って消さず、レポートにignored (options.envCleanIgnoreHomeEntries): .semgrepの 1 行で出す。配列でない・文字列でない要素・空文字・/や NUL を含む・./..・33 件以上はVerifyConfigError(実行はerrorで終わり、無視して通すことはしない)。このリポジトリの verify.yaml への追加は含まない(未対応のサーバが未知キーで設定エラーにするため、マージ・再ビルド後に足す)。仕様は docs/design/task-contract.md §2.6。 -
feat(worktree): Send が効かないプロンプトのウインドウから直接入力モードへ案内する (#2869): 同じウインドウ(
type・question・選択肢ラベルの指紋が同じ)が Send の後も続けて 3 回表示されたとき(Send が 2 回続けて画面を変えなかったとき)、PC のPromptPanelとスマホのMobilePromptSheetに「送信がうまくいかない場合は、直接入力モードで操作してください。」と「直接入力に切り替える」リンクを出す。PC はそのペインの直接入力バーを開き、スマホはターミナルタブ・ターミナル表示へ切り替えてから直接入力キーボードを開く(セッションが動いていないときはリンクを出さない)。数えるのは新規 hookusePromptStuckCounter(PROMPT_STUCK_THRESHOLD=3、ウインドウが 10 秒消えたら/対象が変わったら 0 に戻す)で、送信の成否ではなく画面が変わったかどうかだけで判定する。
Changed
-
feat(cli): CLI がサーバの公開するセッション名で tmux セッションを扱う (#2867): #2866 でセッション名が
mcbd-{ns}-{cli}-{worktreeId}[-{suffix}]になり、ns はサーバの DB にあって CLI では計算できないため、サーバがGET /api/worktreesのトップレベルにtmuxSessionNamespace、GET /api/worktrees/[id]のagentInstances[].sessionNameに実際に使っている名前(旧形式の採用を反映)を返すようにした。commandmate attachはsessionNameでhas-session・attach し、commandmate instancesのTMUX_SESSION列(--jsonのtmuxSession)もsessionNameを表示し(どちらも古いサーバでは旧形式を組み立てる)、ls --jsonのtmuxSessionはtmuxSessionNamespaceから新形式を組み立て(旧形式で採用中のセッションも新形式名で表示される一覧用の近似)、whoamiはlib/cli-tools/session-name.tsのparseSessionNameで新旧両方の形式を分解する。CM_LIVE_ATTACH_HOOK=onの live attach hook も新形式の claude セッションで発火する。古い CLI は新しいサーバのセッション名を組み立てられない。CLI とサーバを同じ版にすること。 -
feat(session): tmux セッション名にサーバごとの名前空間を入れ、同名 worktree を持つ 2 台目のサーバでも自分のセッションを起動できるようにする (#2866): サーバは初回起動時に 16 進 8 桁の名前空間を作って
app_settings(tmux_session_namespace)に保存し(形式違反の保存値は作り直して warn)、以後に作るセッション名はmcbd-{ns}-{cli}-{worktreeId}[-{suffix}]になる(初期化に失敗したとき・CLI・テストでは従来のmcbd-{cli}-{worktreeId}[-{suffix}]のまま)。更新前から動いている旧形式のセッションは rename せず、起動時に#{session_path}が worktree と一致して新形式名が空いているものを採用してそのまま表示・送信・停止でき(旧版に戻したときや旧版のサーバが並行して動くときにも失われない)、停止されるか消えたのを検知した後の起動から新形式名で作る。起動時の worktree ID 追従は旧形式と自サーバの名前空間の名前だけを対象にし、別の名前空間の名前には触れない。env-cleanゲートはどの名前空間の名前も worktree に帰属させる。commandmateCLI(attachなど)・スキル・スクリプトの新形式への追従は B2 で行う。
Fixed
-
fix(skills): orchestrate-monitor に
--no-auto-approveと promptData 判定の PROMPT 承認/保留分岐を移植し、sync-map の cmate-verify fixture 誤検知を直す (#2904):skills-sync-map.mjs check --counterpartが commandmate-skills 側の構造差(skills/cmate-verifyの fixture・run-tests.shを package 外のtests/fixtures/cmate-verify/に置く配置)を byte-identical 違反として 48 件出し続けていたため、file エントリに任意のcounterpartPathを持たせられるようにして.claude/skills/sync-map.jsonの該当 48 件に設定し(run-tests.shは前提パスが異なるためport-requiredに変更)、違反を 0 件にした。あわせて.claude/skills/orchestrate-monitor/scripts/monitor.sh・monitor-lib.shに commandmate-skills PR #77(skills #59)の--no-auto-approveフラグと、promptDataから二択かつ default が肯定のときだけ Enter を送り、それ以外はprompt_hold_reasonで理由付きに保留する判定(held/heldsig状態、ml_autoyes_suppression_reason・ml_prompt_default_label等)を移植し、両リポジトリのコメントを除いたコード差分を 0 行にした(monitor-lib.shのML_CLI_TOOL_IDS末尾のcommand-codeは skills に無い CLI ツール対応のための既存差分で対象外)。 -
fix(worktree-cleanup):
/worktree-cleanupPhase 3 のtmuxセッション終了パターンを現行の命名規則に合わせる (#2903): 旧パターン(feature-${ISSUE_NO}-worktree$)は現行のセッション名(mcbd-<cli>-commandmate-issue-<N>[-<suffix>]、#2866以降はmcbd-<ns>-<cli>-commandmate-issue-<N>[-<suffix>])に一致せず、git worktree remove後もワーカーのtmuxセッションが残っていた。CommandMate経由(roster全インスタンスのkill)を第一手段にし、取りこぼしはtmuxを完全一致(kill-session -t "=name")で回収する2段構成にした。 -
test(api): api-respond-cli-tool テストの terminal-broadcast モックに欠落していた export を追加 (#2902): broadcastTerminalSnapshot および broadcastSessionStatus をモック定義に追加し、モジュール export との不一致による将来の unhandled rejection を防止
-
fix(cmate-verify): standalone 検証ランナー(verify-run.sh)の awk パーサに options.envCleanIgnoreHomeEntries を追加する (#2901): #2890 で本体(TS ローダ)が受理するようになった
$HOME直下の env-clean 除外リストを、awk パーサの許可キー集合(baseRef・skipInPrimaryCheckout・maxLogTailBytes・requireCommit・requireEnvClean の 5 つ)は知らず未知キーとして exit 2 で拒否していたため、このリポジトリの verify.yaml に足せなかった。ブロック形式・フロー形式(空の[]も可)の両方を受理し、要素は本体と同じ規則(非空・/を含まない・./..でない・33 件以上はエラー)で検証する — standalone ランナーは env-clean を判定しないため受理した値は内部 OPT レコードとして流すだけで判定には使わない。.agents/skills/cmate-verify/scripts/verify-run.shにも同じ変更をミラーした。.commandmate/verify.yamlへのキー追加は含まない(稼働中のサーバが #2890 以前のコードで読むため、マージ・再ビルド後にオーケストレーターが行う)。commandmate-skills 側の同名ファイル(Kewton/commandmate-skills#270)と advisor の port は別 Issue のスコープで、その移植と.claude/skills/sync-map.jsonの pin 更新まではsync-map.test.tsが想定どおり赤。 -
fix(codex): 既に動いている codex の共有デーモンにつながって hook のインスタンスが取り違えられる問題を直す (#2891): #2874 の
-c features.daemon_auto_start=falseはデーモンを「新しく起動しない」だけで、既に動いているデーモン(最初に起動したインスタンスの環境変数を持つ)にはつながるため、Codex_Sub(codex-3)の通知と返答が主インスタンスcodexのものとして届いていた(codex-cli 0.157.1・2026-09-27 実測)。hook を注入する codex の起動行に--no-daemon(CODEX_NO_DAEMON_FLAG)を追加し、既存デーモンにもつながらず hook が各セッション自身の環境で動くようにした。未対応の codex に渡すと起動に失敗しうるため、codex --helpの出力に--no-daemonがあるときだけ付ける(codexSupportsNoDaemon。版番号は比べず、実行ファイルごとに 1 回だけ判定してキャッシュし、失敗・タイムアウトは付けない側)。-c features.daemon_auto_start=falseは CommandMate の環境を持つデーモンを新しく作らないため残す。注入しない経路は従来どおり。修正・再ビルド後に codex のインスタンスを再起動すること(稼働中の codex は起動行が古いまま)。 -
fix(orchestrate): unit-related 判定で changelog.d/ を除外し不要な全テスト実行を防ぐ (#2889): classifyChanges の除外条件に changelog.d/ を追加し、ワーカーがコミットする CHANGELOG 断片によって unit-related ゲートが毎回 mode=full に切り替わる問題を解消
-
fix(worktree): スマホで
?view=chatの URL から「直接入力に切り替える」を押すとチャット表示に戻り直接入力キーボードが開かない (#2888):handleStuckSwitchToDirectInputが localStorage にterminalを書いて再マウントしても、再マウントしたMobileTerminalTabのresolveSurfaceModeは URL の?view=chatを localStorage より優先して読み直すため chat に戻っていた。新規clearSurfaceModeParamFromLocation(src/config/surface-mode-config.ts)をwriteSurfaceModeの前に呼び、window.locationのviewパラメータだけを他の query と hash を保ったままhistory.replaceStateで取り除くようにした。 -
fix(realtime): terminal_snapshot の push に promptAnswerable を乗せ、Send 無効化案内の遅延を無くす (#2887): #2870 で
/current-outputに足したpromptAnswerableを WebSocket の push(emitTerminalSnapshot・snapshotFingerprint、src/lib/realtime/terminal-broadcast.ts)が運んでいなかったため、push が健在な間は値が変わっても再送されず、フォールバックポーリング(最大 15 秒)まで届かなかった。push のペイロードとTerminalSnapshotEvent(src/lib/realtime/types.ts)にpromptAnswerable?: booleanを足し(undefinedのときは項目を出さない)、useTerminalPanePollingは push が同キーを持つときだけ即座にその値を採用し、持たないとき(旧サーバ)は従来どおり同一ウインドウなら前の値を保持する。 -
fix(orchestrate): orchestrate-monitor が名前空間つきセッションに介入できない問題を直す (#2886): 介入先の tmux セッション名を
capture --jsonのcliToolIdからmcbd-${cliToolId}-${worktreeId}と組み立てていたため、サーバに名前空間(#2866)があると実際の名前はmcbd-{ns}-{cli}-{id}になり、権限プロンプトの自動承認や Enter の確定が存在しないペインへ送られ届かなくなっていた(NOT delivered)。buildCurrentOutput(GET /api/worktrees/:id/current-outputとcapture --jsonの元)の応答に、実際に使われているセッション名(CLIToolManager.getTool(cliToolId).getSessionName(worktreeId, instanceId)。旧形式の採用も反映)をsessionNameとして追加し(CLI 側の型src/cli/types/api-responses.tsにも optional で追加)、monitor.shはこれを優先して介入先を決める(無ければ従来どおりcliToolIdから組み立て、--session-prefix指定時は従来どおりそちらが優先)。skills 側(Kewton/commandmate-skills)への移植は Kewton/commandmate-skills#268 で別途行われ、その移植と.claude/skills/sync-map.jsonの pin 更新まではsync-map.test.tsが想定どおり赤。 -
fix(api): respond の decisionId 経路で、別の CommandMate サーバが作った同名 tmux セッションの画面を応答ポーリングで会話履歴に取り込まない (#2885):
/api/worktrees/:id/respondのdecisionId経路(エージェントの API で承認を届ける経路)は、ルートの所有確認(#2865)より前で分岐するため確認を通らず、承認の配達後にstartPollingが同名セッションの画面を capture して自サーバの履歴に保存しうった。配達後の後始末(structured-decision.tsのsettleAnsweredDecision)でcheckWorktreeSessionOwnershipを呼び、foreignまたは worktree 行が無い(null)ときはstartPollingとターミナルスナップショットの broadcast を行わずrespond-decision-skip-polling-foreign-sessionを warn で出す。承認の配達結果(レスポンス)は変えず、ownedとabsent(ポーラー側が扱う)は従来どおりポーリングする。 -
fix(detection): codex 0.157.1 の trust ダイアログ(
Trust this folder?)を認識し、起動時の自動承認と/prompt-responseを効かせる (#2884): trust ダイアログの判定がすべてDo you trust(0.155.1 までの文言)に依存しており、文言を変えた 0.157.1 ではgetCodexActiveDialog/getCodexLifecycleDialogが'trust'を返さず、起動時の自動承認が効かずevaluateDialogPresence('codex', …).present === falseで/prompt-responseも拒否していた。3 か所(CODEX_DIALOG_PATTERN・getCodexActiveDialog・getCodexLifecycleDialog)で使う共有定数CODEX_TRUST_QUESTION_PATTERN(Do you trustとTrust this folder\?の両方を認める)をcli-patterns.tsに追加した。0.155.1 以前の判定は変わらない -
fix(codex): codex 0.157 の共有デーモン経由で hook のインスタンスが取り違えられる問題を直す (#2874): codex 0.157 から TUI は既定でマシン共通の
codex app-server --managed-daemonにつながり、hook はデーモンのプロセス(最初に起動したインスタンスの環境変数)の中で動くため、別インスタンス・別 worktree の通知(返答・停止・許可確認)がすべて最初のインスタンスとして届いていた。hook を注入する codex の起動行に-c features.daemon_auto_start=false(CODEX_EMBEDDED_MODE_ARGS)を付け、codex を埋め込みモードで動かして hook が各セッション自身の環境で動くようにした(注入しない経路は従来どおり)。あわせて/api/hooks/agent-eventは、codex のuser_prompt_submitの通知先インスタンスの tmux セッションが無いときagent-event-instance-not-runningを warn で出す(応答は待たず、振り分け直しはしない)。既にデーモンにつながっている codex は取り違えたままなので、再ビルド後に codex のインスタンスを再起動すること。 -
fix(worktree): CommandMate から答えられないプロンプトのウインドウに Send を出さない (#2870):
/prompt-responseの再確認(ツール専用の読み取り → 汎用パーサー → ダイアログの存在確認 → 拒否理由の判定)をassessPromptAnswerabilityに切り出し、/current-outputがそれと同じ判定でpromptAnswerableを返すようにした(汎用パーサーで読んだ prompt のときだけ。isPromptWaiting・promptData・sessionStatusは変えない)。promptAnswerable: falseのとき、PC のPromptPanelとスマホのMobilePromptSheetは選択肢を表示したまま Send と選択を無効にし、「この画面は CommandMate から操作できません。直接入力モードで操作してください。」と #2869 の「直接入力に切り替える」リンクを最初から出す。#2868 のように検出がずれたときに、押しても何も起きない Send が出なくなる。 -
fix(detection): codex 0.157.1 の
/model選択画面を選択リストとして判定する (#2868): 0.157.1 でフッタがenter select · esc back(2 段目の推論レベル選択はenter default · s session · esc back)に変わり、選択画面がmultiple_choiceと読まれて Send がprompt_no_longer_activeで拒否されていた。フッタ行全体に一致するCODEX_PICKER_FOOTER_PATTERN/CODEX_EFFORT_PICKER_FOOTER_PATTERNを追加し、状態判定(分岐 0.8、窓の最後の行のみ)とダイアログ判定の入口で認めるようにした。CODEX_VERIFIED_AGAINSTを 0.157.1 に更新 -
fix(tmux): 別の CommandMate サーバが作った同名 tmux セッションを、表示・送信・停止・採用の対象から外す (#2865): worktree ID はディレクトリ名から作られるため、DB もパスも別の 2 台のサーバが既定ソケット上で同じ
mcbd-<cli>-<worktreeId>を解決し、片方の画面に他方のセッションが映り、送信・キー入力・停止・Auto-Yes の応答・起動時の rename まで他方のエージェントに届いていた。セッションの#{session_path}の realpath が worktree の path の realpath と一致するものだけを自分のものとし(新規src/lib/tmux/session-ownership.ts。path が取れないセッションも安全側で他サーバ扱い)、一致しないセッションはサイドバー・状態表示では稼働扱いにせず、/api/worktrees/:id/*の送信・キャプチャ・キー入力・応答・中断・停止・タイマー登録は HTTP 409(code: 'session_owned_by_other_server'・sessionName・sessionPath)で拒否し、kill-sessionの複数対象では自分のものだけ停止してskippedForeignSessionsを返す。既存セッションの採用(全 8 ツール)はForeignSessionError、WebSocket のターミナル購読はterminal_error、Auto-Yes は応答せず warn、タイマー発火とリレー配送が通るsendUserMessageは送信も応答ポーリング開始もせずstage: 'foreign_session'を返し(worktree 行が無く所有を確認できない場合も同じ)、起動時 rename はskippedSessionsに記録、worktree 削除時の停止は kill しない。tmux list-sessionsは#{session_path}をタブ区切りの最終列で返すようになった。セッション名の変更(名前空間)は #2866 で行うため、同名が埋まっている worktree では自サーバ側のセッションを起動できない(409)。