---
name: autofix
description: |
  Automatically fix issues discovered by the replayer skill. Reads issue details from
  issues.yaml, implements the fix in an isolated worktree, runs validation, and creates
  a PR. Designed to run as independent sessions — one per issue — in parallel.
---

# Auto Fix Skill

Fix an issue from `tools/replayer/issues/issues.yaml` and create a PR.

## Prerequisites

- Git repo with no uncommitted changes on the current branch
- `tools/replayer/issues/issues.yaml` with the target issue
- GitHub token at `tools/replayer/.github_token` (for PR creation)

## Usage

```
Fix ISS-0026
Fix ISS-0026 ISS-0028 ISS-0035    # batch: sequential in one session
```

For parallel execution (recommended for multiple issues):

```bash
# Orchestrator creates worktrees and launches sessions
tools/autofix/run.sh ISS-0026 ISS-0028 ISS-0035
```

---

## Execution Phases

### Phase 1: Load Issue

Read `tools/replayer/issues/issues.yaml` and find the target `ISS-NNNN`.

Extract:
- `title`, `description`, `type`, `severity`
- `affects` → list of `{skill, file}` pairs — these are the files to modify
- `fix_plan` → inline text or path to `ISS-NNNN/plan.md`
- `github_issue` → link back to the GitHub issue (for PR body)

If `fix_plan` points to a `plan.md` file, read it. The plan contains full context:
background, observed errors, root cause analysis, concrete fix steps, and verification.

**Abort if:**
- `status` is not `open` (already fixed or in progress)
- `fix_plan` is empty or says "TODO"

### Phase 2: Assess Fixability

Before writing code, assess **scope** and **fixability**.

#### Step 1: Scope Check — Is this issue in autofix range?

Autofix only handles **platform/UX issues** — code that runs the Investibook app itself.
Diagnosis-related issues require domain expertise and regression testing; they are out of scope.

| `affects` target | Scope | Examples |
|-----------------|-------|---------|
| `frontend/components/` | **In scope** — platform UI | ChatPanel, StudioPanel, SourcesPanel |
| `backend/investibook/api/` | **In scope** — platform API | workspaces.py, chat.py (non-acp routes) |
| `backend/investibook/db/` | **In scope** — platform DB | queries.py (schema/query changes) |
| `agentkit/skills/*/SKILL.md` | **Out of scope** — diagnosis flow | Diagnosis decision rules, knowledge |
| `agentkit/skills/*/scripts/` | **Out of scope** — diagnosis tooling | collect_evidence.py, clinic scripts |
| `backend/investibook/core/acp.py` | **Out of scope** — high blast radius | Agent session management |
| `.opencode/skills/*/scripts/` | **Out of scope** — diagnosis tooling | jira_api.py, API helpers |
| `deploy/` | **Out of scope** — infra | Grafana, K8s manifests |

**If ALL `affects` entries are out of scope → skip the issue entirely.**

#### Step 2: Fixability Assessment

For in-scope issues, assess whether they can be safely auto-fixed:

| Signal | Auto-fixable | Needs human review |
|--------|-------------|-------------------|
| Single file in `affects` | Yes | |
| `type: UX` with inline fix_plan | Yes | |
| `type: TOOLING` with plan.md, in-scope files | Yes | |
| Multi-file with cross-dependencies | | Yes — create PR as draft |
| No clear fix_plan | | Skip entirely |

Set `draft = true` for issues that need human review. The PR will be created as a draft.

### Phase 3: Create Branch

```bash
ISSUE_ID="ISS-NNNN"
BRANCH="fix/${ISSUE_ID}"

# If running in worktree (launched by run.sh), branch already exists
# If running standalone, create from main
git checkout -b "${BRANCH}" origin/main
```

### Phase 4: Implement Fix

Read all files listed in `affects`. Understand the existing code before modifying.

**Rules:**
- Follow the fix_plan steps exactly — do not improvise beyond what's specified
- Keep changes minimal — only modify what the plan says to modify
- Match existing code style (indentation, naming, patterns)
- Do not add extra comments, docstrings, or "improvements" beyond the fix

**For each file type:**

| File | Approach |
|------|----------|
| `*.tsx` (frontend) | Follow existing component patterns. Use `@tidbcloud/uikit` components. |
| `*.py` (backend API) | Follow FastAPI patterns. Add Pydantic models if new fields needed. |
| `*.py` (backend DB) | Follow existing query patterns. Use migrations for schema changes. |

### Phase 5: Validate

Run relevant checks based on what was changed:

```bash
# Frontend changes
cd frontend && pnpm build 2>&1 | tail -20

# Backend changes
cd backend && python -m py_compile investibook/path/to/file.py
```

If validation fails, fix the issue and re-validate. Max 3 attempts, then abort.

### Phase 6: Commit & Push

```bash
git add <specific files only>
git commit -m "$(cat <<'EOF'
fix(ISS-NNNN): <title from issues.yaml>

<one-line description of what was changed>

Closes <github_issue URL>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

git push -u origin "${BRANCH}"
```

### Phase 7: Create PR

