//! Guardián de SDD 25 §7-H4: **ningún camino de spawn de takana usa `pre_exec`**. //! //! POR QUÉ. `std::process::Command` de Rust usa `posix_spawn` cuando puede, y `posix_spawn` es //! PLANO respecto al tamaño del padre: a 256 MB tocados, `fork+exec` costó 3 949 µs contra 231 µs //! (SDD 25 T1, **17×**). En cuanto se le pone un `pre_exec`, Rust apaga ese camino y cae a //! `fork+exec`, que copia el espacio de direcciones del padre. takana lanza builds desde un //! proceso que puede tener cientos de MB mapeados. //! //! Hoy `takana-build::sandbox` lanza `bwrap` sin `pre_exec` — y hasta ahora eso era SUERTE, no una //! regla: nadie lo comprobaba. Esto lo vuelve una regla. //! //! CUÁNDO SE PUEDE APAGAR. Si algún día hace falta de verdad (un `setsid`, un `unshare` que bwrap //! no cubra), la respuesta no es borrar el test: es escribir acá por qué ese caso paga los 17× y //! ponerlo en la lista de excepciones, para que el precio quede a la vista de quien lo lea. use std::path::{Path, PathBuf}; fn raiz_del_workspace() -> PathBuf { // CARGO_MANIFEST_DIR = crates/hammer-build PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() .and_then(Path::parent) .expect("crates/ tiene dos ancestros") .to_path_buf() } fn rs_de(dir: &Path, out: &mut Vec) { let Ok(rd) = std::fs::read_dir(dir) else { return }; for e in rd.flatten() { let p = e.path(); if p.is_dir() { rs_de(&p, out); } else if p.extension().is_some_and(|x| x == "rs") { out.push(p); } } } #[test] fn ningun_spawn_de_hammer_usa_pre_exec() { let raiz = raiz_del_workspace(); let crates = raiz.join("crates"); assert!(crates.is_dir(), "no encontré {} — ¿se movió el workspace?", crates.display()); let mut fuentes = Vec::new(); rs_de(&crates, &mut fuentes); // Un barrido que no encuentra fuentes aprueba por vacío, que es el fallo de CLAUDE.md §3. assert!(fuentes.len() > 20, "sólo {} fuentes: el barrido no miró nada", fuentes.len()); let mut culpables = Vec::new(); for f in &fuentes { if f == Path::new(file!()) || f.ends_with("tests/sin_pre_exec.rs") { continue; } let Ok(txt) = std::fs::read_to_string(f) else { continue }; for (i, l) in txt.lines().enumerate() { let limpia = l.trim_start(); if limpia.starts_with("//") || limpia.starts_with("*") { continue; // documentarlo no es usarlo } if l.contains("pre_exec") { culpables.push(format!("{}:{}: {}", f.display(), i + 1, l.trim())); } } } assert!( culpables.is_empty(), "SDD 25 §7-H4: `pre_exec` apaga el camino `posix_spawn` de Rust y devuelve el spawn a \ `fork+exec` (17× más caro con 256 MB en el padre, SDD 25 T1). Aparece en:\n {}", culpables.join("\n ") ); }