Learn
Auto-Fix

Auto-Fix

Auto-fix closes the loop between an alert and a pull request. A GitHub channel opens an issue for a new error, a coding agent running in your repository's GitHub Actions investigates it, and a publish step turns the result into a pull request or an analysis comment.

TracePath never runs a model. The agent, its vendor and its API key live in your CI, and TracePath's part of the loop is two ordinary HTTP calls: reading the issue's telemetry, and archiving the exception once the fix is proposed.

⚠️

TracePath does not publish packaged GitHub Actions for this yet. There is no reusable workflow and no composite action to reference from your own workflow; the loop below is one you assemble from GitHub's own actions plus the TracePath API. The GitHub notification channel that starts it is fully supported today.

The contract

The loop is three steps, and only the middle one is agent-specific:

StepWhat it doesCredentials it holds
PrepareRefuses issues from anyone but the channel's token owner, extracts the Hash, Exception ID and Occurred at fields from the issue body with strict regexes, and writes a prompt built from those validated fields onlyA read-only TracePath token
AgentReads the prompt, investigates the exception through the TracePath API or MCP server, edits the working tree if a fix is warranted, and writes a report fileThe same read-only token, and no GitHub credential
PublishValidates the report against the working tree, then opens the pull request or comments the analysis, and archives the exceptionA GitHub write token and a TracePath write token

The report file is the whole interface between the agent and the publish step:

STATUS: fixed            # or: analysis
HASH: <16 hex characters>
<markdown: root cause, the fix, how it was verified, the View details link>

The security model is the split, not the agent's tool allowlist. The agent step holds no GitHub credential and only a read-only TracePath token; every write happens in the publish step; and the prompt is assembled from regex-validated fields, never from the issue body — which quotes the exception message and is therefore attacker-influenced. Anyone who can make your application throw can choose the text of that message. That split holds for any agent you wire in, including ones whose sandboxing you cannot configure.

Wiring it up

1. Create two TracePath bot users

The agent step must not be able to change anything in TracePath, and the publish step needs to archive exactly one exception. The least-privilege recipe is two accounts in the organization, each with its own personal access token:

SecretOrganization roleUsed by
TRACEPATH_TOKENreadonlyPrepare and the agent step
TRACEPATH_PUBLISH_TOKENreadonly, with a per-project override to user on the target projectPublish, to archive the exception

Per-project overrides are set from Settings → Team Members; expand the member row and pick the project. One token for both roles works and is strictly weaker: the agent step could then archive exceptions whatever its tool allowlist says.

2. Add the repository secrets

  • TRACEPATH_TOKEN and TRACEPATH_PUBLISH_TOKEN from the step above.
  • GH_PUSH_TOKEN: a fine-grained GitHub token for the repository with Contents, Pull requests and Issues set to read and write. The built-in github.token cannot open a pull request that triggers other workflows, which is why this is separate.
  • Your agent vendor's API key.

Add a repository variable TRACEPATH_PROJECT_ID holding the project's UUID. It is in the dashboard URL when the project is selected.

3. Point the GitHub channel at the repository

Configure the GitHub channel with a label such as tracepath, and attach a New Issue notification rule to it.

4. Add the workflow

# .github/workflows/tracepath-autofix.yml
name: TracePath auto-fix
on:
  issues:
    types: [labeled]
 
