205 lineas en 73 ficheros de crates, mas la prosa de CLAUDE.md y del skill, que se me habian quedado afuera de los barridos anteriores (no eran ni recetas ni docs/ ni scripts/). EL BARRIDO ANCHO ESTUVO A UN COMMIT DE ROMPER EL CORPUS ENTERO. El primer intento reescribia los .rs completos, no solo los comentarios. Entre las lineas de codigo que tocaba estaban SIETE etiquetas de separacion de dominio, que son ENTRADA DE HASH: b"hammer-tree-v1" <- el prefijo de ArtifactHash::of_tree (hash.rs:60) b"hammer-seed-v1" la funcion que hashea TODOS los artefactos: b"hammer-stage1-rootfs-v2" cambiarla mueve los 4750 hashes del store b"hammer-product-rootfs-v3" b"hammer-product-attested-v2" b"hammer-builder-rootfs-v1" b"hammer-attest-dev-rootkey-0001!!" <- clave raiz de atestacion, [u8;32] Revertido y rehecho solo sobre comentarios, esquivando ademas las cadenas crudas de Rust (r#"..."#) porque el SYSTEM_PROMPT del traductor tiene lineas que empiezan como comentario. Controles: las 7 etiquetas siguen ahi, el diff toca CERO lineas de codigo, 605 tests en verde y el hash de zlib sigue en b3:dc363f26. La leccion es la misma de toda esta etapa: un literal que parece prosa puede ser entrada de hash, y la unica forma de saberlo es mirar donde se usa.
74 lines
2.9 KiB
Rust
74 lines
2.9 KiB
Rust
//! 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/<crate> tiene dos ancestros")
|
||
.to_path_buf()
|
||
}
|
||
|
||
fn rs_de(dir: &Path, out: &mut Vec<PathBuf>) {
|
||
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 ")
|
||
);
|
||
}
|