Skip to content

Board-access mutations can commit and settle out of user-intent order #3333

Description

@Chris0Jeky

Summary

frontend/taskdeck-web/src/store/permissionsStore.ts starts updateAccess and revokeAccess immediately and independently for the same access row. The API exposes no expected revision/If-Match field, and BoardAccess is not configured with a concurrency token. Same-entry user intent can therefore be observed or committed out of order.

This is a source-traced successor to #3328 / PR #3330. Keep it separate: #3330 owns stale reads, session replacement and operation-owned loading. This issue owns ordering between live-session writes to one access entry.

Reachable schedules

Two role updates

  1. The user selects Editor, then quickly selects Admin for the same access row.
  2. Both PUTs start concurrently.
  3. The Admin response settles first and the cache becomes Admin.
  4. The older Editor response settles last and replaces it with Editor.

Even if the server committed Admin last, the client can show Editor. Because the server has no revision precondition, commit order itself can also diverge from click order.

Update followed by revoke

An update and revoke for the same access row can race. Depending on database scheduling, the obsolete update may fail after the delete, the delete may remove a newer role change, or the client may report successes/errors in an order that does not match the submitted intent. Current client logic avoids re-inserting an already removed row, but it does not make transport or outcome ordering truthful.

Expected contract

  • One mutation lane per stable access ID serializes update/revoke transport in user-intent order.
  • The first mutation starts transport immediately. Later same-entry work waits; unrelated access IDs and boards remain concurrent.
  • A queued mutation rechecks the initiating session epoch before transport, so logout/login cannot run old intent under replacement credentials.
  • A failed predecessor does not cancel the next queued intent.
  • Success, failure, toast, error and loading settlement remain operation/session owned as established by fix(permissions): keep board-access reads behind confirmed writes #3330.
  • Explicitly document the boundary: this preserves one client’s intent order but does not solve cross-device concurrency without a server revision contract.

Acceptance

  • Deferred Pinia regression for two role updates whose promises would otherwise settle in reverse order; the second transport must not start before the first settles.
  • Update→revoke and revoke→update schedules have explicit outcomes and no resurrection.
  • Failure of the first mutation still permits the queued second mutation to run.
  • Logout/session replacement prevents queued old-session transport.
  • Different access IDs remain concurrent.
  • Existing fix(permissions): keep board-access reads behind confirmed writes #3330 read/mutation/session ownership tests remain green.
  • Frontend lint, typecheck, build, full Vitest and exact-head hosted CI pass.

Server follow-up boundary

The current DTO/entity/configuration expose no expected UpdatedAt or concurrency token. If cross-client last-intent or conflict semantics are required, add a separate backend/API issue with deterministic SQLite races rather than claiming client serialization solves it.

Provenance

Found during fresh-context review of PR #3330 and the corresponding BoardAccessService, DTO and EF configuration on 2026-09-21. Searches found no matching open issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority IIThe active direction's next tranche: wedge capabilities, significant defects, near-term hardening.bugSomething isn't workingfrontendPrimary implementation impact in Vue/TypeScript UI and client runtime.

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions