Use after an IssueOps cycle merged, or for "이슈옵스 정리", "워크트리 지우고 브랜치 지우고 이슈 닫아줘": closes the linked issue and removes the worktree and local branch. Unmerged: issueops-abandon.
Installs into .claude/skills of the current project.
Are you the author of Issueops Cleanup?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/m16khb-org-issueops-cleanup)
---
name: issueops-cleanup
description: 'Use after an IssueOps cycle merged, or for "이슈옵스 정리", "워크트리 지우고 브랜치 지우고 이슈 닫아줘": closes the linked issue and removes the worktree and local branch. Unmerged: issueops-abandon.'
---
# IssueOps Cleanup
Finish one record-backed, merged IssueOps cycle through harness-owned surfaces.
Exactly three effects are authorized after target confirmation: close and verify
the linked parent issue, remove the recorded local worktree, delete its local branch.
Remote branch deletion is a separate decision and `cleanup remote-branch` flow.
This body is the complete actor contract for copied or body-only use. Installed
`issueops`, `issueops-remote-write`, `git-operations`, and `gitlab-usecase` can supply
additional recipes; their absence never requires installation or evaluator material.
Resolve optional links from this file's real, symlink-resolved location, not cwd.
Use only observed CLI contracts; report missing inputs/tools rather than inventing them.
## Entry and authority
Resolve the exact ID as below, then run `issueops next --id "$ISSUEOPS_ID" --json`.
Require `stage.key=done`; otherwise follow the named stage/`next_command`, not cleanup.
Before each durable mutation, re-read the record and `execution whoami --json`.
Use its `record_actor_flags` for records and `claim_actor_flags` for execution
operations where the CLI requires them; never assemble identity flags yourself.
Preserve the exact ID, recorded generation, native actor, source cwd and target
worktree. A released completion is not permission to reclaim or bypass a holder.
`issueops status` and `issueops feedback add` are supported aliases; authenticated
recording belongs to this stage. `quality inspect` is not a semantic skill runner;
`issueops skill-bench` is unsupported.
## Preconditions
1. Prefer the supplied ID/`$ISSUEOPS_ID`; otherwise `issueops list --repo "$PWD" --json`.
Ask which ID only if zero or multiple plausible cycles remain.
2. Run from the record's source repository, never the worktree being removed. A session
started inside the worktree (for example the Herdr space opened for implementation) is a
requester occupant even after `cd`; run cleanup from a session opened in the source repository.
3. Require `done`, a released execution lease, and provider-verified merged PR/MR.
`execution complete` owns done/release; this skill never records completion.
4. Linked child tasks must already have verified close evidence.
5. Dirty worktree, branch mismatch, unreadable provider state
or pending external intent blocks cleanup. Occupants do NOT block: preview
lists `workspace_processes` (pid, command, start time, descendant/collateral
counts); typed apply stops them. An omitted `workspace_processes` means none.
Requester occupancy still blocks: `requester_occupies_worktree` includes
ancestors. `worktree_is_source_checkout` also
blocks. Use a different terminal/worktree, never manual process killing.
6. The remote source branch must be absent, or the user must have chosen to
leave it. On `remote_branch_absent`, present both exits and let the user pick:
- delete through the separately authorized `issueops cleanup remote-branch` flow, then finish;
- finish with `--keep-remote-branch`. Record deletion prevents later typed
deletion through this record. Report `kept_remote_branch` and the audit
fragment `remote_branch_kept=<branch>@<oid>`. The issue-number branch name
and provider link keep it discoverable; finish does not write the issue body.
A remote that cannot be read blocks the same way and takes the same two
exits; `--keep-remote-branch` records `state: "unreadable"` instead of an OID.
7. Prepared and provider-observed merged base must match. On `base_branch_drifted`,
`retargeted_base` or `merged_base_remote_unobserved`, read [base drift](references/base-drift.md).
Unmerged retirement (draft PR/MR, issue and remote branch closure) belongs to
`issueops-abandon`/typed `cleanup abandon`, not this path. An unmerged PR/MR the
developer approved is merged first by `issueops-merge`: `issueops remote merge-pr`
merges without deleting the branch, and that skill's step 6 deletes the remote branch
through `issueops cleanup remote-branch` before handing off here.
## Project docs
Before the preview, read the `.issueops/CAUTIONS.md` index (when absent, write "파일 없음" instead of opening it) and route the task through
`project_docs_route` (or `issueops project route-docs --repo "$PWD" --task "<cleanup
summary>" --json` when MCP is unavailable) to pick the dated `cautions/` and `adr/`
records about cleanup, worktree processes, and remote branches. Recorded cleanup
failures are the first things to check in the preview. In the confirmation summary,
record each caution that applies with its path, or "none applied".
## Preview the exact targets
Read the lifecycle and cleanup inventory:
```text
issueops status --id "$ISSUEOPS_ID" --json
issueops cleanup status --id "$ISSUEOPS_ID" --merged --json
issueops remote close-issue --id "$ISSUEOPS_ID" \
--provider "$PROVIDER" --json
```
Without `--confirm`, close is a dry-run. Verify live Git targets:
```text
git -C "$WORKTREE_PATH" status --short
git -C "$WORKTREE_PATH" symbolic-ref --quiet --short HEAD
git -C "$WORKTREE_PATH" rev-parse --verify HEAD
git -C "$REPO_ROOT" rev-parse --verify "refs/heads/$BRANCH"
```
Require a clean status, a symbolic branch exactly equal to `$BRANCH`, and a
worktree HEAD OID exactly equal to the local branch OID. If the provider exposes
the merged artifact head OID, require it to equal the local branch OID too.
A detached, repurposed, mismatched, or advanced worktree blocks cleanup.
Before confirmation, status `missing` may contain only `completion_reflected`
and `issue_closed`. Its other gates (`pr_phase`, `remote_artifact_*`,
`child_tasks_closed`, `worktree_*`, `branch_match`, `remote_branch_*`) must pass.
Finish preview renders the same two completion gates and their `next_command`;
after reflection only `issue_closed` remains. Do not wait for an empty list
before steps 1-2; only afterward must both previews be clear.
The close-issue dry-run must report `ok: true`. Any other missing gate or provider
error blocks closure: report it and stop before writing.
A parent with delegated child cycles closes their provider tasks first:
`issueops cleanup close-children --id "$ISSUEOPS_ID" --merged` (preview, then `--confirm`)
satisfies `child_tasks_closed`; remote-branch cleanup comes after it. Each child
cycle then runs this skill on its own ID: remote-branch, reflect-completion,
close-issue (already closed is success), finish.
From these results, state:
- lifecycle ID and issue URL;
- provider and verified merged PR/MR;
- exact local worktree path;
- exact local branch, worktree HEAD OID, local branch OID and available merged-head match;
- that the remote branch will not be deleted;
- every process the apply will stop (`workspace_processes`
with pid/command/start time and descendant/collateral counts),
because unsaved work in those processes is lost;
- every readiness blocker.
Missing/ambiguous targets or other blockers mean stop. Never infer targets from an issue title.
## Confirmation boundary
Obtain one explicit confirmation naming all three destructive/external effects:
```text
이슈 <URL>을 닫고, HEAD <WORKTREE_HEAD_OID>인 로컬 워크트리 <PATH>와
OID <BRANCH_OID>인 로컬 브랜치 <BRANCH>를 삭제할까요?
apply가 먼저 종료하는 것: 프로세스 <N>개(<pid:command …>).
원격 브랜치는 삭제하지 않습니다(merge 단계에서 이미 지웠으면 "원격 브랜치는 merge 단계에서 삭제했습니다").
주의사항: <Project docs에서 고른 caution마다 "문서 경로: 해당 문장 인용", 없으면 "열어 본 caution <경로 목록>, 해당 없음">
```
If no processes are listed, say nothing will be stopped. The `주의사항:` line is the
record the `Project docs` section asks for; write it in the confirmation itself.
The latest user message must confirm these exact targets; generic "cleanup" authorizes preview only.
Exception: when the original request explicitly authorized cleanup of **this cycle** in its approval
sentence (e.g. "merge하고 정리까지 끝내줘 ... 정리는 승인함") and no later user message narrowed or
stopped it, that approval stands in for the confirmation only if all of these hold: the preview's
worktree, branch, and issue are the record's; the worktree HEAD and local branch OID equal the
merged head; the worktree has no uncommitted or untracked changes; and `workspace_processes`
lists nothing to stop (descendant and collateral counts zero). The finish preview observes
processes even while `completion_reflected` or `issue_closed` is still missing (exit 1), so read
it there before those writes; an absent `workspace_processes` field there means none. Such an approval also covers the
issue close and completion reflection writes, which still run as preview then the same confirm.
Print the confirmation text above (with the `주의사항:` line) as the record even when not asking.
If anything differs or cannot be determined, show the targets and ask again.
## Apply in fail-closed order
### 1. Reflect completion into the issue
Preserve the team's progress report before closing; otherwise finish blocks on
`completion_reflected` (`issueops list` shows the same gap as `completion_unreflected: true`).
Every remote write uses preview → identical confirm →
readback. Changed body means a new preview; uncertain results mean reconcile,
never repeat a create/write or bypass the harness with raw provider mutations.
- Read the merged PR body and `issueops status --id "$ISSUEOPS_ID" --json`.
- Write one or two result sentences, then three to six flow lines (plan, what the
review changed, implementation with the PR link, what was checked, what is
left). Call `fluent-korean` if installed; otherwise directly review complete
Korean sentences, clear subjects, concrete results and unnecessary modifiers.
Remove secrets. Fix readability warnings or record why each remains.
- Save a temporary file outside the worktree; the issue body is the enduring record.
```text
issueops remote reflect-completion --id "$ISSUEOPS_ID" \
--provider "$PROVIDER" --body-file "$RESULT_FILE" --json
issueops remote reflect-completion --id "$ISSUEOPS_ID" \
--provider "$PROVIDER" --body-file "$RESULT_FILE" --confirm --json
```
Require no `readability` critical finding: full SHA, local path or over 2,000 characters is refused.
Require confirmed `ok: true` and matching body/URL readback; already-reflected is idempotent success.
### 2. Close and verify the issue
```text
issueops remote close-issue --id "$ISSUEOPS_ID" \
--provider "$PROVIDER" --confirm --json
```
Require `ok: true`, `closed: true`, verified closed state; already-closed is idempotent success.
### 3. Re-preview local cleanup
Closure changes readiness: obtain a fresh fingerprint.
```text
issueops cleanup finish --id "$ISSUEOPS_ID" \
--provider "$PROVIDER" --preview --json
```
Carry the user's `--keep-remote-branch` choice into this preview; it reports
`kept_remote_branch` and includes the flag in `next_command`. Without it, a
remaining remote branch blocks on `remote_branch_absent` and yields no fingerprint.
If `worktree_present=true`, repeat all four live Git checks after closure and before apply.
If a prior typed apply removed the worktree but retained the record (`worktree_present=false`):
- do not run `git -C "$WORKTREE_PATH"` commands;
- require evidence of the worktree removal: the prior apply result, or, when that
apply was killed without output, the record's `cleanup_attempt` (operation
`finish`) together with this preview's `worktree_present=false`;
- require `git worktree list --porcelain` not to contain `$WORKTREE_PATH`;
- when `branch_present` is true, re-read `refs/heads/$BRANCH` and require its
OID to equal the OID previously confirmed by the user;
- when `branch_present` is false, require `git show-ref --verify --quiet
"refs/heads/$BRANCH"` to exit `1`.
Require:
- `ok: true`;
- an empty `missing` list (the field is omitted when empty);
- a non-empty `fingerprint`;
- `worktree_path` and `branch` equal the targets the user confirmed;
- when the worktree remains present, its live HEAD OID and local branch OID
equal the confirmed OIDs;
- during typed partial-failure recovery, every still-present branch OID equals
the confirmed branch OID.
Changed path/name/OID means stop and reconfirm new targets. Never reuse a stale fingerprint.
### 4. Apply the emitted command
Execute the preview's `next_command` exactly. It must be the typed form:
```text
issueops cleanup finish --id "$ISSUEOPS_ID" \
--apply --confirm --fingerprint "$FINGERPRINT" --json
```
The emitted command may also carry `--generated-by-executable`, `--generated-by-sha256`, and
`--generated-for-generation`. They bind it to the binary and generation that produced it; keep
them as emitted (a different binary or generation refuses it instead of running a stale command).
Never substitute raw `git worktree remove`, `git branch -d/-D`, `git update-ref`,
`git push origin --delete`, or manual terminal/process stops.
The fingerprint binds worktree, branch OID and occupant receipts.
Apply stops occupants with HUP+TERM, KILL, and re-observation, and
failure stops at `workspace_processes_stop`; it then removes
the worktree and CAS-deletes the local branch; emits audit and deletes the record.
Any destructive failure retains the record. `workspace_processes_stop` failure
means remaining occupants; re-preview rather than improvise.
Never append remote branch deletion to this flow.
## Verify the observable result
After apply:
1. Require `ok: true` and `record_deleted: true`.
2. If the worktree was present, require `worktree_removed: true`, and report
`workspace_processes_stopped` exactly as the
apply returned it.
3. If the local branch was present, require `branch_deleted: true`.
4. From the source repository, verify:
```text
git worktree list --porcelain
git show-ref --verify --quiet "refs/heads/$BRANCH"
```
The removed worktree path must be absent, and `show-ref` must exit `1`.
5. Re-read the remote issue with the provider and require a closed state.
6. If the worktree was removed, close its Herdr space or workspace per
[Herdr close after cleanup](../issueops/references/session-choice-herdr.md#worktree-정리-뒤-herdr-화면-닫기).
7. Report the issue URL, removed worktree, removed local branch, remote issue
state, the Herdr view result, and explicitly state that the remote branch was untouched.
On partial failure report `failed_step` and `next_command`; recover only through the retained
record and the worktree-aware rules above. Do not improvise or report partial work as complete.