fix: persist plugin snapshots for restore

This commit is contained in:
2026-06-16 00:15:04 +09:00
parent ede7acfdf6
commit 07978d2df5
3 changed files with 143 additions and 33 deletions
+36
View File
@@ -1617,6 +1617,42 @@ mod tests {
assert_eq!(restored_snapshot.resolved[0].version, "0.1.0");
}
#[test]
fn currently_documented_manifest_shape_is_accepted() {
let temp = TempDir::new().unwrap();
let workspace = temp.path().join("workspace");
let plugins = workspace.join(".yoi/plugins");
fs::create_dir_all(&plugins).unwrap();
let manifest = r#"
schema_version = 1
id = "example.summarizer"
name = "Example Summarizer"
version = "0.1.0"
description = "Adds a custom summary command."
surfaces = ["hook"]
[[hooks]]
id = "summary"
file = "hooks/summary.md"
"#;
write_stored_zip(
&plugins.join("documented.yoi-plugin"),
&[
("plugin.toml", manifest.as_bytes().to_vec(), 0),
("hooks/summary.md", b"summarize".to_vec(), 0),
],
);
let report = discover_plugins(&PluginDiscoveryOptions::new(&workspace));
assert_eq!(report.diagnostics, vec![]);
assert_eq!(report.packages.len(), 1);
assert_eq!(
report.packages[0].identity.to_string(),
"project:example.summarizer"
);
}
#[test]
fn malformed_manifest_multibyte_diagnostic_is_bounded_and_redacted() {
let temp = TempDir::new().unwrap();
+84 -5
View File
@@ -924,11 +924,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
}
fn pod_metadata(&self, active: Option<PodActiveSegmentRef>) -> PodMetadata {
let mut metadata = PodMetadata::new(self.manifest.pod.name.clone(), active);
if self.manifest.profile.is_some() {
metadata.resolved_manifest_snapshot = serde_json::to_value(&self.manifest).ok();
}
metadata
pod_metadata_for_manifest(&self.manifest, active)
}
fn write_pod_metadata_pending(&self) -> Result<(), PodError> {
@@ -4321,6 +4317,21 @@ fn request_config_from_worker_manifest(wm: &WorkerManifest) -> RequestConfig {
config
}
fn pod_metadata_for_manifest(
manifest: &PodManifest,
active: Option<PodActiveSegmentRef>,
) -> PodMetadata {
let mut metadata = PodMetadata::new(manifest.pod.name.clone(), active);
if should_persist_resolved_manifest_snapshot(manifest) {
metadata.resolved_manifest_snapshot = serde_json::to_value(manifest).ok();
}
metadata
}
fn should_persist_resolved_manifest_snapshot(manifest: &PodManifest) -> bool {
manifest.profile.is_some() || manifest.plugins.has_resolved_plan()
}
fn restore_manifest_from_pod_metadata_snapshot(
pod_name: &str,
snapshot: Option<serde_json::Value>,
@@ -5294,6 +5305,74 @@ permission = "write"
Permission::Write
);
}
#[test]
fn plugin_resolved_manifest_snapshot_is_persisted_without_profile() {
let mut manifest = PodManifest::from_toml(
r#"
[pod]
name = "plugin-snapshot"
[model]
scheme = "anthropic"
model_id = "claude-sonnet-4-20250514"
[worker]
instruction = "saved"
[[scope.allow]]
target = "/snapshot/workspace"
permission = "read"
"#,
)
.unwrap();
assert!(manifest.profile.is_none());
assert!(
pod_metadata_for_manifest(&manifest, None)
.resolved_manifest_snapshot
.is_none()
);
manifest.plugins.resolved = vec![manifest::plugin::ResolvedPluginRecord {
identity: manifest::plugin::SourceQualifiedPluginId::new(
manifest::plugin::PluginSourceKind::Project,
"example",
),
source: manifest::plugin::PluginSourceKind::Project,
package_path: PathBuf::from("/snapshot/workspace/.yoi/plugins/example.yoi-plugin"),
package_label: "example.yoi-plugin".to_string(),
digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
.to_string(),
version: "0.1.0".to_string(),
manifest: manifest::plugin::PluginPackageManifest {
schema_version: 1,
id: "example".to_string(),
name: "Example".to_string(),
version: "0.1.0".to_string(),
description: None,
surfaces: vec![manifest::plugin::PluginSurface::Hook],
runtime: None,
hooks: vec![],
},
enabled_surfaces: vec![manifest::plugin::PluginSurface::Hook],
grants: manifest::plugin::PluginGrantConfig::default(),
config: None,
}];
let metadata = pod_metadata_for_manifest(&manifest, None);
let snapshot = metadata
.resolved_manifest_snapshot
.expect("plugin-resolved manifest should be snapshotted");
let restored: PodManifest = serde_json::from_value(snapshot).unwrap();
assert!(restored.profile.is_none());
assert_eq!(restored.plugins.resolved.len(), 1);
assert_eq!(
restored.plugins.resolved[0].digest,
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
);
assert_eq!(restored.plugins.resolved[0].version, "0.1.0");
}
}
#[cfg(test)]