fix(kanban): scratch tasks must not inherit board.default_workdir (#28818)

Board defaults represent persistent project checkouts. Scratch workspaces
are auto-deleted on completion and must stay under the per-board scratch
root that resolve_workspace() creates. Inheriting default_workdir for a
scratch task pointed the cleanup path at the user's source tree — the
data-loss vector documented in #28818.

The containment guard in _cleanup_workspace (just added) is the safety
rail. This commit prevents the bad state from being created in the first
place: only persistent kinds (dir/worktree) inherit board defaults.

Tests updated to cover the new semantics: scratch with default_workdir
set keeps workspace_path=None; dir/worktree still inherits the board
default.

Salvaged from PR #31315 by @leeseoki0 — prevention layer on top of the
#28819 containment fix by @briandevans.

Co-authored-by: teknium1 <127238744+teknium1@users.noreply.github.com>
This commit is contained in:
leeseoki0 2026-05-24 15:48:13 -07:00 committed by Teknium
parent 23115b5c0f
commit ce529d6072
2 changed files with 31 additions and 5 deletions

View file

@ -2596,13 +2596,32 @@ def test_task_dict_survives_corrupt_created_at(tmp_path, monkeypatch):
# ---------------------------------------------------------------------------
def test_create_task_without_workspace_inherits_board_default_workdir(kanban_home, monkeypatch):
"""Board with default_workdir → create_task without workspace_path → inherits default."""
def test_create_task_scratch_without_workspace_ignores_board_default_workdir(kanban_home, monkeypatch):
"""Scratch tasks must NOT inherit board.default_workdir — would point auto-cleanup
at the user's source tree on completion (#28818)."""
default_wd = "/home/user/project"
kb.create_board("work-proj", default_workdir=default_wd)
with kb.connect(board="work-proj") as conn:
tid = kb.create_task(conn, title="inherited", board="work-proj")
tid = kb.create_task(conn, title="scratch-task", board="work-proj")
t = kb.get_task(conn, tid)
assert t is not None
assert t.workspace_kind == "scratch"
assert t.workspace_path is None
def test_create_task_dir_without_workspace_inherits_board_default_workdir(kanban_home, monkeypatch):
"""Board default_workdir is for persistent dir/worktree workspaces, not scratch."""
default_wd = "/home/user/project"
kb.create_board("work-proj-dir", default_workdir=default_wd)
with kb.connect(board="work-proj-dir") as conn:
tid = kb.create_task(
conn,
title="inherited",
workspace_kind="dir",
board="work-proj-dir",
)
t = kb.get_task(conn, tid)
assert t is not None
assert t.workspace_path == default_wd