Skip to content

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/CODEOWNERS is 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.

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:

TeamAnswerable for
@openregs/core-maintainersThe fallback: tooling, docs, and anything no narrower rule claims.
@openregs/fixreg-maintainersThe FIXREG regime — its canonical texts, its obligation atoms, its graph.
@openregs/spec-maintainersspec/: the schemas and taxonomies every regime is validated against.
@openregs/governanceWho is answerable: this roster, the generated CODEOWNERS, the branch-protection policy.

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:

RegimeMaintainer 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.

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.

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.


Terminal window
make gen-codeowners # regenerate .github/CODEOWNERS from MAINTAINERS.yaml
openregs validate maintainers MAINTAINERS.yaml

The 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.


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.
ItemWhat it isOwed byFirst responseResolutionEscalate after
atom-pra machine-proposed obligation atom waiting on a humanthe regime’s maintainer team (@openregs/fixreg-maintainers for FIXREG)2 business days5 business days10 business days
quarantine-itema document a validation gate refused, waiting on triagethe regime’s maintainer team (@openregs/fixreg-maintainers for FIXREG)2 business days10 business days15 business days
spec-changea change to a schema or taxonomy under spec/@openregs/spec-maintainers3 business days10 business days20 business days
roster-changea change to MAINTAINERS.yaml@openregs/governance3 business days10 business days20 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.


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.

AfterEscalate toWhat you do
10 business days@openregs/core-maintainersComment 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/governanceOpen 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/governanceGovernance 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.

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.

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:

RuleEnforced by
main advances only by merging a branchthe pre-receive hook, mechanically
at least one approving review from a code owner of every changed paththe hook, mechanically
an approval covers one commit; new work dismisses itthe hook, mechanically
nobody approves their own branch; a bot never approvesthe hook, mechanically
every required check green at the merged commit, published by the ci identitythe hook, mechanically
the merge commit records the reviewers it was made onthe hook, mechanically
force-pushes and deletions of main refusedthe hook, mechanically
CODEOWNERS matches the rostermake test, mechanically
this table’s durations match MAINTAINERS.yamlmake test, mechanically
the SLAs and the ladder abovepeople, by hand
the same rules on a hosted GitHub repositorya human applying .github/branch-protection.github.json
who a pusher actually isthe 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.


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