Skip to content
decodecodeveloper docs
Storefront → Blocks → Engineering reference

Keeping drafts current

Automatically incorporate published Git content into editor drafts, with file-level draft-wins selection and no JSON merge downloads.

An editor changes a homepage title while someone else publishes a new footer. Their draft should keep the title and pick up the footer, without asking them to create a branch or rebase.

The hosted site editor manages an internal branch for each draft, created on its first save. Editors work with drafts, previews and publishing; Git remains the durable history. This is the proposed synchronization design, tracked in the roadmap.

Synchronize active drafts, not every branch

The existing GitHub webhook intake can enqueue production changes. Record the latest repository production commit once, and the production commit each draft last incorporated. Comparing those stored identifiers needs no GitHub request.

  • On a production push, mark drafts stale and schedule background synchronization for drafts currently open.
  • When an inactive draft is reopened, synchronize it before returning its current editable snapshot.
  • Before a save or publish, reconcile against the latest production head. Serialize this work with other operations on the same draft.
  • Coalesce several production updates into one synchronization to the newest head. Leave inactive drafts alone until needed.

Keep periodic control-plane reconciliation as a fallback for missed events. Local deco serve keeps editing the developer's working tree and does not perform hosted branch synchronization. Production delivery reads only stored assets, never GitHub.

Synchronizing a draft requires GitHub reads and usually a commit. The cost follows active drafts and actual changes, not visitor traffic. A publication arriving after a synchronization began is incorporated by the next pass; drafts are not promised to match a perpetually moving head instantly.

File-level draft wins

Compare the previous incorporated production tree (B), the current draft tree (D), and the latest production tree (M) by file path and Git blob identity. A missing file is a deletion, distinct from a JSON entry whose value is null.

For each owned saved-content path, if D equals B, take M. Otherwise take D, including deletion. When D equals M there is no remaining override after advancing the incorporated base. This is a whole-file rule: no property-level merge, textual merge or conflict-marker parsing.

File change relative to BResult
Only production changed itAdopt production's blob
Only the draft changed itKeep the draft's blob
Both changed it, even in different JSON propertiesKeep the entire draft file
Draft deleted a file that production editedKeep the deletion
Draft edited a file that production deletedKeep the draft file
Both created the same path differentlyKeep the draft file
Both now contain the same blobUse that blob and clear the override

For example, if the draft changes a hero title and production changes the image in that same file, the whole draft hero wins, including its previous image. If production changes a separate footer file, the draft inherits that footer. This is intentional and matches the whole-block preview overlay, not an exceptional fallback for large files.

After incorporating production, advance the recorded base with the draft commit. Keep enough metadata on the commit to recover the incorporated production SHA if the database update is interrupted. Retain append-only history for managed drafts; automatic synchronization never asks editors to rebase or rewrites their saved commits.

Only retain overrides in the app root's saved-content paths. Code and generated schemas follow production; unrelated production repository and monorepo paths are preserved. Publishing reconciles again using this same rule and writes a content-only diff onto current production, respecting branch protection and required checks.

Reuse blobs without reading JSON

The whole-file decision uses tree metadata only. Reference existing blob IDs in the new tree; synchronization does not download or parse base, draft or production file bodies, even when they are huge. Traverse only owned subtrees, bound metadata concurrency, and detect truncated provider comparisons instead of silently omitting paths.

There is no property-merge size threshold or oversized-conflict fallback to implement. Content reads, editor writes and asset preparation still enforce their own limits; they cannot infer safe heap usage from this cheaper synchronization path.

Saves racing with synchronization

Coordinate saves, synchronization, discard and publication using durable per-draft serialization. External Git pushes still race with Studio: a branch update must verify its expected head, and if it moved, rebuild from the new head and recheck every precondition. Never resolve that race with an unconditional force update.

This policy is separate from two editors saving the same block. The first version may keep last-writer-wins autosave; clients that send ifMatch get an explicit conflict if synchronization or another editor changed the block. Preserve unsaved form state and show that newer stored content is available instead of silently replacing what the editor is typing.

Parse the merged content before committing: every file must still be valid JSON. Synchronization doesn't check content against the schema, and neither does publishing; references and routes are checked by deco check in CI. A failed synchronization leaves the draft intact and blocks publishing with a useful error.

What the editor sees

The draft lifecycle routes expose the latest incorporated production commit, synchronization state and a bounded summary of automatic resolutions. For example: "Updated published content; kept your hero file." Report when the draft file replaced a production change, without requiring manual conflict resolution. Keep the full audit record outside content snapshots.

Emit changes through Studio's existing event delivery for open editors, with a slow fallback poll. The portable content protocol remains polling-based and works without events. Branch creation, synchronization and rebasing are never tasks the business user has to perform.

Studio implementation

The Studio implementation handoff maps this design to existing Fast Preview modules and specifies persisted state, operation contracts, sequencing, initial limits, recovery scenarios and rollout phases.

Preview transport

Synchronization maintains Git content and its incorporated production metadata. Preview delivery is separately optimized: draft overlays contain only cumulative changed blocks and deletion tombstones, applied over whatever production the rendering server already has. The internal Git base is never a required preview baseRevision.