Close a taskwarrior task with landing commit annotation and optional GitHub issue/PR close. Use when finishing a coordination task or marking a work order complete.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Close a taskwarrior task with landing commit annotation and optional GitHub issue/PR close. Use when finishing a coordination task or marking a work order complete.
Git probes (git remote, git rev-parse --short HEAD,
git branch --show-current) write to stderr in a no-git cwd, and stderr
from a Context backtick aborts the skill before its body runs. HEAD
commit, current branch, and remote-presence checks happen in the body
(Steps 2 and 5) via the Bash tool where 2>/dev/null and exit-code
handling are tolerated.
Parameters
Parse $ARGUMENTS:
$0 โ task ID (required)
$1 โ commit hash (optional; defaults to HEAD)
--no-gh โ skip GitHub close/comment even when remote is present
--no-tracker โ skip blueprint tracker drain
--drain-identity โ also clear agent / pid / host / branch / UDAs after closing (default keeps them as audit trail)
worktree
--no-coworker-marker โ skip the /git:coworker-check --release step
Execution
Execute this workflow:
Step 1: Load the task
task "$TASKID"export | jq '.[0]'
Never use task $TASKID info or task $TASKID list โ both can exit 1 and
cancel parallel siblings. export | jq returns valid JSON even when the
task is already closed (treat empty as "no such open task" and abort).
Capture: uuid, bpid, bpdoc, ghid, ghpr, tags, description, plus
identity UDAs agent, pid, host, branch, worktree, and start
(if the task was claimed via /taskwarrior:task-claim).
Capture uuid as $TASK_UUID โ every mutating call from Step 2 onward
(including the post-close Step 6 query) addresses the task by $TASK_UUID,
never $TASKID (see .claude/rules/task-id-stability.md).
Step 2: Resolve commit hash
If $1 is unset, read git rev-parse --short HEAD. Confirm the HEAD commit
actually touches work this task covers before annotating โ a stale HEAD is
a common footgun.
Annotation first, then done โ if close fails (e.g. dependencies), the
annotation is still captured.
Taskwarrior auto-stops a +ACTIVE task on done, so an explicit
task stop is not needed. The task transition removes +ACTIVE and
records the duration. If you want to drain the identity UDAs (so the
closed task does not retain the stamp), do so separately after the
close โ see Step 4b below.
Step 4b: Drain identity UDAs (optional)
After the task is closed, optionally clear the identity stamp left by
the original claim:
Default behaviour is to leave these set on closed tasks โ the audit
trail of "who claimed and landed this" is useful in task-status
recently-completed reports. Drain them only when the user explicitly
asks (e.g. compliance / privacy hygiene), or when handing the queue
file off to another team.
Step 4: Drain the blueprint tracker
If bpdoc is set and points to a valid path, read the file and advance
its status marker in place. Typical patterns by tracker format:
Work-order markdown: check off the relevant bullet, add landed-in line
PRP: flip the "Implementation" status field
Use Edit with a narrow old_string / new_string pair โ do not rewrite
the file. When bpdoc references a shared tracker (manifest.json, global
feature-tracker), fall back to reporting "manual tracker update required"
rather than concurrent-write the shared file.
Step 5: Close linked GitHub items (optional)
When GitHub mode is active and --no-gh was not passed:
ghid set โ offer gh issue close "$GHID" --comment "Closed by $COMMIT_SHORT (branch $BRANCH)"
Always confirm before mutating GitHub state โ the user may want to close
the issue as part of the PR merge rather than ahead of time.
Step 5b: Drop the coworker-check marker
If the task was +ACTIVE (claimed via /taskwarrior:task-claim), the
matching git-side session marker should be released so destructive ops
in this clone are no longer guarded:
Use SlashCommand to invoke `/git:coworker-check --release`.
Skip when the user has more work in flight on this branch โ releasing
the marker lifts the cross-agent guard.
Step 6: Report
Print:
Task closed: id + description + new status
Commit annotated: $COMMIT_SHORT
Tracker drained: path + diff summary, or "skipped"
Unblocked siblings: any tasks whose depends: pointed at this one now
free to start (query via task depends:"$TASK_UUID" export | jq). Once
completed, this task's numeric id becomes 0 and may be reassigned to
an unrelated task โ a query keyed on the stale $TASKID here would check
dependencies against the wrong task. depends: accepts UUIDs, so
$TASK_UUID is the only safe form for this read.
Agentic Optimizations
Context
Command
Load task
task "$TASKID" export | jq '.[0]' (capture .uuid as $TASK_UUID)
Annotate + close
Two separate calls (hook-friendly), both by $TASK_UUID
Blocked children
task depends:"$TASK_UUID" export | jq '.[]'
Skip empty-result failures
Always export | jq
Quick Reference
Step
Command
Load
task TASKID export | jq (capture uuid)
Annotate
task UUID annotate "msg"
Close
task UUID done
Check unblocked siblings
task depends:UUID export | jq '.[]'
GitHub close
gh issue close N --comment "msg"
PR comment
gh pr comment N --body "msg"
Bulk-close patterns
Closing many tasks in one pass has two silent foot-guns. The obvious shape โ for id in 1 2 3; do task $id done; done โ reports success while doing the wrong thing.
Foot-gun 1: numeric IDs renumber after every task done
Numeric IDs are a display index over pending tasks. The moment one closes, every higher ID shifts down by one. A loop over numeric IDs closes the original first task, then keeps targeting wrong tasks as IDs slide underneath the iterator. No error surfaces.
Fix: use UUIDs (immutable). The same applies to any iterated state-changing op โ annotate, modify, delete.
# WRONG โ IDs shift mid-loopforidin 35 36 37 38 39; do task "$id"done; done# Correct โ capture immutable UUIDs first
UUIDS=$(task status:pending project:myrepo export | jq -r '.[].uuid')
for u in$UUIDS; do task "$u"done </dev/null; done# stdin redirect โ see below
Foot-gun 2: task done consumes loop stdin
task done reads from stdin (for confirmation prompts). In a shell for loop, the loop's input is also stdin โ so task done eats subsequent iterations and the loop exits early, usually after one or two passes, with no error.
Symptom: a loop over 15 UUIDs reports "processed 15" but only 1 task closed.
# Fix A โ redirect stdin per inner commandfor u in$UUIDS; do
task "$u" rc.confirmation=no done </dev/null
done# Fix B โ xargs (preferred; each invocation runs in its own subshell with no# stdin link to the source loop)echo"$UUIDS" | xargs -I {} sh -c 'task rc.confirmation=no {} done'
Always pass rc.confirmation=no for batch closes
Without it, taskwarrior may prompt "this task is blocked by N other tasks, complete anyway? (yes/no)" per task, hanging the loop. rc.confirmation=no makes batch closes deterministic.
Annotate before done, not after
Once completed, a task's id becomes 0 and task <id> no longer addresses it โ only the UUID does. Annotate first to keep the UUID-or-ID workflow uniform.
Finding tasks for the bulk close
Use export | jq (never list โ it exits 1 on empty filters and cancels parallel siblings; see .claude/rules/parallel-safe-queries.md):
task status:pending project:myrepo +pr_ready export | jq -r '.[].uuid'# Tasks with no project (CLI filter quirk โ empty value as first filter# errors; sidestep through jq)
task status:pending export | jq -r '.[] | select(.project == null) | .uuid'# Substring matches on description (for markers that aren't real tags)
task status:pending export \
| jq -r '.[] | select(.description | test("\\[triage\\]")) | .uuid'