* refactor(managed-agent): split server into Maven modules Separate contracts, orchestration, persistence, adapters, assembly, and verification while retaining the existing service delivery model. Move tests and build consumers together and document local evidence and pending CI acceptance. Co-authored-by: Qwen-Coder <[email protected]> * refactor(managed-agent): reduce configuration and SDK boilerplate Co-authored-by: Qwen-Coder <[email protected]> * refactor(managed-agent): narrow contracts and simplify test scaffolding Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): repair verification after module migration Resolve shared fixtures and worker paths from the Maven module root, update CI packaging assertions, and align existing test fixtures with the extracted contracts. Restore restricted Workspace constructors and remove redundant test visibility changes without altering inherited MySQL coverage. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): align remaining Harness and OSS fixtures Preserve SDK refusal messages and register the managed OSS adapter at fixture injection points. Keep the existing retry and client shutdown assertions intact. Co-authored-by: Qwen-Coder <[email protected]> * style(managed-agent): order Hosted cleanup Harness imports Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): fix recovery smoke fixture and import order Supply the required transcript coverage in the recovery browser fixture and wait for history before testing a complete handoff. Preserve its existing safety and submission assertions. Order the cleanup coordinator test imports for Checkstyle. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]>
Resident Workspace configuration
This first-release configuration retains one Pod/PVC per tenant/Workspace. It supports Read, Write and Edit through the public files profile. Session close settles and revokes the original context; the next Session retains the same files and runtime. It does not install a node collector or automatically delete or reuse storage. This directory has not been deployed or accepted in ACK.
The runtime is implemented on main by PR #14340. The simplified PR #14758 documents this deployment boundary. The resident path is independent of the node-evidence candidate withdrawn from #14758 and the closed installation proposal #14761. Optional explicit idle retirement is implemented in #14765 and described below. This configuration needs no Go toolchain, custom containerd/shim, node collector or cold-bootstrap installation.
The 2026-10-24 scope decision confirms that complete CSI auditing is not a first-release prerequisite. Session handoff retains the original Pod/PVC; deletion-only idle retirement retains storage ownership. The explicit S2/S3 reclamation and retained-data handoff implementation below requires separate native qualification.
Trusted platform and supported storage
Trust the administrator, Kubernetes control plane, host and qualified CSI driver.
Keep the existing tenant/actor, Workspace generation, original runtime and mount
checks. This configuration does not independently prove continuous host
confinement. The supported driver is diskplugin.csi.alibabacloud.com with
ReadWriteOncePod, Filesystem and ext4; the registered disk serial must match
the actual mounted device. Other drivers or access modes require independent
design and qualification. RWOP does not authorize storage handoff after a node
failure.
Configuration and installation
Use application.example.yaml as an additional Spring configuration file.
Supply the server's existing database, Hosted Harness, Session store, Broker
token and credential-encryption settings separately. The Kubernetes token and CA
settings name local files read by the server; credentials do not belong in this
template or the worker Workspace.
Keep the Session store's global workspace-id equal to the Broker's global
workspace-id, as in the example. The existing Hosted startup contract requires
this match; each public Workspace still resolves its own registered identity.
Before enabling the mapping, register the exact CSI registration with the
existing registration operator and register the matching tenant/Workspace,
generation and storage in the Workspace registry. Use managed-runtime-tools/1
and preapproved-workspace-tools/1 as the Workspace config/policy, then grant the
trusted actor OPERATOR or OWNER. Each physical volume may appear in only one
resident mapping. A registration or a caller-supplied Workspace ID alone does
not enable CSI execution.
Render the exact storage/namespace protection objects with the existing Java
protection operator, passing <reviewed-registration-json> <policy-name> <binding-name>. This prints a Kubernetes JSON List and
does not contact the cluster or database. From the reference distribution,
invoke it with:
bin/qwen-managed-agent-admin csi-protection \
/absolute/path/registration.json resident-protection resident-binding
From a module build, run the same entry from the repository root:
java -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.WorkspaceCsiProtectionMain \
-cp packages/managed-agent-server/server/target/qwen-managed-agent-server-<version>-exec.jar \
org.springframework.boot.loader.launch.PropertiesLauncher \
/absolute/path/registration.json resident-protection resident-binding
After separately authorized installation, pin its actual namespace/policy/binding
UIDs and generations. Select an immutable image digest containing Node.js 22 or
later and the built CLI. The CSI file path uses Node filesystem APIs and reads
Linux /proc/self/mountinfo, /proc/self/fd and the volume's
/sys/dev/block/<major>:<minor>/device/serial; these must be available to UID 1000. The command contains only the executable and CLI path;
the provisioner appends managed-runtime-worker --container-boot and the boot
file path. Optional artifact ConfigMaps must be immutable and explicitly pinned.
The server must reach the Kubernetes API and the original Pod's worker endpoint.
The worker runs as UID/GID 1000, with a read-only root filesystem, no added Linux
capabilities or service-account token, its fixed PVC at /workspace, and its
temporary directory in an emptyDir. The Pod has restartPolicy: Never. A
missing, restarted or replaced original Pod keeps admission closed; this version
does not recreate or adopt a replacement.
Create and close Sessions using the existing public Workspace Session APIs. Only one unfinished Session is admitted. Close must succeed before another Session can begin. A release timeout or unknown outcome retains the reservation and blocks handoff. The selected cwd is fixed for each Session; choose another cwd at the next Session's creation. Shell, MCP, Hooks, child Sessions sharing the runtime and in-place cwd changes are unavailable in this version.
Explicit idle retirement
Normal Session close keeps the Workspace reusable. To retire an idle Workspace, use the private Java maintenance entry with the same deployment configuration, database and Runtime credential key. Run the current schema migration through the normal service upgrade first and fence older Server/Broker binaries. The maintenance command validates the catalog and never migrates it.
Create a bounded request file naming only the original scope and an authorized
audit actor with a current Workspace OWNER grant:
{
"tenantId": "tenant-a",
"workspaceId": "workspace-a",
"workspaceGeneration": 1,
"actorId": "owner-a",
"retirementId": "retire-workspace-a-1"
}
The operator must already control the trusted maintenance host and deployment credentials; the actor field alone does not authenticate anyone. From the reference distribution:
bin/qwen-managed-agent-admin resident-retirement \
inspect /absolute/path/retirement.json \
--spring.config.additional-location=file:/absolute/path/application.yaml
From a module build, run from the repository root with the same arguments:
java -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.ResidentWorkspaceRetirementMain \
-cp packages/managed-agent-server/server/target/qwen-managed-agent-server-<version>-exec.jar \
org.springframework.boot.loader.launch.PropertiesLauncher \
inspect /absolute/path/retirement.json \
--spring.config.additional-location=file:/absolute/path/application.yaml
Replace inspect with begin to atomically refuse new Sessions, acquisition,
activation, dispatch and recovery. It requires terminal Sessions, sealed journal
heads (or permanently deleted heads with cleared writers), released Runtime
Sessions and settled executions. The original binding/active
slot remains and storage stays RESERVED. Reuse the same request after a lost
reply. Inspection is read-only and does not contact Kubernetes.
Deletion is a separate explicit command: replace the action and arguments with
delete-pod /absolute/path/retirement.json <observed-resourceVersion>. It verifies
the original resources through the configured Kubernetes API, then records a
normal UID/resourceVersion-preconditioned Pod DELETE with 30-second grace and
Background propagation. Credentials remain in the existing API/token/CA settings.
No PVC/PV, Secret or protection object is deleted. Same-version retries read the
saved result without resending; a new version needs an explicit new invocation,
with at most eight attempts in total. HTTP replies and Pod absence never release
the storage or reopen the Workspace. Reclamation requires the preparation and completion protocol below.
A lost reply can leave UNKNOWN even when Kubernetes accepted the deletion.
Once the original Pod has a deletionTimestamp or is missing, a different
resourceVersion cannot authorize another DELETE. Same-version replay still
returns the retained attempt without contacting Kubernetes; use inspect to
read that receipt. The eight-attempt cap only permits explicit attempts while
the original Pod still passes every verification check.
See the complete retirement design in English and 简体中文. Native ACK retirement qualification remains pending.
Explicit reclamation and same-tenant data handoff
The S2/S3 design adds a healthy normal cleanup path. Native ACK qualification is pending; do not infer it from the existence of these commands or from the earlier S1 run. Keep the current single-control-plane trust boundary and use a dedicated runtime namespace/PVC. Admission/RBAC must leave only this Managed provisioner able to create Pods using the PVC, with no external controller, Pod creator, PV alias, or administrative storage operation during maintenance and handoff. The existing resource protection does not enforce Pod-creation exclusivity.
The maintenance credential additionally needs Pod list and patch in the runtime namespace, Node Lease get in kube-node-lease, and cluster PV/VolumeAttachment list. It never patches a node, attachment, PVC or PV. Keep these privileges away from the worker and public caller.
Using the same Java entry point, request and deployment configuration as above:
- Close all Sessions, then run
begin /absolute/path/retirement.json. The Workspace remainsDRAININGthroughout retirement and after release. - Read the exact original Pod's current resourceVersion. Run
prepare-reclamation /absolute/path/retirement.json <resourceVersion>before DELETE. It verifies the original running container, healthy node/boot, Node Lease, original attachment and exclusive physical-volume use, saves the baseline, then conditionally adds the operation's finalizer. Read a fresh resourceVersion afterward. - Run
delete-pod /absolute/path/retirement.json <fresh-resourceVersion>. It sends the same normal 30-second graceful DELETE and retains its actual response orUNKNOWN. Deletion-only operations without preparation cannot be retroactively qualified after the Pod disappears. - Run
reclaim /absolute/path/retirement.jsonto observe completion. It requires the retained original Pod and explicitly terminated original container, same node/boot with fresh Ready status and Lease, no remaining node volume use or attachment, and no physical alias or other Pod consumer. While any condition is missing, ownership remains held. After a durable cleanup receipt, it conditionally removes only its finalizer and waits for original Pod absence. It then atomically releases operation/binding/active slot/physical ownership. Runinspectafter an unknown result; the command is bounded and may need another invocation while the platform finishes. - Only after
RELEASED, the administrator may replace the sole configured mapping with a new Workspace ID in the same tenant, provision its reviewed saved Workspace metadata and OWNER grant, and restart with the replacement configuration. Retain the original PVC/PV, registration, disk and files. The new request starts at runtime generation 1 and obtains the next physical reservation revision. The old Workspace/request stays fenced; its terminal receipt remains readable with its current OWNER grant even after configuration replacement and later reuse.
The data policy is retain-same-tenant-admin-handoff/1. Physical revisions advance through reserved/released cycles without resetting; retained tenant ownership rejects a foreign-tenant alias. There is no automatic assignment, data wipe, force-release override or reopening of the retired Workspace. A node/API partition, identity change, force deletion or missing Pod before a saved cleanup receipt retains ownership. Infrastructure repair alone cannot authorize product release. A receipt assumes the exclusive use boundary remains in force and does not authorize external remounts.
Failure handling and acceptance
When an execution or release outcome is unknown, preserve its Session and reservation and refuse the next Session. A missing or changed original Pod, container or mount keeps the Workspace unavailable. Lease expiry, operator inspection or a missing Kubernetes object must not clear the physical owner or authorize a replacement Pod. Normal idle application/Broker restart can use the retained database and original worker; this does not qualify interrupted-Turn, expired-writer or lost-Pod recovery.
Qualify the deployment before claiming acceptance:
- Run A's Write/Edit/Read, close A, then read in B. Check exact file bytes and unchanged Pod/container, binding generation, PVC/PV and reservation. Confirm that A's old context refuses activation and dispatch, the binding stays READY and storage stays RESERVED.
- Exercise concurrent different-key creation and same-key replay against real MySQL; only one unfinished Session may be admitted. Test lost release replies and UNKNOWN execution with controlled peers: the hold must remain and no uncertain effect may be repeated.
- At an idle boundary, restart actual application/Broker processes while retaining the database and worker. Confirm original-runtime adoption without a new Pod. Separately verify refusal of missing or changed identities.
- At a separate idle retirement boundary, begin maintenance and confirm new Sessions and Runtime work refuse. Delete only the original Pod with the observed UID/version, exercise lost replies and same-version retry, and verify that the fence, active slot and original storage reservation remain.
Record source commit, environment, original identities and observed results for each run. Keep component/H2, MySQL, independent-process and ACK evidence separate. These deployment checks remain pending. Existing historical fixtures are not acceptance of a new deployment.
Resident resources continue to incur normal Pod/node/storage costs after Session close. No live cloud installation, paid resource creation or manual physical teardown is authorized by this template. Use a separately approved deployment/maintenance plan for those actions. See the design for the failure and acceptance boundaries.