Introduction

Suppose we ask an agent to update the renewal date on a subscription screen. It finds the controller, changes the saved value, and updates a test. The response looks correct. But the renewal email still shows the old date because a queued job builds it elsewhere in the application.

That change is incomplete, even with a passing test.

Before reaching for a better prompt, I would check whether the repository gives the agent a way to find that job. Where is the renewal flow documented? Do the tests cover the email? Would a developer new to the project miss the same thing?

Those questions are what make Agent Experience (AX) engineering useful to me. We already spend time making projects easier for developers to work in. An agent needs that help too, especially when it starts a task without the history that the team takes for granted.

What Is Agent Experience Engineering?

AX engineering means designing the environment an agent works in so it can find the relevant code and check its changes, with clear limits on what it is allowed to do. Repository structure and tests are part of that environment. So are instructions and tool permissions.

In My Take on Vibe Coding VS Agentic Engineering, I described how I use requirements, planning, and review when working with agents. AX looks at the project that workflow runs against. We can write a careful plan and still miss the renewal email if the repository gives us no clue that it exists.

For our subscription example, the agent needs to follow this path:

Change request
    │
    ▼
Find the owning module ──▶ Find the HTTP, database, and queue paths
    │                                  │
    ▼                                  ▼
Read local rules               Identify behavior that must not change
    │                                  │
    └──────────────┬───────────────────┘
                   ▼
          Make a bounded change
                   │
                   ▼
         Run focused tests and review the diff
                   │
                   ▼
          Human reviews and decides to ship

The queue path is easy to miss here. Finding the HTTP entry point only gets the agent part of the way to understanding what the customer receives.

Make the Change Path Discoverable

Let's say our application has a Billing module that owns subscriptions and renewal emails. Reporting reads subscription state, but should not change it. That ownership is worth writing down before asking an agent to work on either area.

One way to organize those notes is to keep three files under .memory/:

.memory/
├── PROJECT_STRUCTURE.md  Where does the behavior live?
├── STANDARDS.md          How do we work in this project?
└── MEMORY.md             What durable decisions and pitfalls should we remember?

For this hypothetical application, the notes could look like this:

<!-- .memory/PROJECT_STRUCTURE.md -->
# Project Structure

Billing owns subscriptions, payment attempts, and renewal notifications.
For renewal changes, trace the HTTP entry point, renewal action, queued
notification job, and email template. Reporting reads subscription state
but does not change it.
<!-- .memory/STANDARDS.md -->
# Standards

Keep Billing changes within the owning module and its public interfaces.
Run the focused Billing feature tests and check queued notification behavior
before requesting review.
<!-- .memory/MEMORY.md -->
# Project Memory

Renewal emails are built by a queued job, not by the HTTP request. A test
that checks only the response will miss changes to the email content.

The queue note is the useful detail here. It tells the agent where to look beyond the controller. We should confirm it in the implementation before recording it, then correct it if the flow changes. A stale note pointing at the wrong job can send the next task in the wrong direction.

I would keep these files small. They need to help someone enter the codebase, and copying the whole architecture into them creates another document to maintain. A small application might need only a few paragraphs in one file. The split above is useful when structure, standards, and decisions have enough content to warrant separate files.

Naming also does some of this work without documentation. RetrySubscriptionPayment gives us a better starting point than ProcessService. If understanding a class requires reading a separate note every time, I would check whether its name and responsibility could be clearer first.

Write Instructions the Agent Can Use

The agent also needs an entry point for those notes. An AGENTS.md file can provide one if your coding tool reads it. Check your tool's support and loading rules before relying on that filename.

For our example, I would put the scope and review rules alongside the links:

# Working in this repository

Before changing code, read `.memory/PROJECT_STRUCTURE.md`,
  `.memory/STANDARDS.md`, and `.memory/MEMORY.md`.
Check those notes against the code. If they disagree, follow the code and
  correct the stale note.
Keep edits within the requested behavior and its tests.
Run the focused Billing tests, then report what ran and what did not.
Before finishing, update the relevant memory file if you learned a lasting
  fact or changed the project's structure or standards. Skip temporary notes.
Do not modify `.env`, production credentials, or deployment settings.
Ask before migrations that change existing data or external payment behavior.
Never use customer data in fixtures, prompts, or logs.

Moving the renewal job to another module should lead to an update in PROJECT_STRUCTURE.md. Recording "I updated the controller today" would add noise. The next task needs the current change path, not a diary of how we got there.

The restrictions deserve a closer look. Writing "do not modify production credentials" in a file does not prevent a tool from modifying them. We still have to enforce access through file and service permissions, and keep deployment credentials out of the agent's reach unless the task requires them. High impact operations need an explicit human approval step.

Retrieved text can also contain instructions that conflict with the task. A source comment or an external page telling the agent to ignore its rules should be treated as untrusted content. Instructions alone are not a security boundary.

For a renewal bug, I would give the agent local fixtures it can safely inspect. Production customer records should not become test data or end up copied into prompts and logs.

Build a Feedback Loop That Catches the Real Failure

Running php artisan test is useful, but we need to know what the assertions prove. A controller test checking a successful response says nothing about the date rendered in the renewal email.

I would make that date part of the requirement:

When a renewal succeeds, the saved subscription and the queued email both contain the updated renewal date. When payment fails, neither claims the renewal succeeded.

That gives us specific behavior to test. Check the saved date after a successful renewal, then check the job and email content. On a declined payment, assert that the application does not queue a success email.

There is a testing detail worth watching in Laravel. Queue::fake() lets us assert that a job was pushed, but the fake does not execute that job. If the old date is selected inside the job, a dispatch assertion will miss it. Test the job's behavior separately and check the mailable's content where the date is rendered. Laravel's queue testing and mailable testing documentation cover those separate checks.

The working loop remains straightforward:

  1. Write down the expected behavior and the failure case.
  2. Locate the owning code and tests before editing.
  3. Change the smallest part of the flow that satisfies the request.
  4. Run focused tests and static checks used by the project.
  5. Review the diff for unrelated edits, missing paths, and unsafe data handling.
  6. Report the checks that ran and any gaps that remain.

Focused tests can give us a quick first answer. If the agent stops there, its report should say which behavior remains unchecked. I want to know that the email test did not run before reviewing the change, rather than discovering that gap after it ships.

Improve AX Without Building an Agent Platform

I would start with one repeated mistake. If agents keep changing Reporting for a Billing feature, clarify ownership and enforce the dependency rule in code or tests where possible. If they keep missing the renewal email, add coverage for it. Another instruction file is only useful if missing instructions caused the problem.

Then look at the next few tasks that touch that area. Does the agent find the job without being redirected? Does its verification include the email? Are we still making the same correction during review? Those observations tell us more than the length of AGENTS.md.

Some decisions still need us. An agent can trace a payment retry and collect evidence, but someone has to decide whether retrying could charge a customer twice. The same applies to changing existing data in a migration. Better context helps us review those risks. It does not hand responsibility for the release to the agent.

Conclusion

For the subscription example, I would start by documenting the queue path and adding the missing email test. That is enough to address the failure we identified. If the agent still misses it, we can inspect how it finds and uses those notes before adding more tooling.

AX is useful when it leads us to that kind of concrete improvement. I would be cautious about turning it into a separate platform project while the repository still has unclear names or tests that leave customer behavior unchecked.

Try tracing one recent agent change through the background work it touches. Check whether the repository would help a fresh agent find that work without you pointing it out.