Update public restart compatibility metadata

This commit is contained in:
Michel Paulissen 2026-07-04 10:44:15 +02:00
parent f8f91088d5
commit 068c6f00bb
6 changed files with 192 additions and 3 deletions

View file

@ -6097,6 +6097,28 @@ mod tests {
.large_input_policy
.supported_handle_types
.contains(&"SourceSnapshot".to_owned()));
assert!(
inspection
.metadata
.restart_compatibility
.source_edits_can_restart_from_clean_task_boundary
);
assert!(
inspection
.metadata
.restart_compatibility
.requires_clean_checkpoint_boundary
);
assert_eq!(
inspection.metadata.restart_compatibility.compares_task_abi,
inspection.metadata.task_metadata.task_abi
);
assert!(
inspection
.metadata
.restart_compatibility
.incompatible_changes_require_whole_process_restart
);
assert!(!inspection.metadata.embeds_full_container_images);
assert!(inspection
.metadata
@ -6239,6 +6261,91 @@ mod tests {
assert_ne!(first.metadata.identity, second.metadata.identity);
}
#[test]
fn bundle_rebuild_after_source_edit_keeps_restart_compatibility_contract() {
let temp = tempfile::tempdir().unwrap();
fs::create_dir_all(temp.path().join("src")).unwrap();
fs::write(temp.path().join("Cargo.toml"), "[package]\nname='demo'\n").unwrap();
fs::write(
temp.path().join("src/main.rs"),
"fn main() { println!(\"first\"); }\n",
)
.unwrap();
let inspect = |project: &Path| {
bundle_inspection(
BundleInspectArgs {
project: Some(project.to_path_buf()),
source_provider: None,
disabled_source_providers: Vec::new(),
json: true,
},
PathBuf::from("/unused"),
)
.unwrap()
};
let first = inspect(temp.path());
fs::write(
temp.path().join("src/main.rs"),
"fn main() { println!(\"second\"); }\n",
)
.unwrap();
let rebuilt = inspect(temp.path());
assert_ne!(first.metadata.identity, rebuilt.metadata.identity);
assert_ne!(
first
.metadata
.source_metadata
.selected_inputs
.iter()
.find(|input| input.path == "src/main.rs")
.unwrap()
.digest,
rebuilt
.metadata
.source_metadata
.selected_inputs
.iter()
.find(|input| input.path == "src/main.rs")
.unwrap()
.digest
);
assert_eq!(
first.metadata.task_metadata.task_abi,
rebuilt.metadata.task_metadata.task_abi
);
assert_eq!(
first.metadata.restart_compatibility.compares_task_abi,
rebuilt.metadata.restart_compatibility.compares_task_abi
);
assert!(
rebuilt
.metadata
.restart_compatibility
.source_edits_can_restart_from_clean_task_boundary
);
assert!(
rebuilt
.metadata
.restart_compatibility
.requires_clean_checkpoint_boundary
);
assert!(
rebuilt
.metadata
.restart_compatibility
.discards_unflushed_task_local_changes
);
assert!(
rebuilt
.metadata
.restart_compatibility
.incompatible_changes_require_whole_process_restart
);
}
#[test]
fn source_provider_manifest_digest_does_not_include_local_project_path() {
let first = tempfile::tempdir().unwrap();
@ -7712,6 +7819,25 @@ mod tests {
.iter()
.any(|handle| handle == "Artifact")
);
assert_eq!(
report["bundle"]["metadata"]["restart_compatibility"]
["source_edits_can_restart_from_clean_task_boundary"],
true
);
assert_eq!(
report["bundle"]["metadata"]["restart_compatibility"]
["requires_clean_checkpoint_boundary"],
true
);
assert_eq!(
report["bundle"]["metadata"]["restart_compatibility"]["compares_task_abi"],
report["bundle"]["metadata"]["task_metadata"]["task_abi"]
);
assert_eq!(
report["bundle"]["metadata"]["restart_compatibility"]
["incompatible_changes_require_whole_process_restart"],
true
);
assert_eq!(report["status"], "built");
assert_eq!(report["scheduled_work"], false);
}

View file

@ -28,6 +28,7 @@ pub struct BundleMetadata {
pub source_metadata: BundleSourceMetadata,
pub debug_metadata: BundleDebugMetadata,
pub large_input_policy: BundleLargeInputPolicy,
pub restart_compatibility: BundleRestartCompatibility,
pub environments: Vec<EnvironmentResource>,
pub selected_inputs: Vec<SelectedInput>,
pub embeds_full_container_images: bool,
@ -65,6 +66,17 @@ pub struct BundleLargeInputPolicy {
pub supported_handle_types: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleRestartCompatibility {
pub source_edits_can_restart_from_clean_task_boundary: bool,
pub requires_clean_checkpoint_boundary: bool,
pub compares_task_abi: Digest,
pub compares_environment_digests: bool,
pub compares_serialized_args: bool,
pub discards_unflushed_task_local_changes: bool,
pub incompatible_changes_require_whole_process_restart: bool,
}
impl BundleIdentityInputs {
pub fn identity(&self) -> Digest {
let mut parts = vec![
@ -136,6 +148,15 @@ impl BundleIdentityInputs {
"VFS".to_owned(),
],
},
restart_compatibility: BundleRestartCompatibility {
source_edits_can_restart_from_clean_task_boundary: true,
requires_clean_checkpoint_boundary: true,
compares_task_abi: self.task_abi.clone(),
compares_environment_digests: true,
compares_serialized_args: true,
discards_unflushed_task_local_changes: true,
incompatible_changes_require_whole_process_restart: true,
},
environments: self.environments.clone(),
selected_inputs: self.selected_inputs.clone(),
embeds_full_container_images: false,
@ -227,6 +248,25 @@ mod tests {
.large_input_policy
.supported_handle_types
.contains(&"Artifact".to_owned()));
assert!(
metadata
.restart_compatibility
.source_edits_can_restart_from_clean_task_boundary
);
assert!(
metadata
.restart_compatibility
.requires_clean_checkpoint_boundary
);
assert_eq!(
metadata.restart_compatibility.compares_task_abi,
inputs.task_abi
);
assert!(
metadata
.restart_compatibility
.incompatible_changes_require_whole_process_restart
);
assert_eq!(metadata.environments.len(), 1);
assert!(!metadata.embeds_full_container_images);
}

View file

@ -28,7 +28,7 @@ pub use auth::{
};
pub use bundle::{
BundleDebugMetadata, BundleIdentityInputs, BundleLargeInputPolicy, BundleMetadata,
BundleSourceMetadata, BundleTaskMetadata, SelectedInput,
BundleRestartCompatibility, BundleSourceMetadata, BundleTaskMetadata, SelectedInput,
};
pub use capability::{Capability, CapabilityReportError, EnvironmentBackend, NodeCapabilities, Os};
pub use checkpoint::{