Skip to content

Commit 9d4172a

Browse files
committed
fix: batchNearby restores dropped ignorable items when bridge fails
When a single target is followed by ignorable messages and then a boundary (no second target found), the original code silently dropped those ignorable items — contradicting the documented contract that all items are preserved in-order. Fix: track skipped ignorable items in pendingIgnorable, flush them after the batch result so they're never lost. When bridge succeeds (second target found), pending items are consumed into the synthesized batch as expected.
1 parent bd3efaa commit 9d4172a

2 files changed

Lines changed: 16 additions & 8 deletions

File tree

webview-ui/src/utils/__tests__/batchNearby.spec.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,11 @@ describe("batchNearby", () => {
145145
isBoundary,
146146
synthesize: synthesizeBatch,
147147
})
148-
expect(result).toHaveLength(3)
148+
expect(result).toHaveLength(4)
149149
expect(result[0].text).toBe("match-1")
150-
expect(result[1].text).toBe("visible text")
151-
expect(result[2].text).toBe("match-2")
150+
expect(result[1].text).toBe("") // api_req_started restored after single target
151+
expect(result[2].text).toBe("visible text")
152+
expect(result[3].text).toBe("match-2")
152153
})
153154

154155
test("multiple batches separated by boundaries", () => {
@@ -186,7 +187,7 @@ describe("batchNearby", () => {
186187
isBoundary,
187188
synthesize: synthesizeBatch,
188189
})
189-
expect(result).toHaveLength(2) // api_req_started ignorableskipped; [BATCH:match-1,match-2, "feedback"]
190+
expect(result).toHaveLength(2) // bridge succeededpending consumed; [BATCH:match-1,match-2, "feedback"]
190191
expect(result[0].text).toBe("BATCH:match-1,match-2")
191192
expect(result[1].text).toBe("feedback")
192193
})
@@ -204,7 +205,7 @@ describe("batchNearby", () => {
204205
isBoundary,
205206
synthesize: synthesizeBatch,
206207
})
207-
expect(result).toHaveLength(2) // api_req_started ignorableskipped; [BATCH:match-1,match-2, "err"]
208+
expect(result).toHaveLength(2) // bridge succeededpending consumed; [BATCH:match-1,match-2, "err"]
208209
expect(result[0].text).toBe("BATCH:match-1,match-2")
209210
expect(result[1].text).toBe("err")
210211
})
@@ -222,7 +223,7 @@ describe("batchNearby", () => {
222223
isBoundary,
223224
synthesize: synthesizeBatch,
224225
})
225-
expect(result).toHaveLength(2) // api_req_started ignorableskipped; [BATCH:match-1,match-2, "ck"]
226+
expect(result).toHaveLength(2) // bridge succeededpending consumed; [BATCH:match-1,match-2, "ck"]
226227
expect(result[0].text).toBe("BATCH:match-1,match-2")
227228
expect(result[1].text).toBe("ck")
228229
})
@@ -240,7 +241,7 @@ describe("batchNearby", () => {
240241
isBoundary,
241242
synthesize: synthesizeBatch,
242243
})
243-
expect(result).toHaveLength(2) // api_req_started ignorableskipped; [BATCH:match-1,match-2, "done"]
244+
expect(result).toHaveLength(2) // bridge succeededpending consumed; [BATCH:match-1,match-2, "done"]
244245
expect(result[0].text).toBe("BATCH:match-1,match-2")
245246
expect(result[1].text).toBe("done")
246247
})

webview-ui/src/utils/batchNearby.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export function batchNearby<T>(items: T[], options: BatchNearbyOptions<T>): T[]
4343
// Start collecting a batch of targets, skipping ignorable messages in between
4444
const batch: T[] = [items[i]]
4545
let j = i + 1
46+
const pendingIgnorable: T[] = []
4647

4748
while (j < items.length) {
4849
if (isBoundary(items[j])) {
@@ -52,16 +53,22 @@ export function batchNearby<T>(items: T[], options: BatchNearbyOptions<T>): T[]
5253
batch.push(items[j])
5354
j++
5455
} else if (isIgnorableBetweenTargets(items[j])) {
55-
j++ // skip ignorable messages between targets
56+
pendingIgnorable.push(items[j]) // track but don't commit yet
57+
j++
5658
} else {
5759
break // non-ignorable, non-target message stops the batch
5860
}
5961
}
6062

6163
if (batch.length > 1) {
64+
// Bridge succeeded — pending ignorable items are metadata consumed by the batch
6265
result.push(synthesize(batch))
6366
} else {
67+
// Bridge failed — restore pending ignorable items to preserve in-order semantics
6468
result.push(batch[0])
69+
if (pendingIgnorable.length > 0) {
70+
result.push(...pendingIgnorable)
71+
}
6572
}
6673

6774
i = j // items[j] was not consumed — re-examine it on next iteration

0 commit comments

Comments
 (0)