Cleanup collaboration mode variants (#10404)

## Summary

This PR simplifies collaboration modes to the visible set `default |
plan`, while preserving backward compatibility for older partners that
may still send legacy mode
names.

Specifically:
- Renames the old Code behavior to **Default**.
- Keeps **Plan** as-is.
- Removes **Custom** mode behavior (fallbacks now resolve to Default).
- Keeps `PairProgramming` and `Execute` internally for compatibility
plumbing, while removing them from schema/API and UI visibility.
- Adds legacy input aliasing so older clients can still send old mode
names.

## What Changed

1. Mode enum and compatibility
- `ModeKind` now uses `Plan` + `Default` as active/public modes.
- `ModeKind::Default` deserialization accepts legacy values:
  - `code`
  - `pair_programming`
  - `execute`
  - `custom`
- `PairProgramming` and `Execute` variants remain in code but are hidden
from protocol/schema generation.
- `Custom` variant is removed; previous custom fallbacks now map to
`Default`.

2. Collaboration presets and templates
- Built-in presets now return only:
  - `Plan`
  - `Default`
- Template rename:
  - `core/templates/collaboration_mode/code.md` -> `default.md`
- `execute.md` and `pair_programming.md` remain on disk but are not
surfaced in visible preset lists.

3. TUI updates
- Updated user-facing naming and prompts from “Code” to “Default”.
- Updated mode-cycle and indicator behavior to reflect only visible
`Plan` and `Default`.
- Updated corresponding tests and snapshots.

4. request_user_input behavior
- `request_user_input` remains allowed only in `Plan` mode.
- Rejection messaging now consistently treats non-plan modes as
`Default`.

5. Schemas
- Regenerated config and app-server schemas.
- Public schema types now advertise mode values as:
  - `plan`
  - `default`

## Backward Compatibility Notes

- Incoming legacy mode names (`code`, `pair_programming`, `execute`,
`custom`) are accepted and coerced to `default`.
- Outgoing/public schema surfaces intentionally expose only `plan |
default`.
- This allows tolerant ingestion of older partner payloads while
standardizing new integrations on the reduced mode set.

## Codex author
`codex fork 019c1fae-693b-7840-b16e-9ad38ea0bd00`
This commit is contained in:
Charley Cunningham
2026-02-03 09:23:53 -08:00
committed by GitHub
parent aea38f0f88
commit d509df676b
34 changed files with 205 additions and 250 deletions

View File

@@ -847,7 +847,7 @@ async fn user_turn_collaboration_mode_overrides_model_and_effort() -> anyhow::Re
.await?;
let collaboration_mode = CollaborationMode {
mode: ModeKind::Custom,
mode: ModeKind::Default,
settings: Settings {
model: "gpt-5.1".to_string(),
reasoning_effort: Some(ReasoningEffort::High),

View File

@@ -37,7 +37,7 @@ fn collab_mode_with_mode_and_instructions(
}
fn collab_mode_with_instructions(instructions: Option<&str>) -> CollaborationMode {
collab_mode_with_mode_and_instructions(ModeKind::Custom, instructions)
collab_mode_with_mode_and_instructions(ModeKind::Default, instructions)
}
fn developer_texts(input: &[Value]) -> Vec<String> {
@@ -427,7 +427,7 @@ async fn collaboration_mode_update_emits_new_instruction_message_when_mode_chang
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let test = test_codex().build(&server).await?;
let code_text = "code mode instructions";
let default_text = "default mode instructions";
let plan_text = "plan mode instructions";
test.codex
@@ -440,8 +440,8 @@ async fn collaboration_mode_update_emits_new_instruction_message_when_mode_chang
effort: None,
summary: None,
collaboration_mode: Some(collab_mode_with_mode_and_instructions(
ModeKind::Code,
Some(code_text),
ModeKind::Default,
Some(default_text),
)),
personality: None,
})
@@ -488,9 +488,9 @@ async fn collaboration_mode_update_emits_new_instruction_message_when_mode_chang
let input = req2.single_request().input();
let dev_texts = developer_texts(&input);
let code_text = collab_xml(code_text);
let default_text = collab_xml(default_text);
let plan_text = collab_xml(plan_text);
assert_eq!(count_exact(&dev_texts, &code_text), 1);
assert_eq!(count_exact(&dev_texts, &default_text), 1);
assert_eq!(count_exact(&dev_texts, &plan_text), 1);
Ok(())
@@ -517,7 +517,7 @@ async fn collaboration_mode_update_noop_does_not_append_when_mode_is_unchanged()
effort: None,
summary: None,
collaboration_mode: Some(collab_mode_with_mode_and_instructions(
ModeKind::Code,
ModeKind::Default,
Some(collab_text),
)),
personality: None,
@@ -545,7 +545,7 @@ async fn collaboration_mode_update_noop_does_not_append_when_mode_is_unchanged()
effort: None,
summary: None,
collaboration_mode: Some(collab_mode_with_mode_and_instructions(
ModeKind::Code,
ModeKind::Default,
Some(collab_text),
)),
personality: None,

View File

@@ -24,7 +24,7 @@ use tempfile::TempDir;
fn collab_mode_with_instructions(instructions: Option<&str>) -> CollaborationMode {
CollaborationMode {
mode: ModeKind::Custom,
mode: ModeKind::Default,
settings: Settings {
model: "gpt-5.1".to_string(),
reasoning_effort: None,

View File

@@ -412,7 +412,7 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul
let TestCodex { codex, .. } = test_codex().build(&server).await?;
let collaboration_mode = CollaborationMode {
mode: ModeKind::Custom,
mode: ModeKind::Default,
settings: Settings {
model: "gpt-5.1".to_string(),
reasoning_effort: Some(ReasoningEffort::High),

View File

@@ -71,6 +71,15 @@ fn call_output_content_and_success(
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn request_user_input_round_trip_resolves_pending() -> anyhow::Result<()> {
request_user_input_round_trip_for_mode(ModeKind::Plan).await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn request_user_input_round_trip_works_in_pair_mode() -> anyhow::Result<()> {
request_user_input_round_trip_for_mode(ModeKind::PairProgramming).await
}
async fn request_user_input_round_trip_for_mode(mode: ModeKind) -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
@@ -134,7 +143,7 @@ async fn request_user_input_round_trip_resolves_pending() -> anyhow::Result<()>
effort: None,
summary: ReasoningSummary::Auto,
collaboration_mode: Some(CollaborationMode {
mode: ModeKind::Plan,
mode,
settings: Settings {
model: session_configured.model.clone(),
reasoning_effort: None,
@@ -273,8 +282,8 @@ where
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn request_user_input_rejected_in_execute_mode() -> anyhow::Result<()> {
assert_request_user_input_rejected("Execute", |model| CollaborationMode {
async fn request_user_input_rejected_in_execute_mode_alias() -> anyhow::Result<()> {
assert_request_user_input_rejected("Default", |model| CollaborationMode {
mode: ModeKind::Execute,
settings: Settings {
model,
@@ -286,22 +295,9 @@ async fn request_user_input_rejected_in_execute_mode() -> anyhow::Result<()> {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn request_user_input_rejected_in_code_mode() -> anyhow::Result<()> {
assert_request_user_input_rejected("Code", |model| CollaborationMode {
mode: ModeKind::Code,
settings: Settings {
model,
reasoning_effort: None,
developer_instructions: None,
},
})
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn request_user_input_rejected_in_custom_mode() -> anyhow::Result<()> {
assert_request_user_input_rejected("Custom", |model| CollaborationMode {
mode: ModeKind::Custom,
async fn request_user_input_rejected_in_default_mode() -> anyhow::Result<()> {
assert_request_user_input_rejected("Default", |model| CollaborationMode {
mode: ModeKind::Default,
settings: Settings {
model,
reasoning_effort: None,