Skip to content

Merge #977 (durable per-view state base) into the file-write-safety trial branch - #34

Open
easonLiangWorldedtech wants to merge 33 commits into
feat/fws-trial-allfrom
merge-977-into-trial
Open

Merge #977 (durable per-view state base) into the file-write-safety trial branch#34
easonLiangWorldedtech wants to merge 33 commits into
feat/fws-trial-allfrom
merge-977-into-trial

Conversation

@easonLiangWorldedtech

Copy link
Copy Markdown
Owner

This PR merges the head of upstream PR #977 ("fix(webview): add durable per-view state base", easonLiangWorldedtech:feat/view-local-state-base @ f91e19c03) into the file-write-safety (FWS) trial branch feat/fws-trial-all.

What is Zoo-Code-Org#977

PR Zoo-Code-Org#977 introduces a durable per-view state base for the webview: each webview instance gets a stable view id, and non-secret selections (mode, current API config) are persisted per view in registered global state, so parallel/stacked panels keep isolated selections. It adds a viewStateSchema plus a viewStates record to the global settings schema, view-state load/save plumbing in ClineProvider (setViewStateId / saveViewState and the viewLocalState merge in getState), viewStateId routing on the webviewDidLaunch message, a repair path for invalid view pins that does not clobber the global config selection, and test-only API surfaces (approveTaskAsk, selectTaskFollowupSuggestion, getGlobalState, preserveOpenTabs) with matching unit and e2e coverage.

Why it is included here

The FWS trial branch is a stacked trial of the file-write-safety series (change cards, rollback service, per-write checkpoints, checkpoint settings, 18-locale i18n). Including Zoo-Code-Org#977 here produces a combined trial of both series in one branch so reviewers can test the FWS features together with the per-view state base and flag any interaction early, instead of waiting for sequential merges.

Conflict resolution

Three files conflicted; all were resolved by keeping both sides:

The remaining 24 changed files auto-merged cleanly (webview state context, message handler, API types, e2e fixtures, new spec files).

Local gate results

  • tsc --noEmit: src exit 0; webview-ui exit 0
  • eslint --max-warnings=0 on every merge-touched file: src (11 files) exit 0; webview-ui (6 files) exit 0; packages/types (4 files) exit 0. The net change to src/eslint-suppressions.json is a single count reduction (37 -> 36, carried by fix(webview): add durable per-view state base Zoo-Code-Org/Zoo-Code#977 itself); no suppressions were added.
  • vitest run (webview-ui): Test Files 163 passed (163); Tests 1794 passed (1794)
  • vitest run (src): Test Files 475 passed | 1 failed | 3 skipped (481); Tests 8016 passed | 5 failed | 38 skipped (8059). The single failing file is services/rules/__tests__/rules.spec.ts (5 tests) — all fs.symlink EPERM errors specific to this Windows host; the spec and the rules service are byte-identical to the trial base and untouched by this merge.
  • node scripts/find-missing-translations.js: exit 0 — all 17 non-English locales complete across backend, frontend, and package.nls (fix(webview): add durable per-view state base Zoo-Code-Org/Zoo-Code#977 added no new i18n keys).

Note

Merging this PR updates feat/fws-trial-all (the head branch of upstream trial PR Zoo-Code-Org#1413), so that upstream trial PR picks up the combined contents automatically.

easonliang28 and others added 30 commits August 19, 2026 17:54
A just-resolved globalState write can momentarily lag a synchronous globalState.get in the extension host. The per-view writes are already awaited through the serialized view-state write queue before the tasks complete, so poll until both the sidebar and tab persisted selections are visible before asserting, instead of reading globalState once.
Address the CodeRabbit docstring coverage warning on the durable per-view state PR by documenting the new view-state persistence/merge helpers in ClineProvider, the task-scoped API controls in the extension API, and the viewStateId generation/restoration helpers in the webview wrapper.
# Conflicts:
#	src/core/webview/ClineProvider.ts
#	src/core/webview/__tests__/webviewMessageHandler.routerModels.spec.ts
#	src/core/webview/__tests__/webviewMessageHandler.spec.ts
#	src/core/webview/webviewMessageHandler.ts
…sted view-local secrets

- selectTaskFollowupSuggestion() now passes the registered task explicitly to handleModeSwitch(), so answering a follow-up on task B no longer switches the mode of the provider's currently focused task A (resolves review comment on src/extension/api.ts).

- getConfiguration() flattens the nested view-local apiConfiguration onto the top level before the isSecretStateKey() filter, so nested provider secrets (apiKey, openRouterApiKey, ...) cannot leak through the API — a regression introduced by the per-view state base's nested apiConfiguration shape.

- Consolidate the duplicate kimi-code oauth vi.mock in the routerModels spec and replace raw provider identifier literals flagged by the merged zoo/no-raw-provider-identifiers rule with providerIdentifiers.* constants.

Validated: 112 targeted vitest tests pass, pnpm --dir src run check-types clean, eslint --max-warnings=0 clean on all touched files.
The .husky/_ directory contains husky-generated internal shims that were committed by accident. Ignore the directory and untrack the existing shims so they no longer show up as modified files on every install.
- Scope handleModeSwitch to the target task: switches for non-focused tasks no longer rewrite the view's durable mode pin, emit ModeChanged, or activate provider profiles.
- History restore persists the mode through the view's own pin instead of the shared global mode setting.
- loadViewState discards stale results when a newer view id is registered during the load, and pre-launch temporary view ids never write durable entries (orphan prevention).
- deleteProviderProfile re-points persisted view pins that referenced the deleted profile, and only overwrites this view's in-memory profile pin when it actually pinned the deleted profile.
- resetState also clears this view's persisted entry.
- saveViewState is now public and fully typed (no explicit any left in the provider).
- Mock ContextProxy mirrors the real state cache so tests can exercise stale-cache and fresh-read behavior; add regression tests for all of the above.
…iable

- removeRegisteredTask only drops the registration when the stored controller is the same instance, so a replaced task reusing a taskId is not torn down by the old instance's abort/unfocus events.
- selectTaskFollowupSuggestion delivers the answer even when the follow-up mode switch fails, logging the failure instead of losing the user's response.
…selection

- webviewDidLaunch rescue: when the view's own pin is invalid, re-pin the view to the shared global selection if it is still valid instead of overwriting the global setting and activating a global profile from one view's launch path.
- updateSettings persists provider settings through the provider-level setValue so the durable write path is the one the provider serializes.
- Lower the recorded no-explicit-any suppression counts for the touched files (fixes, not new suppressions).
…red fixtures

- view-state.test.ts derives the round count from the follow-up isolation fixture instead of a hardcoded 10, and drops an unused map.
- Document the new mode-switch predicate fixtures alongside the legacy model-scoped fixtures in runTest.ts.
- The webviewDidLaunch describe now assigns its runtime members through a structural LaunchProviderFixture cast instead of per-line as-any, and the getState mock return is typed against the provider signature.
- Drops the webviewMessageHandler.spec.ts suppression count back to the PR base level (35).
…ion spec

Use typed structural casts (ClineProvider / OutputChannel) for the API double and remove the now-empty suppression entry for the file.
CI e2e-mock regression: the mode switch inside a task lands before the
tab webview's launch message registers its stable viewStateId, so the
ephemeral-skip silently dropped the view's durable mode write and the
"sidebar and tab panel keep mode isolated" e2e timed out waiting for
the persisted entries.

Pre-launch writes now persist under the temporary view id and are
re-keyed to the stable id when the webview registers it (setViewStateId
runs the re-key through the serialized write queue before loadViewState).
A pre-existing stable entry wins and the temporary entry is dropped,
since temporary ids are session counters that can collide across window
reloads. The stale-load guard is unchanged.

Unit tests: the ephemeral-skip assertion is replaced with re-key tests
(write under temporary id, re-key on registration, stable entry wins)
and the stale-load test is restructured so it genuinely exercises the
guard through the cached read path.
…elMode spec

Address the CodeRabbit maintainability finding: drop the blanket
no-explicit-any suppression (137) for ClineProvider.parallelMode.spec.ts
and type the spec properly instead.

- Private member access moves from (provider as any).x to bracket
  notation (provider["x"]); public members (saveViewState, setValue,
  setValues, handleModeSwitch, resolveWebviewView, log) drop the cast
  entirely and keep their native generics.
- MockContextProxy now takes vscode.ExtensionContext; memento and mock
  callbacks use unknown instead of any; the webview structural double is
  cast once as unknown as vscode.WebviewView.
- Key/value casts are removed where the key is a valid RooCodeSettings
  key; the one genuine exception (apiConfiguration is a GlobalState key
  outside the proxy's generic) keeps a documented double assertion.
- api-configuration.spec.ts: document the as-unknown-as-ClineProvider
  structural double in the new test (API.getConfiguration only reads
  sidebarProvider.getValues).

check-types clean; parallelMode 49/49 and api-configuration 3/3 green;
eslint --prune-suppressions clean with the parallelMode entry removed
from eslint-suppressions.json and every other count unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants