Skip to content

Commit c02394a

Browse files
authored
feat(worker): surface workspace cleanup failures; close out #12 container artifacts (#62)
With the container backend (#5) merged, the repo checkout is now removed on every path — host and container (--rm) — so the last #12 gap is that a cleanup FAILURE was silently swallowed (.ok()), which #12 flags as leaving private code on disk. Now a failure that leaves the checkout behind is logged loudly and audited (workspace_cleanup_failed). Docs move repo_checkout and container-log handling from deferred to covered. Closes #12. Signed-off-by: Val Alexander <bunsthedev@gmail.com>
1 parent 2baa72b commit c02394a

2 files changed

Lines changed: 36 additions & 6 deletions

File tree

crates/worker/src/lib.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,28 @@ async fn execute_task_with_minter(
344344
)
345345
.await;
346346

347-
// Workspace cleanup ALWAYS runs — success or failure.
348-
tokio::fs::remove_dir_all(&workspace).await.ok();
347+
// Workspace cleanup ALWAYS runs — success or failure. The workspace holds
348+
// the private repository checkout, so a cleanup FAILURE that leaves it on
349+
// disk must be surfaced and audited, never silently swallowed (issue #12).
350+
if let Err(e) = tokio::fs::remove_dir_all(&workspace).await {
351+
// remove_dir_all also errors when the dir never existed; only alarm
352+
// when the checkout is genuinely still on disk.
353+
if tokio::fs::try_exists(&workspace).await.unwrap_or(true) {
354+
error!(
355+
task_id = %task.id,
356+
workspace = %workspace.display(),
357+
"workspace cleanup FAILED — a private checkout may remain on disk: {e:#}"
358+
);
359+
let _ = store
360+
.record_api_read(
361+
&format!("worker:{}", task.id),
362+
&format!("{}/{}", task.repo_owner, task.repo_name),
363+
"workspace_cleanup_failed",
364+
"error",
365+
)
366+
.await;
367+
}
368+
}
349369

350370
// The Check Run ALWAYS reaches a terminal conclusion; both arms complete it.
351371
match outcome {

docs/data-retention.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ survives.
2525
| `audit_events` | Yes | `api_audit`, table states below | See [Audit events](#audit-events). |
2626
| `memory_activity` | Opt-in, retention-limited | `memory_activity` | Per-installation; see [issue #6](memory-contract.md). |
2727
| `logs` / `transcripts` | Not persisted by the adapter || Streamed/redacted only; durable transcripts are out of scope until opt-in retention is designed. |
28-
| `repo_checkout` | **Never** after task cleanup | ephemeral workspace | Workspace is deleted after every task. Container-isolated cleanup is issue #5. |
29-
| `tokens` / `secrets` | **Never** || The brief is tokenless (#4); results, comments, Check Runs, and stored fields are redacted. |
28+
| `repo_checkout` | **Never** after task cleanup | ephemeral workspace | The per-task workspace is deleted after every task (success or failure); the container backend also removes the container (`--rm`). A cleanup failure that would leave a checkout on disk is surfaced and audited (`workspace_cleanup_failed`), never swallowed. |
29+
| `container_logs` | **Not persisted** || The container backend (#5) captures no stdout/stderr into any store; only the redacted failure detail reaches `task_attempts.detail`. |
30+
| `tokens` / `secrets` | **Never** || The brief is tokenless (#4); the git token travels to the runtime by environment name only (never in argv or container-inspect output); results, comments, Check Runs, and stored fields are redacted. |
3031

3132
## Redaction
3233

@@ -78,10 +79,19 @@ never logged or stored.
7879
| Memory | Operator-managed, off by default | Opt-in, per-installation, revocable |
7980
| Worker isolation | May run on host | Container-isolated per task (#5) |
8081

82+
## Worker execution artifacts
83+
84+
The worker runs each task in a per-task workspace, then deletes it — on every
85+
path, success or failure. In the container backend (#5) the task runs in a
86+
fresh container removed by `--rm` (and killed by name on timeout), with a
87+
read-only root, dropped capabilities, resource limits, and only the workspace
88+
mounted; the git token is forwarded by environment *name*, so it never enters
89+
argv or `docker inspect`. If workspace removal ever fails while the checkout is
90+
still on disk, the worker logs it loudly and records a `workspace_cleanup_failed`
91+
audit entry rather than silently leaving private code behind.
92+
8193
## Not yet covered
8294

83-
- Container-scoped artifact handling — `repo_checkout` cleanup guarantees and
84-
container log retention — lands with hosted worker isolation (#5).
8595
- A unified append-only audit-event log (the tables above already provide the
8696
equivalent records; a single stream is a possible future consolidation).
8797
- Durable, opt-in agent transcripts with their own redaction/retention policy.

0 commit comments

Comments
 (0)