Concurrent-Edit Awareness
Feature code: GEN::CEA
Several users can work the same tenant’s data at the same time. The same user can open the same item in two browser tabs. Either case can leave a user looking at data that the backend has already moved on from. Concurrent-Edit Awareness is the platform-wide capability that closes that gap: the system tells the user when the data on screen is no longer current, so they refresh before acting; and when a destructive bulk action is about to fire against a selection that has gone stale, the system catches it before any destructive call is made.
The capability is intentionally a notification, not a merge. Arda does not attempt to reconcile two concurrent edits — that is the user’s job. The platform’s job is to make sure neither user discovers the conflict after clicking the destructive button.
Capability promises
Section titled “Capability promises”The platform makes three concrete promises against the concurrency surface:
- A user with an item open is notified when someone else changes its kanban-card data. The notification appears in the detail panel as a stale-data banner with a Refresh and a Dismiss control. The user can refresh to see the new state or dismiss to keep working with what they have.
- A user with two browser tabs open on the same item sees the same notification within sub-second time of the other tab’s edit. Same-browser, cross-tab propagation is in-process; the user does not have to wait for a network round-trip in either tab.
- A destructive bulk action against a selection that has gone stale is caught at click time. Before the action proceeds, the system re-reads the affected entities. If any selected entity’s underlying data has changed since the user made their selection, the action is aborted and a stale-selection banner asks the user to refresh and re-attempt.
A fourth, weaker, promise applies to the cross-browser case: when two users are on different browsers (different machines, different networks), the second user sees the banner within a bounded latency, not in sub-second time. The bound is operational and is documented in the runtime tuning notes below.
Where the user sees it
Section titled “Where the user sees it”The capability surfaces in three places:
| Surface | What the user sees |
|---|---|
| Item-details panel | A banner at the top of the panel reading “This data has changed elsewhere”, with Refresh and Dismiss buttons. The banner appears when the panel’s view of an item’s kanban cards no longer matches the backend. |
| Items grid — bulk action confirmation | When the user clicks a destructive bulk action (Delete) and the selection has gone stale, a banner above the grid replaces the action’s confirmation dialog. The user must Refresh (which re-issues the cards query and clears the banner) or Dismiss (which closes the banner) before the action can be re-attempted. |
| Order-queue, scan, and card-state surfaces | Same banner mechanics as the detail panel, applied through the same underlying mechanism. Any surface that mounts the detail panel for an item gets the banner for free. |
The banner copy and controls are deliberately consistent across surfaces, so the user learns one interaction and applies it everywhere.
Behavioural contract
Section titled “Behavioural contract”The capability is governed by the following behavioural rules:
- No silent data overwrites. If the user dismisses the banner and proceeds to mutate, the per-mutation server check still enforces optimistic locking — a stale mutation will fail with a server-side conflict surfaced as a toast. The banner exists to make that case rare, not to replace the backend check.
- No false positives from network errors. If the freshness check itself fails (transport error, non-OK response), the banner does not appear. The user keeps seeing what they had, and the next successful check re-evaluates.
- No interference with the user’s own actions. A user who makes a change in tab 1 does not see their own banner appear in tab 1 — the panel they edited refreshes through its own success path. The banner is only for state the user did not originate.
- No amplification under burst. A bulk operation that touches dozens of items in quick succession produces a single coalesced refresh, not one per item.
- Operational kill switch for the cross-process path. The cross-browser polling tick can be disabled per environment without redeploying functional changes; the cross-tab path is unaffected.
Supporting use cases
Section titled “Supporting use cases”The user-flow scenarios that this feature must satisfy are catalogued under the General Behaviours and Reference Data use-case sections:
GEN::INT::0002Concurrent-Edit Awareness — the cross-cutting scenarios that apply to any item-details-panel surface.REF::ITM::0009Bulk-Action Stale-Selection Preflight — the items-specific scenarios for the destructive-bulk-action case.
Implementation reference
Section titled “Implementation reference”The mechanism that delivers this capability is documented in the system reference:
- Editing and Concurrency — Overview
- Staleness Signal — read-side detection and notification.
- Conflict Resolution — bulk-action preflight.
The frozen design decisions are recorded as ADRs:
- ADR-001: Frontend Cache-Invalidation Mechanism
- ADR-002: Cache-Invalidation Coalescing
- ADR-003: Concurrent-Edit Detection Strategy
Operational tuning
Section titled “Operational tuning”The capability exposes two operational levers that take effect at SPA build time:
| Environment variable | Default | Effect |
|---|---|---|
NEXT_PUBLIC_ITEM_CARDS_POLL_MS | 120000 ms (two minutes) | Worst-case latency for the cross-browser case. Any non-positive value disables the cross-process polling entirely (the kill switch). The cross-tab path is unaffected. |
NEXT_PUBLIC_ITEM_CARDS_TTL_MS | 30000 ms | Read-side freshness floor used by the grid’s per-cell TTL check. Lower values produce more background refreshes; higher values let stale-while-revalidate hold out-of-date data on screen for longer. |
Current coverage
Section titled “Current coverage”The capability currently covers the Item entity and any surface that mounts the item-details panel (items page, order-queue, scan modals, card preview). Extension to other entities is mechanical — the same primitives apply to any entity whose detail surface reads through a cached provider.
| Entity | Read-side banner | Bulk-action preflight |
|---|---|---|
| Item | Covered | Covered (Delete) |
| Item Supply | Not yet covered | Not yet covered |
| Business Affiliate | Not yet covered | Not yet covered |
| Order, Kanban Card (detail) | Covered indirectly through the item-details panel; standalone detail surfaces are not yet on the staleness signal |
Copyright: © Arda Systems 2025-2026, All rights reserved