Skip to content

fix: send Not yet to the coordinator as the person's answer - #161

Merged
TheGreatAxios merged 4 commits into
mainfrom
cl-12078-treat-not-yet-as-a-decline-with-the-persons-reason-not-a
Nov 4, 2026
Merged

TheGreatAxios merged 4 commits into
mainfrom
cl-12078-treat-not-yet-as-a-decline-with-the-persons-reason-not-a

Conversation

@TheGreatAxios

@TheGreatAxios TheGreatAxios commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Closes CL-12078.

A decline of one of the coordinator's asks now reaches it as the person's answer. Hub middleware in front of Interchange's reject route writes the decline's message from apps/hub/src/config/decline.ts: "The person has chosen to decline this tool call." followed by their feedback, or, without words, a request to ask them what they would like to do next. Only approvals raised by a project coordinator's run get the note; the coder's package asks pass through unchanged.

On the page, a message sent while a card is open, or an approach chosen while an unrelated card is open, goes as the decline's feedback instead of a bare decline followed by a separate mail. When the ask was already decided elsewhere (409 already_resolved) the words go as mail instead. Not yet puts the cursor in the composer once the decline has saved (the composer is disabled while it saves). The conversation reads the person's words back out of the note, so their message shows as they typed it, and a resumed coordinator reads "Rejected a request: ". The composer placeholder now reads "Say what you'd like changed, or ask a question…".

Constraints

  • Wording stays in configuration
  • The decline goes through Interchange's approval rejection, which already carries an optional message

Verification

  • bun run lint, format:check, typecheck, test, pins:verify, vendor:verify pass.
  • bun run test:e2e passes (9/9) against a throwaway Postgres. The recorded pipeline's decline of the approaches now asserts the coordinator's tool result contains "Their feedback is: I think Approach B is the best", which only the hub's note produces.
  • New unit tests: the middleware writes the note with and without words, leaves other runs' declines and malformed bodies alone; the conversation reads back only the person's words with and without a note; a message or approach choice sent while a card is open is the decline's feedback, not separate mail.
  • Not checked in a running app: the composer focus after Not yet.

A decline of a coordinator's ask now carries a hub-written note: the person's feedback when they typed some, or a request to ask them what next. A message sent while a card is open goes as that feedback instead of a separate mail, Not yet focuses the composer once the decline saves, and the conversation shows only the person's words.
@linear-code

linear-code Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Choosing an approach declines an unrelated open ask with the choice as its reason, a decline's 409 mails the words only when the ask was already decided, its fault shows once, the decline note's lookup is scoped to the tenant, and the hub's note is the one place that tells the coordinator what a decline means.
@TheGreatAxios
TheGreatAxios marked this pull request as ready for review November 4, 2026 20:16
@corbits-triage

corbits-triage Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Triage: In progress

Triage is waiting for code review before it decides. This comment shows the verdict once it has.

Comment thread apps/web/src/pages/workspace/index.tsx Resolved
Comment thread apps/hub/src/app.ts Resolved
Any 409 from declining an open card now sends the person's words as mail, as before, so a failed mail reports its own reason instead of a generic one. The decline note runs only after Interchange's tenant resolution, and a body it cannot read passes through.
@TheGreatAxios
TheGreatAxios merged commit 614c65a into main Nov 4, 2026
9 checks passed
Comment on lines 887 to 891
if (cause instanceof ProductApiError && cause.response.status === 409) {
setGateDone((value) => value + 1);
onChanged();
return null;
return "settled";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 · Any 409, not just already_resolved, makes the decline fall back to mail

saveDecision returns "settled" for every 409 without checking the response code, and declineWith then mails the words as if the ask were already decided. The PR says only already_resolved should do this. Other 409s mean the decline was never saved: project_busy, stale_revision, artifact_ask_coordinator_unavailable and project_archived. In those cases the card stays open, the parked coordinator never gets the person's answer, and their words go as ordinary mail. The person sees no error, and an archived project's mail send can fail with a misleading fault.

The person types a message while a coordinator card is open and the reject call returns 409 with code project_busy (or stale_revision).

Resolve this comment. Triage will not finish while it is open.

Comment on lines +156 to +157
} else if (openApprovalId !== undefined)
return (await decline(openApprovalId)) ? null : notSent;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 · Approach choice drops the specific decline error and returns the generic notSent

Before, a failed 'no' decision returned its own error to the approaches view. Now every failed decline becomes notSent, so the view no longer shows why it failed; only the toast does.

Pick an approach while an unrelated coordinator card is open, and the decline request fails (for example a network or server error).

Resolve this comment. Triage will not finish while it is open.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant