perf: push the first-turn session title as soon as it exists - #1374
Merged
Merged
Conversation
Four delays sat between Nova Micro answering and the user seeing the title: - The title was only checked between agent events, so it waited out the model's time-to-first-token and every long tool call. It now also rides the coordinator's 100ms live status merge (`poll_side_frame`). - The title task awaited its DynamoDB write before finishing, and the stream only emits a finished task. The write is now scheduled in the background. - That write took two round trips (GSI lookup, then update). Rows are born with the static SK, so it is now one keyed update guarded by attribute_exists, falling back to the GSI only for a legacy SK. - A new bedrock-runtime client per title: ~250ms on first use in a process (on the event loop) and a fresh TLS handshake every time. It is now built once, lazily, inside the worker thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the stream outruns title generation, the post-close metadata read sees the placeholder. Retry up to 5 times at 400ms (the same ~1.5s+ window) and stop early once the title arrived another way, so it appears within ~400ms of being written instead of after a fixed 1.5s wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The conversation title is generated by a Nova Micro side-channel call that runs alongside the agent stream. The call itself is quick, but several delays sat between Nova answering and the sidebar/top-nav showing the name.
What changed
Fix 1: poll the title every 100ms, not only between agent events. The one-shot
_session_title_ssecheck only ran when the agent stream yielded. A title that finished during the model's time-to-first-token or a long tool call waited until the next event. The check now also runs in the coordinator's existing live status merge (_merge_agent_status, 100ms), passed per turn aspoll_side_framethe same wayturn_leaseis. That merge runs every step in one context, so the OTel detach fix is unaffected. The between-event check stays, because it is the only route whileAGENT_STATUS_LIVE_DRAIN_ENABLED=false.Fix 2: stop waiting on the DynamoDB write.
generate_conversation_titleawaitedupdate_session_titlebefore returning, and the stream only emits a finished task. The write now runs as a background task, held by a strong reference so it can't be garbage-collected.Fix 3: write the title in one round trip.
update_session_titledid a GSI lookup followed byupdate_item. Every row is now born with the staticS#{session_id}SK, so the write is one keyedupdate_itemguarded byattribute_exists(PK). It falls back to the GSI only for a legacy timestamped SK, and the guard keeps it from creating a ghost row when the row is missing.Fix 4: build the Bedrock client once. A
bedrock-runtimeclient per title cost ~250ms on first use in a process (measured locally), and that time ran on the event loop. It also meant a new TLS handshake every time. The client is now cached and built lazily under a lock inside the worker thread.Fix 5 (SPA): faster fallback. When the stream outruns title generation, the post-close metadata read sees the placeholder. It used to retry once after 1.5s; it now retries up to 5 times at 400ms, the same overall window, and stops early once the title arrived another way.
Deliberately not done
doneto wait for a late title. The SPA clears loading and the Stop button on close, not ondone, so a hold would show "responding" for longer on every fast first turn. Fix 5 covers this case instead.Cost / TTFT
Tests
chat-http.service.spec.ts: 19 passed, 3 new covering retry-until-found, bounded give-up, and stop-when-applied.Verify on dev after merge
Timestamp the
session_titleframe againstdoneon:🤖 Generated with Claude Code