jobs:
  fix:
    if: github.event.label.name == 'tracepath'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      issues: write
    env:
      TRACEPATH_URL: https://app.tracepath.dev
      PROJECT_ID: ${{ vars.TRACEPATH_PROJECT_ID }}
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
 
      - id: prepare
        env:
          GH_TOKEN: ${{ github.token }}
          ISSUE: ${{ github.event.issue.number }}
          ALLOWED_AUTHOR: the-github-login-that-owns-the-channel-token
        run: |
          set -euo pipefail
          author=$(gh issue view "$ISSUE" --json author --jq .author.login)
          if [ "$author" != "$ALLOWED_AUTHOR" ]; then
            echo "proceed=false" >> "$GITHUB_OUTPUT"; exit 0
          fi
          body=$(gh issue view "$ISSUE" --json body --jq .body)
          # Strict: 16 hex characters and nothing else, taken from a labelled line.
          hash=$(printf '%s' "$body" | grep -oE '^Hash: [0-9a-f]{16}$' | head -1 | cut -d' ' -f2 || true)
          if [ -z "$hash" ]; then
            echo "proceed=false" >> "$GITHUB_OUTPUT"; exit 0
          fi
          # The prompt is built from validated fields only, never from $body.
          printf 'Investigate TracePath issue %s in project %s.\n' "$hash" "$PROJECT_ID" > prompt.txt
          printf 'Propose a minimal fix, or explain the root cause if no safe fix exists.\n' >> prompt.txt
          printf 'Write your report to fix-report.md in the STATUS/HASH format.\n' >> prompt.txt
          echo "proceed=true" >> "$GITHUB_OUTPUT"
          echo "hash=$hash" >> "$GITHUB_OUTPUT"
 
      - if: steps.prepare.outputs.proceed == 'true'
        # Your agent step. Give it prompt.txt, the working tree, and the
        # read-only TracePath token. Do NOT give it a GitHub token or the
        # publish token.
        env:
          TRACEPATH_TOKEN: ${{ secrets.TRACEPATH_TOKEN }}
        run: your-agent --prompt-file prompt.txt
 
      - if: steps.prepare.outputs.proceed == 'true'
        env:
          GH_TOKEN: ${{ secrets.GH_PUSH_TOKEN }}
          ISSUE: ${{ github.event.issue.number }}
          HASH: ${{ steps.prepare.outputs.hash }}
          PUBLISH_TOKEN: ${{ secrets.TRACEPATH_PUBLISH_TOKEN }}
        run: |
          set -euo pipefail
          test -f fix-report.md || {
            gh issue comment "$ISSUE" --body "The agent finished without a report."
            exit 1
          }
          status=$(grep -oE '^STATUS: (fixed|analysis)$' fix-report.md | head -1 | cut -d' ' -f2)
          reported_hash=$(grep -oE '^HASH: [0-9a-f]{16}$' fix-report.md | head -1 | cut -d' ' -f2)
          [ "$reported_hash" = "$HASH" ] || { echo "hash mismatch"; exit 1; }
 
          if [ "$status" = "fixed" ]; then
            git diff --quiet && { echo "STATUS: fixed with no changes"; exit 1; }
            branch="tracepath/fix-$HASH-${GITHUB_RUN_ID}"
            git switch -c "$branch"
            git add -A ':!fix-report.md' ':!prompt.txt'
            git -c user.name=tracepath-autofix -c [email protected] \
              commit -m "fix: TracePath issue $HASH"
            git push origin "$branch"
            gh pr create --head "$branch" --title "fix: TracePath issue $HASH" \
              --body "Fixes #${ISSUE}"$'\n\n'"$(cat fix-report.md)"
          else
            gh issue comment "$ISSUE" --body-file fix-report.md
            exit 0
          fi
 
          curl -fsS -X POST \
            "${TRACEPATH_URL}/api/exception-stack-traces/archive?projectId=${PROJECT_ID}" \
            -H "Authorization: Bearer ${PUBLISH_TOKEN}" \
            -H "Content-Type: application/json" \
            -d "{\"hashes\":[\"${HASH}\"],\"resolvePages\":true}"

issues: [labeled] fires for labels applied at creation, so an issue the channel opens with the label starts the run immediately. Only collaborators can apply labels, and the prepare step additionally refuses any issue whose author is not the channel's token owner, so a stranger cannot start a run by filing an issue.

The archive call takes up to 100 hashes per request and answers 422 beyond that. resolvePages: true also closes any unresolved on-call pages that issue opened.

Guardrails worth keeping

The checks in the publish step above exist for specific failure modes. Each should comment its reason on the issue and fail the run rather than pushing anything:

  • No report file, or a STATUS other than fixed / analysis.
  • A HASH line that differs from the hash the prepare step validated — an agent that wandered onto a different issue.
  • STATUS: fixed with an unchanged working tree, or any other status with a changed one.
  • A new file whose name looks like agent scratch output (*.sh, *.patch, *.log, *.tmp, fix-report*), including files inside a newly added directory. The publish step judges the run by git status, so anything else checked out into the working tree has to be excluded first.

Giving the agent access to the data

The agent needs to read the exception, its stack trace, recent occurrences, and the surrounding traces and logs. Two routes:

  • MCP server: point the agent's MCP client at TracePath's remote MCP endpoint with the read-only token. This is the shortest path for agents that speak MCP.
  • HTTP API: POST /api/exception-stack-traces/:hash and the logs and endpoints queries, with Authorization: Bearer <read-only token> and ?projectId=. See the API reference.

Caveats

  • The exception is archived when the pull request opens, not when it merges. Drop the archive call if a closed-unmerged PR would otherwise hide a live error.
  • Two runs on the same input can differ. Add a concurrency group keyed on the issue number so one issue never runs twice at once, and consider deduplicating across issues for the same hash.
  • The agent is told to follow the repository's contribution rules, but that is a prompt-level instruction. Review the diff as you would any contributor's.