fix: carry a run-as-only edit onto the entry a move replaces it with

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vn1jCHUcAdaG9C4NsUehF
This commit is contained in:
Ruben Fiszel
2026-09-14 18:22:19 +02:00
co-authored by Claude Opus 5
parent 228da1bfca
commit a043f76f0c
2 changed files with 32 additions and 2 deletions
+4 -2
View File
@@ -418,9 +418,11 @@ class Entry<V> {
}
/** Take over the edits of a deployed entry this one replaced at its key: each field its value
* changed from its own deployed side, so they stay a draft over what this entry deployed. */
* changed from its own deployed side, so they stay a draft over what this entry deployed.
* Compared exactly, as a save is: a field the draft comparison ignores (run-as) is an edit. */
carryEditsOf(old: Entry<V>): void {
if (old.origin !== 'deployed' || !old.dirty || this.value === undefined) return
if (old.origin !== 'deployed' || old.value === undefined || old.deployed === undefined) return
if (this.value === undefined) return
const edited = old.value as Record<string, unknown>
const base = old.deployed as Record<string, unknown>
const next = snapshot(this.value) as Record<string, unknown>
+28
View File
@@ -530,6 +530,34 @@ describe('item store: one entry per key', () => {
expect(rows.writes.at(-1)).toEqual({ path: 'u/me/b', value: kept })
})
it('keeps a change the draft comparison ignores in an editor a move replaces', async () => {
type Sched = { path: string; summary: string; permissioned_as?: string }
const rows = fakeRows()
const store = createItemStore(rows.port)
const s: Sched = { path: 's', summary: '', permissioned_as: 'u/a' }
const { handle: open } = store.acquire(
{ workspace: 'w', kind: 'trigger_schedule', path: 's' },
{ workspace: 'w', path: 's' },
{
load: async () => ({ deployed: structuredClone(s) }),
write: async () => {}
} as ItemAdapter<Sched>
)
const temporary = newItemPath()
const { handle: moving } = store.acquire(
{ workspace: 'w', kind: 'trigger_schedule', path: temporary },
{ workspace: 'w', path: temporary, template: s },
{ write: async () => {} } as ItemAdapter<Sched>
)
await settle()
open.value = { ...s, permissioned_as: 'u/b' }
expect(open.dirty).toBe(false)
moving.value = { ...s, summary: 'moved' }
expect(await moving.save()).toMatchObject({ ok: true, moved: true })
expect(open.value).toEqual({ ...s, summary: 'moved', permissioned_as: 'u/b' })
})
it('writes an item it moves onto after the saves queued there, and supersedes later ones', async () => {
const rows = fakeRows()
const store = createItemStore(rows.port)