증상 (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 .claude → git worktree remove)가 필요했고, restyle1/designv2 런에서도 동일했다.
원인
safe-cleanup.sh remove_worktrees()(±L109-118)가 git status --porcelain 비어있음을 요구하는데, porcelain은 untracked(??)도 포함한다. "never --force"의 보호 대상(커밋 안 된 작업 내용 유실 방지)과, 워커 세션의 예측 가능한 스크래치는 위험도가 다른데 같은 기준으로 묶여 있다.
제안
.groundwork/와 같은 처리 — 선례가 이미 코드에 있다: worker-guardrails.sh는 자기가 만드는 .groundwork/guardrails.json을 워크트리의 info/exclude에 등록해 status를 오염시키지 않는다. setup-worktrees.sh(또는 launch-session)가 같은 방식으로 .claude/를 워커 워크트리의 info/exclude에 추가하면, porcelain이 깨끗해지고 git worktree remove도 통과한다(ignored 파일은 removal을 막지 않음).
- teardown에 스크래치 아카이브 단계 추가: 삭제 전에 워크트리의
.claude/tmp/ 등 untracked-only 파일을 archive-<date>-<runid>/worker-scratch/<task>/로 복사. 이번 런의 t1-diagnosis.md(실측 rect·카운터팩추얼 기록)처럼 보존 가치가 있는 산출물이 실제로 존재한다 — 조용한 삭제는 "never --force" 정신에 어긋나므로, 아카이브 후 제거가 맞다.
- 거부 기준의 분리: tracked 수정/스테이징이 있으면 지금처럼 SKIP(진짜 작업 유실 위험), untracked-only면 2의 아카이브 후 제거. SKIP 메시지에 어떤 종류의 dirt인지(
modified vs untracked-only: .claude/)를 표기하면 수동 개입 시에도 판단이 빨라진다.
1+2 조합이면 end-of-run contract가 문서 그대로 자동으로 성립한다.
증상 (stickygap1 런, 2026-08-21, dev-loop 1.8.3 실측)
end-of-run contract는 "모든 런은 teardown으로 끝난다"인데, 실제로는 매 런의 teardown이 워크트리 제거에서 SKIP된다:
이때 워크트리의 dirt는 수정/스테이징이 아니라 untracked
.claude/하나였다 (git status --short=?? .claude/). Claude 워커 세션은 자기 cwd에.claude/(settings, tmp 진단 노트)를 만드는 것이 사실상 보장된 동작이라 — 이 레포의 CLAUDE.md는 임시 파일을.claude/tmp/에 두라고 지시하기까지 한다 — SKIP은 예외가 아니라 기본 경로가 된다. 결과적으로 매 런마다 수동 마무리(보존할 진단 노트를 archive로 복사 →rm -rf .claude→git worktree remove)가 필요했고, restyle1/designv2 런에서도 동일했다.원인
safe-cleanup.shremove_worktrees()(±L109-118)가git status --porcelain비어있음을 요구하는데, porcelain은 untracked(??)도 포함한다. "never --force"의 보호 대상(커밋 안 된 작업 내용 유실 방지)과, 워커 세션의 예측 가능한 스크래치는 위험도가 다른데 같은 기준으로 묶여 있다.제안
.groundwork/와 같은 처리 — 선례가 이미 코드에 있다:worker-guardrails.sh는 자기가 만드는.groundwork/guardrails.json을 워크트리의info/exclude에 등록해 status를 오염시키지 않는다.setup-worktrees.sh(또는 launch-session)가 같은 방식으로.claude/를 워커 워크트리의info/exclude에 추가하면, porcelain이 깨끗해지고git worktree remove도 통과한다(ignored 파일은 removal을 막지 않음)..claude/tmp/등 untracked-only 파일을archive-<date>-<runid>/worker-scratch/<task>/로 복사. 이번 런의t1-diagnosis.md(실측 rect·카운터팩추얼 기록)처럼 보존 가치가 있는 산출물이 실제로 존재한다 — 조용한 삭제는 "never --force" 정신에 어긋나므로, 아카이브 후 제거가 맞다.modifiedvsuntracked-only: .claude/)를 표기하면 수동 개입 시에도 판단이 빨라진다.1+2 조합이면 end-of-run contract가 문서 그대로 자동으로 성립한다.