Skip to content

feat(chat): reviews in the stream; quick prompts as chat messages - #1056

Merged
selfcontained merged 1 commit into
mainfrom
agt_1fe338cb78f9/agent-cb78f9
Sep 4, 2026
Merged

feat(chat): reviews in the stream; quick prompts as chat messages#1056
selfcontained merged 1 commit into
mainfrom
agt_1fe338cb78f9/agent-cb78f9

Conversation

@selfcontained

Copy link
Copy Markdown
Owner

Two things a user can see in an agent's Chat feed that were invisible there before.

Reviews render in the stream

When a review is created against an agent's work, a block for it appears inline in that agent's Chat feed and links to the review.

The entry is derived at read time from reviews — a fifth feed source alongside chat messages, status events, agent-to-agent messages and shared media — rather than written as a chat row. So the block always shows the review's current status and counts, there is no second write path to keep in sync, and reviews that already exist show up with no backfill. A review that resolves updates its block in place.

  • Ranked above the existing four sources so in-flight cursors keep their meaning.
  • cursorClause gained an optional table alias: the review source is the only one whose query joins (agents for the reviewer's persona, review_feedback_items for the counts), and bare created_at/id would be ambiguous there.
  • review.created / review.updated / review_feedback.updated now also invalidate the chat feed query.

Visually it is the Reviews sidebar's collapsed row: that header was extracted as ReviewSummaryBlock and both surfaces render it. The sidebar passes expanded (chevron, sticky/pinned classes) and shows the date; the feed omits both — the post it sits in is already stamped. Clicking the block opens that review in the sidebar, expanded, through the same handler the Changes tab already used after submitting one.

Quick phrases and shortcut pins deliver as Chat messages

With the Chat surface on, POST /terminal/inject-phrase and POST /terminal/inject-pin/:pinId now go through ChatService.sendUserMessage — the same call the composer makes — so a click gets a user row, the injection envelope carrying its id, a post in the feed, and reply threading back to it.

The decision and delivery live in apps/server/src/chat/user-prompt.ts behind a narrow deps shape, stated once and reused at both call sites.

  • A phrase pasted to be edited (submit: false) keeps the pane path — there is no message yet.
  • A terminal session keeps the pane path: no CLI behind it to read the envelope.
  • With the flag off, nothing changes.

Behaviour change worth noting: these two clicks now honour the quiet gate, as a composed chat message does, where they previously set gate: false. The hold pill and "Send now" are visible in both Console and Chat.

The pin remains the trust boundary — a disabled or non-shortcut pin is still refused — and an undeliverable click (agent not running) fails with 409 before any row is written, so the feed never shows a post that did not land.

Testing

  • pnpm run check clean; pnpm run finalize:web built.
  • Unit suites green: 3179 server / 1754 web / 60 scripts. New coverage: the review feed source including a tie-break test paging 12 rows at one microsecond-identical instant across all five sources; deliverUserPrompt's routing and fallback; chat-surface cases on the pin route asserting no phantom post on a failed delivery.
  • Full E2E green (195 passed, 13 tmux-skipped).
  • Driven in a browser on an isolated dev stack with the flag on and seeded reviews.

Reviews

  • architecture-review — clean approval, no findings.
  • frontend-ux-review — one finding, fixed and verified: the post header named the reviewer from the agents list (its agent name) while the block said Review · {persona}, so one actor read as two names. reviewAuthor now prefers the server's reviewerName, matching the other author helpers in that file.

🤖 Generated with Claude Code

Two things a user can see in an agent's Chat feed that were invisible
there before.

Reviews now render as a block in the stream, linking to the review
itself. The entry is derived at read time from `reviews` — a fifth feed
source alongside chat messages, status events, agent-to-agent messages
and shared media — so a review's status and counts in the feed are
always its current ones, with no second write path to keep in sync and
no backfill for reviews that already exist. Ranked above the other
sources so existing cursors keep their meaning; `cursorClause` gained an
optional table alias, since the review source is the only one whose
query joins.

The block is the Reviews sidebar's own collapsed row, extracted as
`ReviewSummaryBlock`: the sidebar renders it with a chevron and a date,
the feed without either (the post it sits in is already stamped).
Clicking it opens that review in the sidebar, through the same handler
the Changes tab already used after submitting one.

Quick phrases and shortcut pins now deliver as Chat messages when the
surface is on: the same `sendUserMessage` the composer calls, so they
get a user row, the envelope carrying its id, a post in the feed, and
reply threading. A phrase pasted to be edited (`submit: false`) and a
terminal session keep the pane path, as does everything with the flag
off. These two clicks now honour the quiet gate, as a composed message
does; the pin remains the trust boundary, and an undeliverable click
still fails before any row is written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@selfcontained
selfcontained merged commit 5fe119c into main Sep 4, 2026
1 check passed
@selfcontained
selfcontained deleted the agt_1fe338cb78f9/agent-cb78f9 branch September 4, 2026 17:32
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