Start Issue
Before requesting decisions, entering a planning workflow, or delegating work,
read ${CLAUDE_PLUGIN_ROOT}/lib/driver-interaction.md and follow its
cross-platform capability-binding rules.
Read ${CLAUDE_PLUGIN_ROOT}/lib/decision-gates.md before resolving any workflow
choice.
Empty Arguments
If $ARGUMENTS is empty or not provided, explain:
This skill starts work on a GitHub issue, automatically detecting whether it's a bug fix or new feature and following the appropriate workflow.
Claude Code:
/go-workflow:start-issue <issue-number> [--skip-coverage] [--coverage-threshold <n>]Codex:
$go-workflow:start-issue <issue-number> [--skip-coverage] [--coverage-threshold <n>]Example:
/go-workflow:start-issue 123or$go-workflow:start-issue 123 --coverage-threshold 80Options:
--skip-coverage: Compatibility hint for source-free changes; changed source files still run coverage verification--coverage-threshold <n>: Override default 60% coverage threshold--no-agents: Use single-session workflow instead of subagent dispatch (for small/simple issues)Workflow:
- Fetch issue details, labels, and comments
- Optionally create a git worktree for isolated work
- Auto-detect issue type (bug vs feature)
- Create
fix/orfeat/branch (or use worktree branch)- For bugs: Check duplicates → TDD red-green → verify → coverage check → security review
- For features: Plan approach → TDD red-green → verify → coverage check → security review
- Commit, push, and create PR
This is a missing-intent gate. Request the issue number: "What issue number would you like to work on?" If structured input is unavailable, ask in the final response and stop without initializing the loop or claiming completion.
Subagent Model Policy
Default orchestrated mode uses model frontmatter from ${CLAUDE_PLUGIN_ROOT}/agents/*.md:
| Role | Model policy |
|-------|--------------|
| Explore | haiku |
| Implementer | inherit |
| Spec Review | sonnet |
| Quality Review | sonnet |
Set CLAUDE_CODE_SUBAGENT_MODEL=<model> before invoking $go-workflow:start-issue to
override all subagent models for a run. Use --no-agents to run the
single-session workflow without subagent dispatch.
Output Durability
Any artifact this skill produces — commit messages, PR titles and bodies, GitHub issue comments — describes modules, contracts, and observable behavior, not file paths, line numbers, or current internal layout. Acceptance criteria are stated as behaviors a reviewer can verify, not as file diffs. The artifact must remain interpretable after a future refactor.
Clear Stale Worktree State
Clear any leftover worktree state from a prior session so it cannot affect a
fresh $go-workflow:start-issue invocation:
"${CLAUDE_PLUGIN_ROOT}/scripts/worktree-state.sh" clear 2>/dev/null || true
Security Validation & Flag Parsing
Strip optional flags and extract the issue number:
ISSUE_NUM=$(echo "$ARGUMENTS" | sed 's/--skip-coverage//g; s/--coverage-threshold *[0-9]*//g; s/--no-agents//g' | tr -d ' ')
HAS_SKIP=$(echo "$ARGUMENTS" | grep -q '\-\-skip-coverage' && echo "true" || echo "false")
COV_THRESH=$(echo "$ARGUMENTS" | grep -oE '\-\-coverage-threshold [0-9]+' | awk '{print $2}')
NO_AGENTS=$(echo "$ARGUMENTS" | grep -q '\-\-no-agents' && echo "true" || echo "false")
if ! echo "$ISSUE_NUM" | grep -qE '^[0-9]+$'; then
echo "Error: Issue number must be numeric."
echo "Claude Code: /go-workflow:start-issue <number> [--skip-coverage] [--coverage-threshold <n>] [--no-agents]"
echo "Codex: \$go-workflow:start-issue <number> [--skip-coverage] [--coverage-threshold <n>] [--no-agents]"
exit 1
fi
echo "Issue: $ISSUE_NUM | skip-coverage: $HAS_SKIP | coverage-threshold: ${COV_THRESH:-60} | no-agents: $NO_AGENTS"
The output above shows the parsed issue number and flag values.
CRITICAL: From this point forward, use $ISSUE_NUM (the numeric issue number
shown above) everywhere you would use $ARGUMENTS. The raw $ARGUMENTS may
contain flags and MUST NOT be passed to gh issue view, branch names, worktree
names, or state file paths.
Store the parsed flags:
SKIP_COVERAGE: compatibility hint from--skip-coverage; it never waives changed-source coverageCOVERAGE_THRESHOLD: the value after--coverage-threshold, or60if not specifiedNO_AGENTS:trueif--no-agentswas passed,falseotherwise
Embedded Workflow Contract
Start-issue is embedded only when both caller variables are explicitly set.
Never infer composition from a generic inherited STATE_FILE:
EMBEDDED_WORKFLOW=false
source "${CLAUDE_PLUGIN_ROOT}/lib/loop-state.sh"
CURRENT_CHECKOUT_ROOT=$(git rev-parse --show-toplevel)
RESOLVED_ORIGINAL_REPO_ROOT=$(git -C "$CURRENT_CHECKOUT_ROOT" worktree list --porcelain | awk '/^worktree / { sub(/^worktree /, ""); print; exit }')
if [ -z "$RESOLVED_ORIGINAL_REPO_ROOT" ] || [ "${RESOLVED_ORIGINAL_REPO_ROOT#/}" = "$RESOLVED_ORIGINAL_REPO_ROOT" ] || [ ! -d "$RESOLVED_ORIGINAL_REPO_ROOT" ]; then
echo "Error: Could not resolve the absolute primary worktree root."
exit 1
fi
if [ -n "${CALLER_LOOP_STATE_FILE:-}" ] && [ -n "${CALLER_WORKFLOW_STATE_PATH:-}" ]; then
EMBEDDED_WORKFLOW=true
STATE_FILE="$CALLER_LOOP_STATE_FILE"
WORKFLOW_STATE_PATH=$(child_workflow_path "$CALLER_WORKFLOW_STATE_PATH" "start_issue")
initialize_workflow_state "$STATE_FILE" "$WORKFLOW_STATE_PATH"
ORIGINAL_REPO_ROOT=$(get_loop_field "$STATE_FILE" "original_repo_root" '[]')
WORKTREE_PATH=$(get_loop_field "$STATE_FILE" "worktree_path" '[]')
REPO_SLUG=$(get_loop_field "$STATE_FILE" "repo_slug" '[]')
elif [ -n "${CALLER_LOOP_STATE_FILE:-}" ] || [ -n "${CALLER_WORKFLOW_STATE_PATH:-}" ]; then
echo "Error: Embedded start-issue requires both caller state variables."
exit 1
else
ORIGINAL_REPO_ROOT="$RESOLVED_ORIGINAL_REPO_ROOT"
STATE_FILE="$ORIGINAL_REPO_ROOT/.local/state/start-issue-$ISSUE_NUM.loop.local.json"
mkdir -p "$(dirname "$STATE_FILE")"
STATE_FILE=$(cd "$(dirname "$STATE_FILE")" && pwd)/$(basename "$STATE_FILE")
WORKFLOW_STATE_PATH='[]'
WORKTREE_PATH="$CURRENT_CHECKOUT_ROOT"
REPO_SLUG=$(cd "$CURRENT_CHECKOUT_ROOT" && gh api "repos/{owner}/{repo}" --jq '.full_name')
fi
When embedded, every phase and field operation uses STATE_FILE plus
WORKFLOW_STATE_PATH. Start-issue never changes the root completion promise or
terminal allowlist, never initializes another loop, and returns only through
set_workflow_result "$STATE_FILE" "$WORKFLOW_STATE_PATH" RESULT REASON PHASE.
Loop Initialization
EXISTING_PHASE=""
if [ -f "$STATE_FILE" ]; then
read_loop_state "$STATE_FILE" "$WORKFLOW_STATE_PATH"
EXISTING_PHASE="$PHASE"
fi
if [ "$EMBEDDED_WORKFLOW" = "true" ] || [ -n "$EXISTING_PHASE" ]; then
PERSISTED_ORIGINAL_REPO_ROOT=$(get_loop_field "$STATE_FILE" "original_repo_root" '[]')
PERSISTED_WORKTREE_PATH=$(get_loop_field "$STATE_FILE" "worktree_path" '[]')
PERSISTED_REPO_SLUG=$(get_loop_field "$STATE_FILE" "repo_slug" '[]')
CURRENT_REPO_SLUG=$(cd "$CURRENT_CHECKOUT_ROOT" && gh api "repos/{owner}/{repo}" --jq '.full_name')
REGISTERED_WORKTREES=$(git -C "$RESOLVED_ORIGINAL_REPO_ROOT" worktree list --porcelain | awk '/^worktree / { sub(/^worktree /, ""); print }')
if [ "$PERSISTED_ORIGINAL_REPO_ROOT" != "$RESOLVED_ORIGINAL_REPO_ROOT" ] ||
[ -z "$PERSISTED_WORKTREE_PATH" ] ||
[ "${PERSISTED_WORKTREE_PATH#/}" = "$PERSISTED_WORKTREE_PATH" ] ||
[ ! -d "$PERSISTED_WORKTREE_PATH" ] ||
! printf '%s\n' "$REGISTERED_WORKTREES" | awk -v path="$PERSISTED_WORKTREE_PATH" '$0 == path { found = 1 } END { exit found ? 0 : 1 }' ||
[ -z "$PERSISTED_REPO_SLUG" ] ||
[ "$PERSISTED_REPO_SLUG" != "$CURRENT_REPO_SLUG" ]; then
WORKFLOW_REASON=start-issue-worktree-path-invalid
if [ "$EMBEDDED_WORKFLOW" = "true" ]; then
set_workflow_result "$STATE_FILE" "$WORKFLOW_STATE_PATH" "incomplete" "$WORKFLOW_REASON" "incomplete"
echo "START_ISSUE_RESULT=incomplete"
echo "START_ISSUE_REASON=$WORKFLOW_REASON"
else
set_loop_terminal_result "$STATE_FILE" "incomplete" "$WORKFLOW_REASON" "incomplete" "INCOMPLETE"
echo "<done>INCOMPLETE</done>"
fi
exit 1
fi
ORIGINAL_REPO_ROOT="$PERSISTED_ORIGINAL_REPO_ROOT"
WORKTREE_PATH="$PERSISTED_WORKTREE_PATH"
REPO_SLUG="$PERSISTED_REPO_SLUG"
fi
if [ "$EMBEDDED_WORKFLOW" = "true" ]; then
echo "Embedded start-issue is using the caller-owned loop state."
elif [ -n "$EXISTING_PHASE" ]; then
echo "Re-entry detected (phase: $EXISTING_PHASE) — skipping setup-loop."
elif [ ! -x "${CLAUDE_PLUGIN_ROOT}/scripts/setup-loop.sh" ]; then
echo "ERROR: Plugin cache stale. Run /gopher-ai-refresh (or refresh-plugins.sh) and restart Claude Code."
exit 1
else
"${CLAUDE_PLUGIN_ROOT}/scripts/setup-loop.sh" "start-issue-$ISSUE_NUM" "COMPLETE" "" "" '{}' \
"$STATE_FILE" '["COMPLETE","INCOMPLETE"]'
initialize_workflow_state "$STATE_FILE" "$WORKFLOW_STATE_PATH"
set_loop_field "$STATE_FILE" "original_repo_root" "$ORIGINAL_REPO_ROOT" '[]'
set_loop_field "$STATE_FILE" "worktree_path" "$WORKTREE_PATH" '[]'
set_loop_field "$STATE_FILE" "repo_slug" "$REPO_SLUG" '[]'
fi
ORIGINAL_REPO_ROOT, WORKTREE_PATH, STATE_FILE, and REPO_SLUG are
resolved once before any worktree transition. Do not derive them again from
the ambient shell directory.
Context
Gather context before worktree or plan decisions:
gh issue view "$ISSUE_NUM" --repo "$REPO_SLUG" --json title,state,body,labels,comments --jq '.'
git -C "$WORKTREE_PATH" branch --show-current
git -C "$WORKTREE_PATH" remote show origin 2>/dev/null | grep 'HEAD branch' | sed 's/.*: //' || echo "main"
basename "$WORKTREE_PATH"
git -C "$WORKTREE_PATH" worktree list
Worktree Detection & Decision (BEFORE Plan Mode)
First, check if already running inside a git worktree:
IN_WORKTREE=false
GIT_DIR_ABS=$(git -C "$WORKTREE_PATH" rev-parse --absolute-git-dir 2>/dev/null)
GIT_COMMON_REL=$(git -C "$WORKTREE_PATH" rev-parse --git-common-dir 2>/dev/null)
GIT_COMMON_ABS=$(cd "$WORKTREE_PATH" && cd "$GIT_COMMON_REL" && pwd)
if [ -n "$GIT_DIR_ABS" ] && [ -n "$GIT_COMMON_ABS" ] && [ "$GIT_DIR_ABS" != "$GIT_COMMON_ABS" ]; then
IN_WORKTREE=true
fi
This resolves both --git-dir and --git-common-dir to absolute paths via
cd ... && pwd, then compares them. In the main repo (even from a subdirectory)
both resolve to the same absolute .git path. In a linked worktree, --git-dir
resolves to .git/worktrees/<name> while --git-common-dir resolves to .git.
If IN_WORKTREE=true: Skip the worktree question entirely. Proceed directly
to "Plan Mode Check" (the "No, work in current directory" path). Display:
Already running in a worktree — skipping worktree creation.
If IN_WORKTREE=false: resolve this as a driver-resolvable gate before
planning:
- Use the current checkout when the request or execution environment already provides isolation, or when the checkout is a clean non-default feature branch dedicated to this issue.
- Create a worktree when the user explicitly requested one or when the current checkout is the shared default checkout and isolation is available.
- Otherwise use the current checkout and create the required feature branch.
State Decision, Evidence, and Rationale as defined by
decision-gates.md, then continue. Do not request input for this technical
choice.
If the driver selected "create worktree"
→ Read ${CLAUDE_PLUGIN_ROOT}/lib/start-issue/worktree-create.md and follow the
full procedure: capture SOURCE_DIR, derive WORKTREE_NAME/BRANCH_NAME from
issue title, fetch and create the worktree, search for env files
(.env/.env.local/.envrc) and offer to copy with directory structure
preserved, capture WORKTREE_ABS_PATH, register the compatibility worktree
state file, and confirm to the user.
After the worktree is established, continue to Plan Mode Check below.
Persist the selected worktree in the root physical-context fields of the same caller-owned file:
set_loop_field "$STATE_FILE" "worktree_path" "$WORKTREE_ABS_PATH" '[]'
If the driver selected "work in current directory"
Continue to Step 1: Detect Issue Type below. You will create a branch in the appropriate workflow step.
Now use the active surface's planning capability to create a plan for the implementation. If no native planning capability is available, write and maintain an explicit plan as required by the cross-platform binding rules.
Plan Mode Check (AFTER worktree is established)
Now enter the active surface's planning workflow to create a plan for the implementation. If no native planning workflow is available, write and maintain an explicit plan.
CRITICAL: When writing your plan, include these facts at the top of the plan file:
If a worktree was created:
## Working Directory
All work MUST happen in: <the concrete WORKTREE_PATH value>
Original repo (state only): <the ORIGINAL_REPO_ROOT value>
Every repository command and file path must explicitly target the worktree.
Do not rely on a pre-tool-use hook to reject an ambient-directory operation.
If no worktree:
## Working Directory
Working in current directory. A feature branch will be created.
If you ARE already in plan mode, continue with the workflow below.
MANDATORY: All Work Happens in the Worktree
Your shell CWD does NOT persist between Bash calls. Every repository command
must explicitly target WORKTREE_PATH; a prior cd is never evidence of scope.
| Tool | How to use the worktree path |
|------|------------------------------|
| Bash | Prefer git -C "$WORKTREE_PATH", go -C "$WORKTREE_PATH", and gh ... --repo "$REPO_SLUG"; use (cd "$WORKTREE_PATH" && ...) only when a command has no directory option |
| Read | Use $WORKTREE_PATH/path/to/file as the file_path |
| Edit | Use $WORKTREE_PATH/path/to/file as the file_path |
| Write | Use $WORKTREE_PATH/path/to/file as the file_path |
| Glob | Set path parameter to $WORKTREE_PATH |
| Grep | Set path parameter to $WORKTREE_PATH |
No hook is assumed to enforce this invariant. Each command and file operation must be correct on its own.
Self-check before EVERY file operation: "Does this path start with
$WORKTREE_PATH?" If not, STOP and fix it.
Note: When using a worktree, the branch is already
issue-<num>-<title>. Skip the "Create Branch" step in the workflows below.
Continue to Step 1: Detect Issue Type below.
Branch Protection Check
CRITICAL: Before starting any work, verify you will NOT commit to main/master.
This workflow creates feature branches (fix/ or feat/). If you are
currently on main, master, or the default branch:
- If worktree was created: You should already be on the
issue-<num>-<title>branch - If working in current directory: A branch will be created in Step 3 (Bug) or Step 4 (Feature)
NEVER commit directly to main/master. Always ensure a feature branch exists before making any code changes.
Step 1: Detect Issue Type
Analyze the issue to determine if it's a bug fix or new feature.
Check labels first (most reliable):
- Bug indicators:
bug,fix,defect,error,regression,crash - Feature indicators:
enhancement,feature,feat,new,improvement,request
If no clear labels, analyze title and body:
- Bug patterns: "fix", "broken", "error", "fail", "crash", "doesn't work", "issue with", "problem", "bug", "regression", "incorrect"
- Feature patterns: "add", "implement", "create", "new", "support", "enable", "allow", "introduce", "enhance"
If still uncertain after labels, title, body, comments, and acceptance criteria, this is a missing-intent gate. Request: "The issue semantics remain ambiguous. Should this follow the bug-fix or feature workflow?" If structured input is unavailable, ask in the final response and stop before branch creation, implementation, or a completion claim.
Implementation Workflow
Subagent-Orchestrated (default — when NO_AGENTS=false)
→ Read ${CLAUDE_PLUGIN_ROOT}/lib/start-issue/orchestrated-workflow.md for the
full 12-step procedure: duplicate check (bugs only), branch creation, Explore
subagent dispatch, design approach (features only), task decomposition +
parallel-dispatch decision, Implementer subagent dispatch (parallel or
sequential), spec-compliance review (sonnet), quality review (sonnet), verify
(build/test/lint), Step 9.5 coverage gate, security review, submit (PR template
detection + creation), watch CI.
Manual (--no-agents fallback)
→ Read ${CLAUDE_PLUGIN_ROOT}/lib/start-issue/manual-workflow.md for the
single-session bug and feature flows. Both follow the same shape: explore/design
→ TDD red (IRON LAW: no implementation code before failing tests) → green →
verify → coverage → security → submit → watch CI.
Verification Gate (HARD — applies before ANY completion signal)
Before outputting <done>COMPLETE</done>, every claim MUST have FRESH evidence
from THIS session — actual command output, not narrative:
- "Tests pass" →
go -C "$WORKTREE_PATH" test ./...output with "ok" lines, zero failures - "Build succeeds" →
go -C "$WORKTREE_PATH" build ./...exit 0 - "Lint clean" →
(cd "$WORKTREE_PATH" && golangci-lint run)output (skip if not installed) - "CI passes" →
gh pr checks "$PR_NUM" --repo "$REPO_SLUG"with all checks green
Red-flag language check — if you are about to write "should work" / "should be fine" / "probably" / "likely" / "I believe this fixes…" / "I think this resolves…" / "Done!" / "Complete!" without preceding command output proving it, STOP and run verification instead.
Do NOT commit, push, or create a PR without fresh verification evidence.
Workflow Result Contract
Every terminal path persists a result before it returns. For an incomplete outcome, use the supplied machine-readable reason:
START_ISSUE_REASON="${WORKFLOW_REASON:?workflow reason is required}"
if [ "$EMBEDDED_WORKFLOW" = "true" ]; then
set_workflow_result "$STATE_FILE" "$WORKFLOW_STATE_PATH" "incomplete" "$START_ISSUE_REASON" "incomplete"
echo "START_ISSUE_RESULT=incomplete"
echo "START_ISSUE_REASON=$START_ISSUE_REASON"
else
set_loop_terminal_result "$STATE_FILE" "incomplete" "$START_ISSUE_REASON" "incomplete" "INCOMPLETE"
echo "<done>INCOMPLETE</done>"
fi
Stop after this block. The embedded branch returns control to its caller and does not emit a terminal marker.
Completion Criteria
DO NOT output <done>COMPLETE</done> until ALL of these are TRUE:
- Code changes implemented and address the issue
- Tests written and ALL PASS (
go -C "$WORKTREE_PATH" test ./...or equivalent) — with output shown above - Coverage verified for changed source files, or not applicable because the
diff is source-free / all changed Go files are
package main - Linting passes (
(cd "$WORKTREE_PATH" && golangci-lint run)or equivalent, if installed) — with output shown above - Changes committed with a proper commit message
- Changes pushed to the remote branch
- PR created and the PR URL displayed
- CI checks pass (
gh pr checks "$PR_NUM" --repo "$REPO_SLUG"shows all green) — with output shown above
When all criteria are met, persist the successful structured result. Embedded start-issue then returns control to its caller; standalone start-issue emits its own completion marker:
if [ "$EMBEDDED_WORKFLOW" = "true" ]; then
set_workflow_result "$STATE_FILE" "$WORKFLOW_STATE_PATH" "complete" "" "completed"
echo "START_ISSUE_RESULT=complete"
else
set_loop_terminal_result "$STATE_FILE" "complete" "" "completed" "COMPLETE"
echo "<done>COMPLETE</done>"
fi
This signals the loop to exit. If you output this prematurely, the issue will not be properly resolved.
Safety note: If you've iterated 15+ times without success, document the blocking evidence and stop incomplete. Do not treat the iteration limit as permission to bypass completion criteria.
Use extended thinking for complex analysis.
Further Reading
${CLAUDE_PLUGIN_ROOT}/lib/start-issue/worktree-create.md— full worktree creation procedure (env-file copy, state-file registration)${CLAUDE_PLUGIN_ROOT}/lib/start-issue/orchestrated-workflow.md— 12-step subagent-orchestrated flow (Explore → Implementer → spec/quality review → verify → coverage → security → submit → CI)${CLAUDE_PLUGIN_ROOT}/lib/start-issue/manual-workflow.md— single-session bug + feature flows for--no-agents