Skip to content

Integrate T3Code roadmap slices#98

Open
Jay1 wants to merge 27 commits into
mainfrom
feature/t3code-roadmap-integration
Open

Integrate T3Code roadmap slices#98
Jay1 wants to merge 27 commits into
mainfrom
feature/t3code-roadmap-integration

Conversation

@Jay1

@Jay1 Jay1 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • add the JCode design contract and OpenCode resume hardening
  • add chat wrapping controls, provider maintenance opt-out, composer stabilization, timeline affordances, and provider usage status
  • add managed worktree cleanup, read-only VCS status, Copilot provider ADR/spike, and backend discovery seams

Verification

  • Final roadmap verification wave completed before committing: F1 plan compliance, F2 code quality, F3 manual/data QA, and F4 scope fidelity all confirmed
  • Working tree was clean after 20 atomic commits

Notes

  • Browser QA for affected slices used data/source-surface evidence where Playwright Chromium was unavailable on ubuntu26.04-x64
  • Aikido/security MCP scans were unavailable where attempted during roadmap evidence capture
  • No DPCode adaptation is included; this PR is T3Code-roadmap only

Summary by CodeRabbit

  • New Features

    • Added chat markdown word-wrap toggles for code blocks and tables.
    • Added chat timeline minimap and expandable activity details (commands and file changes).
    • Added runtime/provider usage UI: usage/rate-limit chips and a VCS command-center status panel.
    • Added new settings for chat markdown wrapping and enabling/disabling provider update checks.
    • Added managed worktree cleanup classification and safer cleanup actions.
  • Bug Fixes

    • Improved chat timeline live-edge handling and expand/collapse behavior.
    • Refined session resume logic to reuse or restart provider sessions more reliably.
    • Improved provider “update checks” behavior when disabled (including messaging).

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds backend WSL path/discovery support, managed worktree cleanup classification, OpenCode session resume changes, provider update-check settings and a Copilot spike, chat markdown wrap settings, provider usage and VCS status UI, composer prompt traits, chat timeline details/minimap, and a cockpit design document.

Changes

Backend WSL Support

Layer / File(s) Summary
Backend contracts and path resolution
packages/contracts/src/backend.ts, packages/contracts/src/backend.test.ts, packages/contracts/src/index.ts, apps/server/src/backend/backendPathResolver.ts
Defines backend schemas and WSL/Windows path conversion helpers used by backend resolution.
Backend discovery and resolution
apps/server/src/backend/backendRegistry.ts, apps/server/src/backend/backendRegistry.test.ts
Discovers host and WSL backends, probes distro architecture, and resolves project backends from workspace roots or overrides.

Managed Worktree Cleanup

Layer / File(s) Summary
Managed worktree contract
packages/contracts/src/server.ts, packages/contracts/src/server.test.ts
Extends managed worktree schema fields with branch, dirty/unmerged flags, cleanup status, and cleanup explanation.
GitCore worktree listing
apps/server/src/git/Layers/GitCore.ts, apps/server/src/git/Layers/GitCore.test.ts, apps/server/src/git/Services/GitCore.ts
Parses porcelain worktree output, computes cleanup safety, and exposes managed worktree listing through GitCore.
wsRpc worktree listing
apps/server/src/wsRpc.ts
Replaces the stub worktree RPC with a scoped handler that aggregates managed worktrees across projects.
Web cleanup classification and settings UI
apps/web/src/worktreeCleanup.ts, apps/web/src/worktreeCleanup.test.ts, apps/web/src/routes/_chat.settings.tsx
Classifies worktrees into removable states and updates the settings route delete flow and worktree rows.

Provider, Session, and Settings

Layer / File(s) Summary
OpenCode session resume
apps/server/src/provider/Layers/OpenCodeAdapter.ts, apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
Detects not-found sessions, reuses valid resume cursors via session.update, and avoids aborting winning sessions during concurrent starts.
Provider update checks & Copilot spike
apps/server/src/provider/providerMaintenance.ts, apps/server/src/provider/providerMaintenance.test.ts, apps/server/src/provider/Layers/ProviderHealth.ts, apps/server/src/provider/copilotProviderSpike.ts, apps/server/src/provider/copilotProviderSpike.test.ts, docs/adr/0009-copilot-provider-entry-path.md, docs/adr/README.md
Adds a toggle to skip version-advisory network checks and a Copilot model-source spike documented via ADR.
Settings schema, mapping, and UI
packages/contracts/src/settings.ts, packages/contracts/src/settings.test.ts, apps/web/src/appSettings.ts, apps/web/src/appSettings.test.ts, apps/web/src/components/ChatMarkdown.tsx, apps/web/src/components/ChatMarkdown.test.tsx, apps/web/src/index.css, apps/web/src/routes/_chat.settings.tsx
Adds chatMarkdownWordWrap and enableProviderUpdateChecks, wraps code/table markdown, and adds settings route toggles.

Provider Usage UI

Layer / File(s) Summary
Usage chip logic and components
apps/web/src/components/ProviderUsageStatusChip.logic.ts, apps/web/src/components/ProviderUsageStatusChip.logic.test.ts, apps/web/src/components/ProviderUsageStatusChip.tsx, apps/web/src/components/RuntimeUsageControls.tsx
Derives a usage status chip from rate limits/usage lines and combines it with runtime-mode toggle and context window meter.
BranchToolbar refactor
apps/web/src/components/BranchToolbar.tsx, apps/web/src/components/BranchToolbarEnvironmentPicker.tsx, apps/web/src/components/BranchToolbar.structure.test.ts
Extracts environment picker and reuses shared runtime controls.
VCS command center panel
apps/web/src/components/VcsCommandCenterStatusPanel.logic.ts, apps/web/src/components/VcsCommandCenterStatusPanel.tsx, apps/web/src/components/VcsCommandCenterStatusPanel.test.tsx, apps/web/src/components/GitActionsControl.tsx
Adds a read-only status panel for branch/worktree/sync/PR/provider state inside the git actions menu.
ChatView wiring
apps/web/src/components/ChatView.tsx, apps/web/src/components/ChatView.structure.test.ts
Derives activeProvider and usage summary, passing them into runtime controls and the header.

Composer Prompt Traits

Layer / File(s) Summary
Prompt traits derivation
apps/web/src/components/chat/composerTraits.ts, apps/web/src/components/chat/composerProviderRegistry.tsx, apps/web/src/components/chat/composerProviderRegistry.test.tsx, apps/web/src/components/ChatView.tsx
Replaces raw-prompt ultrathink checks with derived ComposerPromptTraits.

Chat Timeline Enhancements

Layer / File(s) Summary
Session log extraction
apps/web/src/session-logic.ts, apps/web/src/session-logic.test.ts
Extends WorkLogEntry with command result and patch fields.
Activity details & expandable rows
apps/web/src/components/chat/MessagesTimelineActivityDetails.logic.ts, apps/web/src/components/chat/MessagesTimelineActivityDetails.logic.test.ts, apps/web/src/components/chat/MessagesTimelineActivityDetails.tsx, apps/web/src/components/chat/MessagesTimeline.tsx, apps/web/src/components/chat/MessagesTimeline.test.tsx, apps/web/src/components/chat/MessagesTimeline.browser.tsx
Renders expandable command/file-change details within timeline rows.
Timeline minimap
apps/web/src/components/chat/MessagesTimelineMinimap.tsx, apps/web/src/components/chat/MessagesTimelineMinimap.test.tsx
Adds a jump navigation minimap for user turns and proposed plans.
Live-edge scroll helper
apps/web/src/chat-scroll.ts, apps/web/src/chat-scroll.test.ts, apps/web/src/components/ChatView.tsx
Adds resolveTimelineLiveEdge and tailFollowEnabled state for scroll-follow behavior.

Design Documentation

Layer / File(s) Summary
Cockpit design system doc
DESIGN.md
Documents tokens, typography, component patterns, accessibility, and motion rules for the cockpit UI.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

  • Jay1/jcode#14: Both PRs modify apps/web/src/components/ChatMarkdown.tsx and apps/web/src/index.css to extend markdown table wrapping behavior.
  • Jay1/jcode#66: Both PRs modify apps/server/src/provider/Layers/OpenCodeAdapter.ts and its tests around OpenCode session resumption behavior.

Suggested labels: vouch:trusted, size:XXL

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is missing the required Why, Validation checklist, UI Changes, and Reviewer Notes sections from the template. Rewrite the PR description to match the template headings and add the missing Why, Validation, UI Changes, and Reviewer Notes details.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the PR’s main theme of integrating roadmap slices.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.1)
apps/web/src/index.css

