Repository navigation
Show what an agent tool call will do on its approval card - #2088
Merged
Merged
Conversation
At the default tier an approval card was the only human check on an agent's side-effecting tool call, and it named only the run id, the tool and the tool's generic description; its own doc promised an argument summary that was never built, and the ledger kept only a hash of the arguments. A reviewer approved a merge without seeing the repository, the pull request, an outsider's fork head, the method, the branch deletion or the commit text, and a command without seeing what would run where. A rejected merge could be put to the reviewer again on a byte-identical card by adding an inert key, and an approved merge was not pinned to any commit, so a fork's owner could push between approval and execution. Before a call is parked, its tool now resolves what it will do (IAgentTool.PreviewAsync). A node tool's preview comes from its manifest: every declared input, the repository by its path and how the run is bound to it, and for a node naming a pull request its title, head (repository, branch, commit) and base, read with the connection credential. Anything outside the run's bound repositories, such as a fork's head, is flagged. The handler redacts it and puts each value on one line. The call's own arguments are shown whole, so a long command cannot hide its tail past a cut, and a call whose arguments exceed 8,000 characters is answered instead of parked; values the platform read (a pull request's title) are bounded. The card is plain text, because the chat shows a body as typed, and text the model chose cannot mention anyone. The preview is stamped on the ledger row in the same park CAS as the approval token, and the run's tool-call audit and the canvas approval bar show it. A call whose pull request cannot be read is answered without parking. A node tool now refuses input keys its schema does not declare, and advertises additionalProperties false, so an inert key reaches nothing. A reviewer's rejection now sticks to the call's target, keyed server-side: a merge's repository and pull request at the head and base its card pinned, any other call's arguments as the node reads them and the card shows them, so spacing a card shows as one space is one target. A fresh call on a target rejected earlier in the run is Denied without a card, while new commits on a pull request are a new request. Refusing rather than marking the re-ask: the reviewer's reason never reaches the model, so a reworded re-ask is not an informed correction, and a marked card would still invite approval fatigue on an irreversible merge. An expired, unanswered card is not a rejection and can be asked again. A target also holds at most one live card. An agent can open several endpoint connections, and two of them could park the same merge on two cards: rejecting one left the other approvable, and approving it merged the pull request the reviewer had just rejected. A fresh call on a target another call of the run awaits a reviewer on is now Denied, a rejection fails every undecided call of the run on its target, and an approved call re-checks its target before it runs, so a rejection outranks an approval that has not run yet. git.merge_pr pins the head and base its card showed (expectedHeadSha, expectedBaseBranch) and git.pr_review the head (expectedHeadSha), as declared inputs a workflow can bind too. The approved call runs with the row's pins over its own arguments. The pull-request service reads the pull request again just before a pinned merge or review and refuses one that moved (PullRequestMovedException): neither provider takes the base as a precondition, and GitHub records a review against an older commit rather than refusing it. The providers also send the head as their own precondition (GitHub merge sha and review commit_id, GitLab accept and approve sha), so a head that moves after that read fails too. Submitting a review takes a SubmitPullRequestReviewInput, like a merge. Migration 0242 adds approval_preview_jsonb and approval_target (nullable, no backfill). A row parked before it runs as it did.
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.
Summary
approval_preview_jsonbandapproval_target) and shown by the run's tool-call audit and the canvas approval bar.git.merge_prpins the head and base its card showed (expectedHeadSha,expectedBaseBranch), andgit.pr_reviewpins the head.PullRequestServicereads the pull request again just before a pinned merge or review and refuses one that moved (PullRequestMovedException, codepull_request_moved). The providers also send the head as their own precondition: GitHub mergeshaand reviewcommit_id, GitLab accept and approvesha. Submitting a review now takesSubmitPullRequestReviewInput, like a merge.Test plan
AgentToolApprovalPreviewFlowTests:ToolCallApprovalResolverTests(a rejection reaches undecided siblings),ToolCallLedgerServiceTests(awaiting-target lookup)ToolCallPreviewsTests,AgentToolPreviewerTests,McpRequestHandlerTests,PullRequestHeadPinTests(GitHubcommit_id, GitLab approveshaand 409),PullRequestServiceTests(the moved check), merge and review node tests,ToolCallAuditReaderQueryTests(the page query selects the preview),FailureTaxonomyTestsMessageBodyrenders an approval card verbatim with no mention;pnpm lint,tsc, vitest