upstream 取り込み PR #9: 計画後前進分 12 commits + bun.lock regen#411
Conversation
…lete prompt (superset-sh#3688) The v2 workspace delete flow showed two warnings: a generic confirm pane, then a "Uncommitted changes in worktree" pane after destroy hit a conflict. Surface the v1 yellow banner inline on the confirm pane via the existing canDelete preflight, force when warnings are shown, and silently retry on the rare race so the user only ever sees one prompt.
…uperset-sh#3679) * fix(desktop): fail closed when adopted host-service has no version The version gate only killed the host when fetchHostVersion returned a string less than MIN_HOST_SERVICE_VERSION. If the host lacked the host.info route entirely (older releases), the helper returned null and we silently adopted it, producing 404s on project.findByPath and other new routes. Flip the guard to fail-closed: any host that can't prove it meets the minimum version is killed and the manifest removed so the next spawn brings up a compatible service. * fix(desktop): compare host-service versions numerically String comparison made "0.10.0" < "0.2.0" evaluate to true, which would have killed any future double-digit minor version. Split the version on dots and compare segments as integers. Also split the adopt-killing log into two branches so the null case reads "version unknown" instead of "version unknown < 0.2.0". * refactor(desktop): use semver library for host-service version check Replace the hand-rolled split-and-compare with `semver.satisfies`, which already handles malformed input by returning false. The `semver` package is already a dependency. * refactor(desktop): inline host-service version check
…switches (superset-sh#3687) * fix(desktop): keep v2 terminals stable across workspace switches Problem: every v2-workspace switch yanked the xterm wrapper out of the DOM and re-opened a WebSocket, producing a visible "switching and reattaching" flash instead of VSCode-style hide/show. The v2-workspace layout's WorkspaceTrpcProvider has a load-bearing key that unmounts the whole subtree on every switch, so the React component for TerminalPane goes away — but the xterm instance, wrapper div, and transport in terminalRuntimeRegistry should survive, and previously the DOM node didn't. Three entangled issues were fixed together: 1. Parking container. On detach, the wrapper used to be wrapper.remove()'d, taking the rendered canvases with it. Now it's appended to a hidden body-level div (#v2-terminal-parking) so xterm stays attached to the document. Re-mount in the new workspace is a DOM move back from parking to the live container — mirrors the existing v1 persistent-webview pattern at usePersistentWebview.ts:14-27 and VSCode's TerminalInstance setVisible model. 2. DOM vs transport split. The previous single registry.attach() both mounted DOM and opened the WebSocket. That forced TerminalPane to gate attach on ensureSession, which made warm returns wait on a tRPC round-trip (visible delay) and made cold mounts race — opening a WS before the server session existed produced "Session not found. Call terminal. ensureSession first.". Split into mount() (synchronous, DOM-only, safe on every mount) and connect() (called only after ensureSession resolves). Matches VSCode's TerminalInstance.attachToElement + _createProcess and Tabby's XTermFrontend.attach + setSession. 3. Effect dep narrowing. TerminalPane's attach effect used to depend on [terminalId, websocketUrl, initialThemeType, workspaceId]; prop churn during the provider key remount (workspaceId flipping while pane data caught up) forced repeated detach/attach cycles. Narrowed to [terminalId] with the others read through refs. websocketUrl changes now go through registry.reconnect(), which is hard-gated on transport already being live so it never opens a WS before ensureSession has resolved. Log walkthrough on a warm workspace switch: pane:effect-cleanup → registry:detach → runtime:detach (wrapper parks) pane:effect-mount → registry:mount → runtime:attach wasParked:true registry:reconnect-skip same-url pane:ensureSession-ok → registry:connect → transport:connect-skip idempotent Instrumentation stays in this commit for the rollout check; a follow-up strips the termLog calls once this is confirmed in production. * chore(desktop): strip v2 terminal lifecycle instrumentation Removes the temporary termLog tracing added to diagnose the workspace-switch reattach bug. The behavior fix (parking + mount/connect split) landed in the previous commit and is confirmed stable in logs; no functional change here. * fix(desktop): keep v2 browser panes stable across workspace switches Browser webviews were being destroyed on workspace switch, discarding guest-page state (URL, scroll, history) even though the registry was designed to persist them. Root cause: usePaneRegistry wired browser destruction through the Panes library's onRemoved hook: onRemoved: (pane) => browserRuntimeRegistry.destroy(pane.id) Under ideal conditions the v2 layout's `key={`${workspace.id}:${hostUrl}`}` remounts the WorkspaceTrpcProvider subtree on every switch, so each workspace gets its own Workspace component whose previous-panes diff never observes a cross-workspace "removal". But the remount isn't always prompt — layout.tsx's useLiveQuery can return stale WS-A data for a tick while page.tsx's already flipped to WS-B. During that tick the existing WorkspaceContent stays mounted, useV2WorkspacePaneLayout calls store.replaceState(WS-B panes) on the same store instance, and the Panes diff correctly sees "WS-A's browser is gone" → fires onRemoved → destroys the webview. By the time the user returns, attach() runs the cold createEntry path and the guest page is lost. Terminals don't hit this because destruction goes through useGlobalTerminalLifecycle, a global sweep against every workspace's persisted paneLayout — cross-workspace "removal" isn't a removal from the sweep's perspective. Fix: mirror the terminal pattern exactly. - Added useGlobalBrowserLifecycle under _authenticated/components/GlobalBrowserLifecycle/, following the same shape as useGlobalTerminalLifecycle (extract pane.ids from all workspace layouts, diff against previous, 500 ms grace delay destroy to tolerate cross-workspace pane moves). - Mounted <GlobalBrowserLifecycle /> alongside <GlobalTerminalLifecycle /> in _authenticated/layout.tsx. - Removed the onRemoved wiring from usePaneRegistry.tsx — the sweep replaces it. Verified with instrumentation that a workspace switch on a live browser pane no longer reaches browserRuntimeRegistry.destroy, and that closing a browser pane still destroys after the 500 ms grace. Followup to PR superset-sh#3687 (terminal-side fix landed earlier on this branch). Plan doc at apps/desktop/plans/20260423-1226-v2-pane-persistence-across-workspace-switch.md captures the full root-cause analysis for both runtimes as a reference for future pane persistence work. Also removes a now-redundant biome-ignore comment in TerminalPane that biome flagged as having no effect after the dep narrowing in the earlier commit. * fix(desktop): mark parked terminal container inert; clarify reconnect doc Two follow-ups from PR superset-sh#3687 review: - `inert` on #v2-terminal-parking. Parked terminals' internal <textarea> still had `tabindex=0`, so a keyboard user tabbing through the app could land in an off-screen terminal and have keystrokes silently go to the wrong pane. `inert` removes the subtree from the tab order and the accessibility tree, and moves focus out automatically, which also handles the "blur before park" concern for free. aria-hidden added for belt-and-suspenders on older engines. - `reconnect` JSDoc. Clarify that the guard only skips `"disconnected"` (never-opened transport, caller should use ensureSession + connect path). `"connecting"`, `"open"`, `"closed"` are all intentionally allowed through — `connect()` aborts any in-flight or stale socket before opening the new one. * chore(desktop): deslop v2 terminal pane effect and detach comment - Collapse TerminalPane's .then()/.catch() into .catch()/.finally(): one connect() call site instead of two identical guarded calls. Same semantics (connect after ensureSession settles, even on rejection), cancellation check consolidated. - Merge the three connect-related comments into one block explaining "connect regardless of outcome" + idempotency. Drop the redundant "// DOM first" inline that the block comment above already covered. - Trim detachFromContainer's 5-line comment down to 2 lines pointing at getParkingContainer — the helper's docstring already explains the parking rationale in full. * perf(desktop): memoize v2 TerminalPane useSyncExternalStore args The module-level `subscribeToState(terminalId)` built a fresh closure on every render, so `useSyncExternalStore` saw a new subscribe function each time and re-subscribed to `terminalRuntimeRegistry.onStateChange` on every TerminalPane render — this is the anti-pattern React's useSyncExternalStore docs explicitly warn about ("If you don't memoize the subscribe function, React will resubscribe to your store every time your component re-renders"). Inline the helpers and wrap both subscribe + getSnapshot in useCallback keyed on [terminalId]. Re-subscribe now only fires when the pane's terminalId actually changes (cold create / destroy), not on every keystroke-triggered re-render.
…ojectId> (superset-sh#3669) * fix(host-service): place worktrees under ~/.superset/worktrees/<projectId> Mirror v1 desktop's convention of keeping worktrees outside the primary checkout tree, and match v2's existing ~/.superset/repos/<projectId> layout for symmetry. Detection switches to the local `workspaces` table (plus the new root for orphan adoption), so legacy worktrees at <repo>/.worktrees/ keep working without a migration. * chore(host-service): trim worktree-path comments * lint
…rset-sh#3691) Remove the feature card from the Product nav dropdown; show $20 struck through next to $15 on the Pro pricing card when Yearly is selected to make the discount explicit. Pin the price row height so toggling between Monthly and Yearly doesn't cause a vertical shift.
…er (superset-sh#3692) * feat(desktop): v2 AI workspace rename generates title + branch together Post-create rename now makes a single structured-output call returning { title, branchName }, applies the title to v2_workspaces.name, and also renames the git branch (git branch -m in the worktree + updates the host-local workspaces.branch + cloud v2_workspaces.branch). Replaces the naive 20-char slice that was producing mid-word truncations like "New v2 workspaces na". Branch-prefix support for v2 tracked in SUPER-478. * fix(desktop): roll back git rename if cloud write fails; coerce oversized AI names Addresses two review findings on v2 AI rename: - Partial-commit: previously `git branch -m` + host-local `workspaces.branch` were written before the cloud mutate, so a cloud failure left git + local in the new state and the cloud (and the web app) stuck on the old branch. Now we git-rename first, push name+branch to cloud, and only update host-local on cloud success; on cloud throw we git-rename back to the old name. - `.max()` on the zod schema rejected any overlong model output, silently no-op'ing the whole rename. Replaced with `.transform()` pipes that trim the title and sanitize the branch so overshoots are coerced instead of dropped. * fix(desktop): loosen AI workspace name bounds, fall back branch to title slug - Title cap bumped 40 → 150 and drops `.min(1)` — basically trust the small model with `.describe()` guidance. - Branch falls back to a slug of the title when the model's branchName sanitizes to nothing (e.g. all emoji), so branch rename stops being a failure point. * chore(desktop): drop title-slug branch fallback in AI workspace names Per-field gating in the caller already skips the branch rename when branchName is empty; no need to salvage. If the model's output is bad, skip the rename. * refactor(desktop): drop structured-generation wrapper, call Agent.generate directly The wrapper in packages/chat was pure indirection — chat already has @mastra/core as a direct dep. Removed it, added @mastra/core to host-service, and call agent.generate({ structuredOutput }) directly from ai-workspace-names. Also tightened getSmallModel's return type to MastraModelConfig | null so callers don't need casts. * refactor(desktop): hoist AI rename orchestration out of create handler Pulls the ~80-line post-create block out of workspaceCreation.create into applyAiWorkspaceRename in ai-workspace-names.ts — same file as the generator so naming + applying live together. listBranchNames moves to its own util so both callers can share it. Call site in the create handler is now a three-line fire-and-forget.
) * feat(desktop): show PR state as sidebar workspace icon Replace the host-type icon in the v2 dashboard sidebar with a PR state icon (open/merged/closed/draft), colored by state, when a workspace has an associated pull request. Clicking the icon opens the PR on GitHub. Drop the now-redundant bottom-right PR badge, and make the diff stats only colorize when the workspace is active. * fix(desktop): address PR review comments on sidebar PR icon - Stop keydown propagation on the PR icon button so keyboard activation (Enter/Space) does not also trigger the parent row's workspace click. - Use hover:bg-foreground/10 on the PR icon button so hover is visible even when the row is active (which already has bg-muted). - Humanize PR state in the icon tooltip (Open/Merged/Closed/Draft). - Drop the stale rounded class from the diff-stats container now that its background is gone. - Use LuGitPullRequestClosed instead of LuCircleDot for closed PRs. * chore: lint
…set-sh#3695) * fix(desktop): use Alerter for automation detail delete confirm Closes SUPER-436. Replaces native window.confirm() on the automation detail page with the shared alert() helper so the confirm matches the rest of the app's UI. * fix(desktop): surface delete errors via toast.promise Addresses review feedback on superset-sh#3695: the Alerter would silently swallow a failed delete. Wrap mutateAsync in toast.promise so the user sees loading, success, and error states.
…sh#3699) * fix(desktop): honor agent selection in new-workspace modal The modal's agent picker was cosmetic — `selectedAgent` was persisted to localStorage for the UI but never written to the pending row, so the pending page's `buildForkAgentLaunch` always called `getFallbackAgentId` (which hard-prefers claude). Selecting codex, cursor, etc. silently launched claude instead. Thread the selected agent through the pending row into the launch build; "none" now genuinely skips the agent launch (no silent claude substitution). * test(desktop): update buildForkAgentLaunch tests for explicit agent selection Tests were written against the old fallback-to-claude behavior — they passed `agentId: null` and expected Claude to launch. Under the new contract, null is treated as "no selection → no launch". Pass explicit `agentId` in each case. Add coverage for the new null / "none" paths.
…sh#3701) * fix(host-service): count untracked file lines in getStatus (SUPER-472) Untracked files were pushed into `unstaged` with `additions: 0`, so the sidebar LOC delta missed every newly-added file in the working tree (`useDiffStats` sums against-base + staged + unstaged). * feat(host-service): detect renames/copies on untracked files Run git's real rename/copy detection over the working tree by copying .git/index to a temp file, marking untracked files intent-to-add against that copy, and running `git diff -M -C`. Real index is never mutated. Merges matched deleted+untracked pairs into single rename entries with correct additions/deletions, and renders `old → new` in the v2 sidebar file row. Catches: - mv tracked → untracked (rename) - cp tracked → untracked (copy, sourced from the tracked blob) - mv with edits (rename with non-zero stats) Falls back silently to the unmerged deleted+untracked listing on any error (missing index, copy failure, intent-to-add reject). * fix(host-service): address review on getStatus rename/LOC - Cap parallel file I/O at 64 workers in countUntrackedFileLines so workspaces with thousands of untracked files don't EMFILE - Sniff first 8KB for NUL bytes before counting lines so binary files under the 1MB cap (PNGs, lockfiles, compiled artifacts) don't get a meaningless utf-8 line count - Log temp-dir cleanup failures instead of silently swallowing - Add -M -C to staged numstat and propagate file.from as oldPath so staged renames collapse to a single 0/0 entry (matches the working- tree rename detection added in the previous commit)
…uperset-sh#3698) Dev setup writes SUPERSET_HOME_DIR=<worktree>/superset-dev-data (no leading dot), which didn't match the managed-hook regex, so stale notify.sh entries from deleted dev worktrees stuck around in ~/.codex/hooks.json. Widen the pattern to also recognize `superset-dev-data/` and add a regression test.
…h#3700) * fix(desktop): adopt Ghostty keyboard model in v2 terminal v2's terminal runtime only filtered app hotkeys. With kitty keyboard protocol enabled (needed for Shift+Enter disambiguation in claude-code, modifier reporting in neovim/helix), every Mac Cmd chord xterm saw got CSI-u encoded and leaked into TUIs as a literal char — and line-edit niceties like Cmd+Left/Right/Backspace and Option+Left/Right that v1 handles never worked at all. Mirror Ghostty's approach (src/input/key_encode.zig:534-545: "on macOS, command+keys do not encode text"): bubble every Mac Cmd chord out to the host before xterm's kitty encoder runs, then port v1's line-edit chord translators so shell navigation works the same in both renderers. Changes: - Broaden shouldBubbleClipboardShortcut's Mac branch to bubble all Cmd chords (not just Cmd+C/V with selection gating). v1 benefits too. - Port v1's line-edit translators into v2's custom key handler: Cmd+Left/Right/Backspace, Option+Left/Right, Windows Ctrl+Left/Right. Duplicates v1 for now; a follow-up can share the handler properly. - Wire shouldSelectAllShortcut into v2 so Cmd+A selects terminal buffer. - Use xterm.input(data, true) to inject translated sequences into the PTY (fires onData, forwarded by terminal-ws-transport). - Restructure tests around the new Mac rule. * fix(desktop): preventDefault on bubbled clipboard chords Matches VS Code (terminalInstance.ts:1116-1175) and Tabby (xtermFrontend.ts:199-214): call `event.preventDefault()` before returning false from the custom key handler so the browser's default action can't double-fire alongside the Electron `role: 'paste'`/`'copy'` accelerator registered in src/main/lib/menu.ts. Paste still flows — webContents.paste() dispatches the paste event on the focused textarea independently of the DOM keydown default, and xterm's paste listener picks it up. Keydown-only — preventDefault on keyup is a no-op and would suppress kitty protocol release events we still want xterm to handle. * refactor(desktop): collapse translateLineEditChord to a grouped form Seven near-identical 8-line blocks collapsed to a platform+modifier grouped structure with a tiny onlyMod helper. Same behavior, ~55 fewer lines, adding a new chord is one line instead of ten. * revert: don't preventDefault on bubbled clipboard chords Broke Cmd+C/V. On Electron macOS, the browser's keydown → paste-command pipeline is what dispatches the paste event on xterm's textarea, and preventDefault blocks that pipeline. VS Code and Tabby can preventDefault because they implement paste themselves (command system / ClipboardAddon); we rely on xterm's built-in paste listener, so the default must run. Revert of 36cf276.
… resolution upstream superset-sh#3692 (aceb0fd v2 AI workspace rename) が packages/host-service の @mastra/core を 1.26.0-alpha.3 に更新したが、 cherry-pick 時の lockfile conflict resolution で該当 resolution entry が 欠落し、electron-builder の production dependency traversal が "Production dependency @mastra/core not found for package @superset/host-service" で fail していた。 `rm bun.lock && bun install` で完全再生成して整合性担保。 fork 固有依存 (ansi_up, @vscode/ripgrep, @xyflow/react) は全て保持。
📝 WalkthroughWalkthroughターミナルペインのマウント/接続ロジックを分離し、ブラウザペインのライフサイクルを全体的に管理するシステムへ移行。ワークスペース作成時のAI駆動の命名機能を追加。Gitの差分検出ロジックをリネーム/コピー検出に対応。複数の依存関係を更新し、ワークスペース削除フローとUI要素を改善。 Changes
Sequence Diagram(s)sequenceDiagram
participant React
participant TerminalPane
participant TerminalRegistry
participant Terminal
participant WebSocket
React->>TerminalPane: render (mount phase)
TerminalPane->>TerminalRegistry: mount(terminalId, container, appearance)
TerminalRegistry->>Terminal: create & attach to DOM
TerminalRegistry-->>TerminalPane: mounted
TerminalPane->>TerminalPane: ensure server session
TerminalPane->>TerminalRegistry: connect(terminalId, wsUrl)
TerminalRegistry->>WebSocket: open/reuse connection
TerminalRegistry-->>TerminalPane: connected
Note over TerminalPane,WebSocket: WebSocket URL changes
TerminalPane->>TerminalRegistry: reconnect(terminalId, newWsUrl)
TerminalRegistry->>WebSocket: swap endpoint
TerminalRegistry-->>TerminalPane: reconnected
React->>React: unmount workspace
Terminal-->>Terminal: stay in parking container
sequenceDiagram
participant CollectionsProvider
participant GlobalBrowserLifecycle
participant BrowserRegistry
CollectionsProvider->>GlobalBrowserLifecycle: useLiveQuery all v2WorkspaceLocalState
GlobalBrowserLifecycle->>GlobalBrowserLifecycle: extract browser pane IDs from paneLayout
GlobalBrowserLifecycle->>GlobalBrowserLifecycle: diff against previous set
alt Pane ID removed
GlobalBrowserLifecycle->>GlobalBrowserLifecycle: schedule destroy(paneId) after 500ms
else Pane ID reappears
GlobalBrowserLifecycle->>GlobalBrowserLifecycle: clear pending timeout
end
Note over GlobalBrowserLifecycle,BrowserRegistry: Prevents premature destruction during workspace switch
GlobalBrowserLifecycle->>BrowserRegistry: destroy(paneId) after grace window
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
Sherif の multiple-dependency-versions エラーを解消。upstream superset-sh#3692 (aceb0fd) が host-service の @mastra/core を 1.26.0-alpha.3 に アップデートしたが、apps/desktop と packages/chat は 1.25.0 のまま だった。upstream/main では 3 箇所全て 1.26.0-alpha.3 で統一されているため、 fork でも揃える。
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b91b1c4188
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex P1 指摘対応。updateNameFromHost mutation は expectedCurrentName mismatch (ユーザーの並行 rename) 時に既存 row を返す no-op 動作だが、 fork 側の applyAiWorkspaceRename は戻り値を見ずに local DB / git branch を無条件で新 name に書き換えていた。結果: cloud 側は old branch のまま、 local は new branch になり分岐が食い違う。 mutation 結果の branch フィールドを比較し、cloud が accept しなかった 場合は git branch rename を rollback + host-sqlite 更新を skip。
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/$pendingId/buildForkAgentLaunch.test.ts (1)
116-123:⚠️ Potential issue | 🟡 Minorテストのアサーションが
agentId: nullデフォルトによって無意味になっています。
pendingBase()のデフォルトがagentId: nullになったため、このテストは「有効なエージェントが存在しない」ことではなく「agentIdがnull」であることを理由にnullを返しています (resolveAgentIdが最初にnullを返す)。agentConfigs: []のパスを本来意図したとおりに検証するには、明示的にagentIdを指定してください。🧪 提案: 意図したパスを通す
test("returns null when there are no enabled agents", async () => { const build = await buildForkAgentLaunch({ - pending: pendingBase({ prompt: "hi" }), + pending: pendingBase({ prompt: "hi", agentId: "claude" }), attachments: undefined, agentConfigs: [], }); expect(build).toBeNull(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/`$pendingId/buildForkAgentLaunch.test.ts around lines 116 - 123, The test currently relies on pendingBase() defaulting agentId to null which causes resolveAgentId to return null and makes the assertion meaningless; update the test that calls buildForkAgentLaunch so pendingBase is given an explicit non-null agentId (e.g., "agent-1") to ensure the code path where agentConfigs: [] is evaluated by resolveAgentId and the function actually returns null due to no enabled agents rather than due to a null agentId; keep references to buildForkAgentLaunch, pendingBase, resolveAgentId and agentConfigs when making the change.
🧹 Nitpick comments (18)
apps/desktop/src/main/lib/agent-setup/agent-wrappers.test.ts (1)
1189-1250: 冪等性アサーションの追加を推奨同ファイルの他の stale-path 置換テスト(例: Lines 1045-1123 の "replaces stale Codex hook commands from old superset paths")では、1 回目の結果を書き戻してから 2 回目を実行し
JSON.parse(content2)).toEqual(JSON.parse(content))で冪等性を確認しています。本テストも同じgetCodexGlobalHooksJsonContent経路を通すので、回帰検知の一貫性のためにここでも 2 回目実行と冪等性チェックを入れておくと、将来 reconcile ロジックが再適用時にエントリを重複させるような退行を拾えます。♻️ 冪等性チェックの追加案
const content = getCodexGlobalHooksJsonContent(currentHookPath); expect(content).not.toBeNull(); if (content === null) throw new Error("Expected content"); + // Second run should be idempotent + writeFileSync(codexHooksPath, content); + const content2 = getCodexGlobalHooksJsonContent(currentHookPath); + expect(content2).not.toBeNull(); + const parsed = JSON.parse(content) as { hooks: Record< string, Array<{ matcher?: string; hooks: Array<{ type: string; command: string }>; }> >; }; @@ expect( hooks.some((def) => def.hooks.some((hook) => hook.command === staleHookPath), ), ).toBe(false); } + + expect(JSON.parse(content2 as string)).toEqual(JSON.parse(content)); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/lib/agent-setup/agent-wrappers.test.ts` around lines 1189 - 1250, Add an idempotency assertion by calling getCodexGlobalHooksJsonContent a second time and asserting the JSON output is unchanged: after the existing first call/parse (content → parsed), call getCodexGlobalHooksJsonContent again (content2), ensure content2 is not null, parse it and assert JSON.parse(content2) equals JSON.parse(content) (or toEqual on the parsed objects). This uses the existing getCodexGlobalHooksJsonContent helper and the parsed variable names so the test will detect regressions where reconcile logic might duplicate entries on re-application.apps/marketing/src/app/pricing/components/PricingTiers/components/PricingCard/PricingCard.tsx (1)
83-107:resolvePriceに明示的な戻り値型を付与することを推奨します。現状は戻り値型が推論に任されており、
strikethroughはstring | nullとしてユニオンで推論されますが、将来的にいずれかの分岐の戻り値が変わるとサイレントに型がぶれる可能性があります。明示的なinterfaceを定義しておくと意図が明確になり、分岐の追加・変更時のリグレッションを防げます。コーディングガイドラインでも TypeScript でのオブジェクト形状にはinterfaceを優先するよう定められています。♻️ 提案リファクタ
+interface ResolvedPrice { + display: string; + strikethrough: string | null; + note: string; + cadence: string; +} + -function resolvePrice(tier: PricingTier, isYearly: boolean) { +function resolvePrice(tier: PricingTier, isYearly: boolean): ResolvedPrice {As per coding guidelines: "Prefer
interfacefor defining object shapes in TypeScript".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/marketing/src/app/pricing/components/PricingTiers/components/PricingCard/PricingCard.tsx` around lines 83 - 107, Add an explicit return type for resolvePrice by defining a dedicated interface (e.g., ResolvedPrice) that describes the object shape (fields: display: string, strikethrough: string | null, note: string, cadence?: string | null as appropriate) and update the function signature to return that interface; ensure all branches in resolvePrice (fixed, custom, and the monthly/yearly path) return objects conforming to this interface so TypeScript will catch future regressions and keep the shape consistent.apps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/$pendingId/buildForkAgentLaunch.ts (1)
129-176:resolveAgentId呼び出し後の再解決が重複しています。
resolveAgentIdは既にindexResolvedAgentConfigs(configs).get(...)を実行しenabledをチェックした上でAgentDefinitionId | nullを返しています。にもかかわらず、呼び出し側 (Line 132-135) で再度indexResolvedAgentConfigsを実行しenabledを再チェックしており、インデックス構築が 2 回走る上にロジックも重複します。resolveAgentIdに設定オブジェクトを返させるか、ヘルパーに再整理することで冗長な再解決を削除できます。♻️ 提案: 解決結果を一度だけ返す
- const agentId = resolveAgentId(inputs.pending.agentId, inputs.agentConfigs); - if (!agentId) return null; - - const agentConfig = indexResolvedAgentConfigs(inputs.agentConfigs).get( - agentId, - ); - if (!agentConfig || !agentConfig.enabled) return null; + const agentConfig = resolveAgentConfig( + inputs.pending.agentId, + inputs.agentConfigs, + ); + if (!agentConfig) return null; + const agentId = agentConfig.id; @@ -function resolveAgentId( - selected: string | null, - configs: ResolvedAgentConfig[], -): AgentDefinitionId | null { - if (!selected || selected === "none") return null; - const match = indexResolvedAgentConfigs(configs).get( - selected as AgentDefinitionId, - ); - return match?.enabled ? match.id : null; -} +function resolveAgentConfig( + selected: string | null, + configs: ResolvedAgentConfig[], +): ResolvedAgentConfig | null { + if (!selected || selected === "none") return null; + const match = indexResolvedAgentConfigs(configs).get( + selected as AgentDefinitionId, + ); + return match?.enabled ? match : null; +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/`$pendingId/buildForkAgentLaunch.ts around lines 129 - 176, The call to indexResolvedAgentConfigs is being done twice: once inside resolveAgentId and again in the caller to re-fetch agentConfig, causing redundant indexing and checks; change resolveAgentId to return the selected ResolvedAgentConfig | null (not just AgentDefinitionId), e.g. resolveAgentConfig(selected, configs) that performs indexResolvedAgentConfigs(configs) and enabled check, then in the caller use the returned ResolvedAgentConfig directly (replace agentId + agentConfig lookup with the single returned config) so you remove the second indexResolvedAgentConfigs call and duplicate enabled logic; update references to resolveAgentId, agentConfig, and any code that expects an id to instead use the config or derive the id from it.apps/desktop/src/main/lib/host-service-coordinator.ts (1)
291-300: semver satisfies呼び出しのprererelease対応と失敗理由ログの改善を検討
semver.satisfies(version, ">=${MIN_HOST_SERVICE_VERSION}")はデフォルトでincludePrerelease: falseのため、将来 host-service が0.2.0-betaなどのプレリリース版を返した場合、実際には十分新しいバージョンでもチェックが失敗し、正常なサービスが kill されます。プロジェクトはauto-updater.tsでプレリリース版を明示的に扱っているため、ここでも統一的に対応するか、リリースポリシーとしてステーブル版のみに限定することを検討してください。あわせて、ログメッセージの
version ${version} < ${MIN_HOST_SERVICE_VERSION}は、実際には「範囲外(プレリリース、無効な形式など含む)」なので、より正確にincompatibleと表記する方が無難です。参考パッチ
- !semver.satisfies(version, `>=${MIN_HOST_SERVICE_VERSION}`) + !semver.satisfies(version, `>=${MIN_HOST_SERVICE_VERSION}`, { + includePrerelease: true, + })- const reason = version - ? `version ${version} < ${MIN_HOST_SERVICE_VERSION}` - : "version unknown"; + const reason = version + ? `version ${version} incompatible with >=${MIN_HOST_SERVICE_VERSION}` + : "version unknown";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/lib/host-service-coordinator.ts` around lines 291 - 300, The semver check in semver.satisfies(version, `>=${MIN_HOST_SERVICE_VERSION}`) can incorrectly reject prerelease versions and the log message is misleading; update the compatibility logic in the host-service check (the block using semver.satisfies, MIN_HOST_SERVICE_VERSION and organizationId) to explicitly handle prereleases (e.g., pass { includePrerelease: true } to semver.satisfies or first check semver.valid/semver.prerelease) and compute a clear reason string (e.g., "incompatible: prerelease X", "incompatible: invalid version", or "incompatible: version X < MIN_HOST_SERVICE_VERSION") then include that reason and the raw version in the console/processLogger output instead of "version X < MIN_HOST_SERVICE_VERSION" so the log accurately reflects prerelease/invalid-format failures.apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/DestroyConfirmPane/DestroyConfirmPane.tsx (1)
88-96:isCheckingStatus中の視覚的フィードバック不足(軽微な UX 改善)プリフライトチェック中は Delete ボタンが無効化されますが、理由を示す表示(スピナーや "Checking…" ラベル等)がないため、ユーザーにはボタンが不活性に見えるだけになります。チェックは通常短時間ですが、遅い環境では戸惑いの原因になり得ます。
💡 任意の UX 改善案
<Button variant="destructive" size="sm" className="h-7 px-3 text-xs" onClick={onConfirm} disabled={isCheckingStatus} > - Delete + {isCheckingStatus ? "Checking…" : "Delete"} </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/DestroyConfirmPane/DestroyConfirmPane.tsx` around lines 88 - 96, The Delete button becomes disabled during the preflight check but lacks visual feedback; update DestroyConfirmPane to show a spinner and/or change the button label when isCheckingStatus is true (e.g., replace "Delete" with "Checking…" and render a small spinner icon inside the Button while disabled) so users understand why the button is inactive; modify the Button usage in DestroyConfirmPane to conditionally render the spinner and alternate label based on the isCheckingStatus prop while keeping onConfirm and the disabled behavior intact.apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/DashboardSidebarDeleteDialog.tsx (1)
55-67:hasWarningsの重複(任意の軽微リファクタ)同じ
hasChanges || hasUnpushedCommitsが本ファイル(Line 55)とDestroyConfirmPane.tsx(Line 38)の両方で計算されています。バナー表示用とonConfirmのforce引数決定用で用途は異なるため現状でも動作上の問題はありませんが、DestroyConfirmPaneからhasWarningsを props として受け取る/あるいは親で計算して子に渡す、のいずれかに集約すると意図が 1 箇所に集中して追いやすくなります。急ぎの対応は不要です。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/DashboardSidebarDeleteDialog.tsx` around lines 55 - 67, Compute the combined warning flag once in the parent and pass it into the child instead of duplicating the logic: remove the local calculation of hasWarnings in DestroyConfirmPane and add a new boolean prop (e.g., hasWarnings) on DestroyConfirmPane; in the parent (DashboardSidebarDeleteDialog) keep the existing hasWarnings = hasChanges || hasUnpushedCommits and pass hasWarnings to DestroyConfirmPane, then update DestroyConfirmPane to use the received hasWarnings both for banner display and for calling onConfirm/force logic (ensure run(hasWarnings) in the parent or propagate the flag to where run is invoked).packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts (1)
172-208: ロールバック処理の重複をヘルパー化できます。
git branch -m deduped oldBranchName+.catchでログを出すブロックが cloud 失敗時(L175-185)と no-op 検出時(L198-206)でほぼ同一です。将来どちらかだけ修正して差分が入るリスクがあるので、内部ヘルパーに切り出す余地があります。動作に問題は無いので対応は任意です。♻️ 提案
+ const rollbackGitBranch = async (reason: string) => { + await ctx + .git(worktreePath) + .then((g) => g.raw(["branch", "-m", deduped, oldBranchName])) + .catch((rollbackErr) => { + console.warn( + `[applyAiWorkspaceRename] git branch rollback ${reason} (workspace ${workspaceId}, ${deduped} → ${oldBranchName})`, + rollbackErr, + ); + }); + }; + let cloudResult: Awaited< ReturnType<typeof ctx.api.v2Workspace.updateNameFromHost.mutate> >; try { cloudResult = await ctx.api.v2Workspace.updateNameFromHost.mutate(patch); } catch (err) { - if (gitRenamed) { - await ctx - .git(worktreePath) - .then((g) => g.raw(["branch", "-m", deduped, oldBranchName])) - .catch((rollbackErr) => { - console.warn( - `[applyAiWorkspaceRename] git branch rollback failed (workspace ${workspaceId}, ${deduped} → ${oldBranchName})`, - rollbackErr, - ); - }); - } + if (gitRenamed) await rollbackGitBranch("after cloud error"); throw err; } ... if (!cloudAcceptedBranch && gitRenamed) { - await ctx - .git(worktreePath) - .then((g) => g.raw(["branch", "-m", deduped, oldBranchName])) - .catch((rollbackErr) => { - console.warn( - `[applyAiWorkspaceRename] git branch rollback after cloud no-op failed (workspace ${workspaceId}, ${deduped} → ${oldBranchName})`, - rollbackErr, - ); - }); + await rollbackGitBranch("after cloud no-op"); return; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts` around lines 172 - 208, The two identical git rollback blocks in ai-workspace-names.ts should be extracted into a small helper to avoid duplication: create a helper (e.g., rollbackGitBranch or rollbackBranchRename) that accepts ctx, worktreePath, workspaceId, fromBranch (deduped) and toBranch (oldBranchName), calls ctx.git(worktreePath).then(g => g.raw(["branch","-m", fromBranch, toBranch])) and catches errors to console.warn using the same contextual message currently used in both places; then replace the two inline rollback blocks (the try/catch rollback after updateNameFromHost failure and the cloud no-op rollback) with calls to that helper to keep behavior identical and centralize future changes.apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.ts (2)
30-72: 関数名と実際のスコープの乖離(任意のリファクタ提案)macOS ブランチが
return event.metaKeyに広がったことで、この関数は「クリップボードショートカット」を超えた汎用的な Cmd-chord バブリング判定を担うようになりました。Ghostty 由来の設計意図は JSDoc に明記されているので現状でも把握は可能ですが、将来の読み手のために関数名をshouldBubbleTerminalChordなど実態を反映したものに変える/もしくは JSDoc の冒頭でエイリアス的に言及しておくと、呼び出し側(terminal-runtime.ts)での役割が明確になります。挙動は正しく、コメントが充実しているため急務ではありません。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.ts` around lines 30 - 72, The function shouldBubbleClipboardShortcut has drifted to a more general terminal chord-bubbling role; rename it to shouldBubbleTerminalChord (and update its JSDoc to reflect the Ghostty-derived Cmd-chord semantics) and update all call sites (e.g., the caller in terminal-runtime.ts) to use the new name so the API matches behavior and intent.
49-51: Mac キーコード処理の実行順序依存性をドキュメント化する
shouldBubbleClipboardShortcutの JSDoc に、translateLineEditChordおよびshouldSelectAllShortcutの後に呼ばれることが必須である旨をより明確に記載してください。現在の実装は正しい順序を保っていますが、将来の変更時に無言の挙動破壊を防ぐため、この前提条件をより厳密に文書化することで保守性が向上します。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.ts` around lines 49 - 51, Update the JSDoc for the function shouldBubbleClipboardShortcut to explicitly state that it must be invoked after translateLineEditChord and shouldSelectAllShortcut (i.e., it assumes those have already been applied), documenting the Mac-specific keycode handling order dependency so future changes don't break behavior; reference the function name shouldBubbleClipboardShortcut and the dependent helpers translateLineEditChord and shouldSelectAllShortcut in the comment and note the required call order on macOS.apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts (4)
4-4:browserRuntimeRegistryのモジュール配置がアーキテクチャ的に不自然この hook はアプリ全体の副作用を担うグローバルライフサイクルにも関わらず、依存する
browserRuntimeRegistryがワークスペース固有パスの深い位置(_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/BrowserPane/...)に置かれており、グローバルと特定ルートが逆方向に依存しています。対照的にterminalRuntimeRegistryはrenderer/lib/terminal/のニュートラルな位置にあります。今回の PR スコープでは差分を増やす変更になるので必須ではありませんが、将来的には
browserRuntimeRegistryをrenderer/lib/browser/などに移してBrowserPane側から参照する形に逆転させると、依存方向がレイヤに沿って綺麗になります。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts` at line 4, The import of browserRuntimeRegistry in useGlobalBrowserLifecycle comes from a workspace-specific deep path, inverting dependency direction; extract browserRuntimeRegistry into a neutral shared module (e.g., create renderer/lib/browser/browserRuntimeRegistry.ts), export the same identifier, update all callers (useGlobalBrowserLifecycle and BrowserPane and any other consumers) to import from the new shared module instead of the _dashboard/... path, and remove the original file or re-export from it to avoid duplicate definitions; this mirrors how terminalRuntimeRegistry is placed and keeps global lifecycle hooks independent of route-specific code.
94-101: cleanup effect は既存の effect と統合できる行 94-101 の cleanup 専用
useEffect(() => () => {...}, [])は、既存のuseEffect(..., [allWorkspaceRows, collections])の return 部に inline cleanup を書けば十分です(依存配列の再実行時にも毎回走るのを避けたい場合は別 effect のままで可)。ただし現状は依存配列が変わるたびに effect 本体が再実行されるので、cleanup を統合すると「diff 計算の副作用として設定したタイマーが、再実行のたびに全部クリアされる」挙動になり意味が変わります。そのため現在の分離は意図的に正しいです。補足として、その意図をコメントに一言残しておくと将来のリファクタで誤って統合されるのを防げます。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts` around lines 94 - 101, The separate cleanup useEffect that clears pendingDestruction.current timers (the useEffect with an empty dependency array that iterates pendingDestruction.current.values() and calls clearTimeout) is intentionally isolated from the other effect that depends on [allWorkspaceRows, collections] because merging them would cause timers to be cleared on every dependency change; add a short explanatory comment immediately above the empty-deps useEffect stating that it must run only on unmount to avoid clearing timers during dependency-driven re-runs and must not be merged with the useEffect handling allWorkspaceRows/collections to preserve the intended lifecycle semantics.
75-86:setTimeout内でクロージャが古いcollectionsを掴み続ける可能性500ms のタイマーコールバックは effect が走った時点の
collectionsクロージャを掴みます。collectionsの identity がその後変わった場合([allWorkspaceRows, collections]依存から次の effect が走る)、古い collection の.state.values()を参照することになります。実運用では
collectionsが動的に差し替わるケースは稀なので実害の可能性は低いですが、useCollections()が Provider を跨いで切り替わる/テストで mock 差し替えをするようなシナリオでは destroy 判定がミスる可能性があります。collectionsRef = useRef(collections)を最新に更新して callback 内で ref を参照する、あるいは collection identity 変化時に pending を全クリアするなどの対処を検討してください。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts` around lines 75 - 86, The timeout callback uses a stale closure over collections causing incorrect destroy checks; update the effect so the callback reads the latest collections via a ref (e.g., create collectionsRef = useRef(collections) and keep it synced whenever collections changes) and inside the setTimeout callback use collectionsRef.current.values() (or clear pendingDestruction.current when collections identity changes) to ensure extractBrowserPaneIds(freshRows) uses the current collection before calling browserRuntimeRegistry.destroy(browserId); keep DESTROY_DELAY_MS and pendingDestruction.current logic intact and ensure the effect dependency [allWorkspaceRows, collections] still triggers the ref update.
13-27:paneLayout: unknownのキャストを型ガードに置き換えると安全
extractBrowserPaneIdsはrow.paneLayout as WorkspaceState<unknown> | undefinedとキャストしていますが、実際の shape 検証はしていないため、スキーマが変わった時や他のrows型でも silently 通過します。useGlobalTerminalLifecycleと対称にするという設計方針は理解しましたが、少なくともlayout && typeof layout === "object" && "tabs" in layoutのような最小限の runtime ガードを入れておくと、スキーマ差分時のデバッグが楽になります。また
pane.kind === "browser"の分岐でもpane.idが string である保証はキャスト任せなので、typeof pane.id === "string"を併せて確認するとさらに堅牢です。♻️ 参考パッチ
function extractBrowserPaneIds(rows: { paneLayout: unknown }[]): Set<string> { const ids = new Set<string>(); for (const row of rows) { const layout = row.paneLayout as WorkspaceState<unknown> | undefined; if (!layout?.tabs) continue; for (const tab of layout.tabs) { for (const pane of Object.values(tab.panes)) { - if (pane.kind === "browser") { + if (pane.kind === "browser" && typeof pane.id === "string") { ids.add(pane.id); } } } } return ids; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts` around lines 13 - 27, Replace the unsafe cast in extractBrowserPaneIds with runtime type guards: check that row.paneLayout is an object and has a "tabs" property (e.g., layout && typeof layout === "object" && "tabs" in layout) before treating it as WorkspaceState, iterate tabs and panes as now, and additionally verify pane.kind === "browser" && typeof pane.id === "string" before calling ids.add(pane.id); mirror the minimal guards used in useGlobalTerminalLifecycle to keep behavior consistent.apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.test.ts (1)
119-143: Linux の Ctrl+C 挙動テストが欠けている実装の Linux ブランチ(
clipboardShortcuts.ts行 67-71)では Windows と異なりCtrl+CはhasSelectionに関わらず絶対にバブリングしない(=常に PTY に渡って SIGINT になる)仕様になっています。この差分は Windows vs Linux の差別化ポイントとしてレグレッション耐性の高いケースなので、テストに以下を追加しておくと安心です:
Linux Ctrl+C with selection stays with the PTY (SIGINT)Linux Ctrl+C without selection stays with the PTY (SIGINT)♻️ 参考パッチ
{ name: "Linux Ctrl+Insert stays with the PTY", event: makeEvent({ code: "Insert", ctrlKey: true }), options: { isMac: false, isWindows: false, hasSelection: false }, expected: false, }, + { + name: "Linux Ctrl+C with selection stays with PTY (Ctrl+Shift+C is the copy idiom)", + event: makeEvent({ code: "KeyC", ctrlKey: true }), + options: { isMac: false, isWindows: false, hasSelection: true }, + expected: false, + }, + { + name: "Linux Ctrl+C without selection stays with PTY (SIGINT)", + event: makeEvent({ code: "KeyC", ctrlKey: true }), + options: { isMac: false, isWindows: false, hasSelection: false }, + expected: false, + },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.test.ts` around lines 119 - 143, Add two Linux Ctrl+C test cases to the array in clipboardShortcuts.test.ts to cover the Linux-specific behavior in clipboardShortcuts.ts: add one named "Linux Ctrl+C with selection stays with the PTY (SIGINT)" and one "Linux Ctrl+C without selection stays with the PTY (SIGINT)"; both should use event = makeEvent({ code: "KeyC", ctrlKey: true }) with options { isMac: false, isWindows: false } and hasSelection true/false respectively, and both expected = false to assert that Ctrl+C always does not bubble to the UI but is sent to the PTY.apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts (1)
77-85: コメントとコードの齟齬(軽微)。JSDoc は「
modis the only non-shift modifier held」(=Shift の有無は不問) と読めますが、実装は!event.shiftKeyを強制しており Shift を押していると false を返します。実害はありませんが、現行ロジックに合わせてコメントを「no other modifier held (including Shift)」等に揃えるか、Shift を許容したいならコードを調整してください。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts` around lines 77 - 85, The JSDoc for function onlyMod is out of sync with its implementation: the comment says Shift is allowed but the code enforces !event.shiftKey. Either update the JSDoc to state “no other modifier held (including Shift)” to reflect the current behavior, or change the implementation in onlyMod to allow Shift (remove the !event.shiftKey check and adjust logic accordingly) depending on the intended semantics; use the onlyMod signature and usages to guide which variant to keep.apps/desktop/plans/20260423-1226-v2-pane-persistence-across-workspace-switch.md (1)
5-7: ネストしたバッククォートのレンダリング崩れ(軽微なドキュメント修正)。Line 6 と Line 70 の
`key={`${workspace.id}:${hostUrl}`}`はシングルバッククォートのコードスパン内に更にバッククォートが含まれているため、多くの Markdown レンダラで途中で code span が途切れて表示が崩れます。ダブルバッククォートで囲むと安全です。🔧 修正案
-(`layout.tsx:79` uses `key={`${workspace.id}:${hostUrl}`}`). Every pane React +(`layout.tsx:79` uses ``key={`${workspace.id}:${hostUrl}`}``). Every pane React-the v2 layout's `key={`${workspace.id}:${hostUrl}`}` remounts on every +the v2 layout's ``key={`${workspace.id}:${hostUrl}`}`` remounts on everyAlso applies to: 68-72
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/plans/20260423-1226-v2-pane-persistence-across-workspace-switch.md` around lines 5 - 7, ドキュメント内のコードスパン `` `key={`${workspace.id}:${hostUrl}`}` `` がネストしたバッククォートを含んでいるため Markdown レンダリングが崩れています。該当するコードスパンをシングルバッククォートからダブルバッククォートで囲む表記(例: ````key={`${workspace.id}:${hostUrl}`}```)に変更し、同じ表記が使われている他の箇所(コメントで指摘された同一スニペットの出現箇所)も同様に修正してください。apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/FileRow/FileRow.tsx (1)
46-75: ディレクトリをまたぐリネーム/コピー時に旧ディレクトリが表示されない
dirは新しいパス(file.path)から算出されるため、例えばsrc/a/old.ts → src/b/new.tsのようなケースではsrc/b/ old.ts → new.tsと表示され、元の場所(src/a/)が一切見えなくなります。Git の標準的な短縮表記(src/{a/old.ts => b/new.ts})と比べるとユーザーが変更元を追えないのは残念なポイントです。改善案:旧パスと新パスでディレクトリが異なる場合は
oldPathのフルパス(またはディレクトリ)を表示する。♻️ 提案差分
const { dir, basename } = splitPath(file.path); - const oldBasename = - file.oldPath && (file.status === "renamed" || file.status === "copied") - ? splitPath(file.oldPath).basename - : null; + const oldDisplay = + file.oldPath && (file.status === "renamed" || file.status === "copied") + ? (() => { + const old = splitPath(file.oldPath); + return old.dir === dir ? old.basename : `${old.dir}${old.basename}`; + })() + : null;- {oldBasename && ( + {oldDisplay && ( <span className="truncate text-muted-foreground"> - {oldBasename} + {oldDisplay} <span className="px-1">→</span> </span> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/FileRow/FileRow.tsx around lines 46 - 75, The current UI computes dir from the new path only, so renames/copied files that move directories lose the old directory; update the render logic to derive oldDir (e.g., const oldDir = file.oldPath ? splitPath(file.oldPath).dirname : null) and compare it to dir, and when oldDir exists and oldDir !== dir show the oldDir (or the full oldPath dirname) alongside oldBasename (before the arrow) so users can see the original directory; adjust the JSX that currently renders {dir && ...} and {oldBasename && ...} to render oldDir when different from dir and fall back to existing behavior otherwise (use symbols: file.oldPath, oldBasename, oldDir, dir, splitPath, basename).packages/host-service/src/trpc/router/git/git.ts (1)
230-241: 共有参照パターンの意図をコメントで補足したい
entryをuntrackedFilesとunstagedの両方に push して、後続のcountUntrackedFileLinesが mutate したadditionsがunstaged側にも反映されるようにしている点は効率的ですが、読み手には分かりにくいです。将来の改変でうっかり片方だけ新オブジェクト化するとunstagedの additions が 0 のまま残る潜在的なバグにつながります。短いコメントで意図を明示しておくと安全です。♻️ 提案差分
if (file.index === "?" && wd === "?") { + // Same object pushed to both lists so that + // countUntrackedFileLines' in-place additions mutation + // is visible through `unstaged` as well. const entry: ChangedFile = {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/host-service/src/trpc/router/git/git.ts` around lines 230 - 241, ここでは entry オブジェクトを untrackedFiles と unstaged の両方に push して、後続の countUntrackedFileLines が entry.additions を mutate した結果を両方に反映させる「共有参照パターン」を利用しています(つまり同一オブジェクトを参照させていることが意図)。untrackedFiles/unstaged に同じオブジェクトを入れている理由と、将来片方だけコピーしてしまうと差分が生じるリスクがある点を、関数内の該当ブロック(変数名: entry, untrackedFiles, unstaged, countUntrackedFileLines を参照)に短いコメントで明示してください。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx`:
- Around line 137-161: The PR button (the <button> that wraps
DashboardSidebarWorkspaceIcon when pullRequest is truthy) lacks a visible
keyboard focus style; add focus-visible utility classes to its className (e.g.,
focus-visible:outline-none plus a visible ring/ring-offset classes consistent
with the app theme) so Tab-key focus is clearly indicated without changing
behavior, keeping the existing onClick/onKeyDown handlers and aria-label intact.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceIcon/DashboardSidebarWorkspaceIcon.tsx`:
- Around line 62-71: The PR icon render currently short-circuits the offline
branch so offline remote devices (isRemoteDeviceOffline / LuCloudOff) aren't
visually distinguished when pullRequestState is set; modify renderPrimaryIcon
(or the parent that calls it) to apply an offline visual treatment when
isRemoteDeviceOffline is true even if pullRequestState exists—for example, wrap
the selected PrIcon (from PR_ICON_BY_STATE with PR_COLOR_BY_STATE) in a
container or pass a prop that reduces opacity or adds a dimming class when
isRemoteDeviceOffline is true so the PR state remains visible but the icon is
visually marked offline.
In
`@apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts`:
- Around line 56-92: The effect's initial run misses stale registry entries
because prevBrowserIdsRef is initialized to an empty Set; add a registry
enumeration and use it on first effect run: introduce
browserRuntimeRegistry.getAllPaneIds() (or similar) and on mount/useEffect when
prevBrowserIdsRef.current is empty, populate prevBrowserIdsRef (or merge) with
the set returned by getAllPaneIds() so the existing loop will schedule
destruction for pane IDs not present in currentBrowserIds; update useEffect in
useGlobalBrowserLifecycle (references: prevBrowserIdsRef, useEffect,
extractBrowserPaneIds, pendingDestruction, DESTROY_DELAY_MS, allWorkspaceRows,
collections) to call the new API and handle timers consistently.
In
`@apps/marketing/src/app/pricing/components/PricingTiers/components/PricingCard/PricingCard.tsx`:
- Around line 41-45: Replace the visual-only <span> used for the old price in
PricingCard with a semantic element (use <del> or <s>) for the strikethrough
value (the variable strikethrough) and add an appropriate aria-label (e.g.,
"通常価格" or "旧価格") so screen readers know it is the previous price; keep the
existing CSS classes for styling and ensure the active price element has a
contrasting aria-label (e.g., "現在の価格") or role to avoid both being read as
equal.
In `@packages/chat/package.json`:
- Line 39: Update the dependency entry for "@mastra/core" in package.json from
"1.26.0-alpha.3" to the stable release "1.27.0"; locate the "@mastra/core"
dependency key and replace its version string, then run your package manager
(npm/yarn/pnpm) to refresh lockfile and verify install/build.
---
Outside diff comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/`$pendingId/buildForkAgentLaunch.test.ts:
- Around line 116-123: The test currently relies on pendingBase() defaulting
agentId to null which causes resolveAgentId to return null and makes the
assertion meaningless; update the test that calls buildForkAgentLaunch so
pendingBase is given an explicit non-null agentId (e.g., "agent-1") to ensure
the code path where agentConfigs: [] is evaluated by resolveAgentId and the
function actually returns null due to no enabled agents rather than due to a
null agentId; keep references to buildForkAgentLaunch, pendingBase,
resolveAgentId and agentConfigs when making the change.
---
Nitpick comments:
In
`@apps/desktop/plans/20260423-1226-v2-pane-persistence-across-workspace-switch.md`:
- Around line 5-7: ドキュメント内のコードスパン `` `key={`${workspace.id}:${hostUrl}`}` ``
がネストしたバッククォートを含んでいるため Markdown
レンダリングが崩れています。該当するコードスパンをシングルバッククォートからダブルバッククォートで囲む表記(例:
````key={`${workspace.id}:${hostUrl}`}```)に変更し、同じ表記が使われている他の箇所(コメントで指摘された同一スニペットの出現箇所)も同様に修正してください。
In `@apps/desktop/src/main/lib/agent-setup/agent-wrappers.test.ts`:
- Around line 1189-1250: Add an idempotency assertion by calling
getCodexGlobalHooksJsonContent a second time and asserting the JSON output is
unchanged: after the existing first call/parse (content → parsed), call
getCodexGlobalHooksJsonContent again (content2), ensure content2 is not null,
parse it and assert JSON.parse(content2) equals JSON.parse(content) (or toEqual
on the parsed objects). This uses the existing getCodexGlobalHooksJsonContent
helper and the parsed variable names so the test will detect regressions where
reconcile logic might duplicate entries on re-application.
In `@apps/desktop/src/main/lib/host-service-coordinator.ts`:
- Around line 291-300: The semver check in semver.satisfies(version,
`>=${MIN_HOST_SERVICE_VERSION}`) can incorrectly reject prerelease versions and
the log message is misleading; update the compatibility logic in the
host-service check (the block using semver.satisfies, MIN_HOST_SERVICE_VERSION
and organizationId) to explicitly handle prereleases (e.g., pass {
includePrerelease: true } to semver.satisfies or first check
semver.valid/semver.prerelease) and compute a clear reason string (e.g.,
"incompatible: prerelease X", "incompatible: invalid version", or "incompatible:
version X < MIN_HOST_SERVICE_VERSION") then include that reason and the raw
version in the console/processLogger output instead of "version X <
MIN_HOST_SERVICE_VERSION" so the log accurately reflects
prerelease/invalid-format failures.
In `@apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts`:
- Around line 77-85: The JSDoc for function onlyMod is out of sync with its
implementation: the comment says Shift is allowed but the code enforces
!event.shiftKey. Either update the JSDoc to state “no other modifier held
(including Shift)” to reflect the current behavior, or change the implementation
in onlyMod to allow Shift (remove the !event.shiftKey check and adjust logic
accordingly) depending on the intended semantics; use the onlyMod signature and
usages to guide which variant to keep.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/DestroyConfirmPane/DestroyConfirmPane.tsx`:
- Around line 88-96: The Delete button becomes disabled during the preflight
check but lacks visual feedback; update DestroyConfirmPane to show a spinner
and/or change the button label when isCheckingStatus is true (e.g., replace
"Delete" with "Checking…" and render a small spinner icon inside the Button
while disabled) so users understand why the button is inactive; modify the
Button usage in DestroyConfirmPane to conditionally render the spinner and
alternate label based on the isCheckingStatus prop while keeping onConfirm and
the disabled behavior intact.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/DashboardSidebarDeleteDialog.tsx`:
- Around line 55-67: Compute the combined warning flag once in the parent and
pass it into the child instead of duplicating the logic: remove the local
calculation of hasWarnings in DestroyConfirmPane and add a new boolean prop
(e.g., hasWarnings) on DestroyConfirmPane; in the parent
(DashboardSidebarDeleteDialog) keep the existing hasWarnings = hasChanges ||
hasUnpushedCommits and pass hasWarnings to DestroyConfirmPane, then update
DestroyConfirmPane to use the received hasWarnings both for banner display and
for calling onConfirm/force logic (ensure run(hasWarnings) in the parent or
propagate the flag to where run is invoked).
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/`$pendingId/buildForkAgentLaunch.ts:
- Around line 129-176: The call to indexResolvedAgentConfigs is being done
twice: once inside resolveAgentId and again in the caller to re-fetch
agentConfig, causing redundant indexing and checks; change resolveAgentId to
return the selected ResolvedAgentConfig | null (not just AgentDefinitionId),
e.g. resolveAgentConfig(selected, configs) that performs
indexResolvedAgentConfigs(configs) and enabled check, then in the caller use the
returned ResolvedAgentConfig directly (replace agentId + agentConfig lookup with
the single returned config) so you remove the second indexResolvedAgentConfigs
call and duplicate enabled logic; update references to resolveAgentId,
agentConfig, and any code that expects an id to instead use the config or derive
the id from it.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/FileRow/FileRow.tsx:
- Around line 46-75: The current UI computes dir from the new path only, so
renames/copied files that move directories lose the old directory; update the
render logic to derive oldDir (e.g., const oldDir = file.oldPath ?
splitPath(file.oldPath).dirname : null) and compare it to dir, and when oldDir
exists and oldDir !== dir show the oldDir (or the full oldPath dirname)
alongside oldBasename (before the arrow) so users can see the original
directory; adjust the JSX that currently renders {dir && ...} and {oldBasename
&& ...} to render oldDir when different from dir and fall back to existing
behavior otherwise (use symbols: file.oldPath, oldBasename, oldDir, dir,
splitPath, basename).
In
`@apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts`:
- Line 4: The import of browserRuntimeRegistry in useGlobalBrowserLifecycle
comes from a workspace-specific deep path, inverting dependency direction;
extract browserRuntimeRegistry into a neutral shared module (e.g., create
renderer/lib/browser/browserRuntimeRegistry.ts), export the same identifier,
update all callers (useGlobalBrowserLifecycle and BrowserPane and any other
consumers) to import from the new shared module instead of the _dashboard/...
path, and remove the original file or re-export from it to avoid duplicate
definitions; this mirrors how terminalRuntimeRegistry is placed and keeps global
lifecycle hooks independent of route-specific code.
- Around line 94-101: The separate cleanup useEffect that clears
pendingDestruction.current timers (the useEffect with an empty dependency array
that iterates pendingDestruction.current.values() and calls clearTimeout) is
intentionally isolated from the other effect that depends on [allWorkspaceRows,
collections] because merging them would cause timers to be cleared on every
dependency change; add a short explanatory comment immediately above the
empty-deps useEffect stating that it must run only on unmount to avoid clearing
timers during dependency-driven re-runs and must not be merged with the
useEffect handling allWorkspaceRows/collections to preserve the intended
lifecycle semantics.
- Around line 75-86: The timeout callback uses a stale closure over collections
causing incorrect destroy checks; update the effect so the callback reads the
latest collections via a ref (e.g., create collectionsRef = useRef(collections)
and keep it synced whenever collections changes) and inside the setTimeout
callback use collectionsRef.current.values() (or clear
pendingDestruction.current when collections identity changes) to ensure
extractBrowserPaneIds(freshRows) uses the current collection before calling
browserRuntimeRegistry.destroy(browserId); keep DESTROY_DELAY_MS and
pendingDestruction.current logic intact and ensure the effect dependency
[allWorkspaceRows, collections] still triggers the ref update.
- Around line 13-27: Replace the unsafe cast in extractBrowserPaneIds with
runtime type guards: check that row.paneLayout is an object and has a "tabs"
property (e.g., layout && typeof layout === "object" && "tabs" in layout) before
treating it as WorkspaceState, iterate tabs and panes as now, and additionally
verify pane.kind === "browser" && typeof pane.id === "string" before calling
ids.add(pane.id); mirror the minimal guards used in useGlobalTerminalLifecycle
to keep behavior consistent.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.test.ts`:
- Around line 119-143: Add two Linux Ctrl+C test cases to the array in
clipboardShortcuts.test.ts to cover the Linux-specific behavior in
clipboardShortcuts.ts: add one named "Linux Ctrl+C with selection stays with the
PTY (SIGINT)" and one "Linux Ctrl+C without selection stays with the PTY
(SIGINT)"; both should use event = makeEvent({ code: "KeyC", ctrlKey: true })
with options { isMac: false, isWindows: false } and hasSelection true/false
respectively, and both expected = false to assert that Ctrl+C always does not
bubble to the UI but is sent to the PTY.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.ts`:
- Around line 30-72: The function shouldBubbleClipboardShortcut has drifted to a
more general terminal chord-bubbling role; rename it to
shouldBubbleTerminalChord (and update its JSDoc to reflect the Ghostty-derived
Cmd-chord semantics) and update all call sites (e.g., the caller in
terminal-runtime.ts) to use the new name so the API matches behavior and intent.
- Around line 49-51: Update the JSDoc for the function
shouldBubbleClipboardShortcut to explicitly state that it must be invoked after
translateLineEditChord and shouldSelectAllShortcut (i.e., it assumes those have
already been applied), documenting the Mac-specific keycode handling order
dependency so future changes don't break behavior; reference the function name
shouldBubbleClipboardShortcut and the dependent helpers translateLineEditChord
and shouldSelectAllShortcut in the comment and note the required call order on
macOS.
In
`@apps/marketing/src/app/pricing/components/PricingTiers/components/PricingCard/PricingCard.tsx`:
- Around line 83-107: Add an explicit return type for resolvePrice by defining a
dedicated interface (e.g., ResolvedPrice) that describes the object shape
(fields: display: string, strikethrough: string | null, note: string, cadence?:
string | null as appropriate) and update the function signature to return that
interface; ensure all branches in resolvePrice (fixed, custom, and the
monthly/yearly path) return objects conforming to this interface so TypeScript
will catch future regressions and keep the shape consistent.
In `@packages/host-service/src/trpc/router/git/git.ts`:
- Around line 230-241: ここでは entry オブジェクトを untrackedFiles と unstaged の両方に push
して、後続の countUntrackedFileLines が entry.additions を mutate
した結果を両方に反映させる「共有参照パターン」を利用しています(つまり同一オブジェクトを参照させていることが意図)。untrackedFiles/unstaged
に同じオブジェクトを入れている理由と、将来片方だけコピーしてしまうと差分が生じるリスクがある点を、関数内の該当ブロック(変数名: entry,
untrackedFiles, unstaged, countUntrackedFileLines を参照)に短いコメントで明示してください。
In
`@packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts`:
- Around line 172-208: The two identical git rollback blocks in
ai-workspace-names.ts should be extracted into a small helper to avoid
duplication: create a helper (e.g., rollbackGitBranch or rollbackBranchRename)
that accepts ctx, worktreePath, workspaceId, fromBranch (deduped) and toBranch
(oldBranchName), calls ctx.git(worktreePath).then(g => g.raw(["branch","-m",
fromBranch, toBranch])) and catches errors to console.warn using the same
contextual message currently used in both places; then replace the two inline
rollback blocks (the try/catch rollback after updateNameFromHost failure and the
cloud no-op rollback) with calls to that helper to keep behavior identical and
centralize future changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e703f9b8-b4ef-4bcf-830a-f85664527b3a
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (46)
apps/desktop/package.jsonapps/desktop/plans/20260423-1226-v2-pane-persistence-across-workspace-switch.mdapps/desktop/src/main/lib/agent-setup/agent-wrappers-common.tsapps/desktop/src/main/lib/agent-setup/agent-wrappers.test.tsapps/desktop/src/main/lib/host-service-coordinator.tsapps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.tsapps/desktop/src/renderer/lib/terminal/terminal-runtime.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/automations/$automationId/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/DashboardSidebarDeleteDialog.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/ConflictPane/ConflictPane.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/ConflictPane/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/DestroyConfirmPane/DestroyConfirmPane.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/UnknownErrorPane/UnknownErrorPane.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/UnknownErrorPane/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/hooks/useDestroyDialogState/useDestroyDialogState.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDiffStats/DashboardSidebarWorkspaceDiffStats.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceIcon/DashboardSidebarWorkspaceIcon.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/$pendingId/buildForkAgentLaunch.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/$pendingId/buildForkAgentLaunch.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/$pendingId/buildIntentPayload.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pending/$pendingId/dispatchForkLaunch.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/FileRow/FileRow.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsxapps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/PromptGroup.tsxapps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/hooks/useSubmitWorkspace/useSubmitWorkspace.tsapps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/GlobalBrowserLifecycle.tsxapps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/index.tsapps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.tsapps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/index.tsapps/desktop/src/renderer/routes/_authenticated/layout.tsxapps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.test.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/clipboardShortcuts.tsapps/marketing/src/app/components/Header/components/DesktopNav/DesktopNav.tsxapps/marketing/src/app/pricing/components/PricingTiers/components/PricingCard/PricingCard.tsxpackages/chat/package.jsonpackages/chat/src/server/shared/small-model/get-small-model.tspackages/host-service/package.jsonpackages/host-service/src/trpc/router/git/git.tspackages/host-service/src/trpc/router/git/utils/git-helpers.tspackages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.tspackages/host-service/src/trpc/router/workspace-creation/utils/list-branch-names.tspackages/host-service/src/trpc/router/workspace-creation/workspace-creation.tspackages/trpc/src/router/v2-workspace/v2-workspace.ts
💤 Files with no reviewable changes (4)
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/ConflictPane/index.ts
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/UnknownErrorPane/index.ts
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/UnknownErrorPane/UnknownErrorPane.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarDeleteDialog/components/ConflictPane/ConflictPane.tsx
| {pullRequest ? ( | ||
| <button | ||
| type="button" | ||
| onClick={(event) => { | ||
| event.stopPropagation(); | ||
| openUrl.mutate(pullRequest.url); | ||
| }} | ||
| onKeyDown={(event) => { | ||
| if (event.key === "Enter" || event.key === " ") { | ||
| event.stopPropagation(); | ||
| } | ||
| }} | ||
| aria-label={`Open pull request #${pullRequest.number}`} | ||
| className="relative mr-2.5 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded hover:bg-foreground/10" | ||
| > | ||
| <DashboardSidebarWorkspaceIcon | ||
| hostType={hostType} | ||
| hostIsOnline={hostIsOnline} | ||
| isActive={isActive} | ||
| variant="expanded" | ||
| workspaceStatus={null} | ||
| creationStatus={creationStatus} | ||
| pullRequestState={pullRequest.state} | ||
| /> | ||
| </button> |
There was a problem hiding this comment.
PR ボタンにキーボードフォーカス可視化スタイルがない
hover:bg-foreground/10 は定義されていますが、focus-visible: 相当の視覚指示がないため、Tab でフォーカスを当てた際にユーザーが現在位置を認識しづらい状態です。このボタンは role="button" な親行の中にネストされているため、特にキーボード操作時のフォーカス遷移を明示することが重要です。
♿ 提案: focus-visible リング追加
- className="relative mr-2.5 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded hover:bg-foreground/10"
+ className="relative mr-2.5 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded hover:bg-foreground/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {pullRequest ? ( | |
| <button | |
| type="button" | |
| onClick={(event) => { | |
| event.stopPropagation(); | |
| openUrl.mutate(pullRequest.url); | |
| }} | |
| onKeyDown={(event) => { | |
| if (event.key === "Enter" || event.key === " ") { | |
| event.stopPropagation(); | |
| } | |
| }} | |
| aria-label={`Open pull request #${pullRequest.number}`} | |
| className="relative mr-2.5 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded hover:bg-foreground/10" | |
| > | |
| <DashboardSidebarWorkspaceIcon | |
| hostType={hostType} | |
| hostIsOnline={hostIsOnline} | |
| isActive={isActive} | |
| variant="expanded" | |
| workspaceStatus={null} | |
| creationStatus={creationStatus} | |
| pullRequestState={pullRequest.state} | |
| /> | |
| </button> | |
| {pullRequest ? ( | |
| <button | |
| type="button" | |
| onClick={(event) => { | |
| event.stopPropagation(); | |
| openUrl.mutate(pullRequest.url); | |
| }} | |
| onKeyDown={(event) => { | |
| if (event.key === "Enter" || event.key === " ") { | |
| event.stopPropagation(); | |
| } | |
| }} | |
| aria-label={`Open pull request #${pullRequest.number}`} | |
| className="relative mr-2.5 flex size-5 shrink-0 cursor-pointer items-center justify-center rounded hover:bg-foreground/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" | |
| > | |
| <DashboardSidebarWorkspaceIcon | |
| hostType={hostType} | |
| hostIsOnline={hostIsOnline} | |
| isActive={isActive} | |
| variant="expanded" | |
| workspaceStatus={null} | |
| creationStatus={creationStatus} | |
| pullRequestState={pullRequest.state} | |
| /> | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx`
around lines 137 - 161, The PR button (the <button> that wraps
DashboardSidebarWorkspaceIcon when pullRequest is truthy) lacks a visible
keyboard focus style; add focus-visible utility classes to its className (e.g.,
focus-visible:outline-none plus a visible ring/ring-offset classes consistent
with the app theme) so Tab-key focus is clearly indicated without changing
behavior, keeping the existing onClick/onKeyDown handlers and aria-label intact.
| const renderPrimaryIcon = () => { | ||
| if (pullRequestState) { | ||
| const PrIcon = PR_ICON_BY_STATE[pullRequestState]; | ||
| return ( | ||
| <PrIcon | ||
| className={cn("size-3.5", PR_COLOR_BY_STATE[pullRequestState])} | ||
| strokeWidth={1.75} | ||
| /> | ||
| ); | ||
| } |
There was a problem hiding this comment.
PR アイコン表示時にリモートデバイスのオフライン状態が視覚化されない
pullRequestState が設定されると、isRemoteDeviceOffline 分岐(LuCloudOff)に到達しなくなります。PR 付きの remote-device ワークスペースがオフラインのとき、ユーザーはサイドバーのアイコンだけでは「このデバイスは到達不可」であることを判別できません。
意図した仕様(PR 状態を最優先で表示する)であれば問題ありませんが、オフライン時の可視性を残したい場合は、isRemoteDeviceOffline のときだけアイコンの opacity を落とすなどの合成表示を検討してください。
💡 参考: オフライン時に半透明化する差分例
const renderPrimaryIcon = () => {
if (pullRequestState) {
const PrIcon = PR_ICON_BY_STATE[pullRequestState];
return (
<PrIcon
- className={cn("size-3.5", PR_COLOR_BY_STATE[pullRequestState])}
+ className={cn(
+ "size-3.5",
+ PR_COLOR_BY_STATE[pullRequestState],
+ isRemoteDeviceOffline && "opacity-60",
+ )}
strokeWidth={1.75}
/>
);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceIcon/DashboardSidebarWorkspaceIcon.tsx`
around lines 62 - 71, The PR icon render currently short-circuits the offline
branch so offline remote devices (isRemoteDeviceOffline / LuCloudOff) aren't
visually distinguished when pullRequestState is set; modify renderPrimaryIcon
(or the parent that calls it) to apply an offline visual treatment when
isRemoteDeviceOffline is true even if pullRequestState exists—for example, wrap
the selected PrIcon (from PR_ICON_BY_STATE with PR_COLOR_BY_STATE) in a
container or pass a prop that reduces opacity or adds a dimming class when
isRemoteDeviceOffline is true so the PR state remains visible but the icon is
visually marked offline.
| useEffect(() => { | ||
| const currentBrowserIds = extractBrowserPaneIds(allWorkspaceRows); | ||
| const prevBrowserIds = prevBrowserIdsRef.current; | ||
|
|
||
| // Cancel any pending destruction for ids that reappeared (e.g. pane | ||
| // moved between workspaces, user undo, or the transient replaceState | ||
| // churn we were fighting in the first place). | ||
| for (const browserId of currentBrowserIds) { | ||
| const timer = pendingDestruction.current.get(browserId); | ||
| if (timer) { | ||
| clearTimeout(timer); | ||
| pendingDestruction.current.delete(browserId); | ||
| } | ||
| } | ||
|
|
||
| for (const browserId of prevBrowserIds) { | ||
| if (currentBrowserIds.has(browserId)) continue; | ||
| if (pendingDestruction.current.has(browserId)) continue; | ||
|
|
||
| const timer = setTimeout(() => { | ||
| pendingDestruction.current.delete(browserId); | ||
|
|
||
| const freshRows = Array.from( | ||
| collections.v2WorkspaceLocalState.state.values(), | ||
| ); | ||
| const freshIds = extractBrowserPaneIds(freshRows); | ||
|
|
||
| if (!freshIds.has(browserId)) { | ||
| browserRuntimeRegistry.destroy(browserId); | ||
| } | ||
| }, DESTROY_DELAY_MS); | ||
|
|
||
| pendingDestruction.current.set(browserId, timer); | ||
| } | ||
|
|
||
| prevBrowserIdsRef.current = currentBrowserIds; | ||
| }, [allWorkspaceRows, collections]); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# useGlobalTerminalLifecycle の実装と registry の公開 API を確認
fd -t f 'useGlobalTerminalLifecycle.ts' | head -20
fd -t f 'browserRuntimeRegistry.ts' | xargs -I {} rg -n --type=ts -C 2 '^\s*(public\s+)?(destroy|get|has|list|keys|ids)\s*[\(:]' {}Repository: MocA-Love/superset
Length of output: 328
🏁 Script executed:
#!/bin/bash
# Find and inspect browserRuntimeRegistry implementation
fd -t f 'browserRuntimeRegistry.ts' -o 'browserRuntimeRegistry.js'Repository: MocA-Love/superset
Length of output: 232
🏁 Script executed:
#!/bin/bash
# Find where browserRuntimeRegistry is exported or defined
rg -l 'browserRuntimeRegistry' --type ts --type tsx | head -5Repository: MocA-Love/superset
Length of output: 90
🏁 Script executed:
#!/bin/bash
# Get the full useGlobalTerminalLifecycle implementation, especially around prevRef initialization
fd -t f 'useGlobalTerminalLifecycle.ts' -x cat -n {} | head -100Repository: MocA-Love/superset
Length of output: 3302
🏁 Script executed:
#!/bin/bash
# Find and inspect registry implementations
fd -t f 'terminal-runtime-registry' --type fRepository: MocA-Love/superset
Length of output: 130
🏁 Script executed:
#!/bin/bash
# Find browserRuntimeRegistry
fd -t f 'browserRuntimeRegistry' --type fRepository: MocA-Love/superset
Length of output: 218
🏁 Script executed:
#!/bin/bash
# Search more broadly for registry files
fd -type f 'runtime-registry'Repository: MocA-Love/superset
Length of output: 233
🏁 Script executed:
#!/bin/bash
# Read terminalRuntimeRegistry
cat -n apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.tsRepository: MocA-Love/superset
Length of output: 11806
🏁 Script executed:
#!/bin/bash
# Read browserRuntimeRegistry
cat -n apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/\$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.tsRepository: MocA-Love/superset
Length of output: 25211
🏁 Script executed:
#!/bin/bash
# Check browserRuntimeRegistry export and if groups is accessible
rg -n "export.*browserRuntimeRegistry" apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/\$workspaceId/hooks/usePaneRegistry/components/BrowserPane/browserRuntimeRegistry.ts -A 5Repository: MocA-Love/superset
Length of output: 303
🏁 Script executed:
#!/bin/bash
# Check how browserRuntimeRegistry is used in useGlobalBrowserLifecycle
rg -n "browserRuntimeRegistry" apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts -B 2 -A 2Repository: MocA-Love/superset
Length of output: 574
🏁 Script executed:
#!/bin/bash
# Check the full useGlobalBrowserLifecycle file for context about initialization
cat -n apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts | head -100Repository: MocA-Love/superset
Length of output: 4179
初回マウント時に stale な registry エントリがクリーンアップされない
prevBrowserIdsRef は new Set() で初期化されるため、初回 effect 実行時には「以前存在した ID」が空集合です。その結果、アプリ起動直後に registry に残っている browser エントリがあっても、このループではスケジュール対象になりません。
通常起動シナリオではアプリ起動時に registry は空なので問題は顕在化しにくいですが、HMR 中や GlobalBrowserLifecycle が再マウントされた場合、前回セッションで生成された webview がリークし得ます。useGlobalTerminalLifecycle も同じ挙動ですが、そちらは terminalRuntimeRegistry.getAllTerminalIds() で enumeration API を持っています。
browserRuntimeRegistry には現在、公開された enumeration API がないため、初回実行時に registry に存在するすべての pane を列挙して差分を判定する実装に対応するには、新たに getAllPaneIds() のような API を追加するか、別の手段で初回実行を検出して既存 registry の内容を取得する必要があります。両者まとめて対処するのが望ましいです。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/routes/_authenticated/components/GlobalBrowserLifecycle/hooks/useGlobalBrowserLifecycle/useGlobalBrowserLifecycle.ts`
around lines 56 - 92, The effect's initial run misses stale registry entries
because prevBrowserIdsRef is initialized to an empty Set; add a registry
enumeration and use it on first effect run: introduce
browserRuntimeRegistry.getAllPaneIds() (or similar) and on mount/useEffect when
prevBrowserIdsRef.current is empty, populate prevBrowserIdsRef (or merge) with
the set returned by getAllPaneIds() so the existing loop will schedule
destruction for pane IDs not present in currentBrowserIds; update useEffect in
useGlobalBrowserLifecycle (references: prevBrowserIdsRef, useEffect,
extractBrowserPaneIds, pendingDestruction, DESTROY_DELAY_MS, allWorkspaceRows,
collections) to call the new API and handle timers consistently.
| {strikethrough && ( | ||
| <span className="text-2xl font-medium tracking-tight leading-none text-muted-foreground line-through"> | ||
| {strikethrough} | ||
| </span> | ||
| )} |
There was a problem hiding this comment.
取り消し線価格のアクセシビリティを改善してください。
line-through は視覚的な装飾のみで、スクリーンリーダーには「旧価格」であることが伝わりません。<span> ではなく意味論的に <s> もしくは <del> を使用し、さらに aria-label で「通常価格」などのコンテキストを付与することを検討してください。現状だとスクリーンリーダー利用者には取り消し線のない元価格と割引後価格が並列で読み上げられ、どちらが有効な価格か判別できません。
♿ 提案されたアクセシビリティ修正
- {strikethrough && (
- <span className="text-2xl font-medium tracking-tight leading-none text-muted-foreground line-through">
- {strikethrough}
- </span>
- )}
+ {strikethrough && (
+ <s
+ aria-label={`Regular price ${strikethrough}`}
+ className="text-2xl font-medium tracking-tight leading-none text-muted-foreground"
+ >
+ {strikethrough}
+ </s>
+ )}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {strikethrough && ( | |
| <span className="text-2xl font-medium tracking-tight leading-none text-muted-foreground line-through"> | |
| {strikethrough} | |
| </span> | |
| )} | |
| {strikethrough && ( | |
| <s | |
| aria-label={`Regular price ${strikethrough}`} | |
| className="text-2xl font-medium tracking-tight leading-none text-muted-foreground" | |
| > | |
| {strikethrough} | |
| </s> | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/marketing/src/app/pricing/components/PricingTiers/components/PricingCard/PricingCard.tsx`
around lines 41 - 45, Replace the visual-only <span> used for the old price in
PricingCard with a semantic element (use <del> or <s>) for the strikethrough
value (the variable strikethrough) and add an appropriate aria-label (e.g.,
"通常価格" or "旧価格") so screen readers know it is the previous price; keep the
existing CSS classes for styling and ensure the active price element has a
contrasting aria-label (e.g., "現在の価格") or role to avoid both being read as
equal.
| "@ai-sdk/anthropic": "^3.0.43", | ||
| "@ai-sdk/openai": "3.0.36", | ||
| "@mastra/core": "1.25.0", | ||
| "@mastra/core": "1.26.0-alpha.3", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: `@mastra/core` の 1.26.0-alpha.3 バージョンの存在と安定性を確認
# npm レジストリで利用可能なバージョンを確認
npm view `@mastra/core` versions --json | jq -r '.[]' | grep "1.26.0-alpha.3"
# 最新の安定版を確認
npm view `@mastra/core` version
# パッケージの情報を取得
npm view `@mastra/core`@1.26.0-alpha.3 --json | jq '{version, deprecated, dist}'Repository: MocA-Love/superset
Length of output: 907
🌐 Web query:
@mastra/core 1.26.0-alpha.3 changelog breaking changes
💡 Result:
No specific changelog or breaking changes found for @mastra/core 1.26.0-alpha.3. The package exists per Snyk security page (result 9), but detailed release notes for this exact alpha version are not publicly available in searched sources. The official CHANGELOG.md is at https://github.com/mastra-ai/mastra/blob/main/packages/core/CHANGELOG.md (results 21,2,19). Recent stable releases like 1.16.0 (result 3, published 2026-03-26) contain minor and patch changes only, no breaking changes listed. Earlier alphas like 1.14.0-alpha.3 (result 19) and 0.24.7-alpha.3 (result 62) show patch changes. For 1.x series breaking changes, see the 1.0.0 release blog post (result 10): subpath imports required, tool execution signature changed, context namespaces, tool output validation, API renames, pagination changes, observability config, Node 22.13+ minimum, DB migrations. Check the GitHub repo releases page https://github.com/mastra-ai/mastra/releases and packages/core CHANGELOG.md directly for the latest alpha details, as they may have been published after search index.
Citations:
- 1: https://github.com/mastra-ai/mastra/blob/b83e42f04324a07ff012d3b1810fe7d707b4ba00/packages/core/CHANGELOG.md
- 2: https://security.snyk.io/package/npm/%40mastra%2Fcore/1.26.0
- 3: https://mastra.ai/blog/changelog-2026-01-20
- 4: https://github.com/mastra-ai/mastra/blob/cb611a1e/packages/core/CHANGELOG.md
- 5: https://github.com/mastra-ai/mastra/blob/main/packages/core/CHANGELOG.md
- 6: https://github.com/mastra-ai/mastra/releases/tag/%40mastra%2Fcore%401.16.0
@mastra/core を最新の安定版 1.27.0 にアップグレードしてください
@mastra/core のバージョンが 1.26.0-alpha.3(アルファ版)に設定されていますが、npm レジストリで確認したところ最新の安定版は 1.27.0 です。より新しい安定版が利用可能なため、本番環境での使用を考慮して 1.27.0 へのアップグレードをお勧めします。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/chat/package.json` at line 39, Update the dependency entry for
"@mastra/core" in package.json from "1.26.0-alpha.3" to the stable release
"1.27.0"; locate the "@mastra/core" dependency key and replace its version
string, then run your package manager (npm/yarn/pnpm) to refresh lockfile and
verify install/build.
Summary
計画書以降に upstream が前進した 12 commits を取り込み。目標は GitHub UI の "behind" 数値減少。
計画完遂 (PR #2c #408 まで) 後、upstream で新規に追加された non-cherry-picked commits を Codex 事前調査で特定:
43131f6fda8b29fa25be3b5fcc5083a518fe9038e32eae306eb20b4163c8d08678832461b3b70cd5c2bd46ecc05697c8a2d847bc2c38支援 commits
556dfad75style: lint 自動修正 (agent-wrappers.test.tsフォーマット)b91b1c418fix(deps): bun.lock 完全再生成 (@mastra/core 1.26.0-alpha.3resolution 脱落を修復)Conflict 解消方針(Codex 事前分析に基づく)
#3688(useDestroyDialogState.ts):electronTrpc.workspaces.canDelete+ preflight warning + force retry を統合#3687(terminal-runtime-registry.ts, layout.tsx):terminalRendererDebug+ backoff リセットロジックを維持connect/reconnect分割を統合GlobalBrowserLifecycleをMainWindowEffectsと共存させて追加#3692(workspace-creation.ts, v2-workspace.ts):applyAiWorkspaceRename+ AI rename ロジック +workspaceNameWasAutoGeneratedフラグ追加updateNameFromHost手続きを v2-workspace に追加#3699(PromptGroup.tsx):useSubmitWorkspace(projectId, selectedAgent)変更判断委譲事項は全て「採用」方針
~/.superset/worktrees/<projectId>への移動採用(fork のSUPERSET_WORKSPACE_NAME=superset運用と整合)TERM_PROGRAM=kitty(fix(desktop): claim TERM_PROGRAM=kitty so TUIs parse Shift+Enter CSI-u superset-sh/superset#3667) と Ghostty keyboard model (fix(desktop): adopt Ghostty keyboard model in v2 terminal superset-sh/superset#3700) 併用Fork 固有機能ヘルスチェック
baseline 比較で 全項目一致:
ansi_up/@vscode/ripgrep/@xyflow/react依存TERMINAL_OPTIONS(5) /SUPERSET_WORKSPACE_NAME(24) /moonshot-ai.kimi-code(5)v1MigrationState,TiptapPromptEditor1.5.10,dmg.size="4g"listBranchessortOrder/pinDefaultterminalRendererDebuginstrumentation /MainWindowEffectssingleton / 楽観削除 UX / PR checkout+baseBranchSource+fork note+GitHub link commandTest plan
bun install正常完了bun run typecheckグリーン (27/27)bun run lintグリーン注
project_upstream_batch_workflow.md) が記録済み、今後も同じパターンで取り込み継続可能Summary by CodeRabbit
リリースノート
新機能
バグ修正
改善