File contains syntax errors that prevent linting: Line 3: Tailwind-specific syntax is disabled.; Line 5: Tailwind-specific syntax is disabled.; Line 51: Tailwind-specific syntax is disabled.; Line 54: Tailwind-specific syntax is disabled.; Line 61: Tailwind-specific syntax is disabled.; Line 466: Tailwind-specific syntax is disabled.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Jul 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/web/src/routes/_chat.settings.tsx (1)

540-543: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate thread-linking logic diverges from classifyManagedWorktreeCleanupChoices.

normalizeManagedWorktreePath and the linkedThreadsFromSnapshot filter here re-implement the same worktree/thread path-matching logic already defined in worktreeCleanup.ts's normalizeWorktreePath and classifyManagedWorktreeCleanupChoices. Since this copy runs against a freshly-fetched snapshot rather than the memoized managedWorktreeChoices, the two implementations can silently diverge (e.g., a future change to the matching rule in one file won't be reflected in the other), producing inconsistent confirmation-dialog messaging vs. actual cleanup-safety classification.

♻️ Suggested consolidation
-function normalizeManagedWorktreePath(value: string | null | undefined): string | null {
-  const trimmed = value?.trim();
-  return trimmed && trimmed.length > 0 ? trimmed : null;
-}
+// Reuse the shared helper from worktreeCleanup.ts instead of duplicating it here.

Then export normalizeWorktreePath (and ideally a small linkThreadsToWorktreePath helper) from worktreeCleanup.ts and call it from both the memoized classification and the fresh-snapshot lookup in deleteManagedWorktree.

Also applies to: 1306-1312

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/routes/_chat.settings.tsx` around lines 540 - 543, The
thread-linking and worktree path matching logic in
`deleteManagedWorktree`/`linkedThreadsFromSnapshot` is duplicated from
`worktreeCleanup.ts`, which risks the two paths diverging over time. Move the
canonical matching into `worktreeCleanup.ts` by exporting
`normalizeWorktreePath` and, ideally, a shared `linkThreadsToWorktreePath`
helper, then have both `classifyManagedWorktreeCleanupChoices` and the
fresh-snapshot lookup in `deleteManagedWorktree` call that shared logic instead
of `normalizeManagedWorktreePath` and the local filter.
apps/web/src/components/BranchToolbar.tsx (1)

34-50: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Provider usage resolution diverges from ChatView, causing inconsistent chip state for draft threads.

activeProvider here only reads serverThread, so for any thread that hasn't started yet (serverThread is undefined), it resolves to null regardless of the composer's selected/default provider. usageSummary is then computed from that null provider, and both BranchToolbarEnvironmentPicker's rate-limit panel and the RuntimeUsageControls rendered here will show "no provider" data.

Meanwhile, ChatView.tsx computes its own activeProviderUsageSummary with a full fallback chain (session.provider ?? modelSelection.provider ?? selectedProvider) and spreads it into branchToolbarProps — but BranchToolbarProps doesn't declare provider/providerRateLimits/providerUsageLines/providerUsageIsLoading/providerUsageLearnMoreHref, so those computed values are silently dropped when passed to <BranchToolbar {...branchToolbarProps} />. Worse, ChatView renders the same RuntimeUsageControls widget directly (with its own, more complete provider) whenever isGitRepo is false, so the exact same UI element shows different provider/usage data depending on whether the project is a git repo.

🔧 Suggested fix: accept the already-computed provider/usage props instead of recomputing locally
 interface BranchToolbarProps {
   threadId: ThreadId;
   className?: string;
   onEnvModeChange: (mode: EnvMode) => void;
   envLocked: boolean;
   runtimeMode?: RuntimeMode;
   onRuntimeModeChange?: (mode: RuntimeMode) => void;
+  activeProvider?: ProviderKind | null;
   onHandoffToWorktree?: () => void;
   onHandoffToLocal?: () => void;
   handoffBusy?: boolean;
   onCheckoutPullRequestRequest?: (reference: string) => void;
   onComposerFocusRequest?: () => void;
   contextWindow?: ContextWindowSnapshot | null;
   cumulativeCostUsd?: number | null;
   activeContextWindowLabel?: string | null;
   pendingContextWindowLabel?: string | null;
 }
-  const activeProvider =
-    serverThread?.session?.provider ?? serverThread?.modelSelection.provider ?? null;
+  const activeProvider =
+    activeProviderProp ??
+    serverThread?.session?.provider ??
+    serverThread?.modelSelection.provider ??
+    null;

Then thread activeProvider (renamed prop) from ChatView's already-computed value instead of relying solely on serverThread.

Also applies to: 83-84, 194-198, 244-259

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/BranchToolbar.tsx` around lines 34 - 50,
BranchToolbar is recomputing provider usage from serverThread and ignores the
already-computed provider data coming from ChatView, which causes draft threads
to show inconsistent “no provider” state. Update BranchToolbarProps and
BranchToolbar to accept and use the provider/usage props that ChatView already
prepares (activeProvider, providerRateLimits, providerUsageLines,
providerUsageIsLoading, providerUsageLearnMoreHref) instead of deriving usage
locally, and thread the renamed activeProvider through the
BranchToolbar/RuntimeUsageControls/BranchToolbarEnvironmentPicker path so both
git and non-git flows render the same provider data.
apps/web/src/session-logic.ts (1)

