‹ All articles
Agentic Coding
Agentic codingChatGPTClaudeMCPXcodeInstruments

Your coding agent shouldn't grade its own homework

Part two of my Apple skills series: CLAUDE.md, subagents, Sentry MCP and a local verify script. Free templates on GitHub.

Carolane Lefebvre12 min
Your coding agent shouldn't grade its own homework

TL;DR

  • I built a Claude Code Xcode workflow that maintains my iOS and macOS app, Keepio: it reads crashes from Sentry, opens issues, fixes them with Apple’s Xcode agent skills, and gets reviewed by a read-only second agent.

  • The rule that makes it work: the agent never decides that its work is done. Tests, a local verify.sh script, Instruments measurements and Sentry do.

  • Everything runs on Claude and my mac. But you can apply the same workflow to your own agent and device! Xcode Cloud stays for real releases.

  • Work is never sent or published without another review from myself.

  • Templates are on GitHub: copy, replace the placeholders, start small.


A week ago, I wrote about Apple’s agent skills that is a small packages of expertise Xcode 27 gives your coding assistant. Skills make an agent know more. This article is about what comes nextL making it do more, safely, on a real app.

The trigger was Felix Rieseberg’s essay on how to be more ambitious with models . His arguments, in short: most of us still use models for tiny tasks, or “app that generates 20k MMR", but real ambition has four axes. Altitude (delegate outcomes, not tasks), depth (work like the best expert, with real data and tools), range (bring in other disciplines) and an external judge (never let the model grade itself). I applied each one to my own app Keepio.

So, let’s find out what is this all about.


Give the agent an outcome, not a task

We have all done this at least once in our journey:

“Fix this crash”

When you could have done something, a bit better:

“Keep the app crash-free”

"Fix this crash" ends when the crash is fixed. "Keep the app crash-free" never really ends, and that's the point.

Keepio's CLAUDE.md now opens with a short Maintenance mission: zero crashes and zero fatal hangs, compatibility with every new iOS, macOS and Xcode release, and App Store reviews turned into issues. At the end of each session, the agent has to say what still stands between the app and those three outcomes.

The mission is only one part of the file. Stripped down to its headings, Keepio's CLAUDE.md looks like this:

# Keepio — instructions for Claude

## Mandatory workflow          # branches, PRs, versioning, who merges
## Agent roles                 # developer, reviewer, triage
## Checks and communication    # devices to verify, what to report
## Commands                    # build, test, lint, verify.sh
## Project                     # scheme, targets, packages, deployment targets
## Maintenance mission         # the three outcomes, and what is out of scope
## Apple agent skills          # which skill, when
## Sentry                      # priorities, App Review noise, Fixes KEEPIO-<id>
## Work loops                  # crash, hang, new OS version
## Definition of done          # what a tool must prove
## Known pitfalls              # lessons learned the hard way
## Architecture                # extend, never redesign + the app's patterns

Read from top to bottom, it answers four questions for the agent: how we work here, what you're responsible for, how you prove it, and what already went wrong. The full template, with placeholders instead of Keepio's details, is in the GitHub repository.

The part I nearly skipped turned out to matter most: what the agent must not touch without asking. New features, architecture, monetisation, data migrations, dependencies. An ambitious agent with no boundaries will happily "improve" your paywall while fixing a layout bug.


AGENTS.md or CLAUDE.md ?

My repo still had an AGENTS.md from the days when another model reviewed my pull requests. Then I added a CLAUDE.md, and found out the hard way that Claude Code only reads AGENTS.md when no CLAUDE.md exists. Two rule files, one silently ignored, slowly drifting apart.

So there's now a single CLAUDE.md, around 300 lines: workflow, architecture, the maintenance mission, Sentry rules and a list of known pitfalls. CONTRIBUTING.md keeps the human-facing details and the release checklist, and the agent only opens it when it prepares a release.

If you're worried about context size: an @AGENTS.md import is loaded in full anyway, so merging costs nothing extra. One trap if you also use the Claude agent built into Xcode: it keeps its own configuration folder, so anything in your global ~/.claude won't follow it there. Project rules belong in the project.


How do I export Apple’s Xcode skills to Claude Code ?

In my previous article, I went through the skills Xcode 27 ships with. Here's how they ended up inside the repo, so that Xcode's agent and Claude Code in the terminal share exactly the same guidance:

