●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Wiring Claude Code from Crash Reports to a Fix Commit — A Personal Developer's Post-Release Operations Playbook
A working pipeline for personal developers to triage Crashlytics and Sentry reports with Claude Code, drawn from the routine I still run on my own apps every morning.
After running several apps as a solo indie developer for years, one of my morning rituals had quietly turned into a weight I dragged behind me: opening the Firebase Crashlytics dashboard before I had even poured the coffee. On a calm morning it took three or four minutes. On a morning when a fresh OS point release had wandered into the wild, it could swallow more than an hour.
Post-release incident response is meant to be absorbed by a team. An SRE somewhere, an ops engineer, a support desk. A solo developer carries every one of those roles in the same head. Even in the periods when ad revenue had grown into something meaningful, the engine running underneath it was me, reading crash reports almost every morning. To keep building apps for the long haul alongside everything else, something about this routine had to change.
What changed when I began using Claude Code in earnest was that I could move crash reports from "things I read" to "things I process." This article is the working version of that pipeline: not "paste a stack trace into the AI and ask politely," but a real flow for fetching, normalizing, hypothesizing, locating code, and proposing diffs, end to end.
Why post-release work weighs more on a solo developer
The cost of crash report work is not "issues per day × time per issue." A solo developer carries some structural overhead that organizations rarely have to think about.
The first is the cost of context switching. When a Crashlytics email arrives in the middle of a feature design session, you cannot ask a teammate to take it. You either drop the design context entirely or carry the crash in a parallel mental thread. In my own experience the productivity cost is between three and four times the raw time spent.
The second is scale of users. Fifty million cumulative downloads sounds large; the active monthly portion is in the hundreds of thousands, but it is split across tablets, phones, OS versions, and locales. A 0.1% crash rate still floods you with hundreds of new traces every day. Reading them by hand stops being a serious option, but ignoring them shaves your ratings and your revenue.
The third is psychological. Opening a crash report is opening a small mirror onto your own code. Even after years of doing this, seeing your source path at the top of a stack trace puts a small dent in your morning. Repeating that ritual every day is not sustainable, even if the technical workload were manageable.
The conclusion I arrived at is plain: stop reading the reports. Move the reading into Claude Code, and keep only the decisions for myself.
Pre-processing reports into a shape Claude Code can act on
You can paste a raw stack trace into Claude Code and get something useful back. You will not get production-grade triage that way. Raw reports are noisy and largely duplicated. The first step in any pipeline is normalization.
The flow I run is:
Pull the last 24 hours of issues from the Crashlytics or Sentry API as JSON
Normalize the stack trace (collapsing line-number jitter) and compute a stable signature
Merge issues with the same signature, retaining counts, affected users, first-seen, and last-seen
Write the top N issues to a single triage queue file
After that scripted step, what Claude Code sees is a clean priority list, not a dashboard.
A working sample for Sentry — the same shape applies to Crashlytics:
# fetch_top_issues.py# Fetch unresolved issues from the last 24 hours and emit the top ten# by affected user count. Output: triage_queue.json (the file Claude Code reads).import osimport jsonimport timeimport urllib.requestSENTRY_TOKEN = os.environ["SENTRY_AUTH_TOKEN"]ORG_SLUG = os.environ["SENTRY_ORG_SLUG"]PROJECT_SLUG = os.environ["SENTRY_PROJECT_SLUG"]def fetch_issues(hours: int = 24, limit: int = 50) -> list: url = ( f"https://sentry.io/api/0/projects/{ORG_SLUG}/{PROJECT_SLUG}/issues/" f"?statsPeriod={hours}h&query=is:unresolved&sort=user&limit={limit}" ) req = urllib.request.Request(url, headers={"Authorization": f"Bearer {SENTRY_TOKEN}"}) with urllib.request.urlopen(req, timeout=30) as r: return json.loads(r.read())def normalize(issue: dict) -> dict: # Use only the leaf frame as the signature key; drop line numbers. frames = issue.get("metadata", {}).get("function") or "" return { "id": issue["id"], "title": issue["title"], "culprit": issue.get("culprit"), "signature": frames.split("(")[0].strip(), "users": issue.get("userCount", 0), "events": issue.get("count", 0), "first_seen": issue.get("firstSeen"), "last_seen": issue.get("lastSeen"), "permalink": issue.get("permalink"), }if __name__ == "__main__": issues = fetch_issues() triaged = [normalize(i) for i in issues] triaged.sort(key=lambda x: x["users"], reverse=True) queue = triaged[:10] with open("triage_queue.json", "w") as f: json.dump(queue, f, ensure_ascii=False, indent=2, default=str) print(f"wrote {len(queue)} issues at {time.strftime('%Y-%m-%d %H:%M')}")
I run this on cron or GitHub Actions once a day. Every morning the file is already waiting in the repository. There is no dashboard to open.
One detail that quietly determines whether the rest works: symbolication has to happen before this stage. Sentry and Crashlytics will both happily hand you stack traces with mangled symbols, and Claude Code will draw beautifully wrong conclusions from them. I run fastlane upload_symbols_to_sentry on every release as a non-negotiable step.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Replace the daily ritual of opening Crashlytics or Sentry by hand with a 15-minute Claude Code triage flow you fully control
✦Build a continuous pipeline that deduplicates reports, generates root-cause hypotheses, and pinpoints the offending code path in your repository
✦Walk away with a concrete decision rule for which AI-generated patches you can merge directly and which ones (unclear repro, third-party SDK, threading races) must stay in human review
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
When I hand the queue file to Claude Code, I do not use a single prompt. I run a three-layer chain. The reason is simple: when one prompt is asked to "find the cause and produce a fix," the reasoning steps mix together and you cannot tell where the AI lost the thread.
Splitting the layers also lets me inspect each output independently. I want to be off the "reading reports" path, but I am not yet ready to be off the "judging the AI's reasoning" path.
The layers are:
Layer 1: Stack traces → up to three ranked hypotheses. Does not read source code.
Layer 2: Hypotheses → repository search to locate the file and function. grep-equivalent only.
Layer 3: Located code → diff proposal and a self-judgment about whether it is safe to apply.
A working Layer 1 prompt:
You are a triage assistant for a solo developer's crash reports.Input: one issue from triage_queue.json.Task: Based only on the stack trace, the title, and the first/last seen times, list the three most likely root-cause hypotheses, ranked by confidence.Constraints: - Do not look at source code yet. Hypotheses only. - "Repro conditions unclear" and "Likely third-party library issue" are valid hypotheses. - For each hypothesis, add one line: "If this is the cause, look at <which file>".Output: JSON array of {rank, hypothesis, confidence, where_to_look}.
Layers 2 and 3 take the previous layer's output as input. I keep the chain inside a Makefile so it can be run as a single command from the morning routine.
# Makefile excerpttriage: triage_queue.json claude code run prompts/layer1_hypothesis.md \ --input-file triage_queue.json --output hypotheses.json claude code run prompts/layer2_locate.md \ --input-file hypotheses.json --output locations.json claude code run prompts/layer3_diff.md \ --input-file locations.json --output proposed_diffs/review: proposed_diffs/ @echo "Next: open each diff in proposed_diffs/ and decide adoption"
Some mornings I only run layer 1 and skim the hypotheses. Other days I run the full chain in the background and review the diffs in the afternoon. Being able to shape the workflow around real life, instead of around a meeting calendar, is one of the genuine advantages of solo development.
Generating fixes and judging which to leave alone
No matter how high the precision gets, you cannot git apply a layer-3 diff blindly. From years of running this, I keep coming back to three categories of "do not auto-merge":
The first is unclear reproduction. Claude Code will produce a change that fits the surrounding code beautifully, but whether it actually stops the crash depends on whether you can write a repro test. "Added a nil check" or "wrapped the call in try" are symptomatic patches that bury the real cause and the crash returns. I always include in the layer-3 prompt: "Together with the patch, write the smallest test that demonstrates it. If you cannot write that test, mark the patch as not-applicable."
The second is third-party SDK involvement. If the crash is inside a vendor SDK, patching your own code does not solve the underlying problem and may invent a new misuse pattern. If layer 1 raised "library-related" as a hypothesis, I freeze auto-merge for that issue and read the SDK's release notes and issue tracker by hand.
The third is threading and timing. Races between onCreate and onResume, cache invalidation racing with concurrent reads, AppLifecycle observers firing during memory warnings — these rarely yield to a one-shot patch. The larger the install base grows, the more reliably someone hits even a low-probability race. Before letting the AI propose anything, I add instrumentation (Trace, Crashlytics.log) and gather circumstantial evidence first.
There are categories I am willing to merge directly: explicit nil / Optional unwrap fixes, off-by-one and bounds violations, constant or field-name typos, JSON key mismatches, replacing a strong reference with a weak one. The cause is local, the test is easy, the blast radius is small. I encode the rule into the prompt itself:
# Rule appended to every Layer 3 promptA patch may be marked auto-mergeable only if it falls into one of: (1) Adds an explicit nil/Optional check to a previously-unsafe unwrap (2) Adds a bounds check to a collection or array access (3) Fixes a constant, key, or field-name typo (4) Converts a strong reference to weak in a clearly local scope (5) Mirrors a patch already merged for the same pattern in another fileAnything else: mark "Requires human judgement".
Writing the rule down once changes the entire texture of the layer-3 output.
A 15-minute morning routine
With the pipeline in place, my actual morning looks like this:
0–2 min: open the laptop while coffee brews; check that make triage produced output overnight
2–7 min: open hypotheses.json; move issues tagged "library" or "threading" into a separate queue
7–12 min: open proposed_diffs/; merge the ones that fit the auto-merge rule, defer the rest
12–15 min: bundle the merged patches into a single commit and stage a release
Fifteen minutes against the previous forty to sixty. The genuinely large benefit is not the time saved, it is that the first hour of focused work is back in my hands.
Designing the routine to finish in fifteen minutes matters as much as designing the pipeline. Without a hard stop, the speed of the pipeline tempts you to take on more, and you end up busier than before. The point of Claude Code here is not to do more triage. It is to do less. For broader debugging patterns I have written more in Claude Code Debugging in Practice.
Integration patterns: Crashlytics vs. Sentry
I run both. Crashlytics is free and pairs cleanly with personal-scale apps; Sentry's source maps and release tracking suit the web side. Two integration notes:
Crashlytics has no public REST API, so I anchor on the Firebase BigQuery export. A Cloud Run job runs the daily aggregation and writes the same triage_queue.json shape. The query looks like this:
-- Crashlytics BigQuery export aggregationSELECT event_id, ANY_VALUE(blame_frame.subroutine) AS subroutine, ANY_VALUE(blame_frame.file) AS file, COUNT(DISTINCT user_pseudo_id) AS users, COUNT(*) AS events, MIN(event_timestamp) AS first_seen, MAX(event_timestamp) AS last_seenFROM `your-project.firebase_crashlytics.app_xxxxx`WHERE event_timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)GROUP BY event_idORDER BY users DESCLIMIT 10;-- Output: top 10 issues by unique affected users in the last 24 hours
For Sentry the Python script above is the source of truth. Sentry attaches a permalink per issue, which is worth keeping in the queue — Layer 1 occasionally decides it wants to inspect release-version tags.
If you run both, keep the queue schema unified. A source field tells the layers what backend they came from, but the structure is the same:
Same prompts, same layers, same Makefile target. Small detail; large compounding effect.
Operating rules I keep coming back to
After the pipeline starts running, the operational rules I impose on myself end up doing more for quality than the automation does. The current set:
Sit on a risky patch for at least two days. The version of me that reviews crash reports late at night is not the version I want approving a fix.
Issues with fewer than 10 events go to a separate queue, reviewed in batch on weekends.
One issue equals one commit. Bundling issues makes regressions impossible to revert cleanly.
Release notes always cite the issue ID. Users who hit the symptom should be able to verify it has been fixed.
Every fix gets a one-line note in Notion. If the same symptom shows up a third time, it is no longer a fix problem; it is an architecture problem.
None of these are clever. They came from years of getting it wrong. The more processing I hand to the AI, the more these human-side rules quietly hold the system together. Claude Code does not take judgement from me. It hands me better material to judge with, and the last move is still mine to make. The same principle of using AI as a production debugging companion is explored further in Claude AI as a Production Debugging Companion.
Preparation and finishing are worth keeping separate. The system can plane the workpiece into a shape the AI handles well; the final stroke belongs to the maker. That instinct settled in slowly, over years of running post-release incident response entirely on my own as an indie developer.
Where to start
Thank you for reading this far. You do not need to build the full pipeline this week. The smallest useful first step is to export one crash issue — the one with the highest user count over the last seven days — into a single JSON file. Hand that file to Claude Code along with the Layer 1 prompt above, and read the three hypotheses it returns. That is the form I started in, and the rest of the system grew out of it.
I hope your own mornings as a solo developer get a little lighter.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.