Editing and Concurrency — Overview
The Arda items workspace is designed for several users working in parallel against the same tenant. Two of them can open the same item at the same time, edit cards from different browsers, or trigger bulk actions on overlapping selections. The data layer described in Data Flow and Caching caches server data in each browser tab — which is good for responsiveness, but means a tab’s view can fall out of step with the canonical state on the backend the moment another user (or another tab of the same user) writes.
This section documents the two mechanisms that close the loop:
- A staleness signal that tells a user when the data on screen no longer matches what the backend would return for the same request, so they can refresh before acting.
- A conflict-resolution preflight that catches the case where the user has already clicked a destructive bulk action against a selection that went stale between selection and click, and forces them to acknowledge before the action proceeds.
Both mechanisms work against the same ItemCardsProvider cache and the same rId-set diff primitive. They differ in when they fire and in what the user is asked to do.
What can go wrong
Section titled “What can go wrong”Three concurrency scenarios shape the design:
| Scenario | What happens without a signal | Where the user sees it first |
|---|---|---|
| User A and User B both have the same item open; A edits one of its cards. | B keeps seeing the pre-edit cards. If B then clicks an action that the server has already invalidated (e.g., “add to order queue” on a card A just received), the action 409s. | Detail panel open in B’s browser. |
| User A opens two tabs on the same items page; A edits in tab 1. | Tab 2 keeps the pre-edit state in its cache and grid. If A switches to tab 2 and acts, same 409 risk as above; if A only reads, A sees inconsistent state across the tabs of one browser. | Either tab. |
| User A starts a bulk operation on a 50-item selection, walks away, comes back, clicks Delete. | The selection is captured at click time. Cards may have been added, removed, or re-issued in the intervening minutes. The destructive action proceeds against possibly-wrong assumptions. | Delete modal in A’s browser. |
The mechanisms below address each. The staleness signal covers the first two; the conflict-resolution preflight covers the third.
Sub-pages
Section titled “Sub-pages”| Page | Mechanism |
|---|---|
| Staleness Signal | A local invalidation bus over BroadcastChannel for same-browser cross-tab propagation, plus a bounded interval poll for cross-browser propagation, both feeding the ItemCardsProvider cache. Surfaces in the UI as a stale-data banner with explicit Refresh / Dismiss controls. |
| Conflict Resolution | A snapshot-and-diff preflight (useBulkSelectionStaleGuard) that runs before a destructive bulk action proceeds. Compares each selected item’s cached rId set to a fresh server read; on mismatch, raises a banner and aborts so the user can refresh and re-click. |
Design decisions
Section titled “Design decisions”The system-level decisions that shape this section are recorded as ADRs:
- ADR-001: Frontend Cache-Invalidation Mechanism — why a local bus + bounded poll, and not server push or polling-only.
- ADR-002: Cache-Invalidation Coalescing — why a microtask coalescer at the consumer end and tolerant input at the bus end, instead of one or the other.
- ADR-003: Concurrent-Edit Detection Strategy — why an
rId-set diff at the consumer hook, instead of server-side optimistic locking or ETag round-trips.
Related product capability
Section titled “Related product capability”The user-visible promise behind both mechanisms is documented as a product feature in Concurrent-Edit Awareness. The use-case catalogue in General Behaviours — Interactions and Items — Bulk Operations carries the user-flow scenarios that this section’s mechanisms must satisfy.
Copyright: © Arda Systems 2025-2026, All rights reserved