```bash
GITHUB_TOKEN=$(cat tools/replayer/.github_token 2>/dev/null || echo "${GITHUB_TOKEN:-$GH_TOKEN}")
REPO="${GITHUB_ISSUE_REPO:-tidbcloud/investibook}"
DRAFT_FLAG=""  # or "--draft" if Phase 2 determined needs human review

# Use GitHub API directly (gh CLI may not be installed)
curl -sS -X POST "https://api.github.com/repos/${REPO}/pulls" \
  -H "Authorization: Bearer ${GITHUB_TOKEN}" \
  -H "Accept: application/vnd.github+json" \
  -d '{
    "title": "fix(ISS-NNNN): <title>",
    "body": "## Summary\n\n<description>\n\n## Fix Plan\n\n<fix_plan summary>\n\n## Validation\n\n- [ ] Build passes\n- [ ] Manual smoke test after deploy\n\nCloses <github_issue>\n\n---\n_Auto-fixed by autofix skill._",
    "head": "fix/ISS-NNNN",
    "base": "main",
    "draft": <true|false>
  }'
```

### Phase 8: Update issues.yaml

On success, update the issue entry:

```yaml
- id: ISS-NNNN
  status: in_progress  # not "fixed" until PR is merged
  fixed_in: "PR#xxx"   # PR number from Phase 7
  fix_source: autofix  # lets TriagePilot distinguish autofix work from manual fixes
```

Commit this change to the same branch and push.

---

## Orchestrator: run.sh

For parallel execution, `run.sh` handles worktree lifecycle:

```bash
#!/bin/bash
# Usage: tools/autofix/run.sh ISS-0026 ISS-0028 ISS-0035
#
# For each issue:
#   1. Create git worktree at .worktree/ISS-NNNN
#   2. Launch Claude Code session in background
#   3. Wait for all sessions to complete
#   4. Clean up worktrees

set -euo pipefail

REPO_ROOT="$(git rev-parse --show-toplevel)"
WORKTREE_DIR="${REPO_ROOT}/.worktree"
PIDS=()
ISSUES=("$@")

if [ ${#ISSUES[@]} -eq 0 ]; then
  echo "Usage: $0 ISS-NNNN [ISS-MMMM ...]"
  exit 1
fi

mkdir -p "${WORKTREE_DIR}"

for ISSUE_ID in "${ISSUES[@]}"; do
  BRANCH="fix/${ISSUE_ID}"
  WT_PATH="${WORKTREE_DIR}/${ISSUE_ID}"

  # Clean up stale worktree if exists
  if [ -d "${WT_PATH}" ]; then
    git worktree remove "${WT_PATH}" --force 2>/dev/null || true
  fi

  # Create worktree with new branch from main
  git worktree add "${WT_PATH}" -b "${BRANCH}" origin/main

  # Copy github token to worktree
  cp "${REPO_ROOT}/tools/replayer/.github_token" "${WT_PATH}/tools/replayer/.github_token" 2>/dev/null || true

  echo "[${ISSUE_ID}] Worktree created at ${WT_PATH}"
  echo "[${ISSUE_ID}] Launching Claude Code session..."

  # Launch session in background
  (
    cd "${WT_PATH}"
    claude -p "Fix ${ISSUE_ID} per tools/replayer/issues/issues.yaml and tools/autofix/SKILL.md" \
      2>&1 | tee "${WORKTREE_DIR}/${ISSUE_ID}.log"
  ) &
  PIDS+=($!)
done

echo ""
echo "Launched ${#PIDS[@]} sessions. Waiting for completion..."
echo ""

# Wait for all sessions
FAILED=0
for i in "${!PIDS[@]}"; do
  ISSUE_ID="${ISSUES[$i]}"
  PID="${PIDS[$i]}"
  if wait "${PID}"; then
    echo "[${ISSUE_ID}] Completed successfully"
  else
    echo "[${ISSUE_ID}] FAILED (exit code $?)"
    FAILED=$((FAILED + 1))
  fi
done

# Clean up worktrees
echo ""
echo "Cleaning up worktrees..."
for ISSUE_ID in "${ISSUES[@]}"; do
  WT_PATH="${WORKTREE_DIR}/${ISSUE_ID}"
  git worktree remove "${WT_PATH}" --force 2>/dev/null || true
  # Keep branch (it's been pushed)
done

echo ""
echo "Done. ${#ISSUES[@]} issues processed, ${FAILED} failed."
echo "Logs: ${WORKTREE_DIR}/*.log"
```

---

## Issue Selection Guide

When asked to "fix all eligible issues", scan `issues.yaml` and select issues where:

1. `status: open` (not fixed, not in_progress)
2. `fix_plan` is not empty
3. All `affects` entries are **in scope** (see Phase 2 Step 1)
4. `type` is in: `UX`, `TOOLING` (platform-level only)

Sort by: `severity` desc, then `type` priority (TOOLING > UX).

**Skip these — out of scope for autofix:**
- Issues where ANY `affects` entry targets diagnosis files (`agentkit/skills/`, `.opencode/skills/`, `collect_evidence.py`, `SKILL.md`)
- Issues where ANY `affects` entry targets `backend/investibook/core/acp.py`
- Issues where ANY `affects` entry targets infra (`deploy/`, Grafana dashboards)
- `type: REASONING`, `KNOWLEDGE`, `FLOW`, `TEMPLATE`, `CROSS-REF` — these are diagnosis-related by definition
- Issues with `affects` pointing to files that don't exist
- Issues where `fix_plan` says "investigate" or "evaluate" (research, not fix)

---

## Regression After Fix

Autofix only targets platform/UX issues, so diagnosis regression is not applicable.

For frontend changes, the build check in Phase 5 is sufficient validation.
For backend changes, compile check + manual smoke test after deploy.
