You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by wy...@apache.org on 2026/10/17 06:57:33 UTC
(maka) branch rust-preview updated: feat(transport): expose complete owned Host startup connection
This is an automated email from the ASF dual-hosted git repository.
M4n5ter pushed a commit to branch rust-preview
in repository https://gitbox.apache.org/repos/asf/maka.git
The following commit(s) were added to refs/heads/rust-preview by this push:
new 6064eab067 feat(transport): expose complete owned Host startup connection
6064eab067 is described below
commit 6064eab067afa6ebc8c0b1dbf528124e589e2f57
Author: Wang <m4...@gmail.com>
AuthorDate: Sat Oct 17 14:57:26 2026 +0800
feat(transport): expose complete owned Host startup connection
Share Root discovery, CLI startup residency and identity verification with peer connections, then admit the original owned Client. Verify real helper concurrency, receipts and final Root release.
Generated-by: Codex
---
platform/transport/README.md | 10 +
platform/transport/README.zh-CN.md | 10 +
platform/transport/src/connections.rs | 98 +------
platform/transport/src/connections/host.rs | 1 +
platform/transport/src/startup.rs | 133 ++++++++-
products/maka/cli/tests/integration/candidate.rs | 1 +
.../maka/cli/tests/integration/candidate/owned.rs | 312 +++++++++++++++++++++
products/maka/cli/tests/integration/main.rs | 3 +
.../cli/tests/integration/terminal/application.rs | 4 +-
9 files changed, 477 insertions(+), 95 deletions(-)
diff --git a/platform/transport/README.md b/platform/transport/README.md
index 9209c550fc..31e92c1b48 100644
--- a/platform/transport/README.md
+++ b/platform/transport/README.md
@@ -23,6 +23,16 @@
Connects a foreground UI to independently owned Host deployments over local IPC. The launcher supplies trusted connection configuration and method/recovery grants. Public connections discover a Root-bound Host or ask the trusted CLI launcher to start it; cohosts borrow the UI Store. `connections` owns those connections; `Updates` coordinates approved source changes.
+Trusted local applications can use one public entry point with an explicit `startup::Request` selecting the Root, deployment and endpoint grants:
+
+```rust
+let mut client = maka_transport::startup::connect(&maka_executable, request).await?;
+// Use the authenticated Client's catalogue and calls.
+client.close().await?;
+```
+
+The executable must be an absolute path to the trusted Maka CLI. The entry point reuses a live Host or asks that CLI to start it, verifies the Root and Host incarnation, and admits the caller within 40 seconds. It uses the current account's Root namespaces and does not install, update or repair a deployment. Endpoint/native selections prepare startup; a live Host retains its published catalogue and grants. `close` detaches this session; `quit` requests shutdown when no other session rema [...]
+
Remote registration data describes available services. It cannot create permission, choose a different caller or retarget an accepted operation. Closing the UI waits for its original physical calls and locally owned cohost cleanup. Normal quit asks each public Host to stop if no other session remains, atomically with session admission. Explicit UI-only exit detaches and preserves background work.
[Source](src/lib.rs) · [Cargo manifest](Cargo.toml)
diff --git a/platform/transport/README.zh-CN.md b/platform/transport/README.zh-CN.md
index 54b59aac1a..f413f872fa 100644
--- a/platform/transport/README.zh-CN.md
+++ b/platform/transport/README.zh-CN.md
@@ -23,6 +23,16 @@
通过本地 IPC 将前台 UI 连接到独立持有的 Host 部署。启动器提供可信连接配置以及方法和回执恢复权限。公共连接发现绑定 Root 的 Host;不存在时,通过可信 CLI 启动器自动启动。同进程 Host 借用 UI Store。`connections` 管理连接,`Updates` 协调获准的源配置更新。
+可信本地应用可使用一个公开入口,通过显式 `startup::Request` 选择 Root、部署及端点权限:
+
+```rust
+let mut client = maka_transport::startup::connect(&maka_executable, request).await?;
+// 使用已认证 Client 的目录和调用接口。
+client.close().await?;
+```
+
+可执行文件必须是可信 Maka CLI 的绝对路径。入口复用现有 Host,或请求该 CLI 启动 Host,校验 Root 和 Host 实例身份,并在 40 秒内完成调用方接入。它使用当前账户的 Root 命名空间,不安装、更新或修复部署。端点和 native 选择用于准备启动;复用运行中的 Host 时沿用其已发布目录及权限。`close` 断开当前会话;`quit` 请求在没有其他会话时关闭 Host。原始调用须保留到清理得到确认;结果未知的 mutation 应通过原始回执恢复判断,重新连接不会重试操作。
+
远端注册信息只描述可用服务,不能授予权限、替换调用方或改写已接受操作的目标。关闭 UI 时,先等待原始物理调用及本地持有的同进程 Host 清理。默认退出要求公共 Host 在没有其他会话时停止;此判断与会话接入原子执行。显式选择仅退出 UI 时,断开会话并保留后台工作。
[源码](src/lib.rs) · [Cargo 配置](Cargo.toml)
diff --git a/platform/transport/src/connections.rs b/platform/transport/src/connections.rs
index 10b82484ec..212cb1d985 100644
--- a/platform/transport/src/connections.rs
+++ b/platform/transport/src/connections.rs
@@ -26,7 +26,7 @@ use futures_util::{
future::{BoxFuture, join_all},
};
use maka_event_log::{EventLog, root::RootNamespaces};
-use maka_host_runtime::application::host::{Client, discovery};
+use maka_host_runtime::application::host::Client;
use maka_plugin_core::{
composition::{ExecutionLocation, InstanceId, PresentationTarget},
deployment::{DeploymentRef, RemoteObservation},
@@ -397,71 +397,28 @@ impl Connections {
root: PathBuf,
source: DeploymentRef,
) -> Result<crate::transport::Catalogue> {
- let mut startup = None;
- let mut discovered = match self.discover(&root).await {
- Ok(discovered) => discovered,
- Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
- startup = Some(self.start_public_helper(&configuration, &root).await?);
- self.discover(&root)
- .await
- .map_err(|error| error.to_string())?
- }
- Err(error) => return Err(error.to_string()),
- };
let HostLocation::Public { state_domain, .. } = &configuration.location else {
unreachable!()
};
- if discovered.source.deployment != configuration.deployment
- || state_domain
- .as_ref()
- .is_some_and(|expected| *expected != discovered.root_id)
- {
- return Err(
- "discovered Host differs from its configured deployment or State Root".into(),
- );
- }
- let stream = match maka_host_runtime::ipc::connect(&discovered.endpoint).await {
- Ok(stream) => stream,
- Err(error)
- if startup.is_none()
- && error.downcast_ref::<std::io::Error>().is_some_and(|error| {
- error.kind() == std::io::ErrorKind::ConnectionRefused
- || error.kind() == std::io::ErrorKind::NotFound
- }) =>
- {
- startup = Some(self.start_public_helper(&configuration, &root).await?);
- discovered = self
- .discover(&root)
- .await
- .map_err(|error| error.to_string())?;
- maka_host_runtime::ipc::connect(&discovered.endpoint)
- .await
- .map_err(|error| error.to_string())?
- }
- Err(error) => return Err(error.to_string()),
+ let request = crate::startup::Request {
+ root,
+ state_domain: state_domain.clone(),
+ deployment: configuration.deployment,
+ endpoints: configuration.endpoints.clone(),
+ native: configuration.native.clone(),
};
- let status = Client::status_stream(stream, Some(discovered.host_epoch)).await?;
- discovered
- .verify(&status)
- .map_err(|error| error.to_string())?;
- if discovered.source.deployment != configuration.deployment
- || state_domain
- .as_ref()
- .is_some_and(|expected| *expected != discovered.root_id)
- || startup.as_ref().is_some_and(|(_, ready)| {
- discovered.host_epoch != ready.epoch || discovered.root_id != ready.root_id
- })
- {
- return Err("local Host identity changed during startup".into());
- }
+ let prepared = crate::startup::prepare(&self.helper, &request, &self.namespaces).await?;
let client = Client::connect_peer(
- &discovered.endpoint,
- Some(discovered.host_epoch),
+ &prepared.discovery.endpoint,
+ Some(prepared.discovery.host_epoch),
source.clone(),
consumers.clone(),
configuration.endpoints.clone(),
)
.await?;
+ if client.source() != &prepared.discovery.source {
+ return Err("local Host source changed during admission".into());
+ }
let catalogue = client.catalogue().clone();
let connection =
native::Connection::new(Arc::new(ipc::Connector::new(client.call_channel_opener()?)));
@@ -476,36 +433,9 @@ impl Connections {
configuration,
peers: crate::transport::Peers::new(crate::transport::Peer::new(source, consumers)?),
});
- drop(startup);
+ drop(prepared);
Ok(catalogue)
}
- async fn discover(&self, root: &Path) -> std::io::Result<discovery::Discovery> {
- let namespaces = self.namespaces.clone();
- let root = root.to_owned();
- tokio::task::spawn_blocking(move || discovery::read_with_namespaces(&root, &namespaces))
- .await
- .map_err(std::io::Error::other)?
- }
- async fn start_public_helper(
- &self,
- configuration: &Host,
- root: &Path,
- ) -> Result<(crate::startup::Anchor, crate::startup::Ready)> {
- let HostLocation::Public { state_domain, .. } = &configuration.location else {
- unreachable!()
- };
- crate::startup::launch(
- &self.helper,
- crate::startup::Request {
- root: root.to_owned(),
- state_domain: state_domain.clone(),
- deployment: configuration.deployment,
- endpoints: configuration.endpoints.clone(),
- native: configuration.native.clone(),
- },
- )
- .await
- }
async fn start_cohost(
&mut self,
configuration: Host,
diff --git a/platform/transport/src/connections/host.rs b/platform/transport/src/connections/host.rs
index 2414aa0a19..414949992b 100644
--- a/platform/transport/src/connections/host.rs
+++ b/platform/transport/src/connections/host.rs
@@ -19,6 +19,7 @@
//! Public session attachment and locally owned cohost execution.
use super::*;
+use maka_host_runtime::application::host::discovery;
pub(super) struct ConnectedHost {
pub(super) owner: HostLink,
pub(super) connection: Option<Arc<native::Connection>>,
diff --git a/platform/transport/src/startup.rs b/platform/transport/src/startup.rs
index f38a4cfa5d..723e6488eb 100644
--- a/platform/transport/src/startup.rs
+++ b/platform/transport/src/startup.rs
@@ -17,14 +17,22 @@
* under the License.
*/
-//! A finite trusted CLI helper holds startup residency until peer admission.
+//! Discover and admit local callers while the trusted CLI owns Host startup.
use crate::{HostEndpoint, Result};
-use maka_host_runtime::application::transport::{NativeGrant, wire};
+use maka_event_log::root::RootNamespaces;
+use maka_host_runtime::application::{
+ host::{
+ Client,
+ discovery::{self, Discovery},
+ },
+ transport::{NativeGrant, wire},
+};
use serde::{Deserialize, Serialize};
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
process::Stdio,
+ time::Duration,
};
use tokio::process::{Child, ChildStdin, Command};
use uuid::Uuid;
@@ -71,15 +79,124 @@ pub struct Ready {
pub root_id: String,
pub epoch: Uuid,
}
+/// Discover or start the explicitly selected Host and admit an owned caller.
+/// `executable` must be an absolute path to the trusted CLI launcher. Startup
+/// uses the current account's Root namespaces and never installs or repairs it.
+/// Endpoint/native selections prepare startup; a live Host retains its published
+/// catalogue and grants.
+/// Close the returned Client to detach; quit requests idle Host shutdown.
+/// Reconnecting never retries an unconfirmed mutation or confirms its cleanup.
+pub async fn connect(executable: &Path, request: Request) -> Result<Client> {
+ tokio::time::timeout(Duration::from_secs(40), async {
+ let namespaces =
+ RootNamespaces::for_current_account().map_err(|error| error.to_string())?;
+ let prepared = prepare(executable, &request, &namespaces).await?;
+ let client = Client::connect(
+ &prepared.discovery.endpoint,
+ Some(prepared.discovery.host_epoch),
+ )
+ .await?;
+ if client.source() != &prepared.discovery.source {
+ return Err("local Host source changed during admission".into());
+ }
+ drop(prepared);
+ Ok(client)
+ })
+ .await
+ .map_err(|_| "local Host connection/startup deadline elapsed".to_owned())?
+}
+
+pub(super) struct Prepared {
+ pub(super) discovery: Discovery,
+ _startup: Option<Anchor>,
+}
+
+/// Retains startup residency until the caller's actual session is admitted.
+pub(super) async fn prepare(
+ executable: &Path,
+ request: &Request,
+ namespaces: &RootNamespaces,
+) -> Result<Prepared> {
+ request.validate()?;
+ wire::encode_bounded(request, REQUEST_LIMIT)?;
+ if !executable.is_absolute() {
+ return Err("local Host startup executable must be absolute".into());
+ }
+ let mut startup = None;
+ let mut discovered = match discover(&request.root, namespaces).await {
+ Ok(discovered) => discovered,
+ Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
+ startup = Some(launch(executable, request).await?);
+ discover(&request.root, namespaces)
+ .await
+ .map_err(|error| error.to_string())?
+ }
+ Err(error) => return Err(error.to_string()),
+ };
+ check_selection(&discovered, request)?;
+ let stream = match maka_host_runtime::ipc::connect(&discovered.endpoint).await {
+ Ok(stream) => stream,
+ Err(error)
+ if startup.is_none()
+ && error.downcast_ref::<std::io::Error>().is_some_and(|error| {
+ matches!(
+ error.kind(),
+ std::io::ErrorKind::NotFound | std::io::ErrorKind::ConnectionRefused
+ )
+ }) =>
+ {
+ startup = Some(launch(executable, request).await?);
+ discovered = discover(&request.root, namespaces)
+ .await
+ .map_err(|error| error.to_string())?;
+ check_selection(&discovered, request)?;
+ maka_host_runtime::ipc::connect(&discovered.endpoint)
+ .await
+ .map_err(|error| error.to_string())?
+ }
+ Err(error) => return Err(error.to_string()),
+ };
+ let status = Client::status_stream(stream, Some(discovered.host_epoch)).await?;
+ discovered
+ .verify(&status)
+ .map_err(|error| error.to_string())?;
+ if startup.as_ref().is_some_and(|(_, ready)| {
+ discovered.host_epoch != ready.epoch || discovered.root_id != ready.root_id
+ }) {
+ return Err("local Host identity changed during startup".into());
+ }
+ Ok(Prepared {
+ discovery: discovered,
+ _startup: startup.map(|(anchor, _)| anchor),
+ })
+}
+
+fn check_selection(discovered: &Discovery, request: &Request) -> Result<()> {
+ if discovered.source.deployment != request.deployment
+ || request
+ .state_domain
+ .as_ref()
+ .is_some_and(|expected| *expected != discovered.root_id)
+ {
+ return Err("discovered Host differs from its configured deployment or State Root".into());
+ }
+ Ok(())
+}
+
+async fn discover(root: &Path, namespaces: &RootNamespaces) -> std::io::Result<Discovery> {
+ let root = root.to_owned();
+ let namespaces = namespaces.clone();
+ tokio::task::spawn_blocking(move || discovery::read_with_namespaces(&root, &namespaces))
+ .await
+ .map_err(std::io::Error::other)?
+}
+
/// Owns only a finite helper's startup pipe, never a Host process or UI calls.
-/// Dropping it releases startup residency after the real peer is connected.
-pub(crate) struct Anchor {
+struct Anchor {
_child: Child,
_input: ChildStdin,
}
-pub(crate) async fn launch(executable: &Path, request: Request) -> Result<(Anchor, Ready)> {
- request.validate()?;
- wire::encode_bounded(&request, REQUEST_LIMIT)?;
+async fn launch(executable: &Path, request: &Request) -> Result<(Anchor, Ready)> {
let mut child = Command::new(executable)
.args(["host", "ensure"])
.stdin(Stdio::piped())
@@ -89,7 +206,7 @@ pub(crate) async fn launch(executable: &Path, request: Request) -> Result<(Ancho
.map_err(|error| format!("local Host startup helper: {error}"))?;
let mut input = child.stdin.take().expect("piped startup input");
let output = child.stdout.take().expect("piped startup output");
- wire::write_bounded(&mut input, &request, REQUEST_LIMIT).await?;
+ wire::write_bounded(&mut input, request, REQUEST_LIMIT).await?;
let ready: Ready = wire::Reader::new(output).read_bounded(4096).await?;
if !crate::connections::valid_domain(&ready.root_id)
|| ready.epoch.is_nil()
diff --git a/products/maka/cli/tests/integration/candidate.rs b/products/maka/cli/tests/integration/candidate.rs
index ade64193a4..a4274045ee 100644
--- a/products/maka/cli/tests/integration/candidate.rs
+++ b/products/maka/cli/tests/integration/candidate.rs
@@ -28,6 +28,7 @@ use std::{
time::{Duration, Instant},
};
+mod owned;
mod startup;
#[test]
diff --git a/products/maka/cli/tests/integration/candidate/owned.rs b/products/maka/cli/tests/integration/candidate/owned.rs
new file mode 100644
index 0000000000..4e3eb8964b
--- /dev/null
+++ b/products/maka/cli/tests/integration/candidate/owned.rs
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use super::*;
+use crate::local_fixture as support;
+use maka_host_runtime::application::transport::{Grant, HostEndpoint, Reply};
+use maka_plugin_core::operation::{OperationCleanup, OperationOutcome, OperationReply, Receipt};
+use maka_transport::startup::{self, Request};
+use serde_json::json;
+use std::{collections::BTreeMap, sync::Arc};
+use uuid::Uuid;
+
+async fn query(client: &Client, endpoint: &str, method: &str, input: Value) -> Value {
+ let mut call = client
+ .call(endpoint, method.into(), input, Default::default())
+ .await
+ .unwrap();
+ call.start().await.unwrap();
+ let reply = call.receive().await.unwrap();
+ assert!(call.cleanup_confirmed());
+ match reply {
+ Reply::Finished {
+ result: Ok(value),
+ cleanup: Ok(()),
+ } => value,
+ other => panic!("query must complete with confirmed cleanup: {other:?}"),
+ }
+}
+
+#[test]
+fn owned_startup_connects_concurrently_and_preserves_original_effect_and_last_client_lifetime() {
+ let directory = tempfile::tempdir().unwrap();
+ // The real CLI helper uses the current account's control namespace. This
+ // fixture owns only a fresh random Root and cleans its exact registration.
+ let fixture = CandidateFixture::new(directory.path().join("root"));
+ let namespaces = RootNamespaces::for_current_account().unwrap();
+ let runtime = tokio::runtime::Runtime::new().unwrap();
+ runtime.block_on(async {
+ tokio::time::timeout(Duration::from_secs(60), async {
+ let store = maka_event_log::EventLog::for_root(Arc::new(
+ RootOwner::open(&fixture.root, &namespaces).unwrap(),
+ ))
+ .await
+ .unwrap();
+ let deployment = Uuid::new_v4();
+ let (input, packages) = support::host();
+ let source = store
+ .commit_application(deployment, 0, input, vec![], packages)
+ .await
+ .unwrap();
+ store.close().await.unwrap();
+ let endpoints = BTreeMap::from([
+ (
+ "business".into(),
+ HostEndpoint {
+ consumer: support::identity(13),
+ import: "business".into(),
+ grant: Grant {
+ methods: ["read".into(), "write".into()].into(),
+ recovery: false,
+ },
+ },
+ ),
+ (
+ "receipts".into(),
+ HostEndpoint {
+ consumer: support::identity(13),
+ import: "receipts".into(),
+ grant: Grant {
+ methods: ["read".into()].into(),
+ recovery: true,
+ },
+ },
+ ),
+ ]);
+ let request = || Request {
+ root: fixture.root.clone(),
+ state_domain: Some(fixture.root_id.clone()),
+ deployment,
+ endpoints: endpoints.clone(),
+ native: vec![],
+ };
+ let executable = Path::new(env!("CARGO_BIN_EXE_maka"));
+ // No candidate is launched by the fixture. Both public entry calls must
+ // reach the actual CLI ensure/launch serialization and converge.
+ let (first, second) = tokio::join!(
+ startup::connect(executable, request()),
+ startup::connect(executable, request()),
+ );
+ let mut first = first.unwrap();
+ let mut second = second.unwrap();
+ assert_eq!(first.epoch(), second.epoch());
+ assert!(!first.epoch().is_nil());
+ assert_eq!(first.source(), &source);
+ assert_eq!(second.source(), &source);
+ let found = discovery::read(&fixture.root).unwrap();
+ assert_eq!(found.host_epoch, first.epoch());
+ let status = Client::status(&found.endpoint, Some(first.epoch()))
+ .await
+ .unwrap();
+ found.verify(&status).unwrap();
+ assert_eq!(
+ query(&first, "business", "read", Value::Null).await,
+ json!({"effects":0})
+ );
+
+ let mut write = first
+ .call("business", "write".into(), Value::Null, Default::default())
+ .await
+ .unwrap();
+ write.start().await.unwrap();
+ assert!(matches!(
+ write.receive().await.unwrap(),
+ Reply::Prepared { .. }
+ ));
+ let reference = write.reference().unwrap().clone();
+ assert_eq!(reference.source, source);
+ // Preparation owns no dispatched effect. The real Host journal, not a
+ // fabricated caller attestation, admits this mutation on confirmation.
+ assert_eq!(
+ query(&second, "business", "read", Value::Null).await,
+ json!({"effects":0})
+ );
+ let reply = match write.confirm().await.unwrap() {
+ Reply::Mutation {
+ reply,
+ cleanup: Ok(()),
+ } => *reply,
+ other => panic!("owned confirmation must settle its original mutation: {other:?}"),
+ };
+ assert!(write.cleanup_confirmed());
+ assert_eq!(reply.reference, reference);
+ assert_eq!(
+ reply.receipt,
+ Receipt::Recorded {
+ outcome: OperationOutcome::Returned {
+ value: json!({"effects":1})
+ },
+ cleanup: OperationCleanup::Confirmed,
+ }
+ );
+ first.close().await.unwrap();
+ assert!(
+ first
+ .call("business", "read".into(), Value::Null, Default::default())
+ .await
+ .is_err()
+ );
+ let recovered: OperationReply = serde_json::from_value(
+ query(
+ &second,
+ "receipts",
+ "read",
+ serde_json::to_value(&reference).unwrap(),
+ )
+ .await,
+ )
+ .unwrap();
+ assert_eq!(recovered, reply);
+ assert_eq!(
+ query(&second, "business", "read", Value::Null).await,
+ json!({"effects":1})
+ );
+
+ let impossible_helper = directory.path().join("never-created-startup-helper");
+ assert_eq!(
+ startup::connect(Path::new("maka"), request())
+ .await
+ .err()
+ .unwrap(),
+ "local Host startup executable must be absolute"
+ );
+ let mut reused = startup::connect(&impossible_helper, request())
+ .await
+ .unwrap();
+ assert_eq!(
+ reused.epoch(),
+ second.epoch(),
+ "live reuse must not run a helper"
+ );
+ assert_eq!(reused.source(), &source);
+ let mut wrong_domain = request();
+ wrong_domain.state_domain.as_mut().unwrap().replace_range(
+ 0..1,
+ if fixture.root_id.starts_with('0') {
+ "1"
+ } else {
+ "0"
+ },
+ );
+ assert_eq!(
+ startup::connect(&impossible_helper, wrong_domain)
+ .await
+ .err()
+ .unwrap(),
+ "discovered Host differs from its configured deployment or State Root"
+ );
+ let mut wrong_deployment = request();
+ wrong_deployment.deployment = Uuid::new_v4();
+ assert_eq!(
+ startup::connect(&impossible_helper, wrong_deployment)
+ .await
+ .err()
+ .unwrap(),
+ "discovered Host differs from its configured deployment or State Root"
+ );
+ let mut invalid = request();
+ invalid.root = PathBuf::from("relative-root");
+ assert_eq!(
+ startup::connect(&impossible_helper, invalid)
+ .await
+ .err()
+ .unwrap(),
+ "invalid local Host startup selection"
+ );
+
+ let original = fs::read(&fixture.registration).unwrap();
+ let mut stale: Value = serde_json::from_slice(&original).unwrap();
+ stale["pid"] = json!(status.pid.get() + 1);
+ fs::write(&fixture.registration, serde_json::to_vec(&stale).unwrap()).unwrap();
+ let rejected = startup::connect(&impossible_helper, request()).await;
+ fs::write(&fixture.registration, original).unwrap();
+ assert_eq!(
+ rejected.err().unwrap(),
+ "Host discovery is stale, incompatible or not ready"
+ );
+
+ // Individually valid field limits still permit an oversized encoded
+ // request. Live reuse must enforce bytes before selecting/spawning.
+ let mut oversized = request();
+ let mut endpoint = endpoints["business"].clone();
+ endpoint.grant.methods = (0..64)
+ .map(|index| format!("{index:03}{}", "x".repeat(253)))
+ .collect();
+ oversized.endpoints = (0..64)
+ .map(|index| (format!("endpoint-{index}"), endpoint.clone()))
+ .collect();
+ oversized.validate().unwrap();
+ let error = startup::connect(&impossible_helper, oversized)
+ .await
+ .err()
+ .expect("encoded request must be bounded before live reuse");
+ assert!(
+ error.contains("Application frame exceeds byte limit"),
+ "{error}"
+ );
+ assert_eq!(
+ query(&second, "business", "read", Value::Null).await,
+ json!({"effects":1})
+ );
+ reused.close().await.unwrap();
+
+ // Startup Anchor EOF is asynchronous. Observe the real session count,
+ // rather than mistaking helper drop for last-client ownership.
+ tokio::time::timeout(Duration::from_secs(10), async {
+ loop {
+ let status = Client::status(&found.endpoint, Some(second.epoch()))
+ .await
+ .unwrap();
+ found.verify(&status).unwrap();
+ if status.sessions == 1 {
+ break;
+ }
+ tokio::task::yield_now().await;
+ }
+ })
+ .await
+ .expect("startup helper residency must leave only the accepted last Client");
+ second.quit().await.unwrap();
+ tokio::time::timeout(Duration::from_secs(10), async {
+ loop {
+ match RootOwner::open(&fixture.root, &namespaces) {
+ Ok(owner) => {
+ assert_eq!(owner.root_id(), fixture.root_id);
+ drop(owner);
+ break;
+ }
+ Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
+ tokio::task::yield_now().await
+ }
+ Err(error) => {
+ panic!("reopen original Root after last-client quit: {error}")
+ }
+ }
+ }
+ })
+ .await
+ .expect("last-client quit must release the original Root");
+ assert!(!fixture.registration.exists());
+ #[cfg(unix)]
+ assert!(!found.endpoint.exists());
+ })
+ .await
+ .unwrap();
+ });
+}
diff --git a/products/maka/cli/tests/integration/main.rs b/products/maka/cli/tests/integration/main.rs
index 79aa9bd8a1..51e4771f07 100644
--- a/products/maka/cli/tests/integration/main.rs
+++ b/products/maka/cli/tests/integration/main.rs
@@ -22,6 +22,9 @@ mod candidate;
mod code_command;
#[cfg(unix)]
mod desktop;
+#[allow(dead_code)]
+#[path = "../../../../../platform/transport/tests/support/mod.rs"]
+mod local_fixture;
mod sandbox;
mod terminal;
#[path = "../../src/tests.rs"]
diff --git a/products/maka/cli/tests/integration/terminal/application.rs b/products/maka/cli/tests/integration/terminal/application.rs
index 952ee4dc01..10d4a489cf 100644
--- a/products/maka/cli/tests/integration/terminal/application.rs
+++ b/products/maka/cli/tests/integration/terminal/application.rs
@@ -267,9 +267,7 @@ fn plugin_application_setup_rejection_is_clean_and_diagnostics_cannot_emit_escap
);
}
-#[allow(dead_code)]
-#[path = "../../../../../../platform/transport/tests/support/mod.rs"]
-mod local_fixture;
+use crate::local_fixture;
/// Tests retain the business Host independently of every foreground UI. This
/// guard also retires product auto-started Hosts before temporary Roots vanish.
pub(super) struct HostRootGuard(std::path::PathBuf);
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org