Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0e6b16abc | ||
|
|
3a383aede6 |
@@ -52,6 +52,13 @@ const EXCLUDED_PATHS: &[&str] = &[
|
||||
".pytest_cache",
|
||||
".mypy_cache",
|
||||
"vendor",
|
||||
// Agent workaround debris. `.gitconfig_temp` appeared on mission 019fc3ba
|
||||
// when an agent hit git's ownership check and wrote its own safe.directory
|
||||
// config into the repository root. The cause is fixed (mission containers
|
||||
// now carry GIT_CONFIG_* env), but excluding the artefact keeps a stray
|
||||
// workaround out of a user's repository if an agent invents another one.
|
||||
".gitconfig_temp",
|
||||
".gitconfig.tmp",
|
||||
];
|
||||
|
||||
/// Cap on the captured patch. Past this the diff is truncated with a marker
|
||||
@@ -333,6 +340,17 @@ pub async fn record_uncapturable(
|
||||
mission_id: Uuid,
|
||||
phase_id: Uuid,
|
||||
) -> Result<(), String> {
|
||||
// Write a real file behind the artifact. `_outputs` survives teardown, so
|
||||
// it is still writable even though the checkout is gone — and an artifact
|
||||
// row pointing at a path with nothing behind it turns every reader into a
|
||||
// 404 with no explanation.
|
||||
let dir = outputs_root(mission_id).join(phase_id.to_string());
|
||||
if std::fs::create_dir_all(&dir).is_ok() {
|
||||
let _ = std::fs::write(
|
||||
dir.join("diff.patch"),
|
||||
"The mission checkout was removed before this phase's changes could be\n captured. Nothing was lost that had already been captured; this phase\n simply finished after its working tree had been reaped.\n",
|
||||
);
|
||||
}
|
||||
let rel = format!("_outputs/{mission_id}/{phase_id}/diff.patch");
|
||||
cm_db::repo::missions::register_artifact(
|
||||
pool,
|
||||
@@ -386,11 +404,20 @@ mod tests {
|
||||
assert_eq!(parse_diffstat("\n"), (0, 0, 0));
|
||||
}
|
||||
|
||||
/// Build output must never reach a patch. A phase that ran `cargo build`
|
||||
/// leaves a `target/` bigger than the repository.
|
||||
/// Build output and agent workaround debris must never reach a patch. A
|
||||
/// phase that ran `cargo build` leaves a `target/` bigger than the
|
||||
/// repository, and an agent that fought git's ownership check left a
|
||||
/// `.gitconfig_temp` beside the real work.
|
||||
#[test]
|
||||
fn build_output_is_excluded() {
|
||||
for p in ["target", "node_modules", ".venv", "dist", "__pycache__"] {
|
||||
for p in [
|
||||
"target",
|
||||
"node_modules",
|
||||
".venv",
|
||||
"dist",
|
||||
"__pycache__",
|
||||
".gitconfig_temp",
|
||||
] {
|
||||
assert!(
|
||||
EXCLUDED_PATHS.contains(&p),
|
||||
"{p} must be excluded from capture"
|
||||
|
||||
@@ -285,6 +285,24 @@ impl MissionRuntimeProvisioner {
|
||||
let mut env = vec![
|
||||
format!("ZEROCLAW_GATEWAY_PORT={GATEWAY_PORT}"),
|
||||
format!("CM_MISSION_ID={mission_id}"),
|
||||
// Let the agents' git read the checkout.
|
||||
//
|
||||
// The server clones as uid 65532; this container runs as root, so
|
||||
// every `git` an agent runs hits "detected dubious ownership" and
|
||||
// refuses the repository. Agents do not report that as a failure —
|
||||
// they improvise. On mission 019fc3ba one wrote a `.gitconfig_temp`
|
||||
// containing `[safe] directory = /mission/repo` into the repository
|
||||
// root, which then showed up in the captured diff and would have
|
||||
// been committed and pushed to the user's repo alongside the real
|
||||
// work.
|
||||
//
|
||||
// `GIT_CONFIG_*` is git's environment form of `-c` and is
|
||||
// inherited by subprocesses, so it covers the agent's own git, any
|
||||
// tool that shells out to git, and the `git_operations` tool alike.
|
||||
// Scoped to the checkout; never `--global`.
|
||||
"GIT_CONFIG_COUNT=1".to_string(),
|
||||
"GIT_CONFIG_KEY_0=safe.directory".to_string(),
|
||||
"GIT_CONFIG_VALUE_0=/mission/repo".to_string(),
|
||||
];
|
||||
// Provider credentials forwarded into the container.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user