fix: remove caller worker revision fence

This commit is contained in:
2026-08-21 07:11:45 +09:00
parent 614157424f
commit 8bedfcda84
8 changed files with 170 additions and 229 deletions
@@ -48,7 +48,6 @@ pub trait WorkerControlService: Send + Sync {
&self,
runtime_id: &str,
worker_id: &str,
expected_worker_revision: &str,
reason: &str,
) -> Result<WorkspaceResponse, WorkspaceClientError>;
async fn execute_runtime(
@@ -165,11 +164,10 @@ impl WorkerControlService for WorkspaceWorkerControlService {
&self,
runtime_id: &str,
worker_id: &str,
expected_worker_revision: &str,
reason: &str,
) -> Result<WorkspaceResponse, WorkspaceClientError> {
self.client
.execute_worker_remove(runtime_id, worker_id, expected_worker_revision, reason)
.execute_worker_remove(runtime_id, worker_id, reason)
}
async fn execute_runtime(
@@ -530,7 +528,6 @@ struct WorkerStopInput {
#[serde(deny_unknown_fields)]
struct WorkerRemoveInput {
subject: WorkerSubjectInput,
expected_worker_revision: String,
reason: String,
}
@@ -592,7 +589,7 @@ impl WorkerOperation {
"Restore a stopped Backend/Runtime Worker session in the current Workspace."
}
Self::Remove => {
"Remove an eligible stopped, unassigned, non-internal Worker. Supply the current Worker revision and a bounded reason; Backend validation and retention are authoritative."
"Remove an eligible stopped, unassigned, non-internal Worker. Supply a bounded reason; Backend validation and retention are authoritative."
}
}
}
@@ -742,8 +739,6 @@ impl Tool for WorkspaceWorkerTool {
WorkerOperation::Remove => {
let input = parse::<WorkerRemoveInput>(input_json, "WorkerRemove")?;
let (runtime_id, worker_id) = runtime_subject_ids(&input.subject, self.operation)?;
let expected_worker_revision =
non_empty(input.expected_worker_revision, "expected_worker_revision")?;
let reason = non_empty(input.reason, "reason")?;
if reason.len() > 512 {
return Err(ToolError::ExecutionFailed(
@@ -751,12 +746,7 @@ impl Tool for WorkspaceWorkerTool {
));
}
self.control
.remove_runtime_worker(
&runtime_id,
&worker_id,
&expected_worker_revision,
&reason,
)
.remove_runtime_worker(&runtime_id, &worker_id, &reason)
.map_err(control_tool_error)?
}
};
@@ -912,7 +902,7 @@ mod tests {
#[derive(Debug, Default)]
struct RecordingWorkspaceClient {
requests: Mutex<Vec<WorkspaceRequest>>,
removals: Mutex<Vec<(String, String, String, String)>>,
removals: Mutex<Vec<(String, String, String)>>,
}
impl WorkspaceClient for RecordingWorkspaceClient {
@@ -943,13 +933,11 @@ mod tests {
&self,
target_runtime_id: &str,
target_worker_id: &str,
expected_worker_revision: &str,
reason: &str,
) -> Result<WorkspaceResponse, WorkspaceClientError> {
self.removals.lock().unwrap().push((
target_runtime_id.to_string(),
target_worker_id.to_string(),
expected_worker_revision.to_string(),
reason.to_string(),
));
Ok(WorkspaceResponse {
@@ -1160,7 +1148,7 @@ mod tests {
}
#[tokio::test]
async fn worker_remove_forwards_only_target_revision_and_bounded_reason() {
async fn worker_remove_forwards_only_target_and_bounded_reason() {
let client = Arc::new(RecordingWorkspaceClient::default());
let tool = WorkspaceWorkerTool {
operation: WorkerOperation::Remove,
@@ -1173,7 +1161,6 @@ mod tests {
"runtime_id": "runtime-1",
"worker_id": "worker-7",
},
"expected_worker_revision": "2026-08-11T20:00:00Z",
"reason": " retire completed Worker "
})
.to_string(),
@@ -1186,7 +1173,6 @@ mod tests {
[(
"runtime-1".to_string(),
"worker-7".to_string(),
"2026-08-11T20:00:00Z".to_string(),
"retire completed Worker".to_string(),
)]
);
@@ -1194,15 +1180,18 @@ mod tests {
let schema = serde_json::to_value(schemars::schema_for!(WorkerRemoveInput))
.unwrap()
.to_string();
for field in [
"runtime_id",
"worker_id",
"expected_worker_revision",
"reason",
] {
for field in ["runtime_id", "worker_id", "reason"] {
assert!(schema.contains(field));
}
for forbidden in ["proof", "actor", "workspace_id", "policy", "plan", "stage"] {
for forbidden in [
"expected_worker_revision",
"proof",
"actor",
"workspace_id",
"policy",
"plan",
"stage",
] {
assert!(!schema.contains(forbidden), "schema leaked {forbidden}");
}
}
@@ -1223,7 +1212,6 @@ mod tests {
"runtime_id": "runtime-1",
"worker_id": "worker-7",
},
"expected_worker_revision": "revision-1",
"reason": reason,
})
.to_string(),
-1
View File
@@ -272,7 +272,6 @@ pub trait WorkspaceClient: std::fmt::Debug + Send + Sync {
&self,
_target_runtime_id: &str,
_target_worker_id: &str,
_expected_worker_revision: &str,
_reason: &str,
) -> Result<WorkspaceResponse, WorkspaceClientError> {
Err(WorkspaceClientError::Unavailable(