Skip to content

webapp: the settings rows and the tab document stop lying about what landed - #222

Draft
pythonlearner1025 wants to merge 2 commits into
feat/optimistic-held-dialogsfrom
feat/optimistic-stale-rows
Draft

webapp: the settings rows and the tab document stop lying about what landed#222
pythonlearner1025 wants to merge 2 commits into
feat/optimistic-held-dialogsfrom
feat/optimistic-stale-rows

Conversation

@pythonlearner1025

Copy link
Copy Markdown
Member

What part this touches

The settings surfaces and the shell's persistence layer.

Settings hold rows: invites, credentials, provider connections, credential
requests and grant proposals. Each row is written by a control-plane call.

use-workspace-persistence.ts writes the workspace document that remembers
terminal tabs, surface tabs, the preview tab and the side pane width.

Stacked on #220, which is stacked on #205. Review those first.

The gap

Two shapes, both found by the audit on #205.

Rows stay stale until the server answers. A user changes something and the
row keeps its old value until the request returns, or until the next refetch.
Ten surfaces behave this way.

The shell updates at once but cannot undo it. A terminal tab opens
instantly and the document write follows. If that write is rejected, nothing
puts the tab back. The screen and the server disagree, and the user is not
told.

Two of these were worse than slow:

  • Auto-approving a credential request after connecting its provider was fired
    with void. A rejection went nowhere.
  • The workspace document write ignored the document the server returned, so a
    successful write never reconciled either.

The fix

Rows now show the intended result, then reconcile or roll back:

  • Both invite entry points show a pending row. The one-time code is never
    invented — the row appears, the secret arrives with the response.
  • Usage capture, org credentials (add, rotate, grants, revoke, .env import),
    compute credentials and inline setup, provider connect, paste-and-connect and
    disconnect, personal grant revoke, credential request dismiss and deny, and
    grant proposal approve and reject.
  • Grant decisions reuse use-grant-proposals.ts dismiss, settled and reopen.
  • Starting a stopped machine enters provisioning at once, survives a stale
    poll, and restores the stopped pane on failure.
  • Rail rename consumes the canonical workspace and restores the prior title.
  • Preference writes consume their response and restore the acknowledged value.

The persistence fix is ONE change, not twelve. use-workspace-persistence.ts
keeps the last acknowledged document, applies the document the server returns,
serializes a newer edit over an older one, and restores the acknowledged
document when a write is rejected. Every tab action inherits it.

The risk trade

An acknowledged-document rollback can undo an edit the user made while a write
was in flight. Serializing newer edits over older ones is what keeps that from
happening in the common case, but a rejection still restores the last state the
server confirmed. That is the honest trade: the screen agrees with the server,
even when that costs a keystroke.

The alternative was per-action rollback at each call site. That is twelve
copies of one rule, and the audit on #205 found exactly that kind of
duplication elsewhere. One owner is worth the coarser undo.

OAuth completion is deliberately unchanged. connectStartUrl only mints an
external URL, and the provider owns the decision. Showing a connection as made
before the provider agrees would be a lie, not optimism.

Drive is untouched. It is being deleted.

Tests

Fifteen tests. Each drives a deferred promise, asserts the screen changed
BEFORE the request settled, and asserts the restore on rejection. Both
directions, or the test proves nothing.

All fifteen were seen failing with their production code reverted: 31 failed,
126 passed across those 9 files. With the code restored: 157 of 157 pass.

Examples of the failing output:

workspace provider rows > disconnects a provider immediately and restores it on rejection
AssertionError: expected 'Connected' to be 'Connect'
v2 credential surfaces > marks an org request denied immediately and restores pending
AssertionError: expected 'pending' to be 'denied'

Real counts on this branch: 116 webapp files pass, 1039 tests pass, 61 skip.
One it.fails(...) in lody-toaster.test.tsx reports as an expected fail, as
it does on main.

npm run lint:gate passes at baseline. 66 anti-slop findings, 0 house
findings, 8 max-lines warnings. None moved.

npm run typecheck -w @blitzos/webapp passes.

Deploy

The webapp bundle ships inside the control-plane worker. This branch is stacked
on #220 and must not merge before it.

After #220 merges, rebase this onto main and merge it. Merging to main runs
.github/workflows/canary.yml, which deploys the worker to canary. No box code
changed.

Verify after the merge:

curl -s https://blitz-control-plane.minjunesv0.workers.dev/version

Then open settings, change a role or revoke a credential, and watch the row
change before the request finishes.

Rollback:

git revert <merge-sha>
git push origin main

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.

1 participant