fix(voice): log a real send before bookkeeping; document decision reasons

Second review pass on #835's diagnostics. Behaviour is unchanged.

- Report viewport_context.result as soon as the send returns, so a throw
  in the retained-frame bookkeeping cannot log a real send as "error".
- List the decision reasons and when each field is present in
  CURRENT-STATE, note that the rate limit can split a pair, and say which
  codes are defensive.
- Changelog: the decision is logged when an entity-context answer reaches
  the image step, not for every answer.
This commit is contained in:
manjunath22466 committed 2026-11-06 22:47:09 +05:30
1 parent 207eb9805d
commit d0a612786a
4 files changed
+34 -11

No files matched your search

+6 -4
View File
@@ -562,6 +562,12 @@ export class RealtimeViewport {
// the item we just deleted as null and fall through so the caller still
// issues queueResponseCreate WITHOUT the image, instead of stranding the turn.
const sent = this.sendRealtimeEvent(contextEvent, label);
// Report before the bookkeeping below, so a later throw cannot turn a
// real send into a logged "error".
report(
'viewport_context.result',
sent ? 'sent' : 'data-channel-send-failed',
);
this.lastViewportItemId = sent ? newItemId : null;
this.retainedFrame = sent
? {
@@ -570,10 +576,6 @@ export class RealtimeViewport {
cameraKey: this.readCameraKey(),
}
: null;
report(
'viewport_context.result',
sent ? 'sent' : 'data-channel-send-failed',
);
return sent;
}
+21
View File
@@ -536,3 +536,24 @@ test('a step that throws still closes its decision, then rethrows', async () =>
]);
}
});
test('a throw after a real send still logs it as sent', async () => {
let reads = 0;
const { viewport, events } = recordingViewport({
// The first read happens only after the image was handed to the channel.
readCameraKey: () => {
reads += 1;
throw new Error('camera gone');
},
});
await assert.rejects(
viewport.sendVisualContextIfUseful(localContext()),
/camera gone/,
);
assert.equal(reads, 1);
const outcomes = events.filter((item) => outcomeEvents.has(item.event));
assert.deepEqual(
outcomes.map((item) => [item.event, item.detail.reason]),
[['viewport_context.result', 'sent']],
);
});