Greetings, traveler!
A request to add a deep link sounds small: parse a URL and open a screen. In an established iOS application, the URL may arrive through a custom URL scheme or a universal link. A notification can also contain a URL or other data used to navigate after the user taps it. The resulting navigation may need to wait for authentication, pass through an application coordinator, and eventually reach a feature in a separate Swift package.
An agent can write the parser without knowing any of that. It can even produce code that compiles. But if it opens the screen through a new navigation path while the rest of the app uses a coordinator, the implementation may behave differently after a cold launch or when the user is signed out.
The difficulty is not simply describing the requested change. The agent also needs to find the parts of the project that determine where that change belongs. Context engineering is the work of making those parts available at the right time, while keeping the task, project rules, and evidence easy to distinguish.
This article follows a fictional modular iOS application through that process. The package names and examples are illustrative; adapt them to the structure and behavior of your own project.
A prompt is only part of the context
Prompt engineering focuses on how a request is expressed. A useful prompt might describe the new route, give examples of valid URLs, and explain what should happen when a required parameter is missing.
Context engineering has a wider scope. It includes the information the agent receives throughout the task: project instructions, source files, tool output, earlier decisions, build results, and the conversation itself. It also includes how the agent finds that information and what happens to it as the session grows.
For example, this request gives the agent a goal but leaves important decisions open:
Add support for opening an account from a deep link.
A more useful starting point supplies the product behavior and asks the agent to investigate existing conventions:
Add support for opening an account from a deep link.
Example: myapp://account?id=123
If the ID is missing or invalid, do not open an account.
Investigate the existing authentication and navigation flow.
Describe what happens if the user is signed out, including whether
the destination is retained after sign-in or a canceled sign-in.
Before changing code, identify the entry points, URL parsing,
route dispatch, relevant package boundaries, and existing tests.
Report what you found and any behavior that remains unclear.
This still does not tell the agent which file to edit. That is deliberate: a guessed path would turn an assumption into an instruction. The request instead gives the agent a specific investigation to perform.
The engineer remains responsible for the product decisions. In this example, the expected behavior for a signed-out user is not fully specified. If the existing implementation does not settle that question, the agent should surface it instead of inventing an answer.
What actually fits in a context window
The context window is the amount of information a model can work with in a single request. Depending on the system, it can include instructions, conversation history, code, tool definitions and results, and space reserved for the model’s response. The exact accounting varies between products and models.
A large window makes it possible to consider more material at once. It does not make every piece equally useful. A long build log can bury the one compiler diagnostic that matters. An outdated architecture document can compete with the code the agent just inspected. A large search result can make it harder to notice that an important entry point was never found.
There is also a practical cost. More input can increase processing time or expense, depending on the model and pricing. Some tools truncate long output before the model receives it, so asking for an entire file or log does not necessarily mean the agent saw it all.
The agent needs enough surrounding code to understand a contract and enough evidence to verify a claim. A useful question is: what information is needed for the next decision, and where can the agent find more if that information is insufficient?
Start with a map, then follow the request
Imagine the application has several Swift packages: one owns shared navigation contracts, another provides account features, and the app target assembles them. Before changing anything, the agent needs to establish whether that description matches the repository.
A first pass can inspect package manifests, project configuration, and likely entry points:
rg --files -g 'Package.swift' -g '**/*.xcodeproj/project.pbxproj' \
-g '**/*.xcworkspace/contents.xcworkspacedata'
rg -n 'onOpenURL|onContinueUserActivity|NSUserActivityTypeBrowsingWeb|continue userActivity|openURLContexts|connectionOptions|open url' \
--glob '*.swift'
rg -n -i 'deep.?link|universal.?link|route|router' --glob '*.swift'
rg -n 'userNotificationCenter|didReceive response' --glob '*.swift'
These searches are starting points. Entry points may be in an app delegate, scene delegate, SwiftUI scene, or an integration layer, and projects name their types differently. Inspect both the launch path and the path used while the app is already running. UIKit scene setup can receive URLs or activities through connection options; a notification response may enter through a separate handler. Search results identify possible locations, so the agent still needs to read callers and follow values through the code.
By default, rg respects ignore rules and skips hidden files. If an expected project file is missing from the results, inspect the directory and its ignore rules before assuming the file does not exist. Configuration can also live outside the searched Swift files.
For the account link, a useful discovery report might look like this:
Observed:
- The app target receives URLs in App/LinkEntry.swift.
- URLParser converts supported URLs into AppRoute values.
- AppCoordinator handles routes after the session is ready.
- AccountFeature exposes an account destination through a public API.
- RouteParserTests cover several existing routes.
Still unclear:
- Whether a pending route survives an interrupted sign-in.
- Whether a malformed account ID should be logged or silently ignored.
Next:
- Inspect the coordinator's pending-route behavior and its tests.
- Confirm the expected response to malformed IDs.
In a real report, each observation should point to a file and preferably a symbol or line range. That lets the developer, or a later agent, check the claim against the code.
There is a difference between finding a declaration and understanding its role. A route enum might define account(id:), but the agent still needs to find who constructs it, who handles it, and whether an existing feature already owns the destination. Package manifests can help reveal declared dependencies; they do not by themselves show the complete runtime flow.
Load detail when a decision requires it
Progressive disclosure is a practical way to organize this investigation. Start with a small map of the project, then open more detailed material as the task narrows.
For the deep-link change, the sequence could be:
- Read the project entry instructions and identify the app target and relevant packages.
- Search for URL entry points and route definitions.
- Read the code around the actual call chain, including authentication and navigation handoff.
- Inspect neighboring routes and their tests before proposing a change.
- Read feature-specific documentation if the destination contract is still unclear.
The agent can then read the relevant package documentation and navigation code without relying on a single search match.
Progressive disclosure depends on discoverability. A document that is never mentioned in an index, repository instruction, or nearby code is unlikely to help. Likewise, a document with a promising title but obsolete content can be worse than no document at all. A short index should tell the agent where to look; the implementation and tests still need to confirm what is true today.
Skills can use the same idea. An agent may have access to a short description of a reusable workflow and read its detailed instructions only when that workflow becomes relevant. The exact loading behavior depends on the agent. Do not assume every tool discovers skills, instruction files, or nested directories in the same way.
Give durable knowledge a home
Some information is specific to the current task: the requested URL format, acceptance criteria, and unresolved questions. Other information should help with many future tasks: how the application is assembled, how to build it, and which module owns navigation.
Repository instruction files such as AGENTS.md or CLAUDE.md can provide a small entry point for that durable knowledge. Which file is read, when it is read, and whether nested files apply depend on the agent being used. Check its behavior before relying on a particular layout.
A compact project-level file might contain:
## Project orientation
- The app target assembles feature packages from `Packages/`.
- Shared route types live in `Packages/AppNavigation`.
- Start with `docs/navigation.md` for the navigation overview.
## Working in this repository
- Preserve public package APIs unless the task requires a change.
- Do not edit generated files directly.
- Use the commands in `docs/building.md` for local verification.
- Report checks that could not be run and why.
These paths are examples. In a real project, they should point to files that exist and are maintained. The root instructions should help the agent find authoritative details, not duplicate an entire architecture document. Feature-specific rules can live closer to the feature if the agent supports scoped instructions; otherwise, link to them explicitly.
Keep task notes separate from project documentation. A temporary claim such as “the current build fails because a simulator is unavailable” belongs with the work item, not in permanent project instructions. A stable rule such as “generated API clients are produced by this script” belongs in maintained documentation.
Both kinds of information need maintenance. When a module moves or a build scheme changes, update the map alongside the code. It is useful to record an owner or review date for larger architecture documents. For critical decisions, compare the documentation with the current implementation rather than trusting either a stale document or a quick search in isolation.
Preserve decisions when a session gets long
As an agent works, its conversation accumulates searches, file contents, failed commands, and intermediate reasoning. Eventually the system may need to reduce the amount of active context. Different agents do this differently, but three related ideas are worth separating.
Summarization produces a shorter account of selected information. A task summary might record the files inspected, the chosen approach, and unanswered questions.
Compaction is a broader context-management operation in which a system reduces older conversation material, often using a summary, so work can continue. What survives, when it happens, and whether the original history remains accessible depend on the implementation.
Offloading keeps detailed material outside the active context and retains a way to retrieve it. A build log on disk is a simple example: the agent can work from a short diagnostic extract and return to the full output when needed. A file on disk is not automatically available to a future session; its path, access permissions, and retention all matter.
These techniques save space, but summaries are lossy. A useful checkpoint for the example would preserve more than “deep-link work in progress”:
Goal: Add myapp://account?id=<id> support.
Confirmed: URLParser produces AppRoute; AppCoordinator dispatches routes.
Evidence: App/URLParser.swift, App/AppCoordinator.swift, and related tests.
Decision: Reuse the existing account route; do not add a new coordinator.
Open question: What happens if sign-in is interrupted?
Changes: Parser and parser tests modified; coordinator unchanged.
Verification: Parser tests passed on the current changes; app build not run.
Next: Resolve the sign-in question and run the relevant app scheme.
This is an illustrative checkpoint, not a report from an actual build. In practice, include the command and enough version or working-tree information to tell which state a verification result applies to. After further edits, rerun the checks affected by those edits. A previous green result is useful history, but it does not prove that the current code passes.
A checkpoint should also separate observations from assumptions. If the coordinator appears to queue a route but the agent has not traced what happens after a canceled login, that uncertainty should remain visible in the summary.
Keep build output available without flooding the conversation
A full xcodebuild log may contain package resolution, compiler invocations, warnings, script output, and repeated diagnostics. Sending all of it into an agent’s context can consume space without helping the next decision.
One option is to save the complete output and inspect a small portion first. The following example uses Bash and writes a fresh log for each run:
mkdir -p .local-build-logs
log_file="$(mktemp .local-build-logs/build.XXXXXX)"
xcodebuild \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-destination 'generic/platform=iOS Simulator' \
build >"$log_file" 2>&1
build_status=$?
printf 'Build exit code: %s\nFull log: %s\n' "$build_status" "$log_file"
rg -n -i -m 10 -C 3 'error:|fatal error:|BUILD FAILED' "$log_file" || true
exit "$build_status"
Replace the workspace and scheme with values from the project. The command retains the actual xcodebuild exit code before searching the log. The search shows up to ten matching lines and their surrounding context, so it may leave out later diagnostics. Some failures do not match these patterns at all, and matching text alone does not establish why the build failed. If the exit code is nonzero and the extract is inconclusive, inspect other parts of the full log.
This script assumes it can run through the build_status assignment even when xcodebuild fails. If your shell or CI script enables set -e, handle the build failure explicitly so the status and diagnostics are still captured. If you pipe output through tee or a formatter instead, take care to preserve the build command’s exit status rather than reporting only the status of the last command in the pipeline.
Logs may contain local paths, user data, or secrets printed by build scripts. Keep them in an appropriate local or CI artifact location, apply your team’s retention and redaction rules, and avoid committing them to the repository. When sharing an extract, include enough surrounding lines to make the diagnostic understandable without exposing unrelated sensitive output.
The same approach works for large test reports: inspect relevant results first, then return to the full report when the failure needs more context.
How to tell whether the context is helping
A well-organized context should improve the work, not merely produce shorter conversations. For the deep-link task, ask whether the agent identified the real entry points, located the existing route contract, found the relevant tests, and raised questions that the code could not answer. Review the proposed diff for changes outside the necessary modules.
A team can compare approaches using a small set of representative tasks. Give agents the same starting revision and requirements, then compare their findings and final changes. Track missed entry points, incorrect architectural assumptions, relevant tests run, developer corrections, elapsed time, and cost where available. Repeat tasks when practical: a single successful run is weak evidence that a documentation change helped.
The comparison has limits. Agents can take different paths on repeated runs, and a task set can favor the exact documentation it was designed around. Include unfamiliar tasks and review failures, not just successes. More importantly, judge whether the result is correct; a smaller token count or a faster run is useful only if the necessary investigation still happens.
Context also has a maintenance cost. A small project may need only a few build instructions and a clear README. A large modular application may benefit from an architecture map, module ownership, and feature-specific guidance. Add a document when it answers a recurring question, and remove or revise it when the code moves on.
For the account link, the investigation should leave the engineer with a concrete route through the application: where the URL enters, how it becomes an AppRoute, what happens when authentication is required, and which feature receives the destination. With those paths and the remaining questions visible, the team can decide what to change and which tests should cover it.
