Capture original review images and bind the measured inventory

Support static WebP and JPEG without conversion-cache evidence. Bind the measured spec centrally, require complete semantic component previews, and present the initial kit before automatic gate-driven repairs.

AI assistance: implemented and validated with OpenAI Codex.
This commit is contained in:
Paul Bakaus
2026-09-13 21:21:06 -07:00
parent 5b305ccede
commit ded752a5c7
47 changed files with 125 additions and 96 deletions
@@ -110,6 +110,21 @@ pub fn freeze(project: &Path, input: &Value) -> Result<(Value, BTreeMap<String,
}
let mut files = BTreeMap::new();
let mut comp_files = BTreeMap::new();
// The component inventory is measured against this spec. Bind it centrally
// rather than requiring every component author to repeat this dependency.
if input["stage"] == "components" {
let spec_path = ".impeccable/build/spec.json";
comp_files.insert(spec_path.into(), pin(project, spec_path, &mut files)?);
let spec: Value = serde_json::from_slice(&files[spec_path]).map_err(|e| e.to_string())?;
for region in spec["regions"].as_array().ok_or("measured spec needs regions")? {
let component = input["components"].as_array().and_then(|items| items.iter().find(|c| c["id"] == region["id"]))
.ok_or_else(|| format!("component review omitted measured region {}", region["id"]))?;
if matches!(region["kind"].as_str(), Some("text" | "control")) && component["preview"]["kind"] != "page" {
return Err(format!("semantic region {} requires a rendered code preview", region["id"]));
}
}
}
view(&mut packet["comp"], project, &mut files, &mut comp_files)?;
let mut ids = BTreeSet::new();
let components = packet["components"]
@@ -471,3 +471,21 @@ fn verify_requires_native_approval_and_current_manifest_and_dependencies() {
fs::write(f.project.join("shared.css"), b"changed after approval").unwrap();
assert!(super::verify::approved(&f.store,&f.project,"review.json").unwrap_err().contains("changed"));
}
#[test]
fn measured_inventory_is_bound_without_repeated_author_dependencies() {
let f = Fixture::new();
fs::create_dir_all(f.project.join(".impeccable/build")).unwrap();
let path = f.project.join(".impeccable/build/spec.json");
fs::write(&path, br#"{"regions":[{"id":"art","kind":"plate"},{"id":"control","kind":"control"}]}"#).unwrap();
let mut input = f.manifest(); input["stage"] = json!("components");
let dir = store::prepare(&f.store, &f.project, &input).unwrap();
let state = store::read(&dir.join("current.json")).unwrap();
assert!(state["sources"][".impeccable/build/spec.json"].is_string());
let mut missing = input.clone(); missing["components"].as_array_mut().unwrap().pop();
assert!(store::prepare(&f.store, &f.project, &missing).unwrap_err().contains("omitted measured region"));
let mut flattened = input; flattened["components"][1]["preview"] = json!({"kind":"image","path":"art.png"});
assert!(store::prepare(&f.store, &f.project, &flattened).unwrap_err().contains("rendered code preview"));
fs::write(path, br#"{"regions":[]}"#).unwrap();
assert!(store::sources_current(&state).is_err());
}