mirror of
https://github.com/bdbrown4/peltier.io.git
synced 2026-11-07 10:58:28 +00:00
phase3: generalize trust layer for C/C++; wire cJSON target
Trust-layer generalization (zero-regression verified on tokei):
- TargetSpec [build] uses an explicit {out} isolation-dir placeholder
instead of injected CARGO_TARGET_DIR, plus optional sanitizer/
sanitizer_binary templates. Nothing assumes cargo anymore.
- verdict rebuild_pristine_baseline, sanitizer_check, and harnessd
run_verdict candidate build all use {out} substitution. A target with
no sanitizer build caps accepts at needs-human-review.
- tokei/comrak target.toml migrated to the {out} form (CARGO_TARGET_DIR=
{out} + nightly ASan sanitizer command).
cJSON C target (v1.7.19, MIT):
- harness.c bench/golden driver (trust-layer; agent can patch workspace
cJSON.c but not the rig), compiled with clang against the patched tree.
- 5.6 MB deterministic corpus (gen-corpus.sh) + MANIFEST/GOLDEN pins.
- gates = hermetic Unity/CTest suite (19 tests pass); sanitizer =
ASan+UBSan; scripts/diff-fuzz-cjson.py for differential fuzzing.
coz causal profiler integration (SPEC §12):
- harness.c optional -DHOTPATH_COZ throughput progress point.
- scripts/install-coz.sh, coz-profile.sh, coz-summary.py; just coz/install-coz.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EcKPWQ3MQD4NdEHwar96u2
This commit is contained in:
@@ -33,10 +33,31 @@ pub struct Source {
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Build {
|
||||
/// Baseline build command, run from repo root.
|
||||
/// Build command, run from repo root. `{out}` is substituted with an
|
||||
/// isolation directory the harness provides (per-baseline, per-
|
||||
/// candidate, per-sanitizer) — the command MUST place all artifacts
|
||||
/// under it. A cargo target sets `CARGO_TARGET_DIR={out}`; a make
|
||||
/// target builds and copies the binary to `{out}`. Language-agnostic:
|
||||
/// nothing here assumes cargo.
|
||||
pub baseline: String,
|
||||
/// Built binary path, relative to repo root.
|
||||
/// Built binary path with `{out}` substituted — where `baseline`
|
||||
/// leaves the binary given an isolation dir.
|
||||
pub binary: String,
|
||||
/// ASan+LSan (or ASan+UBSan for C/C++) build command; `{out}`
|
||||
/// substituted. Run on every would-be accept (SPEC §8). If absent,
|
||||
/// an accept cannot be sanitizer-verified and is capped at
|
||||
/// needs-human-review.
|
||||
#[serde(default)]
|
||||
pub sanitizer: Option<String>,
|
||||
/// Sanitizer binary path with `{out}` substituted.
|
||||
#[serde(default)]
|
||||
pub sanitizer_binary: Option<String>,
|
||||
}
|
||||
|
||||
/// Substitute the `{out}` isolation-dir placeholder in a build command or
|
||||
/// binary-path template.
|
||||
pub fn subst_out(template: &str, out_dir: &str) -> String {
|
||||
template.replace("{out}", out_dir)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
||||
+11
-10
@@ -339,29 +339,30 @@ fn handle(root: &Path, line: &str) -> Result<Value> {
|
||||
)?)?;
|
||||
let t = pending["target"].as_str().unwrap();
|
||||
let spec = diff_test::target::TargetSpec::load(root, t)?;
|
||||
let bin = Path::new(&spec.build.binary)
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap();
|
||||
let diff_path = root.join(format!("results/pending/{patch_id}.json.diff"));
|
||||
std::fs::write(&diff_path, pending["diff"].as_str().unwrap())?;
|
||||
// The build + verdict pipeline runs for minutes — far past the MCP
|
||||
// transport's per-call cap. Launch it detached, writing progress to
|
||||
// a log; the agent observes the result via the read_verdict op,
|
||||
// which reads the append-only ledger row once written.
|
||||
let cand_dir = format!("targets/{t}/candidate-{patch_id}");
|
||||
let cand_bin = format!("{cand_dir}/release/{bin}");
|
||||
// {out} isolates the candidate build (cargo: CARGO_TARGET_DIR;
|
||||
// make: build+copy into it) — language-agnostic, mirrors the
|
||||
// baseline rebuild in verdict.
|
||||
let cand_dir = root
|
||||
.join(format!("targets/{t}/candidate-{patch_id}"))
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let cand_build = diff_test::target::subst_out(&spec.build.baseline, &cand_dir);
|
||||
let cand_bin = diff_test::target::subst_out(&spec.build.binary, &cand_dir);
|
||||
let log = format!("results/pending/{run_id}.log");
|
||||
// On any failure (candidate build, gates crash) the trap line
|
||||
// marks the log so read_verdict can report "failed" instead of
|
||||
// leaving the agent polling a verdict that will never exist.
|
||||
let script = format!(
|
||||
"{{ set -e\nCARGO_TARGET_DIR={cand} {build}\ncargo run -q -p verdict -- {tgt} \
|
||||
"{{ set -e\n{build}\ncargo run -q -p verdict -- {tgt} \
|
||||
--rebuild-baseline --candidate-bin {cbin} --run-id {rid} --playbook-class {cls} \
|
||||
--hypothesis {hyp} --hotspot {hs} --patch-file {pf}\n}} || echo HOTPATH-PIPELINE-FAILED\n",
|
||||
cand = shq(&cand_dir),
|
||||
build = spec.build.baseline,
|
||||
build = cand_build,
|
||||
tgt = shq(t),
|
||||
cbin = shq(&cand_bin),
|
||||
rid = shq(run_id),
|
||||
|
||||
+48
-42
@@ -79,14 +79,14 @@ fn rebuild_pristine_baseline(
|
||||
"git stash failed"
|
||||
);
|
||||
}
|
||||
let out_dir = root
|
||||
.join(format!("targets/{target}/baseline"))
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let build = std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&spec.build.baseline)
|
||||
.arg(diff_test::target::subst_out(&spec.build.baseline, &out_dir))
|
||||
.current_dir(root)
|
||||
.env(
|
||||
"CARGO_TARGET_DIR",
|
||||
root.join(format!("targets/{target}/baseline")),
|
||||
)
|
||||
.status();
|
||||
if dirty {
|
||||
anyhow::ensure!(
|
||||
@@ -98,13 +98,9 @@ fn rebuild_pristine_baseline(
|
||||
);
|
||||
}
|
||||
anyhow::ensure!(build?.success(), "baseline build failed");
|
||||
let bin_name = std::path::Path::new(&spec.build.binary)
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("bad build.binary path"))?;
|
||||
let baseline = format!("targets/{target}/baseline/release/{bin_name}");
|
||||
let baseline = diff_test::target::subst_out(&spec.build.binary, &out_dir);
|
||||
anyhow::ensure!(
|
||||
root.join(&baseline).exists(),
|
||||
std::path::Path::new(&baseline).exists(),
|
||||
"baseline binary missing: {baseline}"
|
||||
);
|
||||
println!("baseline rebuilt from pristine checkout: {baseline}");
|
||||
@@ -117,40 +113,43 @@ fn rebuild_pristine_baseline(
|
||||
/// were per-attempt manual) and the human audit had to overturn it — the
|
||||
/// accept path now runs the check itself and caps flagged wins at
|
||||
/// needs-human-review. Rejections skip it: they ship nothing.
|
||||
fn sanitizer_check(root: &std::path::Path, target: &str, spec: &TargetSpec) -> Result<bool> {
|
||||
let ws = root.join(format!("targets/{target}/workspace"));
|
||||
let asan_dir = root.join(format!("targets/{target}/asan"));
|
||||
println!("sanitizers: ASan+LSan build (nightly) of the patched tree…");
|
||||
let status = std::process::Command::new("cargo")
|
||||
.args([
|
||||
"+nightly",
|
||||
"build",
|
||||
"-q",
|
||||
"--target",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
])
|
||||
.env("RUSTFLAGS", "-Zsanitizer=address")
|
||||
.env("CARGO_TARGET_DIR", &asan_dir)
|
||||
.current_dir(&ws)
|
||||
/// Returns Some(clean) if the sanitizer gate ran, or None if the target
|
||||
/// declares no sanitizer build (an accept then cannot be verified and is
|
||||
/// capped at needs-human-review by the caller).
|
||||
fn sanitizer_check(
|
||||
root: &std::path::Path,
|
||||
target: &str,
|
||||
spec: &TargetSpec,
|
||||
) -> Result<Option<bool>> {
|
||||
let (Some(build_tpl), Some(bin_tpl)) = (&spec.build.sanitizer, &spec.build.sanitizer_binary)
|
||||
else {
|
||||
println!("sanitizers: target declares no sanitizer build — cannot verify an accept");
|
||||
return Ok(None);
|
||||
};
|
||||
let out_dir = root
|
||||
.join(format!("targets/{target}/asan"))
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
println!("sanitizers: ASan build of the patched tree…");
|
||||
let status = std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(diff_test::target::subst_out(build_tpl, &out_dir))
|
||||
.current_dir(root)
|
||||
.status()?;
|
||||
anyhow::ensure!(status.success(), "sanitizer build failed");
|
||||
let bin_name = std::path::Path::new(&spec.build.binary)
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("bad build.binary path"))?;
|
||||
let asan_bin = asan_dir.join(format!("x86_64-unknown-linux-gnu/debug/{bin_name}"));
|
||||
let cmd = spec
|
||||
.bench
|
||||
.command
|
||||
.replace("{binary}", asan_bin.to_str().unwrap());
|
||||
let asan_bin = diff_test::target::subst_out(bin_tpl, &out_dir);
|
||||
let cmd = spec.bench.command.replace("{binary}", &asan_bin);
|
||||
let out = std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&cmd)
|
||||
// detect_leaks is a no-op for the C/C++ UBSan combo but harmless.
|
||||
.env("ASAN_OPTIONS", "detect_leaks=1")
|
||||
.env("UBSAN_OPTIONS", "halt_on_error=1:print_stacktrace=1")
|
||||
.current_dir(root)
|
||||
.output()?;
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
let clean = out.status.success() && !stderr.contains("ERROR:");
|
||||
let clean =
|
||||
out.status.success() && !stderr.contains("ERROR:") && !stderr.contains("runtime error:");
|
||||
if clean {
|
||||
println!("sanitizers: clean");
|
||||
} else {
|
||||
@@ -161,7 +160,7 @@ fn sanitizer_check(root: &std::path::Path, target: &str, spec: &TargetSpec) -> R
|
||||
println!(" {l}");
|
||||
}
|
||||
}
|
||||
Ok(clean)
|
||||
Ok(Some(clean))
|
||||
}
|
||||
|
||||
fn now_utc() -> String {
|
||||
@@ -265,11 +264,18 @@ fn main() -> Result<()> {
|
||||
v = Verdict::NeedsHumanReview;
|
||||
}
|
||||
if v == Verdict::Accepted {
|
||||
let clean = sanitizer_check(&root, &cli.target, &spec)?;
|
||||
gate_results.sanitizers_clean = clean;
|
||||
if !clean {
|
||||
println!("verdict: sanitizer flag caps the accept at needs-human-review (SPEC §8)");
|
||||
v = Verdict::NeedsHumanReview;
|
||||
match sanitizer_check(&root, &cli.target, &spec)? {
|
||||
Some(true) => gate_results.sanitizers_clean = true,
|
||||
Some(false) => {
|
||||
println!(
|
||||
"verdict: sanitizer flag caps the accept at needs-human-review (SPEC §8)"
|
||||
);
|
||||
v = Verdict::NeedsHumanReview;
|
||||
}
|
||||
None => {
|
||||
println!("verdict: no sanitizer build declared; capping accept at needs-human-review (SPEC §8)");
|
||||
v = Verdict::NeedsHumanReview;
|
||||
}
|
||||
}
|
||||
}
|
||||
let fp = EnvFingerprint::collect(pin, "system-default");
|
||||
|
||||
Reference in New Issue
Block a user