Skip to content

feat(feedback): retry-with-correction — the consequence behind a thumbs down - #1145

Merged
philmerrell merged 2 commits into
developfrom
feature/feedback-retry-correction
Sep 17, 2026
Merged

philmerrell merged 2 commits into
developfrom
feature/feedback-retry-correction

Conversation

@philmerrell

Copy link
Copy Markdown
Contributor

Stacked on #1142 (thumbs capture + read model). Re-target to develop after #1142 merges — an inner stacked PR gets no CI until then, and do not merge #1142 with --delete-branch or this PR closes with it. This PR's diff is the one commit on top.

Completes docs/specs/response-feedback.md §11 PR-1: the spec is emphatic that the thumb decays to nothing without a visible consequence, and #1142 shipped the thumb alone.

Why

A down-thumb that only files a row is a suggestion box. The spec's §7 "retry loop" turns it into a steering act — the user gets a better answer in seconds, and the platform gets a labeled pair. Without it the response rate goes to zero and the whole signal is dead within weeks.

Why a new turn, not /steer

Spec open question 1. /steer targets a running turn through the lease row; a finished turn is corrected by an ordinary next message. So the retry is a prefilled draft the user edits and sends — it goes where messages go (AgentCore Memory), never near the metadata table — and the only thing recorded on the feedback row is which message was the retry.

What

  • ComposerDraftService (session/services/session/composer-draft.service.ts): the one way a feature hands text to the composer without threading a view-child through the several app-chat-input placements. The composer consumes a draft for its own session (sets the textarea, resizes, focuses, clears the request) and never submits on the user's behalf.
  • "Retry with that in mind" in the down-thumb reason row (message-actions.component.ts): asks MessageFeedbackService.requestRetry, which drafts a correction template for the thumb's reason code (six templates, each ending where the user's own words belong) and remembers the pending retry per session.
  • The link: ChatRequestService hands the just-added user message to consumePendingRetry (both send paths). If it is the retry the thumb asked for, the row is PUT again with retryMessageId = that message's index. Never the text.
  • Backend: retryMessageId (optional, ge=0) on the request and the row; the write is now an update_item upsert so a later re-thumb on the same message keeps the link (spec open question 2 answered as record the pair without its content, so Phase 6 can find it once the consent decision is made). FEEDBACK_ROW_PROJECTION gains the field.
  • Admin profile: feedback.retried and feedback.reworkUsd — spec §7 "rework cost": the thumbed message's call rows plus the retry turn's assistant rows, found as the consecutive indexes after the retry (a gap is the next user message). null when nothing is priced. The Feedback tile shows N retried · $x.xx rework.
  • Spec §13 updated with the decision.

What this does not change

  • No new endpoint, flag, or CDK. Same RESPONSE_FEEDBACK_ENABLED kill switch; a hidden pair has no retry button.
  • Nothing in the inference path. The retry is an ordinary user message with an ordinary request.
  • Implicit signals, eval sampling and the author surfaces remain unbuilt (spec §11 PR-2, 4, 5).

Tests

  • Storage (moto): link set once and kept across a later thumb without it, reason removed on re-thumb, negative index refused; existing replace / delete / rollup tests still pass under the update_item rewrite.
  • Routes: retryMessageId forwarded and echoed; -1 is 422.
  • Profile join: retried counted; rework priced from the thumbed rows plus the retry turn's consecutive rows and not the following turn; retry with no rows yet prices only the thumbed side; nothing priced is null.
  • SPA (run from the worktree with a per-package link of the main install and the two preview libraries stubbed): new composer-draft.service spec (2); message-feedback.service spec (+4: template drafted for the reason, link PUT with the index only and consumed once, other-session sends leave it pending, every reason has a template); message-actions spec (+1 retry button, hidden on up); chat-input spec (+2: takes its own session's draft into the textarea without submitting, ignores another session's); chat-request spec (+1: offers the added message to the feedback service); session-profile.util spec (+1). Affected files: 164 passed. ng build (AOT) clean.
  • Full backend suite on this tree: 8810 passed, 3 skipped.
  • Not exercised live in the browser.

🤖 Generated with Claude Code

…bs down

response-feedback spec §11 PR-1's other half: a down-thumb's reason row
offers "Retry with that in mind", which prefills the composer with a
correction template for the reason code (new ComposerDraftService; the
composer consumes drafts for its session and never submits). When the user
sends it, ChatRequestService hands the added user message to
MessageFeedbackService.consumePendingRetry, which PUTs retryMessageId — the
message's index, never its text — onto the F# row. The write is now an
update_item upsert so a later re-thumb keeps the link.

Open question 1 settled as a new turn rather than /steer (which targets a
running turn). The admin profile reports feedback.retried and reworkUsd
(spec §7 "rework cost": the thumbed call rows plus the retry turn's
consecutive assistant rows). Spec §13 updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@philmerrell
philmerrell changed the base branch from feature/message-feedback-pr7 to develop September 17, 2026 00:47
@philmerrell
philmerrell merged commit 8911d85 into develop Sep 17, 2026
6 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