August 14, 2026 · 7 min read
I Delegate Execution to AI, Not Judgment
Where the boundary sits when a solo developer hands work to AI, and where that boundary leaks. It starts with a repository handed over as evidence that was already out of date.
When I build alone, judgment runs short before hands do. One person running an iOS app, a Go backend, and self-hosted Kubernetes spends more time checking what is true now than writing code. So I delegate execution to AI, and I decide what to accept as fact.
This post describes where I drew that boundary and where it actually leaks. I also include what I confirmed while building this blog surface.
At a glance
| Symptom | The checkout given to AI as evidence was 14 commits behind remote, while the manifest lists differed by three each |
| Cause | Comparing counts alone can make the totals match even when the composition is misaligned, hiding the difference |
| Fix | Code owns orchestration, and AI fills one JSON contract at each step. Role permissions are enforced at the registration point, not in the prompt |
| Result | Confirmed the difference between 50% measured when judgment was delegated and 12/12 when only extraction was delegated; I do not write live-only figures by counting files |
The file I handed over as evidence was already different from reality
When I give AI a task, I give it the repository as evidence. While documenting the cluster configuration, I counted ten manifests in clab-app/argocd/applications in my checkout. I counted ten in the same directory on remote origin/main. The counts matched, so I almost moved on, but comparing the lists showed that three differed on each side.
| Present only where | Items |
|---|---|
| Only in my checkout | justsend-ocr, justsend-ocr-normalizer, justsend-web-app |
| Only on remote | discourse, justsend-ops, justsend-render |
My checkout was 14 commits behind remote. At the same time, the live cluster manifest repository had no commits behind and its 11 manifests matched remote. Only one side was stale, so nothing signaled that everything was old.
Querying the live cluster showed 23 ArgoCD Applications, all Synced and Healthy. The 23 I got by counting files and the live 23 happened to be the same. The composition was off by three on each side while the total matched, so a check that compares only counts cannot catch the mismatch.
That leaves one standard. When I give AI a repository as evidence, that evidence is the time of my checkout, not the repository's current state. I do not write figures confirmed only by live state as a way of counting files.
Calling it one app makes the wiring disappear
Counting targets in ios-prod/app/project.yml gives five. JustSendKit is a framework, JustSend is an application, JustSendShare and JustSendWidgets are each an app-extension, and JustSendTests is a bundle.unit-test. There are three product application and app-extension targets; the declaration including tests and the framework has five.
This distinction matters because calling it one app makes the connections disappear in language. JustSend connects JustSendKit and the two extensions as dependencies, and the two extensions use JustSendKit again. JustSendKit links justsend-core with path: JustSendMemoryCore. Calling this a structure with a pinned remote package version would not match the current project.yml.
So I do not cover a defect from core with an app-side workaround. I open the source of truth, JustSendMemoryCore, at the same time so I do not fix the same defect twice.
Measured values when I delegated judgment to AI
The principle written in app/AGENTS.md is not to build on top of what has not been measured. The place where this principle began was an experiment in on-device AI judgment.
The OnDeviceCapabilityProbeTests record shows that measured judgment delegated to AI was 50% over 200 runs. It misclassified 73–87% of declarative prose as execution requests. When I gave the same model only a narrow extraction contract, the title was 12/12.
The conclusion is clear. The problem was not that the model was weak; it was the shape of the task I handed over. So code owns orchestration, and AI fills one JSON contract at each step. I separated the place that decides what to do from the place that fills in blanks.
I delegate only repetition that can be narrowed by a role
I have six agents for customer support and operations. They handle lead, review, community, mail, insight, and operations separately. They use care-mcp, connected to App Store Connect, community forums, a mail server, read-only Kubernetes access, and our own database.
Splitting roles into six did not mean leaving permissions to prompts. Role gating is enforced at the scopedServer registration point, and lead and insight are read-only. Writes that leave a trace outside must pass both the CARE_WRITE_ENABLED, CARE_CLUSTER_WRITE, and CARE_APPROVERS environment gates and a human approval flow.
So what I delegated was repetition that can be narrowed by a role, such as mail, reviews, community, metrics, and first-response operations. I did not delegate product boundaries, core contracts, or changes to deployment manifests. I execute secrets and cluster changes myself; agents touch only as far as manifests.
I build checks that stand without a person watching
I built this blog surface the same way. A post is one Markdown file in the repository, and the build bakes the hub, the three-language lists, and the details of public posts into static HTML. I added checks. If an indexable page lacks any one of title, canonical, hreflang, or JSON-LD, the build stops. A hub that is not indexed must have noindex; if it does not, the build also stops.
I added these checks because a page with a missing head can deploy quietly. The screen looks normal while indexing is broken, so it can take weeks for a person to notice.
Even after adding the checks, three issues appeared outside them.
| What happened | Where it came from |
|---|---|
Public robots.txt, sitemap, and three RSS files returned nginx 403 |
Generated files had mode 0600. The host's umask was 077, and writeFile's mode applies only when creating a new file, so existing file modes remained |
The linux/amd64 image build failed during prerendering |
Chromium on qemu emulation could not start. Increasing the wait to 180000 milliseconds made no difference |
The list page's hreflang pointed to a language that had only a draft |
The language list was built without checking publication status |
The first two were at the contact surface with the deployment environment, not in code logic. Prerendered HTML was newly created in the container and had mode 0644, while only feeds were 0600. The blog route returned 200 and only feeds returned 403, so the cause stayed hidden for a while. The third was a logic defect, so I fixed it in code. I also fixed the same kind of issue where a detail page's x-default pointed to an English URL that did not exist.
So after deployment, opening the public URL directly and checking its status code and content type remains a human task. A passing build and a healthy public address are different facts.
The rules of this surface
A post is one content/blog/<슬러그>/<언어>.md file, and if draft in frontmatter is true, it is omitted from the list, sitemap, RSS, and static generation.
There can be three languages: Korean, English, and Japanese. A post exists only for a language with a file. I write Korean as the source and keep an English edition separately. There is no Japanese file, so it does not appear in the Japanese list or in the alternate-language declarations.
If you hit this
- Is the repository you give AI as evidence from the same point in time as remote?
- Are you comparing only counts, or are you matching the lists?
- Are you delegating judgment to the model, or making it fill a defined blank?
- Are role permissions written in a prompt, or enforced at the registration point?
- Are you treating a passing build and a healthy public address as the same check?
- Does the mode of generated files depend on the host
umask?
The next post covers what I confirmed from records about on-device AI availability decisions, context limits, contracts, and fallbacks.