fix: an unclaimed deploy always confirms, and the prompt keeps warning a loaded teammate draft

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-09-15 18:44:55 +02:00
co-authored by Claude Opus 5
parent 1662dfbc33
commit 60bf076b60
5 changed files with 35 additions and 42 deletions
@@ -409,11 +409,11 @@
// must stay set; `claimed` is the version this deploy can prove it wrote.
const claimed = versionThisDeployWrote(appHistory, $userStore?.username, anchor)
version = appHistory[0]?.version
// A deploy landed beside this one, so the head is someone else's as far as this
// editor knows and is no longer a base it may compare against: `compareVersions`
// confirms instead. A failed anchor read claims nothing either, but it is no
// evidence of that, so it does not arm this.
baseUnknown = claimed === undefined && anchor != null
// With no claim the head may be another deploy's, so it is not a base this editor
// may compare against: `compareVersions` confirms instead until a later deploy
// claims one or the editor is reset. A failed anchor read is indistinguishable
// from that here, and confirming is the side that cannot lose someone's work.
baseUnknown = claimed === undefined
// Re-pin the fork base to the version just written: the editor stays open, so a
// follow-up deploy (or a new edit) would otherwise compare against the now-
// superseded base and falsely warn. parent_version is in
@@ -98,15 +98,12 @@
// staleness outright.
const useVersion = $derived(draftBaseVersion != null && deployedHeadVersion != null)
const isStale = $derived(
// Both paths need a draft to be out of date: the app editors stay open across a
// deploy and clear `draftSavedAt` when it consumes theirs, while the version pair
// stays armed for the draft the next edit starts, which is what the prompt would
// otherwise offer to discard seconds after a successful deploy.
!!onLoadLatestDeploy &&
!!draftSavedAt &&
(useVersion
? draftBaseVersion !== deployedHeadVersion
: !!deployedAt && new Date(draftSavedAt).getTime() < new Date(deployedAt).getTime())
: !!draftSavedAt &&
!!deployedAt &&
new Date(draftSavedAt).getTime() < new Date(deployedAt).getTime())
)
// Key on the versions (not `draftSavedAt`) in the version path, else every
// autosave would mint a new key and re-pop the modal mid-edit.
@@ -567,11 +567,11 @@
// guard compares the claimed base when there is one and the head otherwise.
const claimed = versionThisDeployWrote(appHistory, $userStore?.username, anchor)
version = appHistory[0]?.version
// A deploy landed beside this one, so the head is someone else's as far as this
// editor knows and is no longer a base it may compare against: `compareVersions`
// confirms instead. A failed anchor read claims nothing either, but it is no
// evidence of that, so it does not arm this.
baseUnknown = claimed === undefined && anchor != null
// With no claim the head may be another deploy's, so it is not a base this editor
// may compare against: `compareVersions` confirms instead until a later deploy
// claims one or the editor is reset. A failed anchor read is indistinguishable
// from that here, and confirming is the side that cannot lose someone's work.
baseUnknown = claimed === undefined
closeSaveDrawer()
sendUserToast('App deployed successfully')
@@ -496,24 +496,23 @@
othersDraftsCount={otherDraftsUsers.length}
onOpenOthersDrafts={() => (othersModalOpen = true)}
onDeploy={({ version, head, headBy, headAt }) => {
// The editor stays open across a deploy, so what the out-of-date prompt reads
// has to move with it: the base is what the deploy could claim it wrote
// (unknown when another landed beside it), and the head is what is deployed,
// named by whoever deployed it rather than by the page load's author.
draftBaseVersion = version != null ? String(version) : undefined
// The deploy consumed the draft, so nothing here is out of date yet; the pair
// above describes the draft the next edit will start.
if (version != null && head != null && version !== head) {
// The prompt talks about a draft, and there is none: say what happened
// instead, since this deploy is already superseded.
sendUserToast(`Version ${head} was deployed on top of yours (${version})`)
}
// The editor stays open across a deploy and pins what it wrote onto the value
// itself; the prompt's own pair is what the loader knows, and this deploy
// consumed the draft it described, so the route holds no base until it loads
// again (the deploy guard covers that window).
draftBaseVersion = undefined
draftSavedAt = undefined
if (head != null) {
// Named by whoever deployed the head, not by the page load's author.
deployedHeadVersion = String(head)
deployedBy = headBy
deployedAt = headAt
}
// Another deploy landed on top of this one: there is no draft for the prompt
// to talk about, so say what happened instead.
if (version != null && head != null && version !== head) {
sendUserToast(`Version ${head} was deployed on top of yours (${version})`)
}
}}
/>
</div>
@@ -656,27 +656,24 @@
}
: undefined}
onDeploy={({ version, head, headBy, headAt }) => {
// The version this deploy wrote is the base the next autosave carries; the
// head is what is deployed now. They differ when another deploy landed
// beside this one, and the next draft is then behind from the start. A
// deploy that could not claim a version leaves the base unknown rather
// than keeping the one it just superseded.
// The version this deploy wrote is what the next autosave forks from, so the
// editor carries it; the prompt's own pair is what the loader knows, and this
// deploy consumed the draft it described, so the route holds no base until it
// loads again (the deploy guard covers that window).
parentVersion = version
draftBaseVersion = version != null ? String(version) : undefined
// The deploy consumed the draft, so nothing here is out of date yet; the pair
// above describes the draft the next edit will start.
if (version != null && head != null && version !== head) {
// The prompt talks about a draft, and there is none: say what happened
// instead, since this deploy is already superseded.
sendUserToast(`Version ${head} was deployed on top of yours (${version})`)
}
draftBaseVersion = undefined
draftSavedAt = undefined
if (head != null) {
deployedHeadVersion = String(head)
// Named by whoever deployed the head, not by the page load's author.
deployedHeadVersion = String(head)
deployedBy = headBy
deployedAt = headAt
}
// Another deploy landed on top of this one: there is no draft for the prompt
// to talk about, so say what happened instead.
if (version != null && head != null && version !== head) {
sendUserToast(`Version ${head} was deployed on top of yours (${version})`)
}
}}
onResetToDeployed={reloadDeployed}
{loadedFromDraft}