🤖 refactor: auto-cleanup - #3695
Conversation
|
@codex review |
|
To use Codex here, create a Codex account and connect to github. |
|
Root cause: The Verification: Ran Recommendation: Re-run the failed CI jobs. No code change needed. |
|
@codex review Latest push rebases onto |
|
To use Codex here, create a Codex account and connect to github. |
|
@codex review New in this run: cleanup #3 — deduped the identical blockquote line-prefixing ( |
|
To use Codex here, create a Codex account and connect to github. |
|
@codex review |
|
To use Codex here, create a Codex account and connect to github. |
78cd7b2 to
a676f79
Compare
|
@codex review Added auto-cleanup #5: |
|
To use Codex here, create a Codex account and connect to github. |
a676f79 to
32928e3
Compare
|
To use Codex here, create a Codex account and connect to github. |
32928e3 to
8afce4c
Compare
|
@codex review |
|
To use Codex here, create a Codex account and connect to github. |
8afce4c to
990576b
Compare
|
To use Codex here, create a Codex account and connect to github. |
cd778d7 to
08734b9
Compare
|
@codex review This run adds one behavior-preserving cleanup (#8 in the branch list): dedupes the two identical |
|
To use Codex here, create a Codex account and connect to github. |
08734b9 to
b42facd
Compare
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.
startStdioInstance and startRemoteInstance each defined a byte-identical wrapRawTools lambda over wrapMCPTools. Extract createRawToolWrapper so the wrapping options (activity tracking + marking the live instance closed) live in one place. Behavior-preserving.
disposableExec.ts built the same augmented Error in three places (the
pre-execution abort in execFileAsync, plus both close handlers): an
inline `as Error & { code, signal, stdout, stderr }` cast followed by
four property assignments. Extract createExecError so the rejection
shape is defined once and cannot drift between the paths.
Behavior-preserving: same message strings, same field values, same
rejection timing. The only observable difference is one extra stack
frame, which nothing asserts on.
All ten failure paths in applyProjectPatch rebuilt the same
{ success: false, projectResult: { projectPath, projectName, status: "failed", ... } }
shape by hand. Extract a local failed() helper so each branch only spells out
the error/conflictPaths/note details that actually vary.
Behavior-preserving: the helper's Omit<> parameter makes it impossible to
override projectPath/projectName/status, and results are normalized by
TaskApplyGitPatchProjectResultSchema before serialization, so key ordering is
unchanged.
…chor Ten call sites across ProjectSidebar, WorkspaceMenuBar and ArchivedWorkspaces each re-derived the same anchor for usePopoverError's showError: convert the trigger's viewport rect into document space (rect.top + window.scrollY) and offset a bare 10px past its right edge. Extract resolvePopoverErrorAnchor into usePopoverError, where the anchor type already lives, name the gutter constant once, and give the inline anchor shape a PopoverErrorAnchor alias. Behavior-preserving: every site fed the identical expression to showError, and the element-absent branches all collapse to undefined, which showError already treats as 'use default corner placement'.
The notification settings control set (notify-on-response checkbox, auto-enable checkbox, and docs link) was inlined twice verbatim: once in the hover TooltipContent and once in the click PopoverContent. Build the element once and render it in both slots so the two affordances cannot drift apart.
The three normalize* helpers were structurally identical (typeof guard + Set membership + cast + literal fallback). Collapse them into a single generic normalizePersistedChoice<T>() and regroup VALID_TIME_ZONE_MODES with its sibling constants. Behavior-preserving: same valid-value sets and same fallbacks (30d / duration / local).
The devtools-stripping, Copilot, and Coder fetch wrappers each built their effective request headers with the same six-line block (seed from the Request input, overlay init.headers). Extract it into a single local helper so the three sites cannot drift apart.
#3840 (stack-aware PR indicator) added seven more copies of the `typeof x !== "object" || x === null` guard followed by an `x as Record<string, unknown>` cast, and re-inlined the `data.repository` GraphQL descent that fetchMergeQueueEntry already had. Extract two module-private helpers (matching the existing per-module `isRecord` convention in stableStringify/toolOutputUiOnly/etc.) and route all eleven sites through them. `asRecord` keeps the original semantics exactly, including letting arrays through, so callers still reject them via the field checks that follow. Behavior-preserving; net -17 lines.
#3842 (Grok 4.6) added a second `level === "xhigh" && <predicate>` branch returning "XHIGH", directly beside the existing Anthropic one. Both guards are pure string/regex predicates and both branches return the same label, so they collapse into one condition with an `||` chain. The two situational comments merge into a single explanation of what the branch is for. Behavior-preserving: short-circuit evaluation keeps the Anthropic check first, and the fall-through to THINKING_DISPLAY_LABELS is unchanged.
The node-pty and DuckDB rebuild blocks were near-identical: same stamp-hit short-circuit, same rebuild invocation, same non-fatal failure handler, same three log lines — differing only in label, module path, and stamp file. Collapsed both into a rebuild_native_module() helper. Behavior-preserving: verified with a differential harness that runs the old and new scripts against identical fake node_modules trees across 11 scenarios (cold/warm stamps, each module alone, neither module, no electron, headless, dependency install, rebuild failure, missing npx/bunx). Output and resulting stamp files were byte-identical in every case.
isGeminiFlashThinkingLevelModelName grew one near-identical startsWith(tier) && !startsWith(tier + "-lite") clause per Flash release (3.5, 3.6, and now 3.7 in #3845). Collapse the dotted tiers into a prefix list plus a shared matcher so the next repoint is a one-line addition. The dashless gemini-3-flash tier keeps its stricter exact-or-dash boundary check and stays spelled out. Behavior-preserving: verified against the previous implementation over 200k generated model IDs with zero mismatches.
`TaskGroupListItem` computed `props.isRunActive === true || props.runningCount > 0` twice: once inside `getAggregateVisualState` to decide the "active" status-dot state, and once as the `hasRunningWork` local that drives `data-running`, the green kind icon, and the brighter title text. Extracted a module-level `groupHasRunningWork(props)` predicate and routed both sites through it so the status dot and the running-state text/icon treatment cannot drift apart. Behavior-preserving: identical expression, same call sites, no control-flow change.
The sidebar activity formatters repeated the same `count === 1 ? "" : "s"` ternary at three call sites. Extract a local `pluralSuffix` helper so the pluralization rule lives in one place. Behavior-preserving.
#3844 (generalize Coder AI Gateway to arbitrary provider instances) left resolveCoderWireCanonicalModel and resolveCoderMetadataCanonicalModel each inlining a byte-identical prelude: the `<instance>/<model>` separator scan, the empty-half guard, the resolveCoderGatewayProvider lookup over both parsed provider lists, and the null-provider guard. Extract it into a module-private splitCoderGatewayModelId helper returning { provider, modelId }. Behavior-preserving: identical guards in identical order, same null outcomes, same instance-stripped model ID. Auto-cleanup checkpoint: f3a1cba
Both the flat day list and an expanded machinery group branched on isRuleKind() to choose between TimelineRuleRow and TimelineEventRow with byte-identical props. Extract a TimelineRow dispatcher and route both call sites through it. Behavior-preserving: same components, same props, same keys.
Four writers in GitPatchArtifactService.generate() built the same "existing artifact, or a freshly seeded pending one" expression inline: the three workspace-shape guards (path/runtimeConfig/name missing) and ensureProjectArtifact. All four copies were byte-identical. Extract seedPendingArtifact(existing) plus failGeneration(error), which collapses each guard from a 20-line updateArtifact call to one line. The `??` short-circuit is preserved, so buildPendingProjectArtifacts is still only evaluated when no artifact has been persisted yet. The `!entry` guard and the outer catch keep their inline empty-project seeds: neither has a workspace entry in scope to enumerate projects from.
put() and get() each inlined the identical sha256 digest + `sha256:` prefix construction to derive a blob's content address. Extracted into a module-level blobRefFor() helper so both paths derive the ref through one expression.
The taskExecutionStatus and taskStatus switches each carried their own copies of the Queued/Running/Completed/Interrupted presentation literals, so a label or icon class could drift between the two paths. Both now read from a single outcome-keyed table.
…Service Five per-event handlers (stream delta, reasoning delta, tool-call start/delta/end) each opened with the same four lines: drop replayed events, look up the workspace's active stream, bail when there is none, then extend lastEventTimestampMs. Extract touchActiveStream() so that shared bookkeeping lives in one place and the five handlers cannot drift apart. handleStreamStart keeps its own replay guard because it creates the state rather than looking it up; handleStreamAbort/handleStreamEnd are untouched because they never had the replay guard or the watermark bump.
The nine analytics hooks each repeated the same two-step conversion: narrow the Date filters to epoch milliseconds for a stable dependency array, then rebuild Dates inside the effect. Extract toEpochMs/fromEpochMs so the round-trip (and the reason for it) lives in one place. Behavior-preserving: both helpers are literal extractions of the duplicated expressions.
agentSession.ts open-coded the same errno narrow-and-compare three times:
const errno =
typeof error === "object" && error !== null && "code" in error
? (error as { code?: unknown }).code
: undefined;
#3907 added the newest copy in clearProviderConfigFixableAbandonMarkers,
joining the two in loadAutoRetryEnabledPreference and persistAutoRetryState.
src/node/utils/fs.ts already exports isErrnoWithCode for exactly this
pattern ("Centralised because fs / runtime callers across the node layer
need this exact narrow-and-compare pattern and previously each open-coded
it"), and it is used at ~20 other node-layer call sites. agentSession was
the remaining outlier.
Behavior-preserving: isErrnoWithCode(error, "ENOENT") is equivalent to
errno === "ENOENT" (the extra `error &&` truthiness guard only excludes
null, which the original ruled out via `error !== null`), and
!isErrnoWithCode(...) is equivalent to errno !== "ENOENT". The three-way
branch in loadAutoRetryEnabledPreference keeps a single local
(isMissingPreferenceFile) so the check is still evaluated once.
|
@codex review This run rebased the stack onto current |
|
To use Codex here, create a Codex account and connect to github. |
Auto-cleanup run: rebased onto
|
Summary
This is the long-lived auto-cleanup PR. Each run, the auto-cleanup agent reviews new commits merged to
main, rebases onto the latestmain, 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–78 (all previous runs; #33 and #43 were later dropped as superseded by `main`)
Dedupe memory sweep
recordUsagecallbacks (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 emitsanalyticsIngest. Extracted into a privatemakeSweepUsageRecorder(...)helper.Dedupe the "memory scope is full" cap check (
MemoryService). ThecreateandsaveFile(new-file) paths each inlined a byte-identical block that calledstore.listFiles(), compared the count againstMEMORY_MAX_FILES_PER_SCOPE, and threw aMemoryCommandErrorwith the same message. Extracted into a privateassertScopeHasRoom(store, scope)helper.Dedupe blockquote line formatting in the bash monitor wake prompt (
bashMonitorWakeStore.ts).buildBashMonitorWakePromptrendered 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.Dedupe the
tool_searchremoval inprepareToolSearch(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-intool_searchentry from the tool record. Extracted into a module-levelwithoutToolSearch(tools)helper.Dedupe the Anthropic cache-create token extraction in
accumulateProviderMetadata(usageHelpers.ts). The function inlined the same verbose(metadata.anthropic as { cacheCreationInputTokens?: number } | undefined)?.cacheCreationInputTokens ?? 0cast twice (once for the accumulated metadata, once for the current step). Extracted into a module-privategetAnthropicCacheCreateTokens(metadata)helper.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 resolvemappedToModelaliases, bothgetThinkingPolicyForModelandhasExplicitThinkingPolicyinlined the identicalgetExplicitThinkingPolicy(resolveModelForMetadata(modelString, providersConfig ?? null))call. Extracted into a privategetExplicitThinkingPolicyForModel(modelString, providersConfig)helper.Dedupe queue entry clear-callback projection (
messageQueue.ts). After 🤖 feat: queue messages behind special sends instead of erroring (FIFO message queue) #3696 rewroteMessageQueueinto FIFOQueueEntryitems, bothgetClearCallbacksandremoveWorkspaceTurninlined the identical spread that builds aQueueClearCallbacksobject from an entry's optionalonCanceled/onAcceptedPreStreamFailurefields. Extracted into a privateentryClearCallbacks(entry)helper.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 identicalsplit(":", 2)+origin !== "openai" || !modelNamecheck twice — once for the request model and once for the resolved capability target — and the destructuredorigin/modelNamelocals were unused past their guard in both places. Extracted into a module-privateisOpenAIOriginModel(canonical)helper.Dedupe the
tool-call-execution-startemit inStreamManager(streamManager.ts). 🤖 fix: start tool elapsed timers when execute() actually runs #3716 introduced theToolCallExecutionStartEvent, emitted from two places:applyToolExecutionStart(part already stored) and the"tool-call"case that consumes apendingExecutionStartrecorded before the part landed. Both inlined the byte-identicalthis.emit("tool-call-execution-start", { type, workspaceId, messageId, toolCallId, timestamp } satisfies ToolCallExecutionStartEvent)block, differing only in thetoolCallId/timestampsource. Extracted into a privateemitToolCallExecutionStart(workspaceId, streamInfo, toolCallId, timestamp)helper.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.jsoncproviderExtrasUNDER the Mux-built provider-options namespace (short-circuiting when there are no extras, deep-merging viamergeProviderExtrasUnderMuxwhen the namespace is a plain object). The two differed only in the namespace key and the overrides source. Extracted into a module-levelmakeModelParameterExtrasMerger(namespaceKey, providerExtras)factory that returns the merger closure.Unify the legacy
tool_searchpart-rename helper (toolCatalog.ts). 🤖 fix: avoid OpenAI tool search name collision #3719 renamed the built-in tool-search tool totool_catalog_searchand added request-time rewriting of historicaltool_searchcall/result parts. It introduced two byte-identical helpers —renameLegacyToolSearchCallPart(part: ToolCallPart)andrenameLegacyToolSearchResultPart(part: ToolResultPart)— that differ only in the part type; the rename body is identical. Collapsed both into a single genericrenameLegacyToolSearchPart<T extends { toolName: string }>(part: T)and dropped the now-unusedToolCallPart/ToolResultPartimports.Trim duplicated context-cap rationale comment (
codexOAuth.ts). 🤖 fix: cap GPT-5.6 context over Codex OAuth #3724 added the GPT-5.6 family toCODEX_OAUTH_CONTEXT_WINDOW_OVERRIDESand 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.)Dedupe flat-section pinned block resolution (
pinnedReorder.ts). 🤖 feat: project-less scratch chats #3723 added a scratch branch tolocatePinnedBlockthat renders scratch chats as one flat "Chats" section, mirroring the existing multi-project branch. Both branches inlined the byte-identicalcollectFlatSectionRows(...).filter(isWorkspacePinned).map((row) => row.id)projection, theif (!pinnedIds.includes(meta.id)) return nullguard, and thereturn { fullOrder: pinnedIds, blockIds: pinnedIds }shape — differing only in theincludeRowpredicate ((row) => row.kind === "scratch"vsisMultiProject). Extracted into a privatelocateFlatSectionPinnedBlock(meta, sortedWorkspacesByProject, includeRow)helper.Dedupe JSON-wrapped tool-output unwrap (
workflowRunMessages.ts). 🤖 fix: stop terminal workflow await loops #3725 addedisTerminalWorkflowRunToolOutput, which re-inlined the byte-identicaloutput.type === "json" && "value" in outputcontainer check already used bystripWorkflowRunRecordForModelto detect the{ type: "json", value }SDK/UI wrapper before recursing on the inner value. Extracted the check into a module-privateisJsonWrappedOutput(output)helper that both functions call, moving the shared rationale into the helper's doc comment. No control flow or return-shape change.Hoist
errorTypelocal infinalizeWorkspaceTurnFromStreamError(taskService.ts). 🤖 fix: keep workspace-turn handles running through auto-retryable stream errors #3729 reworked workspace-turn stream-error settlement, and the reworked function readevent.errorTypethree times and repeated theevent.errorType != nullguard once for theexplicitRecoverycomputation and once in the recoveryif. Hoisted a singleconst errorType = event.errorTypeand routed all uses through it, deduplicating the repeated member access and null guard.ErrorEventis a Zod-inferred plain data type, so the property read has no side effects; pure behavior-preserving simplification with no control-flow change.Extract
buildSkillDescriptorhelper 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) addeduser-invocable/argument-hint/when_to_usefrontmatter and normalized them viaresolveSkillAdvertise/resolveSkillUserInvocable/resolveSkillWhenToUse. Both descriptor-building sites —readSkillDescriptor(theagent_skill_listtool) andreadSkillDescriptorFromDir(agentSkillsService discovery) — then inlined the byte-identical 7-field object literal mappingparsed.frontmatter+scopeinto anAgentSkillDescriptorbeforeAgentSkillDescriptorSchema.safeParse. Extracted the mapping into a sharedbuildSkillDescriptor(frontmatter, scope)inagentSkill.ts(co-located with theresolveSkill*helpers it calls) and dropped the now-unusedresolveSkill*imports at both call sites. Callers still runsafeParsethemselves since they handle validation failure differently. No behavior change.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 duringtask_await) reworked the delivery gate indrainBashMonitorWakesso a match is re-checked against the shown frontier while pinned to its originating process instance viaDate.parse(record.createdAt). The new non-blockinggetMonitorWakeDeliveryStatebranch and the fallbackgetSettledShownThroughOffsetbranch each inlined the identicalDate.parse(record.createdAt)call as theoriginNotAfterMsargument. Hoisted a singleconst 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.parseis pure, so the hoist is behavior-preserving.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 addedremoveWorkspaceData(archive/remove DevTools cleanup) directly beside the existingclear; both inlined the byte-identicalconst pendingLoad = this.loadingPromises.get(workspaceId); if (pendingLoad) { await pendingLoad; }guard that drains any in-flightloadFromDiskbefore mutating in-memory state so a late load cannot repopulate stale data after the mutation. Extracted into a privateawaitPendingLoad(workspaceId)helper with the shared rationale in its doc comment; both call sites keep their situational one-line comment. No control-flow change.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 callbackredirectUrifrom request headers — preferring theOriginheader (used verbatim when it parses as a URL), then falling back tox-forwarded-host/hostwith the forwarded proto (defaulting tohttp), and returningErr("Missing Host header")when no usable Host header exists. Extracted into a module-levelresolveMcpOauthRedirectUri(headers)helper that returns the resolved URI orundefined; each handler now mapsundefinedto the sameErr.startServerFlowisasync(its returned promise is passed through unawaited), so moving the call out of the origin-branchtrycannot change behavior — thetryonly ever guarded the synchronousnew URL(...)construction. No header semantics or return-shape change.Extract
getTotalTokenshelper 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 inCostsTab(session model rows),WorkspaceStore(sessiontotalTokens),tokenMeterUtils(calculateTokenMeterDatatotal),sessionUsageService(per-modeltotalTokensaccumulation), and twice incli/run.ts(budgethasTokensgates). Added agetTotalTokens(usage)helper inusageAggregator.ts, co-located with and mirroring the existinggetTotalCost(same five-component iteration;undefined→0), and routed all six sites through it. The four-component sum incli/debug/costs.ts(which omitscacheCreate) was intentionally left untouched to preserve its existing behavior.Hoist the duplicated
dedupeKeyssnapshot inremoveByDedupeKeyPrefix(messageQueue.ts). 🤖 refactor: support incremental subagent reports #3714 (incremental sub-agent reports) addedMessageQueue.removeByDedupeKeyPrefix, which spread the entry'sdedupeKeysSetinto an array once for thematchingKeysprefix filter and then re-spread the sameSetinside theentry.messages.filter(...)callback — once per message iteration — to map each message index back to its dedupe key. TheSetis not mutated until afterkeptMessagesis computed, so both reads observe the same ordered snapshot. Hoisted a singleconst 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.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 fromtask_await) added two read-time reconciliation helpers —persistRepairedSettledWorkspaceTurnandreviveRetryingWorkspaceTurn— that each open their settlement-lock body with the byte-identical guard: reload the handle viagetWorkspaceTurnandreturn currentunless 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-levelisReconciledWorkspaceTurnUnchanged(current, record)type guard, co-located with 🤖 fix: report live workspace-turn state from task_await instead of stale settlements #3738's ownisSelfHealEligibleSettledWorkspaceTurn, so the generic "compareupdatedAttoo, not just status" rationale lives in one doc comment while each call site keeps its situational note. Thecurrent is WorkspaceTurnTaskHandleRecordreturn type preserves the non-null narrowing thatreviveRetryingWorkspaceTurnrelies on after the guard. No control-flow or return-shape change.Dedupe the fire-and-forget archive-all catch (
TaskGroupListItem.tsx). 🤖 fix: archive all sidebar variants #3741 (archive all sidebar variants) added anonArchiveAllprop invoked from two places — the archive keyboard-shortcut branch inonKeyDownand theArchive all variantscontext-menu item'sonClick. Both inlined the byte-identical fire-and-forgetprops.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 localarchiveAll(buttonElement)helper so the swallow-and-surface rationale lives in one place. No control flow, arguments, or error-handling change.Extract
someDescendantAgentTaskWorkspacehelper for sticky-descendant queries (taskService.ts). [tasks] 🤖 feat: support sticky subagents #3744 (sticky subagents) added two adjacent query methods —hasStickyDescendantsandhasUnarchivedStickyDescendants— 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 privatesomeDescendantAgentTaskWorkspace(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 ownassert. The helper'sdescendant != null && predicate(descendant)guard is equivalent to the priorindex.byId.get(descendantId)?.taskSticky === trueform, so no behavior changes.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 theisKimiK3Modelpredicate, 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 ofbuildProviderOptionsthen 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: truealone falls back to the unsupported default medium effort). Comment-only; behavior-preserving.Drop the redundant
structuredOutputguard at subagent report call sites (taskService.ts). 🤖 feat: present subagent reports in chat #3742 (present subagent reports in chat) extractedformatSubagentReportUserMessage, which already omitsstructuredOutputfrom the report envelope when it isundefined(its internal!== undefinedconditional spread). Both call sites — the incrementalin_progressprogress report in theagent_reportpath and the terminalcompletedreport indeliverReportToParentUnlocked— nonetheless re-implemented that exact...(report.structuredOutput !== undefined ? { structuredOutput: report.structuredOutput } : {})guard before handing the value to the helper. Each now forwardsreport.structuredOutputdirectly, 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 asundefined.Extract
isZipMediaTypehelper 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 checkZIP_MEDIA_TYPES.includes(normalized as (typeof ZIP_MEDIA_TYPES)[number])was inlined byte-identically in bothisSupportedStagedAttachmentMediaTypeandgetSupportedStagedAttachmentMediaType. Extracted a module-privateisZipMediaType(normalized)helper so theas consttuple cast lives in one place; both call sites now readisZipMediaType(normalized). Behavior-preserving.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) computedgoalCommandBypassedForAttachments(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 resolveparsedandattachmentsidentically, so the boolean is now computed once above the routing and both inline copies dropped. Pure, behavior-preserving.Dedupe the anchored Anthropic model-id regex construction (
ai/models.ts). TheANTHROPIC_NATIVE_1M_PATTERNS/ANTHROPIC_BETA_1M_PATTERNSlists that backgetAnthropic1MContextModeeach spelled outnew 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-privateanthropicModelIdPattern(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.Dedupe MCP header telemetry flag derivation (
orpc/router.ts). Everymcp_server_config_changedcapture site recomputed thehas_headers/uses_secret_headerspayload 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 vscan) and a server-based one that prefixed both with aserver.transport !== "stdio"guard, needed becauseheadersonly exists on the HTTP-ish arm of theMCPServerInfounion. ExtracteddescribeMcpHeaderTelemetry(headers)and a thindescribeMcpServerHeaderTelemetry(server)wrapper that returns{ hasHeaders: false, usesSecretHeaders: false }for stdio — exactly whattransport !== "stdio" && …already evaluated to — so the "what counts as a secret header" rationale lives in one doc comment. Behavior-preserving; ~80 lines removed.Extract
_child_dirshelper for job folder discovery (benchmarks/terminal_bench/prepare_leaderboard_submission.py).find_job_folderswalked directory trees with three copies of the same "iterate a directory, keep only the subdirectories" pattern: two nestedfor item in <dir>.iterdir(): if item.is_dir(): job_folders.append(item)loops (the directjobs/branch and the per-artifact branch) plus afor artifact_dir in artifacts_dir.iterdir(): if not artifact_dir.is_dir(): continueskip-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 resultingjob_foldersordering are unchanged; behavior-preserving, 9 lines removed.Drop the redundant
"exec"fallback duplication fornormalizeAgentId(workspaceModeAi.ts,WorkspaceModeAISync.tsx,ChatInput/index.tsx).normalizeAgentId(value, fallback)incommon/utils/agentIds.tsalready declaresfallback: string = WORKSPACE_DEFAULTS.agentId, andWORKSPACE_DEFAULTS.agentIdis"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 passesWORKSPACE_DEFAULTS.agentId). All four now omit the argument. With the literal gone,workspaceModeAi.ts's module-privatenormalizeAgentId(agentId)wrapper existed only to supply that fallback, so it and its aliasednormalizeAgentId as normalizeWorkspaceAgentIdimport were removed in favor of importingnormalizeAgentIddirectly. Behavior-preserving: the omitted argument resolves to the identical string.Name the digest truncation bounds in the timeline mapper (Dropped — superseded upstream. This cleanup introduced module-privatetimelineMapper.ts).DIGEST_MAX_LENGTH/DIGEST_ELLIPSISconstants so thattruncateDigest's117no longer silently encoded120 - "...".length.mainthen solved the same problem better in 🤖 fix: dedupe timeline preview text and sub-agent rows, tint agent event categories #3861, which lifted the bound into an exportedTIMELINE_ROW_DIGEST_MAX_LENGTH = 120incommon/orpc/schemas/timeline.tsand shares it between the mapper andTimelinePanel(which comparestext.length === TIMELINE_ROW_DIGEST_MAX_LENGTHto detect truncated rows). Keeping this commit would have re-introduced a module-private duplicate of a constant that is now shared, so the commit was dropped during the rebase rather than force-resolved; the file keepsmain's version verbatim. Numbering is left intact so earlier entries still line up.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) addedreadMonitorWakeProcesses, which pullsrecordsoffmuxMetadataand then adisplayNameoff each record. BecausemuxMetadatacrosses the oRPC boundary asany, both reads spelled out the same defensive guard inline —typeof x === "object" && x !== null ? (x as Record<string, unknown>)[field] : undefined— and the pre-existingreadMuxMetadataFieldin 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-privatereadObjectField(value, field): unknownand rewrote all three sites in terms of it, following the precedent already set bygetWorkflowResultFieldincommon/utils/workflowRunMessages.tsandreadPreviewTextintimelineService.ts. Behavior-preserving: all three guards admitted exactly the same shapes (non-null objects, arrays included, functions excluded bytypeof), and each caller still applies its own narrowing afterwards (typeof value === "string"for the metadata fields,Array.isArrayforrecords), so every input maps to the same result as before. 14 lines removed, 11 added; no new exports.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 sevenwindow.matchMedia(...)call sites across five renderer files (the sidebar width override, bothhandleOpenTerminalpopout branches, the menu bar'sisTouchMobileScreen,UserMessage'sisMobileTouch, and the composer'suseStateinitializer plus itschange-listener effect). Each copy was independently responsible for staying in sync with the matching@mediablock inglobals.css, which is the actual source of truth for the styles these branches mirror. Hoisted to an exportedMOBILE_TOUCH_MEDIA_QUERYinsrc/constants/layout.ts— directly aboveMOBILE_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 oversrc/), so eachmatchMediacall 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.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 localwrapperClassNameconstants plus one inlineclassNameon 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 withwrap={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 toCHAT_DOCK_TOAST_OVERLAY_CLASSinsrc/constants/layout.ts, directly belowCHAT_DOCK_GUTTER_CLASSwhose15pxinset it mirrors. Behavior-preserving: the twowrapperClassNamesites now reference the identical string they previously defined locally, and the composer's stack composes it ascn(CHAT_DOCK_TOAST_OVERLAY_CLASS, "flex flex-col gap-2")— the same utility set with no conflicting utilities, sotailwind-mergeyields the same computed styles (only the class attribute's token order shifts). The value stays a literal string inlayout.tsbecause Tailwind scans source text, the same constraint already documented onCHAT_DOCK_GUTTER_CLASS.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-dockmousedownhandler gated on the bare magic numberevent.button !== 0— the same primary-button guard the diff review drag-select handler already spelled out inline. Named the check once asisPrimaryMouseButton(event)inbrowser/utils/events.ts, next to the existingisEventFromDialogPortal/stopKeyboardPropagationevent 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.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 sharecomposerPickerOptionClasswithAgentModePickerand to accent the selected row. In the process the row grew to recomputevalue === modelfour separate times — for the option class'sisSelected, foraria-selected, for theProviderIcon'stext-accent/text-mutedternary, and for the model-name span's accent — plusindex === highlightedIndextwice (data-highlightedand the option class'sisHighlighted). Hoisted both intoisSelected/isHighlightedlocals at the top of themapcallback, matching the naming the siblingAgentModePickeralready uses for the same two states, so the row's state is named once and the four accent/ARIA consumers cannot drift apart.Share the workspace footer pill class (
WorkspaceFooterBar.tsx). 🤖 feat: link the footer GitHub slug to the repository #3762 turned the footer's GitHubowner/reposlug into a link and — per its own PR description — styled it "to match the siblingLast promptpill", 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-levelFOOTER_PILL_CLASS: the anchor consumes it directly, and the button composes it ascn(FOOTER_PILL_CLASS, "cursor-pointer border-0 bg-transparent").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) addedinstallInactiveAnimationPauseand 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-linetry/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. ExtractedinstallInactiveAnimationPauseSafely()into the installer's own module, directly beneathinstallInactiveAnimationPause, 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.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.MessageRendererroutes onmessage.bashMonitorWake != nullto pickBashMonitorWakeMessageoverUserMessage;ChatPane'suserMessageNavigationByHistoryIdmemo independently filters onmessage.bashMonitorWake == nullso 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. ExtractedisBashMonitorWakeMessage(message: DisplayedUserMessage)intomessageUtils, alongside the existingDisplayedMessagepredicates (shouldShowInterruptedBarrier,computeBashOutputGroupInfos), and pointed both call sites at it. Both files already imported frommessageUtils, so this adds no new module edge.Dedupe the monitor disposition branch in
terminate()(backgroundProcessManager.ts). 🤖 fix: cancel stale background monitor wakes #3776 (cancel stale background monitor wakes) gaveBackgroundProcessManager.terminate()a newoptions.monitorDispositionparameter and, to honour it, inlined the same five-lineif (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 thetry. 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 aresolveMonitorForTermination(proc, shouldFlush)private helper placed next tostopMonitor/cancelMonitor, so the disposition rule lives in one place and the two exit paths cannot drift apart.Share the queued-message action button classes (Dropped during an earlier run's rebase — superseded upstream. 🤖 feat: refine transient transcript interactions #3790 rewrote this action row wholesale:QueuedMessage.tsx).Editshrank to anh-6/px-1.5button andSend nowmoved into a new queue-status dropdown, so both copies of the deduped class string are gone frommainand the shared constant had no second caller left. The file now matchesmainbyte-for-byte. Original rationale: 🤖 fix: restore queued message text hierarchy #3781 restored the queued draft's text hierarchy by dropping theEditandSend nowlabels fromtext-xstotext-[11px]— and had to make that one-token edit twice, because both buttons inlined a byte-identicalflex h-7 items-center gap-1.5 rounded-md px-2.5 text-[11px] font-medium transition-colorsrun of geometry/typography utilities and differed only in their colour treatment (text-muted+ hover forEdit;bg-pending/10+ disabled states forSend now). Hoisted the shared half into aQUEUED_ACTION_BUTTON_CLASSNAMEconstant and composed each button's colours on top withcn(...), so the next typography tweak lands in one place instead of drifting between the two.Extract
parseSubagentReportFromMessagehelper 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 byparseSubagentReportEnvelope(text). 🤖 fix: avoid duplicate subagent completion responses #3783 (avoid duplicate subagent completion responses) added the third and fourth copies inTaskService.findProgressRespondedTaskIdsandTaskService.hasAcceptedSubagentProgressReport, joining the pre-existing copies in TaskService's sibling synthetic-report discovery andAgentSession.isVisibleCompletedSubagentReportMessage. Extracted the projection intoparseSubagentReportFromMessage(message)insubagentReportEnvelope.ts, co-located with the string parser it wraps, and routed all four sites through it. TheMuxMessageimport isimport type, so the module stays runtime-dependency-free;parseSubagentReportEnveloperemains 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 rewroteTaskService.findProgressRespondedTaskIdsand deletedhasAcceptedSubagentProgressReport, so this run's rebase dropped those two hunks and kept main's replacement code verbatim.)Drop stale
userOverridablereferences from the experiments UI (ExperimentsSection.tsx,useExperiments.ts). Two comments still described auserOverridableexperiment flag that no longer exists anywhere insrc/: the Settings list actually filters onshowInSettings !== false, andisExperimentEnabledreturnsundefinedwhenever no explicit localStorage override exists, not only for "user-overridable" experiments. Comment-only change.Reuse the
isTaskAwaitMessagepredicate in the transcript projection (transcriptRenderProjection.ts). 🤖 fix: refine task wait transcript presentation #3788 addedtask_awaitpoll grouping along with a small family of helpers, three of which re-inlined the samemessage.type === "tool" && message.toolName === "task_await"shape test thatisTaskAwaitMessagealready performs —getTaskAwaitResultEntries,hasTaskAwaitCallFailure, andhasTaskAwaitCallInterruption. WideningisTaskAwaitMessageinto a type predicate (message is Extract<DisplayedMessage, { type: "tool" }>) lets those three delegate to it and still read the tool-onlystatus/resultfields, so the shape test now lives in exactly one place.Dedupe the non-blank string coercion in
taskReportLinking(taskReportLinking.ts). Every field this module pulls out of persisted tool args/results is typedunknown, so each read hand-rolled the same three-part check:typeof x === "string",x.trim().length > 0, then usex.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 newgetAgentTypeFromTaskToolArgs,getBashSpawnTaskId, andgetBashSpawnInfoFromArgs). All seven now call one file-localcoerceNonBlankStringhelper.Drop the redundant
agentTypere-check intask_awaitawaited rows (TaskToolCall.tsx). Whiletask_awaitis still in flight,TaskAwaitToolCallbuilds oneawaitedRowsentry per pending task and resolves each row's agent type withresolvePersistedAgentId(metadata, "")— an intentionally empty fallback. The very next line already collapses that empty string toundefined(resolvedAgentType.length > 0 ? resolvedAgentType : undefined), but theawaitedRows.push({ ... })literal then re-tested the same value withagentType && agentType.length > 0 ? agentType : undefined. That second guard cannot change anything:undefinedshort-circuits to theundefinedarm, and any string that survived the first line is non-empty by construction. Replaced with theagentType,shorthand, and left a comment on the normalizing line recording why the empty fallback exists so the intent survives the removed guard.Hoist the duplicated
isGrok45Modelcheck in xAI model creation (providerModelFactory.ts). 🤖 fix: honor mapped Grok 4.5 aliases #3804 (mapped Grok 4.5 aliases) introduced acapabilityModellocal fromresolveModelForMetadataand calledisGrok45Model(capabilityModel)to choose betweenprovider.responses(modelId)andprovider.chat(modelId). 🤖 feat: default Grok Responses to store=false for ZDR parity #3807 (thestore=falseZDR default) then added a second, byte-identicalisGrok45Model(capabilityModel)call three lines below, to gateinjectGrok45StoreDefault. Both read the sameconst, andisGrok45Modelis a pure regex test over a prefix-stripped string, so the two evaluations are guaranteed to agree. Folding them into a singleconst isGrok45drops the redundant call and lets the ternary collapse onto one line. It also restores consistency withbuildProviderOptions, which already keeps exactly such anisGrok45local for the same predicate. Net+2 −3.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 thisunknownmuxMetadata a bash-monitor wake?" in two places —MessageQueue.isNextEntryBashMonitorWake(queue head) andAgentSession.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 | undefinedin the other. The second is the worse of the two: it asserts aunknownfield into the fullMuxMessageMetadataunion purely to reach?.type.messageQueue.tsalready 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. ExtractedisBashMonitorWakeMetadataalongside those siblings, exported it, and reused it at both call sites. The declaredBashMonitorWakeMetadatainterface carries onlytype, matching what the guard actually validates — neither caller readsrecords, 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.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 privateextractErrorMessage, 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 nosuccessflag thatdisplayedMessageBuilderreconstructs for a failure inside a nestedcode_execution/PTC call.AgentSkillListToolCall.toSkillListViewalready inlined the same resolution — sameisToolErrorResultfirst branch, same!("success" in x)+typeof error === "string"fallback — and both files carried the same rationale comment. Extracted intoextractToolErrorMessage(result)inShared/toolUtils.tsx, co-located with theisToolErrorResult/isFailedToolOutputfamily it belongs to, and routed both cards through it, dropping their now-unusedisToolErrorResultimports.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 gaveTaskServicetwo 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) andconsumeRespondedAgentTerminalAttention(the responded-scan that decides whether a pending wake is already answered). Each inlined the identicalmessage.partsflatten — sameExtract<…, { type: "text" }>predicate, same.map((part) => part.text), same"\n"separator — before handing the string toparseTerminalSubagentTaskId. ExtractedjoinMessageText(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.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.warnwith the owning plugin directory as prefix, then push aseverity: "error"diagnostic describing the same problem. That pair was hand-written three times across two functions. ExtractedpushErrorDiagnosticso the log prefix, the severity, and the diagnostic shape live in one place.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 extractedCreationProjectSelectso the scratch page could reuse the project picker, but it left the options construction inlined at both call sites: each independently didArray.from(userProjects.keys()).map((path) => ({ value: path, label: formatProjectHierarchyLabel(path, userProjects) })). ExtractedprojectSelectOptions(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 rendersselectedLabelas 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.Share the MCP raw-tool wrapper across both startup paths (
mcpServerManager.ts). 🤖 feat: migrate MCP client to official TS SDK v2 with 2026-07-28 spec support #3822's SDK v2 migration gave 2026-07-28 connections an on-demandrefreshTools, and because refreshing re-wraps a freshly listed tool set, each startup path needed its wrapping options in a reusable place. Both paths solved it the same way and landed on byte-identical local lambdas —startStdioInstanceat the old L2191 andstartRemoteInstanceat the old L2436, eachwrapMCPTools(raw, { onActivity, onClosed: () => { if (instanceRef.current) instanceRef.current.isClosed = true; } }), closing over identically-typedonActivity: () => voidandinstanceRef: { current: MCPServerInstance | null }locals. Hoisted to a module-levelcreateRawToolWrapper(onActivity, instanceRef)returning that same closure, so both call sites collapse toconst wrapRawTools = createRawToolWrapper(onActivity, instanceRef).Share the augmented exec-failure error construction (
disposableExec.ts). 🤖 feat: back up Mux settings to a git repository #3767 neededexecFileAsyncto grow abort signals, timeouts, output caps, and process-tree kills, and that growth left the module building its rejection value in three separate places: the new pre-execution abort guard, plus theclosehandlers ofexecAsyncandexecFileAsync. Each one hand-wrote the same inlineas Error & { code, signal, stdout, stderr }cast and then assigned the same four properties. ExtractedcreateExecError(message, outcome)behind a namedExecErrorinterface so the rejection contract is declared once instead of three times.Dedupe the failed project-result construction in
applyProjectPatch(tools/task_apply_git_patch.ts). The multi-project patch-apply path grew one guard at a time — path-component validation, artifact resolution, expected-HEAD checks, dry-run worktrees, dirty-overlap rejection, conflict-recovery notes — and every new guard hand-wrote another{ success: false, projectResult: { projectPath, projectName, status: "failed", ... } }literal, reaching ten copies of the same six-line preamble inside a single function. Extracted a localfailed(details)helper whose parameter isOmit<TaskApplyGitPatchProjectResult, "projectPath" | "projectName" | "status">, so the project identity and the"failed"status cannot be overridden from a call site and each branch now spells out only theerror/conflictPaths/failedPatchSubject/notethat actually vary. Net −59 lines; the two success returns (status: "applied") are deliberately left inline, since they share only two of the fields.Dedupe the popover-error anchor math (
hooks/usePopoverError.ts+ProjectSidebar.tsx,WorkspaceMenuBar.tsx,ArchivedWorkspaces.tsx). Ten call sites each re-derived the same anchor before handing it tousePopoverError'sshowError: convert the trigger'sgetBoundingClientRect()into document space viarect.top + window.scrollY, then offset a bare10pastrect.right. The clones had drifted into four different spellings of one expression — alet anchor+if (buttonElement)block (×3 inProjectSidebar), an IIFE inside a ternary, arect ? {…} : undefinedinline ternary (×2 inWorkspaceMenuBar), and anif (anchorEl) showError(…, {…}) else showError(…)if/else inArchivedWorkspaces— which is exactly the shape that silently disagrees once someone tunes the gutter. ExtractresolvePopoverErrorAnchorintousePopoverError, which already owns the anchor type and theshowErrorsignature that consumes it, name the gutter asPOPOVER_ERROR_ANCHOR_GAP_PX, and give the repeated inline{ top: number; left: number }shape aPopoverErrorAnchoralias.Dedupe the notification settings panel (
WorkspaceMenuBar.tsx). The notify-on-response control set — the twoCheckboxlabels plus the "Agents can also notify on specific events" docs paragraph — was inlined twice verbatim: once inside the hoverTooltipContentand once inside the clickPopoverContentof the samePopover. The two copies were 34 lines each and byte-identical once indentation is stripped. Extracted to a singlenotificationSettingsContentelement rendered in both slots. Net −28 lines.Dedupe the persisted-choice normalization (
Analytics/AnalyticsDashboard.tsx). The dashboard header persists three independent selections — time range, timing metric, and (as of 🤖 feat: add analytics timezone mode #3839) timezone mode — and each had its ownnormalize*function with an identical body: atypeof value === "string"guard, aSetmembership test, a cast to the union, and a literal fallback. A single genericnormalizePersistedChoice<T extends string>(value, validValues, fallback)now covers all three, so the next persisted dropdown adds a call instead of a fourth copy of the same three lines.Extract
mergeFetchHeadersfor provider fetch wrappers (services/providerModelFactory.ts). Three of the file'sfetchwrappers — the DevTools-header stripper ingetProviderFetch, the Copilot wrapper, and the Coder wrapper added by 🤖 feat: add Coder provider with "Login with Coder" OAuth #3835 — each opened with the same six-line block to compute the request's effective headers: seed aHeadersfrominput.headerswheninput instanceof Request, then overlayinit.headersso per-call values win. One local helper now serves all three.Dedupe unknown-to-record narrowing in
PRStatusStore(browser/stores/PRStatusStore.ts). 🤖 feat: stack-aware PR indicator with gh-stack dropdown #3840 (stack-aware PR indicator) addedparseStackViewOutputandmergeStackPullRequestMetadata, which walk untrustedgh stack view/gh api graphqloutput field by field. Between them they introduced seven new copies of the same two-step narrowing — anif (typeof x !== "object" || x === null) return <bail>;guard immediately followed by anx as Record<string, unknown>cast — andmergeStackPullRequestMetadatare-inlined the three-leveldata→repositoryGraphQL envelope descent thatfetchMergeQueueEntryalready had. Extracted a module-privateasRecord(value)returningRecord<string, unknown> | null(matching the existing per-moduleisRecordconvention instableStringify.ts,toolOutputUiOnly.ts,transcriptShare.ts, andworkflowReportPayload.tsrather than adding a cross-layer import), plus agraphqlRepositoryFields(raw)that composes it for thedata.repositorydescent. Routed all eleven sites in the file through them, including the four that predate 🤖 feat: stack-aware PR indicator with gh-stack dropdown #3840 (summarizeStatusCheckRollup,parseMergeQueueEntry, and the two levels insidefetchMergeQueueEntry).asRecordreproduces the original predicate exactly — notably it still lets arrays through, sincetypeof [] === "object"— so every call site keeps rejecting arrays via the field checks that follow it rather than at the guard. Net −17 lines.Dedupe the native-xhigh label branches in
getThinkingDisplayLabel(common/types/thinking.ts). 🤖 feat: make Grok 4.6 the default Grok model #3842 (Grok 4.6 as the default Grok) taught the label helper that Grok 4.6 exposesxhighas a real reasoning effort, and did so by appending a secondif (level === "xhigh" && <predicate>(modelString)) return "XHIGH";line directly beneath the existing Anthropic Opus 4.7+ one. Both guards repeat the samelevel === "xhigh"test and both branches return the same literal, so they collapse into one condition with an||chain over the two model predicates, and the two one-line situational comments merge into a single explanation of what the branch is for.anthropicSupportsNativeXhighandisGrok46Modelare pure regex tests over the prefix-stripped model string, and||short-circuits in the original Anthropic-first order, so the fall-through toTHINKING_DISPLAY_LABELSis unchanged.Extract
rebuild_native_modulein the postinstall script (scripts/postinstall.sh). The script's last two sections rebuilt node-pty and DuckDB for Electron's ABI with structurally identical blocks: the same stamp-file short-circuit and "already rebuilt … – skipping" log, the same$REBUILD_CMD @electron/rebuild -f -m <path>invocation, the same four-line non-fatal failure handler (warn, point atmake rebuild-native,exit 0), the sametouchof the stamp, and the same "rebuilt successfully (cached at …)" log. They differed only in three values — display label, module path, and stamp file — so they collapse into onerebuild_native_module label module_path stamp_filehelper called twice, leaving each call site'selsebranch ("package missing – skipping") in place. The failure path deliberately keepsexit 0inside the function rather thanreturn-ing: a rebuild failure must abort the whole script with success (a broken rebuild degrades desktop terminal/DB features but must not failbun install), and that intent is now stated in a comment above the helper.Dedupe the Gemini Flash thinking-level tier prefixes (
common/utils/thinking/policy.ts).isGeminiFlashThinkingLevelModelNameanswers "does this bare model ID use Google'sthinkingLevelconfig instead of Gemini 2.xthinkingBudget?", and it had grown one hand-writtennormalized.startsWith(tier) && !normalized.startsWith(tier + "-lite")clause per Flash release — 3.5, then 3.6, and now 3.7 in 🤖 feat: make Gemini 3.7 Flash the default Gemini Flash model #3845. The three dotted tiers now live in aGEMINI_FLASH_THINKING_LEVEL_TIERSlist consumed by a singlematchesFlashTierExcludingLitematcher, so the next Flash repoint is a one-line list addition rather than another copy of the prefix/-litepair. The dashlessgemini-3-flashtier is deliberately not folded into the list: it uses a stricter exact-or-dash boundary check (=== "gemini-3-flash" || startsWith("gemini-3-flash-")), and generalizing that to the dotted tiers — or relaxing it to a plain prefix match — would be a logic change rather than a dedupe, so it stays spelled out with a comment saying why.Dedupe the task-group running-work predicate (
browser/components/ProjectSidebar/TaskGroupListItem.tsx).TaskGroupListItemspelled outprops.isRunActive === true || props.runningCount > 0in two places: once insidegetAggregateVisualStateto decide whether the aggregate status dot renders"active", and once as thehasRunningWorklocal that drives the row'sdata-runningattribute, the green (text-content-success) kind glyph, and the brightertext-content-primarytitle. Extracted a module-levelgroupHasRunningWork(props)predicate and routed both sites through it, so the status dot and the icon/title running treatment cannot drift apart if the definition of "working" changes again.Dedupe the plural-suffix logic in the sidebar activity labels (
browser/components/AgentListItem/AgentListItem.tsx). 🤖 feat: add setting to hide sub-agents in the left sidebar #3847 (hide sub-agents in the sidebar) addedformatHiddenSubAgentsPresentation, which brought two fresh copies of thecount === 1 ? "" : "s"ternary into a file that already inlined it informatSubAgentCount— three hand-written copies of the same regular-noun pluralization rule, each spelled against a differently-named count expression (count,agentCount,summary.subAgentCount), which is what kept them from reading as duplication. Extracted a module-levelpluralSuffix(count)helper and routed all three sites through it.Extract
splitCoderGatewayModelIdfor the Coder gateway canonical-identity resolvers (common/constants/coderOAuth.ts). 🤖 feat: generalize Coder AI Gateway integration to arbitrary provider instances #3844 generalized the Coder AI Gateway to arbitrary provider instances, and its two canonical-identity resolvers —resolveCoderWireCanonicalModel(wire origin) andresolveCoderMetadataCanonicalModel(pricing/capability identity) — each opened with a byte-identical 13-line prelude: scan for the<instance>/<model>separator, reject IDs with an empty instance or model half, resolve the instance name throughresolveCoderGatewayProviderover both parsed provider lists, and bail when no provider matches. Extracted into a module-privatesplitCoderGatewayModelId(gatewayModelId, metadata)returning{ provider, modelId }ornull, and routed both resolvers through it.Dedupe the timeline rule-vs-event row dispatch (
browser/features/RightSidebar/Timeline/TimelinePanel.tsx). 🤖 feat: collapse scheduler machinery noise in the timeline panel #3858 taught expanded machinery groups to render the rule rows they absorb, which added a second copy of a branch the flat day list already had: testisRuleKind(getTimelineEventKind(event)), then render eitherTimelineRuleRoworTimelineEventRowwith a byte-identicalkey/event/selected/onSelectprop set. The two components have had identical prop signatures all along, so the branch was pure dispatch. Extracted aTimelineRowcomponent that performs it once and routed both call sites through it.Extract
buildCommandReplacementfor slash suggestion trailing spaces (browser/utils/slashCommands/suggestions.ts). Two of the three suggestion builders in this file inlined the same two-step expression to decide whether a completion inserts a trailing space:const appendSpace = definition.appendSpace ?? true;followed by`${base}${appendSpace ? " " : ""}`.buildTopLevelSuggestionsused it for top-level commands, andbuildSubcommandSuggestionsused it for subcommands — the only difference being the replacement base (/${key}vs. the joined/${prefixTokens} ${key}path). Extracted a module-levelbuildCommandReplacement(base, definition)that owns theappendSpace ?? truedefault, and moved the rationale for that default (the caret should land where arguments go;appendSpace: falseopts out for commands that are complete on their own) into its doc comment. The top-level callback also had`/${definition.key}`written twice — once fordisplay, once as the replacement base — so it now hoists a singledisplaylocal and passes it as the base, which is the same string it always built.Dedupe the pending patch-artifact seed in
GitPatchArtifactService.generate()(node/services/gitPatchArtifactService.ts). Four writers insidegenerate()each inlined a byte-identical 13-line expression to resolve "the artifact already on disk, or a freshly built pending one": the three workspace-shape guards (workspacePathmissing,runtimeConfigmissing,workspaceNamemissing) and the per-projectensureProjectArtifactupdater. The three guards additionally repeated the whole surroundingupdateArtifact((existing) => failPendingProjectArtifacts({ artifact: …, error: …, updatedAtMs: nowMs }))call, differing only in the error string. Extracted aseedPendingArtifact(existing)closure (defined immediately afterconst ws = entry.workspace;, so it captures the narrowedentry/wsthe seed needs) plus afailGeneration(error)wrapper, collapsing each guard from 20 lines to one. The!entryguard and the outercatchintentionally keep their inlineprojectArtifacts: []seeds — neither has a workspace entry in scope to enumerate project repos from, and the outer catch also stampsupdatedAtMs: Date.now()rather thannowMs.Dedupe the blob content-address computation in
BlobStore(node/utils/journal/blobStore.ts). 🤖 feat: add shared agent-foundation layer (event spine, journal kit, sandbox host, capability grants) #3865 (shared agent-foundation layer) added the journal kit's content-addressedBlobStore, whoseput()andget()each inlined the same byte-identical address derivation (sha256 hex digest of aBuffer, prefixed withsha256:) and differed only in what they did with the result:put()names new content before writing it, whileget()re-derives the address of the bytes just read and compares it against the requested ref to detect corruption. Extracted ablobRefFor(bytes)helper so the write path and the verification path cannot drift — if the two ever disagreed, every blob would fail its own integrity check on read.Dedupe the sub-agent status presentations into one outcome-keyed table (
browser/components/SubAgentTasksDecoration/SubAgentTasksDecoration.tsx). 🤖 feat: port anti-slop lint rules and enable switch exhaustiveness checking #3868 rewrotegetSubAgentStatusPresentationso the optionaltaskExecutionStatusis narrowed before itsswitch, leaving two sibling switches consulted in sequence. Four presentations — Queued, Running, Completed, Interrupted — were spelled out twice, once per switch, each repeating its label, itslucide-reacticon, and its icon class. Both switches satisfy exhaustiveness independently, so the duplication was invisible to the compiler. Extracted a module-levelSUB_AGENT_STATUS_PRESENTATIONStable keyed by outcome rather than status (the two status unions are not parallel:taskExecutionStatus: "starting" | "running"both render Running, whereastaskStatus: "starting"renders a distinct Starting), plus a namedSubAgentStatusPresentationinterface reused as the function's return type.Dedupe the replay/lookup/watermark prelude in
SessionTimingService(node/services/sessionTimingService.ts). Five of the eight stream-lifecycle handlers —handleStreamDelta,handleReasoningDelta,handleToolCallStart,handleToolCallDelta,handleToolCallEnd— each opened with the same four lines encoding three invariants: replayed events must never re-time a stream, an event for a workspace with no active stream has nothing to update, and anything surviving both still counts as stream activity. Extracted a privatetouchActiveStream(data)returning the resolvedActiveStreamStateornull, typedPick<StreamDeltaEvent, "replay" | "workspaceId" | "timestamp">so all five event shapes match structurally. Named "touch" because the lookup deliberately advances the watermark.handleStreamStart(creates state rather than resolving it) andhandleStreamAbort/handleStreamEnd(never had the guard or the bump) were deliberately left alone.Dedupe the analytics date-filter epoch round-trip (
browser/hooks/useAnalytics.ts). All nine analytics data-fetching hooks re-derived the sameDate-> epoch-ms ->Dateround-trip, 18 call sites in total, which exists so each hook'suseEffectdependency array holds primitives rather than aDatereference that is fresh on every render. Extracted module-leveltoEpochMs/fromEpochMshelpers as literal extractions of the duplicated expressions, and recorded that previously-unwritten rationale in a comment.Dedupe the budget-exceeded reporting in the
runCLI (cli/run.ts).xum run --budgetenforced its limit from three independent points in the chat listener (stream-end,session-usage-delta, and the pre-send precheck), and each one inlined the same three statements: set thebudgetExceededflag, emit the machine-readablebudget-exceededJSON line, and write the yellow human-readable line — including three copies of the format string that defines the user-visible message. Extracted onereportBudgetExceeded(cost, budgetLimit)closure beside thebudgetExceededdeclaration it writes to; each site becomes a single call. The stream-end site's deliberate omission ofsession.interruptStream(...)stays at the call site, and the similar-lookingbudget-errorpreludes were left alone because their messages genuinely differ.Dedupe the oversized pass-through gate in
transformMCPResult(node/services/mcpResultTransform.ts). 🤖 fix: cap MCP tool result text output before it enters history #3891 capped MCP tool result text at 64KB and inlined the same measure-compare-log prelude in both non-standard pass-through branches (toolResultpassthrough and content-less result objects); the copies differed only in thelog.warnmessage and the notice wrapper. ExtractedoversizedPassthroughBytes(result, logMessage): number | nullbesidejsonByteLength, returning the measured size so each caller can still build its own notice.Replace
agentSession.ts's three open-coded errno checks with the centralizedisErrnoWithCodehelper (node/services/agentSession.ts). 🤖 fix: re-enable retry when provider config changes after credential errors #3907 added a third copy of the sametypeof error === "object" && error !== null && "code" in errornarrow-and-compare, joining pre-existing copies inloadAutoRetryEnabledPreference()andpersistAutoRetryState().node/utils/fs.tsalready exportsisErrnoWithCodefor exactly this pattern and it is used at ~20 node-layer call sites, soagentSession.tswas converted rather than growing a fourth local variant.This run
Cleanup #79 — dedupe the canonical/legacy plan-path resolution in the runtime helpers (
src/node/utils/runtime/helpers.ts).readPlanFile()andhasNonEmptyPlanFile()each opened with the same three lines:#3905 (
make .xum canonical for project metadata) edited both copies in the same commit — renaming themuxHomelocal toxumHomein each — which is exactly the "same thing had to be edited twice" signal this agent looks for.The duplication also encodes a real invariant.
hasNonEmptyPlanFile()is the cheap existence probe that decides whether plan-mode UI advertises a plan;readPlanFile()is the reader that serves the content and transparently migrates the legacy by-workspace-id file onto the canonical per-project path. If the two ever disagreed about where a plan lives, the probe would advertise a plan the reader could not find, or vice versa.Extracted a module-private
getPlanFilePaths(runtime, workspaceName, projectName, workspaceId)that returns{ planPath, legacyPath }, with a short comment recording why both readers must resolve through one place. Both call sites now destructure from it. Net: 6 lines removed, 12 added (the helper plus its doc comment); the two function bodies each lose their three-line prelude.Equivalence. The helper makes the same three calls, in the same order, with the same arguments, and is invoked at the same point in each function. In
hasNonEmptyPlanFile()it still runs after the defensive empty-identifier guard, soruntime.getXumHome()is still not called for invalid inputs. Neither function referenced thexumHomelocal anywhere else, and no exported signature, log message, or returned value changes.movePlanFile()in the same file was deliberately left alone: it resolves two canonical paths (old and new workspace name) and no legacy path, so it does not fit this shape and folding it in would mean a helper with an unused return field.Considered and rejected
workspaceService.ts(~L3745getPlanPathsForWorkspace-style filtering and ~L9660 plan cleanup) — also renamedmuxHome->xumHomeby 🤖 refactor: make .xum canonical for project metadata #3905, but the first site additionally needs the home value itself for itsxumHome.startsWith("~")tilde-expansion branch, so a shared helper would have to returnxumHomeas a third field that the other three call sites ignore. Deduping across two modules with a widened return shape is more than one extremely-low-risk step; deferred rather than forced.listProjectMetadataRelativePaths/getCanonicalProjectMetadataRelativePath(common/compat/legacyMux.ts, 🤖 refactor: make .xum canonical for project metadata #3905) — the two share a one-linerelativePath.length > 0 ? ... : ""suffix computation. Extracting a helper for a single ternary would add indirection without removing meaningful duplication.compactionHandler.ts/agentSession.tsand lands with 88 lines of new tests. The changed branches are single-use; there is no repeated block to extract.xum_agents_*,xum_config_*,xumignore.ts) — the legacy/canonical fan-out is already funneled through the sharedPROJECT_METADATA_DIR_NAMES/listProjectMetadataRelativePathscontracts incommon/compat/legacyMux.ts. Nothing left worth a follow-up extraction.Validation
make static-checkpasses end to end: ESLint, bothtsgoprojects, Prettier, shell/Python formatting, shellcheck, hadolint, the generated-file and docs-sync checks, and the code-to-docs link check.bun test src/node/utils/runtime/helpers.test.tspasses (8 tests, 28 assertions). That suite covers both converted functions' path resolution directly, including the cases that pin the invariant this cleanup protects:readPlanFilefalling back to the local, SSH, and Docker runtime-home legacy paths "not a hardcoded~/.xumroot", and the resolved-before-quoting migration command.No test was added. The change moves three lines into a private helper with no behavioral delta; a test asserting that
helpers.tscallsgetPlanFilePathswould be tautological.Rebase note
This run rebased the 74-commit stack onto
cabfd0a0(currentmain), picking up #3912, #3913, #3905, and #3916. The rebase was clean — no conflicts, no cleanup commits dropped.Risks
Very low for this run's change: one file, one module-private helper, no control-flow change, no exported API change, and no user-visible string touched. The extracted lines are three pure path-construction calls with no I/O.
The affected product area is plan-file resolution (plan-mode content reads, the legacy-to-canonical plan migration, and the has-a-plan probe behind plan-mode UI). A regression would require the helper to resolve a different path than the inlined code, which is ruled out by the call-for-call equivalence above and pinned by the runtime-home fallback tests.
The broader risk profile of the stack is unchanged: 79 behavior-preserving refactors, intended to be reviewed and squash-merged as one. The branch sits directly on current
main.Auto-cleanup checkpoint: cabfd0a
Generated with
xum• Model:anthropic:claude-opus-5• Thinking:xhigh