Repository navigation
vps2: resolve per-view state without persisting/exposing secret-bearing provider settings, and load it before the first state post #2152
Description
Context
Two CodeRabbit pre-merge errors on the vps2 view-state series' owner unit (#1546, F1a) describe the same architectural debt: viewLocalState does not exist on upstream main (git grep viewLocalState upstream/main is empty), so the series introduces it, and the owner unit is the right place to fix it once instead of in each replayed branch.
1. Secret-bearing provider settings in the view-local state (Security Boundaries)
ClineProvider stores full provider settings in viewLocalState.apiConfiguration (profile loading copies getProfile()), and the public ClineProvider.getValues() result can therefore carry nested provider configurations (including secrets) beyond the sidebar they belong to. The review asks to remove secret-bearing settings from the public getValues() result, or to recursively sanitize every nested provider configuration before API.getConfiguration() returns it, and to add a regression test that activates a profile.
2. Late async loadViewState() in the constructor (Persistence Integrity)
The provider starts void this.loadViewState() in its constructor, but loadViewState() returns early because the stable view identity is not known until the webview launch message arrives; the state is therefore loaded later than the first postStateToWebview(). The review's resolution: send the stable view identity in the webview launch message, await provider.setViewStateId(message.viewStateId) before the first postStateToWebview(), and overlay viewLocalState onto getState() for mode, currentApiConfigName, etc.
Why a follow-up issue instead of an in-PR patch
- Both changes move the series baseline (
viewLocalStateis shared by all replay units); landing them in seven branches would produce seven divergent copies. - Fix 1 changes the shape of a public provider API (
getValues()) and needs a decision about where per-view overrides are resolved (store the profile NAME and resolve at read time vs store settings and sanitize on read). - Fix 2 changes the launch/state ordering that the claim-token work in security: bind view-state ids to a host-minted claim token (cross-view secret disclosure) #2151 already touches; the two should be designed together.
Acceptance criteria
- Decide the overlay model: per-view state stores the profile name (and mode/selection) only, or
getValues()/API.getConfiguration()sanitizes nested provider settings before exposing them. - Regression test: activate a profile in one view and assert that
getValues()/API.getConfiguration()for another view (or the shared path) carries no secret-bearing nested configuration. - The stable view id is known before the first
postStateToWebview()(launch-handlerawait setViewStateId(...)before the post), andloadViewState()no longer races the constructor. - Test: the first posted state after launch already reflects the persisted per-view state for
mode/currentApiConfigName. - Replayed units inherit the fix via refresh (no second implementation).
References
- feat(provider): persist per-view view-state identity and durable viewStates #1546 review (pre-merge errors: Security Boundaries, Persistence Integrity)
- security: bind view-state ids to a host-minted claim token (cross-view secret disclosure) #2151 (view-state claim binding, same launch/state area)
- Series: fix(core/webview): merge view-local state into getState for per-view overrides #1550, fix(webview): send stable view-state id on launch and re-pin per-view state #1552, test(webview): F2a - profile-mutation state semantics and the mode-rollback guard #1919 and the other replay units
Activity