tusk

star 0

Get the most important task that is ready to be worked on

gioe By gioe schedule Updated 5/19/2026

name: tusk description: Get the most important task that is ready to be worked on

Tusk Skill

The primary interface for working with tasks from the project task database (via tusk CLI). Use this to get the next task, start working on it, and manage the full development workflow.

Use /create-task for task creation — handles decomposition, deduplication, criteria, and deps. Use tusk task-insert only for bulk/automated inserts.

Setup: Discover Project Config

Before any operation that needs domain or agent values, run:

tusk config

This returns the full config as JSON (domains, agents, task_types, priorities, complexity, etc.). Use the returned values (not hardcoded ones) when validating or inserting tasks.

Commands

Get Next Task (default - no arguments)

Finds the highest-priority task that is ready to work on (no incomplete dependencies), opens a session for it, flips its status to In Progress, opens a skill-run row for cost tracking, and returns the same JSON blob documented under "Begin Work on a Task" below — all in one call.

tusk task-start --force --skill tusk

The --force flag bypasses the zero-criteria guard only (emits a warning rather than hard-failing). It does not bypass dep blocking or unresolved external blockers — those are separate guards. To bypass an unmet blocks-type dependency, pass --force-deps (use sparingly — blocks deps exist for a reason). The --skill tusk flag opens a skill_runs row attributed to this task; run_id is returned under skill_run.run_id in the JSON — capture it for the cancel/finish calls later.

Empty backlog: If the command exits with code 1, the backlog has no ready tasks. Check why:

tusk -header -column "SELECT status, COUNT(*) as count FROM tasks GROUP BY status"
  • If there are no tasks at all (or all are Done): inform the user the backlog is empty and suggest running /create-task to add new work.
  • If there are To Do tasks but all are blocked: inform the user and suggest running /tusk blocked to see what's holding them up.
  • If there are In Progress tasks: inform the user and suggest running /tusk wip to check on active work.

Do not suggest /groom-backlog or /retro when there are no ready tasks — those skills require an active backlog or session history to be useful.

On success, the JSON blob's task.id is the task you just started and skill_run.run_id is the open skill-run row. Immediately proceed to step 1b of the "Begin Work on a Task" workflow — do not wait for additional user confirmation.

Begin Work on a Task (with task ID argument)

When called with a task ID (e.g., /tusk 6), begin the full development workflow. When called with no argument, the "Get Next Task" step above has already run tusk task-start --force --skill tusk for you — skip Step 1 entirely and pick up at Step 1b (Workflow routing), using the JSON blob and the skill_run.run_id you already captured.

Follow these steps IN ORDER:

  1. Start the task and begin cost tracking — fetch details, check progress, create/reuse session, set status, and open the skill-run row in one call:

    tusk task-start <id> --force --skill tusk
    

    The --force flag bypasses the zero-criteria guard only (emits a warning rather than hard-failing) — it does not bypass dep blocking or unresolved external blockers. If the task has unmet blocks-type dependencies, the call exits 2 with the blocker list; pass --force-deps to bypass that guard with a warning (use sparingly — blocks deps exist for a reason). The --skill tusk flag opens a skill_runs row so this session's spend can be attributed to the task. This returns a JSON blob with these keys:

    • task — full task row (summary, description, priority, domain, assignee, etc.)
    • progress — array of prior progress checkpoints (most recent first). If non-empty, the first entry's next_steps tells you exactly where to pick up. Skip steps you've already completed (a task workspace may already be recorded, some commits may already be made). Use git log --oneline in the task workspace to see what's already been done.
    • criteria — array of acceptance criteria objects (id, criterion, source, is_completed, criterion_type, verification_spec). These are the implementation checklist. Work through them in order during implementation. Mark each criterion done (tusk criteria done <cid>) as you complete it — do not defer this to the end. Non-manual criteria (type: code, test, file) run automated verification on done; use --skip-verify if needed. If the array is empty, proceed normally using the description as scope.
    • session_id — the session ID to use for the duration of the workflow (reuses an open session if one exists, otherwise creates a new one)
    • skill_run{run_id, skill_name, started_at, task_id} for the skill-run row opened by --skill. Capture skill_run.run_id — it's referenced by every exit path below.

    Hold onto session_id from the JSON — it will be passed to tusk merge in step 12 to close the session. Do not pass it to tusk task-done; use tusk merge for the full finalization sequence.

    Early-exit cleanup: If any step below causes the skill to stop before reaching the final /retro invocation in Step 12, first call tusk skill-run cancel <run_id> to close the open row, then stop. Otherwise the row lingers as (open) in tusk skill-run list forever. The explicit cancel calls below cover the known post-start early-exit paths; if you hit an unexpected bail-out, cancel before returning.

    Pre-start exits don't need cancel. If tusk task-start --force --skill tusk exits 1 (empty backlog — "No ready tasks found") or exits 2 (task not found, already Done, has unmet blocks-deps without --force-deps, has open external blockers, or missing criteria without --force), the skill-run row is never opened, so there is no run_id to cancel. Just stop.

1b. Workflow routing — If the task's workflow field (from the task object in step 1) is non-null, the task uses a custom workflow instead of the default development cycle. Look up the corresponding skill:

Read file: .agents/skills/<workflow>/SKILL.md

If the file exists, cancel the /tusk skill-run (the handoff skill will open its own run) and stop following the steps below, following that skill's instructions instead, passing the task ID and session_id from step 1:

tusk skill-run cancel <run_id>

If the file does not exist, log a warning ("Workflow '' not found — falling back to default development cycle") and continue with step 2 (no cancel — the /tusk run stays open for the rest of the default flow).

  1. Create or reuse the task-owned workspace IMMEDIATELY:

    tusk task-worktree create <id> <brief-description-slug>
    

    This creates a recorded task workspace and feature branch, or returns the existing recorded workspace for the task. Parse the JSON response, then cd into workspace_path before exploring, editing, testing, committing, or merging. If created is false, continue from that existing workspace; do not create another branch or overlapping worktree. If you are already in the returned workspace_path, stay there.

    For LaughTrack scraper work, task-worktree create also links apps/scraper/.venv from the primary checkout into the task workspace when the primary checkout has that venv. This makes acceptance commands such as cd apps/scraper && .venv/bin/python3 -m pytest ... work in task-owned worktrees without manual setup. If the primary checkout has no scraper venv, create it there first with cd apps/scraper && make setup-venv.

    For LaughTrack web work, task-worktree create links ignored local resources from the primary checkout when they exist: apps/web/node_modules, apps/web/.env.local, and apps/scraper/.env. This makes cd apps/web && npm run type-check and npm run dev work in task-owned worktrees without manual symlink setup, while leaving any existing local files in the task workspace untouched.

    If you need to inspect recorded workspaces before deciding where to continue, run:

    tusk task-worktree list --format json
    

    Use the row for this task when present. The recorded workspace is the normal task boundary; do not use tusk branch for the default /tusk workflow.

    Deliverable check: If deliverable_check_needed from step 1 is true, run:

    tusk check-deliverables <id>
    

    (Replace <id> with the actual task ID.) This command checks all branches for commits referencing the task and, if none are found, scans the task description and criteria for referenced file paths and tests whether they exist on disk. Act on the recommendation field:

    • "commits_found"[TASK-<id>] commits exist on a non-default branch (typically a stale feature branch from a prior session). Switch to it or cherry-pick the relevant commits before proceeding to Explore.
    • "merged_not_closed"[TASK-<id>] commits already exist on the default branch AND their diff overlaps with files referenced in this task (or there is no scope signal to compare against). Treat as the orphaned-task case: work was merged without being finalized through tusk merge. The SHAs are listed in default_branch_commits. Skip implementation entirely. Mark all criteria done with --skip-verify, then jump straight to step 12 to close out the session — tusk merge will detect the already-merged state and finalize without re-merging.
    • "merged_not_closed_low_confidence"[TASK-<id>] commits exist on the default branch but their diff (listed in default_branch_commit_files) does NOT overlap with files referenced in this task's description / acceptance criteria / verification specs, NOR with files modified on any [TASK-<id>] commit on a feature branch. This is the prefix-match false-positive case (issue #606, original incident TASK-1691): another task's commit was likely tagged with this task's [TASK-N] prefix by mistake. Verify before acting — inspect each commit listed in default_branch_commits (git show <sha>) and confirm whether it actually represents this task's work. If yes, treat as merged_not_closed (skip implementation, jump to step 12). If no, ignore the on-default commits and proceed normally with Explore → Implement as if the recommendation were implement_fresh.
    • "mark_done" — no commits, but deliverable files listed in files already exist on disk. Mark all criteria done with --skip-verify and proceed directly to step 9 (commit + merge) without reimplementing.
    • "criteria_complete_no_commits" — every non-deferred acceptance criterion is already marked is_completed=1, but there are no [TASK-<id>] commits anywhere AND no deliverable files on disk. This is a salvage / converged-work / speculative-mark signal (issue #578, original incident TASK-1714): a prior session marked criteria done without producing any committed deliverable. Common causes: (1) lost-work — a prior agent did real work but couldn't commit cleanly (dirty worktree, branch protection, bundled unrelated changes on a salvage branch); (2) convergent-evolution — separate tasks effectively achieved the goal, so no fresh commits are needed for THIS task; (3) speculative pre-marking — criteria were marked done at the start of a prior session without backing code. Do NOT silently proceed as implement_fresh. Instead: (a) read the task's progress notes via tusk task-get <id> and inspect any next_steps references; (b) git branch -a | grep TASK-<id> for stale branches and inspect their diff against the default branch (git log <branch>..origin/<default> and git show <sha>) to determine whether the work is obsolete vs. still relevant; (c) surface the options to the user — re-implement (proceed with Explore → Implement as if implement_fresh), accept-as-converged (close via tusk abandon <id> --reason completed --note "<rationale referencing the converging task or commits>"), or abandon (close via tusk abandon <id> --reason wont_do --note "..."). Do not pick the path unilaterally.
    • "implement_fresh" — no commits, no deliverable files found, and at least one non-deferred criterion is still incomplete (or the task has no criteria at all). Proceed normally and implement from scratch.
  2. Determine the best subagent(s) based on:

    • Task domain
    • Task assignee field (often indicates the right agent type)
    • Task description and requirements
  3. Confirm failure — Run the failing tests before exploring any code when the task is about fixing an existing failure. This confirms the bug still exists and avoids wasted investigation.

    When to run this step:

    • task_type: bug → always run
    • task_type: test AND the summary/description indicates fixing a failing or flaky test → run
    • task_type: test AND the summary/description indicates writing new tests (no existing failure to reproduce, e.g. "Add tests for X", "Write test suite for Y") → skip this step entirely and proceed to Explore
    • All other task types (feature, chore, docs, etc.) → skip
    1. Check the task description and acceptance criteria for specific test commands or test names to run.
    2. If specific tests are named, run them directly. Otherwise, use tusk test-detect to find the project's test command, then run the most relevant subset.
    3. If tests pass: the issue may already be fixed or the description may be inaccurate — run tusk skill-run cancel <run_id>, surface this to the user, and stop before investigating further.
    4. If tests fail: capture the failure output. Use it as the primary diagnostic anchor in step 5 (Explore).
  4. Explore the codebase before implementing — use a sub-agent to research:

    • What files will need to change?
    • Are there existing patterns to follow?
    • What tests already exist for this area?
    • For each file you plan to modify, grep it for keywords related to the feature (e.g., the concept name, the config key, the resource type). If a helper function already exists that covers what you're about to write, use it instead of duplicating the logic.

    Report findings before writing any code.

5b. Scope check — only implement what the task describes. The task's summary and description fields define the full scope of work for this session. If the description references or links to external documents (evaluation docs, design specs, RFCs), treat them as background context only — do not implement items from those docs that go beyond what the task's own description asks for. Referenced docs often describe multi-task plans; implementing the entire plan collapses future tasks into one PR and defeats dependency ordering.

  1. Delegate the work to the chosen subagent(s).

  2. Implement, commit, and mark criteria done. Work through the acceptance criteria from step 1 as your checklist — one commit per criterion is the default. For each criterion in order:

    1. Implement the changes that satisfy it

    2. Commit and mark the criterion done atomically using tusk commit --criteria:

      tusk commit <id> "<message>" "<file1>" ["<file2>" ...] --criteria <cid>
      

      An alternative -m flag form is also supported (useful when file paths come first):

      tusk commit <id> "<file1>" ["<file2>" ...] -m "<message>" --criteria <cid>
      

      This runs tusk lint (advisory — never blocks), stages the listed files, commits with the [TASK-<id>] <message> format and Co-Authored-By trailer, and marks the criterion done — all in one call. The criterion is bound to the new commit hash automatically. Duplicate [TASK-N] prefixes in the message are stripped automatically, and bare -- separators are silently ignored.

      Always quote file paths — zsh expands unquoted brackets ([id], [slug]) as glob patterns before the shell passes arguments to tusk commit. Any path component containing [, ], *, ?, or spaces must be wrapped in double quotes (e.g., "apps/api/[id]/route.ts").

      Avoid backticks and unescaped $ in commit messages — even inside double quotes, zsh and bash treat backticks as command substitution and $VAR / $(…) as variable expansion. A message that references code (e.g. explaining a flatMap { $0.isEmpty ? nil : $0 } ?? "US" change) fails with zsh: parse error near '}' before tusk ever sees the args. Drop the backticks (use plain identifiers) or escape every metacharacter — double-quoting alone does not protect them. This is the same class of zsh-quoting hazard as the file-paths note above, just hitting the message argument instead.

      Grouping criteria: 2–3 genuinely co-located criteria (e.g., a schema change and its migration) may share one commit — use one --criteria flag per ID:

      tusk commit <id> "<message>" "<file1>" ["<file2>" ...] --criteria <cid1> --criteria <cid2>
      

      Always include a brief rationale in the commit message when grouping. Never bundle all criteria onto a single end-of-task commit. Exception: if several criteria all land in one new file or one inseparable file-local change, bundle them in one commit with an explicit rationale instead of truncating/restoring the file just to simulate separate commits.

    If a criterion does not apply to the implementation path you chose (e.g., a mutually-exclusive "do X OR document why exempt" pair where you did X), use tusk criteria skip — NOT tusk criteria done --skip-verify:

    tusk criteria skip <cid> --reason "not applicable: chose <chosen-branch> over <skipped-branch>"
    

    done --skip-verify stamps the criterion with HEAD's commit hash, leaking an unrelated commit into the audit trail and triggering "shares commit" warnings between unrelated criteria. skip sets is_deferred=1 with the rationale recorded in deferred_reason; the task-done gate and v_criteria_coverage view exclude deferred criteria automatically, so the task closes cleanly. Reserve done --skip-verify for criteria that ARE satisfied but cannot be auto-verified (the cases below).

    If the task has no git-trackable file changes (e.g., a venv install, a runtime config change, an OS-level operation, or a DB-only deliverable like tusk conventions update / tusk lint-rule add), skip tusk commit entirely — it requires at least one file argument and will fail with exit code 1 (usage error) if none are provided. Mark criteria done directly:

    tusk criteria done <cid> --skip-verify
    

    Once every criterion is marked done, the feature branch will have no [TASK-<id>] commits to merge — close out via Step 12's tusk abandon <id> --reason completed --note "<rationale>" path rather than tusk merge (which refuses on an empty branch).

    If a criterion requires filing follow-up tasks (typical for investigation/triage tasks whose criteria read "file focused follow-up tasks covering each distinct break"), do NOT call tusk task-insert directly. Dupe-check first so a freshly-filed sibling task isn't immediately superseded by an existing one:

    tusk dupes check "<proposed summary>"
    

    If the check returns a match, amend the existing task (e.g., tusk criteria add <id> "<criterion>" or tusk task-update <id>) instead of creating a new one. If no match is found, prefer /create-task over a raw tusk task-insert/create-task runs the same dedup check, decomposes scope, and applies the project's task conventions in one call. Use tusk task-insert only when scripting bulk inserts where the dedup step has already been done.

    After each tusk commit in foreground mode, run git status --short to confirm your files were staged and committed — a zero-exit commit that produced no diff (e.g. all files were already tracked with no changes) will silently succeed without staging anything.

    Web contract coverage: API and frontend task domains should run both focused web tests and npm run type-check. DTO/schema changes can pass Vitest while still breaking TypeScript contracts in Server Components, Client Components, or shared route types, so the domain gate must catch those regressions before commit or merge.

    If tusk commit fails with pathspec did not match any files (exit code 3, git-add error), first check whether the file was already committed in a prior tusk commit call for this task (e.g., when all changes go into a single file committed with earlier criteria), or whether the file was removed via git rm (which stages the deletion — tusk commit then can't find the path to re-add). In either case, git add && git commit would also fail — just mark the remaining criteria done directly:

    tusk criteria done <cid> --skip-verify
    

    If the error is a genuine pathspec mismatch (not an already-committed file), always pass file paths relative to the repo root (e.g., ios/SomeFile.swift, not SomeFile.swift from inside ios/). If the error persists, fall back to a path-limited commit:

    git add -- "<file1>" ["<file2>" ...]
    git commit -o -- "<file1>" ["<file2>" ...] -m "[TASK-<id>] <message>" --trailer "Co-Authored-By: Codex Sonnet 4.6 <noreply@anthropic.com>"
    

    git commit -o -- <files> limits the commit to the listed paths so unrelated pre-staged changes cannot leak into the task commit. Then mark criteria done with tusk criteria done <cid> --skip-verify as usual.

    If tusk commit fails with pathspec '…' is beyond a symbolic link (exit code 3), the path lives under a symlinked directory that git add refuses to traverse. In tusk's own repo this hits any path under .agents/skills/<name>/, because each skill is a symlink to skills/<name>/. Retry with the real source path:

    tusk commit <id> "<message>" "skills/<name>/SKILL.md" --criteria <cid>
    

    More generally: if ls -la on any parent directory shows it is a symlink, use the link's target path instead.

    If a pre-commit auto-formatter (e.g. black, ruff --fix, prettier, gofmt) rewrites a staged file in-place, tusk commit detects the index/working-tree divergence, re-stages the reformatted content, and retries the commit exactly once — no manual intervention required. If the retry also fails (the formatter produces unstable output on every run), bypass hooks with:

    tusk commit <task_id> "<message>" "<file>" --skip-verify
    

    If the commit removes a file from git tracking (i.e., the staged change is a git rm --cached deletion, not a file modification), do NOT use tusk commit — it retries gitignored paths with git add -f, which re-adds the file and defeats the deletion. Use git commit directly:

    git commit -m "[TASK-<id>] <message>" --trailer "Co-Authored-By: Codex Sonnet 4.6 <noreply@anthropic.com>"
    

    Then mark criteria done with tusk criteria done <cid> --skip-verify.

    If tusk commit exits 6 (blocking lint violation) — the commit did NOT land. A non-advisory lint rule fired (Rule 1 raw sqlite3, Rule 3 hardcoded DB path, Rule 11 bad SKILL.md frontmatter, Rule 16 DB-backed blocking rules, Rules 18/19 MANIFEST drift, Rule 21 multi-trailing-newlines, etc.). The violating rule's output is printed verbatim — fix it, then retry tusk commit. Advisory-only rules (Rule 13 VERSION bump missing, Rule 15 big-bang commits, Rule 17 DB-backed advisory, etc.) still print WARN lines but do NOT exit non-zero and do NOT block. If the violation is a known false positive or pre-existing state you can't resolve in this commit, bypass with --skip-lint (lint only) or widen to --skip-verify (lint, tests, and pre-commit hooks):

    tusk commit <id> "<message>" "<file>" --skip-lint --criteria <cid>
    

    Lint output during commit is now filtered: only rules with violations print — passing rules are suppressed. If the last lint pass was clean, you won't see any lint output at all.

    If tusk commit exits 5 (test_command timeout) — the configured test_command exceeded its timeout and was killed before producing an exit code. The stderr message names the resolved timeout and source. The resolution chain is TUSK_TEST_COMMAND_TIMEOUT env var > config.test_command_timeout_sec in tusk/config.json > default (240s). If the failure is just slow first-run compilation (cold xcodebuild, Bazel cold cache, large Rust compile), retry with a per-invocation override:

    TUSK_TEST_COMMAND_TIMEOUT=600 tusk commit <id> "<message>" "<file>" --criteria <cid>
    

    If the slow path is permanent for this project, raise test_command_timeout_sec in tusk/config.json instead of overriding on every call. Do not blindly raise the timeout when the command genuinely hangs (e.g. waiting on interactive input or a missing dependency) — make the command non-interactive and fix the underlying hang first.

    If tusk commit hard-fails because tests fail (exit code 2 — test_command is set and returned non-zero), first verify the failure is not pre-existing before entering the diagnosis loop:

    Pre-existing failure check — run the tests against HEAD with any local changes safely set aside:

    tusk test-precheck
    

    Or pass an explicit command when the config-resolved one isn't what you want to check against:

    tusk test-precheck --command "<test_command>"
    

    tusk test-precheck resolves the test command from --command, then config.test_command, then tusk test-detect. When the working tree is dirty it stashes local changes under a uniquely-named entry, runs the test against HEAD, and pops that entry by reference — never by top-of-stack. When the working tree is clean it runs the test directly without touching git stash at all. Output is JSON on stdout: {pre_existing, exit_code, test_command, stashed}; the test command's own output is redirected to stderr so programmatic callers can json.loads(stdout) directly. Do not fall back to the raw git stash && … ; git stash pop snippet — when the tree is clean, the empty git stash becomes a no-op and git stash pop will pop a stale foreign entry and silently trash unrelated state. If precheck exits non-zero, it prints a recovery message on stderr (always including the stash message, when one was created) so you can finish the pop manually; it never silently falls through with changes orphaned in the stash list.

    • If pre_existing is true — the failure is pre-existing and unrelated to your changes. Skip the diagnosis loop entirely. Do not attempt to fix tests in files you did not modify during this session. Fall back immediately to:

      git add -- "<file1>" ["<file2>" ...]
      git commit -o -- "<file1>" ["<file2>" ...] -m "[TASK-<id>] <message>" --trailer "Co-Authored-By: Codex Sonnet 4.6 <noreply@anthropic.com>"
      

      Then mark criteria done with tusk criteria done <cid> --skip-verify. The -o -- <files> form is required here too; a plain git commit would include any unrelated paths that were staged before this task.

    • If pre_existing is false — your changes introduced the failure. Proceed with the diagnosis loop below. Do not modify any code until you've completed steps 1–2:

    1. Read the full test output — scroll through the entire failure log. Do not make any code changes until you understand what failed and why.
    2. Trace the root cause — open the relevant source files and identify the exact lines responsible for the failure.
    3. Implement a fix — make the minimal change required to address the root cause.
    4. Retry tusk commit with the same arguments.

    Repeat up to 3 times. If tests still fail after 3 attempts, run tusk skill-run cancel <run_id>, surface the full failure output and a summary of what was tried to the user, then stop — do not continue looping.

    1. Log a progress checkpoint:
    tusk progress <id> --next-steps "<what remains to be done>"
    
    • All commits should be on the feature branch (feature/TASK-<id>-<slug>), NOT the default branch.

    The next_steps field is critical — write it as if briefing a new agent who has zero context. Include what's been done, what remains, decisions made, and the branch name.

    Schema migration reminder: If the commit adds or modifies a migration in bin/tusk-migrate.py (or bumps cmd_init's fresh-DB user_version stamp in bin/tusk), run tusk migrate on the live database immediately after committing.

  3. Review the code locally before considering the work complete.

  4. Verify all acceptance criteria are done before pushing:

    tusk criteria list <id>
    

    If any criteria are still incomplete, address them now. If a criterion was intentionally skipped, note why in the PR description.

  5. Run convention lint (advisory)tusk commit already runs lint before each commit. If you need to check lint independently before pushing:

    tusk lint
    

    Review the output. This check is advisory only — violations are warnings, not blockers. Fix any clear violations in files you've already touched. Do not refactor unrelated code just to satisfy lint.

  6. Run /review-commits — check the review mode first:

    tusk config review
    
    • mode = disabled (or review key missing): skip review, proceed to step 12.
    • mode = ai_only: run /review-commits by following the instructions in:
      Read file: <base_directory>/../review-commits/SKILL.md for task <id>
      

      Warning: Do NOT spawn a pr-review-toolkit:code-reviewer agent directly as a shortcut. That agent receives only a manually reconstructed diff — not the real git diff output — which causes false-positive review findings. The /review-commits skill exists specifically to fetch and pass the real diff verbatim; bypassing it removes that safeguard.

      After /review-commits completes with verdict APPROVED, proceed to step 12. If verdict is CHANGES REMAINING, run tusk skill-run cancel <run_id>, surface the unresolved items to the user, and stop.
  7. Finalize — merge, push, and run retro. Execute as a single uninterrupted sequence — do NOT pause for user confirmation between steps:

    tusk merge <id> --session $SESSION_ID
    

    tusk merge closes the session, merges the feature branch into the default branch, pushes, deletes the feature branch, and marks the task Done. It returns JSON including an unblocked_tasks array. If there are newly unblocked tasks, note them in the retro.

    Already-merged path: If the feature branch was previously merged and deleted (e.g. via a PR that was merged in another session), tusk merge detects this automatically when you are on the default branch — it prints Note: TASK-<id> — no feature branch found; already on '<branch>'. Branch was previously merged., closes the session, pushes, and marks the task Done without re-merging. If tusk merge exits 0 in this scenario, proceed to /retro as normal.

    Diverged branch — rebase fallback: If tusk merge exits non-zero because the feature branch has diverged from the default branch (fast-forward-only merge not possible), run:

    tusk merge <id> --session $SESSION_ID --rebase
    

    --rebase rebases the feature branch onto the default branch before merging. If the rebase produces conflicts, resolve them (git rebase --continue) and retry.

    Not-on-default fallback: If tusk merge exits non-zero with No branch found matching feature/TASK-<id>-* or worktree-TASK-<id>-* and you are NOT on the default branch, switch to the default branch first (git checkout <default_branch>), then retry tusk merge <id> --session <session_id>.

    Sibling-worktree DB fallback: If the default branch is checked out in a sibling worktree and the primary checkout is unusable, run the merge from the sibling worktree while pinning tusk to the primary repo's DB:

    TUSK_PROJECT=<primary_repo_path> tusk merge <id> --session $SESSION_ID --rebase
    

    This is the correct fallback when running tusk merge from the sibling worktree fails with no such table: task_sessions: that worktree has the git state needed for the merge, but tusk resolved its database relative to the sibling CWD. TUSK_PROJECT keeps tusk pointed at the primary repo's project database while git commands operate in the current worktree.

    PR mode: If the project uses PR-based merges (merge.mode = pr in config, or when passing --pr), use:

    tusk merge <id> --session $SESSION_ID --pr --pr-number <N>
    

    This squash-merges via gh pr merge instead of a local fast-forward.

    No-commit closure (wont_do / duplicate / completed): If the task should be closed without shipping any code, use tusk abandon instead of tusk merge:

    tusk abandon <id> --reason wont_do|duplicate|completed --session $SESSION_ID [--note "<rationale>"]
    

    Three reason values are accepted:

    • wont_do — an evaluation/spike whose answer is "don't do it".

    • duplicate — the task turns out to overlap an already-tracked one.

    • completed — the goal was met but no [TASK-N] commits land on the default branch. Two sub-cases:

      • convergent-completion (issue #580): separate work landing on the default branch between filing and pickup already satisfied the goal, so there is nothing left to ship.
      • DB-only deliverable (issue #669): the deliverable is a SQLite row written via a tusk subcommand (tusk conventions update, tusk conventions add, tusk lint-rule add, tusk glossary set-definition, etc.) — the feature branch is intentionally empty because nothing in the working tree changes.

      Pass --note "<rationale>" in both cases and reference the converging task(s)/commit(s) or the DB write performed — tusk abandon records it on task_progress as [abandon: completed] <note>, which is the audit signal that distinguishes this case from a normal tusk merge close (no [TASK-N] commits will be on the default branch for this task either).

    tusk abandon switches off the feature branch, deletes it (force), closes the session, and marks the task Done with the given closed_reason in one call. Refuses if the feature branch has commits not on the default branch — in that case use tusk merge to ship the work, or delete the branch manually if you really want to discard it. The optional --note records the decision rationale on task_progress so the audit trail survives. After tusk abandon exits 0, run /retro exactly as you would after tusk merge.

    After tusk merge (or tusk abandon) exits 0, close out the /tusk skill-run so its cost is captured before /retro starts its own run:

    tusk skill-run finish <run_id>
    

    Then emit the canonical end-of-run summary before handing off to /retro:

    tusk task-summary <id> --format markdown
    

    This prints a single markdown block with the task identity, closed reason, total cost, wall/active duration, diff stats (files changed, lines added/removed, commit count), criteria counts, review pass count, and reopen count. Show it verbatim to the user — do not re-render or summarize it. Runs on both the merge and abandon paths; diff stats are filtered to commits that reference [TASK-<id>] so shared-branch pollution never appears in the numbers.

    Then run /retro immediately — do not ask "shall I run retro?". Invoke it to review the session, surface process improvements, and create follow-up tasks.

Other Subcommands

If the user invoked a subcommand (e.g., /tusk done, /tusk list, /tusk blocked), read the reference file:

Read file: <base_directory>/SUBCOMMANDS.md

Skip this section when running the default workflow (no subcommand argument).

Install via CLI
npx skills add https://github.com/gioe/laughtrack --skill tusk
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator