Skip to content

test(e2e): port view-state suite with reload and rehydration coverage (vps2 F6) - #1565

Open
easonLiangWorldedtech wants to merge 439 commits into
Zoo-Code-Org:mainfrom
easonLiangWorldedtech:vps2/f6-e2e-viewstate
Open

easonLiangWorldedtech wants to merge 439 commits into
Zoo-Code-Org:mainfrom
easonLiangWorldedtech:vps2/f6-e2e-viewstate

Conversation

@easonLiangWorldedtech

@easonLiangWorldedtech easonLiangWorldedtech commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Draft PR — vps2 unit F6 (e2e view-state suite + fresh-tab API surface).

Tracking: fork issue 41 (vps2 series ledger). Upstream issue: #1564 (this series' gap record; the review lineage is #929, A5 re-review minor 2). Port source: the CS worktree apps/vscode-e2e (e9a44b2, the A5 re-review state) — 3 files byte-identical to CS; the reload test is F6-authored.

What this unit does at head 56264d1

  1. apps/vscode-e2e (the F6 port, no production code) — modes.json debug-mode fixture entry; runTest.ts addViewStateFixtures wiring + model-agnostic post-switch predicates; fixtures/view-state.ts per-task marker fixtures + the three-panel ten-rounds isolation plan; suite/view-state.test.ts (2 CS tests byte-identical + the F6-authored sidebar webview reload/rehydration test).
  2. src/activate/registerCommands.ts — the exported createClineTabPanel (fresh tab, never reuses the tracked panel) with the view-state listener that re-tracks the active panel; this round it was reconciled with main's typed options + shared WebviewFocusTracker (openClineInNewTab keeps the serialized flow; createTabPanelUnlocked delegates the fresh-creation body to createClineTabPanel).
  3. src/extension/api.ts — startNewTask keeps the fresh-tab createClineTabPanel call (wired with the sidebar provider's tracker) instead of main's factory.createInNewTab, so API tabs never evict a tracked tab's task; the required ClineProviderFactory constructor parameter is kept; the context field the auto-merge dropped is restored; the readiness race (viewStateReadiness + isDisposed) members are restored.
  4. src/core/webview/ClineProvider.ts — loadViewState mutation-marker load-merge (this unit's reviewed design: a completed mutation, including a clear, wins over stale persisted values), the setValue mode validation, and the corrupt-entry pruning with its exact drop log. This round the F1a view-identity review fixes are ported on top (temporary-id authorship flag, ownsTargetEntry re-key gate, null-prototype viewStates merged with the pruning loop, loadViewState ownership guard, setValue/setValues rollback) and the per-view mergedStateValues reads keep main's new alwaysDenyUnapprovedCommands.
  5. Specs — every ClineProvider construction passes the shared WebviewFocusTracker (93 cp.spec + 49 parallelMode sites); the api specs pass the required ClineProviderFactory (never called; tab creation runs through the mocked createClineTabPanel); createTask option expectations include main's toolRepetitionSoftLimit: 0; the ported main-side panel test pins the MdmService double locally; the sticky-mode persisted-view-state-keys suite, rollback tests and viewStates global-setting tests are ported, with the re-key tests authoring entries through the real write path.
  6. Upstream main 793b65b merged (this round): 7 conflicts reconciled with both-sides review (registerCommands + spec, api.ts, ClineProvider + spec, Task.spec, ExtensionStateContext.spec.tsx).

Budget

Unit delta vs the stack base stays within budget (the e2e port is 4 files, +543/-0; the review-fix layer is production-hardening carried from the pre-merge review). The PR diff vs upstream/main is larger because the branch is stacked on the unmerged F1-F5 series; the series merges bottom-up, at which point each PR's CI gate measures its own unit. The CI mutation-diff changed-line advisory is a by-construction artifact of the stacked diff.

How to test

  • pnpm --dir apps/vscode-e2e test (mocked e2e) — the "Roo Code View State" suite incl. the sidebar reload/rehydration test.
  • vitest run src/core/webview/__tests__/ClineProvider.parallelMode.spec.ts — pruning, retention, profile-mutation and multi-instance describes.
  • vitest run src/activate/__tests__/registerCommands.spec.ts — fresh-tab creation, tracked-tab reuse, init-failure cleanup, panel title test.
  • vitest run src/extension/__tests__/api-task-control.spec.ts — startNewTask fresh-tab wiring without evicting a tracked tab's task.

Verification numbers (head 56264d1)

  • vitest: ClineProvider.spec 294/294, Task.spec 190/190, full core batch (cp.spec + sticky + parallelMode + registerCommands + Task + 3 api specs + webviewMessageHandler + flicker-free-cancel) 742/742, api-start-new-task 6/6, remaining factory-based api/extension specs 71/71; webview-ui ExtensionStateContext 25/25; packages/types global-settings 9/9. (Local donor types dist was refreshed from source mid-round, which retired the previously-known local-only failures.)
  • eslint --max-warnings=0 clean on all touched files; eslint-suppressions ClineProvider.spec.ts 196 -> 194 (decrease), no count increased.
  • tsc: only 2 local-only errors remain (donor @roo-code/telemetry dist predates the 4-arg captureConsecutiveMistakeError signature; CI builds from source).
  • Negative controls: forcing the re-key ownership check to true reddens sticky-mode "does not merge a stale temporary-id entry"; reverting startNewTask to the factory design reddens api-start-new-task routing + tab-failure tests; both restored byte-exact (sha256 match).
  • Prettier: full-repo check passes on the PR merge tree (re-verified at the current head).

Updated this round (b2333b1 -> d5a5895)

  • Merged upstream/main 793b65b; resolved 7 conflicts with both-sides review.
  • Reconciled the fresh-tab createClineTabPanel surface with main's typed options + shared tracker; api.ts keeps fresh-tab semantics and the new required factory parameter; restored the auto-merge-dropped context field and viewStateReadiness/isDisposed members.
  • Ported the F1a view-identity review fixes into this unit's ClineProvider copy while keeping this unit's setValue mode validation, corrupt-entry pruning log and mutation-marker load-merge semantics; adapted the re-key tests to author entries through the real write path; ported the sticky-mode persisted-view-state-keys suite, rollback tests and viewStates global-setting tests.
  • Added the required WebviewFocusTracker constructor argument to 93 ClineProvider.spec and 49 parallelMode.spec constructions; api specs pass the required ClineProviderFactory; createTask expectations include toolRepetitionSoftLimit: 0; the ported main-side panel test pins the MdmService double locally.
  • Suppression count ClineProvider.spec.ts 196 -> 194.
  • (Second commit 56264d1) Adapted main's api-start-new-task.spec.ts to this unit's fresh-tab design: the spec now mocks the registerCommands createClineTabPanel module function, asserts the exact {context, outputChannel, webviewFocusTracker} payload and pins that the factory is not used for tab creation (CI caught the unadapted spec failing 3 tests at the first head).

Mutation gate (carried)

  • Unit-delta local Stryker preflight was certified at the pre-merge head; the stacked CI mutation-diff reports a changed-line cap artifact by construction (inherited shared content).

Series mechanics

  • PR base is main; the branch is stacked on the F5 head; merge order F1a -> F1b -> F1c -> F2 -> F3 -> F4 -> F5 -> F6 -> F7.
  • CS not-ported register (for consistency): kimi-code OAuth try/catch + routerModels.spec +29; ApiConfigManager min-w-0->grow; ApiConfigManager.visual.tsx deletion + 2 PNG baselines; mojibake comment; providers/, .coderabbit.yaml, .github/, CONTRIBUTING.md, .gitignore churn — none of these touch the files in this PR.

Pre-merge ERROR-row pre-check (head 599844c)

  • Security Boundaries / view-state id rows: addressed at 599844c. setViewStateId now rejects non-string ids (crafted postMessage), bounds the key length (MAX_VIEW_STATE_ID_LENGTH), and refuses ids already owned by another live instance; a refused view stays on its temporary id against shared state. Regression tests: non-string, oversized, claimed-by-another-view. Negative controls: disabling the ownership check reddens exactly the claim test; widening the length bound reddens exactly the oversized test; restores byte-exact (hash match).
  • All three refusal paths resolve viewStateReadiness, so API-driven task creation never falls through to its timeout when a registration is refused.
  • The spec gained an afterEach clearing the static instance registry (suites construct providers directly and never dispose most of them; leaked instances would claim ids ahead of later tests).
  • Persistence Integrity setValues/setValue ordering rows: verified already addressed by the pushed code: _saveViewLocalStateFromMutation persists the non-secret subset before updating the buffer; the re-key is a single queued write (no delete-then-write window; authorship clears only after the write lands); the mutation hook keeps the buffer synced across setValue/setValues and profile upsert/activation/deletion.
  • Proofs at 599844c: sticky-mode 33/33; eslint --max-warnings=0 clean; tsc clean except the two known local telemetry-dist noise lines; prettier merge-ref gate ALL-MATCHED.
  • Non-activating saves (upsertProviderProfile with activate=false, and the zoo-gateway callback's non-active path) rewrote the profile on disk but left pinned views serving the pre-save snapshot; the non-activating branch now refreshes pinned sibling views (and this view when it holds the pin), and the callback routes through upsertProviderProfile so the refresh cannot be forgotten.
  • getState() no longer spreads the pinned overlay over the shared provider settings (a key the profile never defined leaked in from the shared profile: model id, region, tuning). The overlay is a complete snapshot: used in place, with only apiProvider falling back to shared state.
  • Regression tests: "refreshes views pinned to a profile saved without activation"; "does not leak shared provider keys into a pinned view's api configuration; the zoo-gateway callback test asserts the upsert path.
  • Negative controls: dropping the else-branch refresh reddens exactly the refresh test, reverting the merge semantics reddens exactly the leak test; restores byte-exact (hash match).
  • Proofs at fbad9cc: ClineProvider.spec green, sticky-mode and webviewMessageHandler green, eslint --max-warnings=0 clean, tsc clean except the two known local telemetry-dist noise lines, prettier merge-ref gate ALL-MATCHED.

Follow-up: overlay read from the view buffer (required-lane fix)

The previous round's overlay-completeness change read the overlay from the merged state values, which also carry the raw apiConfiguration state key. That key is a partial patch written by shared-store paths, not a view snapshot: with no overlay present and the raw key partial, getState() served only the partial object and dropped the shared provider settings the previous merge produced (the parallel-mode activation tests caught exactly this - the activated profile's model id vanished; required lane red on the units carrying that spec). The overlay is now read from the view buffer itself, where a present value is by contract a complete profile and an absent one means the shared store serves the settings.

Proofs: ClineProvider.parallelMode.spec green where present (the six synchronization tests that regressed), ClineProvider.spec green including the overlay-completeness tests, sticky-mode and webviewMessageHandler green; negative control: reverting the overlay to a shallow merge over the shared settings still reddens exactly the leak test; restores byte-exact (hash match); eslint --max-warnings=0 clean; tsc clean except the two known local telemetry-dist noise lines; prettier merge-ref gate ALL-MATCHED.

Follow-up: active task handlers rebuilt on non-activating saves (head eb27fac)

A review round on the view-identity unit found that the non-activating-save refresh updated pinned views' buffers but not their active tasks' API handlers, and the posted state prefers the task's own apiConfiguration: a conversation running in a view pinned to the saved profile kept sending the old token (e.g. after the Zoo Gateway callback rotated it) even though the view's buffer had caught up. The refresh now rebuilds the affected tasks' handlers in the pinned views, and the non-activating branch rebuilds this view's own handler when it holds the pin.

Proofs: ClineProvider.spec green (the pinned-sibling refresh test now asserts the active task's handler is rebuilt with the saved settings, not just the private buffer); negative control: dropping the rebuild from the refresh reddens exactly that assertion, restores byte-exact (hash match); parallelMode/sticky-mode/webviewMessageHandler green; eslint --max-warnings=0 clean; tsc clean except the two known local telemetry-dist noise lines; prettier merge-ref gate ALL-MATCHED.

Follow-up: test-quality round from the review (head 320fd43)

The review round on the api-wiring unit named four test-quality issues; the ones present in this unit are fixed here: the viewStates describe block moved out of the alwaysDenyUnapprovedCommands block (test names carried the wrong subject), the prune test's toBeDefined() survival checks replaced with exact-shape toEqual assertions (they pass even if pruning strips fields), the stale-temporary-entry test's fixed 10 ms wait replaced with awaiting the load itself (the negative assertion could pass vacuously on a slow load), and the readiness-cancel test's fake timers restored in a finally block (a failed assertion left fake timers active and hung a later real-timer test).

Proofs: global-settings 9/9 where present, ClineProvider.spec green, sticky-mode green where present, api-task-control green where present; negative controls: pruning that strips updatedAt reddens exactly the prune test, a loadViewState that adopts the unowned temporary-id entry reddens exactly the stale-entry test, restores byte-exact (hash match); eslint --max-warnings=0 clean (src and types); prettier merge-ref gate ALL-MATCHED.

Follow-up: review-round fixes from the series (head b6341af)

A review round on this unit produced three fixes. The view-state readiness bound no longer rejects headless callers whose webview never launched (it only rejects when the webview has launched and readiness failed), and the IPC start-task listener catches rejections since the IPC server does not await async listeners (new headless-fallback and launched-stuck tests; negative control: an unconditional timeout reddens exactly the fallback test). The readiness-cancel test now pins the bound's own timer via fake timers and a zero timer count instead of a global clearTimeout spy, and the mode-ordering test asserts the switch has not been called before the pending initialization is released (negative controls: dropping the timer clear / the initialization await reddens exactly the respective assertion).

Proofs: Task.spec 190/190, api-task-control 26/26; negative controls: dropping the initialization await reddens exactly the ordering test's in-flight assertion, dropping the readiness timer's clear reddens exactly the timer-count assertion, an unconditional timeout reddens exactly the headless-fallback test, restores byte-exact (hash match); eslint --max-warnings=0 clean; tsc clean except the two known local telemetry-dist noise lines; prettier merge-ref gate ALL-MATCHED.


Updated this round (CodeRabbit follow-up, head e27816f)

  • Mode-switch atomicity (ported from the sibling unit): handleModeSwitchUnlocked now captures the pre-switch task mode with a task-write rollback and reconciles every write it owns (shared mode, durable view pin, task mode, task history) when a failure lands after the task-mode write - including the mode-linked profile activation - so submitUserMessage delivers on the pre-switch mode and profile instead of a half-applied switch; regression test included.
  • Non-activating profile saves no longer force-rebuild the API handler of a view whose active task follows a different sticky profile (both rebuild sites guarded by taskApiConfigName); regression tests cover both sites.
  • Test hygiene: prune assertions compare full kept entries; the two re-key storage tests seed through the durable write path so their assertions can fail; sticky-mode afterEach disposes the per-test provider before resetting the instance registry; the webview state-payload test renamed.
  • Verified: ClineProvider.spec 298 green, sticky-mode 34 green, parallelMode 40 green, eslint clean, tsc real=0, prettier clean; negative controls each exactly one red.

Updated this round (CodeRabbit follow-up 3, head ed668c4)

  • Mode-switch reconcile (this unit's thread): the trailing postStateToWebview now runs outside the reconciled region - a UI-refresh failure after a fully committed switch (ModeChanged emitted, activation landed) is logged, not rolled back. The catch also skips the rollback once the mode-linked activation has started writing shared state (a mode-only rollback would pair the old mode with the new profile) and when the mutation signal has aborted (the queue has advanced; a newer mutation may own the mode stores). Three regression tests included (negative controls: removing either guard or re-throwing the post reddens exactly one test each). Ported series-wide.
  • Verified: sticky-mode 37 green, ClineProvider.spec 298 green, parallelMode 40 green, eslint --max-warnings=0 clean, tsc real=0, prettier clean.

Updated this round (CodeRabbit follow-up 4, head 03913bf)

  • Mode-switch events now match the stores: ModeChanged is emitted only after the switch commits (a reconciled failure no longer leaves listeners on the rolled-back mode), and the rollback re-emits TaskModeSwitched with the restored mode for API/IPC consumers. The lock path no longer posts inside the reconciled region (it falls through to the committed-switch tail, where a post failure is logged). The mode-default recording reads the view's effective profile (getValues overlay) instead of the shared slot, so a sibling view's activation cannot become this mode's default. Three regression tests included (negative controls: each mutation reddens exactly its test).
  • Verified: sticky-mode 40 green, ClineProvider.spec 298 green, parallelMode 40 green, eslint --max-warnings=0 clean (suppressions pruned: cp spec no-explicit-any count reduced), tsc real=0, prettier clean.

Updated this round (CodeRabbit follow-up 5, head 1759c8e)

  • The committed-switch guard now flips only when the activation has actually written the activated profile (onSharedWriteStart callback), so a mode-linked activation whose own profile lookup fails (deleted profile) still reconciles the switch. The catch that keeps a switch (activation had written, no abort) now emits ModeChanged and posts state before rethrowing, so listeners and the webview learn the committed mode; the abort branch intentionally stays silent (a newer mutation may own the fields - documented in code). In-try abort checkpoints after the mode writes no longer return silently: they fall through to the emit/post tail so the committed mode reaches listeners and the webview. Three regression tests; negative controls: each mutation reddens exactly its test. The activation-failure test double now mirrors the onSharedWriteStart flip.
  • Verified: sticky-mode 43 green, ClineProvider.spec 298 green, parallelMode 40 green, eslint --max-warnings=0 clean, tsc real=0, prettier clean.

Updated this round (CodeRabbit follow-up 6, head 9e20cc9)

  • Abort-silence rule settled: the emit/post tail announces a switch only while its committed mode is still current (after an abort, the tail checks the effective mode store; a superseded commit stays silent because the newer mutation already announced its own mode). This keeps both invariants: a committed switch is never left un-notified while it is current, and no stale announce clobbers a newer owner. Regression tests cover both branches (still-current -> emitted; superseded -> silent); negative controls: dropping the guard reddens the superseded test, always-silencing reddens the still-current test.
  • Verified: sticky-mode 44 green, ClineProvider.spec 298 green, parallelMode 40 green, eslint --max-warnings=0 clean, tsc real=0, prettier clean.

Updated this round (CodeRabbit follow-up 7, head 786a1ce)

  • The lookup-failure regression test now exercises the REAL activateProviderProfileUnlocked (providerSettingsManager.activateProfile rejects) instead of a stub, so the production placement of the committed-switch flip (only after activateProfile resolves) is covered; a matching test keeps the switch when activateProfile resolves and a later shared write (setModeConfig) rejects. Negative controls: moving the flip above the activateProfile await reddens exactly the rollback test; removing it reddens exactly the kept-switch test.
  • Verified: sticky-mode 45 green, ClineProvider.spec 298 green, parallelMode 40 green, eslint --max-warnings=0 clean, tsc real=0, prettier clean.

Updated this round (CodeRabbit follow-up 8, head 0e9337f)

  • The kept-switch regression test now also pins the task-side state on the committed mode: the task's in-memory mode stays architect and the last task-history write carries mode architect (a trailing rollback of the task-side writes after the committed-switch point would now be caught). Negative control: an extra history write with a different mode in the kept branch reddens exactly this test.
  • Verified: sticky-mode 45 green, ClineProvider.spec 298 green, parallelMode 40 green, eslint --max-warnings=0 clean, tsc real=0, prettier clean.

Stryker suppression comments removed

Per the AGENTS.md policy (never add Stryker directives; remove them when a file is touched), all // Stryker disable directives were dropped from this PR. Equivalence rationale for the removed suppressions:

  • Stryker disable next-line ConditionalExpression: defensive clobber guard, unreachable per the ordering argument above.
  • Stryker disable next-line StringLiteral: setPanel branches only on type === "sidebar", so any other literal routes to the identical tab-ref assignment
  • Stryker disable next-line StringLiteral: setPanel only distinguishes "sidebar"; any other value routes to the tab-ref assignment
  • Stryker disable next-line ConditionalExpression: at the catch, this call's own state-change handler only ever re-points the tracked ref at newPanel itself, so tabPanel === newPanel is the only reachable state here; a concurrent re-pointing race is not representable in this spec.
  • Stryker disable next-line ConditionalExpression: only tool_use and mcp_tool_use entries ever carry an id — the text block pushed in the text case has no id field and the start events reaching this guard always carry the string id locked in by processRawChunk — so the type check cannot change the outcome of entry.id === event.id for any entry this pipeline produces; the false replacement is additionally killed by the rejection tests.
  • Stryker disable next-line ConditionalExpression: event.name is always a string on this path — the only delta events reaching this handler are emitted by processRawChunk with the tracked start name (never undefined), so the legacy nameless branch below is unreachable defensive code and the conditional always takes the named side.
  • Stryker disable next-line ConditionalExpression: streamingKey can never be undefined here — makeStreamingKey always returns a string and the legacy lookup branch above is unreachable (see the directive on the event.name check) — so the null side is unreachable defensive code.
  • Stryker disable next-line ConditionalExpression, EqualityOperator: streamingKey is always a defined string on this path (makeStreamingKey never returns undefined; the legacy branch is unreachable per the directives above), and processStreamingChunk returns a partial ToolUse for a known key, so both defensive conditions reduce to the partialToolUse check and cannot change the observed behavior.
  • Stryker disable next-line LogicalOperator: event.name is always a string on this path — finalizeRawChunks emits end events with the tracked start name (a string, set before the tracker can start), so the ?? event.id fallback guards a nameless end event this pipeline cannot produce.
  • Stryker disable next-line ConditionalExpression, LogicalOperator, EqualityOperator: the both-miss case is unreachable via the raw-chunk pipeline — end events carry the locked start name so the compound key resolves the registered entry, and a same-ID rejected-start end is always processed after the accepted start's end has already removed that entry; this defensive path protects against a cross-name end event the pipeline cannot produce.
  • Stryker disable next-line ConditionalExpression: unreachable defensive region — getStreamingToolCallById can only resolve when the both-miss case above is reachable, which the raw-chunk pipeline cannot produce (see the directive there).
  • Stryker disable next-line CallExpression,ObjectLiteral: _updateViewLocalStateFromMutation applies only mode, currentApiConfigName, apiConfiguration and provider-settings keys, so a listApiConfigMeta-only payload (deleted call or empty object) is a behavior-preserving no-op.
  • Stryker disable next-line ConditionalExpression: in this fallback the view does not pin the deleted profile, so when the deleted profile was not the shared selection profileToActivate equals the shared selection itself; forcing the branch only rewrites that same value back into the store.
  • Stryker disable next-line ConditionalExpression,BlockStatement: hostile non-stabilizing encodings are unreachable from the webview (its posts are plain link targets); the bound is defensive
  • Stryker disable next-line CallExpression: hostile non-stabilizing encodings are pinned by the direct decodeUntrustedPathToStable bound test; the webview never posts such values
  • Stryker disable next-line ConditionalExpression,OptionalChaining: equivalent mutant - when localStorage is unavailable the guard-false path and the throwing body both return this.fallbackState from this catch
  • Stryker disable next-line ConditionalExpression,OptionalChaining: equivalent mutant - when localStorage is unavailable the guard-false path and the throwing body both mark the write failed and return newState

CR round 2026-10-15 (b)

  • Ported the payload-customModes fix for setValues mode validation (identical flagged block; regression test added) so the units stay byte-identical at merge.

CR round 2026-10-15 (c)

CR round 2026-10-15 (d)

  • Ported: post-flatten shared-provider fallback for explicitly-undefined nested apiProvider (identical flagged block; regression test added).

CR round 2026-10-15 (e)

CR round 2026-10-15 (f)

  • The createTask registration-failure rollback now restores the shared mode and the view's own mode pin independently (unset pin stays unset); divergent-pin regression test added. Same fix in all six units carrying the flagged block.

Row-9 update (head 44d7a56)

  • setGlobalState persists parsed.data instead of the raw payload: non-strict schemas strip unknown fields only in the parsed value, so credential-shaped extras cannot reach durable global state (CR Security Boundaries).
  • Activation writes from the stored record (read-back feeds every post-save consumer; CR Security Boundaries).
  • Clear/re-point cache divergence is guarded at the ContextProxy#updateGlobalState layer on this unit; the new tests pin that guard.

Verification: new tests are focused negative controls (reverting each guard reddens exactly the matching test); ClineProvider.spec, webviewMessageHandler.spec spot-checks, api specs, spec-inclusive tsc --noEmit, eslint --max-warnings=0 (suppression counts unchanged) and prettier all green on this head.

Upstream refresh (head 44d7a56)

Merged the current upstream/main (merge commit; the row-9 fix commit is unchanged and is the first parent) to clear the review gate's mergeability hold. Upstream refactored upsertProviderProfile into a new persistProviderProfile method and added a runToCompletion queue option; this unit's transactional body was ported into the new method unchanged and upstream's Promise.allSettled activation fix was adopted in it. ClineProvider.spec, webviewMessageHandler.spec, tsc --noEmit, eslint (suppression counts unchanged) and prettier re-verified green at this head. The first refreshed head's windows unit-test lane caught that upstream's new updateProfileModel tests in ClineProvider.apiHandlerRebuild.spec.ts mock the settings manager with a static getProfile, which starves the post-save read-back; the mock now repoints getProfile at what saveConfig wrote (the real manager's behavior), and the file's 38 tests pass locally alongside the rest of the verification set.

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

Labels

awaiting-author PR is waiting for the author to address requested changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants