Conversation
Typing in the composer caused the entire ChatView (~4900 lines) to re-render on every keystroke because `useComposerThreadDraft` subscribed to the full draft including `prompt`. This cascaded to MessagesTimeline, ChatHeader, BranchToolbar, PlanSidebar, and terminal drawers. - Extract `ChatComposer` (memo'd) that owns the prompt/images/terminal context store subscriptions, all composer-local state, and derived values (menu items, provider state, send state, debounced queries). ChatView now uses granular store selectors for only `runtimeMode`, `interactionMode`, and `activeProvider` — none of which change on keystroke. - Extract `ExpandedImageDialog` (memo'd) for the image preview modal. - Wrap `BranchToolbar` in `memo` and stabilize `onRevertUserMessage` with `useCallback` so already-memo'd children (MessagesTimeline, ChatHeader) properly skip re-renders. - Expose `ChatComposerHandle` for cross-cutting operations (focus, cursor reset, terminal context insertion, send context). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…-extraction # Conflicts: # apps/web/src/components/ChatView.tsx
- Use the resolved composer draft target when clearing and restoring state - Keep draft images and terminal contexts intact across sends - Make Enter send while Shift+Enter inserts a newline
- Split footer composer controls into memoized subcomponents - Separate reusable new-thread logic from route-aware hook - Add composer draft model selector and thread-jump hint cleanup
- Pass prompt context into cursor resets - Recompute trigger after send/retry mutations
- Use the composer's send context as the source of truth - Remove fallback to stale selected provider state
- Migrate service tags and context access to Effect `Context` - Fix boolean flag precedence so explicit `false` overrides env/bootstrap - Update related caches, tests, and CLI negation handling
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Nested Effect in mock
unrefproperty value- Replaced
Effect.succeed(Effect.void)withEffect.voidto avoid creating a nestedEffect<Effect<void>>in the mock.
- Replaced
Or push these changes by commenting:
@cursor push 1e8c525414
Preview (1e8c525414)
diff --git a/apps/server/src/provider/Layers/ProviderRegistry.test.ts b/apps/server/src/provider/Layers/ProviderRegistry.test.ts
--- a/apps/server/src/provider/Layers/ProviderRegistry.test.ts
+++ b/apps/server/src/provider/Layers/ProviderRegistry.test.ts
@@ -44,7 +44,7 @@
exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(result.code)),
isRunning: Effect.succeed(false),
kill: () => Effect.void,
- unref: Effect.succeed(Effect.void),
+ unref: Effect.void,
stdin: Sink.drain,
stdout: Stream.make(encoder.encode(result.stdout)),
stderr: Stream.make(encoder.encode(result.stderr)),You can send follow-ups to the cloud agent here.
Co-authored-by: codex <[email protected]>
ApprovabilityVerdict: Needs human review Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. No code changes detected at You can customize Macroscope's approvability policy. Learn more. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Redundant lockedProvider computation creates subtle guard mismatch
- Removed the redundant deriveLockedProvider re-derivation and effectiveLockedProvider merge in ChatComposer, so it now uses the parent's lockedProvider prop directly as the single source of truth, eliminating the potential divergence with the parent's callback guard.
Or push these changes by commenting:
@cursor push 0db96d6396
Preview (0db96d6396)
diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx
--- a/apps/web/src/components/chat/ChatComposer.tsx
+++ b/apps/web/src/components/chat/ChatComposer.tsx
@@ -39,12 +39,8 @@
expandCollapsedComposerCursor,
replaceTextRange,
} from "../../composer-logic";
+import { deriveComposerSendState, readFileAsDataUrl } from "../ChatView.logic";
import {
- deriveComposerSendState,
- deriveLockedProvider,
- readFileAsDataUrl,
-} from "../ChatView.logic";
-import {
type ComposerImageAttachment,
type DraftId,
type PersistedComposerImageAttachment,
@@ -546,18 +542,11 @@
const selectedProviderByThreadId = composerDraft.activeProvider ?? null;
const threadProvider =
activeThreadModelSelection?.provider ?? activeProjectDefaultModelSelection?.provider ?? null;
- const computedLockedProvider = deriveLockedProvider({
- thread: activeThread,
- selectedProvider: selectedProviderByThreadId,
- threadProvider,
- });
- const effectiveLockedProvider = lockedProvider ?? computedLockedProvider;
-
const unlockedSelectedProvider = resolveSelectableProvider(
providerStatuses,
selectedProviderByThreadId ?? threadProvider ?? "codex",
);
- const selectedProvider: ProviderKind = effectiveLockedProvider ?? unlockedSelectedProvider;
+ const selectedProvider: ProviderKind = lockedProvider ?? unlockedSelectedProvider;
const { modelOptions: composerModelOptions, selectedModel } = useEffectiveComposerModelState({
threadRef: composerDraftTarget,
@@ -612,7 +601,7 @@
const searchableModelOptions = useMemo(
() =>
AVAILABLE_PROVIDER_OPTIONS.filter(
- (option) => effectiveLockedProvider === null || option.value === effectiveLockedProvider,
+ (option) => lockedProvider === null || option.value === lockedProvider,
).flatMap((option) =>
modelOptionsByProvider[option.value].map(({ slug, name }) => ({
provider: option.value,
@@ -624,7 +613,7 @@
searchProvider: option.label.toLowerCase(),
})),
),
- [effectiveLockedProvider, modelOptionsByProvider],
+ [lockedProvider, modelOptionsByProvider],
);
// ------------------------------------------------------------------
@@ -1825,7 +1814,7 @@
compact={isComposerFooterCompact}
provider={selectedProvider}
model={selectedModelForPickerWithCustomFallback}
- lockedProvider={effectiveLockedProvider}
+ lockedProvider={lockedProvider}
providers={providerStatuses}
modelOptionsByProvider={modelOptionsByProvider}
{...(composerProviderState.modelPickerIconClassNameYou can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 333b878. Configure here.
- Replace `makeUnsafe` calls with validated `make` constructors - Update affected integration and unit tests to match the safer ID flow
Dismissing prior approval to re-evaluate ca0d44f
There was a problem hiding this comment.
🟢 Low
The runtime variable declared at line 238 shadows the module-level runtime declared at line 69, so the cleanup in afterEach (lines 82-85) disposes the wrong (uninitialized) variable while the actual ManagedRuntime created by each test is leaked. This causes SQLite connections and other resources to accumulate across tests.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts around line 238:
The `runtime` variable declared at line 238 shadows the module-level `runtime` declared at line 69, so the cleanup in `afterEach` (lines 82-85) disposes the wrong (uninitialized) variable while the actual `ManagedRuntime` created by each test is leaked. This causes SQLite connections and other resources to accumulate across tests.
Evidence trail:
- apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts lines 69-72 (REVIEWED_COMMIT): module-level `let runtime = null`
- apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts lines 82-85 (REVIEWED_COMMIT): `afterEach` disposes `runtime` which is always null
- apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts line 238 (REVIEWED_COMMIT): `const runtime = ManagedRuntime.make(layer)` - local declaration shadows module-level
- apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts lines 273-288 (REVIEWED_COMMIT): `createHarness` returns object without runtime, runtime is never assigned to module-level variable
- git_grep results: only assignments are line 85 (`runtime = null`) and line 238 (`const runtime = ...`), confirming no assignment to module-level runtime
- Switch the test fixture to `EnvironmentId.make` - Keep replay recovery coverage aligned with validated IDs
Co-authored-by: codex <[email protected]>
- Read bootstrap envelope once and reuse it - Preserve headless startup overrides while simplifying precedence checks - Use bootstrap values directly for observability settings


Summary
ServiceMap.ServicetoContext.Serviceand updates runtime plumbing to use EffectContextAPIs.falseflags override env and bootstrap defaults, and adds coverage for canonical--no-<flag>negation.Testing
bun fmtbun lintbun typecheckbun run testNote
Medium Risk
Broad Effect beta migration touches many core server service tags, cache construction, and branded ID creation, so regressions are possible despite being mostly mechanical. CLI config precedence semantics change (explicit
falseflags now win), which may alter startup behavior in some environments.Overview
Bumps usage to newer Effect beta APIs by migrating many server service tags from
ServiceMap.ServicetoContext.Service, updating call sites to useContext-based runtime plumbing, and adjustingCache.makeWith/Effect.tryPromiseusage to match new signatures.Fixes
resolveServerConfigboolean option precedence so explicit CLIfalsevalues (and canonical--no-<flag>negations) are preserved over env/bootstrap defaults, and adds/updates CLI tests to cover this behavior.Replaces widespread branded-id constructors from
*.makeUnsafe(...)to*.make(...)across server/desktop code and tests (e.g.,EventId,ThreadId,TurnId,CheckpointRef,AuthSessionId).Reviewed by Cursor Bugbot for commit b6e4505. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Bump Effect ecosystem dependencies to beta.45 and migrate service classes to
Context.Serviceeffectand all@effect/*packages from4.0.0-beta.43to4.0.0-beta.45in package.json.ServiceMap.ServicetoContext.Serviceacross the server, matching the new Effect API.Effect.services()withEffect.context<never>()and updatesLayer.effectServices/ServiceMap.maketoLayer.effectContext/Context.makeat all affected call sites.Cache.makeWithcall signatures to pass the lookup function as the first argument and remaining options as a second object.Schema.withDecodingDefaultandSchema.withConstructorDefaultthunks (e.g.() => false) toEffect.succeed(false)across contracts schemas.*.makeUnsafe(...)branded ID constructors with*.make(...)throughout production code and tests.resolveBooleanFlagin scripts/build-desktop-artifact.ts andresolveServerConfigin apps/server/src/cli.ts so explicitfalseCLI flags are no longer overridden by env or bootstrap values.UserInputQuestionschema constructor default for its boolean field now resolves tofalseinstead ofOption.some(false).Macroscope summarized b6e4505.