> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boxd.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Fix-on-issue loop

> Label an issue or PR boxd-fix. Get Claude's commit pushed back, live on *.boxd.sh.

Label any GitHub issue or PR `boxd-fix` — or drop a `/boxd-fix` (or `/boxd-preview --fix`) comment — and you get back Claude's fix: a new PR with a live preview URL for issues, or a commit pushed straight to the existing PR for PRs. Each run gets its own fork, destroyed on PR close.

## How it works

Fix builds on the [preview platform](/preview-environments/per-pr-preview-urls): it reuses the same on-golden listener, fork, branch-sync, and teardown, and adds the Claude + PR step on top. No GitHub Actions, no runner queue, no `BOXD_API_KEY` repo secret.

```
GitHub event ───► https://hooks.<your-golden>.boxd.sh/hooks/<trigger>
                                  │
                                  ▼
                  webhook(8) on your golden VM
                                  │
                  ┌───────────────┴───────────────┐
                  ▼                               ▼
        fix-handler.sh                     teardown (shared)
  (forks golden, syncs the branch,      (destroys the fork when
   runs Claude, pushes/opens the PR)     the PR closes)
```

Three triggers fire the fix handler; teardown is the preview platform's shared hook:

| Trigger                                      | Fires on               | Result                                                              |
| -------------------------------------------- | ---------------------- | ------------------------------------------------------------------- |
| `/boxd-fix` or `/boxd-preview --fix` comment | `issue_comment`        | Fork golden → Claude → new PR (issue) **or** push to PR branch (PR) |
| `boxd-fix` label on an issue                 | `issues.labeled`       | Fork golden → Claude → new PR off the default branch                |
| `boxd-fix` label on a PR                     | `pull_request.labeled` | Fork golden → Claude → push to the PR branch                        |
| PR closed                                    | `pull_request.closed`  | Destroy the matching fork (shared with preview)                     |

Live status lands on the issue/PR as a comment that updates while Claude works (✓ preview VM booted, 🤖 Claude is reading the code, ⏳ opening the PR / pushing the fix). The fork stays alive while the PR is open so reviewers can poke at it. PR close triggers `boxd machine remove`.

The whole thing is wired up by a single Claude Code slash command.

## Set it up

Fix is the agent layer, so set up the chain first: [**`/boxd-setup-golden`**](/preview-environments/set-up-a-golden) → [**`/boxd-setup-preview`**](/preview-environments/per-pr-preview-urls) → **`/boxd-setup-fix`**. With those done, wire fix on top — **two ways**:

<Tabs>
  <Tab title="From your laptop">
    Install the boxd CLI on your laptop (this also drops the skill):

    ```bash theme={"theme":"github-dark"}
    curl -fsSL https://boxd.sh/downloads/install.sh | sh
    ```

    Then run the skill in the repo:

    ```text theme={"theme":"github-dark"}
    /boxd-setup-fix
    ```
  </Tab>

  <Tab title="Inside the golden (in-VM)">
    Open an agent shell on the golden (`boxd connect <golden>`) and run it there —
    every boxd VM already ships the boxd CLI and skill, so there's nothing to install.
    It wires the VM you're on directly, no `boxd machine exec`/API key:

    ```text theme={"theme":"github-dark"}
    /boxd-setup-fix
    ```
  </Tab>
</Tabs>

<Note color="#E05A6D">
  Claude Code only for now — [reach out](mailto:contact@boxd.sh) for Codex, OpenCode, or any other agent.
</Note>

