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

# Review Base Registry Engine changes in Casework

> Connect Base Registry Engine (BReg) to Registry Casework, submit a frozen proposal for policy-owned review, and apply an exactly approved result under current source authority.

import QuickstartMeta from '../../../components/QuickstartMeta.astro';

This journey joins two independent responsibilities. Base Registry Engine (BReg) authors and
freezes a proposed registry change. Casework evaluates that proposal under its own policy. BReg
applies only an exact approved result, and only through current source authorization and guards.

<QuickstartMeta
  outcome="A BReg proposal reviewed under Casework policy and applied through BReg's guarded source action."
  time="About 30 minutes after both local projects start"
  level="Local evaluation only"
  prerequisites={['Matching bregctl and caseworkctl releases', 'Running PostgreSQL', 'A shell, curl, and Python 3']}
/>

## Create and connect the projects

Use the paired starters. Their shared source namespace is `professional-licences`; Casework
requires its source id to equal BReg's `registry.id` so submission and source-context reads address
the same record.

```sh
mkdir -p review-work
bregctl init review-work/registry --template professional-licences
caseworkctl init review-work/casework --template professional-review

bregctl check review-work/registry
bregctl explain change-requests review-work/registry
caseworkctl source add review-work/registry \
  --project review-work/casework --source-id professional-licences
caseworkctl source add review-work/registry \
  --project review-work/casework --source-id professional-licences --apply

caseworkctl check review-work/casework
caseworkctl explain review-work/casework
caseworkctl test review-work/casework
caseworkctl package review-work/casework --output review-work/casework-package
```

The first `source add` is a preview. It shows the exact BReg authoring changes, the Casework
source description, and a candidate BReg runtime fragment without writing them. The `--apply`
run checks a staged BReg copy before it writes `registry.yaml`, `dev-clients.yaml`, the imported
description, and the runtime fragment. It provisions no secrets and activates neither product.
Review `review-work/casework/sources/professional-licences.breg-runtime.yaml`, replace its example
endpoints and secret references, and merge it into the launcher-owned BReg runtime file.

For the local evaluation, the applied `dev-clients.yaml` already binds BReg's authority client to
Casework's default loopback port. Start BReg first so Casework can borrow its retained issuer:

```sh
bregctl dev start review-work/registry
caseworkctl dev start review-work/casework \
  --source-project review-work/registry
curl --fail --silent http://127.0.0.1:8090/ready
curl --fail --silent http://127.0.0.1:8092/ready
```

When you finish, preserve or remove each product's owned local database explicitly:

```sh
caseworkctl dev stop review-work/casework
bregctl dev stop review-work/registry
```

The generated fragment includes both sides of the connection: the lifecycle event destination
and read-only Casework source profile, plus `reviewAuthorities.casework`. The starter keeps
`onApproved.mode: manual`, uses result polling, and has no completion destination. These defaults
keep application authority in an explicit BReg operator action.

## Author the BReg request

The request binds to a logical authority and Casework policy. Application remains a separate
source choice:

```yaml
changeRequest:
  effects:
    - target: {fromField: record}
      operation: patch
      set:
        licensed-activities: {fromField: licensed-activities}
  review:
    authority: casework
    policyId: scope-correction
  onApproved:
    mode: manual
  retention:
    mode: operator_erase
```

The submitter permission needs only source lifecycle operations. The application permission is
ordinary BReg authority:

```yaml
permissions:
  - entity: scope-correction
    operations: [create, get, patch, submit_request, revise_request, cancel_request]
    readableFields: [record, licensed-activities]
    writableFields: [record, licensed-activities]
    requestVisibility: owner
    rowBoundaries: []
  - entity: scope-correction
    operations: [get, apply_request]
    readableFields: [record, licensed-activities]
    readableRequestFields: [review_state]
    applyTargets:
      - entity: professional-license
        rowBoundaries: []
    rowBoundaries: []
```

Casework reviewer profiles and stages belong in the Casework project. Do not duplicate them as
BReg permissions.

{/* Evidence: products/breg/starters/professional-licences/core/registry.yaml;
    products/casework/examples/professional-review/casework.yaml. */}

## Bind the authority client

