Honor feature overrides when starting the app-server daemon (#60489)

## Why

`codex app-server daemon start` ignored CLI feature overrides, while `restart` already parsed and applied them.

## What changed

Parse and validate feature overrides for `start` using the same handling as `restart`, including `--enable`, `--disable`, and `-c features.<name>=<bool>`. Pass the overrides through to daemon startup so they are saved when launching a stopped daemon. Starting an already running daemon preserves its process and saved overrides.

## Testing

Add coverage for applying overrides on a fresh start and preserving them on a subsequent `start`. Extend validation coverage to ensure both `start` and `restart` reject non-toggle feature keys without changing settings or the running process.

GitOrigin-RevId: 9edbfdfc723146674051a9f0713e528b552eeb00
This commit is contained in:
iceweasel-oai
2026-11-10 01:31:19 +00:00
committed by copyberry
parent 6aa88f9946
commit 4dcd8df891
3 changed files with 73 additions and 30 deletions
+9 -5
View File
@@ -72,7 +72,9 @@ const STATE_DIR_NAME: &str = "app-server-daemon";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LifecycleCommand {
Start,
Start {
feature_overrides: BTreeMap<String, bool>,
},
Restart {
feature_overrides: BTreeMap<String, bool>,
},
@@ -243,7 +245,7 @@ pub async fn run(command: LifecycleCommand) -> Result<LifecycleOutput> {
#[cfg(windows)]
if matches!(
command,
LifecycleCommand::Start | LifecycleCommand::Restart { .. }
LifecycleCommand::Start { .. } | LifecycleCommand::Restart { .. }
) {
backend::windows::ensure_not_elevated()?;
}
@@ -423,8 +425,8 @@ impl Daemon {
let _operation_lock = self.acquire_operation_lock().await?;
let selected = self.current_installation()?;
match command {
LifecycleCommand::Start => selected
.start(&BTreeMap::new())
LifecycleCommand::Start { feature_overrides } => selected
.start(&feature_overrides)
.await
.map_err(|err| err.error),
LifecycleCommand::Restart { feature_overrides } => {
@@ -1503,7 +1505,9 @@ mod tests {
)
.expect("legacy saved threads");
daemon
.run(super::LifecycleCommand::Start)
.run(super::LifecycleCommand::Start {
feature_overrides: Default::default(),
})
.await
.expect_err("missing backend binary");
assert!(!daemon.recovery_file().expect("recovery path").exists());