Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix: address chat lifecycle hook UAT findings - #27551

Closed
ibetitsmike wants to merge 6 commits into
mike/chat-hooks/apifrom
mike/chat-hooks/uat-fixes
Closed

fix: address chat lifecycle hook UAT findings#27551
ibetitsmike wants to merge 6 commits into
mike/chat-hooks/apifrom
mike/chat-hooks/uat-fixes

Conversation

@ibetitsmike

Copy link
Copy Markdown
Collaborator

Stack Context

This stack adds chat lifecycle hooks (#27401#27428#27429#27430). Full UAT against the stack tip surfaced defects that are in scope for the feature but not yet fixed. This PR carries the self-contained ones.

It sits on top of the stack rather than in the PRs the fixes logically belong to, because those heads already carry green CI and completed reviews. The stack merges together, so placement here changes nothing about what ships.

What?

Five independent fixes, one per commit:

Fix Area Symptom
HTTP/2 dispatch retry coderd/util/xnet The documented single connection retry never fired for HTTP/2 consumers
Dispatch log noise coderd/x/agenthooks Every failed dispatch logged a full stack trace
Denied execute label site A blocked command rendered as Ran cat, as if it had executed
Hook notice placement site The "prompt was rewritten" card rendered above the message it annotates
Convergence latency docs docs "briefly displays the original input" understated the window

Why?

HTTP/2 retry. Dispatcher.post retries only when xnet.IsConnectionError is true. An aborted response mid-request classifies as connection_error over HTTP/1.1 and is retried, but over HTTP/2 it arrives as a stream error, classified protocol_error, and the chat turn fails. Go's default transport negotiates h2 against any TLS consumer, so this is the common deployment shape, not an edge case.

Only transient codes (NO_ERROR, INTERNAL_ERROR, REFUSED_STREAM, CANCEL, ENHANCE_YOUR_CALM) are treated as retryable. PROTOCOL_ERROR and friends stay terminal so a malformed consumer response is not retried. The retry reuses the dispatch ID, which the consumer contract already requires to be idempotent.

Worth noting for review: net/http bundles its own HTTP/2 implementation with unexported error types. net/http/h2_error.go bridges only the struct form of a stream error to x/net/http2, so a pointer target or a GOAWAY target never matches. The check is deliberately narrow for that reason, and a test drives a real h2 server rather than asserting against synthetic x/net values that production never produces.

Denied execute label. #27430 already fixed this misrepresentation for write_file and edit_files. execute was the missed case. The label derives from isError (the tool-result protocol flag), not from the execute result's success field, so a real command that ran and exited non-zero correctly keeps its "Ran" wording.

Convergence docs. A batch dispatches sequentially before the assistant row commits, so the first call's original input stays visible until the whole batch is admitted. The bound scales with the number of tool calls in the step, not a single CODER_CHAT_HOOK_TIMEOUT.

Testing

  • New h2 and h1 abort tests drive a real TLS server; red-green verified by reverting the production change and confirming all three new tests fail.
  • go test -race ./coderd/util/xnet/... ./coderd/x/agenthooks/...
  • Story assertions added for the denied-execute label and the notice ordering.
  • pnpm lint:types, pnpm lint:compiler, pnpm run lint-docs.

Mux prepared this PR on Mike's behalf.

The pointer and GOAWAY branches were unreachable. net/http bundles its own
HTTP/2 types and h2_error.go bridges only the struct form of a stream error,
so errors.As never matched the other three targets. Their test coverage
asserted x/net values that production never produces.

Also correct the stale-input window in the hooks docs: a batch dispatches
sequentially before the assistant row commits, so the bound scales with the
number of tool calls rather than one timeout.
@github-actions

Copy link
Copy Markdown
Contributor

Docs preview

Check off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here.

@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

Closing: these fixes belong in the existing stack PRs, not in follow-ups. Folding them in place:

Mux closed this on Mike's behalf.

@ibetitsmike
ibetitsmike deleted the mike/chat-hooks/uat-fixes branch July 27, 2026 22:27
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