xcrun agent skills export --output-dir "$PWD/.claude/skills"

My first attempt used a relative path and failed with a "read-only file system" error. The command hands the export over to Xcode, which resolves relative paths from the root of the disk. An absolute path fixes it. And if xcode-select points to a beta, prefix the command with DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer to export the skills of the Xcode you actually ship with.

Exporting is only half of it. The agent also needs to know when each skill applies:

Skill

When Keepio uses it

swiftui-specialist

Writing and reviewing SwiftUI

app-resizability

Any layout change, iPhone Duo folded and unfolded

device-interaction

Checking changed screens in the simulator before handing back

swiftui-whats-new-27

New APIs, always behind if #available(iOS 27, macOS 27, *), since Keepio still supports iOS 26

xctrace

Planning Instruments measurements for hangs (Xcode 27.2 beta)

One rule sits above the table: when a skill and the project's conventions disagree, the project wins. Apple's skills describe how Apple recommends building any app; your CLAUDE.md describes how yours is built.

Skills aren't the only thing Apple hands to agents, either. Xcode 27 runs Claude as a built-in agent with its own tools. xcrun mcpbridge exposes those Xcode tools to an external agent such as Claude Code, once you allow external agents in Xcode's settings. And simctl and xctrace give any agent the simulators and Instruments from the command line.


Let production data decide what to fix

Crazy thing to say right? Well, here’s the thing: I added Sentry to Keepio after noticing that App Store Connect showed far fewer crashes than I expected. Sentry found many more. So the agent now works from Sentry: it ranks real crashes and hangs, drafts the issues, and I pick which ones get fixed.

Three pieces do the work:

  • a project .mcp.json declaring the Sentry MCP server, scoped to a single organisation and project, authenticated with OAuth through /mcp;

  • a /triage command that hands the job to a sentry-triage subagent: fatal issues first, iOS and macOS kept apart with the os.name tag, a ranked report, and draft GitHub issues. Then it stops, and I choose what gets created;

  • an Xcode Cloud hook, ci_scripts/ci_post_xcodebuild.sh, that uploads the dSYMs and links each release to its commit with sentry-cli releases set-commits. A fix committed with Fixes KEEPIO-<id> should then resolve the Sentry issue in the release that ships it. That's Sentry's documented behaviour; I'll confirm it on Keepio's next release.


The agent never decides that it’s done

If you take one idea from this article, take this one. It's Felix's external judge, and everything else depends on it. A model reviewing its own work is still the same model, so on Keepio a tool decides when work is done, never the agent.

On Keepio, "done" means scripts/verify.sh passes. The script runs SwiftLint, the build-number check, the repository's own script tests, and the Xcode tests, actually executed, on the newest iPhone simulator, on the minimum deployment target and on macOS. It picks whatever simulators are installed and prints a Markdown summary that goes into every pull request. A /verify command runs it and explains failures from the logs.

Why not Xcode Cloud? I'm on the free tier, and I want App Store Connect to contain builds that are ready for TestFlight or release, not hundreds of test runs (perhaps i’m doing too much? But that’s how it is with me!).

The first run was humbling. Every test destination failed, and two of the failures were real bugs:

  • A CI script had lost its executable bit when I replaced it with a downloaded copy. Xcode Cloud would have skipped it, and with it the dSYM upload of my next release.

  • The macOS UI tests hadn't compiled since September. One test called XCUIDevice.orientation, which only exists on iOS… oops.

Neither had anything to do with the agent being clever. They surfaced because something finally ran everything, on every platform.


A second agent that can read, but not touch

Each pull request now gets a review from a second Claude agent that starts with a blank slate. It hasn't seen the developer agent's reasoning, so it judges the diff and the evidence rather than the intentions.

scripts/agent-review.sh <pr> checks out the PR in a temporary git worktree and runs:

claude -p --agent reviewer --effort high \
  --tools "Read,Grep,Glob,Bash" \
  --allowedTools "Read" "Grep" "Glob" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git show:*)" \
  --permission-mode dontAsk --strict-mcp-config --no-session-persistence

The reviewer can't write anything, and that's enforced by configuration rather than by a polite sentence in a prompt. Anything outside the allowed tools is refused without asking, and no MCP server gets loaded. Small diffs outside sensitive paths (migrations, entitlements, CI, the agent rules themselves) get a medium effort to save usage.