`/boxd-setup-fix` stages the fix handler, registers the three fix webhooks (reusing the preview platform's HMAC secret), creates the `boxd-fix` label, and sets up a headless-browser MCP for screenshot verification. **It needs no Claude token** — every boxd VM ships logged into Claude, and a fork is a full copy of the golden, so Claude runs in the fork already authenticated.

What you'll be asked for, once:

| Step | What             | How                                                                                            |
| ---- | ---------------- | ---------------------------------------------------------------------------------------------- |
| 1    | Confirm the plan | One short message listing the golden, the fix webhooks, and what's already present vs. missing |

That's it — nothing to paste. Label any issue `boxd-fix` and the loop runs.

<Note>
  **Optional — run fixes under a different Claude identity.** By default the fork uses the golden's own inherited Claude login. To use a *different* account (e.g. a dedicated service identity, or a headless golden with no login), run `claude setup-token` in your terminal and paste the printed token into the one-liner the skill prints — it lands at `/etc/boxd-fix/claude.token` and takes precedence over the inherited login. The value never appears in chat.
</Note>

## Patterns

### What the bot actually does

The agent reads the issue/PR body, the linked files, and the project's `AGENTS.md` / `CLAUDE.md`. The app is already running on the forked golden (forks inherit the running state), so for UI changes it takes a "before" screenshot at `https://<fork>.boxd.sh`, makes its changes, verifies them via `chrome-devtools-mcp` (`navigate_page` → `wait_for` → `evaluate_script`), takes an "after" screenshot, commits, and exits.

The handler on the golden then pushes the result:

* **Issue trigger**: force-push to a fresh `boxd-fix/issue-<N>` branch and open a new PR with the preview URL pinned to the top.
* **PR comment / PR label trigger**: push commits directly onto the PR's existing head branch — Claude appears as a co-author on the PR.

### Customizing the prompt

The skill ships a default prompt at `/opt/boxd-platform/claude-prompt.md` on the golden. To override it per-repo, commit `.github/claude-prompts/fix-issue.md` to your repo — the handler picks it up automatically on each trigger after a `git fetch`. Bias the prompt toward your conventions: which tests to run, which files to avoid, which style guide to follow.

### Triggering from a PR comment

`/boxd-fix` on a PR comment runs Claude against that PR's branch (not main), then pushes commits straight onto the PR. Useful for "hey Claude, the heading should be smaller" review feedback.

### Triggering by label on a PR

Labeling a PR `boxd-fix` works the same as a `/boxd-fix` comment — fork → Claude → push to the PR branch.

<Note color="#E05A6D">
  The PR is opened (or commits are pushed) by your user account using a `gh` token persisted on the golden during `/boxd-setup-golden`. No "Allow Actions to create PRs" org toggle needed.
</Note>

## Trust caveat

A fork is a full copy of the golden, so it inherits the golden's **Claude login** (and its `gh` auth) — the same login every boxd VM ships with. If your app on the fork is reachable by untrusted parties, treat those credentials as exposed on it; the cleanup hook destroys forks promptly on PR close, which is the main mitigation. (An optional override token at `/etc/boxd-fix/claude.token`, if you set one, is inherited the same way.) The trade-off vs. the prior GHA-based design: no `BOXD_API_KEY` round-trip, no GHA queue, no runner minutes — Claude auth lives on your own VM instead of GitHub Secrets.

## FAQ

<AccordionGroup>
  <Accordion title="What happens when the agent gets stuck?">
    The fork stays alive until you close the PR. SSH in with `boxd connect <fork-name>`, tail `/var/log/golden-preview/<fork-name>.log` for the full Claude transcript, then run `claude --resume <session_id>` to take over interactively.
  </Accordion>

  <Accordion title="How much does each run cost in compute?">
    A fork is \~160ms, then you pay for the time the VM is up. The preview platform sets forks to idle-suspend, so a typical fix run is a few minutes of active VM time. End-to-end (label → PR opened) we've measured \~2 minutes for a small change.
  </Accordion>

  <Accordion title="What do I need set up first?">
    A golden (`/boxd-setup-golden`) with the preview platform on it (`/boxd-setup-preview`). Fix layers on that — it reuses the preview platform's fork/sync/teardown and adds only the Claude run. A preview-only golden stays free of the Claude token until you run `/boxd-setup-fix`.
  </Accordion>

  <Accordion title="What if the trigger fires but nothing happens?">
    Tail the global handler log: `boxd machine exec <golden> -- 'tail -f /var/log/golden-preview.log'`. The HMAC check is the most common failure — webhook(8) returns 200 with body `Hook rules were not satisfied` on HMAC mismatch (it intentionally doesn't 401, so attackers can't probe for the URL). Re-run `/boxd-setup-fix` to rotate the secret.
  </Accordion>

  <Accordion title="What if I want a different agent?">
    Today the slash command targets Claude Code. For Codex, Cursor, or OpenCode wiring, email `contact@boxd.sh` and we'll prioritize.
  </Accordion>
</AccordionGroup>

## Next

<Columns cols={2}>
  <Card title="Agent sandboxes" icon="https://mintcdn.com/azin/Ax1V0serIwQf0x_2/images/icons/robot.svg?fit=max&auto=format&n=Ax1V0serIwQf0x_2&q=85&s=e91e744c2d3cd5da053167486b6834bf" href="/agents/agent-sandboxes" width="16" height="16" data-path="images/icons/robot.svg">
    The primitive underneath the loop. Hand an agent a full VM.
  </Card>

  <Card title="Fork from a golden" icon="https://mintcdn.com/azin/Ax1V0serIwQf0x_2/images/icons/copy.svg?fit=max&auto=format&n=Ax1V0serIwQf0x_2&q=85&s=f3623fe516eebf87b33b3a1022852286" href="/cloud-dev-boxes/fork-from-a-golden" width="16" height="16" data-path="images/icons/copy.svg">
    Where the warm copy comes from. \~160ms per fork.
  </Card>
</Columns>
