Greetings, traveler!
A request to add a filter to a SwiftUI screen sounds simple. Add a Picker, filter an array, and show the result. But a real app has a history: status types may already exist, data may be owned by another layer, and navigation may recreate the screen whenever the user returns to it.
An AI assistant can generate the picker in seconds. Whether that code fits the app is a separate question. A coding agent can investigate the project before writing the code. With the right tools and permissions, it can follow the data flow, edit the relevant files, and run checks. The developer still has to decide what the feature should do and whether the result is good enough to ship.
Let’s look at how that process works and what changes when we give an agent a task instead of asking for a code snippet.
From Code Suggestions to Engineering Tasks
When you ask an assistant for a function, you usually provide the context and integrate the answer yourself. You decide which parts of the project to show it, where the code belongs, and how to test it. This can be an efficient way to solve a focused problem, especially when you already know the surrounding code.
A coding agent can take on more of that work. It can search a repository, inspect files, edit code, run commands, and use their results to decide what to do next. If a build fails, it can read the compiler error and attempt a fix.
The line between an assistant and an agent is not always sharp. A chat interface may provide tools, while a coding agent can answer a question without touching the project. What matters in practice is what the system can inspect and do, and what decisions remain with the developer.
Access also varies. A repository connection does not automatically give an agent control of an iOS simulator. Building, running tests, and interacting with an app each require suitable tools and an environment that supports them.
Model, Agent, and Harness
These terms are used differently across products, so I’ll use them in a specific way here.
A model processes the information it receives and generates a response. That response might be an explanation, code, or a structured request to use a tool. The model does not execute a shell command or read your files on its own.
An agent is the running system that uses a model and a harness to work on a task. It can pursue that task through a sequence of responses and permitted actions.
The harness is the software around the model that coordinates the work: it supplies context, exposes tools, manages interactions, and applies execution rules. Some teams use the term more broadly to include the workflows and checks surrounding the agent. Neither definition is universal; the useful question is which part of the system is responsible for each capability.
For example, the model can request a build. The harness checks the applicable permissions, invokes the tool, and returns its output to the model. The actual command runs through the tool and its execution environment. The model can then use the compiler output to decide what to do next.
What Happens Inside an Agent Loop
A typical agent works in a loop: it receives a task and available context, produces a response or tool request, receives any tool result, and decides whether to continue, ask for input, or finish.
For our filter, a short sequence could look like this:
Developer: Add a status filter to the activity list.
Agent: Search for the activity list and its data model.
Tool: Returns matching files.
Agent: Inspect the screen, navigation, and existing status type.
Tool: Returns the relevant code.
Agent: Add the filter and run the focused tests.
Tool: Returns a compilation error.
Agent: Correct the change and run the tests again.
Tool: Returns a passing result.
Agent: Report the changes and the checks performed.
This is an illustrative sequence, not a guarantee. The agent might search in the wrong place, misinterpret the code, or be unable to run the tests. Tool results provide feedback, but interpreting that feedback is part of the work. A passing test, for example, says something about the behavior it covers; it does not prove that the whole screen works.
The sequence depends on what the agent can actually see and do. If it cannot read the navigation code, it has to work from the information provided in the request. If it can inspect the project but cannot run the app, it may be able to build the change while leaving the navigation behavior unverified.
Permissions matter as well. A project instruction can tell an agent which files to avoid, while an access restriction can prevent it from modifying those files. Before delegating a task, the developer needs to know which commands and files are available and what happens when an action is outside those boundaries.
One Feature, Three Ways to Work
Suppose an existing app shows activities loaded from local storage. The screen has no filter yet. The feature request is to show all, active, or completed activities and keep the selected filter when the user leaves the list and returns. It does not need to survive termination of the app.
This example is hypothetical. Its code and outcomes illustrate one possible project, not a change performed in an actual repository.
With code generation, a developer might paste the view into a chat and ask for a picker and filtering logic. The assistant produces a suggestion; the developer checks how it fits the app, integrates it, and verifies the behavior. This is a reasonable choice when the relevant code is already understood and the change is small.
With agent delegation, the developer asks an agent to implement the feature in the repository. The agent can find the relevant types and make the change across files. A broad request, however, can leave important details open: what counts as returning to the screen, where state belongs, and what evidence is needed before the work is considered complete.
A managed workflow is a way to organize that same agent work. The task describes the outcome and constraints, the environment supplies appropriate tools and permissions, and the developer defines the checks and decisions that matter. It does not require a different kind of AI or an elaborate process.
| Question | Code generation | Agent delegation | Managed agent workflow |
|---|---|---|---|
| Who gathers context? | Mainly the developer | The agent can inspect the project | The agent investigates within the task and access provided |
| Who defines success? | The developer, sometimes implicitly | Requirements may be broad | Expected behavior and checks are made explicit |
| Who verifies the result? | Mainly the developer | Depends on the request and environment | Checks and reporting are agreed as part of the work |
| When does the developer participate? | Integration and review | Ambiguity and review | Product decisions, unresolved questions, and review |
The boundary between the last two columns is not absolute. A developer can delegate a task to an agent and give it a careful workflow in the same request.
Following the State Through the App
Here’s a small version of the existing screen:
struct ActivityListView: View {
@State private var viewModel: ActivityListViewModel
init(store: ActivityStore) {
self.viewModel = ActivityListViewModel(store: store)
}
var body: some View {
List(viewModel.activities) { activity in
ActivityRow(activity: activity)
}
}
}Assume the app uses Observation, and ActivityListViewModel is an @Observable reference type. Looking only at this view, an assistant might add a @State property for the selected filter. That can work while this view’s identity remains alive. It does not establish that the selection will remain after navigation destroys and recreates the screen.
In our example, the agent follows the navigation and finds that the app creates a new ActivityListView and view model every time the user opens the list. It also finds an existing ActivityStatus type and a longer-lived ActivityCoordinator, which already owns state shared across visits to this screen.
That discovery changes the implementation. The filter selection needs an owner that survives the return trip. A simplified version of the relevant change could look like this:
enum ActivityFilter: Equatable {
case all
case status(ActivityStatus)
}
@Observable
final class ActivityCoordinator {
var selectedFilter: ActivityFilter = .all
// Existing navigation and screen setup.
}The screen can bind its control to selectedFilter, and the view model can apply it to the activities using the existing status values. In this particular app, the coordinator survives the navigation path covered by the requirement, so it is a suitable owner. Another app might use a parent model or a different state container. The important discovery is the lifetime of that owner, not the name of the type.
The requirement can still leave a product decision open. Imagine the app has two activity lists in separate tabs. Should they share a filter? The agent may find that storing the selection in a shared coordinator would make that happen, while storing it in separate tab models would not. If the ticket does not answer the question, the agent can explain the alternatives and ask the developer. Once the developer chooses, it can continue.
That is a useful point for human involvement: the developer is resolving a product decision the code alone cannot settle. It does not require approving every file read or routine edit.
Defining the Work and Checking the Result
A request for the example might be:
Add a status filter to the existing activity list.
Use the project's existing activity status and state management conventions.
Keep the selection when the user leaves the list and returns to it.
The selection does not need to persist after the app is terminated.
Before editing, trace the list's data and state ownership. If the existing
architecture leaves a product decision unclear, describe the options and ask.
Run the relevant tests and build the app if the environment permits.
Report what you changed, which checks ran, and what remains unverified.
The request gives the agent room to investigate while making the expected behavior visible. It is only one part of the workflow, though. The environment must also provide the necessary files and commands, and the team needs a way to review the result. Some checks can be automated in scripts or CI; others need a person to inspect the behavior or decide what to do with an unanswered question.
For this feature, useful evidence would include:
- A build of the relevant app target to catch compiler and integration errors visible to that configuration.
- Focused tests for all, active, and completed filtering, including an empty result.
- A navigation test or a manual simulator check that selects a filter, leaves the screen, returns, and checks that the selection and list still match.
- A review of the diff and state ownership, including whether the change follows the app’s conventions.
Each check answers a different question. A filtering test alone cannot establish that selection survives navigation. A build for the simulator also does not mean the app was launched or its UI inspected.
A useful completion report might say: “The filtering tests passed and the app built for the simulator. I could not verify navigation because the simulator device failed to launch.” The developer can then decide how to close that gap. If the test suite itself cannot run, the report should say so rather than imply it passed.
The Developer’s Role
Delegating implementation changes where a developer spends time. The agent can trace code, propose a state owner, make edits, and collect test results. The developer defines the desired behavior, resolves product questions the available information cannot answer, and judges whether the change and its evidence are sufficient.
Review still calls for understanding the code. If the agent places state in a long-lived object, someone needs to verify that the object actually has the required lifetime and that sharing the state does not introduce unwanted behavior. If it introduces an abstraction, the team needs to be able to maintain it after the task is over.
The amount of process should fit the change. Renaming a private property may need little more than a quick edit and a diff review. Work on authentication or payments can justify a more explicit plan, broader checks, and approval at sensitive steps.
There is also a practical cost: describing the task, reviewing the investigation, inspecting the diff, and following up on missing checks all take time. After trying the workflow, compare that total with the effort the task would have taken directly. One task will not give you a reliable productivity metric, but it can reveal where delegation helped and where it created extra work.
Getting Started
You do not need to build a custom harness to try this. Choose a coding agent that can work with a local copy of your project, then pick a contained task whose expected behavior you understand.
Give the agent the outcome and constraints, ask it to inspect the existing implementation, and specify the checks you expect. Provide only the access needed for the task. When it finishes, read the diff and examine the evidence, including anything it could not verify.
After a few tasks, look for recurring problems. If the agent repeatedly picks the wrong build scheme or misses an accessibility requirement, a project instruction or an automated check may help. Add structure in response to those observations, and keep enough understanding of the code to take responsibility for the result.
