fix(gui): fixture emits todo/write at the real tool boundary

The tool appends the snapshot mid-execution, between tool/call and
tool/result; the fixture spliced it after step/end with a post-turn
timestamp, so acceptance never exercised the production ordering. A spec
pins call → snapshot → result with monotonic times.
This commit is contained in:
Chinesezjc
2026-07-23 15:56:28 +08:00
parent 02abe3c821
commit f2a9c09429
2 changed files with 21 additions and 2 deletions

View File

@@ -133,8 +133,12 @@ function buildAlphaLog(): SessionEvent[] {
]
const todoArgs = JSON.stringify({ todos: fixtureTodos })
toolTurn(64, 'todo_write', todoArgs, 'Updated todo list: 1 pending, 1 in progress, 1 completed.')
// The tool appends the snapshot event inside its own turn; splice it before the trailing turn/end.
events.splice(events.length - 1, 0, { type: 'todo/write', time: time += 800, data: { todos: fixtureTodos } })
// The real tool appends the snapshot mid-execution — between tool/call and
// tool/result — so the fixture reproduces that exact ordering (the last
// toolTurn events run ... tool/call, tool/result, step/end, turn/end).
const callIndex = events.length - 4
const callTime = events[callIndex]?.time as number
events.splice(callIndex + 1, 0, { type: 'todo/write', time: callTime + 400, data: { todos: fixtureTodos } })
events.forEach((e, i) => { e.seq = i })
return events as unknown as SessionEvent[]
}

View File

@@ -71,6 +71,21 @@ describe('createFixtureApi', () => {
expect(empty.result.value).toEqual({ events: [], hasMore: false })
})
it('emits the todo/write snapshot at the real tool boundary: between tool/call and tool/result, timestamps monotonic', async () => {
const api = createFixtureApi()
const tail = await api.sessions.history(req({ sessionId: sid('fx-alpha'), maxMessages: 10 }))
if (!tail.result.ok) throw new Error('history failed')
const events = tail.result.value.events.map(e => e.event)
const todoAt = events.findIndex(e => e.type === 'todo/write')
expect(todoAt).toBeGreaterThan(0)
// Production ordering (the tool appends mid-execution): call → snapshot → result.
expect(events[todoAt - 1]?.type).toBe('tool/call')
expect(events[todoAt + 1]?.type).toBe('tool/result')
const times = events.slice(todoAt - 1, todoAt + 2).map(e => e.time)
expect(times[0]).toBeLessThanOrEqual(times[1] ?? 0)
expect(times[1]).toBeLessThanOrEqual(times[2] ?? 0)
})
it('create adds a session and pushes host/session-added to open host streams', async () => {
const api = createFixtureApi()
const abort = new AbortController()