For `bregctl dev`, `source add --apply` adds the paired local clients to the BReg project. The
resulting authority binding has this shape:

```yaml
clients:
  - id: casework-producer
    accessProfiles: []
    scopes: [casework:reviews:request]
    claims: {}
reviewAuthorities:
  casework:
    endpoint: http://127.0.0.1:8092/
    profile: integration-requester
    producerId: registry-breg
    recoveryDays: 7
    client: casework-producer
```

The local issuer provisions the service client and BReg uses maintained client-assertion token
acquisition. If the client has no explicit resource mapping, local development uses the generated
BReg audience; otherwise it uses the declared mapped resource. Scopes must belong to that resource.
No access token or private key is embedded in generated runtime configuration.

The Casework starter admits the stable subject derived from the professional-licences starter's
instance id and `integration-requester` client. Before packaging for a deployment, replace that
local issuer and subject in `reviewProducers` with the deployed BReg client identity, and replace
the example authority endpoint and secret references in the generated BReg runtime fragment.

In a deployed runtime, `reviewAuthorities.<id>` keeps `endpoint`, `profile`, `producerId`, and
`recoveryDays`, and uses either maintained `privateKeyJwt` settings or an explicitly supported
opaque `tokenRef`. `profile` is the Casework requester profile sent on every authority request.
`producerId` is the independent protocol identity used in the submission digest.

{/* Evidence: products/breg/DEV.md;
    crates/registry-bregctl/src/dev/config.rs;
    crates/registry-breg/src/runtime_config.rs;
    crates/registry-review-client/src/client.rs. */}

## Migrate, serve, and check readiness

For an operated deployment, migrate both databases through the product commands, start both
servers under their service managers, and check setup readiness:

```sh
bregctl --format json apply \
  --runtime-config /etc/registry-breg/runtime.yaml \
  --package /srv/registry/package --initial
casework --runtime-config /etc/registry-casework/runtime.yaml migrate

bregctl doctor --runtime-config /etc/registry-breg/runtime.yaml
caseworkctl doctor --runtime-config /etc/registry-casework/runtime.yaml
breg --config /etc/registry-breg/runtime.yaml
casework --runtime-config /etc/registry-casework/runtime.yaml serve

curl --fail --silent https://registry.example.org/ready
curl --fail --silent https://casework.example.org/ready
```

`doctor` checks configuration, package, database, identity, source connectivity, and directory
setup. It does not report the progress of a submitted review. Use the resources below for that.

## Submit and observe the exact binding

Create the request as its owner, read its advertised `submit_request` action, and send the returned
`ifMatch` with a fresh `Idempotency-Key`. Submit as a person: the starter's `editor` client carries
the human actor marker, because the review stage excludes the request's initiator and BReg names an
initiator only for a human caller. A service or agent submission is refused with
`review.initiator-required` when Casework admits it, and the submitter who tries to claim the review
task is refused with `review.initiator-excluded`.
Submission freezes:

- the request and target facts used by the proposal;
- the proposal version and effect digest;
- `review.authority` and `review.policyId`;
- application preconditions and `onApproved` behavior.

BReg durably records the submission job before contacting Casework. The worker uses one stable
idempotency key and expected submission digest until it receives the exact accepted binding.
Ambiguous responses are recovered with the same request rather than creating a second review.

A profile with `readableRequestFields: [review_state]` receives
`data.request.review`. Its closed sections are `submission`, `result`, `delivery`, `application`,
and `recovery`. Optional identifiers and timestamps are omitted until known. These fields report
reconciliation state. They grant neither a Casework decision nor a BReg write.

Follow the `submit_request` action advertised on the BReg request instead of constructing an
internal route. Keep its `ifMatch` and use a new `Idempotency-Key`. Then follow the identifiers the
response and request projection return.

## Decide in Casework

Open the task through the Casework profile authorized by the policy. Casework obtains source
context with the caller's delegated credential and the configured source profile, then enforces
its stage, queue, assignment, independence, and decision rules. Complete every stage required by
the policy.

The starter's review stage excludes the submitter, so the person who submitted the request cannot
claim, be assigned, or decide its review. A reviewer either approves or chooses the
`changes-requested` outcome ("Request changes") with a reason. A send-back reaches BReg as a
`changesRequested` review result. The request stays `submitted`, and BReg now offers its owner only
`revise_request`, carrying `rebase: false`, and `cancel_request`. The submitter follows the
`revise_request` action with the `rebase` value it carries, and BReg records the new draft as a
revision. The submitter then patches the draft and submits it again, and Casework opens a fresh
review for the new proposal version.

The terminal result carries the exact review request id, subject binding, full policy binding, and
submission digest. BReg accepts it only when every value matches the durable submission. An early
or unmatched completion remains in the bounded inbox for reconciliation. Redelivery is idempotent.

## Apply at BReg

For manual application, read the request under the BReg applier profile and follow the advertised
`apply_request` action. Send its `ifMatch`, a stable idempotency key, and the displayed proposal
binding:

```json
{
  "proposalVersion": 1,
  "effectDigest": "sha256:<64 lower-case hex digits>"
}
```

BReg verifies the exact approved Casework result, then rechecks the current applier grant, target
authority, target revisions, and application preconditions. Effects and the source receipt commit
atomically. If the HTTP response is lost, replay the exact request with the same idempotency key.
BReg returns its stored receipt before any Casework or Evidence network call.

For `onApproved.mode: automatic`, the reconciliation worker uses a separately configured ordinary
executor credential against this same BReg HTTP action. Revoking that profile blocks the job. It
does not borrow the submitter, reviewer, or completion sender's authority.

Automatic application is opt-in. Change the authored request to
`onApproved: {mode: automatic, executor: registry-automatic}`, grant one ordinary BReg
`apply_request` profile, repeat `bregctl check`, `bregctl explain change-requests`, and both
`source add` runs, then provision the generated `reviewExecutors.registry-automatic` credential.
The executor still passes the current profile, target authority, revision, and precondition checks
at the time it applies.

## Choose result delivery explicitly

Polling is the default. BReg polls the Casework result resource and treats `202` as pending, `200`
as the retained terminal result, `410` as expired, and an empty `404` as concealed or unknown.
The producer result feed provides the same terminal results in pages with a `nextCursor`.

Completion delivery is optional. Add a Casework producer `completion` block and repeat the
authoring loop to generate `completionTokenRef` and `completionRecipient` in the BReg authority
binding. The completion receiver acknowledges delivery only with an empty HTTP `204 No Content`.
Any other success status, or a `204` carrying response bytes, is not an acknowledgement and
Casework retries the same bounded event. Completion is a wake-up signal; BReg still fetches and
correlates the authoritative retained result before it records approval.

## Diagnose one review

Use each owning resource for its state:

| Question | Owning observation |
| --- | --- |
| Was the request admitted and which policy is running? | `GET /v1/review-requests/{requestId}`: lifecycle, policy, active stage, and timestamps. |
| Who can act and what context can they see? | `GET /v1/review-tasks`, the task resource, and `/context`: queue, task state, holder, revision, and the current source binding. |
| Is a terminal result retained? | `GET /v1/review-requests/{requestId}/result`: `202` pending, `200` retained, `410` expired, or empty `404` concealed or unknown. |
| Which terminal results have not been consumed? | `GET /v1/review-results?cursor=...&limit=...`: bounded items and `nextCursor`. |
| Has BReg submitted, received, or applied it? | Read `data.request.review`: `submission`, `result`, `delivery`, `application`, and `recovery`, with identifiers and timestamps omitted until known. |

The BReg projection reports the durable state and recovery code. After submission it also reports
the fixed `submission.recoveryDeadline`. Automatic application jobs report bounded
`application.attempts`; `application.nextAttemptAt` appears only while queued or applying and is
omitted after the job is blocked or applied; do not query the database directly or treat `doctor`
as a workflow console.

## Verify the boundary

After application, check all three observations:

1. The target record has the expected new revision and values.
2. The source request is `applied` and has the same proposal version and effect digest.
3. The Casework result remains a review result, not the source application receipt.

Also test a substituted digest, stale target, withdrawn request, and revoked applier. Each must
fail without a partial target write. See [Base Registry Engine API reference](../../reference/breg-api/#change-requests),
[Declare change requests and actions](../../configure/breg-change-control/), and
[How Casework works](../../explanation/how-casework-works/) for the owning contracts.