mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-22 00:03:35 +00:00
fix: credit only a row the live item actually wrote
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d53075edef
commit
8f70a64d50
@@ -249,6 +249,9 @@ class Entry<V> {
|
||||
* each, and each row a caller says it wrote for one of them. A row left over after that is a
|
||||
* row nobody here has the value of. */
|
||||
private values = 0
|
||||
/** Whether the last value handed in made this entry write a row of its own. Loading, it
|
||||
* cannot: the caller's own row is then the only one, and is already counted. */
|
||||
private seedWroteRow = false
|
||||
private queue: Promise<unknown> = Promise.resolve()
|
||||
private ports: ItemRowPort
|
||||
private store: StoreInternals
|
||||
@@ -528,18 +531,21 @@ class Entry<V> {
|
||||
* rather than leaving the row to this entry: the two are one value, and counting the second
|
||||
* row as unaccounted would read as someone else having written it. */
|
||||
noteRow(): void {
|
||||
this.values++
|
||||
if (this.seedWroteRow) this.values++
|
||||
}
|
||||
|
||||
applyExternal(value: V): number {
|
||||
this.externals++
|
||||
this.values++
|
||||
this.seedWroteRow = false
|
||||
if (this.loaded && serialize(value) === serialize(this.value)) return this.revision
|
||||
this.edits++
|
||||
this.pristine = false
|
||||
this.removed = false
|
||||
this.replaceValue(value)
|
||||
const rowsBefore = this.ports.rowMark(this.key)
|
||||
this.reconcile()
|
||||
this.seedWroteRow = this.ports.rowMark(this.key) !== rowsBefore
|
||||
return this.revision
|
||||
}
|
||||
|
||||
|
||||
@@ -906,6 +906,33 @@ describe('item store: origins', () => {
|
||||
expect(rows.writes.at(-1)).toEqual({ path: 'u/me/r', value: typed })
|
||||
})
|
||||
|
||||
it('is not fooled by the chat crediting a row a loading item never wrote', async () => {
|
||||
const rows = fakeRows()
|
||||
const read = deferred<ItemLoad<Res>>()
|
||||
const store = createItemStore(rows.port)
|
||||
const key: ItemKey = { workspace: 'w', kind: 'resource', path: 'u/me/r' }
|
||||
const { handle: item } = store.acquire(
|
||||
key,
|
||||
{ workspace: 'w', path: 'u/me/r' },
|
||||
adapter(() => read.promise)
|
||||
)
|
||||
await settle()
|
||||
|
||||
// The chat seeds and persists while the first GET is out. Loading, this entry writes no
|
||||
// row of its own, so the chat's save is the only row that value produced.
|
||||
item.applyExternal({ ...deployedRes, description: 'from the chat' })
|
||||
store.bridge.noteRow('w', 'resource', 'u/me/r')
|
||||
rows.handExternally('u/me/r')
|
||||
// The legacy agent editor then writes the same resource: a row nobody here has.
|
||||
rows.handExternally('u/me/r')
|
||||
read.resolve({ deployed: deployedRes })
|
||||
await settle()
|
||||
|
||||
// Two rows, one of them unaccounted for. Crediting the chat twice would hide it and let
|
||||
// this editor post over the agent editor's newer draft.
|
||||
expect(item.canSave).toBe(false)
|
||||
})
|
||||
|
||||
it('stays blocked when an unseen row follows the edit it kept', async () => {
|
||||
const rows = fakeRows()
|
||||
const read = deferred<ItemLoad<Res>>()
|
||||
|
||||
Reference in New Issue
Block a user