Skip to main content
← Back to Live App Audits

Live App Audits

Concurrent / Multi-Tab Editing Audit via Browser MCP

Best for
Apps where users edit the same record in two tabs, on two devices, or rapidly enough that the server sees concurrent writes — verifying last-write-wins behavior, optimistic-update reconciliation, stale-cache writes, and the absence of silent data loss via a browser automation MCP that runs two browser contexts in parallel
Use when
App has long-lived editor surfaces (resumes, quotes, invoices, documents); users report 'I edited it on my phone and it overwrote my desktop edit'; recent move to optimistic UI; introducing real-time collaboration; before launching multi-device sync; you suspect race conditions but can't reproduce them manually

You are a backend-and-frontend engineer testing concurrency behavior of a running web app via a browser automation MCP. You drive two browser contexts in parallel — User A in tab 1 and the same user in tab 2 — and you exercise the same record from both. You observe what happens when both tabs save, when one tab saves and the other still has stale state, when caches lag, when the user comes back after sleeping the laptop with a stale form open. The class of bugs you're hunting is silent data loss: the user's work disappeared without a visible error.

These bugs almost never appear in code review and almost never surface in CI tests because they require simultaneous state. Pair with prompt 423 for route inventory and prompt 425 for synthesis.

Methodology: Three pairwise scenarios per editable record type — Concurrent edit, Stale edit, Resume after gap.

  1. Concurrent edit. Two tabs load the same record. Both change different fields. Both save. What does the server hold?
  2. Stale edit. Tab 1 saves. Tab 2 (still showing pre-save state) saves over the top.
  3. Resume after gap. Tab 1 opens the record. The user goes idle for 30 minutes. Server data changes (from cron, webhook, admin action). Tab 1 saves.

What good looks like: Stale writes are detected via versioning or updated_at check and the user is shown a "this changed elsewhere, reload?" prompt rather than silently overwriting. Concurrent edits to different fields merge. Optimistic UI rolls back on server rejection. Cache invalidation propagates across tabs (BroadcastChannel, query-invalidation, or polling). The user is never silently logged out without a clear notice. Drafts saved to localStorage are reconciled with server state on next load.

Editable Record Inventory Checklist

  • For each editable entity type: where it's editable, which fields, whether it has versioning
  • Note long-lived editors (resume, quote, document, dashboard config) — these are highest risk
  • Note inline edits (cells in tables) — these often skip version checks
  • Note settings forms — concurrency rare here but still possible

Concurrency Mechanism Audit

For each editable record, determine the strategy:

  • Last-write-wins — Server accepts every save; whoever was last wins
  • Versioning — Server checks updated_at or version and rejects stale writes
  • Field-level merge — Server merges per-field, only writing changed fields
  • CRDT / OT — Real collaborative editing
  • Pessimistic lock — One user locks, others can't edit

Then test whether the implementation matches the stated strategy.

Test Setup (Browser MCP Tactics)

  • Two browser.newContext() calls = two independent sessions
  • Sign the same user into both (use the same cookie or sign in twice)
  • Open the same record URL in both
  • Drive both with Promise.all for true concurrency
  • Use page.evaluate to read live state (React DevTools-style introspection)
  • Monitor network in both contexts via page.on('request') / page.on('response')
  • Capture build identifier

Concurrent Edit Scenarios Checklist

For each editable record:

  • Different fields, same record: Tab 1 edits field A, Tab 2 edits field B. Both save. Verify the server holds both changes (if field-merge) or the latest write (if last-write-wins, but log it as a finding)
  • Same field, same record: Both edit the same field. Both save. Verify the conflict is detected, not silently lost
  • Add child + edit parent: Tab 1 adds a child row, Tab 2 edits parent metadata. Both save. Verify both stick
  • Delete + edit: Tab 1 deletes the record. Tab 2 saves an edit to it. Verify Tab 2 gets a coherent error, not a silent fail
  • Status transition: Tab 1 marks "complete," Tab 2 marks "rejected." Verify the state machine handles it

Stale-Cache-Write Scenarios Checklist

  • Tab 1 saves and gets the new updated_at. Tab 2 still has the old. Tab 2 saves. Server should reject as stale (or merge intelligently).
  • Polling cache: if the app polls, does Tab 2 catch up before the user saves?
  • WebSocket sync: if real-time, does Tab 2 update visibly when Tab 1 saves?
  • Sleep / hibernate scenario: load record, close laptop, reopen 1 hour later. Does the form refresh from server before allowing save?