49-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop the redundant DetailedWorkLogEntry alias

WorkLogEntry already includes output, stdout, stderr, exitCode, durationMs, and patch, so this local intersection can be simplified to WorkLogEntry unless the readonly modifiers are intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/session-logic.ts` around lines 49 - 72, Simplify the local
work-log type by removing the redundant DetailedWorkLogEntry intersection and
using WorkLogEntry directly in session-logic.ts. The relevant symbols are
WorkLogEntry and the alias currently defined around it; if any readonly intent
was meant for subagent-related fields, preserve that explicitly instead of
duplicating the same output/stdout/stderr/exitCode/durationMs/patch properties.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/server/src/git/Layers/GitCore.ts`:
- Around line 458-460: The `listManagedWorktrees` parsing in `GitCore` only
matches an exact `prunable` line, so it misses `git worktree list --porcelain`
entries that include a reason suffix. Update the `prunable` handling in the
worktree entry parser to recognize lines that start with `prunable` and still
set `currentPrunable`, preserving the reason text for any existing parse flow.
Keep the fix localized around the `listManagedWorktrees` logic and the
`prunable` branch in `GitCore`.

In `@apps/server/src/provider/Layers/OpenCodeAdapter.test.ts`:
- Around line 1484-1531: The OpenCodeAdapter resume test only checks session
reuse, so it can still pass even if the resumed event changes or session.update
drops permission data. Strengthen the existing test in OpenCodeAdapter.test by
asserting the resumed turn emits the expected resumed event and that the
sessionUpdate payload includes the runtime-mode permissions for the resumed
session. Use the existing session and turn assertions as anchors, and add
focused expectations around the mocked runtime event and sessionUpdate call
details.

In `@apps/server/src/provider/Layers/ProviderHealth.ts`:
- Around line 2044-2055: `enrichStatuses` is refetching settings instead of
using the snapshot already loaded by `loadProviderStatuses`, which can cause
inconsistent enrichment behavior. Update `enrichStatuses` to accept the upstream
`settings` (or at least `enableProviderUpdateChecks`) as an argument and pass it
through from the `loadProviderStatuses` call site. Keep the existing
`Effect.forEach`/`enrichProviderStatusWithVersionAdvisory` flow unchanged, but
remove the internal `serverSettings.getSettings` read so the same settings
snapshot is reused end to end.

In `@apps/web/src/components/chat/MessagesTimeline.tsx`:
- Around line 1895-1896: The expand/collapse state in SimpleWorkEntryRow is
shared across every changed-file button, so multi-file entries toggle together
and hide the per-file diff-open behavior. Update the per-file rendering in
MessagesTimeline to scope expansion by changed file path (or move the patch
toggle out of the file buttons), and make the
FileChangeActivityDetails/ActivityEntryDetails flow target the clicked file
instead of one shared expanded boolean. Preserve the existing
onOpenTurnDiff(turnId, changedFilePath) behavior for entries with patch data so
a file click can still open that specific diff when applicable.

