Repository navigation
fix(agentcore): read checkpoint answers given in the gate UI - #587
Conversation
The gate UI answers a checkpoint (confirm_summary / request_plan_approval)
with the generic QuestionEditor, which writes option indices:
{ answers: [{ selectedOptions: [i], freeText? }] }. chosenLabel only read
perQuestion, freeText, decision or a bare string, so it returned '' for
every UI answer. The agent was told "nothing is authorized yet" after the
human clicked "Looks correct", re-raised the checkpoint, and looped; on the
resume leg no receipt was minted and a UI "Request changes" never recorded
the withdrawal.
chosenLabel now resolves a single well-formed index against the gate's own
questions[0].options at every call site (inline answer, resume-leg
rehydration, resume message). Only that exact label can authorize: a
multi-select, out-of-range or non-integer index, or a typed note, names no
label and re-asks. answerFreeText carries the UI note to the agent as
feedback.
JWThewes
left a comment
There was a problem hiding this comment.
That's a good finding. Thanks for this one. Looks good to me.
Review follow-up: chosenLabel takes the gate row so the answer and the options it indexes always come from the same row, and rehydration reads the latest gate's label once. Drop dead guards (the array branch for questions, which is always a JSON string; the negative-index check; the string branches in answerFreeText and resolveCheckpointAnswer), fix the misleading comments, and parametrise the existing tests over both answer shapes instead of copying them.
|
Sorry I had a review agent run a little cleaning. Should be good right now @JWThewes |
|
TEST PURPOSES ONLY — DO NOT TAKE INTO ACCOUNT. Automatically computed PR review signals (deterministic)Cognitive complexity and branch coverage help focus review on functions and their tests; these signals are advisory. Complexity and coverage — 2 functions
🟢 Improving · 🟠 Needs attention · 🔴 High risk · 🟣 Highest risk — = unavailable · n/a = not applicable · * = incomplete tests Isobar mapFixed repository map: red marks changed files; green marks static name matches or import links across components. These links indicate possible reach, not proven runtime impact or a risk grade. Signals
Legend
|

Summary
Clicking Looks correct on a checkpoint gate (
confirm_summary/request_plan_approval) in the UI never authorizes anything. The agent re-raises the checkpoint after every click, up toCHECKPOINT_MAX_ROUNDS.Cause. The UI renders checkpoint gates with the generic
QuestionEditor, which stores option indices:{ "answers": [{ "selectedOptions": [0] }] }chosenLabel(lambda/agentcore/mcp/process-bridge.js) only readperQuestion[0].answer,freeText,decisionor a bare string, so it returned""for every UI answer:formatResumeAnswer) told the agentThe human answered the summary-confirmation checkpoint: "" — nothing is authorized yet … call confirm_summary again.rehydrateAuthorizationsnever minted the receipt.re-ask.Observed on a deployed 2.11 stack: four answered rounds, each stored as
{answers:[{selectedOptions:[0]}]}with option 0 = "Looks correct", and the agent kept saying the confirmation "came back empty".Fix.
chosenLabel(answer, questions)resolves a single well-formed index against the gate's ownquestions[0].options, whichraiseCheckpointwrites, so the labels stay owned by the platform. All five call sites pass the gate'squestions.answerFreeTextpasses the reviewer's note (answers[0].freeText) to the agent as the requested-changes feedback.Tests
New tests use the real UI shape:
perQuestionand UI shapesformatResumeAnswerapprove and rejectThe UI-shape tests fail on
mainand pass with this change.vitest run --project=agentcore: everything passes exceptspawn-shutdown.test.js(5 s timeout under full-suite load). It passes 9/9 when run alone, repeatedly, and does not touch this code.npm run lint: 0 errors. The 3 warnings in the touched files are pre-existing.npm run format:checkandnpm run secretlint: clean.Not in this PR
changeControlChoice(run-stage.js) parses the strict change-control gate the same way, so it probably has the same blind spot for UI answers. I left it out to keep this PR focused; it can be a follow-up that shares the index-to-label helper.