How do I stop an AI reviewer from redesigning my architecture ?

The first question I asked about the reviewer was whether it actually understood how Keepio is put together: how views connect, which components already exist, where logic is supposed to live. It knew the rules from CLAUDE.md. It didn't know the code itself. And reviewers, human or not, love suggesting a "cleaner" architecture. The short answer: read-only tools, a rule to follow the nearest existing code, and a ban on new layers.

In a codebase maintained by one person, consistency beats elegance. So the rule is short: extend, never redesign. Before approving any new type, the reviewer has to look for an existing equivalent (a duplicate counts as a finding) and compare the change with its nearest sibling: another list view and its ViewModel, another sheet, another use case. New layers, dependencies or state-management patterns are off the table. If it thinks the structure itself is wrong, it can only raise that as a question for me.

To help with that, Claude generated a docs/architecture-map.md once, listing modules, screens, navigation routes and shared components. I reviewed it, since I'm the one who knows the intent behind the code, and the agents read it only before structural changes.


My issues became prompts

Keepio's issues were already user stories. "As someone who…, I want…, so that…" covers the why, and the acceptance criteria cover part of the judge. Three new sections in the template cover the rest:

Section

What goes in it

Starting data

What was measured, with its source, and what is only assumed

Target

A number when it makes sense: no main-thread block over 250 ms at launch

How it is proven

A test that fails before the fix, a before/after measurement, the devices checked, no recurrence in Sentry

/implement <issue> reads the target and the proof as its definition of done, and treats every assumption as something to check. The same issue still reads well for me in six months, and I no longer rewrite context every time I start a session.


Let’s be real: What it costs

Short answer: probably nothing more than the Claude subscription you may already have. There's no API key, no server and no paid CI in this setup.

Piece

What it costs you

Claude Code, subagents, reviewer

Included in a Claude Pro or Max subscription, shared with your claude.ai usage

Apple's agent skills, xcrun, simctl, xctrace

Included with Xcode

verify.sh and the tests

Your Mac's time, nothing else

Xcode Cloud

Optional: only used for real releases, never for test runs

Sentry

Optional: the triage loop needs it, the rest of the workflow doesn't

The real question is usage, not money. Subscriptions don't count tokens: they reset every five hours, with a weekly cap on top. According to Anthropic's support page, an average Max 5x user ($100 a month) can send roughly 50 to 200 Claude Code prompts in each five-hour window; Pro ($20) gives about a fifth of that.

For a small app in maintenance, my estimate, not yet a measurement, is 50 to 150 prompts a week: one or two issues through /implement, a review each, and a weekly /triage. That fits comfortably in Max 5x and is likely to fit in Pro, as long as you don't run a marathon session in a single five-hour window.

Four habits keep usage low on any plan:

  • run the reviewer with Sonnet for small diffs (AGENT_REVIEW_MODEL=sonnet) and keep Opus for sensitive ones;

  • launch ! scripts/verify.sh directly when you expect it to pass: no tokens at all, and /verify only when something fails;

  • one issue per session, then /clear, because a long conversation costs more with every message;

  • save device-interaction screenshots for the screens you actually changed.


Templates: the Claude Code Xcode workflow on GitHub

The templates are on GitHub: CLAUDE.md, the two subagents, the /implement, /triage and /verify commands, verify.sh, agent-review.sh, the Xcode Cloud hook and the issue template, with placeholders instead of Keepio's values. Start with one file, verify.sh, and grow from there.
You can access it here.

And don’t forget: have fun agentic-coding!

Frequently asked questions

How do I use Apple's Xcode skills in Claude Code?

Export them with xcrun agent skills export --output-dir "$PWD/.claude/skills" (absolute path) and commit the folder.

Does Claude Code read AGENTS.md?

Only when no CLAUDE.md exists. Use one file, or import @AGENTS.md from CLAUDE.md.

How do I stop an AI reviewer from changing my architecture?

Give it read-only tools, tell it to compare with the nearest existing sibling, and forbid new layers and out-of-scope refactors.

Can an agent fix Sentry issues automatically?

It can triage and fix them; a Fixes <SHORT-ID> commit resolves the issue when a release containing it is created. Keep human validation before merge.

Do I need Xcode Cloud for this?

No. A local verification script runs the tests; Xcode Cloud only archives releases.