In `@apps/web/src/components/chat/MessagesTimelineActivityDetails.tsx`:
- Around line 40-46: The command-activity check is duplicated in
isCommandActivity and can drift from the existing isCommandEntry logic in
session-logic.ts. Move or export the shared predicate from toDerivedWorkLogEntry
in session-logic.ts, then import and reuse it in MessagesTimelineActivityDetails
so both places rely on the same command-classification rule.
- Around line 175-184: CommandOutputBlock truncates long command output without
telling the user, so add a visible truncation note when visibleLines is shorter
than lines. Update CommandOutputBlock in MessagesTimelineActivityDetails to
render a small “showing last 40 lines” style indicator alongside the
ActivityDetailBlock content whenever COMMAND_OUTPUT_TAIL_LINES is applied, using
the existing props.title, getRenderableCommandOutputLines, and visibleLines
logic.
- Around line 1-204: Extract the pure predicate/formatting helpers from
MessagesTimelineActivityDetails into a separate
MessagesTimelineActivityDetails.logic.ts module and add matching tests in a
.logic.test.ts file. Move functions like hasExpandableActivityDetails,
hasCommandActivityDetails, isCommandActivity, hasFileChangeActivityDetails,
isFileChangeActivity, formatWorkspaceRelativePath,
getRenderableCommandOutputLines, and related output-detection helpers so the
React component only renders JSX and imports the logic. Keep the component names
ActivityEntryDetails, CommandActivityDetails, and FileChangeActivityDetails as
the UI entry points, and cover the extracted logic with unit tests.

In `@apps/web/src/components/chat/MessagesTimelineMinimap.tsx`:
- Around line 65-76: Memoize the timeline item derivation in TimelineMinimap so
it does not rescan rows on every parent re-render. Update
MessagesTimelineMinimap.tsx by using useMemo around
deriveTimelineMinimapItems(rows), and consider wrapping TimelineMinimap with
memo if it still re-renders with unchanged props. Keep the existing
TIMELINE_MINIMAP_MIN_ITEMS guard and onJump props behavior unchanged.

In `@apps/web/src/components/ChatView.structure.test.ts`:
- Around line 6-11: The structural test for runtimeUsageControlsProps is too
dependent on the exact ChatView.tsx text layout and adjacent branchToolbarProps
ordering, so it can fail on harmless reformatting. Update
ChatView.structure.test.ts to avoid the brittle regex in
extractRuntimeUsageControlsPropsSource() and instead verify the wiring through a
render-based assertion or AST-based inspection using a stable symbol from
ChatView.tsx such as runtimeUsageControlsProps (and, if needed, the ChatView
component) so the test remains valid across formatting changes.

In `@apps/web/src/index.css`:
- Around line 823-828: Remove the deprecated word-break: break-word declaration
from the wrapped chat markdown code block styles in index.css, since
overflow-wrap: anywhere already covers the wrapping behavior. Update both
affected rule blocks that target .chat-markdown
.chat-markdown-codeblock--wrapped pre and .chat-markdown
.chat-markdown-codeblock--wrapped pre code so only the non-deprecated
white-space and overflow-wrap declarations remain.
- Around line 1143-1156: The wrap toggle button in the chat table scroll
container is positioned in a way that lets it move out of view during horizontal
scrolling. Update the `.chat-markdown .chat-markdown-table-wrap-button` behavior
so it stays visible inside `.chat-markdown-table-scroll` when the table
overflows horizontally, using the existing table scroll/wrap styles as the
anchor for the fix. Keep the hover/focus visibility logic for
`.chat-markdown-table-scroll:hover .chat-markdown-table-wrap-button` and
`.chat-markdown-table-scroll:focus-within .chat-markdown-table-wrap-button`
intact while changing the positioning approach so the button remains accessible
in the unwrapped state.

In `@apps/web/src/session-logic.ts`:
- Around line 737-739: The command classification logic is duplicated between
session-logic extraction and MessagesTimelineActivityDetails.tsx rendering,
which risks future drift. Extract the predicate from session-logic.ts into a
shared helper such as isCommandWorkLogEntry and use it both in the command-entry
handling path around extractCommandResult/extractChangedFiles/extractToolPatch
and in the existing isCommandActivity logic. Keep the helper colocated with the
current classification symbols so both modules import the same source of truth.

In `@docs/adr/README.md`:
- Line 28: Update the metadata table in the README to keep it current after
adding the new ADR row. Specifically, adjust the “Last reviewed” value in the
document’s metadata table so it reflects this change, and make sure the entry in
the ADR index that includes 0009 is accompanied by the updated review date. Use
the README’s metadata table and ADR list entry as the reference points when
making the change.

In `@packages/contracts/src/backend.ts`:
- Around line 30-37: The Backend schema currently allows invalid combinations
because Backend.kind and Backend.connection.kind are modeled as separate
independent fields. Update Backend in backend.ts so kind is derived from
connection (or refactor the schema into a single discriminated union) and make
sure the Backend and BackendConnection definitions enforce only valid local/wsl
combinations. Keep the fix centered on the Backend and BackendConnection symbols
so the type no longer represents domain-invalid states.

