Skip to content

🤖 refactor: auto-cleanup - #3695

Open
mux-bot[bot] wants to merge 53 commits into
mainfrom
auto-cleanup
Open

🤖 refactor: auto-cleanup#3695
mux-bot[bot] wants to merge 53 commits into
mainfrom
auto-cleanup

Conversation

@mux-bot

@mux-bot mux-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

This is the long-lived auto-cleanup PR. Each run, the auto-cleanup agent reviews new commits merged to main, rebases onto the latest main, and applies at most one extremely low-risk, behavior-preserving cleanup. The branch accumulates a small stack of independent cleanups until it is merged.

Cleanups in this branch

Cleanups 1–53 (older runs)
  1. Dedupe memory sweep recordUsage callbacks (MemoryConsolidationService). The consolidation sweep and the harvest sweep each inlined the same 15-line callback that routes billed usage to the headless-usage sidecar and emits analyticsIngest. Extracted into a private makeSweepUsageRecorder(...) helper.

  2. Dedupe the "memory scope is full" cap check (MemoryService). The create and saveFile (new-file) paths each inlined a byte-identical block that called store.listFiles(), compared the count against MEMORY_MAX_FILES_PER_SCOPE, and threw a MemoryCommandError with the same message. Extracted into a private assertScopeHasRoom(store, scope) helper.

  3. Dedupe blockquote line formatting in the bash monitor wake prompt (bashMonitorWakeStore.ts). buildBashMonitorWakePrompt rendered both the matched-output lines and the lost-monitor script with the identical .map((line) => \> ${line}`).join("\n")blockquote pattern in two places. Extracted into a module-levelblockquoteLines(lines)` helper.

  4. Dedupe the tool_search removal in prepareToolSearch (toolCatalog.ts). Both fallback branches (PTC enabled, and empty deferred catalog) inlined the identical { [TOOL_SEARCH_TOOL_NAME]: _removed, ...rest } destructure to drop the built-in tool_search entry from the tool record. Extracted into a module-level withoutToolSearch(tools) helper.

  5. Dedupe the Anthropic cache-create token extraction in accumulateProviderMetadata (usageHelpers.ts). The function inlined the same verbose (metadata.anthropic as { cacheCreationInputTokens?: number } | undefined)?.cacheCreationInputTokens ?? 0 cast twice (once for the accumulated metadata, once for the current step). Extracted into a module-private getAnthropicCacheCreateTokens(metadata) helper.

  6. Dedupe capability-model thinking-policy resolution (thinking/policy.ts). After 🤖 feat: integrate GPT-5.6 Sol/Terra/Luna with native max effort and pro-mode toggle #3708 taught the thinking policy to resolve mappedToModel aliases, both getThinkingPolicyForModel and hasExplicitThinkingPolicy inlined the identical getExplicitThinkingPolicy(resolveModelForMetadata(modelString, providersConfig ?? null)) call. Extracted into a private getExplicitThinkingPolicyForModel(modelString, providersConfig) helper.

  7. Dedupe queue entry clear-callback projection (messageQueue.ts). After 🤖 feat: queue messages behind special sends instead of erroring (FIFO message queue) #3696 rewrote MessageQueue into FIFO QueueEntry items, both getClearCallbacks and removeWorkspaceTurn inlined the identical spread that builds a QueueClearCallbacks object from an entry's optional onCanceled / onAcceptedPreStreamFailure fields. Extracted into a private entryClearCallbacks(entry) helper.

  8. Dedupe the OpenAI-origin model check in openaiExplicitPromptCachingAvailable (cacheStrategy.ts). After 🤖 feat: GPT-5.6 explicit prompt cache breakpoints for direct OpenAI #3712 added the GPT-5.6 explicit-prompt-caching eligibility gate, the function inlined the identical split(":", 2) + origin !== "openai" || !modelName check twice — once for the request model and once for the resolved capability target — and the destructured origin/modelName locals were unused past their guard in both places. Extracted into a module-private isOpenAIOriginModel(canonical) helper.

  9. Dedupe the tool-call-execution-start emit in StreamManager (streamManager.ts). 🤖 fix: start tool elapsed timers when execute() actually runs #3716 introduced the ToolCallExecutionStartEvent, emitted from two places: applyToolExecutionStart (part already stored) and the "tool-call" case that consumes a pendingExecutionStart recorded before the part landed. Both inlined the byte-identical this.emit("tool-call-execution-start", { type, workspaceId, messageId, toolCallId, timestamp } satisfies ToolCallExecutionStartEvent) block, differing only in the toolCallId/timestamp source. Extracted into a private emitToolCallExecutionStart(workspaceId, streamInfo, toolCallId, timestamp) helper.

  10. Dedupe model-parameter extras merge (aiService.ts). After 🤖 feat: apply mid-turn thinking-level changes at the next model step #3718 added mid-turn thinking-level rebuilds, the initial-model path and the fallback-model path each inlined a byte-identical closure (mergeModelParameterExtras / mergeNextModelParameterExtras) that folds providers.jsonc providerExtras UNDER the Mux-built provider-options namespace (short-circuiting when there are no extras, deep-merging via mergeProviderExtrasUnderMux when the namespace is a plain object). The two differed only in the namespace key and the overrides source. Extracted into a module-level makeModelParameterExtrasMerger(namespaceKey, providerExtras) factory that returns the merger closure.

  11. Unify the legacy tool_search part-rename helper (toolCatalog.ts). 🤖 fix: avoid OpenAI tool search name collision #3719 renamed the built-in tool-search tool to tool_catalog_search and added request-time rewriting of historical tool_search call/result parts. It introduced two byte-identical helpers — renameLegacyToolSearchCallPart(part: ToolCallPart) and renameLegacyToolSearchResultPart(part: ToolResultPart) — that differ only in the part type; the rename body is identical. Collapsed both into a single generic renameLegacyToolSearchPart<T extends { toolName: string }>(part: T) and dropped the now-unused ToolCallPart / ToolResultPart imports.

  12. Trim duplicated context-cap rationale comment (codexOAuth.ts). 🤖 fix: cap GPT-5.6 context over Codex OAuth #3724 added the GPT-5.6 family to CODEX_OAUTH_CONTEXT_WINDOW_OVERRIDES and rewrote the map's inline comment with a sentence that restated the rationale already given in the map's doc comment directly above it. Dropped the duplicated rationale sentence, keeping only the tier-specific explanation. Comment-only; no behavior change. (Re-applied on top of [openai] 🤖 fix: use 372K GPT-5.6 OAuth context #3730, which later rewrote the same inline comment and re-introduced the duplicate.)

  13. Dedupe flat-section pinned block resolution (pinnedReorder.ts). 🤖 feat: project-less scratch chats #3723 added a scratch branch to locatePinnedBlock that renders scratch chats as one flat "Chats" section, mirroring the existing multi-project branch. Both branches inlined the byte-identical collectFlatSectionRows(...).filter(isWorkspacePinned).map((row) => row.id) projection, the if (!pinnedIds.includes(meta.id)) return null guard, and the return { fullOrder: pinnedIds, blockIds: pinnedIds } shape — differing only in the includeRow predicate ((row) => row.kind === "scratch" vs isMultiProject). Extracted into a private locateFlatSectionPinnedBlock(meta, sortedWorkspacesByProject, includeRow) helper.

  14. Dedupe JSON-wrapped tool-output unwrap (workflowRunMessages.ts). 🤖 fix: stop terminal workflow await loops #3725 added isTerminalWorkflowRunToolOutput, which re-inlined the byte-identical output.type === "json" && "value" in output container check already used by stripWorkflowRunRecordForModel to detect the { type: "json", value } SDK/UI wrapper before recursing on the inner value. Extracted the check into a module-private isJsonWrappedOutput(output) helper that both functions call, moving the shared rationale into the helper's doc comment. No control flow or return-shape change.

  15. Hoist errorType local in finalizeWorkspaceTurnFromStreamError (taskService.ts). 🤖 fix: keep workspace-turn handles running through auto-retryable stream errors #3729 reworked workspace-turn stream-error settlement, and the reworked function read event.errorType three times and repeated the event.errorType != null guard once for the explicitRecovery computation and once in the recovery if. Hoisted a single const errorType = event.errorType and routed all uses through it, deduplicating the repeated member access and null guard. ErrorEvent is a Zod-inferred plain data type, so the property read has no side effects; pure behavior-preserving simplification with no control-flow change.

  16. Extract buildSkillDescriptor helper for skill discovery (common/orpc/schemas/agentSkill.ts + agent_skill_list.ts + agentSkillsService.ts). 🤖 feat: skills refresh — invocation control, $ARGUMENTS, dynamic context, .claude compat #3728 (skills refresh) added user-invocable / argument-hint / when_to_use frontmatter and normalized them via resolveSkillAdvertise / resolveSkillUserInvocable / resolveSkillWhenToUse. Both descriptor-building sites — readSkillDescriptor (the agent_skill_list tool) and readSkillDescriptorFromDir (agentSkillsService discovery) — then inlined the byte-identical 7-field object literal mapping parsed.frontmatter + scope into an AgentSkillDescriptor before AgentSkillDescriptorSchema.safeParse. Extracted the mapping into a shared buildSkillDescriptor(frontmatter, scope) in agentSkill.ts (co-located with the resolveSkill* helpers it calls) and dropped the now-unused resolveSkill* imports at both call sites. Callers still run safeParse themselves since they handle validation failure differently. No behavior change.

  17. Hoist duplicated Date.parse(record.createdAt) in the bash monitor delivery gate (workspaceService.ts). 🤖 fix: defer bash monitor wakes during task_await #3732 (defer bash monitor wakes during task_await) reworked the delivery gate in drainBashMonitorWakes so a match is re-checked against the shown frontier while pinned to its originating process instance via Date.parse(record.createdAt). The new non-blocking getMonitorWakeDeliveryState branch and the fallback getSettledShownThroughOffset branch each inlined the identical Date.parse(record.createdAt) call as the originNotAfterMs argument. Hoisted a single const originNotAfterMs = Date.parse(record.createdAt) before the branches (with a clarifying comment on why the origin timestamp pins the check) and routed both calls through it. Date.parse is pure, so the hoist is behavior-preserving.

  18. Dedupe the "wait for any in-flight load" block in DevToolsService (devToolsService.ts). 🤖 fix: clean up devtools.jsonl on archive/remove and reap orphaned session dirs #3733 added removeWorkspaceData (archive/remove DevTools cleanup) directly beside the existing clear; both inlined the byte-identical const pendingLoad = this.loadingPromises.get(workspaceId); if (pendingLoad) { await pendingLoad; } guard that drains any in-flight loadFromDisk before mutating in-memory state so a late load cannot repopulate stale data after the mutation. Extracted into a private awaitPendingLoad(workspaceId) helper with the shared rationale in its doc comment; both call sites keep their situational one-line comment. No control-flow change.

  19. Dedupe MCP OAuth redirect URI resolution (router.ts). Both the global (mcpOauth.startServerFlow) and per-project (projects.mcpOauth.startServerFlow) handlers inlined the byte-identical block that derives the OAuth callback redirectUri from request headers — preferring the Origin header (used verbatim when it parses as a URL), then falling back to x-forwarded-host/host with the forwarded proto (defaulting to http), and returning Err("Missing Host header") when no usable Host header exists. Extracted into a module-level resolveMcpOauthRedirectUri(headers) helper that returns the resolved URI or undefined; each handler now maps undefined to the same Err. startServerFlow is async (its returned promise is passed through unawaited), so moving the call out of the origin-branch try cannot change behavior — the try only ever guarded the synchronous new URL(...) construction. No header semantics or return-shape change.

  20. Extract getTotalTokens helper for total-token sums (usageAggregator.ts + 6 call sites). 🤖 feat: show per-model cost breakdown in workspace Costs tab #3739 (per-model cost breakdown in the Costs tab) added a fifth+ copy of the "sum every usage component" expression — input + cached + cacheCreate + output + reasoning .tokens — already inlined byte-for-byte in CostsTab (session model rows), WorkspaceStore (session totalTokens), tokenMeterUtils (calculateTokenMeterData total), sessionUsageService (per-model totalTokens accumulation), and twice in cli/run.ts (budget hasTokens gates). Added a getTotalTokens(usage) helper in usageAggregator.ts, co-located with and mirroring the existing getTotalCost (same five-component iteration; undefined0), and routed all six sites through it. The four-component sum in cli/debug/costs.ts (which omits cacheCreate) was intentionally left untouched to preserve its existing behavior.

  21. Hoist the duplicated dedupeKeys snapshot in removeByDedupeKeyPrefix (messageQueue.ts). 🤖 refactor: support incremental subagent reports #3714 (incremental sub-agent reports) added MessageQueue.removeByDedupeKeyPrefix, which spread the entry's dedupeKeys Set into an array once for the matchingKeys prefix filter and then re-spread the same Set inside the entry.messages.filter(...) callback — once per message iteration — to map each message index back to its dedupe key. The Set is not mutated until after keptMessages is computed, so both reads observe the same ordered snapshot. Hoisted a single const dedupeKeyList = [...entry.dedupeKeys] before the filter and routed both reads through it, eliminating the per-message re-spread. Pure behavior-preserving simplification with no control-flow change.

  22. Dedupe the settled workspace-turn reconciliation guard (taskService.ts). 🤖 fix: report live workspace-turn state from task_await instead of stale settlements #3738 (report live workspace-turn state from task_await) added two read-time reconciliation helpers — persistRepairedSettledWorkspaceTurn and reviveRetryingWorkspaceTurn — that each open their settlement-lock body with the byte-identical guard: reload the handle via getWorkspaceTurn and return current unless it is still the exact record we reconciled against (current != null && current.status === record.status && current.updatedAt === record.updatedAt). Extracted the condition into a module-level isReconciledWorkspaceTurnUnchanged(current, record) type guard, co-located with 🤖 fix: report live workspace-turn state from task_await instead of stale settlements #3738's own isSelfHealEligibleSettledWorkspaceTurn, so the generic "compare updatedAt too, not just status" rationale lives in one doc comment while each call site keeps its situational note. The current is WorkspaceTurnTaskHandleRecord return type preserves the non-null narrowing that reviveRetryingWorkspaceTurn relies on after the guard. No control-flow or return-shape change.

  23. Dedupe the fire-and-forget archive-all catch (TaskGroupListItem.tsx). 🤖 fix: archive all sidebar variants #3741 (archive all sidebar variants) added an onArchiveAll prop invoked from two places — the archive keyboard-shortcut branch in onKeyDown and the Archive all variants context-menu item's onClick. Both inlined the byte-identical fire-and-forget props.onArchiveAll(...).catch(() => { /* the sidebar owner surfaces archive failures through its shared error UI */ }) block, differing only in optional-call syntax (inert because the prop is defined in both branches). Extracted a local archiveAll(buttonElement) helper so the swallow-and-surface rationale lives in one place. No control flow, arguments, or error-handling change.

  24. Extract someDescendantAgentTaskWorkspace helper for sticky-descendant queries (taskService.ts). [tasks] 🤖 feat: support sticky subagents #3744 (sticky subagents) added two adjacent query methods — hasStickyDescendants and hasUnarchivedStickyDescendants — that each rebuilt the agent-task index the same way (loadConfigOrDefault()buildAgentTaskIndex(cfg)listDescendantAgentTaskIdsFromIndex(index, workspaceId).some(...)) and differed only in the .some() predicate. Extracted a private someDescendantAgentTaskWorkspace(workspaceId, predicate) helper that resolves each descendant entry and threads it through the predicate (keeping .some() short-circuiting); the two public methods now just supply their predicate and keep their own assert. The helper's descendant != null && predicate(descendant) guard is equivalent to the prior index.byId.get(descendantId)?.taskSticky === true form, so no behavior changes.

  25. Drop the duplicated Kimi K3 max-effort rationale (providerOptions.ts). 🤖 feat: add native Kimi K3 support via a new Moonshot AI provider #3737 (native Kimi K3 via a new Moonshot AI provider) added the isKimiK3Model predicate, whose docstring is the authoritative statement that K3 always reasons and supports only the max reasoning effort and that the provider-options branches key off it. Both the Moonshot and OpenRouter branches of buildProviderOptions then restated that same lead sentence verbatim, so the duplicated sentence was trimmed from each while keeping only the branch-specific "send it explicitly" rationale (Moonshot: don't rely on the API default; OpenRouter: enabled: true alone falls back to the unsupported default medium effort). Comment-only; behavior-preserving.

  26. Drop the redundant structuredOutput guard at subagent report call sites (taskService.ts). 🤖 feat: present subagent reports in chat #3742 (present subagent reports in chat) extracted formatSubagentReportUserMessage, which already omits structuredOutput from the report envelope when it is undefined (its internal !== undefined conditional spread). Both call sites — the incremental in_progress progress report in the agent_report path and the terminal completed report in deliverReportToParentUnlocked — nonetheless re-implemented that exact ...(report.structuredOutput !== undefined ? { structuredOutput: report.structuredOutput } : {}) guard before handing the value to the helper. Each now forwards report.structuredOutput directly, and the helper documents that it owns the omission. Behavior-preserving: the helper's internal guard yields byte-identical envelope output whether the key is absent or passed explicitly as undefined.

  27. Extract isZipMediaType helper for staged attachment media-type checks (supportedAttachmentMediaTypes.ts). 🤖 feat: stage arbitrary pasted/dropped files into the workspace from chat #3746 (stage arbitrary pasted/dropped files) generalized the ZIP-only staged-attachment pipeline to arbitrary files, and in doing so the cast-laden ZIP membership check ZIP_MEDIA_TYPES.includes(normalized as (typeof ZIP_MEDIA_TYPES)[number]) was inlined byte-identically in both isSupportedStagedAttachmentMediaType and getSupportedStagedAttachmentMediaType. Extracted a module-private isZipMediaType(normalized) helper so the as const tuple cast lives in one place; both call sites now read isZipMediaType(normalized). Behavior-preserving.

  28. Hoist the duplicated /goal-bypass-for-attachments check in the ChatInput send handler (ChatInput/index.tsx). 🤖 feat: stage arbitrary files from the creation composer #3748 (stage arbitrary files from the creation composer) computed goalCommandBypassedForAttachments (parsed?.type === "goal-set" && attachments.length > 0) verbatim in two mutually-exclusive branches of the send handler: the creation-variant route and the workspace send path (the latter carrying a "mirror the creation-composer bypass" comment). Both branches resolve parsed and attachments identically, so the boolean is now computed once above the routing and both inline copies dropped. Pure, behavior-preserving.

  29. Dedupe the anchored Anthropic model-id regex construction (ai/models.ts). The ANTHROPIC_NATIVE_1M_PATTERNS / ANTHROPIC_BETA_1M_PATTERNS lists that back getAnthropic1MContextMode each spelled out new RegExp(`^<id>${OPTIONAL_VERSION_SUFFIX}$`, "i") per entry — ten near-identical copies restating the anchoring, the optional dated-snapshot suffix interpolation, and the case-insensitive flag, so every new model (Opus 5 in 🤖 feat: add support for Claude Opus 5 #3750 being the latest) had to repeat the whole construction. Extracted a module-private anthropicModelIdPattern(baseModelId) helper and reduced both lists to base model-id strings mapped through it. Generated regex sources and flags are byte-identical to before, and base ids are literal model names with no regex metacharacters, so matching is unchanged.

  30. Dedupe MCP header telemetry flag derivation (orpc/router.ts). Every mcp_server_config_changed capture site recomputed the has_headers / uses_secret_headers payload flags inline — eight copies across the global and per-project MCP routers (add, remove, setEnabled, setToolAllowlist). Two variants existed: an input-based one (Boolean(input.headers && Object.keys(input.headers).length > 0) plus the "secret" in v scan) and a server-based one that prefixed both with a server.transport !== "stdio" guard, needed because headers only exists on the HTTP-ish arm of the MCPServerInfo union. Extracted describeMcpHeaderTelemetry(headers) and a thin describeMcpServerHeaderTelemetry(server) wrapper that returns { hasHeaders: false, usesSecretHeaders: false } for stdio — exactly what transport !== "stdio" && … already evaluated to — so the "what counts as a secret header" rationale lives in one doc comment. Behavior-preserving; ~80 lines removed.

  31. Extract _child_dirs helper for job folder discovery (benchmarks/terminal_bench/prepare_leaderboard_submission.py). find_job_folders walked directory trees with three copies of the same "iterate a directory, keep only the subdirectories" pattern: two nested for item in <dir>.iterdir(): if item.is_dir(): job_folders.append(item) loops (the direct jobs/ branch and the per-artifact branch) plus a for artifact_dir in artifacts_dir.iterdir(): if not artifact_dir.is_dir(): continue skip-guard at the top of the per-artifact scan. Extracted a module-level _child_dirs(path) helper returning [child for child in path.iterdir() if child.is_dir()] and rewrote all three sites in terms of it. iterdir() ordering and the resulting job_folders ordering are unchanged; behavior-preserving, 9 lines removed.

  32. Drop the redundant "exec" fallback duplication for normalizeAgentId (workspaceModeAi.ts, WorkspaceModeAISync.tsx, ChatInput/index.tsx). normalizeAgentId(value, fallback) in common/utils/agentIds.ts already declares fallback: string = WORKSPACE_DEFAULTS.agentId, and WORKSPACE_DEFAULTS.agentId is "exec". Four call sites in the per-agent workspace AI settings paths nonetheless passed the bare literal "exec", re-hardcoding the centralized default that the signature supplies — exactly the kind of duplicated constant that goes stale if the default agent ever changes (every other "workspace default agent" call site either omits the argument or passes WORKSPACE_DEFAULTS.agentId). All four now omit the argument. With the literal gone, workspaceModeAi.ts's module-private normalizeAgentId(agentId) wrapper existed only to supply that fallback, so it and its aliased normalizeAgentId as normalizeWorkspaceAgentId import were removed in favor of importing normalizeAgentId directly. Behavior-preserving: the omitted argument resolves to the identical string.

  33. Name the digest truncation bounds in the timeline mapper (timelineMapper.ts). 🤖 feat: add a durable per-workspace timeline #3755 (durable per-workspace timeline) added truncateDigest, which condenses a user prompt's text parts into a single-line timeline row title, with both of its bounds inlined as bare literals: normalized.length <= 120 ? normalized : `${normalized.slice(0, 117)}...` . The 117 silently encodes 120 - "...".length, an invariant a reader can only confirm by counting the ellipsis, and the sibling helper in the same feature (truncateTimelineDigest in common/orpc/schemas/timeline.ts) already spells the identical normalize-then-ellipsize pattern as TIMELINE_TEXT_MAX_LENGTH / TIMELINE_TEXT_MAX_LENGTH - 3. Introduced module-private DIGEST_MAX_LENGTH = 120 and DIGEST_ELLIPSIS = "..." and derived the slice as DIGEST_MAX_LENGTH - DIGEST_ELLIPSIS.length, so the "a truncated digest still totals DIGEST_MAX_LENGTH" invariant is stated rather than implied, and the tighter-than-schema bound is explained in a comment. The two helpers were deliberately not merged: the mapper's 120-char row-title bound is intentionally tighter than the 600-char boundTimelineTextFields safety net applied later, so collapsing them would change what gets persisted.

  34. Dedupe the defensive unknown-field reads in the timeline mapper (timelineMapper.ts). 🤖 feat: classify machine-authored turns on the workspace timeline #3756 (machine-authored turn classification) added readMonitorWakeProcesses, which pulls records off muxMetadata and then a displayName off each record. Because muxMetadata crosses the oRPC boundary as any, both reads spelled out the same defensive guard inline — typeof x === "object" && x !== null ? (x as Record<string, unknown>)[field] : undefined — and the pre-existing readMuxMetadataField in the same file carried a third copy of it as an early return, so one file held three hand-rolled versions of "index a field off a value that might not be an object". Extracted a module-private readObjectField(value, field): unknown and rewrote all three sites in terms of it, following the precedent already set by getWorkflowResultField in common/utils/workflowRunMessages.ts and readPreviewText in timelineService.ts. Behavior-preserving: all three guards admitted exactly the same shapes (non-null objects, arrays included, functions excluded by typeof), and each caller still applies its own narrowing afterwards (typeof value === "string" for the metadata fields, Array.isArray for records), so every input maps to the same result as before. 14 lines removed, 11 added; no new exports.

  35. Share the mobile-touch media query constant (constants/layout.ts, App.tsx, WorkspaceMenuBar.tsx, WorkspaceShell.tsx, ChatInput/index.tsx, UserMessage.tsx). 🤖 feat: redesign workspace chrome (footer info bar, title header, creation hero, composer) #3753 (workspace chrome redesign) leaned further on Mux's mobile-affordance gate, and the string that defines it — (max-width: 768px) and (pointer: coarse) — was copied verbatim into seven window.matchMedia(...) call sites across five renderer files (the sidebar width override, both handleOpenTerminal popout branches, the menu bar's isTouchMobileScreen, UserMessage's isMobileTouch, and the composer's useState initializer plus its change-listener effect). Each copy was independently responsible for staying in sync with the matching @media block in globals.css, which is the actual source of truth for the styles these branches mirror. Hoisted to an exported MOBILE_TOUCH_MEDIA_QUERY in src/constants/layout.ts — directly above MOBILE_TOUCH_TARGET_PX, which already documents this same coarse-pointer environment — and rewrote all seven call sites to use it. Behavior-preserving: every call site passed a byte-identical literal (verified by an exact-match grep over src/), so each matchMedia call receives precisely the value it did before; the only other diff is Prettier rejoining four now-shorter expressions onto single lines. Four of the five consumers already imported from @/constants/layout, so this adds just one new import statement.

  36. Share the docked toast overlay placement class (constants/layout.ts, ConnectionStatusToast.tsx, ChatInputToast.tsx, ChatInput/index.tsx). Three toast hosts each hard-coded the same absolute overlay box — pointer-events-none absolute right-[15px] bottom-full left-[15px] z-[1000] mb-2 [&>*]:pointer-events-auto — as two identically-named local wrapperClassName constants plus one inline className on the composer's toast stack. ConnectionStatusToast's own doc comment asserts that it "uses the same overlay placement as ChatInputToast", so the invariant was real but enforced only by copy-paste, and the composer renders both components with wrap={false} under a third copy of the box — so a drifting inset would misalign a toast depending on which host happened to wrap it. Hoisted to CHAT_DOCK_TOAST_OVERLAY_CLASS in src/constants/layout.ts, directly below CHAT_DOCK_GUTTER_CLASS whose 15px inset it mirrors. Behavior-preserving: the two wrapperClassName sites now reference the identical string they previously defined locally, and the composer's stack composes it as cn(CHAT_DOCK_TOAST_OVERLAY_CLASS, "flex flex-col gap-2") — the same utility set with no conflicting utilities, so tailwind-merge yields the same computed styles (only the class attribute's token order shifts). The value stays a literal string in layout.ts because Tailwind scans source text, the same constraint already documented on CHAT_DOCK_GUTTER_CLASS.

  37. Share the primary mouse button guard (browser/utils/events.ts, ChatPane.tsx, DiffRenderer.tsx). 🤖 fix: keep iPad composer clicks from selecting the whole transcript #3759's new composer-dock mousedown handler gated on the bare magic number event.button !== 0 — the same primary-button guard the diff review drag-select handler already spelled out inline. Named the check once as isPrimaryMouseButton(event) in browser/utils/events.ts, next to the existing isEventFromDialogPortal / stopKeyboardPropagation event helpers, and pointed both call sites at it so each reads as intent instead of a DOM constant. The helper accepts both React synthetic and native mouse events.

  38. Name the ModelSelector row's selection/highlight state (ModelSelector.tsx). 🤖 fix: align composer pickers and size local workers by memory #3760 reworked the model dropdown option row to share composerPickerOptionClass with AgentModePicker and to accent the selected row. In the process the row grew to recompute value === model four separate times — for the option class's isSelected, for aria-selected, for the ProviderIcon's text-accent/text-muted ternary, and for the model-name span's accent — plus index === highlightedIndex twice (data-highlighted and the option class's isHighlighted). Hoisted both into isSelected / isHighlighted locals at the top of the map callback, matching the naming the sibling AgentModePicker already uses for the same two states, so the row's state is named once and the four accent/ARIA consumers cannot drift apart.

  39. Share the workspace footer pill class (WorkspaceFooterBar.tsx). 🤖 feat: link the footer GitHub slug to the repository #3762 turned the footer's GitHub owner/repo slug into a link and — per its own PR description — styled it "to match the sibling Last prompt pill", which in practice meant copying that pill's 13-utility Tailwind string verbatim into the new <a>. The two strings then differed only by the three <button> resets (cursor-pointer, border-0, bg-transparent), so any future restyle of one pill would silently drift from the other. Extracted the shared styling into a module-level FOOTER_PILL_CLASS: the anchor consumes it directly, and the button composes it as cn(FOOTER_PILL_CLASS, "cursor-pointer border-0 bg-transparent").

  40. Share the safe inactive-animation pause install (browser/utils/inactiveAnimations.ts, main.tsx, terminal-window.tsx). 🤖 perf: reduce idle dev CPU usage #3768 (reduce idle dev CPU usage) added installInactiveAnimationPause and wired it into both renderer entrypoints. Because the pause is a pure optimization that must not be able to take startup down with it — AGENTS.md's "startup-time initialization must never crash the app" rule — each entrypoint wrapped the call in its own five-line try/catch, and the two blocks were byte-identical down to the comment (// Animation throttling is an optimization and must never block renderer startup.). Both also discard the disposer the installer returns, so the duplication was pure ceremony repeated per entrypoint rather than anything either window customized. Extracted installInactiveAnimationPauseSafely() into the installer's own module, directly beneath installInactiveAnimationPause, so the swallow policy is stated once where the risk lives and any future entrypoint (or a third window) inherits it by calling one function. The doc comment records both the "never fail startup" rationale and the deliberate disposer drop, which was previously implicit in the call sites.

  41. Share the bash monitor wake message predicate (utils/messages/messageUtils.ts, ChatPane.tsx, MessageRenderer.tsx). 🤖 fix: quiet monitor wake events in chat #3779 gave background monitor wakes their own quiet transcript presentation, which split one concept — "this persisted user turn is a machine-authored monitor event, not a human prompt" — across two files that each re-derived it inline. MessageRenderer routes on message.bashMonitorWake != null to pick BashMonitorWakeMessage over UserMessage; ChatPane's userMessageNavigationByHistoryId memo independently filters on message.bashMonitorWake == null so the prev/next prompt arrows skip wakes. The two tests are the same classification written twice with opposite polarity, and they have to stay in agreement: if only one is updated when the wake representation changes, the transcript renders a quiet event that the navigation arrows still count as a prompt (or vice versa), which is a silent UX bug rather than a type error. Extracted isBashMonitorWakeMessage(message: DisplayedUserMessage) into messageUtils, alongside the existing DisplayedMessage predicates (shouldShowInterruptedBarrier, computeBashOutputGroupInfos), and pointed both call sites at it. Both files already imported from messageUtils, so this adds no new module edge.

  42. Dedupe the monitor disposition branch in terminate() (backgroundProcessManager.ts). 🤖 fix: cancel stale background monitor wakes #3776 (cancel stale background monitor wakes) gave BackgroundProcessManager.terminate() a new options.monitorDisposition parameter and, to honour it, inlined the same five-line if (shouldFlushMonitor) { this.stopMonitor(proc, true); } else { this.cancelMonitor(proc); } block at both of the method's two monitor-retirement points: the idempotent already-terminated shortcut and the live-kill path inside the try. The two copies are byte-identical and must stay that way — they answer the same question ("does this caller still want a wake?") for the same process. Replaced both with a resolveMonitorForTermination(proc, shouldFlush) private helper placed next to stopMonitor / cancelMonitor, so the disposition rule lives in one place and the two exit paths cannot drift apart.

  43. Share the queued-message action button classes (QueuedMessage.tsx). Dropped during this run's rebase — superseded upstream. 🤖 feat: refine transient transcript interactions #3790 rewrote this action row wholesale: Edit shrank to an h-6/px-1.5 button and Send now moved into a new queue-status dropdown, so both copies of the deduped class string are gone from main and the shared constant had no second caller left. The file now matches main byte-for-byte. Original rationale: 🤖 fix: restore queued message text hierarchy #3781 restored the queued draft's text hierarchy by dropping the Edit and Send now labels from text-xs to text-[11px] — and had to make that one-token edit twice, because both buttons inlined a byte-identical flex h-7 items-center gap-1.5 rounded-md px-2.5 text-[11px] font-medium transition-colors run of geometry/typography utilities and differed only in their colour treatment (text-muted + hover for Edit; bg-pending/10 + disabled states for Send now). Hoisted the shared half into a QUEUED_ACTION_BUTTON_CLASSNAME constant and composed each button's colours on top with cn(...), so the next typography tweak lands in one place instead of drifting between the two.

  44. Extract parseSubagentReportFromMessage helper for report-envelope history scans (subagentReportEnvelope.ts + 4 call sites). Subagent report envelopes reach history as synthetic user messages, so every scanner that wants the parsed envelope must first reconstruct the message text. Four sites inlined the byte-identical projection — message.parts.filter((part): part is Extract<typeof part, { type: "text" }> => part.type === "text").map((part) => part.text).join("\n") followed by parseSubagentReportEnvelope(text). 🤖 fix: avoid duplicate subagent completion responses #3783 (avoid duplicate subagent completion responses) added the third and fourth copies in TaskService.findProgressRespondedTaskIds and TaskService.hasAcceptedSubagentProgressReport, joining the pre-existing copies in TaskService's sibling synthetic-report discovery and AgentSession.isVisibleCompletedSubagentReportMessage. Extracted the projection into parseSubagentReportFromMessage(message) in subagentReportEnvelope.ts, co-located with the string parser it wraps, and routed all four sites through it. The MuxMessage import is import type, so the module stays runtime-dependency-free; parseSubagentReportEnvelope remains exported for the callers that already hold a text string (timelineMapper.ts, tests). (Since reduced to two live call sites: 🤖 fix: resume parents directly from sub-agent reports #3816 rewrote TaskService.findProgressRespondedTaskIds and deleted hasAcceptedSubagentProgressReport, so this run's rebase dropped those two hunks and kept main's replacement code verbatim.)

  45. Drop stale userOverridable references from the experiments UI (ExperimentsSection.tsx, useExperiments.ts). Two comments still described a userOverridable experiment flag that no longer exists anywhere in src/: the Settings list actually filters on showInSettings !== false, and isExperimentEnabled returns undefined whenever no explicit localStorage override exists, not only for "user-overridable" experiments. Comment-only change.

  46. Reuse the isTaskAwaitMessage predicate in the transcript projection (transcriptRenderProjection.ts). 🤖 fix: refine task wait transcript presentation #3788 added task_await poll grouping along with a small family of helpers, three of which re-inlined the same message.type === "tool" && message.toolName === "task_await" shape test that isTaskAwaitMessage already performs — getTaskAwaitResultEntries, hasTaskAwaitCallFailure, and hasTaskAwaitCallInterruption. Widening isTaskAwaitMessage into a type predicate (message is Extract<DisplayedMessage, { type: "tool" }>) lets those three delegate to it and still read the tool-only status / result fields, so the shape test now lives in exactly one place.

  47. Dedupe the non-blank string coercion in taskReportLinking (taskReportLinking.ts). Every field this module pulls out of persisted tool args/results is typed unknown, so each read hand-rolled the same three-part check: typeof x === "string", x.trim().length > 0, then use x.trim(). 🤖 feat: expose sub-agent model and thinking level in task schedule and report #3789 and 🤖 feat: show task kind and spawn intent in single-task task_await summary #3793 grew that from four copies to seven (getTaskIdsFromToolResult ×3, getTitleFromTaskToolArgs, the new getAgentTypeFromTaskToolArgs, getBashSpawnTaskId, and getBashSpawnInfoFromArgs). All seven now call one file-local coerceNonBlankString helper.

  48. Drop the redundant agentType re-check in task_await awaited rows (TaskToolCall.tsx). While task_await is still in flight, TaskAwaitToolCall builds one awaitedRows entry per pending task and resolves each row's agent type with resolvePersistedAgentId(metadata, "") — an intentionally empty fallback. The very next line already collapses that empty string to undefined (resolvedAgentType.length > 0 ? resolvedAgentType : undefined), but the awaitedRows.push({ ... }) literal then re-tested the same value with agentType && agentType.length > 0 ? agentType : undefined. That second guard cannot change anything: undefined short-circuits to the undefined arm, and any string that survived the first line is non-empty by construction. Replaced with the agentType, shorthand, and left a comment on the normalizing line recording why the empty fallback exists so the intent survives the removed guard.

  49. Hoist the duplicated isGrok45Model check in xAI model creation (providerModelFactory.ts). 🤖 fix: honor mapped Grok 4.5 aliases #3804 (mapped Grok 4.5 aliases) introduced a capabilityModel local from resolveModelForMetadata and called isGrok45Model(capabilityModel) to choose between provider.responses(modelId) and provider.chat(modelId). 🤖 feat: default Grok Responses to store=false for ZDR parity #3807 (the store=false ZDR default) then added a second, byte-identical isGrok45Model(capabilityModel) call three lines below, to gate injectGrok45StoreDefault. Both read the same const, and isGrok45Model is a pure regex test over a prefix-stripped string, so the two evaluations are guaranteed to agree. Folding them into a single const isGrok45 drops the redundant call and lets the ternary collapse onto one line. It also restores consistency with buildProviderOptions, which already keeps exactly such an isGrok45 local for the same predicate. Net +2 −3.

  50. Share the bash-monitor-wake metadata type guard (messageQueue.ts, agentSession.ts). 🤖 fix: keep workspace turns alive across bash-monitor-wake queue cuts #3797 needed to answer "is this unknown muxMetadata a bash-monitor wake?" in two places — MessageQueue.isNextEntryBashMonitorWake (queue head) and AgentSession.hasPendingBashMonitorWakeContinuation (mid-dispatch, dequeued but not yet streaming) — and each site hand-rolled the check with a different unsound cast: (muxMetadata as Record<string, unknown>).type === "bash-monitor-wake" in one, this.dispatchingQueuedEntryMuxMetadata as MuxMessageMetadata | undefined in the other. The second is the worse of the two: it asserts a unknown field into the full MuxMessageMetadata union purely to reach ?.type. messageQueue.ts already keeps a family of narrow, module-local metadata guards (isCompactionMetadata, isAgentSkillMetadata, isWorkspaceTurnMetadata, hasReviews) built on exactly this shape, so the new code was the odd one out in its own file. Extracted isBashMonitorWakeMetadata alongside those siblings, exported it, and reused it at both call sites. The declared BashMonitorWakeMetadata interface carries only type, matching what the guard actually validates — neither caller reads records, so widening the claim would be dishonest. Net effect: two type assertions removed, one definition of "what a wake looks like" instead of two that can drift, and the file's established guard pattern restored.

  51. Share the persisted tool-error message extraction (Shared/toolUtils.tsx, TimelineEventToolCall.tsx, AgentSkillListToolCall.tsx). feat: add a timeline_event transcript card #3814's new timeline card added a private extractErrorMessage, which resolves the two error shapes a transcript card can be handed: the standard { success: false, error } that top-level tools persist, and the bare { error } with no success flag that displayedMessageBuilder reconstructs for a failure inside a nested code_execution/PTC call. AgentSkillListToolCall.toSkillListView already inlined the same resolution — same isToolErrorResult first branch, same !("success" in x) + typeof error === "string" fallback — and both files carried the same rationale comment. Extracted into extractToolErrorMessage(result) in Shared/toolUtils.tsx, co-located with the isToolErrorResult / isFailedToolOutput family it belongs to, and routed both cards through it, dropping their now-unused isToolErrorResult imports.

  52. Share the terminal-attention message text join (taskService.ts). 🤖 fix: resume parents directly from sub-agent reports #3816 rewrote sub-agent terminal handoff so the parent resumes directly from an injected report, and in doing so gave TaskService two history scans that both classify rows by parsing a sub-agent report/failure envelope: ensureAgentTerminalMessages (the repair pass that appends a missing report before the wake-up) and consumeRespondedAgentTerminalAttention (the responded-scan that decides whether a pending wake is already answered). Each inlined the identical message.parts flatten — same Extract<…, { type: "text" }> predicate, same .map((part) => part.text), same "\n" separator — before handing the string to parseTerminalSubagentTaskId. Extracted joinMessageText(message) next to that parser so both scans share one definition. These two genuinely must agree: if one drifted on the separator or started admitting a non-text part, the repair pass could append a report the responded-scan can't match, and the parent would be woken to integrate a report already sitting in its history.

  53. Share the agent-plugin error diagnostic push (agentPlugins/discovery.ts). 🤖 feat: experimental Agent Plugins 1.0.0 support (skills + MCP) #3815 landed Agent Plugins 1.0.0 discovery, whose §11.3 failure-isolation contract means practically every failure path ends in the same two-step move: log.warn with the owning plugin directory as prefix, then push a severity: "error" diagnostic describing the same problem. That pair was hand-written three times across two functions. Extracted pushErrorDiagnostic so the log prefix, the severity, and the diagnostic shape live in one place.

  1. Share the creation project-picker option list (ChatInput/CreationProjectSelect.tsx, CreationControls.tsx, ChatInput/index.tsx). 🤖 feat: add project switcher to the scratch creation page #3817 extracted CreationProjectSelect so the scratch page could reuse the project picker, but it left the options construction inlined at both call sites: each independently did Array.from(userProjects.keys()).map((path) => ({ value: path, label: formatProjectHierarchyLabel(path, userProjects) })). Extracted projectSelectOptions(userProjects) next to the component that consumes it, and gave the option shape a name (CreationProjectOption) so the prop type and the builder agree. These two copies have to stay in lockstep: the trigger renders selectedLabel as an explicit child rather than letting Radix mirror the matched <SelectItem/>, so if one call site's label formatting drifted from the other's, the picker would show a label that never matches any item in its own dropdown.

This run

Considered origin/main from the previous checkpoint c994e455f through 5da66a7a6 (HEAD), covering the single commit merged since:

  • feat: add project switcher to the scratch creation page (🤖 feat: add project switcher to the scratch creation page #3817, 5da66a7a6) — adds a project picker to the scratch composer so mobile users can see and change their creation scope, plus a set of tests/ui/ isolation fixes (a self-healing installDom() teardown, an eager react-dnd require, and a new restoreModulesAfterSuite helper) for pre-existing harness bugs that the new files exposed on CI.

Cleanup taken (see #54 above). The commit's own refactor is the thing that created the duplication: pulling the picker markup into CreationProjectSelect deduplicated the rendering, but both callers still had to build the options array themselves, so the formatProjectHierarchyLabel-per-key mapping got written twice. Moving that builder next to the component finishes the extraction — and it is the one duplicate here with a real drift consequence, since CreationProjectSelect deliberately renders selectedLabel as an explicit <SelectValue/> child instead of relying on Radix's mirror of the matched item's text.

The scratch call site keeps its literal for the leading { value: SCRATCH_PROJECT_CONFIG_KEY, label: SCRATCH_PROJECT_NAME } entry and just spreads the helper after it; folding the Scratch entry into the helper would have meant a flag parameter for a one-caller case.

Removing the last formatProjectHierarchyLabel reference from index.tsx also collapsed that file's two-name import back to a single-name one.

Considered and rejected:

  • The Select* imports in CreationControls.tsx. The natural suspicion after an extraction is that the primitives are now dead, but RadixSelect/SelectTrigger/SelectValue/SelectContent/SelectItem are all still used by three other selects in the file (workspace type, source branch, devcontainer config). Verified per-symbol before ruling it out.
  • tests/ui/dom.ts's self-heal recursion and the require("react-dnd") pin. Load-order-sensitive test-harness code whose correctness depends on exactly when module evaluation happens. Off-limits for a behavior-preserving pass — the commit message notes the original bug did not even reproduce locally.
  • restoreModulesAfterSuite adoption at the remaining mock.module sites. A real consistency improvement, but it changes test teardown behavior across seven-plus files, which is neither local nor behavior-preserving.
  • The (() => { ... })() IIFE wrapping the picker in CreationControls. It exists to scope the selected local inside JSX; unwrapping it would mean hoisting the local out of the render tree for no behavioral gain.

Validation

  • make static-checkexit 0, fully green: ESLint, both tsgo --noEmit projects, Prettier, shfmt, ruff, the docs/codegen sync checks, code-to-docs links, shellcheck, and hadolint. Note for future runs: this environment ships without shfmt, uvx, and hadolint, so the target aborts partway with a not found error until they are installed — that is a missing-binary symptom, not a finding.
  • bun test src/browser/features/ChatInput100 pass, 0 fail (11 files). bun test src/browser/components/ProjectPage1 pass, 0 fail, covering the CreationControls picker call site.
  • No test added. The helper is the two former inline expressions verbatim, and both call sites keep the same array contents in the same order; a new test could only re-assert Map iteration order.

Risks

Minimal, and confined to the creation composer's project picker. Array.from(map.keys()).map(...) moved into a function unchanged, so option identity, ordering (Map insertion order), and label formatting are all preserved; the scratch site still prepends its Scratch entry before the spread. The only typing change is naming the previously-anonymous { value, label } shape, which is structurally identical.

Worth noting for reviewers: projectSelectOptions returns a fresh array each render, exactly as the inline expressions did. React Compiler handles the memoization here as it did before, so this is not a new unstable-reference source.

Auto-cleanup checkpoint: 5da66a7


Generated with mux • Model: anthropic:claude-opus-5 • Thinking: xhigh • Cost: $0.00

@mux-bot

mux-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from a2454d3 to 450337d Compare July 8, 2026 17:01
@mux-bot

mux-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

⚠️ Auto-fixup: CI failure appears to be infrastructure/flaky, not caused by this cleanup commit — no code pushed.

Root cause: The Static Checks job failed with the runner annotation "The self-hosted runner lost communication with the server" after running the full 10m, and its log blob was never uploaded (BlobNotFound). Test / Integration was cancelled and Required failure are both downstream of that (Required only aggregates results).

Verification: Ran make static-check locally on this PR's HEAD (a2454d31b). ESLint, both TypeScript configs (tsgo --noEmit ×2), and Prettier all pass. The only local miss is fmt-shell-check because shfmt isn't installed in the fixup sandbox — and this PR changes no shell files, so it's irrelevant.

Recommendation: Re-run the failed CI jobs. No code change needed.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from 450337d to e32952d Compare July 9, 2026 00:30
@mux-bot

mux-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Latest push rebases onto origin/main (through #3698) and adds one low-risk, behavior-preserving cleanup: extract the duplicated per-scope "memory scope is full" cap check in MemoryService.create / saveFile into a private assertScopeHasRoom(store, scope) helper. No control flow, thresholds, error types, or events change. make static-check (ESLint + both tsconfigs + Prettier) and bun test memoryService.test.ts (75/0) pass locally.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from e32952d to d8d0ba5 Compare July 9, 2026 09:10
@mux-bot

mux-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

New in this run: cleanup #3 — deduped the identical blockquote line-prefixing (> per line, joined by newlines) in buildBashMonitorWakePrompt into a module-level blockquoteLines(lines) helper. Behavior-preserving; validated by the existing buildBashMonitorWakePrompt output-format tests (21/21 pass).

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from d8d0ba5 to 78cd7b2 Compare July 9, 2026 20:38
@mux-bot

mux-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from 78cd7b2 to a676f79 Compare July 10, 2026 00:30
@mux-bot

mux-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Added auto-cleanup #5: getCodexOauthContextLimit now reuses the already-resolved, non-null compatibilityModelId with the isCodexOauthAllowedModelId / isCodexOauthRequiredModelId variants instead of re-invoking isCodexOauthAllowedModel / isCodexOauthRequiredModel (which re-derive the same id and re-scan providersConfig). Behavior-preserving; targeted tests + make static-check (minus sandbox-only shfmt) pass.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from a676f79 to 32928e3 Compare July 10, 2026 09:08
@mux-bot

mux-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Added cleanup #6: extracted a module-private getAnthropicCacheCreateTokens(metadata) helper in usageHelpers.ts to dedupe two byte-identical Anthropic cache-create token reads inside accumulateProviderMetadata. Behavior-preserving; rebased onto latest main (checkpoint ec47caf).

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from 32928e3 to 8afce4c Compare July 10, 2026 16:47
@mux-bot

mux-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from 8afce4c to 990576b Compare July 11, 2026 00:26
@mux-bot

mux-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Added cleanup #7: deduped the byte-identical QueueClearCallbacks projection in MessageQueue.getClearCallbacks and removeWorkspaceTurn into a private entryClearCallbacks(entry) helper (behavior-preserving). Rebased onto latest main and advanced the checkpoint to 956ac533e.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch 2 times, most recently from cd778d7 to 08734b9 Compare July 12, 2026 00:27
@mux-bot

mux-bot Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

This run adds one behavior-preserving cleanup (#8 in the branch list): dedupes the two identical openai-origin model checks in openaiExplicitPromptCachingAvailable (cacheStrategy.ts, introduced by #3712) into a module-private isOpenAIOriginModel(canonical) helper. The destructured origin/modelName locals were unused past their guard. Rebased onto origin/main (48722b9); make static-check passes (except fmt-shell-check, which needs shfmt unavailable in this env) and the cacheStrategy/providerOptions test suites pass (173).

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@mux-bot
mux-bot Bot force-pushed the auto-cleanup branch from 08734b9 to b42facd Compare July 13, 2026 12:53
mux-bot Bot added 27 commits August 8, 2026 00:09
The native/beta 1M-context pattern lists repeated the same
`new RegExp(`^<id>${OPTIONAL_VERSION_SUFFIX}$`, "i")` construction ten
times, so each new model entry had to restate the anchoring and flags.
Extract anthropicModelIdPattern() and map base model ids through it;
generated regex sources and flags are unchanged.
…ntId

normalizeAgentId's fallback parameter already defaults to
WORKSPACE_DEFAULTS.agentId ("exec"), so the four call sites that passed
"exec" explicitly were re-hardcoding the centralized default. Dropping the
literal also makes the workspaceModeAi wrapper (whose only job was supplying
that fallback) dead, so it and its aliased import are removed.
truncateDigest inlined both bounds as bare literals, where 117 silently
encoded 120 - "...".length. Introduce DIGEST_MAX_LENGTH/DIGEST_ELLIPSIS
and derive the slice from the ellipsis so the invariant that a truncated
digest still totals DIGEST_MAX_LENGTH is explicit, matching the existing
convention in truncateTimelineDigest.

The 120-char bound is deliberately tighter than the 600-char schema-level
boundTimelineTextFields safety net, so the helpers stay separate.
The `(max-width: 768px) and (pointer: coarse)` literal that gates Mux's
mobile affordances was copied into seven `window.matchMedia` callsites
across five renderer files, each independently responsible for staying in
sync with the matching `@media` block in globals.css.

Hoist it to `MOBILE_TOUCH_MEDIA_QUERY` in `src/constants/layout.ts`,
alongside `MOBILE_TOUCH_TARGET_PX`, which already documents the same
coarse-pointer environment.

Behavior-preserving: every callsite passed a byte-identical string, so
each `matchMedia` call receives exactly the value it did before.
Three toast hosts hard-coded the same absolute overlay box, with a doc comment
asserting they stay identical. Hoist it into src/constants/layout.ts so the
invariant is enforced by the shared constant instead of by copy-paste.
The composer dock focus handler added in #3759 repeated the bare `button !== 0` magic number already used by the diff review drag-select handler. Name the check once in browser/utils/events so both call sites read as intent.
The dropdown option row recomputed `value === model` four times and
`index === highlightedIndex` twice across the option class, ARIA state,
and accent styling. Extract them as `isSelected`/`isHighlighted` locals so
the row's state is named once and can't drift apart, matching the naming
already used by the sibling AgentModePicker.

Behavior-preserving: identical expressions, same evaluation per row.
The repository link added in #3762 duplicated the 'Last prompt' pill styling verbatim; extract it so the two footer affordances cannot drift apart.
#3779 routes monitor wakes to a dedicated transcript component and excludes
them from prev/next prompt navigation, but each site re-derived the check as an
inline bashMonitorWake null test in a different file. Extract
isBashMonitorWakeMessage into messageUtils so both consumers agree by
construction.
Four call sites inlined the identical 'join every text part, then parse the
subagent report envelope' projection. #3783 added two more copies in
findProgressRespondedTaskIds and hasAcceptedSubagentProgressReport, joining the
pre-existing copies in TaskService sibling-report discovery and AgentSession's
isVisibleCompletedSubagentReportMessage.

Extract the projection into parseSubagentReportFromMessage, co-located with the
string parser it wraps, and route all four sites through it.
#3784 removed the `userOverridable` field from ExperimentDefinition along
with the `exp.userOverridable === true` clause in the Settings filter, but
left the comments that described that clause. The ExperimentsSection comment
now misdescribed the code: the filter keys on `showInSettings`, and every
experiment is a local opt-in toggle, so the "non-overridable ones are hidden"
rationale no longer applies.

Comment-only; no behavior change.
The module reads every field out of persisted tool args/results typed as
`unknown`, and #3789/#3793 grew that from four to seven copies of the same
"is a string, is not blank, use the trimmed form" check. Collapse those seven
into a single coerceNonBlankString helper.

The two task_await result reads are deliberately left alone: they validate a
field is non-blank but then store the raw, untrimmed value, so routing them
through the helper would change what gets persisted.
resolvePersistedAgentId's empty fallback is already normalized to undefined one line
above, so the second 'agentType && agentType.length > 0' guard could never change the
value. Pass the normalized local directly and record why the fallback exists.
resolveModelForMetadata's capability model was passed to isGrok45Model twice
in the same block (once to pick responses vs chat, once to gate the store=false
injection). The predicate is a pure regex test over a const, so hoisting it into
a local is behavior-preserving and matches buildProviderOptions, which already
keeps an isGrok45 local.
Both call sites added in #3797 re-implemented the same unknown -> wake
check with different unsound casts. Extract isBashMonitorWakeMetadata
alongside the sibling guards in messageQueue.ts and reuse it, so the
queue-head and mid-dispatch checks cannot drift.
TimelineEventToolCall's extractErrorMessage and AgentSkillListToolCall's inline
check both handled the same two persisted error shapes. Extract into
extractToolErrorMessage in Shared/toolUtils.
Both terminal-attention history scans in TaskService flattened MuxMessage
text parts with the same filter/map/join before handing the result to
parseTerminalSubagentTaskId. Extract joinMessageText so the two scans
cannot drift on the part filter or separator.

Behavior-preserving: identical expression, same call order.
resolveComponentPath and discoverPluginAt each wrote the same
log.warn-then-push-error-diagnostic block by hand (3 copies). Extract
pushErrorDiagnostic so the log prefix, severity, and diagnostic shape
stay in one place. Behavior-preserving: identical log text and
diagnostic objects, same log-before-push ordering.
@mux-bot

mux-bot Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Auto-cleanup run: rebased onto 5da66a7a6 and added one behavior-preserving cleanup (#54) — extracted projectSelectOptions(userProjects) from the two inline copies left behind by #3817's CreationProjectSelect extraction. Please focus review on cleanup #54; earlier cleanups were reviewed in prior rounds.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants