Skip to content

Add /auto-review-denials retry approval flow#19058

Merged
won-openai merged 8 commits intomainfrom
auto-review-denial-ux
Apr 27, 2026
Merged

Add /auto-review-denials retry approval flow#19058
won-openai merged 8 commits intomainfrom
auto-review-denial-ux

Conversation

@won-openai
Copy link
Copy Markdown
Collaborator

@won-openai won-openai commented Apr 23, 2026

Why

Auto-review can deny an action that the user later decides they want to retry. Today there is no TUI surface for selecting a recent denial and sending explicit approval context back into the session, so users have to restate intent manually and the retry can be reviewed without the original denied action context.

This adds a narrow TUI-driven path for approving a recent denied action while still keeping the retry inside the normal auto-review flow.

What Changed

  • Added /auto-review-denials to open a picker of recent denied auto-review actions.
  • Added a small in-memory TUI store for the 10 most recent denied auto-review events.
  • Selecting a denial sends the structured denied event back through the existing core/app-server op path.
  • Core now injects a developer message containing the approved action JSON rather than the full assessment event.
  • Auto-review transcript collection now preserves this specific approval developer message so follow-up review sessions can see the user approval context.
  • Added TUI snapshot/unit coverage for the picker and approval dispatch path.
  • Added core coverage for retaining the approval developer message in the auto-review transcript.

Verification

  • cargo test -p codex-core collect_guardian_transcript_entries_keeps_manual_approval_developer_message
  • cargo test -p codex-tui auto_review_denials
  • cargo test -p codex-tui approving_recent_denial_emits_structured_core_op_once

Notes

This intentionally keeps retries going through auto-review. The approval signal is context for the exact previously denied action, not a blanket bypass for similar future actions.

@won-openai won-openai changed the title No findings Add /auto-review-denials retry approval flow Apr 23, 2026
@won-openai won-openai marked this pull request as ready for review April 23, 2026 00:43
@won-openai won-openai requested a review from a team as a code owner April 23, 2026 00:43
@won-openai
Copy link
Copy Markdown
Collaborator Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 075815efa6

ℹ️ 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".

Comment thread codex-rs/tui/src/chatwidget.rs
Comment thread codex-rs/core/src/session/handlers.rs
Comment thread codex-rs/core/src/guardian/prompt.rs
Comment thread codex-rs/tui/src/chatwidget.rs Outdated
Comment thread codex-rs/core/src/session/handlers.rs
@etraut-openai
Copy link
Copy Markdown
Collaborator

/auto-review-denials is a pretty long slash command name — much longer than any others we currently have. I also don't think we should have built-in slash commands with hyphens in the name. Maybe shorten to just /autoreview?

@won-openai
Copy link
Copy Markdown
Collaborator Author

/auto-review-denials is a pretty long slash command name — much longer than any others we currently have. I also don't think we should have built-in slash commands with hyphens in the name. Maybe shorten to just /autoreview?

or we can do /denials, i think it's more intuitive! thoughts? @etraut-openai @dylan-hurd-oai

@etraut-openai
Copy link
Copy Markdown
Collaborator

I guess I'd lean toward /autoreview over /denials. I suspect we may want to provide additional controls over auto-review parameters in the future, and this would allow us to expand the UI without adding another slash command.

@won-openai won-openai force-pushed the auto-review-denial-ux branch 4 times, most recently from fe7d9a3 to cc47130 Compare April 26, 2026 05:22
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

self.thread_id = Some(event.session_id);

P1 Badge Clear denial cache when switching to a different thread

on_session_configured_with_display_and_fork_parent_title replaces self.thread_id but never resets recent_auto_review_denials. Since that cache is stored once on ChatWidget, a new thread can inherit denials from the previous thread, and /denials may submit a stale GuardianAssessmentEvent against the wrong thread context.

ℹ️ 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".

@won-openai
Copy link
Copy Markdown
Collaborator Author

@codex review

