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:
| Step | What it does | Credentials it holds |
|---|---|---|
| Prepare | Refuses 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 only | A read-only TracePath token |
| Agent | Reads 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 file | The same read-only token, and no GitHub credential |
| Publish | Validates the report against the working tree, then opens the pull request or comments the analysis, and archives the exception | A 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:
| Secret | Organization role | Used by |
|---|---|---|
TRACEPATH_TOKEN | readonly | Prepare and the agent step |
TRACEPATH_PUBLISH_TOKEN | readonly, with a per-project override to user on the target project | Publish, 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_TOKENandTRACEPATH_PUBLISH_TOKENfrom 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-ingithub.tokencannot 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
STATUSother thanfixed/analysis. - A
HASHline that differs from the hash the prepare step validated — an agent that wandered onto a different issue. STATUS: fixedwith 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 bygit 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/:hashand the logs and endpoints queries, withAuthorization: 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
concurrencygroup 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.