Skip to content

Commit f5c9d8e

Browse files
fix(catalog): preserve raw apply no-ops
agent-identity: dev3.direct.omp.2cshu64q agent-persona: generalist agent-supervisor: unavailable agent-tool: OMP agent-tool-version: 18.0.9 agent-runtime: OMP 18.0.9 tooling-profile: dotfiles@b607597
1 parent eb7ed64 commit f5c9d8e

2 files changed

Lines changed: 104 additions & 1 deletion

File tree

src/catalog_transaction.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,33 @@ impl DeclarationProjection {
280280
}
281281
}
282282

283+
fn raw_declarations_match(
284+
catalog: &Path,
285+
current: &DeclarationProjection,
286+
desired: &DeclarationProjection,
287+
) -> bool {
288+
if !current
289+
.files
290+
.iter()
291+
.all(|(path, file)| desired.files.get(path) == Some(file))
292+
{
293+
return false;
294+
}
295+
296+
let mut live_profile_modules = BTreeMap::new();
297+
for path in &desired.profile_modules {
298+
if current.files.contains_key(path) {
299+
continue;
300+
}
301+
if add_profile_module(catalog, Path::new(path), &mut live_profile_modules).is_err() {
302+
return false;
303+
}
304+
}
305+
desired.files.iter().all(|(path, file)| {
306+
current.files.get(path) == Some(file) || live_profile_modules.get(path) == Some(file)
307+
})
308+
}
309+
283310
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
284311
enum ProjectionSource {
285312
Current,
@@ -1628,7 +1655,12 @@ pub fn apply(request: ApplyRequest) -> Result<ApplyResult> {
16281655
&desired.workspace_dirs,
16291656
)?
16301657
};
1631-
if current.root_sha256 == desired.root_sha256 && same_pty_root {
1658+
let same_declarations = if raw_preimage {
1659+
raw_declarations_match(&catalog, &current, &desired)
1660+
} else {
1661+
current.root_sha256 == desired.root_sha256
1662+
};
1663+
if same_declarations && same_pty_root {
16321664
return Ok(ApplyResult {
16331665
schema: if raw_preimage {
16341666
RAW_APPLY_SCHEMA
@@ -2266,6 +2298,11 @@ fn add_profile_module(
22662298
"profile module is not a no-follow regular file: {}",
22672299
relative.display()
22682300
);
2301+
anyhow::ensure!(
2302+
metadata.nlink() == 1,
2303+
"profile module is hard-linked: {}",
2304+
relative.display()
2305+
);
22692306
let limit = agent_spec::profile::DEFAULT_MODULE_LIMIT_BYTES;
22702307
anyhow::ensure!(
22712308
metadata.len() <= limit as u64,

tests/catalog_apply.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,20 @@ fn raw_preimage_accepts_valid_bytes_and_wrong_cas_preserves_declarations() {
19941994
let temp = tempfile::tempdir().unwrap();
19951995
let valid = temp.path().join("valid");
19961996
write_agent(&valid, "worker", false);
1997+
fs::write(
1998+
agent_dir(&valid, "worker").join("agent.kdl"),
1999+
"agent \"worker\" {\n host \"host\"\n workspace \".workspace\"\n argv \"true\"\n}\n",
2000+
)
2001+
.unwrap();
2002+
let workspace = agent_dir(&valid, "worker").join(".workspace");
2003+
fs::create_dir(&workspace).unwrap();
2004+
fs::create_dir_all(valid.join("resolvers")).unwrap();
2005+
fs::copy(DEMO_WASM_SRC, valid.join("resolvers/observe.wasm")).unwrap();
2006+
fs::write(
2007+
valid.join("catalog.kdl"),
2008+
profile_catalog_config(&[("dev.example.observe", "resolvers/observe.wasm")]),
2009+
)
2010+
.unwrap();
19972011
let valid_prepared = temp.path().join("valid-prepared");
19982012
snapshot(&valid, &valid_prepared);
19992013
let valid_raw_snapshot = raw_snapshot(&valid, &temp.path().join("valid-raw"));
@@ -2003,6 +2017,7 @@ fn raw_preimage_accepts_valid_bytes_and_wrong_cas_preserves_declarations() {
20032017
String::from_utf8_lossy(&valid_raw_snapshot.stderr)
20042018
);
20052019
let valid_raw_snapshot: Value = serde_json::from_slice(&valid_raw_snapshot.stdout).unwrap();
2020+
let generation_before = fs::read(valid.join(".st2/catalog-generation")).ok();
20062021
let valid_raw_apply = raw_apply(
20072022
&valid,
20082023
&valid_prepared,
@@ -2013,9 +2028,60 @@ fn raw_preimage_accepts_valid_bytes_and_wrong_cas_preserves_declarations() {
20132028
"{}",
20142029
String::from_utf8_lossy(&valid_raw_apply.stderr)
20152030
);
2031+
let valid_raw_apply: Value = serde_json::from_slice(&valid_raw_apply.stdout).unwrap();
2032+
assert_eq!(valid_raw_apply["status"], "unchanged");
2033+
assert!(!valid.join(".st2/catalog-apply-incomplete").exists());
2034+
assert_eq!(
2035+
fs::read(valid.join(".st2/catalog-generation")).ok(),
2036+
generation_before
2037+
);
2038+
assert!(workspace.is_dir());
2039+
2040+
fs::write(valid.join("resolvers/observe.wasm"), b"stale module").unwrap();
2041+
let stale_module_apply = raw_apply(
2042+
&valid,
2043+
&valid_prepared,
2044+
valid_raw_snapshot["rootSha256"].as_str().unwrap(),
2045+
);
2046+
assert!(
2047+
stale_module_apply.status.success(),
2048+
"{}",
2049+
String::from_utf8_lossy(&stale_module_apply.stderr)
2050+
);
2051+
let stale_module_apply: Value =
2052+
serde_json::from_slice(&stale_module_apply.stdout).unwrap();
2053+
assert_eq!(stale_module_apply["status"], "applied");
2054+
assert_eq!(
2055+
fs::read(valid.join("resolvers/observe.wasm")).unwrap(),
2056+
fs::read(DEMO_WASM_SRC).unwrap()
2057+
);
2058+
assert!(workspace.is_dir());
2059+
2060+
let live_module = valid.join("resolvers/observe.wasm");
2061+
let module_alias = temp.path().join("observe-alias.wasm");
2062+
fs::hard_link(&live_module, &module_alias).unwrap();
2063+
let hard_linked_module_apply = raw_apply(
2064+
&valid,
2065+
&valid_prepared,
2066+
valid_raw_snapshot["rootSha256"].as_str().unwrap(),
2067+
);
2068+
assert!(
2069+
hard_linked_module_apply.status.success(),
2070+
"{}",
2071+
String::from_utf8_lossy(&hard_linked_module_apply.stderr)
2072+
);
2073+
let hard_linked_module_apply: Value =
2074+
serde_json::from_slice(&hard_linked_module_apply.stdout).unwrap();
2075+
assert_eq!(hard_linked_module_apply["status"], "applied");
2076+
fs::write(&module_alias, b"mutated alias").unwrap();
2077+
assert_eq!(
2078+
fs::read(&live_module).unwrap(),
2079+
fs::read(DEMO_WASM_SRC).unwrap()
2080+
);
20162081

20172082
let invalid = temp.path().join("invalid");
20182083
write_invalid_agent(&invalid, "worker");
2084+
fs::create_dir(agent_dir(&invalid, "worker").join(".workspace")).unwrap();
20192085
ensure_external_pty_config(&invalid);
20202086
let declaration = agent_dir(&invalid, "worker").join("agent.kdl");
20212087
let context = agent_dir(&invalid, "worker").join("resources/context/now.md");

0 commit comments

Comments
 (0)