---

Outside diff comments:
In `@apps/web/src/components/BranchToolbar.tsx`:
- Around line 34-50: BranchToolbar is recomputing provider usage from
serverThread and ignores the already-computed provider data coming from
ChatView, which causes draft threads to show inconsistent “no provider” state.
Update BranchToolbarProps and BranchToolbar to accept and use the provider/usage
props that ChatView already prepares (activeProvider, providerRateLimits,
providerUsageLines, providerUsageIsLoading, providerUsageLearnMoreHref) instead
of deriving usage locally, and thread the renamed activeProvider through the
BranchToolbar/RuntimeUsageControls/BranchToolbarEnvironmentPicker path so both
git and non-git flows render the same provider data.

In `@apps/web/src/routes/_chat.settings.tsx`:
- Around line 540-543: The thread-linking and worktree path matching logic in
`deleteManagedWorktree`/`linkedThreadsFromSnapshot` is duplicated from
`worktreeCleanup.ts`, which risks the two paths diverging over time. Move the
canonical matching into `worktreeCleanup.ts` by exporting
`normalizeWorktreePath` and, ideally, a shared `linkThreadsToWorktreePath`
helper, then have both `classifyManagedWorktreeCleanupChoices` and the
fresh-snapshot lookup in `deleteManagedWorktree` call that shared logic instead
of `normalizeManagedWorktreePath` and the local filter.

In `@apps/web/src/session-logic.ts`:
- Around line 49-72: Simplify the local work-log type by removing the redundant
DetailedWorkLogEntry intersection and using WorkLogEntry directly in
session-logic.ts. The relevant symbols are WorkLogEntry and the alias currently
defined around it; if any readonly intent was meant for subagent-related fields,
preserve that explicitly instead of duplicating the same
output/stdout/stderr/exitCode/durationMs/patch properties.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 004b6917-a9b6-4219-b98b-97bd28619f3c

📥 Commits

Reviewing files that changed from the base of the PR and between b7930a3 and 0d61a2d.

📒 Files selected for processing (59)
  • DESIGN.md
  • apps/server/src/backend/backendPathResolver.ts
  • apps/server/src/backend/backendRegistry.test.ts
  • apps/server/src/backend/backendRegistry.ts
  • apps/server/src/git/Layers/GitCore.test.ts
  • apps/server/src/git/Layers/GitCore.ts
  • apps/server/src/git/Services/GitCore.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.ts
  • apps/server/src/provider/Layers/ProviderHealth.ts
  • apps/server/src/provider/copilotProviderSpike.test.ts
  • apps/server/src/provider/copilotProviderSpike.ts
  • apps/server/src/provider/providerMaintenance.test.ts
  • apps/server/src/provider/providerMaintenance.ts
  • apps/server/src/serverSettings.test.ts
  • apps/server/src/wsRpc.ts
  • apps/web/src/appSettings.test.ts
  • apps/web/src/appSettings.ts
  • apps/web/src/chat-scroll.test.ts
  • apps/web/src/chat-scroll.ts
  • apps/web/src/components/BranchToolbar.structure.test.ts
  • apps/web/src/components/BranchToolbar.tsx
  • apps/web/src/components/BranchToolbarEnvironmentPicker.tsx
  • apps/web/src/components/ChatMarkdown.test.tsx
  • apps/web/src/components/ChatMarkdown.tsx
  • apps/web/src/components/ChatView.structure.test.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/GitActionsControl.tsx
  • apps/web/src/components/ProviderUsageStatusChip.logic.test.ts
  • apps/web/src/components/ProviderUsageStatusChip.logic.ts
  • apps/web/src/components/ProviderUsageStatusChip.tsx
  • apps/web/src/components/RuntimeUsageControls.tsx
  • apps/web/src/components/VcsCommandCenterStatusPanel.logic.ts
  • apps/web/src/components/VcsCommandCenterStatusPanel.test.tsx
  • apps/web/src/components/VcsCommandCenterStatusPanel.tsx
  • apps/web/src/components/chat/MessagesTimeline.browser.tsx
  • apps/web/src/components/chat/MessagesTimeline.test.tsx
  • apps/web/src/components/chat/MessagesTimeline.tsx
  • apps/web/src/components/chat/MessagesTimelineActivityDetails.tsx
  • apps/web/src/components/chat/MessagesTimelineMinimap.test.tsx
  • apps/web/src/components/chat/MessagesTimelineMinimap.tsx
  • apps/web/src/components/chat/composerProviderRegistry.test.tsx
  • apps/web/src/components/chat/composerProviderRegistry.tsx
  • apps/web/src/components/chat/composerTraits.ts
  • apps/web/src/index.css
  • apps/web/src/routes/_chat.settings.tsx
  • apps/web/src/session-logic.test.ts
  • apps/web/src/session-logic.ts
  • apps/web/src/worktreeCleanup.test.ts
  • apps/web/src/worktreeCleanup.ts
  • docs/adr/0009-copilot-provider-entry-path.md
  • docs/adr/README.md
  • packages/contracts/src/backend.test.ts
  • packages/contracts/src/backend.ts
  • packages/contracts/src/index.ts
  • packages/contracts/src/server.test.ts
  • packages/contracts/src/server.ts
  • packages/contracts/src/settings.test.ts
  • packages/contracts/src/settings.ts

