feat: caller-selected Backend Jobs and bounded subjektiv consolidation (T-704)

This commit is contained in:
2026-10-06 19:45:37 +09:00
parent 6f9621ca2e
commit d12cdc9106
36 changed files with 4085 additions and 291 deletions
+5
View File
@@ -258,6 +258,8 @@ impl std::fmt::Debug for WorkspaceApiRef {
}
}
pub use worker::BackendJobExecutionBinding;
/// Canonical Runtime Worker creation request.
///
/// Browser/product launch semantics are resolved by a backend before this
@@ -302,6 +304,9 @@ pub struct CreateWorkerRequest {
/// may enable subjektiv policy, but only this host-owned bit activates it.
#[serde(default, skip_serializing_if = "is_false")]
pub subjektiv_attached: bool,
/// Backend-only immutable Job/attempt capability. Never inferred from Profile policy.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub backend_job: Option<BackendJobExecutionBinding>,
}
/// Last persisted Worker lifecycle status.
+2
View File
@@ -3129,6 +3129,7 @@ mod tests {
language: "English".to_string(),
}),
subjektiv_attached: false,
backend_job: None,
}
}
@@ -4685,6 +4686,7 @@ mod ws_tests {
language: "English".to_string(),
}),
subjektiv_attached: false,
backend_job: None,
}
}
+111 -2
View File
@@ -898,7 +898,9 @@ impl Runtime {
message: "persisted Worker restore request is unavailable".to_string(),
}
})?;
if existing_request.create_fingerprint != request.create_fingerprint {
if existing_request.create_fingerprint != request.create_fingerprint
|| existing_request.backend_job != request.backend_job
{
return Err(RuntimeError::InvalidRequest(format!(
"worker {} was already created with a different fingerprint",
request.worker_id
@@ -1068,7 +1070,9 @@ impl Runtime {
message: "persisted Worker restore request is unavailable".to_string(),
}
})?;
if existing_request.create_fingerprint != request.create_fingerprint {
if existing_request.create_fingerprint != request.create_fingerprint
|| existing_request.backend_job != request.backend_job
{
return Err(RuntimeError::InvalidRequest(format!(
"worker {} was already created with a different fingerprint",
request.worker_id
@@ -6242,6 +6246,16 @@ fn validate_logical_workdir_attachments(
}
fn validate_create_worker_request(request: &CreateWorkerRequest) -> Result<(), RuntimeError> {
if let Some(binding) = &request.backend_job {
binding
.validate()
.map_err(|message| RuntimeError::InvalidRequest(message.into()))?;
if request.workspace_api.is_none()
|| (binding.subjektiv_consolidation && !request.subjektiv_attached)
{
return Err(RuntimeError::InvalidRequest("Backend Job capability requires Workspace authority and consolidation requires trusted subject attachment".into()));
}
}
if request.create_fingerprint.trim().is_empty() {
return Err(RuntimeError::InvalidRequest(
"create_fingerprint must not be empty".to_string(),
@@ -7112,9 +7126,95 @@ mod tests {
language: "English".to_string(),
}),
subjektiv_attached: false,
backend_job: None,
}
}
#[cfg(feature = "fs-store")]
#[test]
fn backend_job_binding_survives_restart_and_restore_without_profile_inference() {
let dir = tempfile::tempdir().unwrap();
let options = crate::fs_store::FsRuntimeStoreOptions {
root: dir.path().join("runtime"),
runtime_id: "job-runtime".into(),
display_name: None,
};
let backend = Arc::new(TestExecutionBackend::default());
let runtime =
Runtime::with_fs_store_and_execution_backend(options.clone(), backend.clone()).unwrap();
runtime.store_config_bundle(test_bundle()).unwrap();
let mut request = task_request("bound Job");
request.workspace_api = Some(WorkspaceApiRef {
workspace_id: "local".into(),
base_url: "https://backend.invalid".into(),
});
request.backend_job = Some(crate::catalog::BackendJobExecutionBinding {
job_id: "job-1".into(),
attempt_id: "attempt-1".into(),
input_revision: Some("revision-1".into()),
subjektiv_consolidation: false,
});
let owner = scope("local", "server-job-test");
let created = runtime
.create_worker_scoped(&owner, request.clone())
.unwrap();
assert_eq!(
backend.job_bindings.lock().unwrap().as_slice(),
&[request.backend_job.clone()]
);
let mut changed = request.clone();
changed.backend_job.as_mut().unwrap().attempt_id = "attempt-other".into();
assert!(runtime.create_worker_scoped(&owner, changed).is_err());
drop(runtime);
let restored_backend = Arc::new(TestExecutionBackend::default());
let restored =
Runtime::with_fs_store_and_execution_backend(options, restored_backend.clone())
.unwrap();
assert_eq!(
restored
.lock()
.unwrap()
.worker(&created.worker_ref)
.unwrap()
.request
.as_ref()
.unwrap()
.backend_job,
request.backend_job
);
restored
.restore_worker(
&created.worker_ref,
restored.test_restore_request(&created.worker_ref),
)
.unwrap();
assert_eq!(
restored_backend.job_bindings.lock().unwrap().as_slice(),
&[request.backend_job]
);
}
#[test]
fn backend_job_binding_requires_workspace_and_consolidation_attachment() {
let mut request = task_request("Job binding validation");
request.backend_job = Some(crate::catalog::BackendJobExecutionBinding {
job_id: "job-1".into(),
attempt_id: "attempt-1".into(),
input_revision: None,
subjektiv_consolidation: true,
});
assert!(validate_create_worker_request(&request).is_err());
request.workspace_api = Some(WorkspaceApiRef {
workspace_id: "local".into(),
base_url: "https://backend.invalid".into(),
});
assert!(validate_create_worker_request(&request).is_err());
request.subjektiv_attached = true;
assert!(validate_create_worker_request(&request).is_ok());
request.backend_job.as_mut().unwrap().attempt_id.clear();
assert!(validate_create_worker_request(&request).is_err());
}
#[test]
fn durable_worker_request_omits_repository_credentials() {
let mut request = task_request("worker-secret-redaction");
@@ -7537,6 +7637,7 @@ mod tests {
#[derive(Default)]
struct TestExecutionBackend {
spawn_result: Mutex<Option<WorkerExecutionSpawnResult>>,
job_bindings: Mutex<Vec<Option<crate::catalog::BackendJobExecutionBinding>>>,
dispatch_result: Mutex<Option<WorkerExecutionResult>>,
stop_result: Mutex<Option<WorkerExecutionResult>>,
stop_gate: Mutex<Option<Arc<RestoreGate>>>,
@@ -7653,6 +7754,10 @@ mod tests {
}
fn spawn_worker(&self, request: WorkerExecutionSpawnRequest) -> WorkerExecutionSpawnResult {
self.job_bindings
.lock()
.unwrap()
.push(request.request.backend_job.clone());
if let Some(result) = self.spawn_result.lock().unwrap().take() {
return result;
}
@@ -7726,6 +7831,10 @@ mod tests {
&self,
request: WorkerExecutionRestoreRequest,
) -> WorkerExecutionSpawnResult {
self.job_bindings
.lock()
.unwrap()
.push(request.request.backend_job.clone());
*self.restore_count.lock().unwrap() += 1;
self.restore_operation_ids
.lock()
@@ -1086,10 +1086,26 @@ fn restored_workdir_router(
Ok(router)
}
fn validate_backend_job_profile(
manifest: &manifest::WorkerManifest,
request: &CreateWorkerRequest,
) -> Result<(), String> {
if let Some(binding) = &request.backend_job {
let policy = &manifest.feature.subjektiv.profile;
if policy.consolidation_tools != binding.subjektiv_consolidation
|| (binding.subjektiv_consolidation && policy.extraction.enabled)
{
return Err("Backend Job consolidation_tools must match its trusted consolidation grant and extraction must be disabled".into());
}
}
Ok(())
}
fn bind_workspace_memory_settings(
manifest: &mut manifest::WorkerManifest,
request: &CreateWorkerRequest,
) -> Result<(), String> {
validate_backend_job_profile(manifest, request)?;
let Some(snapshot) = request.memory_settings.as_ref() else {
if request.workspace_api.is_some() || request.subjektiv_attached {
return Err(
@@ -1143,6 +1159,7 @@ fn validate_worker_memory_settings(
manifest: &manifest::WorkerManifest,
request: &CreateWorkerRequest,
) -> Result<(), String> {
validate_backend_job_profile(manifest, request)?;
let Some(expected) = request.memory_settings.as_ref() else {
if request.subjektiv_attached || request.workspace_api.is_some() {
return Err(
@@ -1419,6 +1436,11 @@ impl RuntimeWorkerFactory for ProfileRuntimeWorkerFactory {
}
})?;
let worker = prepared.worker_mut();
if let Some(binding) = request.request.backend_job.as_ref() {
worker
.bind_backend_job(binding.clone())
.map_err(str::to_string)?;
}
validate_worker_memory_settings(worker.manifest(), &request.request)?;
worker
.finalize_subjektiv_session_attribution(
@@ -1684,6 +1706,11 @@ impl RuntimeWorkerFactory for ProfileRuntimeWorkerFactory {
}
Err(err) => return Err(format!("failed to restore Worker from metadata: {err}")),
};
if let Some(binding) = request.request.backend_job.as_ref() {
worker
.bind_backend_job(binding.clone())
.map_err(str::to_string)?;
}
validate_worker_memory_settings(worker.manifest(), &request.request)?;
worker
.finalize_subjektiv_session_attribution(
@@ -7221,6 +7248,58 @@ mod tests {
workspace_api: None,
memory_settings: None,
subjektiv_attached: false,
backend_job: None,
}
}
#[test]
fn backend_job_profile_policy_matches_trusted_grant_without_implicit_changes() {
let mut manifest = WorkerManifest::from_toml(
r#"
[worker]
name = "job-policy-test"
pwd = "./"
[model]
scheme = "anthropic"
model_id = "chosen-model"
auth = { kind = "none" }
[engine]
instruction = "default"
[[scope.allow]]
target = "./"
permission = "read"
"#,
)
.unwrap();
let mut request = create_request("policy");
for granted in [false, true] {
request.backend_job = Some(crate::catalog::BackendJobExecutionBinding {
job_id: "job-1".into(),
attempt_id: "attempt-1".into(),
input_revision: None,
subjektiv_consolidation: granted,
});
for tools in [false, true] {
for extraction in [false, true] {
manifest.feature.subjektiv.profile.consolidation_tools = tools;
manifest.feature.subjektiv.profile.extraction.enabled = extraction;
let expected = tools == granted && (!granted || !extraction);
let before = serde_json::to_value(&manifest).unwrap();
assert_eq!(
validate_backend_job_profile(&manifest, &request).is_ok(),
expected
);
assert_eq!(
bind_workspace_memory_settings(&mut manifest, &request).is_ok(),
expected
);
assert_eq!(
validate_worker_memory_settings(&manifest, &request).is_ok(),
expected
);
assert_eq!(serde_json::to_value(&manifest).unwrap(), before);
}
}
}
}