Registry stack documentation: machine-readable Markdown.
Index of all pages: https://docs.registrystack.org/dev/llms.txt
Full corpus: https://docs.registrystack.org/dev/llms-full.txt

# Actions and Proposals

> Declare Actions that change records at once or through a reviewed Proposal, with their Plans, review authorities, and Evidence.

If your Base Registry Engine (BReg) registry needs changes beyond field-by-field edits, declare
them as Actions. Here you see what an Action declares, when it applies at once and when it becomes
a reviewed Proposal, and how a Plan computes its effects.
[Change a registry with an Action](../../tutorials/change-a-registry-with-an-action/) runs both.

## Start from the facility template

```sh
bregctl init --template facility-registry facility
bregctl check facility
```

The template declares three Actions: `register-facility`, `correct-address`, and
`deactivate-facility`; `bregctl explain actions facility` shows each one compiled.

## What an Action declares

An [Action](../../reference/glossary/#action) has typed `inputs`, an `execution` mode, a `plan`
of effects, and optional `commitGuards`. `deactivate-facility` holds all four:

```yaml title="registry.yaml" test-excerpt="facility/registry.yaml"
  - id: deactivate-facility
    display:
      label: Deactivate facility
      description: Submit a durable deactivation that requires no external review.
    execution:
      mode: proposal
      proposal:
        review: {type: none}
        application: {mode: manual}
        retention: {mode: retain}
    commitGuards:
      targets:
        - {input: facility, revision: unchanged}
        - {input: facility, field: status, equals: active}
    inputs:
      - {id: facility, apiName: facilityId, type: reference, target: facility, required: true, classification: internal}
    plan:
      type: fixed
      effects:
        - id: facility-status
          target: {fromInput: facility}
          operation: patch
          set:
            status: {value: inactive}
```

Commit guards are checked when the change commits: here, the facility must be unchanged since
the Proposal froze it, and still active. The caller never chooses the execution mode.

Who may run an Action is an access-profile grant, `invoke` for an immediate Action and `start`
for a Proposal:

```yaml title="registry.yaml" test-excerpt="facility/registry.yaml"
      actions:
        - action: correct-address
          operations: [start]
```

Other Proposal operations, such as `submit`, `cancel`, or `apply`, are granted separately under
`permissions.proposals`, so whoever drafts a change need not apply it.

{/* evidence: crates/registry-breg/src/contract.rs (ActionSource);
    crates/registry-breg/src/immediate_actions.rs; products/breg/examples/facility/registry.yaml. */}

## Immediate Actions return an ActionReceipt

`register-facility` declares `execution: {mode: immediate}`. One request checks the guards,
writes every effect, and commits the new revisions, the receipt, the audit entry, and any outbox
events together, or none of them. The answer is an
[ActionReceipt](../../reference/glossary/#action-receipt) naming each changed Record and its new revision.

A Record type can require a Proposal for an operation:

```yaml title="registry.yaml" test-excerpt="facility/registry.yaml"
    mutationPolicy:
      patch: proposal-required
```

With this on `facility`, neither a direct patch nor an immediate Action may patch a facility; only
a Proposal can. `register-facility` still creates facilities at once.

{/* evidence: crates/registry-breg/src/mutation/action.rs (apply_action_effect_group);
    crates/registry-breg/src/immediate_actions.rs. */}

## Proposals

An Action in `proposal` mode creates a [Proposal](../../reference/glossary/#proposal) instead.
`correct-address` requires review:

```yaml title="registry.yaml" test-excerpt="facility/registry.yaml"
    execution:
      mode: proposal
      proposal:
        review:
          type: required
          authority: casework
          source: facility-registry
```

A Proposal starts as a `draft` its author can edit. Submitting it freezes the inputs, the
computed effects and their digest, the target revisions, and the Action revision; review and
application work on that frozen submission, so a later change to the Plan or policy cannot
reinterpret it. Revise or Rebase returns it to a new draft, which must be submitted, and
reviewed, again.

A Proposal ends in one of three states:

- `applied`: the registry wrote the frozen effects, under the commit guards as they stand at that
  moment. With no review it follows submission; with required review it needs current approval of
  that exact submission.
- `rejected`: the review authority rejected the submission. This is final. A reviewer who wants
  another attempt returns it with changes requested instead, which keeps it `submitted` and offers
  Revise.
- `cancelled`: a caller with the `cancel` grant withdrew it before it applied.

Application is automatic, under a restricted system grant for that one submission, unless the
Action declares `application: {mode: manual}`, as `deactivate-facility` does; then a caller with
the `apply` grant applies it. The Proposal's `applicationStatus` reports progress: `awaiting-review`, `pending`,
`applying` while a worker is writing it, then `applied`, or `blocked` with a reason and a
recovery hint.

{/* evidence: crates/registry-breg/src/proposal_workflow.rs (ProposalState, revise, rebase,
    cancel, reject, apply); crates/registry-breg/src/mutation/proposal.rs (application status);
    crates/registry-breg/src/artifacts.rs (proposal_application_status_schema). */}

## Review authorities, declared twice

The project names each review authority it relies on:

```yaml title="registry.yaml" test-excerpt="facility/registry.yaml"
reviewAuthorities:
  - id: casework
    protocol: id.registrystack.org/protocols/review/v1
    display:
      label: Casework review authority
      description: Logical authenticated review authority resolved by deployment configuration.
```

The deployment binds that `id` to a running service. For local development, the template's
README gives the `dev-clients.yaml` binding:

```yaml title="README.md" test-excerpt="facility/README.md"
reviewAuthorities:
  casework:
    endpoint: http://127.0.0.1:8100/
    profile: requester
    producerId: facility-registry
    recoveryDays: 30
    client: requester
```

In production the same map sits in the runtime configuration (`runtime.yaml`), with a `tokenRef`
or `privateKeyJwt` credential instead of `client`. A missing binding disables only the operations
that need it. An unreachable authority never counts as approval.

Registry Casework is one such authority. The registry sends it the frozen submission and applies
only the correlated result;
[Review a Proposal in Casework](../../tutorials/review-breg-changes-in-casework/) pairs the two.

{/* evidence: crates/registry-breg/src/runtime_config.rs (RawReviewAuthorityConfig,
    activate_review_authorities); products/breg/examples/facility/README.md. */}

## Plans and Evidence

A [Plan](../../reference/glossary/#planner) is fixed, like `deactivate-facility`'s, or computed
by a Rhai Function. `correct-address` names its Function and bounds what it may write:

```yaml title="registry.yaml" test-excerpt="facility/registry.yaml"
    plan:
      type: function
      function: correct-address-plan
      inputs: [facility, address-line, city, postal-code]
      writes:
        - id: facility-address
          target: {fromInput: facility}
          operation: patch
          fields: [address-line, city, postal-code]
      refusals:
        - {code: invalid-postal-code, label: Postal code is invalid}
```

The project's `functions` entry names its script:

```rhai title="plans/correct-address.rhai" test-excerpt="facility/plans/correct-address.rhai"
fn plan(ctx) {
    let postal_code = ctx.inputs["postal-code"];
    postal_code.trim();
    if postal_code == "" {
        return #{
            refusal: #{
                code: "invalid-postal-code",
                field: "postal-code"
            }
        };
    }
```

The planner receives `ctx.inputs`, holding only the inputs the Plan lists. It returns either a
`refusal`, whose `code` the Action declares, or `effects`: a list of maps, each with the `id` of a
declared write and its `set` or `clear` fields. The declaration fixes each write's target and
operation, so the script cannot redirect one. Omitting a write skips it; the caller still needs
authority over its target.

Effects apply in authored order. A value `fromEffect` must name an earlier `create`, as
`register-facility`'s assignment names its new facility. Two effects that land on the same Record
are written together as one revision; two effects that write the same field fail `bregctl check`.

An Action with a Rhai Plan may also declare `evidence`: at most two capabilities, each naming an
Evidence provider the project declares, a requirement, its subjects, and the outputs it may read.
The script calls `evidence::resolve("<capability id>", subjects)` once per capability and
receives only those outputs; a failed call refuses the operation. `commitGuards.evidence`
instead checks a requirement at commit and never changes the effects. The provider's endpoint and
credentials belong to the runtime configuration; BReg inherits no Evidence authorization.

{/* evidence: crates/registry-breg/src/action_handler.rs (execute_compiled_handler);
    crates/registry-breg/src/action_outcome.rs (decode_document, validate_proposed_effects);
    crates/registry-breg/src/plan.rs (order_plan_effects, order_effect_groups);
    crates/registry-breg/src/mutation/action.rs (ordered_action_effect_groups);
    crates/registry-breg/src/immediate_actions.rs (remember_write);
    crates/registry-breg/src/action_evidence_contracts.rs;
    crates/registry-breg/src/contract.rs (CommitEvidenceSource). */}

## Next

- [Change a registry with an Action](../../tutorials/change-a-registry-with-an-action/)
- [Control access and reads](../breg-access/)
- [Review a Proposal in Casework](../../tutorials/review-breg-changes-in-casework/)
- [Base Registry Engine API reference](../../reference/breg-api/#proposals)