From 3559f7671bf93d45afa40462f6c8bc7d5edb2422 Mon Sep 17 00:00:00 2001 From: Jeremi Joslin Date: Mon, 9 Nov 2026 16:02:59 +0700 Subject: [PATCH 1/2] docs: guide adopters on legal footing, partners and the law's lifecycle Adopters were left to infer who owns a register, how to work with partner institutions, and what to do when the engine cannot express their law. The docs now ask for a short legal-footing block with "not stated" allowed, map partner situations to products, show the disclosure ladder, model the law's lifecycle and report engine gaps, frame PublicSchema as a starting vocabulary inside and a contract at the edge, and name the exits that are true today. Signed-off-by: Jeremi Joslin --- .../configuration-defined-registry.mdx | 8 ++ ...ta-minimization-and-purpose-limitation.mdx | 21 +++++ .../registry-modeling-patterns.mdx | 81 +++++++++++++++++++ .../explanation/where-each-rule-lives.mdx | 8 ++ docs/site/src/content/docs/index.mdx | 2 + .../docs/reference/breg-configuration.mdx | 6 ++ docs/site/src/content/docs/start/adopt.mdx | 62 ++++++++++++++ .../docs/start/evidence-quickstart.mdx | 4 + .../derive-a-registry-from-publicschema.mdx | 16 +++- products/platform/CONFIG-CONVENTIONS.md | 10 ++- 10 files changed, 212 insertions(+), 6 deletions(-) diff --git a/docs/site/src/content/docs/explanation/configuration-defined-registry.mdx b/docs/site/src/content/docs/explanation/configuration-defined-registry.mdx index 50129d8fc8..fb09a8c0e0 100644 --- a/docs/site/src/content/docs/explanation/configuration-defined-registry.mdx +++ b/docs/site/src/content/docs/explanation/configuration-defined-registry.mdx @@ -110,6 +110,14 @@ every write. crates/registry-breg/src/idempotency.rs; crates/registry-breg/src/lifecycle.rs; crates/registry-breg/src/history_store.rs. */} +## Can I leave? + +Yes, by three routes that are true today. Your project is plain YAML in your own version control, +and it runs on the products without anything else. You can take your data out with +[`bregctl data export`](../../operate/breg-data/#export-with-a-checkpoint). And the records +live in a PostgreSQL database you run. Nothing in this model needs a separate hosted service to keep +working. + ## Next - [Author a registry project](../../configure/breg/) diff --git a/docs/site/src/content/docs/explanation/data-minimization-and-purpose-limitation.mdx b/docs/site/src/content/docs/explanation/data-minimization-and-purpose-limitation.mdx index 56d19debfa..26b13dbcff 100644 --- a/docs/site/src/content/docs/explanation/data-minimization-and-purpose-limitation.mdx +++ b/docs/site/src/content/docs/explanation/data-minimization-and-purpose-limitation.mdx @@ -101,6 +101,27 @@ one problem response with the same title and body shape, so a caller cannot use whether a record exists. The outcomes stay distinct in the audit record; collapsing is a property of what the caller sees, not of what the operator can review. +## Ask for the weakest answer that does the job + +When you decide what crosses to or from another institution, start at the weakest answer and climb +only when it cannot do the job. In plain words, from weakest to strongest: + +1. Nothing: refuse when the purpose, authority, or scope is missing. +2. A description of what exists, with no person-level data. +3. Counts, rates, or other totals. +4. A match, mismatch, or "cannot tell" on facts the caller already holds. +5. A computed fact, such as "is registered" or "is eligible", without the record behind it. +6. A fact the person states, labeled as self-stated. +7. A rendered document filtered to what is allowed. +8. A signed credential or receipt for a named purpose and validity period. +9. Selected fields of the record, only when the law allows a record-level look and the access is + scoped, filtered, and audited. + +Registry Evidence serves the middle of this ladder through its closed value forms, and a Base Registry Engine +(BReg) access profile serves the last rung. Record the purpose or legal basis for each access grant and +for each partner relationship. Where the source is silent, write "not stated"; that is allowed, and +it is a finding to raise with whoever owns the question, not a reason to stop. + ## Data stays at source: distributed custody Underneath all of this is an architectural choice that matters for minimization at the system diff --git a/docs/site/src/content/docs/explanation/registry-modeling-patterns.mdx b/docs/site/src/content/docs/explanation/registry-modeling-patterns.mdx index 0f7a59fbf0..56a28746c6 100644 --- a/docs/site/src/content/docs/explanation/registry-modeling-patterns.mdx +++ b/docs/site/src/content/docs/explanation/registry-modeling-patterns.mdx @@ -27,6 +27,29 @@ bregctl check facility The excerpts below come from the template's `registry.yaml`. +## Write down the legal footing + +A register has one legal owner. When two agencies both claim a register, that is joint control, and +the law settles it before you configure anything. For each register, write a short block, and +write "not stated" wherever your sources are silent; an honest gap is more useful than a guess: + +- the law that creates it; +- the body that law mandates; +- the data controller; +- any operator distinct from the rule-maker; +- the legal effect of an entry: constitutive (the entry creates the status), declaratory (it + records a status that exists anyway), or informational. + +Fill in a gap when you find the answer. Keep the block beside the register's description, so a reviewer sees who owns the data before +reading the fields. + +## Combine starters by authority + +When you combine starters, keep together what one authority keeps, and link the rest by reference. +A farmer and the land parcels they work belong in one project only if one authority keeps both. If +a land authority keeps the parcels, the farmer register holds a reference to the parcel, not a copy +of it. + ## Separate an object from its registration Keep a thing separate from its registration when their lifecycles differ. A facility can change @@ -154,6 +177,64 @@ directly. An immediate Action commits several effects atomically, such as a faci first assignment, and a Proposal Action keeps a durable draft for review and later application. [Actions and Proposals](../../configure/breg-actions/) explains how to choose. +## Keep together what must be right together + +Keep together what must be right together, change it through one Action, and link the rest by +reference. For example, a household and its memberships must agree about who belongs, so one Action +changes them together. The person stays a separate Record type that memberships point to, and an +enrolment in a programme is its own record, because a different body decides it and it changes on +a different schedule. + +Name each Action after what happens in the programme, in the law's words: `register-birth` or +`decide-eligibility`, not `update-record`. + +A short worked example, from a decision process to a recorded decision. Suppose the law says an +officer decides whether a household is enrolled. The process has three steps: the household applies, +an officer reviews, and the decision is recorded. Model the decision as a proposal-mode +[Action](../../reference/glossary/#action): the application is submitted as a +[Proposal](../../reference/glossary/#proposal), the officer's review is the review it requests, +and applying it writes the enrolment record, with the decision and the officer's reference, in one +change. The recorded decision is then an ordinary record that other offices can read under their +own access profiles. + +## Model the law's lifecycle + +Model the lifecycle the law describes, even when the engine cannot express all of it, and report +the gap. Never bend a legal lifecycle to fit the engine. + +For example, a law may allow revoking a licence that is active or suspended. A commit guard +compares one field of one record with one literal or one input, so it cannot say "active or +suspended". The tempting workaround is to force revocation to pass through suspension first, which +adds a step the law does not have. Instead, keep revocation one step of the lifecycle and build it +as two Actions with the same effect, one guarded on `active` and one on `suspended`, and note in +the project that the pair exists only because of the engine gap. A visible, labelled gap can be +fixed later; a bent lifecycle hides in the data. + +## Choose a licensing shape + +Licensing has two shapes. Choose by the legal question: does your law issue a new licence at +renewal, or extend the same one? + +- **Extends the same licence.** Keep one stable licence with dated terms, and record regulatory + decisions (suspension, renewal, revocation) as separate records. +- **Issues a new licence.** Create a successor licence for each renewal and link it to the previous + one. + +If your sources do not say, write "not stated" for the legal question and ask the licensing +authority before you model renewals. + +Write periods with an inclusive last day. A `date` period whose source records the last day in +force names it with `temporal.lastDayField` instead of `endField`, so a period ending on 31 +December includes that day. The [API reference](../../reference/breg-api/) describes how the +temporal routes read it. + +## Derive roles from the job + +Roles come from the programme's job descriptions and the law's purposes, not from the product's +permissions. Start with who does what in the programme, then grant each role the reads and Actions +its job needs. Record why each grant exists, in a sentence ("the officer decides enrolment under +the programme's rules"); write "not stated" if you cannot yet say. + ## Declarations, provenance, and documents Keep a reported claim separate from the object it describes when the claim has its own reporter, diff --git a/docs/site/src/content/docs/explanation/where-each-rule-lives.mdx b/docs/site/src/content/docs/explanation/where-each-rule-lives.mdx index b3eb6433aa..c307bd1c45 100644 --- a/docs/site/src/content/docs/explanation/where-each-rule-lives.mdx +++ b/docs/site/src/content/docs/explanation/where-each-rule-lives.mdx @@ -72,6 +72,14 @@ products hold what happens around a record. products/casework/README.md; products/messaging/README.md; products/scheduling/README.md; crates/registry-breg/src/contract.rs (ACTION_PLAN_ABI_V1). */} +## Keep numbers that change in records, not in scripts + +Rates, thresholds, and fee tables change more often than the logic that uses them. Store each as a +dated record (a Record type with `temporal` validity, one record per period) and keep the Action +plan or script small: it takes the value as an input rather than carrying the number. A change +then becomes a reviewed, dated record with its own history, not an edit to code. The table above +shows how a workflow reads the record in force and passes it to the Action. + ## Choosing a plan for an Action Start with a `fixed` plan, which writes declared values and needs no code. Move to a Rhai Function diff --git a/docs/site/src/content/docs/index.mdx b/docs/site/src/content/docs/index.mdx index 2a7a47252b..a376d485f7 100644 --- a/docs/site/src/content/docs/index.mdx +++ b/docs/site/src/content/docs/index.mdx @@ -65,6 +65,8 @@ Every tutorial names the role it is written for. One institution often holds sev - The data publisher is the institution that maintains records through BReg or publishes metadata through Manifest. - The consumer or verifier is the service that calls a product API and verifies what it receives. - The operator is whoever installs, configures, and runs a deployment. +- The programme officer runs the programme and signs off the terms, labels, and refusal wording that officers and the public read. +- The legal drafter confirms that rules, tables, formulas, and deadlines match the law, and notes the legal basis beside each. ## Beyond the first run diff --git a/docs/site/src/content/docs/reference/breg-configuration.mdx b/docs/site/src/content/docs/reference/breg-configuration.mdx index 931f6eaaee..754dc1e6ed 100644 --- a/docs/site/src/content/docs/reference/breg-configuration.mdx +++ b/docs/site/src/content/docs/reference/breg-configuration.mdx @@ -63,6 +63,12 @@ Action inputs keep their existing `label` fallback to `apiName`; the separate op `check` warns when two access profiles in the same namespace have equivalent labels, naming both IDs. Use `check --deny-warnings` when that warning must block a review. +Take labels and refusal wording from the law and the forms your programme already uses, not from +product terms. Keep a short table of the programme's terms, and have a programme officer sign it +off before the labels are authored. BReg labels are a single string each; for documents in more +than one language, put the translations in the per-locale label tables of the +[Render bundle](../render-configuration/). + {/* Evidence: crates/registry-bregctl/src/check.rs, Authoring and authored_paths(); crates/registry-bregctl/src/lib.rs, explain(); crates/registry-breg/src/artifacts.rs, inspect_registry() and action_input_metadata(); diff --git a/docs/site/src/content/docs/start/adopt.mdx b/docs/site/src/content/docs/start/adopt.mdx index eec948e5b6..24a2312df8 100644 --- a/docs/site/src/content/docs/start/adopt.mdx +++ b/docs/site/src/content/docs/start/adopt.mdx @@ -67,6 +67,57 @@ retries, tracing, and signed delivery. You may publish a static others understand the module. Inclusion is optional metadata; it neither certifies availability nor enables the module. Real data services use datasets; workers need no fictional dataset. +## Choose what goes in the registry + +Put in the facts your law makes you the source of and the decisions you are accountable for. +Build the smallest version of supporting parts. Leave payroll, document management, and generic +workflow where they are, and reach them through their own systems. + +Spend modeling time and legal review on rules, tables, formulas, and deadlines, because that is +where policy lives. Keep a rule that an approver signs in a table or formula rather than in code, +and note the legal basis beside each one in your own project notes. If a rule has no stated basis, +write "not stated" and raise it for review. + +## Working with other institutions + +Start from the partner and the situation, then pick the product. "Do not integrate" is always a +valid answer. + +| Situation | What you do | Product | +| --- | --- | --- | +| Others need a fact about your records | Publish a narrow answer, not the record | [Evidence](../../explanation/how-evidence-works/) | +| A partner needs to read or change your records | Publish a governed record API | [BReg](../../explanation/configuration-defined-registry/) | +| You need a fact held by someone else | Ask for the narrowest answer that does the job | Evidence, as the consumer | +| You must load another system's data | Copy it in through a reviewed translator | [Adapter](https://github.com/registrystack/registry-stack/blob/main/products/adapter/README.md) packs; its ingestion job is not yet released | +| A standard governs your edge | Follow it at the edge; keep your own model inside | Adapter facade | +| A national system sets the contract | Follow its contract at the edge | Adapter facade | +| The person can carry the proof | Let them bring it as a credential | Evidence with [OID4VCI delivery](../../configure/evidence-oid4vci/) | +| Integration is not justified | Do not integrate, and write down why | None | + +Keep a one-page relationship sheet per partner. Write "not stated" for any line the source is +silent on, and treat that as a finding to raise rather than a blocker. + +- The partner. +- The legal basis. +- Who defines the question. +- What crosses. +- The product that serves it. +- Who owns changes. + +Promise Read first. Promise Write only when the source can refuse a stale update (see `If-Match` +in the [BReg API](../../reference/breg-api/)), retry a create safely (`Idempotency-Key`), and +return a version. + +Point to records another office keeps; do not copy them. Store their identifier with its scheme, +and ask through Evidence when a decision needs the fact. In BReg an event record's subject or +source can hold an identifier another system owns, declared with `external: {namespace}`; see the +[event records guide](https://github.com/registrystack/registry-stack/blob/main/products/breg/event-records.md). + +Payment and treasury hand-offs are custom code today. Build them as an application or a module +(the third and fourth paths above); no Registry Stack product sends payments. + +For how much to share, see [the disclosure ladder](../../explanation/data-minimization-and-purpose-limitation/#ask-for-the-weakest-answer-that-does-the-job). + ## Choose the next step Combine the smallest set of paths that meets the job. A configured register can have one custom @@ -74,3 +125,14 @@ route and an independently deployed reminder worker. Add [Coordinator](../coordinator/) only when a durable, reviewed sequence across waits is useful. Before deployment, use the [operator handoff](../../operate/) to assign credentials, backups, retention, and recovery to named operators. + +## Signs it is time to climb + +Stay on the lightest path that works. Any one of these means a part of your work is a process, +not only a register, so consider the next path (a small extension, an application, or a module): + +- A rule needs facts from more than one record. +- A matter continues over weeks. +- Money is owed or paid. +- The law sets a clock. +- Two parties must both agree. diff --git a/docs/site/src/content/docs/start/evidence-quickstart.mdx b/docs/site/src/content/docs/start/evidence-quickstart.mdx index 3ef4722170..b257e714f2 100644 --- a/docs/site/src/content/docs/start/evidence-quickstart.mdx +++ b/docs/site/src/content/docs/start/evidence-quickstart.mdx @@ -23,6 +23,10 @@ such as `urn:example:requirement:adult-status:v1`, and the subject it is about. only the values the question declares. Asked whether a person is an adult, Registry Evidence reads a birth date from your system and returns `true`; the date stays with you. +A Requirement URI is minted by the body whose mandate defines the question, often the consuming +programme or a sector authority. There is no central registry of Requirement URIs; where a standard +such as SP-DCI already defines a code for the same question, reuse it. + ## What you define and run You write an [Evidence project](../../reference/glossary/#evidence-project), kept in version diff --git a/docs/site/src/content/docs/tutorials/derive-a-registry-from-publicschema.mdx b/docs/site/src/content/docs/tutorials/derive-a-registry-from-publicschema.mdx index 55095a0c31..497e1ab3db 100644 --- a/docs/site/src/content/docs/tutorials/derive-a-registry-from-publicschema.mdx +++ b/docs/site/src/content/docs/tutorials/derive-a-registry-from-publicschema.mdx @@ -41,9 +41,13 @@ PublicSchema is a published vocabulary for social protection and public-service households, memberships, programmes, and the code lists that describe them. The BReg component carries a copy of PublicSchema 0.3.0, so deriving from it needs no network. -Starting from the model gives you field names, types, and code lists that other institutions -already use, so a ministry system or a partner's data maps onto your registry without a round of -naming meetings. It also settles the mechanical choices the same way every time: each concept you +PublicSchema is a starting vocabulary inside your registry and a contract at its edge. Starting +from the model gives you field names, types, and code lists that other institutions already use. +Your fields carry PublicSchema concept URIs (the `conceptUri` of each Record type and the +`concepts` of each field in `registry.yaml`), so a mapping to a partner starts from shared +definitions. Your register may diverge where your mandate needs it. Naming is where the +programme's language gets settled, with the people who run it; the model gives you a draft to +settle it from, not a replacement for that conversation. Deriving also settles the mechanical choices the same way every time: each concept you select becomes a Record type with an identifier the caller supplies, each property becomes a bounded field, an enumeration short enough to read becomes a closed list of codes, and a property the model marks sensitive becomes a `restricted` field. @@ -147,6 +151,12 @@ the model. Keep this notice with the project and with any package built from it. Keep it with the project and with any package you build from it. +The generated catalogue names a placeholder body. Under `manifestProjection.catalog` in +`registry.yaml`, the `publisher`, the entry in `authorities`, and the dataset `owner` are all +`"{title} Authority"` (with a made-up IRI under your reserved base), and `publicService.competentAuthority` points at that authority's `id`. Replace them with the body +your law names for the register. Where your sources do not say, write "not stated" in the +dataset description rather than inventing a body; you can fill it in later. + `model/selection.yaml` is the input the project was derived from. You edit it later when you derive a candidate. diff --git a/products/platform/CONFIG-CONVENTIONS.md b/products/platform/CONFIG-CONVENTIONS.md index dfa6044fa1..dae4f0f03f 100644 --- a/products/platform/CONFIG-CONVENTIONS.md +++ b/products/platform/CONFIG-CONVENTIONS.md @@ -36,9 +36,13 @@ In scope: checkpoints, development state); it follows every rule except CFG-SCHEMA-2 and CFG-SCHEMA-6, which cover the files people edit. An **output format** is written for people or for tools we do not own and is never read back - (explain output, `--format json` reports); it is registered with - `reader: none`, follows CFG-ENV, CFG-NAME, CFG-ID, CFG-QTY, and CFG-EMPTY, - and is exempt from CFG-YAML-1, CFG-CHECK-1, CFG-SCHEMA-2, and CFG-SCHEMA-6. + by a tool we maintain (explain output, `--format json` reports); it is + registered with `reader: none`, follows CFG-ENV, CFG-NAME, CFG-ID, CFG-QTY, + and CFG-EMPTY, and is exempt from CFG-YAML-1, CFG-CHECK-1, CFG-SCHEMA-2, and + CFG-SCHEMA-6. A product output that a maintained tool reads is a registered + contract: it is a read-back format, registered with a reader and a schema + that name that tool, whatever command prints it. Explain output a + maintained tool reads belongs here, not under `reader: none`. A **build artifact** is a read-back format that its own product's build writes and reads again and that no one edits (the compiled package model in BReg's `package.json`). It is registered with `audience: generated` and From 305ec95f451c7ac8fc3e48533174888be3ce2589 Mon Sep 17 00:00:00 2001 From: Jeremi Joslin Date: Mon, 9 Nov 2026 16:14:57 +0700 Subject: [PATCH 2/2] docs: apply review fixes to adopter modelling guidance Signed-off-by: Jeremi Joslin --- .../configuration-defined-registry.mdx | 15 +++++--- ...ta-minimization-and-purpose-limitation.mdx | 7 +++- .../registry-modeling-patterns.mdx | 37 ++++++++++--------- .../explanation/where-each-rule-lives.mdx | 7 +--- docs/site/src/content/docs/index.mdx | 4 +- .../docs/reference/breg-configuration.mdx | 4 +- docs/site/src/content/docs/start/adopt.mdx | 31 +++++++++------- .../derive-a-registry-from-publicschema.mdx | 17 +++++---- products/platform/CONFIG-CONVENTIONS.md | 4 +- 9 files changed, 70 insertions(+), 56 deletions(-) diff --git a/docs/site/src/content/docs/explanation/configuration-defined-registry.mdx b/docs/site/src/content/docs/explanation/configuration-defined-registry.mdx index fb09a8c0e0..17f35b2427 100644 --- a/docs/site/src/content/docs/explanation/configuration-defined-registry.mdx +++ b/docs/site/src/content/docs/explanation/configuration-defined-registry.mdx @@ -112,11 +112,16 @@ every write. ## Can I leave? -Yes, by three routes that are true today. Your project is plain YAML in your own version control, -and it runs on the products without anything else. You can take your data out with -[`bregctl data export`](../../operate/breg-data/#export-with-a-checkpoint). And the records -live in a PostgreSQL database you run. Nothing in this model needs a separate hosted service to keep -working. +Yes, by three routes. Your project is YAML, plus any Rhai, SQL, or Wasm files it includes, in your +own version control, and it runs on the products without anything else. You can take your current +records out, with the fields you name, using +[`bregctl data export`](../../operate/breg-data/#export-with-a-checkpoint) through an access +profile that has `allowDataExport`. And the records live in a PostgreSQL database you run. Nothing +in this model needs a separate hosted service to keep working. + +{/* Evidence: crates/registry-breg/src/wasm_handler.rs; crates/registry-breg/src/derived_sql.rs; + crates/registry-breg/src/tooling.rs, allowDataExport; + docs/site/src/content/docs/operate/breg-data.mdx. */} ## Next diff --git a/docs/site/src/content/docs/explanation/data-minimization-and-purpose-limitation.mdx b/docs/site/src/content/docs/explanation/data-minimization-and-purpose-limitation.mdx index 26b13dbcff..f0d17d27a1 100644 --- a/docs/site/src/content/docs/explanation/data-minimization-and-purpose-limitation.mdx +++ b/docs/site/src/content/docs/explanation/data-minimization-and-purpose-limitation.mdx @@ -117,11 +117,14 @@ only when it cannot do the job. In plain words, from weakest to strongest: 9. Selected fields of the record, only when the law allows a record-level look and the access is scoped, filtered, and audited. -Registry Evidence serves the middle of this ladder through its closed value forms, and a Base Registry Engine -(BReg) access profile serves the last rung. Record the purpose or legal basis for each access grant and +Registry Manifest publishes descriptions, Base Registry Engine (BReg) statistical datasets serve +totals, Registry Evidence serves matches, computed facts and signed credentials, Registry Render +produces documents, and a BReg access profile serves the last rung. Record the purpose or legal basis for each access grant and for each partner relationship. Where the source is silent, write "not stated"; that is allowed, and it is a finding to raise with whoever owns the question, not a reason to stop. +{/* Evidence: products/breg/STATISTICS.md; products/breg/native-patterns.md. */} + ## Data stays at source: distributed custody Underneath all of this is an architectural choice that matters for minimization at the system diff --git a/docs/site/src/content/docs/explanation/registry-modeling-patterns.mdx b/docs/site/src/content/docs/explanation/registry-modeling-patterns.mdx index 56a28746c6..ba2e465b6b 100644 --- a/docs/site/src/content/docs/explanation/registry-modeling-patterns.mdx +++ b/docs/site/src/content/docs/explanation/registry-modeling-patterns.mdx @@ -29,9 +29,9 @@ The excerpts below come from the template's `registry.yaml`. ## Write down the legal footing -A register has one legal owner. When two agencies both claim a register, that is joint control, and -the law settles it before you configure anything. For each register, write a short block, and -write "not stated" wherever your sources are silent; an honest gap is more useful than a guess: +A register has one legal owner. When two agencies both claim a register, that is joint control; +record which law settles it, or "not stated". For each register, write a short block, and write +"not stated" wherever your sources are silent; an honest gap is more useful than a guess: - the law that creates it; - the body that law mandates; @@ -47,8 +47,8 @@ reading the fields. When you combine starters, keep together what one authority keeps, and link the rest by reference. A farmer and the land parcels they work belong in one project only if one authority keeps both. If -a land authority keeps the parcels, the farmer register holds a reference to the parcel, not a copy -of it. +a land authority keeps the parcels, the farmer register holds the parcel's identifier with its scheme, not a +copy of it. ## Separate an object from its registration @@ -162,6 +162,10 @@ one operator, several, or one primary beside other roles. A Record type's `tempo names its start and end fields; exclusivity needs a separate `temporal-non-overlap` constraint with the right `scopeFields`. A date or role field alone imposes nothing. +When your source states the last day in force, name it with `temporal.lastDayField` instead of +`endField`, so a period ending on 31 December includes that day. The +[API reference](../../reference/breg-api/) describes how the temporal routes read it. + Keep effective time separate from recording time: "who operated the facility on June 1?" differs from "what had the registry recorded on June 1?". Test adjacent and open-ended periods and a correction to an earlier period. @@ -190,12 +194,16 @@ Name each Action after what happens in the programme, in the law's words: `regis A short worked example, from a decision process to a recorded decision. Suppose the law says an officer decides whether a household is enrolled. The process has three steps: the household applies, -an officer reviews, and the decision is recorded. Model the decision as a proposal-mode -[Action](../../reference/glossary/#action): the application is submitted as a -[Proposal](../../reference/glossary/#proposal), the officer's review is the review it requests, -and applying it writes the enrolment record, with the decision and the officer's reference, in one -change. The recorded decision is then an ordinary record that other offices can read under their -own access profiles. +an officer reviews, and the decision is recorded. Model the officer's decision as an +[Action](../../reference/glossary/#action), `decide-enrolment`, whose inputs carry the outcome and +the officer's reference and whose plan writes a decision record either way. If the law requires a +second look, make it a proposal-mode Action: a reviewer approves or rejects that exact submission +(a [Proposal](../../reference/glossary/#proposal)), and only applying it writes the record. The +recorded decision is then an ordinary record that other offices can read under their own access +profiles. + +{/* Evidence: crates/registry-breg/src/contract.rs, ActionSource, ActionValueSource, ProposalApplicationSource; + docs/site/src/content/docs/configure/breg-actions.mdx. */} ## Model the law's lifecycle @@ -221,12 +229,7 @@ renewal, or extend the same one? one. If your sources do not say, write "not stated" for the legal question and ask the licensing -authority before you model renewals. - -Write periods with an inclusive last day. A `date` period whose source records the last day in -force names it with `temporal.lastDayField` instead of `endField`, so a period ending on 31 -December includes that day. The [API reference](../../reference/breg-api/) describes how the -temporal routes read it. +authority. ## Derive roles from the job diff --git a/docs/site/src/content/docs/explanation/where-each-rule-lives.mdx b/docs/site/src/content/docs/explanation/where-each-rule-lives.mdx index c307bd1c45..2e71afd9bc 100644 --- a/docs/site/src/content/docs/explanation/where-each-rule-lives.mdx +++ b/docs/site/src/content/docs/explanation/where-each-rule-lives.mdx @@ -74,11 +74,8 @@ products hold what happens around a record. ## Keep numbers that change in records, not in scripts -Rates, thresholds, and fee tables change more often than the logic that uses them. Store each as a -dated record (a Record type with `temporal` validity, one record per period) and keep the Action -plan or script small: it takes the value as an input rather than carrying the number. A change -then becomes a reviewed, dated record with its own history, not an edit to code. The table above -shows how a workflow reads the record in force and passes it to the Action. +Keep a rate or fee table in a dated record, as the parameters row shows, so each change is a +reviewed record with history, not a code edit. ## Choosing a plan for an Action diff --git a/docs/site/src/content/docs/index.mdx b/docs/site/src/content/docs/index.mdx index a376d485f7..35815e55d3 100644 --- a/docs/site/src/content/docs/index.mdx +++ b/docs/site/src/content/docs/index.mdx @@ -65,8 +65,8 @@ Every tutorial names the role it is written for. One institution often holds sev - The data publisher is the institution that maintains records through BReg or publishes metadata through Manifest. - The consumer or verifier is the service that calls a product API and verifies what it receives. - The operator is whoever installs, configures, and runs a deployment. -- The programme officer runs the programme and signs off the terms, labels, and refusal wording that officers and the public read. -- The legal drafter confirms that rules, tables, formulas, and deadlines match the law, and notes the legal basis beside each. + +Two roles outside the technical team shape a registry: the programme officer, who runs the programme and signs off the terms, labels, and refusal wording that officers and the public read, and the legal drafter, who confirms that rules, tables, formulas, and deadlines match the law and notes the legal basis beside each. ## Beyond the first run diff --git a/docs/site/src/content/docs/reference/breg-configuration.mdx b/docs/site/src/content/docs/reference/breg-configuration.mdx index 754dc1e6ed..6561417b70 100644 --- a/docs/site/src/content/docs/reference/breg-configuration.mdx +++ b/docs/site/src/content/docs/reference/breg-configuration.mdx @@ -64,8 +64,8 @@ Action inputs keep their existing `label` fallback to `apiName`; the separate op naming both IDs. Use `check --deny-warnings` when that warning must block a review. Take labels and refusal wording from the law and the forms your programme already uses, not from -product terms. Keep a short table of the programme's terms, and have a programme officer sign it -off before the labels are authored. BReg labels are a single string each; for documents in more +product terms. Keep a short table of the programme's terms; author the labels from a table a programme officer +agrees is a good source. BReg labels are a single string each; for documents in more than one language, put the translations in the per-locale label tables of the [Render bundle](../render-configuration/). diff --git a/docs/site/src/content/docs/start/adopt.mdx b/docs/site/src/content/docs/start/adopt.mdx index 24a2312df8..f05569e9b0 100644 --- a/docs/site/src/content/docs/start/adopt.mdx +++ b/docs/site/src/content/docs/start/adopt.mdx @@ -87,10 +87,9 @@ valid answer. | --- | --- | --- | | Others need a fact about your records | Publish a narrow answer, not the record | [Evidence](../../explanation/how-evidence-works/) | | A partner needs to read or change your records | Publish a governed record API | [BReg](../../explanation/configuration-defined-registry/) | -| You need a fact held by someone else | Ask for the narrowest answer that does the job | Evidence, as the consumer | +| You need a fact held by someone else | Ask for the narrowest answer that does the job | [Evidence, as the consumer](../../tutorials/verify-an-assertion-as-a-consumer/) | | You must load another system's data | Copy it in through a reviewed translator | [Adapter](https://github.com/registrystack/registry-stack/blob/main/products/adapter/README.md) packs; its ingestion job is not yet released | -| A standard governs your edge | Follow it at the edge; keep your own model inside | Adapter facade | -| A national system sets the contract | Follow its contract at the edge | Adapter facade | +| A standard or national system fixes the wire contract | Serve it at the edge | Adapter facade profile | | The person can carry the proof | Let them bring it as a credential | Evidence with [OID4VCI delivery](../../configure/evidence-oid4vci/) | | Integration is not justified | Do not integrate, and write down why | None | @@ -118,21 +117,27 @@ Payment and treasury hand-offs are custom code today. Build them as an applicati For how much to share, see [the disclosure ladder](../../explanation/data-minimization-and-purpose-limitation/#ask-for-the-weakest-answer-that-does-the-job). -## Choose the next step - -Combine the smallest set of paths that meets the job. A configured register can have one custom -route and an independently deployed reminder worker. Add -[Coordinator](../coordinator/) only when a durable, reviewed sequence across waits is useful. -Before deployment, use the [operator handoff](../../operate/) to assign credentials, backups, -retention, and recovery to named operators. +{/* Evidence: products/adapter/README.md, adapter-job; products/adapter/EFFECTIVE-CONTRACT.md, facade; + products/breg/event-records.md, namespace; + docs/site/src/content/docs/reference/breg-api.mdx, If-Match and Idempotency-Key; + docs/site/src/content/docs/configure/evidence-oid4vci.mdx, OID4VCI. */} -## Signs it is time to climb +## Signs part of your work is a process -Stay on the lightest path that works. Any one of these means a part of your work is a process, -not only a register, so consider the next path (a small extension, an application, or a module): +Any one of these means part of your work is a process, not only a register. Check +[where each rule lives](../../explanation/where-each-rule-lives/) first; a timer, workflow or +review policy may already hold it. - A rule needs facts from more than one record. - A matter continues over weeks. - Money is owed or paid. - The law sets a clock. - Two parties must both agree. + +## Choose the next step + +Combine the smallest set of paths that meets the job. A configured register can have one custom +route and an independently deployed reminder worker. Add +[Coordinator](../coordinator/) only when a durable, reviewed sequence across waits is useful. +Before deployment, use the [operator handoff](../../operate/) to assign credentials, backups, +retention, and recovery to named operators. diff --git a/docs/site/src/content/docs/tutorials/derive-a-registry-from-publicschema.mdx b/docs/site/src/content/docs/tutorials/derive-a-registry-from-publicschema.mdx index 497e1ab3db..86669f2017 100644 --- a/docs/site/src/content/docs/tutorials/derive-a-registry-from-publicschema.mdx +++ b/docs/site/src/content/docs/tutorials/derive-a-registry-from-publicschema.mdx @@ -43,11 +43,12 @@ copy of PublicSchema 0.3.0, so deriving from it needs no network. PublicSchema is a starting vocabulary inside your registry and a contract at its edge. Starting from the model gives you field names, types, and code lists that other institutions already use. -Your fields carry PublicSchema concept URIs (the `conceptUri` of each Record type and the -`concepts` of each field in `registry.yaml`), so a mapping to a partner starts from shared -definitions. Your register may diverge where your mandate needs it. Naming is where the +Your registry carries PublicSchema concept URIs (the `conceptUri` of each Record type under +`manifestProjection`, and `concepts` on each field the catalogue describes), so a mapping to a +partner starts from shared definitions. Your register may diverge where your mandate needs it. Naming is where the programme's language gets settled, with the people who run it; the model gives you a draft to -settle it from, not a replacement for that conversation. Deriving also settles the mechanical choices the same way every time: each concept you +settle it from, not a replacement for that conversation. Deriving also settles the mechanical +choices the same way every time: each concept you select becomes a Record type with an identifier the caller supplies, each property becomes a bounded field, an enumeration short enough to read becomes a closed list of codes, and a property the model marks sensitive becomes a `restricted` field. @@ -151,10 +152,10 @@ the model. Keep this notice with the project and with any package built from it. Keep it with the project and with any package you build from it. -The generated catalogue names a placeholder body. Under `manifestProjection.catalog` in -`registry.yaml`, the `publisher`, the entry in `authorities`, and the dataset `owner` are all -`"{title} Authority"` (with a made-up IRI under your reserved base), and `publicService.competentAuthority` points at that authority's `id`. Replace them with the body -your law names for the register. Where your sources do not say, write "not stated" in the +The generated catalogue names a placeholder body. Under `manifestProjection` in `registry.yaml`, +`catalog.publisher`, the `authorities` entry, and each dataset `owner` are `"{title} Authority"` +with an IRI under `https://.example.invalid`, and `publicService.competentAuthority` points at +that authority's `id`. Replace them with the body your law names for the register. Where your sources do not say, write "not stated" in the dataset description rather than inventing a body; you can fill it in later. `model/selection.yaml` is the input the project was derived from. You edit it later when you derive diff --git a/products/platform/CONFIG-CONVENTIONS.md b/products/platform/CONFIG-CONVENTIONS.md index dae4f0f03f..acd9718381 100644 --- a/products/platform/CONFIG-CONVENTIONS.md +++ b/products/platform/CONFIG-CONVENTIONS.md @@ -40,8 +40,8 @@ In scope: registered with `reader: none`, follows CFG-ENV, CFG-NAME, CFG-ID, CFG-QTY, and CFG-EMPTY, and is exempt from CFG-YAML-1, CFG-CHECK-1, CFG-SCHEMA-2, and CFG-SCHEMA-6. A product output that a maintained tool reads is a registered - contract: it is a read-back format, registered with a reader and a schema - that name that tool, whatever command prints it. Explain output a + contract: it is a read-back format, registered with its schema and a `reader` entry + naming that tool's reading function, whatever command prints it. Explain output a maintained tool reads belongs here, not under `reader: none`. A **build artifact** is a read-back format that its own product's build writes and reads again and that no one edits (the compiled package model in