# Rules

What your agent may do by default, and what BasaltDB holds or refuses.

BasaltDB decides every action from how many rows it touches and what kind of statement it is. These are the defaults. They apply to every agent.

## The defaults

| What the agent asks for                                                      | What happens                                               |
| ---------------------------------------------------------------------------- | ---------------------------------------------------------- |
| A read (`SELECT`)                                                            | Allowed, within a row limit and a time limit. Recorded.    |
| An insert or an update of 1 to 10 rows                                       | Goes through. The reason is recorded and old values saved. |
| An insert or an update of 11 to 50 rows                                      | Held until you approve it, with a preview of the rows.     |
| Any `DELETE` of up to 50 rows                                                | Held until you approve it, with a preview of the rows.     |
| `CREATE`, `ALTER`, `DROP`, `TRUNCATE` (DDL)                                  | Refused. Agents can't drop or change tables.               |
| More than 50 rows in one action                                              | Refused.                                                   |
| Several statements at once, `COPY`, data-changing `WITH`, unlisted functions | Refused.                                                   |

## How rows are counted

Rows are counted per action, and cascaded rows count too. A delete of 5 rows that cascades into 50 more counts as 55 and is refused. A delete that cascades into 3 rows counts as 8 and is held.

The count is made in your Postgres by the guard, not only by the engine. That is why a direct connection with the agent's credential meets the same limits.

## What an agent sees

A write that goes through returns `"status": "APPLIED"` and the number of rows:

```json
{ "status": "APPLIED", "rows": 3 }
```

A write that is held does not run. The agent gets `APPROVAL_REQUIRED` with a link for you, and shows it to you.

A write that is refused is a tool error with a code and, for a refused statement, a reason:

```json
{ "code": "STATEMENT_REFUSED", "reason": "DDL_NOT_ALLOWED" }
```

A held write is `APPROVAL_REQUIRED`, too many rows is `ROW_LIMIT_EXCEEDED`, and DDL is `STATEMENT_REFUSED` with the reason `DDL_NOT_ALLOWED`. [Errors](/reference/errors/) lists every code and what to do about it.

## Why these numbers

- **Up to 10 rows** covers the everyday work an agent does on its own: fix a record, add a note, qualify a lead.
- **11 to 50 rows** is a change large enough that you should look at the exact rows first.
- **Deletes** are never applied without you, because a deleted row is the hardest thing to notice later.
- **Over 50 rows** belongs in a migration or a data pipeline that a person runs. A loop of small writes is not blocked. It lands in one run and you can undo it at once.

A held write waits for your approval. If you do not answer within 24 hours, it is blocked. See [Approvals](/concepts/approvals/).

## What the rules do not catch

A change that is valid, touches one row and is wrong for your business goes through. It is recorded and you can undo it later, but nothing stops it first. See [Limits](/limits/).

## Next

- [Reference: limits](/reference/limits/): the numbers and defaults in one table.
- [When something is refused](/guides/refused/): what to do when an agent reports a refusal.
- [Undo](/concepts/undo/): what happens after a change went through.
