Skip to content

feat(design): give the canvas layout route a revision, so saves, reads and change events order themselves #3203

Open

Description

@doblas

Before submitting

  • I searched open and closed issues and did not find a request for this feature.
  • I reviewed this request and removed credentials, tokens, private paths, hostnames, and other sensitive data.

Problem or opportunity

Follow-up from @egdev6's review of #3146 (Gentle Design canvas, layout and minimap; part of #2740).

assets/design-canvas/canvas-layout.js keeps the layout of a design consistent between the page and design/<slug>/layout.json with several counters and flags: saves in flight, reads in flight, change reads in flight, a pending read, layout views, and the move in progress. It recognizes the page's own save, when its file-change event comes back, by comparing JSON.stringify output with what it last saved.

That works, and each race has a regression test, but the rules are spread over several functions, and the string comparison depends on the server writing keys in the same order as the page sent them. Each new race so far needed another counter or flag.

Proposed outcome

The api/designs/<slug>/layout route returns a revision for the file (a counter or an ETag-like hash), on GET and in the POST reply:

  • The page sends the revision it built on with each save, and the server can refuse a save based on a stale revision. That turns out-of-order saves and "another page changed it meanwhile" into one explicit answer.
  • A read or a change event is applied only when its revision is newer than the one the page shows. That covers stale reads and the page's own save echo without comparing JSON strings.
  • canvas-layout.js drops most of its counters in favor of that one rule.

Alternatives considered

  • Keep the counters and flags, which are covered by tests today. That means less churn, but every new race adds state.
  • Compare file modification times instead of a revision. That is coarser, and two saves within the same timestamp tick would look alike.

Additional context

Activity

  1. egdev6 commented on Nov 13, 2026

    @egdev6
    SponsorContributor

    @doblas thanks for writing this up. Triage: worth doing, but it needs a few decisions before implementation, and I'd split it.

    • The complexity in canvas-layout.js is real. I couldn't confirm the key-order fragility in practice, though: lib/design-layout.ts rebuilds the layout preserving entry order, so the JSON comparison is stable today.
    • A content hash identifies a version but doesn't order versions, and an in-memory counter is lost on restart and doesn't see external edits. Whatever we pick, the compare-and-set has to happen inside the existing per-file writer queue, and it still won't protect against writers outside that queue (another process or an editor).
    • Putting the revision inside layout.json is a format change: extra keys are rejected today.

    Decisions needed first: where the revision lives (in the file, a sidecar, or memory), what GET/POST return for a missing file, and the UX of a 409 conflict versus today's "the other page's change wins".

    Suggested first slice: agree on and test the concurrency contract on the server side, then simplify the client in a second PR.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions