AI engineering · Agentic delivery
Building Effective Agents in Practice: Delivering Jira and Crashlytics Fixes as Pull Requests
A change can start as a Jira ticket, a plain spec, or a crash report, and the path to a pull request can be automated. This article uses the framework from Anthropic’s Building effective agents to show how: a simple workflow, with one agent step in the middle, checks around it, and a person at the end.
- Based on Anthropic’s Building effective agents: start simple, use workflows where the path is known, and an agent only where the steps cannot be predicted.
- Three inputs (Jira, plain spec, Crashlytics) become one task format, then follow the same pipeline.
- The pipeline is a workflow with one agent step. RAG is a retrieval capability inside it, not an alternative.
- Prompts are written by code from templates. The agent can open a draft PR; a person merges.
Contents
1. The idea behind it
This article applies Anthropic’s engineering essay Building effective agents (Dec 2024) to one job: delivering a code change. Its advice is to prefer “simple, composable patterns” over complex frameworks, and it separates two kinds of agentic systems:
- Workflows: LLMs and tools orchestrated through predefined code paths.
- Agents: LLMs that dynamically direct their own process and tool use.
The essay describes five workflow patterns and then the agent. Everything in this article is built from these six pieces.
The essay’s guidance: find the simplest solution and add complexity only when it clearly improves results. Agents trade latency and cost for flexibility, so use one where the number of steps cannot be predicted. Software delivery is a good fit because tests give the agent objective feedback, while a person still reviews the result.
2. The cycle
3. Three ways in
| Input | What starts it | What intake fetches | Watch out for |
|---|---|---|---|
| Jira ticket | A button, or an automation rule on a label such as ai-ok | Summary, description, type, acceptance criteria, links | Vague tickets. Stop and ask when criteria are missing |
| Plain spec | A command or a workflow run | The text itself (optionally cleaned up by a model) | No fields, so the goal must be extracted |
| Crashlytics issue | A Firebase alert: new fatal issue, regression, or velocity | Stack trace, exception, app version, OS and device, user count, logs | Obfuscated traces and personal data (see next section) |
Whatever the source, intake produces the same JSON, so every later step is shared:
{
"source": "crashlytics",
"id": "a1b2c3",
"type": "bug",
"goal": "Fix NullPointerException in LoginViewModel.onSubmit",
"evidence": {
"users": 4210, "version": "3.8.1", "os": "Android 13-14",
"top_frame": "LoginViewModel.kt:57"
},
"acceptance": [
"No crash when the email is null",
"A unit test reproduces the crash first"
],
"scope": ["app/src/main/java/com/example/login/"],
"open_questions": []
}
4. Crashlytics to fix
A crash is a bug report written by production. The new part of this flow is the middle: the model works out where and why, then the agent decides and applies a fix. See also Firebase Crashlytics: find and fix crashes faster.
- Triage first. Only act on fatal issues above a user threshold, in your own code, on a supported app version. Skip SDK-only traces. Deduplicate by issue id.
- Readable traces. Upload mapping files (R8 or dSYM) so frames are symbolicated. A model cannot fix
a.b.c(Unknown). - Strip personal data from logs and custom keys before they enter a prompt.
- Locate. The model maps frames to source files (code search,
git blame), states a root-cause hypothesis and a confidence level. - Test first. The agent must write a failing unit test that reproduces the crash, then a minimal fix. No reproducing test, no PR.
- Close the loop. The PR links the Crashlytics issue. After release, a falling crash rate confirms the fix; a regression alert restarts the flow.
How the crash data reaches the pipeline depends on your setup: Firebase alerts through a Cloud Function, a scheduled query on the BigQuery export, or an MCP server that exposes Crashlytics to the agent. Check the current Firebase documentation for what is available.
5. The patterns applied
The pipeline is a workflow with one agent step. Each pattern from the essay has a natural place in it.
| Pattern | Where it appears |
|---|---|
| Prompt chaining | The backbone: intake, prompt, agent, checks, PR. Gates between steps: completeness (missing criteria stop the run), triage (crash thresholds), diff-size guard |
| Routing | Classify the input (feature, bug, chore, crash) and pick the matching template. Optionally send routine tickets to a smaller model and hard ones to a stronger model |
| Parallelization | Sectioning: tests, lint and the secret scan run side by side. Voting: several independent review prompts look for risky changes |
| Orchestrator-workers | For a large ticket touching many files, a planner splits the work at run time and workers handle each part. Use only when one agent run is not enough |
| Evaluator-optimizer | The checks judge the change and the failure follow-up prompt makes the agent improve it. The optional review pass is the same idea with a second model as evaluator |
| Agent | The coding step itself: the number of steps is unpredictable, each step gets ground truth from running the tests, and the run is capped by turns, time and cost |
A chain of four steps plus one agent run already delivers most of the value. Add routing, parallel checks, a reviewer or an orchestrator one at a time, and only if measured results improve.
6. Where the LLM is needed
Rule of thumb: use a model where the input is fuzzy (language, code that needs understanding) and the output needs judgement; use code where the answer is exact (pass or fail, a branch name, an API call). Never let a model grade its own tests.
7. LLM, RAG or agent?
The essay’s building block is the augmented LLM: a model with retrieval, tools and memory. RAG is the retrieval part of that block. A workflow adds predefined code paths around it, and an agent lets the model direct its own loop. This pipeline uses the last two: a workflow, with one agent step inside.
- Default retrieval: the agent searches the code itself (grep, glob, file reads). No vector database is needed for most repositories.
- Add a RAG index only for knowledge code search cannot reach: a huge monorepo, design docs and runbooks, or similar past crashes and their fixes. Offer it to the agent as a search tool.
8. Parts of the augmented LLM
Whichever tool you use (Claude Code, Codex, or one you build), a coding agent is the essay’s augmented LLM running in a loop. Knowing the parts around it shows what to configure, what to restrict, and what to watch.
| Component | In this pipeline |
|---|---|
| Instructions | CLAUDE.md or AGENTS.md plus the generated task prompt |
| Tools | File read and edit, shell with an allowlist, git, an MCP server for Jira or Crashlytics |
| Retrieval and memory | The agent’s own code search, a plan file, the failure log from the last attempt |
| Permissions and sandbox | A throwaway runner, a narrow token, no production access |
| Guardrails and observability | Turn, time and cost caps; independent checks; saved prompts, diffs and costs |
The series Claude Code in scripts and CI and Codex in scripts and CI show how to configure these in real tools.
9. Automatic prompts
Nobody types a prompt in this cycle. Code writes up to six, from templates versioned in the repo.
| # | Prompt | Fires when | Template |
|---|---|---|---|
| 1 | Extract the task (optional) | Input is free text | extract-task.md |
| 2 | Main task prompt | Once per run | task-bug.md, task-feature.md |
| 3 | Failure follow-up | A check fails, mid-run | fix-failure.md |
| 4 | Review the diff (optional) | All checks pass | review-diff.md |
| 5 | PR description | Before the draft PR | pr-description.md |
| 6 | Questions for the author | Spec has gaps | clarify.md |
Two examples: the main prompt for a crash, and the automatic follow-up.
# task-bug.md (crash) # fix-failure.md (sent by the pipeline)
Read CLAUDE.md first. The checks failed after your change.
<crash id="{{id}}">{{evidence}}</crash> Step: {{step}}
1. Name the likely root cause + confidence. Output: {{log_tail}}
2. Write a test that FAILS because of it. Fix the cause. Never weaken tests.
3. Make the smallest fix. Run all checks. Attempt {{n}} of {{max}}.
Max 150 changed lines. Report: cause, files, test, doubts.
10. Tool design for coding agents
The essay calls the interface between the model and its tools the agent-computer interface (ACI) and says it deserves as much care as a human interface. It also notes that tool definitions need as much prompt engineering as the main prompt. Applied here:
| Advice from the essay | In a delivery pipeline |
|---|---|
| Write tool descriptions like a docstring for a junior developer: examples, edge cases, boundaries | Name and describe each tool and script clearly (run_tests, open_pr) and say when not to use it |
| Choose formats close to natural text; avoid formats that need line counting or escaping | Prefer simple edit tools over asking the model to emit large diffs or JSON-wrapped code |
| Poka-yoke: change the tool so mistakes are harder | Require absolute paths (the essay’s own fix for path errors), allowlist shell commands, block forbidden paths, give the reviewer read-only tools |
| Test the tools with many example inputs and watch the mistakes | Replay past tickets and crashes through the pipeline and fix the tool, not only the prompt |
11. Push, review and the one-click button
name: ticket-to-pr
on:
workflow_dispatch:
inputs: { source: { description: "PROJ-123 or crashlytics:<issue>", required: true } }
permissions: { contents: write, pull-requests: write }
jobs:
deliver:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- run: python3 tools/intake.py "${{ inputs.source }}" > task.json # Jira, spec or Crashlytics
- run: python3 tools/compile_prompt.py task.json > prompt.md # templates, no model
- run: claude -p "$(cat prompt.md)" --allowedTools "Read,Edit,Bash(npm test:*)" --max-turns 30 > result.json
- run: ./tools/verify.sh # tests, lint, diff guard, secret scan
- run: ./tools/open_pr.sh task.json result.json # branch, commit, push, draft PR
This is a sketch, not a drop-in file. Check each tool’s current documentation for flags and permissions, and give each secret the narrowest scope.
12. Principles, guardrails and rollout
The essay ends on three principles. Here is what each means for this pipeline.
| Principle | Practice |
|---|---|
| Simplicity | Start with a chain and one agent run. Call the CLI or API directly before adopting a framework, and add patterns only when measured results improve |
| Transparency | Show the plan (a plan file), keep the prompts, the output, the diff and the cost of every run, linked from the PR |
| A well-built ACI | Narrow, documented, tested tools (previous section) |
The essay also warns that agents cost more and errors can compound, and recommends sandbox testing and guardrails, stopping conditions such as a maximum number of iterations, and human checkpoints.
- Sandbox and least privilege. Fresh checkout, no production access, a token that can push
ai/*branches and open draft PRs, nothing more. Protectmain. - Untrusted input. Ticket text, comments, crash messages and logs can carry hidden instructions. Wrap them as data, restrict the tools, and keep secrets out of the prompt.
- Limits. Caps on turns, time, cost and diff size; forbidden paths (CI config, secrets, migrations); a kill switch.
- Human checkpoints. A person approves every merge. The essay stresses that human review stays crucial for fit with the wider system.
- Roll out in steps. Start with small bugs as draft PRs reviewed line by line. Track acceptance rate, rework, review time and cost, and widen only when the numbers hold.
You do not need an IDE to deliver a routine change, but you still need engineering judgement to approve it. The scarce skills become writing testable specs and reading diffs.
13. Takeaways and glossary
- Follow Building effective agents: start simple, and add a pattern only when it clearly improves results.
- Jira, plain specs and Crashlytics crashes share one pipeline once intake produces a common task format.
- The pipeline is a workflow with one agent step; RAG is a retrieval capability inside it.
- Prompts are generated from templates, including the mid-run failure follow-up.
- The agent proposes, checks verify, and a person merges.
| Term | In plain words |
|---|---|
| Workflow | LLMs and tools orchestrated through predefined code paths. |
| Agent | An LLM that directs its own process and tool use in a loop until done or limited. |
| Augmented LLM | A model with retrieval, tools and memory. |
| RAG | Retrieve relevant passages first, then have the model answer from them. |
| ACI | Agent-computer interface: how tools are defined, described and presented to the model. |
| Poka-yoke | Designing a tool so that mistakes are hard to make. |
| Auto-prompt | A prompt built by code from a template and the state of the run. |
| MCP | Model Context Protocol: a standard way to connect an agent to tools such as Jira or GitHub. |
Links
- Source: Building effective agents, Anthropic engineering (Erik S. and Barry Zhang, Dec 19, 2024). The page notes that the tooling landscape has changed since it was written.
- In this series: Claude Code in scripts and CI · Codex in scripts and CI · Firebase Crashlytics · Git quicksheet
- Claude Code · Claude Code GitHub Action · Codex · MCP
- Firebase Crashlytics · Jira automation · GitHub Actions