Files
kandev/docs/plans/ssh-remote-task-dir-reclamation/plan.md
T
Henry 8f21873d45 feat(ssh): reclaim remote task directories on terminal archive or delete (#3000)
Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
2026-08-25 06:56:59 +00:00

4.1 KiB

spec, system_design, created, status
spec system_design created status
docs/specs/executors/requirements/remote-task-directory-reclamation.md docs/specs/executors/system-design/remote-task-directory-reclamation.md 2026-08-24 done

Implementation Plan: SSH Remote Task-Directory Reclamation

Overview

Build the v2 housekeeper bottom-up, so that the safety machinery exists and is proven before anything can call it. Wave 1 delivers the two independent primitives — the read-only safety probe plus guarded removal in the lifecycle package, and the per-profile opt-in plumbing in orchestrator/executor — neither of which can delete anything on its own. Wave 2 wires them into the durable cleanup job, which is the only caller and the point at which removal becomes reachable. Wave 3 adds the settings UI, the ADR, and the documentation corrections.

The order is deliberate: task 01 is written and tested with no caller, so a mistake in the probe cannot reach a real host through a half-built job phase.

Safety constraint on all tasks

No test may delete anything outside a t.TempDir(). The removal path is exercised through the sshRemoteRunner interface with recorded command outputs, or against a temporary directory. No test creates, targets, or removes a path under a real Kandev workspace root, and no test runs rm -rf against a path derived from the developer's environment. A test that deletes its own workspace destroys the run that produced it.


Waves

Wave Tasks Parallel-safe
1 01, 02 Yes — disjoint packages
2 03 No — depends on 01 and 02
3 04, 05 Yes — disjoint (web / docs)

Backend

Task 01 — Probe and guarded removal (lifecycle)

New executor_ssh_reclaim.go with SSHTaskDirReclaimer, the four-probe safety table, the path-shape guard, and rm -rf behind a sshRemoteRunner seam. Pure verdict logic, no callers.

Task 02 — Per-profile opt-in (orchestrator/executor, lifecycle)

MetadataKeySSHReclaimTaskDir, entry in profileConfigAuthoritativeKeys and persistentMetadataKeys. Proves a task-supplied value cannot enable the feature.

Task 03 — Cleanup-job phase (task/service)

Snapshot capture, ownership resolution, invocation, and the skip-versus-error outcome folding. This is where the regression coverage for removal-on-terminal and preservation-on-ordinary-stop lands.


Frontend

Task 04 — Settings toggle

Per-profile switch in the SSH executor profile settings with blast-radius copy, routed through t() and translated into all five locales.


Documentation and decisions

Task 05 — ADR and doc corrections

ADR for the opt-in default and the cleanup-job placement; docs/public/feature-status.md SSH row updated to describe shipped behavior.

apps/backend/AGENTS.md and the Out of scope (v1) item in docs/specs/executors/system-design/ssh-executor.md is corrected as part of this design package rather than at ship time — the first is a present-tense factual error about today's code, and the second is the specification boundary this package moves.


Task checklist

  • 01 — task-01-reclaim-probe-and-removal.md
  • 02 — task-02-profile-opt-in.md
  • 03 — task-03-cleanup-job-phase.md
  • 04 — task-04-settings-toggle.md
  • 05 — task-05-adr-and-docs.md

Risks

  • The probe is the whole feature. A false "safe" verdict is silent, irreversible data loss on a machine Kandev does not own. Task 01's verdict table must be exhaustive over probe-failure modes, and every unknown must resolve to skip.
  • Ownership signal coverage. The environment-ownership check is the existing precedent, but a parent task whose executor_running rows were pruned while its directory survives would not be seen by the row-level check. The environment check is the primary defence and the row check is secondary; task 03 must cover the inherit_parent case directly.
  • Archive is reversible, removal is not. An unarchived task re-materializes its checkout. That is acceptable under an opt-in setting but must be stated in the settings copy (task 04).