From 2ad2a482571bc030a7be298217cf7956bd164da4 Mon Sep 17 00:00:00 2001 From: AnyEnv Sandbox Date: Sun, 25 Oct 2026 15:46:11 +0000 Subject: [PATCH 1/2] fix: wait for prepared template readiness within its deadline --- .agents/README.md | 2 + ...-25-prepared-template-readiness-cloud.json | 70 +++++++++++++++ .../2026-10-25-prepared-template-readiness.md | 50 +++++++++++ .../2026-10-25-prepared-template-readiness.md | 52 +++++++++++ CHANGELOG.md | 2 + client-sdk/pyproject.toml | 2 +- client-sdk/src/agent_sandbox/_version.py | 2 +- client-sdk/src/agent_sandbox/api/template.py | 26 ++++-- .../tests/test_api/test_template_launch.py | 86 +++++++++++++++++++ docs/en/guide/template-workflows.md | 2 + docs/zh/guide/template-workflows.md | 2 + .../src/agent_sandbox/server/_version.py | 2 +- 12 files changed, 290 insertions(+), 8 deletions(-) create mode 100644 .agents/evidence/verify/2026-10-25-prepared-template-readiness-cloud.json create mode 100644 .agents/evidence/verify/2026-10-25-prepared-template-readiness.md create mode 100644 .agents/notes/proposed/feature/2026-10-25-prepared-template-readiness.md diff --git a/.agents/README.md b/.agents/README.md index 078e2443..fd130fab 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -172,3 +172,5 @@ Builtin transport: [direct HTTP](notes/implemented/architecture/2026-10-18-nativ - [Bounded public PyPI readback](notes/proposed/process/2026-10-25-pypi-public-readback.md) · [verification](evidence/verify/2026-10-25-pypi-public-readback.md) - [Public runtime fixture images](notes/proposed/process/2026-10-25-public-runtime-fixture-images.md) · [verification](evidence/verify/2026-10-25-public-runtime-fixture-images.md) + +- [Prepared-template readiness](notes/proposed/feature/2026-10-25-prepared-template-readiness.md) · [verification](evidence/verify/2026-10-25-prepared-template-readiness.md). diff --git a/.agents/evidence/verify/2026-10-25-prepared-template-readiness-cloud.json b/.agents/evidence/verify/2026-10-25-prepared-template-readiness-cloud.json new file mode 100644 index 00000000..c970065d --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-prepared-template-readiness-cloud.json @@ -0,0 +1,70 @@ +{ + "public_a2": { + "packages": { + "agent-sandbox-client": "0.4.0a2", + "agent-sandbox-server": "0.4.0a2" + }, + "created": 1, + "cleanup_confirmed": 1, + "first_ready_exit": 1, + "first_ready_seconds": 0.524, + "first_ready_connection_refused": true, + "acknowledged_process_running_at_first_probe": true, + "same_owned_port_later_ready": true, + "later_health_seconds_after_first_probe": 5.364, + "diagnostic_probe_count": 7, + "same_pid_running_after_health": true, + "unchanged_failed_result_returned_to_launcher": true, + "launch_error_reason": "readiness-command-failed", + "launch_error_phase": "before-invoke", + "result": "CONFIRMED_BUG", + "script_sha256": "d9e7a27d76e953210f0f7fda27fded8592e1ff9e3fc40984f405fb8533708bf4" + }, + "candidate": { + "packages": { + "agent-sandbox-client": "0.4.0a3", + "agent-sandbox-server": "0.4.0a3" + }, + "runtime_source_sha256": "ddf984968e5f2dcdf280616196e4a4c62e2175b0e524e0cfb79b73b1b88efcb3", + "cases": [ + { + "mode": "delayed-ready", + "ready_exits": [ + 1, + 1, + 1, + 1, + 1, + 0 + ], + "same_owned_server_pid_alive": true, + "independent_typed_absence": true, + "result": "PASS" + }, + { + "mode": "deadline", + "ready_exits": [ + 1 + ], + "typed_deadline_failure": true, + "probe_to_failure_seconds": 1.174, + "independent_typed_absence": true, + "result": "PASS" + }, + { + "mode": "cancel", + "ready_exits": [ + 1 + ], + "cancelled": true, + "independent_typed_absence": true, + "result": "PASS" + } + ], + "created": 3, + "cleanup_confirmed": 3, + "result": "PASS", + "script_sha256": "e59c64622435cc7cdd5e28dc8ce37b0fe809145188c16db6d8bcf4923bcf3ccc" + }, + "limits": "Public a2 causal reproduction and candidate editable installation are separate. This is not whole-catalog or public a3 acceptance." +} diff --git a/.agents/evidence/verify/2026-10-25-prepared-template-readiness.md b/.agents/evidence/verify/2026-10-25-prepared-template-readiness.md new file mode 100644 index 00000000..91d46f2b --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-prepared-template-readiness.md @@ -0,0 +1,50 @@ +# Prepared-template readiness verification + +Date: 2026-10-25 +Result: PARTIAL + +Scope: prepared-reference Python `ready_cmd` polling, four regressions, bilingual +launch documentation and the lockstep 0.4.0a3 version/dependency metadata. Base is +main cdf807a4a40079e7d215c0d299b061f0c895a8cb. No provider implementation changes. + +## Original failure and cause + +OpenSandbox run [40206364834](https://github.com/Alibaba-Agent-Sandbox/awesome-providers/actions/runs/40206364834) +failed in the public a2 Python prepared launch with readiness-command-failed. +Default source CLI build/push/launch, named application command, direct/proxy +authentication probes and independent Node proxy call had passed. Final runner +cleanup passed. This was not a failed image build or push. + +Installed public a2 was reproduced on an actual Express `base` reference. A stdlib +HTTP server delayed its listener five seconds. Its first ready probe returned exit1 +(connection refused) after0.524s. The exact owned server PID was alive before and +after the same port became healthy5.364s later, inside the15s budget. A diagnostic +observer returned the original failed result unchanged; a2 still rejected launch. +Independent typed NotFound confirmed cleanup. An earlier diagnostic incorrectly +assumed process-list membership for a child PID and failed its own assertion; its +sandbox was also confirmed absent. Both original receipts remain preserved. + +## Executed checks + +Python3.11.15, new isolated editable candidate pair0.4.0a3 (not a public package), +no source-path injection into public a2. Candidate runtime bytes and sanitized +cloud results are in the adjacent JSON receipt. + +- New regression baseline against a2: 3 FAIL, 1 PASS (cancellation already cleaned). +- Focused `pytest tests/test_api/test_template_launch.py -q`: PASS,25 tests. +- `make lint typecheck`: PASS for both packages. +- `make hygiene`: PASS. +- Repository health before final record insertion:106 PASS,1 WARN,0 FAIL. +- Actual candidate cloud: delayed listener initially returned five nonzero probes, + then exit0 and successful launch; exact server PID remained alive. Permanent + unready launch used a1s budget and raised the existing phase error after1.174s + including cleanup. Cancellation after the first probe preserved cleanup. + All3 owned sandboxes were independently typed NotFound; no recovery was needed. +- Independent read-only review of runtime/tests: no blocking finding. Budget is + per ready-command phase, not a new whole-launch deadline. + +Full owner suites/build, final health, latest-head remote CI, provider T4, review, +normal merge and public a3 release/readback remain pending. There is no Docker on +this local host. No public a3 or whole-catalog acceptance is claimed. + +Issue: https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/121 diff --git a/.agents/notes/proposed/feature/2026-10-25-prepared-template-readiness.md b/.agents/notes/proposed/feature/2026-10-25-prepared-template-readiness.md new file mode 100644 index 00000000..cbc57c07 --- /dev/null +++ b/.agents/notes/proposed/feature/2026-10-25-prepared-template-readiness.md @@ -0,0 +1,52 @@ +# Decision: Wait for prepared-template readiness within its existing budget + +Status: proposed + +## Problem + +Public a2 acknowledges the started process, then runs `ready_cmd` once. A valid +application can still be initializing, so an early nonzero health probe fails +launch before `readiness_timeout` expires. OpenSandbox T4 observed this after +successful default CLI build, push, launch and application calls. + +## Decision + +Poll nonzero readiness results inside one monotonic `readiness_timeout` budget. +Constrain each command await by its remaining budget and preserve the provider's +positive integer command timeout. Keep the existing failure reason/phase and owned +cleanup, including cancellation. Do not replay creation or startup. Prepare the +immutable lockstep client/server 0.4.0a3 pair; a2 artifacts remain unchanged. + +## API Design + +No parameters, launch modes, provider declarations or states are added. This budget +covers `ready_cmd` probes; existing startup-acknowledgement and per-service health +budgets are unchanged. Invalid results and transport errors still fail immediately. + +## Alternatives considered + +Adding a fixed sleep to the provider test would hide the product race. Retrying the +entire create/launch could duplicate resources. A new readiness framework or global +launch-budget redesign is unnecessary for this defect. + +## Dependencies + +- https://github.com/Alibaba-Agent-Sandbox/awesome-providers/pull/40 +- Existing public a2 and CLI readiness semantics; no provider implementation change. + +## Test Strategy + +Regress delayed success after early failure, bounded permanent failure and hanging +probes, cancellation and owned cleanup. Compare installed public a2 with the exact +candidate against a real prepared sandbox. Run owner checks and complete real +OpenSandbox T4 on the candidate; public a3 installation/readback follows publication. + +## Acceptance criteria + +Focused/full tests, owner CI, real readiness scenarios and provider T4 pass with +independent owned cleanup. Normal review/merge and lockstep publication/readback +complete before claiming public delivery. Preserve failed and historical receipts. + +[Verification](../../../evidence/verify/2026-10-25-prepared-template-readiness.md). + +Issue: https://github.com/Alibaba-Agent-Sandbox/python-sdk/issues/121 diff --git a/CHANGELOG.md b/CHANGELOG.md index 47749022..ea24c13e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### Fixed +- Wait for prepared-template readiness inside one bounded `readiness_timeout` instead of failing after the first early nonzero probe; preserve timeout/cancellation cleanup. Prepare lockstep client/server 0.4.0a3 without changing published a2 artifacts. / 修复模板进程启动后首次就绪探测过早失败的问题,在单一预算内等待就绪,并保留超时和取消时的资源清理;准备同版本双包 0.4.0a3,不覆盖公开 a2。 + - Build both SDK offline fixtures from the exact public a2 server on official PyPI, remove unreachable local-wheel fallback, and verify actual guest HTTP behavior and cleanup in CI. / SDK 两个离线夹具统一从官方 PyPI 安装精确 a2 服务端,删除不可达的本地 wheel 回退,并通过 CI 验证真实镜像 HTTP 行为及清理。 ### Fixed diff --git a/client-sdk/pyproject.toml b/client-sdk/pyproject.toml index 01ccb076..d947531d 100644 --- a/client-sdk/pyproject.toml +++ b/client-sdk/pyproject.toml @@ -44,7 +44,7 @@ dependencies = [ "tomli>=2.0; python_version < '3.11'", # The client uses the matching local server runtime for its server/declarative helpers. # Keep the required distribution version equal to ``_version.py``. - "agent-sandbox-server==0.4.0a2", + "agent-sandbox-server==0.4.0a3", ] [project.optional-dependencies] diff --git a/client-sdk/src/agent_sandbox/_version.py b/client-sdk/src/agent_sandbox/_version.py index 2cc3eb2a..4fbec93e 100644 --- a/client-sdk/src/agent_sandbox/_version.py +++ b/client-sdk/src/agent_sandbox/_version.py @@ -1 +1 @@ -__version__ = "0.4.0a2" +__version__ = "0.4.0a3" diff --git a/client-sdk/src/agent_sandbox/api/template.py b/client-sdk/src/agent_sandbox/api/template.py index 1c7cab0d..ae7675fc 100644 --- a/client-sdk/src/agent_sandbox/api/template.py +++ b/client-sdk/src/agent_sandbox/api/template.py @@ -5,6 +5,7 @@ import asyncio import copy import json +import math import os import re from pathlib import Path @@ -297,11 +298,26 @@ async def launch_template( await _dispatch_start(sandbox, snapshot["start_cmd"], readiness_timeout) journal.append("started", sandbox_id=sandbox.id) if snapshot.get("ready_cmd"): - result = await sandbox.commands.run( - snapshot["ready_cmd"], timeout=int(readiness_timeout) - ) - if not isinstance(result, ProcessResult) or result.exit_code != 0: - raise TemplateLaunchError("readiness-command-failed", "before-invoke") + loop = asyncio.get_running_loop() + deadline = loop.time() + readiness_timeout + while True: + remaining = deadline - loop.time() + if remaining <= 0: + raise TemplateLaunchError("readiness-command-failed", "before-invoke") + try: + result = await asyncio.wait_for( + sandbox.commands.run( + snapshot["ready_cmd"], timeout=max(1, math.ceil(remaining)) + ), + timeout=remaining, + ) + except asyncio.TimeoutError: + raise TemplateLaunchError("readiness-command-failed", "before-invoke") from None + if not isinstance(result, ProcessResult): + raise TemplateLaunchError("readiness-command-failed", "before-invoke") + if result.exit_code == 0: + break + await asyncio.sleep(min(0.25, max(0, deadline - loop.time()))) for name, service in plan["services"].items(): client = await sandbox.application_client(service["port"], token=token) clients[name] = client diff --git a/client-sdk/tests/test_api/test_template_launch.py b/client-sdk/tests/test_api/test_template_launch.py index 7dffc823..0acef506 100644 --- a/client-sdk/tests/test_api/test_template_launch.py +++ b/client-sdk/tests/test_api/test_template_launch.py @@ -425,3 +425,89 @@ async def test_other_protocol_is_exposed_without_guessing_http_routes( server.health.assert_not_awaited() server.discover.assert_not_awaited() server.aclose.assert_awaited_once() + + +@pytest.fixture +def readiness_launch(tmp_path, monkeypatch): + """Keep startup acknowledgement distinct from application readiness.""" + body = manifest("managed") + body.update(start_cmd="start application", ready_cmd="check application", provides={}) + sandbox = SimpleNamespace( + id="owned", + provider="fixture", + kill=AsyncMock(), + commands=SimpleNamespace(run=AsyncMock()), + ) + monkeypatch.setattr( + "agent_sandbox.api.template.Sandbox.create", AsyncMock(return_value=sandbox) + ) + startup = AsyncMock() + monkeypatch.setattr("agent_sandbox.api.template._dispatch_start", startup) + options = dict( + provider=provider(["sandbox.create", "sandbox.delete", "exec.run"]), + reference="prepared-reference", + journal_path=tmp_path / "run", + ) + return body, sandbox, startup, options + + +async def test_readiness_retries_early_failure_after_start_acknowledgement(readiness_launch): + from agent_sandbox.models.process import ProcessResult + + body, sandbox, startup, options = readiness_launch + sandbox.commands.run.side_effect = [ProcessResult(exit_code=1), ProcessResult(exit_code=0)] + async with await launch_template(body, readiness_timeout=2, **options): + startup.assert_awaited_once() + assert sandbox.commands.run.await_count == 2 + assert all( + call.args == (body["ready_cmd"],) for call in sandbox.commands.run.await_args_list + ) + sandbox.kill.assert_awaited_once() + + +@pytest.mark.parametrize("probe", ["nonzero", "hang"]) +async def test_readiness_failure_respects_one_deadline_and_cleans_owned(readiness_launch, probe): + import asyncio + + from agent_sandbox.models.process import ProcessResult + + body, sandbox, _, options = readiness_launch + entered = asyncio.Event() + + async def run(*args, **kwargs): + entered.set() + if probe == "hang": + await asyncio.Event().wait() + return ProcessResult(exit_code=1) + + sandbox.commands.run.side_effect = run + started = asyncio.get_running_loop().time() + with pytest.raises(TemplateLaunchError, match="readiness-command-failed"): + await asyncio.wait_for(launch_template(body, readiness_timeout=0.08, **options), 2) + elapsed = asyncio.get_running_loop().time() - started + assert entered.is_set() and 0.04 <= elapsed < 1 + assert all(call.kwargs["timeout"] == 1 for call in sandbox.commands.run.await_args_list) + sandbox.kill.assert_awaited_once() + + +async def test_readiness_cancellation_cleans_owned_sandbox(readiness_launch): + import asyncio + + body, sandbox, _, options = readiness_launch + entered, cancelled = asyncio.Event(), asyncio.Event() + + async def run(*args, **kwargs): + entered.set() + try: + await asyncio.Event().wait() + finally: + cancelled.set() + + sandbox.commands.run.side_effect = run + launch = asyncio.create_task(launch_template(body, readiness_timeout=30, **options)) + await asyncio.wait_for(entered.wait(), 2) + launch.cancel() + with pytest.raises(asyncio.CancelledError): + await launch + assert cancelled.is_set() + sandbox.kill.assert_awaited_once() diff --git a/docs/en/guide/template-workflows.md b/docs/en/guide/template-workflows.md index 16dc6f14..a15818e3 100644 --- a/docs/en/guide/template-workflows.md +++ b/docs/en/guide/template-workflows.md @@ -57,6 +57,8 @@ failure of a later service. Journals exclude tokens, environment values and sign they do not constitute verified cloud receipts. One effective `ABX_SERVER_TOKEN` value is used both for the sandbox environment and the application clients. +After process-start acknowledgement, the launcher polls a nonzero `ready_cmd` result within one `readiness_timeout` budget. Each probe uses the remaining budget; a successful exit completes readiness. Expiration raises `readiness-command-failed` and removes the owned sandbox. Cancellation also retains owned cleanup. This waits for application readiness, which is separate from acknowledging that its process started. + ## Migrating specification 4 Use `spec_version: 5`; remove `workflow_contract_version`, `default_workflow`, and diff --git a/docs/zh/guide/template-workflows.md b/docs/zh/guide/template-workflows.md index 05ec5bad..4f7512bb 100644 --- a/docs/zh/guide/template-workflows.md +++ b/docs/zh/guide/template-workflows.md @@ -51,6 +51,8 @@ Python 不构建源码。先使用 CLI 准备并传入生成的 Provider 引用 后续服务失败也会关闭之前解析的所有客户端。日志不记录令牌、环境变量值和签名 URL, 也不等于云端验证证据。同一个最终 `ABX_SERVER_TOKEN` 同时用于沙箱环境和客户端。 +收到进程启动确认后,启动器会在一个 `readiness_timeout` 预算内重复执行尚未成功的 `ready_cmd`。每次探测受剩余预算约束,退出码为 0 时才完成就绪检查;预算耗尽会抛出 `readiness-command-failed` 并删除本次创建的沙箱,取消启动同样保留资源清理。进程已经启动与应用已经就绪是两个独立条件。 + ## 从规范 4 迁移 改为 `spec_version: 5`,删除 `workflow_contract_version`、`default_workflow`、 diff --git a/server-sdk/src/agent_sandbox/server/_version.py b/server-sdk/src/agent_sandbox/server/_version.py index 2cc3eb2a..4fbec93e 100644 --- a/server-sdk/src/agent_sandbox/server/_version.py +++ b/server-sdk/src/agent_sandbox/server/_version.py @@ -1 +1 @@ -__version__ = "0.4.0a2" +__version__ = "0.4.0a3" From 7ed4c2627acdc7dde6370384b5bb95d00c6cf53f Mon Sep 17 00:00:00 2001 From: AnyEnv Sandbox Date: Sun, 25 Oct 2026 15:53:13 +0000 Subject: [PATCH 2/2] test: record complete candidate readiness and OpenSandbox acceptance --- ...-10-25-prepared-template-readiness-t4.json | 106 ++++++++++++++++++ .../2026-10-25-prepared-template-readiness.md | 38 +++++-- 2 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 .agents/evidence/verify/2026-10-25-prepared-template-readiness-t4.json diff --git a/.agents/evidence/verify/2026-10-25-prepared-template-readiness-t4.json b/.agents/evidence/verify/2026-10-25-prepared-template-readiness-t4.json new file mode 100644 index 00000000..5151a022 --- /dev/null +++ b/.agents/evidence/verify/2026-10-25-prepared-template-readiness-t4.json @@ -0,0 +1,106 @@ +{ + "run_url": "https://github.com/Alibaba-Agent-Sandbox/awesome-providers/actions/runs/40208641347", + "run_id": 40208641347, + "provider_head": "b78ea200ba7ccb30cd868ae609cad9c7b5031e31", + "job_id": 121214919187, + "conclusion": "success", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Run actions/checkout@v4", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Run actions/setup-node@v4", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Run actions/setup-python@v5", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Fetch reviewed host sources", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Install locked CLI and matching SDK packages", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Project parser with actual host staging and pinned official SDK", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Verify SDK fixture and CLI share an isolated home", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Start real Docker-backed OpenSandbox", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Run direct plugin, installed contract kit and environment tests", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Run Python SDK scenarios with streamed diagnostics", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Run CLI lifecycle, commands and files", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Run native project build, registry push and automatic launch", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Cleanup and verify no sandbox containers remain", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Post Run actions/setup-python@v5", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Post Run actions/setup-node@v4", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Post Run actions/checkout@v4", + "status": "completed", + "conclusion": "success" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success" + } + ], + "candidate_sdk_source": "2ad2a482571bc030a7be298217cf7956bd164da4", + "template_source": "26d6e24e8b29b6c73b364d954dc0279d22a6f9d5", + "cli_source": "6dfdf9897ad76bf2fdb7eb1ccf455252b2dcb9fb", + "node_source": "63d7cd8ece221b9f968eef51eeb932ea334c90d3", + "host_install": "exact candidate source pair 0.4.0a3, not public PyPI", + "guest_server_version": "0.4.0a2", + "scope": "full existing OpenSandbox T4 default/portable/proxy, CLI/Python/Node application calls and final no-owned-container check" +} diff --git a/.agents/evidence/verify/2026-10-25-prepared-template-readiness.md b/.agents/evidence/verify/2026-10-25-prepared-template-readiness.md index 91d46f2b..036444e8 100644 --- a/.agents/evidence/verify/2026-10-25-prepared-template-readiness.md +++ b/.agents/evidence/verify/2026-10-25-prepared-template-readiness.md @@ -16,9 +16,9 @@ authentication probes and independent Node proxy call had passed. Final runner cleanup passed. This was not a failed image build or push. Installed public a2 was reproduced on an actual Express `base` reference. A stdlib -HTTP server delayed its listener five seconds. Its first ready probe returned exit1 -(connection refused) after0.524s. The exact owned server PID was alive before and -after the same port became healthy5.364s later, inside the15s budget. A diagnostic +HTTP server delayed its listener five seconds. Its first ready probe returned exit 1 +(connection refused) after 0.524s. The exact owned server PID was alive before and +after the same port became healthy 5.364s later, inside the 15s budget. A diagnostic observer returned the original failed result unchanged; a2 still rejected launch. Independent typed NotFound confirmed cleanup. An earlier diagnostic incorrectly assumed process-list membership for a child PID and failed its own assertion; its @@ -26,24 +26,42 @@ sandbox was also confirmed absent. Both original receipts remain preserved. ## Executed checks -Python3.11.15, new isolated editable candidate pair0.4.0a3 (not a public package), +Python 3.11.15, new isolated editable candidate pair 0.4.0a3 (not a public package), no source-path injection into public a2. Candidate runtime bytes and sanitized cloud results are in the adjacent JSON receipt. - New regression baseline against a2: 3 FAIL, 1 PASS (cancellation already cleaned). -- Focused `pytest tests/test_api/test_template_launch.py -q`: PASS,25 tests. +- Focused `pytest tests/test_api/test_template_launch.py -q`: PASS, 25 tests. - `make lint typecheck`: PASS for both packages. - `make hygiene`: PASS. -- Repository health before final record insertion:106 PASS,1 WARN,0 FAIL. +- `make format-check build dist-check`: PASS; all four distributions built, + both wheels installed together in a clean environment, imports and pip check + passed. The built client wheel's runtime bytes match the cloud-tested source. +- Full client suite: 2,275 PASS, 26 explicit skips. +- `make test` server suite: 877 PASS, 3 FAIL. Two raw-HTTP tests observed 501/502 + instead of expected 400, matching the earlier local-environment failures. The + package-list test initially lacked pytest because the new uv environment had + no pip. Installing pip in that environment and rerunning that test passed. + The original failures remain recorded; no assertion or fixture was weakened. +- Final repository health after record insertion: 106 PASS, 1 WARN, 0 FAIL. - Actual candidate cloud: delayed listener initially returned five nonzero probes, - then exit0 and successful launch; exact server PID remained alive. Permanent - unready launch used a1s budget and raised the existing phase error after1.174s + then exit 0 and successful launch; exact server PID remained alive. Permanent + unready launch used a 1s budget and raised the existing phase error after 1.174s including cleanup. Cancellation after the first probe preserved cleanup. - All3 owned sandboxes were independently typed NotFound; no recovery was needed. + All 3 owned sandboxes were independently typed NotFound; no recovery was needed. - Independent read-only review of runtime/tests: no blocking finding. Budget is per ready-command phase, not a new whole-launch deadline. -Full owner suites/build, final health, latest-head remote CI, provider T4, review, +The full existing OpenSandbox T4 passed in +[run 40208641347](https://github.com/Alibaba-Agent-Sandbox/awesome-providers/actions/runs/40208641347), +provider source b78ea200ba7ccb30cd868ae609cad9c7b5031e31 and exact SDK candidate +2ad2a482571bc030a7be298217cf7956bd164da4. All job steps succeeded, including +default/portable/proxy launch, CLI and Python application calls, independent Node +proxy call and final owned-container absence. Adjacent JSON records exact inputs. +This is a candidate host pair; the actual guest server remained public a2. +The final follow-up changes only this evidence, not the tested runtime bytes. + +Latest-head remote CI, final review, normal merge and public a3 release/readback remain pending. There is no Docker on this local host. No public a3 or whole-catalog acceptance is claimed.