content_entry(GuardianTranscriptEntryKind::User, content)
}
}
ResponseItem::Message { role, content, .. } if role == "developer" => {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so that auto-review is able to send the override message

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if entry.kind.is_user() || retained_non_user_entries >= GUARDIAN_RECENT_ENTRY_LIMIT {
continue;

P2 Badge Prevent pruning of preserved manual-approval context

Manual-approval developer entries are collected, but transcript rendering treats them as generic non-user entries under GUARDIAN_RECENT_ENTRY_LIMIT and message token budget. With enough newer entries, this approval context is dropped, so follow-up guardian reviews can miss the very retry-approval context this change intends to preserve.

ℹ️ 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".

}

pub(crate) fn approve_recent_auto_review_denial(&mut self, thread_id: ThreadId, id: String) {
let Some(event) = self.recent_auto_review_denials.take(&id) else {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep denied action available until submit succeeds

approve_recent_auto_review_denial removes the selected denial from recent_auto_review_denials before the async SubmitThreadOp is actually sent/acknowledged. If thread_approve_guardian_denied_action fails, the user cannot reselect that denial and must wait for a new denial event. This makes transient app-server failures permanently consume the retry option.

Useful? React with 👍 / 👎.

Comment thread codex-rs/tui/src/slash_command.rs Outdated
Comment on lines +24 to +25
#[strum(to_string = "autoreview")]
AutoReviewDenials,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match slash command token to documented feature name

The change is described as adding /auto-review-denials, but this variant is wired with #[strum(to_string = "autoreview")], making autoreview the serialized/parsed command token. Users invoking /auto-review-denials will not match this command, creating an intent/documentation mismatch for the new flow.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@won-openai which one are we using here? Let's keep this simple

Comment on lines +1265 to +1269
Treat this as approval to perform that exact action in the same context in which it was originally requested.
Do not assume this also authorizes similar operations with different payloads.

Stored Guardian assessment event JSON:
{event_json}"#,
Approved action:
{approved_action_json}"#,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore untrusted-data warning for approved action JSON

This developer message now embeds event.action JSON without the previous "untrusted data" guard. Because action fields can contain model/user-controlled text (e.g., command/tool args), that text is now placed in a privileged developer-role prompt and may be followed as instructions, weakening prompt-injection defenses in approval retries.

Useful? React with 👍 / 👎.

Comment thread codex-rs/core/src/session/handlers.rs
Copy link
Copy Markdown
Collaborator

@dylan-hurd-oai dylan-hurd-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with a few small things to address

Comment thread codex-rs/core/src/guardian/prompt.rs
Comment thread codex-rs/tui/src/chatwidget.rs Outdated

self.bottom_pane.show_selection_view(SelectionViewParams {
title: Some("Auto-review Denials".to_string()),
subtitle: Some("Select one denied action to approve for one retry.".to_string()),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Select a denied action to approve. - let's keep this simple for now

Comment thread codex-rs/tui/src/slash_command.rs Outdated
Comment on lines +24 to +25
#[strum(to_string = "autoreview")]
AutoReviewDenials,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@won-openai which one are we using here? Let's keep this simple

@won-openai won-openai force-pushed the auto-review-denial-ux branch from 3fb01fb to 78a4d34 Compare April 27, 2026 03:18
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 78a4d346d3

ℹ️ 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".

Comment on lines 1247 to +1253
return;
}

let event_json = match serde_json::to_string_pretty(&event) {
Ok(event_json) => event_json,
let approved_action = serde_json::json!({
"action": &event.action,
"outcome": "allowed",
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify denied review ID before injecting approval context

approve_guardian_denied_action only checks event.status == Denied and then turns the client-supplied payload into a developer approval message. A forged/stale GuardianAssessmentEvent can therefore authorize an action that was never actually denied in this thread. Validate event.id (and thread/turn binding) against server-side denied-review state before honoring it.

Useful? React with 👍 / 👎.

Comment on lines +1268 to +1269
Approved action:
{approved_action_json}"#,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat approved action payload as untrusted text

The developer message now inlines {approved_action_json} but no longer says to ignore instructions inside that JSON. Since action fields can contain model/user-controlled strings (commands, args), this can inject instruction-like text into privileged developer context and bias subsequent auto-review decisions.

Useful? React with 👍 / 👎.

@won-openai won-openai merged commit 8033b6a into main Apr 27, 2026
25 checks passed
@won-openai won-openai deleted the auto-review-denial-ux branch April 27, 2026 03:43
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants