runtime: use pod flag for session identity

This commit is contained in:
Keisuke Hirata 2026-06-08 10:48:00 +09:00
parent b6af761da0
commit 15f54df0ba
No known key found for this signature in database
3 changed files with 80 additions and 36 deletions

View File

@ -39,10 +39,6 @@ struct Cli {
#[arg(long, value_name = "PATH")]
project: Option<PathBuf>,
/// Internal typed pod-name override for session restore launched by the TUI.
#[arg(long, value_name = "NAME", requires = "session", hide = true)]
session_pod_name: Option<String>,
/// Internal resolved manifest config for delegated child Pod spawning.
#[arg(
long,
@ -85,7 +81,7 @@ struct Cli {
/// concurrent writers are prevented by the pod-registry.
/// Mutually exclusive with `--adopt` (spawned children always start
/// fresh).
#[arg(long, value_name = "UUID", conflicts_with_all = ["adopt", "pod"])]
#[arg(long, value_name = "UUID", conflicts_with_all = ["adopt"])]
session: Option<SegmentId>,
}
@ -101,9 +97,8 @@ fn runtime_workspace_root(cli: &Cli) -> Result<PathBuf, String> {
}
fn runtime_pod_name(cli: &Cli, workspace_root: &Path) -> String {
cli.session_pod_name
cli.pod
.as_deref()
.or(cli.pod.as_deref())
.map(str::to_string)
.unwrap_or_else(|| default_pod_name_for_workspace(workspace_root))
}
@ -171,7 +166,7 @@ where
}
fn apply_session_restore_overrides(manifest: &mut PodManifest, cli: &Cli) -> Result<(), String> {
if let Some(pod_name) = cli.session_pod_name.as_deref().or(cli.pod.as_deref()) {
if let Some(pod_name) = cli.pod.as_deref() {
manifest.pod.name = pod_name.to_string();
}
Ok(())
@ -733,12 +728,28 @@ permission = "write"
}
#[test]
fn pod_flag_conflicts_with_session() {
let segment_id = session_store::new_segment_id();
let segment_id = segment_id.to_string();
let err = Cli::try_parse_from(["yoi pod", "--pod", "agent", "--session", &segment_id])
.unwrap_err();
assert_eq!(err.kind(), clap::error::ErrorKind::ArgumentConflict);
fn pod_flag_is_runtime_identity_for_session_restore() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().join("explicit-workspace");
let store = tmp.path().join("sessions");
std::fs::create_dir(&workspace).unwrap();
let segment_id = session_store::new_segment_id().to_string();
let cli = Cli::try_parse_from([
"yoi pod",
"--workspace",
workspace.to_str().unwrap(),
"--session",
&segment_id,
"--pod",
"explicit-name",
"--store",
store.to_str().unwrap(),
])
.unwrap();
assert_eq!(cli.session.unwrap().to_string(), segment_id);
assert_eq!(cli.pod.as_deref(), Some("explicit-name"));
assert_eq!(runtime_pod_name(&cli, &workspace), "explicit-name");
}
#[test]
@ -835,6 +846,20 @@ permission = "write"
assert_eq!(cli.pod.as_deref(), Some("agent"));
}
#[test]
fn old_session_pod_name_identity_alias_is_rejected() {
let segment_id = session_store::new_segment_id().to_string();
let err = Cli::try_parse_from([
"yoi pod",
"--session",
&segment_id,
"--session-pod-name",
"agent",
])
.unwrap_err();
assert_eq!(err.kind(), clap::error::ErrorKind::UnknownArgument);
}
#[test]
fn removed_profile_pod_name_alias_is_rejected() {
let err = Cli::try_parse_from(["yoi pod", "--profile-pod-name", "agent"]).unwrap_err();

View File

@ -54,7 +54,10 @@ pub enum LaunchMode {
Resume,
/// `yoi --session <UUID>`: skip the picker, go straight to the
/// resume name dialog with `id` baked in.
ResumeWithSession(SegmentId),
ResumeWithSession {
id: SegmentId,
pod_name: Option<String>,
},
/// `yoi panel`: open the workspace panel from the current workspace.
Panel,
}
@ -93,8 +96,8 @@ pub async fn launch(options: LaunchOptions) -> ExitCode {
socket_override,
} => single_pod::run_pod_name(pod_name, socket_override, runtime_command).await,
LaunchMode::Resume => single_pod::run_resume(runtime_command).await,
LaunchMode::ResumeWithSession(id) => {
single_pod::run_spawn(Some(id), None, None, runtime_command).await
LaunchMode::ResumeWithSession { id, pod_name } => {
single_pod::run_spawn(Some(id), pod_name, None, runtime_command).await
}
LaunchMode::Panel => single_pod::run_panel(runtime_command).await,
};

View File

@ -297,11 +297,6 @@ fn parse_args_slice(args: &[String]) -> Result<Mode, ParseError> {
"--profile can only be used for fresh spawn".to_string(),
));
}
if pod_name.is_some() && session.is_some() {
return Err(ParseError(
"--pod and --session are mutually exclusive".to_string(),
));
}
if pod_name.is_some() && resume {
return Err(ParseError(
"--pod and --resume are mutually exclusive".to_string(),
@ -324,6 +319,12 @@ fn parse_args_slice(args: &[String]) -> Result<Mode, ParseError> {
}
let pod_name = pod_name.or(positional);
if socket_override.is_some() && session.is_some() {
return Err(ParseError(
"--socket can only be used with --pod attach mode".to_string(),
));
}
if let Some(profile) = profile {
return Ok(Mode::Tui {
mode: LaunchMode::Spawn {
@ -333,6 +334,12 @@ fn parse_args_slice(args: &[String]) -> Result<Mode, ParseError> {
workspace_root,
});
}
if let Some(id) = session {
return Ok(Mode::Tui {
mode: LaunchMode::ResumeWithSession { id, pod_name },
workspace_root,
});
}
if let Some(pod_name) = pod_name {
return Ok(Mode::Tui {
mode: LaunchMode::PodName {
@ -348,13 +355,6 @@ fn parse_args_slice(args: &[String]) -> Result<Mode, ParseError> {
workspace_root,
});
}
if let Some(id) = session {
return Ok(Mode::Tui {
mode: LaunchMode::ResumeWithSession(id),
workspace_root,
});
}
Ok(Mode::Tui {
mode: LaunchMode::Spawn {
pod_name: None,
@ -563,13 +563,29 @@ mod tests {
}
#[test]
fn parse_rejects_pod_and_session() {
let segment_id = session_store::new_segment_id().to_string();
let err = parse_args_from(["--pod", "agent", "--session", &segment_id]).unwrap_err();
assert_eq!(
err.to_string(),
"--pod and --session are mutually exclusive"
);
fn parse_session_accepts_explicit_runtime_pod_identity() {
let segment_id = session_store::new_segment_id();
match parse_args_from([
"--session",
&segment_id.to_string(),
"--pod",
"explicit-name",
])
.unwrap()
{
Mode::Tui {
mode:
LaunchMode::ResumeWithSession {
id,
pod_name: Some(pod_name),
},
..
} => {
assert_eq!(id, segment_id);
assert_eq!(pod_name, "explicit-name");
}
_ => panic!("expected ResumeWithSession mode with explicit pod name"),
}
}
#[test]