GeezerVison is showing github.com — feat(coderd): run the prompt hooks when a turn starts from a flagged message by ThomasK33 · Pull Request #32075 · coder/coder
Conversation
ThomasK33
commented
Nov 12, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
Why
Part of the Automations RFC (CODAGT-1135). An automation will publish a message into a chat on behalf of a webhook or a schedule. That message never goes through the HTTP send path, so its user_prompt_submit hooks do not run there. The RFC has chatd run them when the message starts a turn, keyed only on the prompt_hooks_pending flag, so chatd needs no knowledge of automations.
Stacked on #32067. Review only this diff. It merges after #32067.
This PR teaches chatd that one thing. Nothing sets the flag yet (the publish path is a later PR), so every chat behaves as before.
What changed
- A turn step first settles the oldest message with
prompt_hooks_pending set. With lifecycle hooks off it only clears the flag. Otherwise it dispatches user_prompt_submit outside any transaction, then applies the result in one short transaction through a new ResolvePromptHooks transition and ends the task. The history change starts the next task, as after a steer delivery.
- A rewrite replaces the message content. A denial soft-deletes the message and fails the chat with kind
hook_denied and the hook's user message, in the same transaction, so a crash cannot leave the message deleted and the chat running. Messages queued behind it stay queued.
- A dispatch failure keeps the flag and fails the chat with kind
hook_dispatch_failed, like the other generation-time hooks. The hooks run again when the chat resumes. A worker that dies before the transaction commits, or loses its lease, leaves the flag set, so the next worker runs the hooks again.
- The flag follows a message through the queue: queue inserts and promotions carry it, a queued edit clears it (the edit path already ran the hooks), and a soft delete or
ClearContext clears it, so no stale flag blocks the down migration.
- A flagged message stays out of every model prompt until its hooks have run:
GetChatMessagesForPromptByChatID skips it, and so does the manual title path.
Scope
- No writer of the flag, no read of
automation_id or any automation, no experiment, no SDK fields.
- A human message still runs its hooks once, when it is sent.
Tradeoffs
- The hooks run at least once per flagged message. A consumer that must not see a prompt twice has to be idempotent, as for any retried dispatch.
- The hooks use the generation capacity class, not the admission class, because the message is already accepted.
Blast radius
Every chat turn now scans its loaded history for the flag at every step. With the flag false the step is unchanged, and a test pins that a human message runs its hooks once, not again at turn start. The prompt query gains one AND prompt_hooks_pending = false filter.
Live check
Built ./enterprise/cmd/coder, ran coder server with agent-lifecycle-hooks, the stock scripts/agenthooks-server (and a throwaway denying consumer) and a fake OpenAI server, and set the flag by SQL: allow, rewrite, denial (chat error hook_denied, the model got no request), a human turn with the flag false (one hook call), and hooks off (flag cleared, no hook call) all behaved as above.
ARCHITECTURE.md prose was drafted by an agent at the user's request; the user will review it.
Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
chatgpt-codex-connector[bot] reacted with thumbs up emoji
…hooks
chatd will run the user_prompt_submit hooks when a turn starts from a
message that has prompt_hooks_pending set. This adds the queries that
step needs.
ResolveChatMessagePromptHooks clears the flag and either rewrites the
content or soft-deletes the message. InsertChatMessages and
InsertChatQueuedMessageWithCreator carry the flag, and
UpdateChatQueuedMessageContent clears it because an edit runs the hooks
on the new content. Both soft deletes clear the flag, so a deleted row
never blocks the down migration. ClearChatMessagesPromptHooks serves a
context reset.
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-sonnet-5-5` • Thinking: `xhigh`_
…solvePromptHooks
A message keeps its prompt_hooks_pending flag when it moves from the
queue into history, so chatd can run the hooks when the message starts a
turn. ResolvePromptHooks is the transition that clears the flag and
rewrites or soft-deletes the message. It runs from R0, R1 and R1P and
does not change the status.
A flagged row must be a user message visible to the user and the model,
because chatd looks for flagged rows in the history the user sees.
ClearContext clears the flags of the rows it hides, since those messages
never start a turn.
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-sonnet-5-5` • Thinking: `xhigh`_
…flagged message
When a generation step finds a message with prompt_hooks_pending set, it
runs the user_prompt_submit hooks first, outside any transaction, and
one short transaction applies the result and clears the flag. A rewrite
replaces the content, a denial deletes the message and fails the chat
with kind hook_denied in the same transaction, and a failure that is not
a denial fails the chat and keeps the message. With hooks off the flag is
cleared and the message stays as saved. If a worker dies before the
transaction commits, the flag is still set and the next worker runs the
hooks again.
Nothing sets the flag yet, so no existing chat is affected. A manual
title no longer sends a message that still waits for its hooks to a
model.
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-sonnet-5-5` • Thinking: `xhigh`_
…repeat a behavior
The history query skips deleted rows, so a flagged message is never
deleted. Each removed test is covered by a test that a mutant of the
product code turns red alone.
…very prompt
Reading the prompt query for the title and summary generators sent a
flagged message to a model before its hooks ran. The query now skips it.
Also drop the content version input and the context boundary skip that no
path can reach, and describe the transition in the state diagram.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Codex Review Summary: ✅ Completed
This comment shows the latest Codex review activity on this pull request.
| Review |
Status |
Commit |
Review trigger |
| 📝 Code Review |
✅ Completed 2026-11-12T13:54:29.860511Z |
e5ffac6 |
Manual request |
| 🔒 Security Review |
✅ Completed 2026-11-12T13:56:13.690855Z |
e5ffac6 |
Manual request |
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review" or "@codex security review".
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
🛡️ Codex Security Review
Security review completed. No security issues were found in this pull request.
Reviewed commit: a18a332628
View security finding report
Only the user who started this review can view the report in Codex.
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Codex Review: Didn't find any major issues. You're on a roll.
Reviewed commit: a18a332628
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Codex Review: Didn't find any major issues. You're on a roll.
Reviewed commit: e5ffac67ba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
🛡️ Codex Security Review
Security review completed. No security issues were found in this pull request.
Reviewed commit: e5ffac67ba
View security finding report
Only the user who started this review can view the report in Codex.
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This branch has not been deployed
No deployments
Uh oh!
There was an error while loading. Please reload this page.
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
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.
|