active/fin/SKILL.md
Finalize completed PR or local checkout work. Use when explicitly invoked
npx skillsauth add kevinslin/skills finInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill at the end of a task before the final user-facing report.
Run fin [context] [target].
gh: finalize from a GitHub PR context. Use this when the task should land by merging the current remote PR or when the matching PR already merged and only cleanup/final verification remains. The original fin workflow maps to this context.local: finalize from a local checkout. Use this when the task should land directly from local git state without depending on GitHub PR state.[target]: optional for gh only. Accept a PR number, PR URL, or branch name. Examples: fin gh 85117, fin gh https://github.com/owner/repo/pull/85117.HEAD, treat that as a preflight issue, not a valid finalization state. Create a short-lived local branch from the current commit before auto-detecting context, checking mergeability, or attempting worktree cleanup.gh or local, respect it throughout the flow. Do not silently switch later just because repo state would make the other path easier.gh has an explicit [target], lock that PR target before current-branch detection. Use the target PR as the source of truth for state, mergeability, comments, checks, spec matching, merge, and automation cleanup.[target] but the immediately preceding active heartbeat or delayed-merge instruction names exactly one PR and the user asks to merge, finalize, ignore a waiting period, or ignore a proof gate, treat that PR as an explicit gh target after one live PR-state check. Report the target source as heartbeat automation.[target] but the immediately preceding task in the same thread completed or repaired exactly one PR, such as after trigger:fix-pr, trigger:fix-pr-conflict, or a PR-specific babysit/CI run, treat that PR as an explicit gh target after one live PR-state check. Report the target source as active task context. If the current checkout points at another branch or PR, mention the mismatch and ignore the unrelated checkout for PR state, spec archival, merge, automation, and cleanup decisions unless it blocks local cleanup.gh target does not match the current branch, do not silently fall back to the current branch's PR. Either run the remote-PR finalization path for that explicit target, or stop with a target mismatch before any spec archival, branch cleanup, or merge.gh when the current branch has an open or already-merged PR that corresponds to the branch being finalized.local when the current branch has no matching PR and the work should land directly from local git state.gh targets, not as current-branch auto-detection.gh / local, stop and ask the user which context to use.Target: PR #<number>, branch <headRefName>, source=<current checkout|explicit user PR|heartbeat automation|active task context>. When multiple PRs have been mentioned in the session, prefix every PR-specific state claim with the PR number.main means the default branch of the repository addressed by origin; do not assume the literal branch name is main.
For gh, query the target repository's authoritative default branch and the locked PR's baseRefName.
For local, resolve and verify the default branch associated with origin, using the remote provider when available or refs/remotes/origin/HEAD as a fallback. If the origin main branch cannot be determined unambiguously, stop.
Compare canonical short branch names. When a local fallback returns refs/remotes/origin/<name> or origin/<name>, retain the raw result as evidence and pass only <name> to the gate.
Pass the two resolved names through the mandatory machine gate before any spec archival, landing, merge, cleanup, or full-finalization wording:
python3 ./scripts/check_default_branch.py \
--context gh \
--repository-default-branch "<resolved-origin-main>" \
--target-base-ref "<locked-target-base-ref>"
Resolve ./scripts/check_default_branch.py relative to this SKILL.md, replace the quoted placeholders with the resolved values, and use --context local for local finalization. Do not replace this command with a manual comparison or a same-named repository script.
Continue only when the command exits 0 and its JSON reports status: "pass", matches: true, and every allow field as true. Retain that JSON as finalization evidence. Treat a nonzero exit, malformed output, or any other result as a hard blocker.
Rerun the gate if the locked target, repository default branch, or target base ref changes. The latest passing record is the only valid authorization for the finishing actions.
A mismatch blocks intermediate, stacked, release, and feature branches. Use the script's retarget-or-create-PR message in the report.
If the PR already merged into a non-main branch, the gate still fails: stop before archival or cleanup, report that it did not land in origin's main branch, and do not describe it as fully finalized.
main checkout lives.~/.fin.yaml for repo-specific finalization instructions before landing or cleanup. If the file exists, parse entries shaped as workspace: [{path: ..., instructions: ...}].~/.fin.yaml exists but cannot be parsed, do not treat the parse failure as "no hooks". Report the parser error separately and inspect the raw file. Continue only when the raw content is clearly non-executable, unambiguous context; if it might contain commands, hooks, destructive instructions, or ambiguous cleanup requirements, stop before spec archival, landing cleanup, or linked-worktree removal and report the malformed config as the blocker.workspace[].path against the normalized non-worktree checkout root for the branch's repository, not against a transient linked worktree path. Resolve symlinks and trailing slashes before comparing.~/.fin.yaml workspace entry exists, record its instructions and apply them after the PR merge / already-merged confirmation or local landing succeeds, but before removing any linked worktree. This preserves ignored or untracked files that may need to be copied out of the soon-to-be-removed worktree.~/.fin.yaml instructions as routing context for active spec discovery. If the instructions mention a $specy, $mem, or .mem artifact root, derive candidate docs roots from that instruction before falling back to the default docs root.~/.fin.yaml is missing or no workspace entry matches the non-worktree checkout root, state that no repo-specific final hooks applied and continue.HEAD, create a temporary local branch from the current commit first. Prefer the repo's normal task-branch prefix when one exists, otherwise use a short codex/ branch name derived from the task.HEAD into a named branch, lock that branch identity for the rest of the run. Do not continue finalization from anonymous detached state.gh, do not fall back to local-only landing. If the user requested local, do not silently land via PR merge just because a PR exists.gh without an explicit target, identify the current PR and check its state before testing mergeability or attempting any merge command.gh with an explicit target, identify that target PR directly with GitHub before consulting current-branch PR state. If the current checkout points at another PR, report the mismatch in the target identity line and ignore the other PR unless it blocks local cleanup.gh, identify any active PR babysit/watch automation for the target PR or branch when automation state is visible. Record its id so it can be updated or deleted after the PR lands.gh, record an explicit user request to keep the active task in the foreground until a pending auto-merge finishes. The request does not waive the locked target identity or any blocker and enables foreground monitoring only if persistent heartbeat automation is unavailable.main refresh, and retrospective.gh, when the target PR is still open, confirm it is mergeable against the verified origin main branch.
gh pr view reports UNKNOWN/indeterminate mergeability while checks and reviews otherwise look green, poll the REST pull-request endpoint once or twice for mergeable and mergeable_state before invoking conflict repair. Treat REST mergeable: true with mergeable_state: clean as the mergeability confirmation; treat repeated null/unknown as indeterminate and wait or report it.local, confirm the current branch is mergeable into the verified local origin main branch.gh flow is blocked only by base-branch conflicts, run trigger:fix-pr-conflict against the locked target PR and let it try to restore a clean merge state.gh flow is blocked by broader PR issues, or conflict repair needs a fuller pass, run trigger:fix-pr against the locked target PR.local flow is blocked only by trunk drift, run trigger:sync-branch or otherwise rebase the current branch onto the merge target before retrying the check.gh context, re-confirm mergeable: true for the exact PR head, then use a repository-supported administrator or maintainer override merge. If merge commits are disallowed, retry once with the supported squash method. Never use the override to merge a conflicting or indeterminate head.fin auto-merge heartbeat automation by exact automation id first, then by the locked repository, PR number, and head branch.${CODEX_HOME:-$HOME/.codex}/automations for the exact automation id, PR number, or head branch. Treat this as read-only fallback evidence; never delete automation files directly.~/.agents/profile directly. Treat the profile as work only when it contains a trimmed, non-comment line exactly equal to name=work. For a non-work profile, skip Linear unless the user or current task context explicitly identifies a Linear issue to finish.dev.llm-session when needed. Use the installed linear:linear connector skill and its connected Linear tools; do not use linear-cli or a browser fallback.thread_link be the exact deep link codex://threads/<thread-id>. Search with list_issues(query=thread_link, assignee="me", includeArchived=true) first. If that produces no exact match, run one unscoped list_issues(query=thread_link, includeArchived=true) search and follow its pagination until exhausted. If the connector cannot exhaust the candidate set, treat lookup as ambiguous instead of guessing.get_issue before comparison. Retain only issues whose complete description contains thread_link exactly, and expose only each exact match's id, team, status, status type, and URL.thread_link match or an issue the user explicitly identified as the ticket for this task. Do not infer linkage from a search hit, similar title, branch name, PR number, assignee, or project.backlog, unstarted, or started as pending. Treat completed and canceled issues as terminal and leave them unchanged.DOCS_ROOT when it is configured.~/.fin.yaml instructions mention an absolute .mem artifact root for $specy, $mem, or notes, add <that-root>/main when the instruction points at .mem, or the path itself when it already points at .mem/main../docs.specy's layout rule for each candidate root: active specs live directly under $DOCS_ROOT/specs/.$DOCS_ROOT/specs/ as active specs. Ignore files already under $DOCS_ROOT/specs/.archive/.specs: an active spec folder lives directly under a candidate $DOCS_ROOT/specs/<spec-slug>/, with optional sidecars such as milestones/, flows/, reports/, cook/, checklist.md, or data/. Treat the folder as the active spec unit.spec.md..archive/
until the whole folder spec is complete.$DOCS_ROOT/specs/.archive/, creating the directory if needed. For folder specs, move the whole folder so sidecars such as checklist.md and data/ stay with the completed spec.specy's convention exactly: when a single-file spec is complete, move it to $DOCS_ROOT/specs/.archive/ and keep the same filename.$dev.worktrees cleanup-landed as the only executor for destructive local worktree and local-branch cleanup during fin. Do not reproduce its reset, clean, removal, orphan recovery, or branch deletion steps manually.~/.fin.yaml final hook;$dev.worktrees cleanup-landed.ready or noop, then rerun the exact command in execute mode.blocked, partial, a nonzero exit, identity drift, or failed postconditions as a finalization blocker. Preserve its journal and rerun the exact command after resolving the blocker.complete or noop, with the target path, worktree registration, and local branch absent and the base still containing the landed commit.gh Context WorkflowMERGED, do not run trigger:merge-pr; record the merge commit or merged-at details when available and proceed as an already-landed PR.trigger:merge-pr immediately after the matching spec has been marked complete and archived.gh pr merge <target>, after the matching spec has been marked complete and archived. Do not use current-branch merge shortcuts for a different PR.autoMergeRequest, state, mergedAt, mergeCommit, mergeStateStatus, and status checks.autoMergeRequest present with the PR still OPEN as auto-merge pending, not as blocked, while checks remain green and no explicit cancellation or failing required check is present.MERGED, verify mergedAt and mergeCommit, then continue with normal post-merge cleanup.autoMergeRequest still present, create or update exactly one dedicated thread heartbeat named fin: auto-merge <owner>/<repo>#<number>. Reuse an existing exact match instead of creating duplicates. Store the locked repository, PR number and URL, base and head branches, head SHA, merge method, autoMergeRequest.enabledAt, and the deferred fin stage in its context.autoMergeRequest, state, mergedAt, mergeCommit, mergeStateStatus, head SHA, and required checks. While the same head remains OPEN with auto-merge present and no terminal blocker, keep the heartbeat active, report auto-merge pending, and leave post-merge cleanup, Linear completion, and full-finalization wording deferred.MERGED with non-empty mergedAt and mergeCommit; then resume this workflow at step 5 in the same wake. If the auto-merge request is removed or cancelled, the head changes unexpectedly, a required check fails, or conflicts appear, stop the heartbeat as blocked rather than resolving it as successful, preserve cleanup state, and report the exact evidence.auto-merge pending plus the automation error and leave cleanup deferred; do not imply that monitoring was established.autoMergeRequest, state, mergedAt, mergeCommit, mergeStateStatus, head SHA, and required checks on every poll. Continue until the locked head is MERGED with non-empty mergedAt and mergeCommit, then resume at step 5. Stop only for a terminal blocker: the auto-merge request is removed or cancelled, the head identity changes, a required check fails, conflicts or unmergeability appear, or live polling can no longer continue. Report the exact blocker and preserve deferred cleanup. Never infer or use an administrator, approval, or blocker override from a request to watch.main, delete matching automation, and report that no local target checkout cleanup was applicable.~/.fin.yaml. If a hook fails, stop and preserve the worktree and branch.main: run git fetch <remote> <base>:<base> only when <base> is not checked out in any worktree, then confirm containment with git merge-base --is-ancestor <mergeCommit> <base>.<base> is checked out elsewhere or the ref cannot fast-forward, fall back to the normal checkout-and-pull path when that is safe. Otherwise, stop with a local-main refresh blocker.local landing flows that need to merge the completed local branch into main.main if needed and if the non-switching base-ref refresh path was not used.main to origin/main.main HEAD, or the refreshed local base ref when the non-switching path was used, contains the PR merge commit before any local worktree or branch deletion.main refresh fails for any other reason, stop and report the exact git error instead of claiming the task is fully finalized.$dev.worktrees cleanup-landed with that exact path, PR head branch, PR head SHA, refreshed base ref, merge commit, and actual merge mode.$dev.worktrees cleanup-landed in branch-only mode with the same branch/head/base/landing proof.gh pr merge --delete-branch already removed the remote branch but could not remove a linked local branch, do not retry the merge. The cleanup script owns the remaining local transaction.local Context Workflowtrigger:commit-code first.main checkout when possible, not from an attached feature worktree.main before merging when the repo has a remote, but do not discard the completed work while doing so.main. Use a non-fast-forward merge unless the user explicitly requested a different merge style.main, do not fabricate a merge commit; instead verify that main already contains the intended change set and continue.main is appropriate for the repo, push the updated main after the local merge. If the flow is intentionally local-only, state that explicitly in the report.main if needed.main HEAD includes the merge commit or direct commit that landed the completed work.main, confirm the pushed remote ref contains the landed commit too.~/.fin.yaml. If verification or a hook fails, preserve the worktree and branch and report the exact blocker.$dev.worktrees cleanup-landed with its exact path, branch, recorded head, local base ref, landed commit, and actual merge mode.$dev.worktrees cleanup-landed in branch-only mode.noop or state that no local cleanup was applicable.main refresh or verification have completed. Do not complete the issue while a PR is merely auto-merge pending or while finalization is in a partial local-cleanup state.completed live. Prefer the case-insensitive name Done when multiple completed statuses exist; if no unique destination can be resolved, leave the issue unchanged and report the ambiguity.completed before claiming success.fin, never complete an issue that was not locked during lookup, and never bulk-complete multiple matches.partial finalization: Linear issue not verified complete with the issue id when known and the exact blocker.$ag-learn after the task lands in the requested context, after any matching spec has been archived, and after any matching repo-specific final hooks from ~/.fin.yaml have completed.gh, run it after the PR merge or already-merged confirmation, repo-specific final hooks, local base containment proof, and deterministic cleanup complete when applicable.local, run it after the local merge, repo-specific final hooks, local base verification, and deterministic cleanup complete when applicable.ag-learn finds no meaningful improvement opportunities, say that explicitly.gh or local.status, repository_default_branch, target_base_ref, and matches fields. For a missing or failed gate, report the blocker and retarget-or-create-PR guidance instead of a finished state.gh, state the target identity line with PR number, branch, and source before reporting mergeability, checks, blockers, merge, or cleanup. If another PR was also present in the current checkout, explicitly state that it was not the finalization target.fix-pr-conflict / fix-pr / sync-branch / manual repair.gh, state whether the PR was already merged and merge-pr was skipped, or whether merge-pr ran successfully, including whether the remote merge succeeded directly or required separate post-merge worktree cleanup because local branch deletion failed.gh, if auto-merge was enabled but the PR has not merged yet, report auto-merge pending, include the PR URL, head SHA, auto-merge method, autoMergeRequest.enabledAt when available, heartbeat automation id and next check when creation succeeded, and explicitly state that local cleanup requiring merged proof was deferred.auto-merge pending.local, state whether the branch landed via local merge, was already on main, or was blocked before landing.status, whether the worktree path and registration are absent, whether the local branch was deleted, and whether cleanup was not applicable. Do not claim or report global prune as a normal step.gh, state whether any PR babysit/watch automation was found and whether it was deleted, already absent, or blocked.main containment check used as the cleanup proof.main checkout or base ref was updated or verified successfully, identify the resulting main tip when relevant, and say whether the normal checkout path or non-switching base-ref path was used.main refresh was blocked by unrelated dirty changes, call that out as partial local cleanup: include the merge commit, the dirty-main error, which cleanup steps did complete, and which local branch or verification step was intentionally deferred.~/.fin.yaml was checked, whether it parsed successfully, whether a workspace entry matched the non-worktree checkout root, and whether the matched repo-specific final hooks completed or were skipped.local pushed main, state whether the push succeeded. If it intentionally remained local-only, say that explicitly.ag-learn saved the learning note.main, completed retrospective..archive unless the task is actually complete../scripts/check_default_branch.py record. Never merge or finalize a PR whose base is not the repository's origin main branch.HEAD; create a named branch first.gh to local or from local to gh.gh vs local when the argument is omitted and branch PR state clearly determines the context.gh from a no-argument invocation unless the PR belongs to the current branch being finalized, or a heartbeat-derived or active-task-derived PR target has been locked after live PR verification.trigger:merge-pr when GitHub reports the matching PR is already MERGED; use the existing merged state and continue finalization from there.gh repair via fix-pr-conflict or fix-pr cannot restore a mergeable state, stop and report the blockage instead of continuing the finalization flow.local repair via branch sync or rebase cannot restore a mergeable state, stop and report the blockage instead of continuing the finalization flow.merge-pr in gh mode before the matching spec is marked complete and archived, unless an explicit blocker override authorizes landing a mergeable PR while leaving that incomplete spec active and unarchived.local mode.OPEN/BLOCKED shortly after auto-merge was successfully enabled. Use the explicit auto-merge pending state unless the auto-merge request disappears, checks fail, or conflicts appear.gh pr merge --delete-branch local branch-deletion failures caused only by linked worktree attachment as a failed merge when the remote PR already landed.squash or rebase mode to the cleanup script.--execute before every matching repo-specific final hook succeeds.blocked or partial, preserve it and rerun the exact command after resolving the reported blocker.~/.fin.yaml workspace paths against temporary linked worktree roots; match the non-worktree checkout root for the branch's repository.~/.fin.yaml workspace entry. If the additional instructions are ambiguous, destructive, or cannot be verified, stop and report the blocker before cleanup.~/.fin.yaml; a parse failure is a finalization preflight issue unless raw content is clearly non-executable and unambiguous. Never delete a linked worktree while malformed fin config might contain unrun hooks or cleanup instructions.--repo; the script enforces this boundary.main or the safely refreshed local base ref still points behind the landed result unless the user explicitly says not to refresh or verify it.specy convention already present in the workspace.spec.md when the active spec uses a folder schema. Move the entire spec folder so checklist, data, and other sidecars remain attached.ag-learn output just because the task was straightforward; run it and report either the proposals or the explicit no-learning result.fin was run with either an explicit gh / local argument, a heartbeat-derived or active-task-derived PR target, or no argument and a context auto-detected from current-branch PR state../scripts/check_default_branch.py returned exit 0 with status: "pass", matches: true, and every allow field set to true for the exact PR base or local merge target. The JSON record was retained; a missing, malformed, or failed gate stopped finalization.gh mode, any explicit PR number, PR URL, branch target, heartbeat-derived PR target, or active-task-derived PR target was locked before current-branch PR detection and reused for every PR-state, mergeability, merge, automation, and cleanup decision.HEAD, it was converted into a named branch before context detection and landing.main or its base branch before spec archival, unless the matching PR was already merged; any detected conflicts were handled with trigger:fix-pr-conflict, trigger:fix-pr, trigger:sync-branch, or an equivalent local repair flow.$DOCS_ROOT/specs/.archive/; folder specs were moved as whole folders only
when the parent spec was complete, and milestone or sidecar completions inside
still-active parent specs were recorded without archiving the parent.~/.fin.yaml was checked, parsed or explicitly handled as malformed, and any workspace entry matching the non-worktree checkout root was applied before linked-worktree removal.gh mode, the matching PR was checked for an existing MERGED state before attempting merge; trigger:merge-pr has been run after archival only when the PR was not already merged, or the missing-PR condition was reported explicitly.MERGED, mergedAt, and mergeCommit verification.MERGED or a reported terminal blocker. A verified merge resumed the normal refresh, containment proof, cleanup, Linear, and retrospective steps.local mode, the completed branch has been merged into local main or verified as already landed there.ready or noop; its execution returned complete or noop with path, registration, and local branch absent plus base-containment proof true. No global worktree prune was run, and unrelated worktrees and branches were left untouched.main containing the PR merge commit, not branch ancestry alone.main checkout was checked for concurrent tracked or untracked changes immediately before refresh, then refreshed or verified to include the landed work before the task was reported complete; otherwise the required partial local cleanup state was reported.$ag-learn has been run.main refresh or verification result.development
Create, rename, audit, or close a tracked Codex task whose turns and status are persisted in the local thread ledger. Only use when directly invoked.
testing
Manage Git preflight, branch and worktree creation, and completed-work cleanup. Use when explicitly invoked.
databases
Automatically use for durable knowledge, configured project-context lookup, and schema-backed artifact layouts.
content-media
Create, update, or optimize skills and SKILL.md content.