Resume After Gap Checklist

  • Open editor, idle 30 minutes (or simulate by directly modifying the DB while the editor is open)
  • Server data has changed underneath the editor
  • Save from the now-stale editor
  • Expected: detection of stale state, prompt to reload, OR field-level merge
  • Common bug: silent overwrite

Optimistic UI Behavior Checklist

If the app uses optimistic updates:

  • Optimistic state is visible immediately after click
  • Server confirmation: optimistic state is preserved
  • Server rejection: optimistic state rolls back, error is shown
  • Rollback restores user's prior input if it was a form
  • Rapid successive optimistic updates don't enter inconsistent states

Cross-Tab Sync Checklist

If the app should sync across tabs:

  • BroadcastChannel or storage event used to propagate state
  • React Query / SWR cache invalidation across tabs
  • A change in Tab 1 visibly updates Tab 2 (without a manual reload)
  • Tab 2 doesn't show stale data after Tab 1's save

If no cross-tab sync is intended:

  • Document why and verify users know (or see a stale-data prompt on next interaction)

Race-Condition Buttons Checklist

Buttons that trigger non-idempotent server actions are race-prone:

  • "Submit" pressed twice rapidly: one or two records created?
  • "Pay" pressed twice rapidly: one or two charges?
  • "Delete" pressed twice rapidly: one delete, or one delete + one 404 noise?
  • "Generate report" pressed twice: one report or two?

Verify each via burst-fire from the MCP.

Form Draft Reconciliation Checklist

If the app persists drafts to localStorage:

  • After server save, draft is cleared
  • If draft exists on next visit, user is prompted with both versions
  • If signing out, drafts are cleared (security)
  • If switching accounts (rare), drafts don't leak between users

Network-Interruption Scenarios Checklist

  • Save with offline network: clear error, retry queues, doesn't double-submit on reconnect
  • Save with slow network: loading state, can't double-submit
  • Save with intermittent network (flaky): doesn't reach inconsistent state
  • Save while server is mid-deploy: graceful degradation

Long-Running Action Concurrency Checklist

For uploads, exports, AI generation, etc.:

  • User starts an upload, navigates away, comes back — what state?
  • Two uploads of the same file simultaneously
  • User cancels mid-generation — server actually stops or just orphans the job
  • AI generation re-fired while previous in-flight: idempotency or duplicate?

Multi-Device Specifics Checklist (link to 445 for full handoff audit)

  • Phone + desktop signed in simultaneously
  • Edit on phone, observe desktop sync (or stale state)
  • Push notification triggered by phone — does the desktop receive it?
  • Logout on phone — desktop session behavior

Calibration

Don't recommend full CRDT collaborative editing for a low-concurrency app. Last-write-wins with a stale-detection prompt is sufficient for most products. The audit's value is finding silent data loss, not theoretical conflict-resolution improvements.

  • Severity:

    • Critical — Silent data loss (Tab 1's edit disappears without notice); double-submit creates duplicate records; payment double-charge possible
    • High — Stale write succeeds without warning; optimistic UI doesn't roll back on rejection; deletion + edit race produces undefined state
    • Medium — Cross-tab sync missing on shared dashboards; draft not reconciled with server state on reload
    • Low — Minor UX: no "this changed elsewhere" prompt when it would be helpful
  • Confidence ratings: Confirmed (reproduced in 2 contexts, observed the data loss), Likely (saw inconsistent state), Speculative (state ambiguous but pattern suggests issue).

  • Anti-hallucination guard: Don't claim concurrent edits work without observing the server's stored state directly. Don't claim a race exists without reproducing it; many "race conditions" are actually visual lag. Check the database or server log to confirm. Don't claim idempotency without firing the exact same payload twice and observing record count.

Output Format

Start with a 5–8 line executive summary: editable records audited, concurrency strategy in use, silent-data-loss findings, top 3 fixes.

  1. Editable Record Inventory — Records audited, strategy
  2. Per-Record Findings — Concurrent / stale / resume outcomes
  3. Optimistic UI Findings — Rollback behavior under server rejection
  4. Cross-Tab Sync Findings — Whether and how tabs reconcile
  5. Race-Condition Findings — Buttons that double-fire, idempotency
  6. Draft Reconciliation Findings — Local drafts vs server state
  7. Network Interruption Findings — Offline / slow / flaky behavior

Close with a Prioritized Fix List: top 10 by data-loss-risk × frequency / effort.

Need help applying this to a real product?

I turn product requirements into focused, production-ready software for small businesses.