Comment thread apps/server/src/git/Layers/GitCore.ts Outdated
Comment thread apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
Comment thread apps/server/src/provider/Layers/ProviderHealth.ts
Comment thread apps/web/src/components/chat/MessagesTimeline.tsx
Comment thread apps/web/src/components/chat/MessagesTimelineActivityDetails.tsx Outdated
Comment thread apps/web/src/index.css
Comment thread apps/web/src/index.css
Comment thread apps/web/src/session-logic.ts
Comment thread docs/adr/README.md
Comment thread packages/contracts/src/backend.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/server/src/provider/Layers/ProviderHealth.ts (1)

2044-2055: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Thread the same settings snapshot into capability resolution too.

This still mixes snapshots: loadProviderStatuses probes providers with the outer settings, but getProviderMaintenanceCapabilities(status.provider) re-reads settings during enrichment. A concurrent settings edit can therefore return provider statuses from one snapshot and version-advisory/update capability data from another in the same refresh. Pass the already-loaded settings through this path as well.

Proposed fix
-    const getProviderMaintenanceCapabilities = Effect.fn("getProviderMaintenanceCapabilities")(
-      function* (provider: ProviderKind) {
-        const settings = yield* serverSettings.getSettings;
+    const getProviderMaintenanceCapabilities = Effect.fn("getProviderMaintenanceCapabilities")(
+      function* (provider: ProviderKind, settings?: ServerSettings) {
+        const resolvedSettings = settings ?? (yield* serverSettings.getSettings);
         if (provider === "cursor") {
           return makeProviderMaintenanceCapabilities({
             provider,
             packageName: null,
-            updateExecutable: getProviderBinaryPath(provider, settings) || "agent",
+            updateExecutable: getProviderBinaryPath(provider, resolvedSettings) || "agent",
             updateArgs: ["update"],
             updateLockKey: "cursor-agent",
           });
         }
-        if (provider === "opencode" && isExternalOpenCodeRuntimeActive(settings)) {
+        if (provider === "opencode" && isExternalOpenCodeRuntimeActive(resolvedSettings)) {
           return makeProviderMaintenanceCapabilities({
             provider,
             packageName: null,
@@
-        const binaryPath = getProviderBinaryPath(provider, settings) || null;
+        const binaryPath = getProviderBinaryPath(provider, resolvedSettings) || null;
         return yield* resolveProviderMaintenanceCapabilitiesEffect(definition, {
           binaryPath,
           env: process.env,
           platform: process.platform,
         }).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem));
       },
     );
@@
-          getProviderMaintenanceCapabilities(status.provider).pipe(
+          getProviderMaintenanceCapabilities(status.provider, settings).pipe(

Also applies to: 2079-2109

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/provider/Layers/ProviderHealth.ts` around lines 2044 - 2055,
The enrichment path in enrichProviderStatuses is re-reading settings during
capability lookup, which can mix snapshots from different refreshes. Update
getProviderMaintenanceCapabilities usage in enrichProviderStatuses (and the
related flow covered by
loadProviderStatuses/enrichProviderStatusWithVersionAdvisory) to accept the
already-loaded settings snapshot, so provider status enrichment and capability
resolution both use the same ServerSettings instance. Ensure the settings object
passed into enrichProviderStatuses is threaded through the capability resolution
call instead of fetching settings again.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/session-logic.ts`:
- Around line 81-85: The command work-log detection in isCommandWorkLogEntry is
using nullish coalescing, which skips a valid raw command when command is an
empty string. Update the boolean check to fall back with logical OR on
workEntry.command and workEntry.rawCommand so empty command values still count
as command entries. This should preserve the derived entry handling in
toDerivedWorkLogEntry so command rows keep output, stdout, stderr, exitCode, and
durationMs.

---

Duplicate comments:
In `@apps/server/src/provider/Layers/ProviderHealth.ts`:
- Around line 2044-2055: The enrichment path in enrichProviderStatuses is
re-reading settings during capability lookup, which can mix snapshots from
different refreshes. Update getProviderMaintenanceCapabilities usage in
enrichProviderStatuses (and the related flow covered by
loadProviderStatuses/enrichProviderStatusWithVersionAdvisory) to accept the
already-loaded settings snapshot, so provider status enrichment and capability
resolution both use the same ServerSettings instance. Ensure the settings object
passed into enrichProviderStatuses is threaded through the capability resolution
call instead of fetching settings again.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dc619534-6ef7-4016-b9d8-739c7f8026fd

📥 Commits

Reviewing files that changed from the base of the PR and between 0d61a2d and 57eec4a.

📒 Files selected for processing (18)
  • apps/server/src/backend/backendPathResolver.ts
  • apps/server/src/backend/backendRegistry.test.ts
  • apps/server/src/git/Layers/GitCore.test.ts
  • apps/server/src/git/Layers/GitCore.ts
  • apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
  • apps/server/src/provider/Layers/ProviderHealth.ts
  • apps/web/src/components/ChatView.structure.test.ts
  • apps/web/src/components/chat/MessagesTimeline.test.tsx
  • apps/web/src/components/chat/MessagesTimeline.tsx
  • apps/web/src/components/chat/MessagesTimelineActivityDetails.logic.test.ts
  • apps/web/src/components/chat/MessagesTimelineActivityDetails.logic.ts
  • apps/web/src/components/chat/MessagesTimelineActivityDetails.tsx
  • apps/web/src/components/chat/MessagesTimelineMinimap.tsx
  • apps/web/src/index.css
  • apps/web/src/session-logic.ts
  • docs/adr/README.md
  • packages/contracts/src/backend.test.ts
  • packages/contracts/src/backend.ts

Comment on lines +81 to +85
export function isCommandWorkLogEntry(workEntry: CommandWorkLogEntryInput): boolean {
return (
workEntry.itemType === "command_execution" ||
workEntry.requestKind === "command" ||
Boolean(workEntry.command ?? workEntry.rawCommand)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use || here instead of ??.

Boolean(workEntry.command ?? workEntry.rawCommand) misclassifies entries when command === "" but rawCommand is present. In that case toDerivedWorkLogEntry skips output, stdout, stderr, exitCode, and durationMs, so command rows lose their activity details. Fall back on any non-empty raw command here.

Proposed fix
 export function isCommandWorkLogEntry(workEntry: CommandWorkLogEntryInput): boolean {
   return (
     workEntry.itemType === "command_execution" ||
     workEntry.requestKind === "command" ||
-    Boolean(workEntry.command ?? workEntry.rawCommand)
+    Boolean(workEntry.command || workEntry.rawCommand)
   );
 }
📝 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.

Suggested change
export function isCommandWorkLogEntry(workEntry: CommandWorkLogEntryInput): boolean {
return (
workEntry.itemType === "command_execution" ||
workEntry.requestKind === "command" ||
Boolean(workEntry.command ?? workEntry.rawCommand)
export function isCommandWorkLogEntry(workEntry: CommandWorkLogEntryInput): boolean {
return (
workEntry.itemType === "command_execution" ||
workEntry.requestKind === "command" ||
Boolean(workEntry.command || workEntry.rawCommand)
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/session-logic.ts` around lines 81 - 85, The command work-log
detection in isCommandWorkLogEntry is using nullish coalescing, which skips a
valid raw command when command is an empty string. Update the boolean check to
fall back with logical OR on workEntry.command and workEntry.rawCommand so empty
command values still count as command entries. This should preserve the derived
entry handling in toDerivedWorkLogEntry so command rows keep output, stdout,
stderr, exitCode, and durationMs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant