chore(deps): 主要依存を最新メジャーへ更新#3
Conversation
… ts 6) - Root: biome 1.9→2.4, typescript 5→6, vitest 2→4, @vitest/coverage-v8 2→4, @types/node 22→24, packageManager pnpm 9→10, engines node>=22/pnpm>=10 - frontend: next 15→16, zod 3→4, dompurify 3.4.0→3.4.1, @vitejs/plugin-react 追加, @types/dompurify 削除(v3は型同梱) - ai-engine: @anthropic-ai/claude-agent-sdk 0.2.114→0.2.117, zod 3→4, tsx 4.19→4.21 - core/storage: zod 3→4 破壊的変更への対応: - zod v4: z.record 第一引数(キー型)必須化 - vitest 4: environmentMatchGlobs 廃止 → projects で jsdom/node 分離、vi.fn の arrow → function(new 対応) - biome 2: biome migrate で config 変換、import 順自動整理、useExhaustiveDependencies 強化で reload を useCallback 化 - Next 16: next-env.d.ts を build が自動更新 検証: typecheck / lint / test(447) / build 全て通過
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 28 minutes and 45 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (66)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
* feat(ai-engine): チャットにコンテキストノードを渡せるようにする issue #11 のうちサーバ側を実装する。 - ChatRunner.runUserTurn が contextNodeIds: string[] を受け取り、ProjectStore から該当 Node を引いて prompt 内の <context_nodes> ブロックに埋め込む。 - Node 型ごとの主要属性 (priority / options / filePath など) は formatNodeForContext で簡潔にテキスト化。座標など UI ノイズは省く。 - /chat WS の user_message スキーマに contextNodeIds (optional) を追加。 - システムプロンプトに「context_nodes は AI 直接編集ではなく proposal 経由で 応答する」旨を明記し、ADR-0005 (AI 提案フロー) を尊重する。 ノードの「更新・削除」は AI が直接行わず、新規 proposal を出してユーザーに 採用判断を委ねる方針 (CLAUDE.md 大原則 #3)。 * feat(frontend): Chat タブにノードを @ メンションする UI を追加 issue #11 のフロント側実装。 - ChatContextBar コンポーネントを ChatInput の上に配置し、 「+ ノードを追加」ボタンでキャンバスのノードを type 別に選択できる。 - 選択中ノード (canvas で 1 件選択中) を一発添付するショートカットも用意。 - chip の × / 「すべて解除」で個別 / 一括解除。 - Zustand に chatContextNodeIds 配列と add/remove/clear アクションを追加。 - sendChatMessage 時に store の context を WS フレームに同梱する。 削除済みノード id はクライアント側でフィルタしてから送る。 - スレッド切替・close・reset・clearBoard で context を自動クリア (永続化はしない / chats/<id>.yaml にも書かない)。 加えて React 19.2 では `React.act` が development build にしか含まれず、 testing-library が実行時に参照するため jsdom テストの NODE_ENV を development 固定にした (vitest.config.ts)。 ノードの「更新・削除」依頼は AI が直接実行せず proposal で応答する 既存のフロー (ADR-0005) に乗せる。 * test(frontend): Chat コンテキストノード操作の E2E を追加 - ChatContextBar/chip に data-testid を付与し E2E から特定可能に - 5 ケース: 初期状態、選択中ノード追加、ピッカー追加、x削除、すべて解除 - ai-engine 未起動でも UI と Zustand store の挙動だけを検証する - 送信は WS 必須なので未入力時の送信ボタン disabled で代替確認 * fix(ai-engine): context_nodes をユーザーメッセージより前に配置 runUserTurn 内で空 assistant message を append した後に buildChatPrompt を 呼んでいたため、履歴末尾が assistant となり <current_user_message> タグが 出ず、結果として <context_nodes> がユーザー入力より後ろに並ぶバグがあった。 prompt スナップショット → 空 assistant append の順に並べ替え、 履歴 → context → current の正しい順序を保証する。 回帰防止のため順序検証テストを追加: - <context_nodes> が <current_user_message> より前に位置すること - 今ターンの user 入力が <conversation_history> 内に出現しないこと 併せて proposal ノードの formatNodeForContext から sourceAgentId を除外し 「未採用の AI 提案」note を追加 (codex セカンドオピニオン Minor 採用)。 sourceAgentId は AI にとって意味の無い内部属性のため。 * feat(ai-engine): contextNodeIds の最大件数を 20 に制限 WS の user_message スキーマで contextNodeIds に .max(20) を付与する。 prompt 肥大化と悪意ある大量送信を抑止する (codex セカンドオピニオン Major-2)。 上限超過は zod エラーとして bad_request でクライアントに返る。 * chore: CodeRabbit レビューを再トリガー * chore: CodeRabbit レビュー再トリガー * chore: CodeRabbit レビューを再トリガー * chore: CodeRabbit 再トリガー (marker) * refactor(frontend): Chat コンテキスト UI のアクセシビリティとロジックを整理 - ピッカートグルボタンの aria-label を pickerOpen に応じて動的に切り替え、 SR でも開閉状態が伝わるようにする - ピッカーの空状態にも role=dialog / aria-label / 閉じるボタンを付与し、 非空状態と対称な構造にする - type 別グルーピングを Map 構築 + 再走査の 2 段から、NODE_META のキー順で Object.values(nodes).filter(...) を直接組む 1 パス構成に書き直す * test(frontend): Chat コンテキスト E2E の selector を plain string に new RegExp(TARGET_TITLE_PICKER) は escape 漏れリスクがあり Playwright の getByRole({ name }) は plain string でも部分一致するため、regex を外して 意図を明確にする。 * fix(frontend): クライアント側にも contextNodeIds 上限ガードを追加 サーバ側 (ai-engine の MAX_CHAT_CONTEXT_NODES = 20) と同じ値で クライアントの addChatContextNode でも先回りで弾く。 名前付き定数 MAX_CHAT_CONTEXT_NODES で意図を明示し、 無駄な WS フレーム送信と UI/サーバの状態乖離を防ぐ。 * test(ai-engine): contextNodeIds テスト名から「後方互換」表現を削除
CodeRabbit (PR #21) Major 4 件 + Minor 1 件への対応。 [Heavy lift / Major #4] OAuth callback を構造化 WS メッセージで送る これまで callback URL paste 後に「[OAuth callback for ${mcpServerId}] ...」 という自然文を sendChatMessage で送り、AI に mcpServerId を解釈させて mcp__<id>__complete_authentication を呼ばせていた。複数 MCP server を 同時運用する状況で AI が別 server の complete_authentication を選び、 元の auth_request カードが pending のまま残るリスクがあった。 UI から WS の構造化メッセージ {type:'oauth_callback', mcpServerId, callbackUrl} を送り、サーバ側で確定的に「指定 server の complete_authentication を呼べ」とプロンプトを生成する設計に変更。 - ai-engine/server.ts: ChatOAuthCallbackSchema 追加、handler 分岐 - ai-engine/chat-runner.ts: runOAuthCallback メソッド追加 (runUserTurn ラッパー) - frontend/lib/ws.ts: sendOAuthCallback 追加 - frontend/lib/store.ts: sendOAuthCallback action 追加 - frontend/auth-request-card.tsx: sendChatMessage → sendOAuthCallback - 関連 test 7 件を新 API に追従 [Quick win / Major #1] chat_auth_request を discriminated union 化 ai-engine/stream.ts: status='failed' のときのみ failureMessage を必須に する型分割。schema.ts (AuthRequestBlockSchema) の superRefine と型レベル で整合させ、永続化時の検証エラーを型チェックで早期検出。chat-runner の emit 側と test の type narrowing も追従。 [Quick win / Major #3] callback URL の username/password 拒否 auth-request-card.tsx: isLikelyCallbackUrl で URL 内資格情報 (user:pass@) を弾く。schema.ts (McpServerConfigSchema.url) と整合。 [Quick win / Major #5] 最新の pending auth_request を更新 frontend/lib/store.ts: chat_auth_request reducer の走査を末尾起点に変更。 再認証で複数 pending が並ぶケースで、古いカードを更新して新しいカードが pending のまま残る問題を解消。 [Quick win / Minor #2] window.open spy の cleanup を afterEach に auth-request-card.test.tsx: vi.restoreAllMocks() を afterEach に集約し、 テスト失敗時に spy が漏れて後続に影響する経路を塞ぐ。 [見送り / Minor (codex)] auth-detector.ts の id 命名規則 ADR codex セカンドオピニオン Minor m2 (regex / schema 二重メンテの ADR メモ) は 本 PR スコープ外として見送り。
) * refactor: MCP 認証を OAuth 2.1 / SDK 任せに切替、Tally から PAT/API key を完全排除 Premise 9 撤回: PAT only → MCP プロトコルの OAuth 2.1 採用 (user sovereignty)。 理由: - Atlassian 公式 Rovo MCP は OAuth 2.1 ネイティブ、Claude Agent SDK は MCP の 'needs-auth' status を扱う built-in support を持つ → SDK 任せで auto auth flow が回る - sooperset/mcp-atlassian を使う場合も PAT は MCP server 自身の env で持つ前提に - Tally プロセスから PAT/API key の概念が完全消滅 (project.yaml / メモリ / ログのいずれにも残らない) 実装変更: - core: McpServerConfigSchema から auth フィールド削除、関連 hardening (envVar regex / Basic+Bearer 分岐) も削除。schema test を auth-less に整理 - ai-engine: buildMcpServers の auth header 構築ロジック削除、url のみで HTTP config (req は SDK が必要に応じて WWW-Authenticate を解釈)。chat-runner / agent-runner も auth に依存しない。env 未設定 throw test は仕様変更のため削除 - frontend: 設定 dialog から auth scheme dropdown / envVar 入力欄を削除、URL のみのシンプル UI、caption も「OAuth 2.1 / API token は MCP 任せ」に書き換え * feat(chat): OAuth 2.1 認証フローを auth_request ブロックで 1 等地化 外部 MCP の OAuth 認証フロー (authenticate / complete_authentication tool_use) を assistant 文中の生 tool_use ではなく、専用カードで扱う。「Atlassian で認証」 ボタン (新規タブ) と callback URL paste 入力欄を統合。 - packages/core/src/schema.ts: ChatBlock に \`auth_request\` variant を追加 (mcpServerId / mcpServerLabel / authUrl / status / failureMessage)。 status と failureMessage の整合を superRefine で固定: failed なのに message 無し / pending・completed に message が付いているケースを reject。 - packages/ai-engine/src/auth-detector.ts (新規): \`mcp__<id>__authenticate\` / \`complete_authentication\` パターンの分解、tool_result.output からの auth URL 抽出。文末の句読点・括弧を URL に含めない処理 (`...state=xyz.` のような 自然文出力で認可リンクが壊れるのを防ぐ)。 - packages/ai-engine/src/stream.ts: \`chat_auth_request\` event を ChatEvent に追加。 - packages/ai-engine/src/chat-runner.ts: tool_use を stash → tool_result とペアに なった瞬間に auth_request ブロックへ変換 (raw な tool_use/tool_result は出さない)。 \`handleAuthToolResult\` / \`findLatestPendingAuthRequest\` を新設。 complete_authentication 受領時は同 mcpServerId の最新 pending を更新。 - packages/frontend/src/components/chat/auth-request-card.tsx (新規): 認証ボタン (新規タブ) と callback URL paste 入力欄を 1 カードに集約。 callback URL は localhost / 127.0.0.1 のみ受け付ける軽い形式チェック付き。 - packages/frontend/src/components/chat/chat-message.tsx: auth_request → AuthRequestCard dispatch。 - packages/frontend/src/lib/store.ts: \`chat_auth_request\` event 反映 (pending append / completed・failed in-place 更新)。 - core/schema.test.ts: auth_request バリエーション 6 件 (基本 + 状態遷移 reject) - ai-engine/auth-detector.test.ts: parseAuthToolName / extractAuthUrl 計 13 件 (末尾句読点 strip 含む) - ai-engine/chat-runner.test.ts: auth_request 変換フロー 3 件 (authenticate / complete success / complete failure) - frontend/auth-request-card.test.tsx: pending/completed/failed 描画と paste 検証 7 件 注: ChatRunner は per-turn sdk.query() のままで、long-lived Query 化は別 PR (PR-C) で扱う。本 PR の auth_request UX 単体では「auth 1 回 → 同 thread の 次 turn で再 auth が必要」(SDK subprocess が turn ごとに再生成されるため OAuth state が引き継がれない) という挙動を残す。 * test(frontend): mcpServers PATCH の事前登録を assert 「mcpServers を空配列で全消去できる」テストの事前 PATCH の status を assert していなかった。失敗していると後続の「空配列で削除」ケース が偽の成功 (空 → 空) で通る恐れがあったため修正。 * fix(frontend): chat_auth_request reducer に pending 不在 failed の append 分岐 これまで failed/completed イベントは「同 mcpServerId の pending ブロック を更新」する経路しか持たず、URL 抽出失敗等で pending を append する 前に failed が確定するケースで in-memory state に反映されなかった (YAML には書かれるがリロードまで UI に出ない → 「何も起きない」と見える)。 evt.status==='failed' で同 mcpServerId の pending が見つからない場合、 pending と同様に該当 messageId に新規 append する。completed で pending 不在は異常系のため無視 (現状維持)。 codex セカンドオピニオン (PR #21) Major M1 指摘対応。 * fix(ai-engine): complete_authentication 専用 turn の空 assistant をプレースホルダで埋める callback URL paste 後の turn では SDK 応答が complete_authentication tool_use/tool_result のみで、handleAuthToolResult は過去 message の pending ブロックを更新するだけ。結果として現 turn の assistantMsgId には何も append されず、空のアシスタント bubble が thread に蓄積し、 再生時の prompt にもノイズとして入り込む問題があった。 textBuffer が空 + 対象 message のブロックが 0 件なら、プレースホルダ 「(認証処理を完了しました)」テキストを replaceMessageBlocks で書く。 codex セカンドオピニオン (PR #21) Major M2 指摘対応。 * fix(frontend): auth-request-card で IPv6 loopback (::1) も callback URL として受付 McpServerConfigSchema.url は loopback として localhost / 127.0.0.1 / ::1 / [::1] を許容しているが、AuthRequestCard 側の isLikelyCallbackUrl は localhost / 127.0.0.1 のみ。IPv6 優先環境で SDK がリダイレクト先を http://[::1]:XXXXX/callback?... で通知すると「認証完了」ボタンが永久 に無効になる可能性があった。schema.ts と loopback 判定を揃える。 codex セカンドオピニオン (PR #21) Minor m1 指摘対応。 * fix(chat): PR-B CR 3 周目 5 件対応 (Quick win 4 + Heavy lift 1) CodeRabbit (PR #21) Major 4 件 + Minor 1 件への対応。 [Heavy lift / Major #4] OAuth callback を構造化 WS メッセージで送る これまで callback URL paste 後に「[OAuth callback for ${mcpServerId}] ...」 という自然文を sendChatMessage で送り、AI に mcpServerId を解釈させて mcp__<id>__complete_authentication を呼ばせていた。複数 MCP server を 同時運用する状況で AI が別 server の complete_authentication を選び、 元の auth_request カードが pending のまま残るリスクがあった。 UI から WS の構造化メッセージ {type:'oauth_callback', mcpServerId, callbackUrl} を送り、サーバ側で確定的に「指定 server の complete_authentication を呼べ」とプロンプトを生成する設計に変更。 - ai-engine/server.ts: ChatOAuthCallbackSchema 追加、handler 分岐 - ai-engine/chat-runner.ts: runOAuthCallback メソッド追加 (runUserTurn ラッパー) - frontend/lib/ws.ts: sendOAuthCallback 追加 - frontend/lib/store.ts: sendOAuthCallback action 追加 - frontend/auth-request-card.tsx: sendChatMessage → sendOAuthCallback - 関連 test 7 件を新 API に追従 [Quick win / Major #1] chat_auth_request を discriminated union 化 ai-engine/stream.ts: status='failed' のときのみ failureMessage を必須に する型分割。schema.ts (AuthRequestBlockSchema) の superRefine と型レベル で整合させ、永続化時の検証エラーを型チェックで早期検出。chat-runner の emit 側と test の type narrowing も追従。 [Quick win / Major #3] callback URL の username/password 拒否 auth-request-card.tsx: isLikelyCallbackUrl で URL 内資格情報 (user:pass@) を弾く。schema.ts (McpServerConfigSchema.url) と整合。 [Quick win / Major #5] 最新の pending auth_request を更新 frontend/lib/store.ts: chat_auth_request reducer の走査を末尾起点に変更。 再認証で複数 pending が並ぶケースで、古いカードを更新して新しいカードが pending のまま残る問題を解消。 [Quick win / Minor #2] window.open spy の cleanup を afterEach に auth-request-card.test.tsx: vi.restoreAllMocks() を afterEach に集約し、 テスト失敗時に spy が漏れて後続に影響する経路を塞ぐ。 [見送り / Minor (codex)] auth-detector.ts の id 命名規則 ADR codex セカンドオピニオン Minor m2 (regex / schema 二重メンテの ADR メモ) は 本 PR スコープ外として見送り。 * fix(chat): runOAuthCallback を ephemeral 実装 + サーバ側 callback URL 検証強化 CodeRabbit (PR #21) 4 周目 Major 2 件対応。 [Major #1 / Heavy lift] runOAuthCallback の ephemeral 化 これまで runOAuthCallback は \`yield* this.runUserTurn(text, [])\` を 呼んでおり、callback URL (code/state 含む) が user message として永続化 され、後続の prompt 再構築でも再送される問題があった。さらに通常 turn の ままなので、AI が他のツール (create_node 等) を実行する余地が残っていた。 専用の ephemeral 実行経路に切替: - chatStore.appendMessage を呼ばない (callback URL を chat 履歴に残さない) - assistantMsgId は ephemeral (handleAuthToolResult の orphan 失敗 fallback 以外では参照されない) - 対象 server の config のみ buildMcpServers に渡す - allowedTools = [\`mcp__<id>__complete_authentication\`] の 1 件のみ - tool_use は kind === 'complete_authentication' && id 一致のみ stash - text block と stash 不一致の tool_result は emit せず黙って捨てる - handleAuthToolResult が過去 message の最新 pending を更新する経路は維持 [Major #2] サーバ側 callbackUrl 検証強化 ChatOAuthCallbackSchema.callbackUrl が \`z.string().url()\` のみで、 loopback / no-credentials / code+state 必須を確認していなかった。 WS は UI 経由でない外部クライアントからも到達しうるので、isValidCallbackUrl ヘルパで isLikelyCallbackUrl (UI 側) と同じ条件をサーバ側でも検証する。
CodeRabbit (PR #22) 2 周目への対応。Heavy lift #3 (OAuth callback の ephemeral 化) は前 PR と同様 long-lived 維持で見送り、CR thread に返信。 [Major / chat-runner.ts:349] close と ensureQuery の競合 close() が startQueryInternal の途中 (await projectStore.getProjectMeta() 等) で走ると、shutdown 後に sdk.query() が作られて subprocess が孤立する race があった。close() で ensureQueryInflight を join してから tearDown する。 [Major / chat-runner.ts:358] SDK メッセージの常時ログによる機密漏洩 runOutputLoop の console.log が全 SDK メッセージを redactMcpSecrets でラップ してログしていたが、message.content や result の OAuth callback URL の code/state がサーバーログに残るリスクがあった。type だけ出す形に絞り、 本文は出さない。redactMcpSecrets の import も削除。 [Major / chat-runner.ts:386] 異常終了後の pendingApprovals 残存 runOutputLoop の catch / EOF ブランチで queue を閉じるだけだったので、 turn 失敗後に UI から approveTool() が来ると create_node / create_edge 等 の side effect が走る恐れがあった。rejectAllPendingApprovals helper を 新設し、両ブランチで pendingApprovals を一括 reject(false) する。 [Major / chat-runner.ts:625] runOAuthCallback の currentTurn 解放保証 this.currentTurn = turnState の直後から全体を try/finally で囲み、 appendMessage 等の中間ステップで throw しても currentTurn が解放される ことを保証 (turn_in_progress で次の turn を受け付けられなくなるのを防ぐ)。 runUserTurn 側も同じパターンで全体を try/finally に統一。
…保持) (PR-C、stacked on #21) (#22) * refactor(chat): ChatRunner を long-lived Query 化 + codex 指摘 4 件対応 PR-C の主機能: 1 ChatRunner = 1 sdk.query() = 1 subprocess に固定して、 MCP HTTP transport の OAuth 状態 (PKCE / token) を turn 跨ぎで保持する。 main 最新 (PR-A の MCP 統合 + PR-B の OAuth 2.1 / auth_request UX + 私の修正多数: XML escape / externalToolUseIds / ephemeral runOAuthCallback / 空 assistant プレースホルダ等) と統合した形で再実装。 ## 新規ファイル - packages/ai-engine/src/async-input.ts: SDK の streaming input mode 用 AsyncIterable<SdkUserMessage> 実装。push 可能 + close 即時 teardown (CR Major 反映: close 後 next() 即 done / FIFO waiter キュー) - packages/ai-engine/src/async-input.test.ts: 8 件 (push / waiter / close / FIFO / close 後 即 done) ## agent-runner.ts - SdkLike.query を `prompt: string | AsyncIterable<SdkUserMessageLike>` に拡張 - SdkUserMessageLike + SdkQueryHandle (任意 close()) 型追加 ## chat-runner.ts 新規フィールド: - query / input / outputLoopDone / outputLoopFailed / currentTurn / cachedExternalConfigById - ensureQueryInflight (Promise キャッシュ、再入ガード) 新規 interface: - TurnState: 1 user turn の間だけ生きる mutable state (assistantMsgId / queue / textBuffer / stashedAuthUses / externalConfigById / externalToolUseIds) 新規メソッド: - ensureQuery: 1 度だけ query 立ち上げ + 出力ループ起動。inflight Promise キャッシュで再入ガード (codex Major 3) - startQueryInternal: 内部の起動本体 - runOutputLoop: SDK message を currentTurn の queue に振り分け - dispatchSdkMessage: 1 メッセージ処理 (text/tool_use/tool_result/result) - tearDownQuery / close: リソース cleanup。close は outputLoopDone を退避 してから tearDownQuery を呼ぶ (codex 致命 Minor: close 順序) 挙動変更: - runUserTurn: 入口で currentTurn ガード (codex Major 1: turn 並走防止) - input null チェックを invariant assertion で表明 (codex Major 2) - 空 assistant message 永続化を ensureQuery 前に移動 (long-lived では bg loop が即起動するため、後置きだと race で空 message 残る) - ephemeral runOAuthCallback も long-lived query 経由に統合 (callback URL は input.push 経由で SDK に渡るが chatStore には 永続化されない) main 最新の機能を保持: - auth-detector / handleAuthToolResult / stashedAuthUses - externalToolUseIds (TurnState に統合) - escapeXmlText / escapeXmlAttr (buildChatPrompt) - 空 assistant プレースホルダ「(認証処理を完了しました)」 (dispatchSdkMessage の result 処理に統合) buildMcpServer は 1 引数化、handler 内で this.currentTurn から assistantMsgId / emit を動的解決 (1 度作った MCP サーバを turn 跨ぎ で使い回せる、turn 中は不変)。 ## server.ts - WS close 時に runner.close() を呼ぶ。close 内部の reject は .catch で warn (codex Major: void close() で unhandled rejection 化を回避) ## chat-runner.test.ts - startCapturePromptText helper を追加: prompt が AsyncIterable<...> に変わったので、最初に push された user message の content を 奪取する (string にも互換) - 既存 test の prompt キャプチャ 4 箇所を helper 経由に書き換え ## テスト pnpm typecheck: 4/4 PASS pnpm test: core 93 + storage 88 + ai-engine 213 + frontend 273 = 667 PASS pnpm lint: exit 0 * fix(chat): CR 5 周目 Major 1 + Minor 1 対応 (Major 1 は見送り) CodeRabbit (PR #22) 1 周目への対応。 [Minor / chat-runner.ts:246] ensureQuery 失敗時の空 assistant ロールバック long-lived Query 化に伴い空 assistant message を ensureQuery 前に append するよう変更したが、ensureQuery 失敗時に空バブルが履歴に残る問題があった。 chatStore に message 単位の delete API が無いため、catch 内で replaceMessageBlocks でエラー内容を blocks に書き込む形でロールバックする。 [Major / chat-runner.ts:361] 正常 EOF と subprocess 終了の区別 runOutputLoop の正常 EOF 経路では従来 chat_turn_ended のみを emit していたが、 これだと「予期しない subprocess 終了」も「明示 shutdown」も区別できず、 途中で切れた turn が正常完了に見えてしまう問題があった。 ChatRunner.isClosing フラグを追加し、close() 内で立てる。runOutputLoop の EOF ブランチで isClosing が false なら agent_failed event を先に emit してから chat_turn_ended で turn を閉じる。 [Major / chat-runner.ts:599] OAuth callback の long-lived 経路統合 (見送り) callback URL を this.input に push することで会話 context に turn 跨ぎで残る、allowedTools 単一制約が prompt 依存になる、という懸念。 PR-C の主目的 (OAuth state を turn 跨ぎで保持) と SDK 制約のトレードオフ。 ephemeral 化すると subprocess 分離で state が引き継がれず再認証が必要になる。 本 PR では long-lived 維持を選択し、prompt 内で「他 tool は呼ぶな」と明示する 形でモデル依存の防御を入れる。SDK の MCP transport が状態共有 API を提供する までは追加対応見送り。CR thread に詳細を返信。 * fix(chat): CR 2 周目 Major 4 件対応 (Quick win) CodeRabbit (PR #22) 2 周目への対応。Heavy lift #3 (OAuth callback の ephemeral 化) は前 PR と同様 long-lived 維持で見送り、CR thread に返信。 [Major / chat-runner.ts:349] close と ensureQuery の競合 close() が startQueryInternal の途中 (await projectStore.getProjectMeta() 等) で走ると、shutdown 後に sdk.query() が作られて subprocess が孤立する race があった。close() で ensureQueryInflight を join してから tearDown する。 [Major / chat-runner.ts:358] SDK メッセージの常時ログによる機密漏洩 runOutputLoop の console.log が全 SDK メッセージを redactMcpSecrets でラップ してログしていたが、message.content や result の OAuth callback URL の code/state がサーバーログに残るリスクがあった。type だけ出す形に絞り、 本文は出さない。redactMcpSecrets の import も削除。 [Major / chat-runner.ts:386] 異常終了後の pendingApprovals 残存 runOutputLoop の catch / EOF ブランチで queue を閉じるだけだったので、 turn 失敗後に UI から approveTool() が来ると create_node / create_edge 等 の side effect が走る恐れがあった。rejectAllPendingApprovals helper を 新設し、両ブランチで pendingApprovals を一括 reject(false) する。 [Major / chat-runner.ts:625] runOAuthCallback の currentTurn 解放保証 this.currentTurn = turnState の直後から全体を try/finally で囲み、 appendMessage 等の中間ステップで throw しても currentTurn が解放される ことを保証 (turn_in_progress で次の turn を受け付けられなくなるのを防ぐ)。 runUserTurn 側も同じパターンで全体を try/finally に統一。
ADR-0011 PR-E5 (最終): buildMcpServers が expiry 近接 token を transparent に refresh し、E2E テストで PR-E1〜E4 の統合シナリオを通す。docs を整備して ADR-0011 を Accepted に確定。 ai-engine: - buildMcpServers: REFRESH_BUFFER_MS (5 min) 以内に expire する token は refreshAccessToken を呼んで store に書き戻す。失敗時は expired ? null : token で fall back (refresh_token 失効 → MCP 401 → UI 再認証経路へ)。 loadUsableToken ヘルパで 1 server 分の取得を切り出し - build-mcp-servers.test.ts: refresh path 5 ケース追加 (expiry 直前 + refresh 成功 / rotate 無し refresh / refresh 4xx 失敗 → null / refreshToken 無し expired → null / 期限十分先で refresh しない) - 新規 oauth-e2e.test.ts: orchestrator → loopback callback → token store → buildMcpServers → header 注入の 1 シナリオ + 透過 refresh の 1 シナリオ docs: - README に「外部 MCP (Atlassian) の OAuth セットアップ」section 追加 - docs/adr/0011-tally-managed-oauth-flow.md: status を Accepted (PR-E5 merge をもって 確定) に、PR 分割実績テーブル更新 (E1-E2-E3a-E3b-E4-E5)、完了後の確定事項セクション追加 codex セカンドオピニオン対応: - Major #1: refresh 後の expiresAt は HTTP ラウンドトリップ後の Date.now() を起点に計算 - Major #2: refresh の write race を comment 化 (MVP 単一ユーザー前提なので last-write-wins 許容、将来 multi-tenant 化時に mutex 追加が必要) - Minor #3: README の redirect URI を「Tally 起動後に表示される実ポートを Atlassian console に登録」と明記 (port=0 静的登録は Atlassian が拒否するため) - Minor #4: ADR status を 'Accepted (PR-E5 merge をもって確定)' に hedge テスト: ai-engine 242 pass (+7 PR-E5 新規)、core 94 / storage 97 / frontend 282 すべて pass typecheck clean / biome lint clean
Summary
@types/dompurifyは DOMPurify v3 が型を同梱しているため削除、@vitejs/plugin-reactを新規追加破壊的変更への対応
z.record(z.unknown())→z.record(z.string(), z.unknown())environmentMatchGlobs廃止 →projectsで jsdom / node を分離。vi.fn(() => fake)はnewで呼べなくなったため function 形式にbiome migrateで config 自動変換。useExhaustiveDependencies強化によりapp/page.tsxのreloadをuseCallbackでラップ。import 順が多数自動整理されているnext-env.d.tsを build が自動更新(triple-slash → import)Test plan
pnpm typecheck(全4パッケージ)pnpm lint(0 errors / 3 warnings)pnpm test(65 files / 447 tests)pnpm build(Next 16 を含む)pnpm devで実アプリの挙動を動作確認(キャンバス / AI Engine WebSocket / プロジェクト作成)