Governance
OpenRegs turns law into data other people rely on. Somebody has to be answerable for each answer it gives, and this document says who, by when, and what to do when that fails. It is deliberately short: everything here that can be checked by a machine is checked by one, and the parts that cannot are marked as such.
- Who owns what lives in
MAINTAINERS.yaml, which is the single source of truth..github/CODEOWNERSis generated from it and the pre-receive hook reads it; neither is written by hand. - How to contribute is
CONTRIBUTING.md. - How a review is actually driven, command by command, is
docs/runbooks/atom-review.md.
1. Maintainer teams
Section titled “1. Maintainer teams”Ownership is always assigned to a team, never to a person, so that one person
leaving never leaves a path unowned. The teams, as MAINTAINERS.yaml declares
them:
| Team | Answerable for |
|---|---|
@openregs/core-maintainers | The fallback: tooling, docs, and anything no narrower rule claims. |
@openregs/fixreg-maintainers | The FIXREG regime — its canonical texts, its obligation atoms, its graph. |
@openregs/spec-maintainers | spec/: the schemas and taxonomies every regime is validated against. |
@openregs/governance | Who is answerable: this roster, the generated CODEOWNERS, the branch-protection policy. |
Per-regime teams
Section titled “Per-regime teams”Each regime under regimes/ names exactly one maintainer team, in the
regimes: block of the roster. That team owns the regime’s canon/ and atoms/
— the two paths where merging turns a machine proposal into law — and everything
else in the regime directory:
| Regime | Maintainer team |
|---|---|
fixreg | @openregs/fixreg-maintainers |
A new regime is not governed until it has an entry there. make gen-codeowners
routes it; nothing else has to be edited, and nothing else may be.
Machine identities
Section titled “Machine identities”openregs-bot (role proposer) opens atomize/* branches and can never
approve one. openregs-ci (role ci) is the only identity whose check results
count. Both restrictions are enforced by the pre-receive hook, not by convention.
Joining and leaving
Section titled “Joining and leaving”Adding a name to MAINTAINERS.yaml adds somebody who can merge law into the
canon, so /MAINTAINERS.yaml is itself an owned path: @openregs/governance must
approve. A maintainer may step down by opening the same kind of pull request; a
team that has stopped answering is dealt with by the ladder in §4, which ends in
that pull request being opened by somebody else.
2. Ownership has one source
Section titled “2. Ownership has one source”make gen-codeowners # regenerate .github/CODEOWNERS from MAINTAINERS.yamlopenregs validate maintainers MAINTAINERS.yamlThe test suite runs the generator with --check and fails when the committed
.github/CODEOWNERS and the roster have drifted. Editing CODEOWNERS by hand moves
ownership somewhere the roster does not know about, which is the one failure a
single source of truth exists to prevent.
Two pattern forms are supported and the generator refuses to emit anything else:
* and a root-anchored path (trailing slash for a directory). A pattern the hook
cannot evaluate is a rule that silently owns nothing.
3. Review service levels
Section titled “3. Review service levels”These are promises about time, and they are machine-readable: the
review_slas: block of MAINTAINERS.yaml carries the same durations, and the
test suite fails if this table and that block disagree. Durations are business
days throughout — measuring a review promise in calendar days quietly promises
the weekend too. The clock starts when the item is opened.
- First response — somebody with standing has read it and said something.
- Resolution — merged, closed, or a stated reason it is neither. “Still reading Article 5” is a resolution if it is written down; silence is not.
- Escalate after — past this, the ladder in §4 opens.
| Item | What it is | Owed by | First response | Resolution | Escalate after |
|---|---|---|---|---|---|
atom-pr | a machine-proposed obligation atom waiting on a human | the regime’s maintainer team (@openregs/fixreg-maintainers for FIXREG) | 2 business days | 5 business days | 10 business days |
quarantine-item | a document a validation gate refused, waiting on triage | the regime’s maintainer team (@openregs/fixreg-maintainers for FIXREG) | 2 business days | 10 business days | 15 business days |
spec-change | a change to a schema or taxonomy under spec/ | @openregs/spec-maintainers | 3 business days | 10 business days | 20 business days |
roster-change | a change to MAINTAINERS.yaml | @openregs/governance | 3 business days | 10 business days | 20 business days |
Why atom-pr is the tightest clock: proposing an atom costs a second and
reviewing one does not, so the queue in front of the humans is the thing that
grows. Why quarantine-item is looser: its fix is usually a mapper change plus a
replay, not a reading — see
docs/runbooks/quarantine-triage.md.
What an SLA does not do. It does not weaken a single rule in §5. An atom PR that nobody has reviewed by day 5 is late; it is not thereby merged. Lateness is answered by escalation, never by lowering the bar for a merge, because the bar is the only thing standing between a machine’s reading of a statute and an answer somebody relies on.
4. Escalation
Section titled “4. Escalation”Each rung is measured from when the item was opened, not from the previous rung, so a stalled item climbs the ladder on its own schedule.
| After | Escalate to | What you do |
|---|---|---|
| 10 business days | @openregs/core-maintainers | Comment on the item naming the team. Any core maintainer may review it in the responder team’s place. The code-owner rule is not waived: this works because core-maintainers own the fallback, and reviewing a regime’s paths means being added to that regime’s team. |
| 20 business days | @openregs/governance | Open an issue naming the team and link the item. Governance answers in writing with one of three things: a reviewer assigned by name, a decision that the item will not be taken, or a date it will be revisited. |
| 30 business days | @openregs/governance | Governance proposes a change to MAINTAINERS.yaml — a member added to the responder team, or the regime reassigned. Nothing else on this ladder changes who is answerable; this rung is the one that does. |
Escalation is public. It happens on the item and in the repository, never in a private message, because “who was asked and when” is part of the record for the same reason “who reviewed what” is.
Conflict and disagreement
Section titled “Conflict and disagreement”A disagreement about what a text means is settled by the maintainer team that
owns the regime, in the pull request, in writing — the merge commit’s
Reviewed-by trailer then records who accepted that reading. A disagreement
about process, or between teams, goes to @openregs/governance, whose decision
stands until a later pull request changes the rule it applied. Neither is a vote:
someone with standing decides, and the record says who.
Security reports
Section titled “Security reports”Do not open a public issue for a vulnerability in the tooling. Mail
@openregs/core-maintainers (addresses are in MAINTAINERS.yaml) and expect a
first response inside the atom-pr first-response window. A wrong answer about
the law is not a security report — it is an atom PR, and it goes through §5 like
everything else.
5. What is enforced by a machine, and what is not
Section titled “5. What is enforced by a machine, and what is not”Governance that depends on everybody remembering it is not governance. This is the line:
| Rule | Enforced by |
|---|---|
main advances only by merging a branch | the pre-receive hook, mechanically |
| at least one approving review from a code owner of every changed path | the hook, mechanically |
| an approval covers one commit; new work dismisses it | the hook, mechanically |
| nobody approves their own branch; a bot never approves | the hook, mechanically |
| every required check green at the merged commit, published by the ci identity | the hook, mechanically |
| the merge commit records the reviewers it was made on | the hook, mechanically |
force-pushes and deletions of main refused | the hook, mechanically |
| CODEOWNERS matches the roster | make test, mechanically |
this table’s durations match MAINTAINERS.yaml | make test, mechanically |
| the SLAs and the ladder above | people, by hand |
| the same rules on a hosted GitHub repository | a human applying .github/branch-protection.github.json |
| who a pusher actually is | the transport, not the hook |
every commit a pull request adds carries a Signed-off-by | .github/workflows/dco.yml, mechanically — but on the forge only |
The last four are honest limits, not oversights. Branch protection on a hosted
repository is server-side configuration and no test here may talk to GitHub;
openregs review github-settings renders the payload a human applies, and names
the three rules GitHub has no setting for. The hook authorizes, it does not
authenticate: the pushing identity arrives in OPENREGS_PUSHER, which a real
transport sets. And the sign-off is checked by a workflow rather than by the
hook, so a branch pushed straight at a bare remote is never asked for one — a
trailer is a claim a person makes, and the pull request is where they make it.
6. Contribution rights, and the licence they carry
Section titled “6. Contribution rights, and the licence they carry”Contributions are taken under a DCO. Every commit a pull request adds carries
a Signed-off-by trailer naming its author — the Developer Certificate of Origin
1.1, which is a statement the contributor makes rather than rights they assign.
The rule, the reasoning and the mechanics are
CONTRIBUTING.md under Sign your work;
.github/workflows/dco.yml checks it on every pull request, over the commits
that pull request adds and no others, so the rule reaches forward and never back.
Why a DCO and not a contributor licence agreement: the reviewers this project depends on are domain experts inside regulated firms, and a CLA asks them for a legal review before their first comment. The cost of that choice is stated rather than buried — a DCO grants no right to relicense a contribution later, so it narrows what the project may do with its own licence, and the narrowing starts with the first outside contribution.
What licence that is, for the serve plane, is not yet decided. Whether the
tree stays Apache-2.0 with the trademark carrying the commercial line, or the
serve plane is relicensed to AGPL before a hosted product reaches general
availability, is open. The analysis — the rehosting risk, what copyleft would
cost the people who self-host, how a partial relicense breaks the
machine-readable code/data split, and a recommendation — is
docs/decisions/d3-license-posture.md.
It is pending sign-off, and nothing in the tree has moved on account of it:
the code is Apache-2.0 (/LICENSE), the corpus CC-BY-4.0
(/regimes/LICENSE-DATA), and MAINTAINERS.yaml already makes both files
@openregs/governance’s to change, because relicensing is a governance act and
not a fallback edit.
If that decision goes the other way, the DCO goes with it — relicensing optionality needs a CLA — and the migration, including the part that is not automatable, is §7 of the same document.
7. Amending this document
Section titled “7. Amending this document”GOVERNANCE.md is owned by @openregs/core-maintainers through the fallback rule
and MAINTAINERS.yaml by @openregs/governance. Changing a duration means
changing both — the roster block and the table in §3 — in one pull request,
because the test suite will not let them disagree. That is the point: the promise
and its statement move together or not at all.
Rendered from openregs/openregs@cbe9615:GOVERNANCE.md