Context — deferred from #341
#341 made the editable Dashboard and Workbench two editors over one committed StoredWorkspaceV1, with all edits (dashboard commands, saved-query mutations, import/replace) flowing through the single per-tab app.serializeWrite queue. That guarantees same-tab write ordering and consistent same-tab projection/export.
It does not protect against a cross-tab stale overwrite: WorkspaceRepository.commit() is documented last-write-wins with no compare-and-swap (src/workspace/workspace-repository.ts). Two browser tabs (Dashboard in one, Workbench in another) each hold their own serializeWrite chain and their own committedWorkspace baseline. If tab A commits, tab B's next commit — built from B's now-stale baseline — silently clobbers A's change. #341's own "Cross-tab minimum contract" section explicitly allows deferring this as long as reload consistency holds (it does) and same-tab writes are strictly serialized (they are).
Goal
Detect and refuse a stale cross-tab overwrite instead of silently clobbering. Options (pick one during design):
- Revision CAS in the repository:
commit(candidate, expectedRevision?) compares the persisted workspace/dashboard revision and returns a typed conflict result when it moved; callers surface a "workspace changed in another tab — reload" affordance.
- Storage-event / BroadcastChannel invalidation: a tab that observes another tab's commit refreshes its
committedWorkspace baseline (and re-projects) before its next write.
Acceptance
Non-goals
Filed as a scoped follow-up per #341's "Cross-tab minimum contract" section.
Context — deferred from #341
#341 made the editable Dashboard and Workbench two editors over one committed
StoredWorkspaceV1, with all edits (dashboard commands, saved-query mutations, import/replace) flowing through the single per-tabapp.serializeWritequeue. That guarantees same-tab write ordering and consistent same-tab projection/export.It does not protect against a cross-tab stale overwrite:
WorkspaceRepository.commit()is documented last-write-wins with no compare-and-swap (src/workspace/workspace-repository.ts). Two browser tabs (Dashboard in one, Workbench in another) each hold their ownserializeWritechain and their owncommittedWorkspacebaseline. If tab A commits, tab B's next commit — built from B's now-stale baseline — silently clobbers A's change. #341's own "Cross-tab minimum contract" section explicitly allows deferring this as long as reload consistency holds (it does) and same-tab writes are strictly serialized (they are).Goal
Detect and refuse a stale cross-tab overwrite instead of silently clobbering. Options (pick one during design):
commit(candidate, expectedRevision?)compares the persisted workspace/dashboard revision and returns a typedconflictresult when it moved; callers surface a "workspace changed in another tab — reload" affordance.committedWorkspacebaseline (and re-projects) before its next write.Acceptance
Non-goals
Filed as a scoped follow-up per #341's "Cross-tab minimum contract" section.