Skip to content

Correlate question answers back to the question that asked them - #501

Merged
TheGreatAxios merged 4 commits into
cl-7190-ask-user-message-response-gatefrom
cl-7191-correlate-question-answers
Aug 30, 2026
Merged

Correlate question answers back to the question that asked them#501
TheGreatAxios merged 4 commits into
cl-7190-ask-user-message-response-gatefrom
cl-7191-correlate-question-answers

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Summary

Stacked on #500 (CL-7190). postQuestion mints a stable questionId and returns { messageId, questionId } — its own doc comment claimed a caller could correlate a later answer with it, but runAskUser called postQuestion as a bare statement and discarded the return entirely. On the answering side, a question response was relayed as an ordinary sendWorkbenchMessage with no interchangeCorrelationId, so the reactor's tryCorrelate (unmodified, pre-existing) had nothing to match against a parked message_response gate — an answer was just "the next thing the user said." An agent asking two questions before either was answered would apply the wrong answer to the wrong one.

  • beforeAskUser (CL-7190) already reuses postQuestion's questionId as its gate's correlationId. This PR wires the other half: the blocks/responses route now passes the answered block's own id as correlationId when relaying a question's answer — verified that for a question block, blockId is its questionId verbatim, the same way a poll's block id is its pollId (packages/chat/src/schema.ts's block_responses table comment).
  • That id threads through the plain-message mail path: SendWorkbenchMessageInputQueuedTurndispatchTurnBatchDispatchTurnInput/dispatchTurnWorkbenchMail.sendMailplatform-adapter.tsSendFoldedMailParams/deliverFoldedMailMIMEUserMessageParams/sendUserMessage, landing as headers.interchangeCorrelationId — the exact header tryCorrelate already reads generically, no reactor change needed.
  • Batching fix caught in review: dispatchTurnBatch initially took last.correlationId (mirroring how it already takes last.principalId), but a Critique pass found this drops the correlation entirely when the answer isn't the batch's last queued message (e.g. an unrelated follow-up queues behind it before the batch drains) — silently degrading to timeout-only resolution instead of the documented "whole blob still resolves" limitation. Fixed to take whichever queued turn in the batch carries a correlationId, with a regression test proving it.
  • postQuestion's stale doc comment (claiming the tool result "names" the id) is corrected.

Test plan

  • cd packages/interaction-tools && bun run typecheck && bun test — 11/11 pass
  • cd packages/folded-runs && bun run typecheck && bun test — 61/61 pass
  • cd vendor/intx/hub-sessions && bun run typecheck && bun test — 44/44 pass
  • cd packages/chat && bun run typecheck && bun test — 724/724 pass (37 skip, pre-existing)
  • bun run check:killdates, check:tool-package-pins, check:tool-package-freshness, check:licenses, check:deletion, check:browser-safe-subpaths — all pass
  • bunx prettier --check on every touched file
  • CI (workspace-wide) — not run locally per instructions; polled centrally

Interchange notes

No upstream Interchange defect. tryCorrelate/reactor correlation matching (vendor/intx/inference/src/reactor.ts) is untouched by this PR — it already read headers.interchangeCorrelationId generically; the gap was entirely in this repo's own packages/chat/packages/folded-runs mail-dispatch chain and one vendored hub-sessions field (UserMessageParams.correlationId) that hardcoded undefined.

A related, narrower architectural note (a crash-window race between postQuestion's external POST and the gate's durable commit) was filed separately as CL-7248 — out of scope for both this PR and CL-7190.

DO NOT MERGE — review together with #500; this branch depends on it.

Closes CL-7191.

Proves the block's own id (the same id postQuestion mints as questionId)
rides an answer's mail as its correlationId end to end through the real
HTTP route, including when batching would otherwise strand it: a batch
whose last queued message isn't the answer itself must still carry the
answer's correlationId, not drop it because the last message has none.
postQuestion minted a stable questionId that runAskUser discarded, and the
question-response route set no interchangeCorrelationId on the relayed
answer, so nothing tied answer N to question N beyond arrival order — two
questions asked before either was answered would resolve on a guess, not
a match. ask_user's beforeToolExtension (CL-7190, stacked below this)
already reuses questionId as its message_response gate's correlationId;
this wires the other half of the round trip.

The blocks/responses route now passes the answered block's own id (which
IS its questionId, the same way a poll's block id is its pollId) as
correlationId when relaying a question's answer. That id threads through
the plain-message mail path — SendWorkbenchMessageInput -> QueuedTurn ->
dispatchTurnBatch -> DispatchTurnInput/dispatchTurn -> WorkbenchMail.sendMail
-> platform-adapter -> SendFoldedMailParams/deliverFoldedMailMIME ->
UserMessageParams/sendUserMessage -- landing as headers.interchangeCorrelationId
on the InboundMessage the reactor's pre-existing, unmodified tryCorrelate
already reads generically. dispatchTurnBatch takes whichever queued turn in
a batch carries a correlationId, not only the batch's last message, since
principalId legitimately tracks "whoever sent last" but a gate answer does
not.
Records the UserMessageParams.correlationId delta in VENDORED.md and
vendor/intx/hub-sessions/VENDORED-FROM, with a matching tree-hash update
in scripts/checks/kill-dates.txt.
@TheGreatAxios
TheGreatAxios merged commit 3d153cd into main Aug 30, 2026
5 checks passed
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.

1 participant