[codex] Remove workspace owner usage nudge gate (#20509)

## Summary
- make workspace owner nudge handling unconditional in the TUI now that
it is fully rolled out
- keep `workspace_owner_usage_nudge` as a removed no-op compatibility
flag so old configs/app overrides remain accepted during rollout
- remove flag-disabled test setup

## Companion PR
- https://github.com/openai/openai/pull/876351 removes the Codex Apps
Statsig rollout gate override after this change is available to the
app/runtime path

## Validation
- `just write-config-schema`
- `just fmt`
- `cargo test -p codex-features`
- `cargo test -p codex-tui status_and_layout`
This commit is contained in:
richardopenai
2026-05-12 17:07:33 -07:00
committed by GitHub
Unverified
parent ac466c0dbd
commit b6e718591b
3 changed files with 4 additions and 81 deletions
+3 -2
View File
@@ -231,7 +231,8 @@ pub enum Feature {
TuiAppServer,
/// Prevent idle system sleep while a turn is actively running.
PreventIdleSleep,
/// Enable workspace-specific owner nudge copy and prompts in the TUI.
/// Removed compatibility flag retained as a no-op now that workspace owner
/// usage nudges are always enabled.
WorkspaceOwnerUsageNudge,
/// Legacy rollout flag for Responses API WebSocket transport experiments.
ResponsesWebsockets,
@@ -1153,7 +1154,7 @@ pub const FEATURES: &[FeatureSpec] = &[
FeatureSpec {
id: Feature::WorkspaceOwnerUsageNudge,
key: "workspace_owner_usage_nudge",
stage: Stage::UnderDevelopment,
stage: Stage::Removed,
default_enabled: false,
},
FeatureSpec {
+1 -21
View File
@@ -2890,18 +2890,7 @@ impl ChatWidget {
self.maybe_send_next_queued_input();
}
fn workspace_owner_usage_nudge_enabled(&self) -> bool {
self.config
.features
.enabled(Feature::WorkspaceOwnerUsageNudge)
}
fn on_rate_limit_error(&mut self, error_kind: RateLimitErrorKind, message: String) {
if !self.workspace_owner_usage_nudge_enabled() {
self.on_error(message);
return;
}
let rate_limit_reached_type = self.codex_rate_limit_reached_type.map(|kind| {
if matches!(error_kind, RateLimitErrorKind::UsageLimit) {
match kind {
@@ -6919,9 +6908,7 @@ impl ChatWidget {
}
fn open_workspace_owner_nudge_prompt(&mut self, credit_type: AddCreditsNudgeCreditType) {
if !self.workspace_owner_usage_nudge_enabled()
|| self.add_credits_nudge_email_in_flight.is_some()
{
if self.add_credits_nudge_email_in_flight.is_some() {
return;
}
@@ -6969,10 +6956,6 @@ impl ChatWidget {
&mut self,
credit_type: AddCreditsNudgeCreditType,
) -> bool {
if !self.workspace_owner_usage_nudge_enabled() {
return false;
}
self.add_credits_nudge_email_in_flight = Some(credit_type);
true
}
@@ -6985,9 +6968,6 @@ impl ChatWidget {
.add_credits_nudge_email_in_flight
.take()
.unwrap_or(AddCreditsNudgeCreditType::Credits);
if !self.workspace_owner_usage_nudge_enabled() {
return;
}
let message = match (credit_type, result) {
(AddCreditsNudgeCreditType::Credits, Ok(AddCreditsNudgeEmailStatus::Sent)) => {
"Workspace owner notified."
@@ -797,57 +797,9 @@ async fn rate_limit_switch_prompt_popup_snapshot() {
assert_chatwidget_snapshot!("rate_limit_switch_prompt_popup", popup);
}
#[tokio::test]
async fn workspace_owner_usage_nudge_flag_disabled_keeps_generic_rate_limit_error() {
{
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type =
Some(RateLimitReachedType::WorkspaceMemberUsageLimitReached);
chat.on_rate_limit_snapshot(Some(limits));
chat.on_rate_limit_error(
RateLimitErrorKind::UsageLimit,
"Usage limit reached.".to_string(),
);
let rendered = drain_insert_history(&mut rx)
.into_iter()
.map(|lines| lines_to_single_string(&lines))
.collect::<String>();
assert!(
rendered.contains("Usage limit reached."),
"rendered: {rendered}"
);
}
{
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type =
Some(RateLimitReachedType::WorkspaceMemberUsageLimitReached);
chat.on_rate_limit_snapshot(Some(limits));
chat.on_rate_limit_error(
RateLimitErrorKind::UsageLimit,
"Usage limit reached.".to_string(),
);
let popup = render_bottom_popup(&chat, /*width*/ 100);
assert!(
!popup.contains("Request a limit increase from your owner"),
"popup: {popup}"
);
assert_no_owner_nudge_or_rate_limit_refresh(&mut rx);
}
}
fn enable_workspace_owner_usage_nudge(chat: &mut ChatWidget) {
chat.set_feature_enabled(Feature::WorkspaceOwnerUsageNudge, /*enabled*/ true);
}
#[tokio::test]
async fn workspace_member_credits_depleted_prompts_and_sends_credits() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(RateLimitReachedType::WorkspaceMemberCreditsDepleted);
chat.on_rate_limit_snapshot(Some(limits));
@@ -867,7 +819,6 @@ async fn workspace_member_credits_depleted_prompts_and_sends_credits() {
#[tokio::test]
async fn workspace_member_usage_limit_prompts_and_sends_usage_limit() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(RateLimitReachedType::WorkspaceMemberUsageLimitReached);
chat.on_rate_limit_snapshot(Some(limits));
@@ -887,7 +838,6 @@ async fn workspace_member_usage_limit_prompts_and_sends_usage_limit() {
#[tokio::test]
async fn header_rate_limit_snapshot_preserves_member_limit_type_for_error_prompt() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
let mut usage_limits = snapshot(/*percent*/ 100.0);
usage_limits.rate_limit_reached_type =
Some(RateLimitReachedType::WorkspaceMemberUsageLimitReached);
@@ -917,7 +867,6 @@ async fn header_rate_limit_snapshot_preserves_member_limit_type_for_error_prompt
#[tokio::test]
async fn usage_limit_error_remaps_stale_member_credits_state_to_usage_limit_prompt() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(RateLimitReachedType::WorkspaceMemberCreditsDepleted);
chat.on_rate_limit_snapshot(Some(limits));
@@ -954,7 +903,6 @@ async fn workspace_owner_limit_states_do_not_prompt_for_owner_nudge() {
),
] {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(limit_type);
chat.on_rate_limit_snapshot(Some(limits));
@@ -984,7 +932,6 @@ async fn workspace_owner_limit_states_render_state_specific_messages() {
let mut rendered_cases = Vec::new();
for (limit_type, error_kind, expected) in cases {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(limit_type);
chat.on_rate_limit_snapshot(Some(limits));
@@ -1007,7 +954,6 @@ async fn workspace_owner_limit_states_render_state_specific_messages() {
#[tokio::test]
async fn missing_rate_limit_reached_type_does_not_prompt_or_refresh() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
chat.on_rate_limit_snapshot(Some(snapshot(/*percent*/ 100.0)));
chat.on_rate_limit_error(
@@ -1022,7 +968,6 @@ async fn missing_rate_limit_reached_type_does_not_prompt_or_refresh() {
#[tokio::test]
async fn workspace_owner_nudge_default_no_dismisses_without_sending() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(RateLimitReachedType::WorkspaceMemberCreditsDepleted);
chat.on_rate_limit_snapshot(Some(limits));
@@ -1039,7 +984,6 @@ async fn workspace_owner_nudge_default_no_dismisses_without_sending() {
#[tokio::test]
async fn workspace_owner_nudge_reappears_after_dismissing_no() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(RateLimitReachedType::WorkspaceMemberUsageLimitReached);
chat.on_rate_limit_snapshot(Some(limits));
@@ -1082,7 +1026,6 @@ async fn workspace_owner_credits_nudge_completion_renders_feedback() {
let mut rendered_cases = Vec::new();
for (result, expected) in cases {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
chat.start_add_credits_nudge_email_request(AddCreditsNudgeCreditType::Credits);
chat.finish_add_credits_nudge_email_request(result);
let rendered = drain_insert_history(&mut rx)
@@ -1119,7 +1062,6 @@ async fn workspace_owner_usage_limit_nudge_completion_renders_feedback() {
let mut rendered_cases = Vec::new();
for (result, expected) in cases {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
enable_workspace_owner_usage_nudge(&mut chat);
chat.start_add_credits_nudge_email_request(AddCreditsNudgeCreditType::UsageLimit);
chat.finish_add_credits_nudge_email_request(result);
let rendered = drain_insert_history(&mut rx)