Name the Bot on a hop that was refused, retried or lost, not only on one that landed - #306
Merged
davidmckayv merged 3 commits intoAug 31, 2026
Conversation
…one that landed
The Audit screen's Bot column reads `payload.bot` and nothing else, and renders a dash without it.
`agent.handoff_offered` says so in its own comment, added when the accepted pair was fixed:
without this the two handoff rows are the only Bot actions on a screen headed "Every action a
Bot took" that name no Bot.
The pair got the key. The four rows either side of it did not: `agent.handoff_refused`, both
`agent.handoff_retried` rows, and `agent.handoff_failed` carry `from` and no `bot`, so every one of
them draws a dash.
Those are the rows the trail exists for. `server/src/audit.ts` says it outright of the first one —
"a hop that happened is visible in the transcript anyway; a hop that was refused is invisible
everywhere else" — and the same holds for one that ran out of attempts, which is where a person's
unanswered question ends. So the screen named the Bot on the outcome you could already see, and drew
a dash on the three you could not.
`agent.escalated` and `agent.escalation_failed` one file over share a single payload and get this
right on both outcomes, which is the shape being restored here.
Pairs with CopilotKit#302, which stops that same screen calling `agent.handoff_refused` "Allowed". Together a
refused hop reads as a refusal and says whose it was; either alone leaves half the row wrong.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
kevin9327
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso and
tylerslaton
as code owners
August 31, 2026 11:38
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
# Conflicts: # CHANGELOG.md
davidmckayv
previously approved these changes
Aug 31, 2026
davidmckayv
left a comment
Contributor
There was a problem hiding this comment.
Verified + CI verify green; template-appropriate correctness fix.
# Conflicts: # CHANGELOG.md
davidmckayv
approved these changes
Aug 31, 2026
davidmckayv
left a comment
Contributor
There was a problem hiding this comment.
CI verify green pre-rebase; CHANGELOG-only rebase; template-appropriate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
The Audit screen's Bot column reads
payload.botand nothing else(
app/src/routes/_authed/admin/audit.tsx:259-264), rendering-when it is absent.agent.handoff_offeredexplains why that matters, in the comment added when it was given the key(
server/src/agents/handoff.ts:386-389):agent.handoff_deliveredgot it too (handoff-runner.ts:358-360, "See the same key onagent.handoff_offered"). The four rows either side of that pair did not:payload.botagent.handoff_offeredhandoff.ts:390agent.handoff_deliveredhandoff-runner.ts:360agent.handoff_refusedhandoff.ts:112-120-agent.handoff_retried(second attempt)handoff-runner.ts:268-274-agent.handoff_retried(lease gone elsewhere)handoff-runner.ts:342-348-agent.handoff_failedhandoff-runner.ts:408-415-So the screen names the Bot on the one outcome you can already see elsewhere, and draws a dash on the
three you cannot.
server/src/audit.ts:341-347says this of the first of them:The same holds for
agent.handoff_failed, which is where a person's unanswered question ends.agent.escalated/agent.escalation_failedone file over share a single payload carryingbot(
server/src/agents/escalation.ts:119-135), so escalation is right on both outcomes. That is theshape this restores — four lines, no other change.
Pairs with #302, which stops the same screen labelling
agent.handoff_refusedas Allowed.Together, a refused hop reads as a refusal and says whose it was. Either alone leaves half the row
wrong. They touch different halves of the repo and merge independently (only the
CHANGELOG.mdanchor collides).
Where it runs
work.fromBotIdcomes off the queued rowevery replica reads, and
from.botIdoff the signed run assertion, so any replica deliveringthe hop writes the same value.
audit_eventsis append-only and these are the sameinserts with one more key.
Boundary and audit
attributable, which is what an audit row is for.
from.botIdcomes from the signed run assertion and
work.fromBotIdfrom the row this deployment wrote,never from anything a model produced.
Changelog
CHANGELOG.md, underUnreleased.Proof
Three assertions, in the two files that already assert this for the accepted pair:
server/tests/agent-handoff.test.ts— the existing "both outcomes leave a row naming the run andthe reason" now asserts
boton the refused half as well as the accepted one, and a new casewalks all five refusal reasons (
no_task,not_granted,unknown_bot, depth cap, fan-outcap).
server/tests/agent-handoff-runner.test.ts— a sibling to the existing "a delivery names the Botthat handed the work over", covering
agent.handoff_retriedandagent.handoff_failed.Against
main'shandoff.tsandhandoff-runner.tswith the new assertions in place:With the change: