Skip to content

feat: implement endsTurn behavior for ask-question action and enhance… - #2774

Open
manucorporat wants to merge 2 commits into
mainfrom
fix-ask-user-question
Open

feat: implement endsTurn behavior for ask-question action and enhance…#2774
manucorporat wants to merge 2 commits into
mainfrom
fix-ask-user-question

Conversation

@manucorporat

Copy link
Copy Markdown
Contributor

… guided question handling

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Here's a visual recap of what changed:

Visual recap

Open the full interactive recap

@builder-io-integration builder-io-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Builder reviewed your changes and found 1 potential issue 🟡

Review Details

Code Review Summary

PR #2774 adds explicit endsTurn handling for ask-question, prevents later tool calls in the same assistant message from executing after a successful user-blocking action, and scopes agent-written guided-question payloads to the asking chat thread. The overall approach is sound: the loop reports a typed input_required outcome, preserves the existing integration message prefix, and the new tests cover both stopping and non-stopping actions plus thread visibility. This is a standard-risk change because it modifies shared agent-loop behavior and client state synchronization.

Key Findings

🟡 MEDIUM: The guided-question state retention check does not include threadId, so identical questions can remain associated with the previous chat when one thread replaces another.

🧪 Browser testing: Will run after this review (PR touches UI code)

// user can still answer it by going back to that chat — but it must not
// render here.
const visiblePayload =
payload && payloadBelongsToThread(payload, threadId) ? payload : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Include thread identity when retaining a question payload

When setPayload preserves the previous payload, its equality check compares only the questions fingerprint and clientResolveId, not threadId. If another chat writes an otherwise identical question, the old payload can remain associated with the first thread: it is hidden in the new chat and reappears when returning to the old one. Include threadId in the preservation condition and add a replacement-flow regression test.

Additional Info
Found by 1 of 3 code-review agents; confirmed by inspecting the state-retention effect and thread filtering.

Fix in Builder

@netlify

This comment has been minimized.

@builder-io-integration builder-io-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Builder reviewed your changes and found 4 potential issues 🟡

Review Details

Incremental Code Review Summary

The latest PR head adds the package changeset; the implementation diff for endsTurn, guided-question thread filtering, and chat propagation remains otherwise unchanged. The previously reported thread-retention issue is still present and remains open, so it was intentionally not reposted. This is a standard-risk change because it modifies shared agent-loop control flow, action replay behavior, and user-facing guided-question delivery.

New Findings

🟡 MEDIUM: endsTurn actions can be included in parallel-safe batches, allowing later calls to execute before the yielding action sets the flag.

🟡 MEDIUM: Successful journal/ledger replay paths can bypass the new endsTurn transition and continue a resumed run.

🟡 MEDIUM: Validation failures returned as ordinary strings by ask-question are treated as successful yields, leaving the user waiting for a card that was never persisted.

🟡 MEDIUM: Synthetic tool_start events for skipped calls can cause Slack extraction to select a skipped question instead of the executed one.

🧪 Browser testing: Will run after this review (PR touches UI code)

Comment on lines +6191 to +6194
if (turnYieldedToUser) {
await flushParallelBatch();
toolResultParts.push(skipToolCallAfterYield(toolCall));
continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Treat endsTurn actions as parallel-batch boundaries

turnYieldedToUser is checked only while building batches. If an endsTurn action is also marked read-only or parallel-safe, it can be grouped with later compatible calls and Promise.all starts them before the yielding action completes, violating the contract that remaining calls must not execute. Make endsTurn actions a batch boundary or reject incompatible parallel flags, and add a regression test.

Additional Info
New finding from 1 of 3 incremental review agents; the generic exported ActionEntry contract permits these combinations.

Fix in Builder

...(actionEntry.chatUI ? { chatUI: actionEntry.chatUI } : {}),
});
recordToolResult(result, isError);
if (!isError && actionEntry.endsTurn === true) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Apply endsTurn when replaying completed actions

Successful journal replay and interrupted-write recovery paths return before this normal execution branch, even though they represent an action that already completed successfully. On a resumed run, a replayed ask-question can therefore continue into another model iteration and issue another question. Centralize the successful endsTurn transition or invoke it in each successful replay/recovery path, with a resume regression test.

Additional Info
New finding from 1 of 3 incremental review agents; replay/recovery paths were traced in production-agent.ts.

Fix in Builder

...(actionEntry.chatUI ? { chatUI: actionEntry.chatUI } : {}),
});
recordToolResult(result, isError);
if (!isError && actionEntry.endsTurn === true) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Do not yield when ask-question delivery fails validation

ask-question returns several validation failures as strings (for example, invalid JSON in options) rather than throwing, so isError remains false and this branch reports input_required even though no question card was persisted. Use a typed action error or an explicit successful-delivery signal so malformed calls can be corrected instead of leaving the user waiting for a nonexistent card.

Additional Info
New finding from 1 of 3 incremental review agents; validation-return behavior is in context-tools.ts.

Fix in Builder

const result =
`Not executed: ${toolCall.name} was called after an action that ends the turn. ` +
`The turn is paused for the user's answer — call it again on a later turn if still needed.`;
send({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Do not emit real tool starts for skipped calls

The skip path emits a synthetic tool_start for a call that never ran. Slack’s input-request extraction can select the last ask-question tool start, so when a duplicate question is skipped it may deliver the skipped question text instead of the executed question that actually paused the run. Omit the synthetic start or make extraction select the successful call’s id, and add a duplicate-question Slack regression test.

Additional Info
New finding from 1 of 3 incremental review agents; cross-checked against the skipped-call event construction and Slack extraction behavior.

Fix in Builder

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants