Skip to content

orchestrate: teardown always SKIPs worktree removal because worker sessions predictably create untracked .claude/ scratch #129

Description

@choiyounggi

증상 (stickygap1 런, 2026-08-21, dev-loop 1.8.3 실측)

end-of-run contract는 "모든 런은 teardown으로 끝난다"인데, 실제로는 매 런의 teardown이 워크트리 제거에서 SKIP된다:

remove-worktrees: SKIP — uncommitted changes in .../.worktrees/task-t1-sticky-gap (never --force).

이때 워크트리의 dirt는 수정/스테이징이 아니라 untracked .claude/ 하나였다 (git status --short = ?? .claude/). Claude 워커 세션은 자기 cwd에 .claude/(settings, tmp 진단 노트)를 만드는 것이 사실상 보장된 동작이라 — 이 레포의 CLAUDE.md는 임시 파일을 .claude/tmp/에 두라고 지시하기까지 한다 — SKIP은 예외가 아니라 기본 경로가 된다. 결과적으로 매 런마다 수동 마무리(보존할 진단 노트를 archive로 복사 → rm -rf .claudegit worktree remove)가 필요했고, restyle1/designv2 런에서도 동일했다.

원인

safe-cleanup.sh remove_worktrees()(±L109-118)가 git status --porcelain 비어있음을 요구하는데, porcelain은 untracked(??)도 포함한다. "never --force"의 보호 대상(커밋 안 된 작업 내용 유실 방지)과, 워커 세션의 예측 가능한 스크래치는 위험도가 다른데 같은 기준으로 묶여 있다.

제안

  1. .groundwork/와 같은 처리 — 선례가 이미 코드에 있다: worker-guardrails.sh는 자기가 만드는 .groundwork/guardrails.json을 워크트리의 info/exclude에 등록해 status를 오염시키지 않는다. setup-worktrees.sh(또는 launch-session)가 같은 방식으로 .claude/를 워커 워크트리의 info/exclude에 추가하면, porcelain이 깨끗해지고 git worktree remove도 통과한다(ignored 파일은 removal을 막지 않음).
  2. teardown에 스크래치 아카이브 단계 추가: 삭제 전에 워크트리의 .claude/tmp/ 등 untracked-only 파일을 archive-<date>-<runid>/worker-scratch/<task>/로 복사. 이번 런의 t1-diagnosis.md(실측 rect·카운터팩추얼 기록)처럼 보존 가치가 있는 산출물이 실제로 존재한다 — 조용한 삭제는 "never --force" 정신에 어긋나므로, 아카이브 후 제거가 맞다.
  3. 거부 기준의 분리: tracked 수정/스테이징이 있으면 지금처럼 SKIP(진짜 작업 유실 위험), untracked-only면 2의 아카이브 후 제거. SKIP 메시지에 어떤 종류의 dirt인지(modified vs untracked-only: .claude/)를 표기하면 수동 개입 시에도 판단이 빨라진다.

1+2 조합이면 end-of-run contract가 문서 그대로 자동으로 성립한다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions