The editor loop is the tightest of the agent loops, because you never leave the file and you see
every change in place before you accept it. The tradeoff is that accepting is one keystroke, which
pulls the work toward many small edits. That is excellent for refining a feature and worse for
building a whole one, where you want the model, the policy, the controller, the screens and the tests
to arrive as one coherent set. This page is the loop that works around that.

## Start in Plan mode

Press Shift and Tab from the chat input to rotate modes, or pick Plan from the mode dropdown in
Agent. The agent asks clarifying questions, reads the codebase, and writes an implementation plan
before it writes any code. You review and edit that plan as markdown, then build it when it looks
right.

This is worth the extra step for a whole feature, because the plan is where a wrong assumption is
cheap to fix. The two assumptions to check every time:

- **Is the resource account-scoped?** It should be, and the plan should say so. Everything holding
  customer data in this app belongs to an `Account`. See
  [Accounts and Tenancy](/docs/core-concepts/accounts-and-tenancy).
- **Is the feature paid or free?** This is the one input no agent can infer from a feature
  description, and it decides whether the controller includes `RequireEntitlement`. Say which one you
  want in the first message. See [Gating a Feature](/docs/billing-entitlements/gating-a-feature).

Plans are saved to your home directory by default. Click "Save to workspace" to move a plan into the
repository, which is what you want if the feature spans more than one session or another person will
pick it up.

## The first pass

Name the shape rather than describing it:

```
Add a Bookmarks resource following the same shape as the example Project slice:
model, policy, controller, views, routes, and specs. Free, not gated.
```

"The same shape as the example Project slice" points at files that already exist, which is far more
precise than any description you could type, and it is exactly what `crud-with-billing` does if you
invoke the skill directly instead. Either route produces the same thing. See
[Adding a Resource](/docs/building-features/adding-a-resource).

What comes back should touch the model, the migration, the policy, the controller, the views, the
route, and three spec files.

## Read two files properly, skim the rest

**The migration.** It must have `t.references :account, null: false, foreign_key: true`. A missing
`account_id` is the one mistake in this codebase that is expensive to fix later, because by then
there are rows.

**The policy.** It must inherit the account-filtering `Scope`, and the controller must call
`policy_scope` on collections and `authorize` on individual records. Look at
`app/policies/project_policy.rb` next to it if you are unsure what correct looks like. See
[Authorization](/docs/core-concepts/authorization).

Everything else, skim. Views and route lines are cheap to fix and obvious when wrong.

The habit worth building here is to look at what the diff touched, not only what the summary says.
The summary describes intent; the file list describes blast radius. When they disagree, believe the
file list.

## Verify before you accept a run of edits

```bash
bin/check
```

RSpec, RuboCop, Brakeman, bundler-audit, the importmap audit, and a secret scan. Green is the
definition of done. Run it after a run of edits rather than after a dozen, because the tight loop
makes it genuinely easy to get twelve edits deep, and then the failure could be from any of them.

One spec is worth opening by hand rather than trusting the summary: the tenant-isolation test in the
generated request spec, where a signed-in user asking for another account's record gets a 404. If it
is missing, the resource is not finished. See
[Verifying Your Setup](/docs/getting-started/verifying-your-setup) for what each tool in `bin/check`
catches.

## Two ways this goes wrong in an editor specifically

**Context you did not intend.** Cursor reads the files you have open along with the ones it decides
are relevant. If a suggestion is oddly shaped, check your tabs before blaming the model. The flip
side is the cheapest trick available: open the existing example of the thing you are building in
another tab before you ask.

**Small steps hiding structural drift.** Twelve good local edits can arrive at a structure nobody
would have chosen deliberately. Every so often, read the whole feature as one thing rather than as
the diff you last accepted.

## The failures that produce no error at all

Three of them, all caused by the enforced Content Security Policy, and all invisible: the button just
does nothing.

- A form that redirects to an external host needs that host in `policy.form_action` in
  `config/initializers/content_security_policy.rb`, because `form-action` covers the whole navigation
  a form starts, including the redirect.
- That same form needs `data-turbo="false"`, on the form or the submit button, or Turbo submits it
  with `fetch` and tries to follow the cross-origin redirect as an XHR, which `connect-src` blocks.
  Both conditions must hold.
- An inline `<script>` in a view is dropped, because script tags carry a nonce. Interactive behaviour
  goes in `app/javascript/controllers/<name>_controller.js`, which auto-registers with no importmap
  edit and no build step.

The `controllers.mdc` rule described in [Rules for One Shot](/docs/cursor/rules-for-one-shot) exists
to put all three in front of the agent whenever it touches a controller, a view, or a Stimulus
controller.

## Next

You have the whole loop. Move on to publishing:
[Writing a Blog Post](/docs/content-seo/writing-a-blog-post).
