fix: isolate standalone target resolution
This commit is contained in:
+117
-22
@@ -217,6 +217,24 @@ fn resolve_tui_target<R: CliConnectionResolver + ?Sized>(
|
||||
selection: &TargetSelection,
|
||||
workspace_root: &Path,
|
||||
) -> Result<Box<dyn Target>, ParseError> {
|
||||
if selection.explicit_local {
|
||||
return resolve_connection_aware_cli_connection(
|
||||
connection_resolver,
|
||||
command,
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
if selection.backend_url.is_none()
|
||||
&& let Ok(target) =
|
||||
resolve_connection_aware_cli_connection(connection_resolver, command, false, None, None)
|
||||
&& target.kind() == TargetKind::Standalone
|
||||
{
|
||||
return Ok(target);
|
||||
}
|
||||
|
||||
let workspace_id = match selection.workspace_id.clone() {
|
||||
Some(workspace_id) => Some(workspace_id),
|
||||
None => resolve_workspace_id_from_root(workspace_root)?,
|
||||
@@ -1160,23 +1178,22 @@ fn read_client_default_connection() -> Result<ClientDefaultConnection, ParseErro
|
||||
}
|
||||
|
||||
fn read_client_config() -> Result<Option<ClientConfigFile>, ParseError> {
|
||||
let path = client_global_config_path();
|
||||
read_client_config_from_global_path(path.as_deref())
|
||||
}
|
||||
|
||||
fn read_client_config_from_global_path(
|
||||
path: Option<&Path>,
|
||||
) -> Result<Option<ClientConfigFile>, ParseError> {
|
||||
let Some(path) = path else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(overlay) = read_client_config_overlay(path)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
let mut config = ClientConfigFile::default();
|
||||
let mut found = false;
|
||||
|
||||
if let Some(path) = client_global_config_path() {
|
||||
if let Some(overlay) = read_client_config_overlay(&path)? {
|
||||
config.apply_overlay(overlay);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
let cwd_path = client_cwd_config_path()?;
|
||||
if let Some(overlay) = read_client_config_overlay(&cwd_path)? {
|
||||
config.apply_overlay(overlay);
|
||||
found = true;
|
||||
}
|
||||
|
||||
Ok(found.then_some(config))
|
||||
config.apply_overlay(overlay);
|
||||
Ok(Some(config))
|
||||
}
|
||||
|
||||
fn read_client_config_overlay(path: &Path) -> Result<Option<ClientConfigOverlay>, ParseError> {
|
||||
@@ -1194,14 +1211,10 @@ fn client_global_config_path() -> Option<PathBuf> {
|
||||
manifest::paths::data_dir().map(|dir| dir.join("client").join("config.toml"))
|
||||
}
|
||||
|
||||
fn client_cwd_config_path() -> Result<PathBuf, ParseError> {
|
||||
Ok(current_dir()?.join(".yoi").join("client.config.toml"))
|
||||
}
|
||||
|
||||
fn client_config_location_message() -> String {
|
||||
match client_global_config_path() {
|
||||
Some(path) => format!("{} or <cwd>/.yoi/client.config.toml", path.display()),
|
||||
None => "<data_dir>/client/config.toml or <cwd>/.yoi/client.config.toml".to_string(),
|
||||
Some(path) => path.display().to_string(),
|
||||
None => "<data_dir>/client/config.toml".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2249,6 +2262,88 @@ backend = "shared"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_standalone_ignores_malformed_repository_workspace_identity() {
|
||||
let repository = tempfile::tempdir().unwrap();
|
||||
std::fs::create_dir_all(repository.path().join(".yoi")).unwrap();
|
||||
std::fs::write(
|
||||
repository.path().join(".yoi/workspace.toml"),
|
||||
"this is not valid toml = [",
|
||||
)
|
||||
.unwrap();
|
||||
let resolver = FixedCliConnectionResolver {
|
||||
backend_url: "http://fake-backend.example",
|
||||
};
|
||||
|
||||
let target = resolve_tui_target(
|
||||
&resolver,
|
||||
CliCommand::DefaultTui,
|
||||
&TargetSelection {
|
||||
explicit_local: true,
|
||||
..TargetSelection::default()
|
||||
},
|
||||
repository.path(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(target.kind(), TargetKind::Standalone);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_standalone_ignores_malformed_repository_workspace_identity() {
|
||||
let repository = tempfile::tempdir().unwrap();
|
||||
std::fs::create_dir_all(repository.path().join(".yoi")).unwrap();
|
||||
std::fs::write(
|
||||
repository.path().join(".yoi/workspace.toml"),
|
||||
"this is not valid toml = [",
|
||||
)
|
||||
.unwrap();
|
||||
let resolver = FixedCliConnectionResolver {
|
||||
backend_url: "http://fake-backend.example",
|
||||
};
|
||||
|
||||
let target = resolve_tui_target(
|
||||
&resolver,
|
||||
CliCommand::DefaultTui,
|
||||
&TargetSelection::default(),
|
||||
repository.path(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(target.kind(), TargetKind::Standalone);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_config_reader_uses_only_the_supplied_global_path() {
|
||||
let root = tempfile::tempdir().unwrap();
|
||||
let global = root.path().join("client/config.toml");
|
||||
std::fs::create_dir_all(global.parent().unwrap()).unwrap();
|
||||
std::fs::write(
|
||||
&global,
|
||||
"default_connection = \"local\"\n[backends.main]\nurl = \"http://backend.example\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::create_dir_all(root.path().join("repository/.yoi")).unwrap();
|
||||
std::fs::write(
|
||||
root.path().join("repository/.yoi/client.config.toml"),
|
||||
"this is not valid toml = [",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let config = read_client_config_from_global_path(Some(&global))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config.default_connection,
|
||||
ClientDefaultConnection::Standalone
|
||||
);
|
||||
assert_eq!(
|
||||
config.backends["main"].url.as_deref(),
|
||||
Some("http://backend.example")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_backend_target_inherits_workspace_identity_from_workspace_root() {
|
||||
let workspace = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -31,7 +31,7 @@ worker
|
||||
|
||||
## CLI / TUI routing
|
||||
|
||||
- `yoi` の connection-aware command は `TargetKind::Standalone | Backend` の二択で dispatch する。`--local` と client config の `default_connection = "local"` は Standalone を選ぶ入力であり、旧 LocalBackend を有効化しない。
|
||||
- `yoi` の connection-aware command は `TargetKind::Standalone | Backend` の二択で dispatch する。`--local` と client config の `default_connection = "local"` は Standalone を選ぶ入力であり、旧 LocalBackend を有効化しない。 Client config は repository `.yoi/client.config.toml` を読まず、repository `.yoi/workspace.toml` は Backend Workspace identity が必要な場合だけ参照する。
|
||||
- Standalone の通常起動は `StandaloneHost`、restore は専用 `StandaloneStore` の session picker を使う。Workspace Worker list、PID、Unix socket、subprocess は探索しない。
|
||||
- `workers`、Backend Worker restore、Workspace panel、Ticket、Objective は Backend authority を要求する。Standalone から repository-local filesystem backend へ fallback しない。
|
||||
- `yoi worker` は Runtime や明示的な process-owned integration が使う direct Worker entrypoint として残るが、通常の `yoi` / TUI 起動経路からは呼び出さない。
|
||||
|
||||
Reference in New Issue
Block a user