# Protecting policy

An agent can edit policy, but the change can't ship without a person's
approval, wherever it is written. The compiler compares the application
before and after, because access widens wherever policy is written and
also when a route becomes public.

Each widening needs an acceptance: a file in `acceptances/` in which a
person accepts that change
([Acceptances](/docs/acceptances.md)). Breaking API
changes and database drops are accepted the same way. `jadpo check`
compares the change with the default branch at its merge base through
`access.jadpo_record`, who can do what, which it keeps at the root beside
`api/`, and the API record. These need a person:

- a caller added to any grant list, an entity's or a field's
  (`library.Book.read: PolicyChange.widened`);
- a field's own row, or its `FieldAccess.server_only`, removed
  (`PolicyChange.restriction_removed`);
- tenancy loosened: an entity that belonged to the tenant now shared by
  every tenant, or the app's tenant gone (`PolicyChange.tenancy_loosened`),
  or a new entity shared by every tenant
  (`PolicyChange.shared_entity_added`);
- an action's line under `actions:` added or removed, since a listing
  replaces its entities' grants (`PolicyChange.listing_added`,
  `.listing_removed`), and a caller added to an action's line;
- a listed query's line removed, or a caller added to it;
- a route that needed a signed-in caller made open to anyone
  (`"main GET /books": PolicyChange.route_public`);
- a route that needed the person's passkey no longer asking for it
  (`"main POST /payouts/{payout_id: Uuid}/send":
  PolicyChange.confirmation_removed`);
- a decline of `policy.call_ungranted`, a security rule
  (`PolicyChange.call_ungranted_declined`).

Narrowing needs no one: a caller removed, a restriction added, a query
newly listed, a new route even one anyone may call, a new entity that
belongs to the tenant. Access also widens when an output gains a field or
an action starts touching another entity; [`jadpo
approval`](/docs/reviewing-a-change.md) shows those ways
there.

Schedules and subscribers act as the system
([Policy and background work](/docs/policy-and-background-work.md)), and what `Access.system` is granted
shows in the permissions report. The compiler's own purge of soft-deleted
records ([The purge schedule](/docs/schedules.md#the-purge-schedule)) is
the only operation outside policy. Erasing a person needs `delete` on
their record, and then reaches every record of theirs as purge does
([Erasure](/docs/erasure.md)).
