governance: external contribution model (issues/discussions/RFCs/PRs) (#143)

Formalize the public contribution surface. Maintainers keep a separate internal
process and are exempt from the intake gates; everyone stays bound by review,
CODEOWNERS, and branch protection.

Model:
- Issues = problem reports only (bug form + config.yml redirects ideas to
  Discussions and disables blank issues).
- Discussions = ideas + RFC incubation.
- RFCs = anyone (incl. external) authors docs/rfcs/NNNN-*.md; a maintainer
  merging it is acceptance. Distinct from the maintainer-internal
  docs/dev/rfc-00N-* track.
- PRs = link an `accepted` issue or accepted RFC, or use the trivial fast-lane
  (typos/docs/deps). Enforced softly to start (template + review).

Adds GOVERNANCE.md, rewrites CONTRIBUTING.md, adds docs/rfcs/ (README +
template), .github issue/PR/discussion templates. Wires docs/rfcs/ into the
doc-link checker (excluded like releases; linked from docs/dev/index.md).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andrew Altshuler 2026-06-06 23:58:08 +03:00 committed by GitHub
parent c7365bf8ef
commit 343f1f17ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 406 additions and 8 deletions

34
.github/DISCUSSION_TEMPLATE/rfc.yml vendored Normal file
View file

@ -0,0 +1,34 @@
labels: ["rfc"]
body:
- type: markdown
attributes:
value: |
Use this to **incubate an RFC** — socialize a design and reach rough
consensus before writing the formal document. When it's ready, graduate
it into a pull request that adds `docs/rfcs/NNNN-title.md`
(see [docs/rfcs/README.md](../blob/main/docs/rfcs/README.md)); a
maintainer merging that PR is acceptance.
For a plain feature request or open-ended idea, use the **Ideas**
category instead. For bugs, open an [Issue](../../issues/new/choose).
- type: textarea
id: problem
attributes:
label: Problem / motivation
description: What needs solving, and why is it worth the long-run cost?
validations:
required: true
- type: textarea
id: sketch
attributes:
label: Proposed direction (sketch)
description: A rough shape of the design. Detail comes later in the RFC document.
validations:
required: true
- type: textarea
id: invariants
attributes:
label: Invariants touched
description: Which items in docs/dev/invariants.md does this affect or risk? Any deny-list brush?
validations:
required: false

55
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View file

@ -0,0 +1,55 @@
name: Bug report
description: Report a reproducible problem or wrong behavior in OmniGraph.
title: "bug: <short summary>"
labels: ["bug", "needs-triage"]
body:
- type: markdown
attributes:
value: |
Issues are for **reporting problems** — concrete, reproducible bugs.
For ideas, feature requests, or questions, please use
[Discussions](../../discussions) instead.
For a security vulnerability, follow [SECURITY.md](../../blob/main/SECURITY.md) — do **not** file it here.
A maintainer will triage this; once labelled **`accepted`** it's open for a pull request
(see [GOVERNANCE.md](../../blob/main/GOVERNANCE.md)).
- type: textarea
id: what-happened
attributes:
label: What happened
description: What went wrong, and what you expected instead.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Steps to reproduce
description: Minimal steps, commands, schema/query, or a failing snippet.
placeholder: |
1. omnigraph init ...
2. omnigraph ...
3. observed: ... / expected: ...
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: Output of `omnigraph --version` (or the engine/crate version) and how you installed it.
validations:
required: true
- type: input
id: environment
attributes:
label: Environment
description: OS, architecture, and storage backend (local FS / S3 / RustFS / MinIO).
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs / output
description: Relevant error text or logs. Will be rendered as code.
render: shell
validations:
required: false

13
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,13 @@
# Issues are for problem reports only. Disable blank issues so everything is
# routed: bugs through the form, everything else to Discussions / SECURITY.md.
blank_issues_enabled: false
contact_links:
- name: 💡 Idea, feature request, or RFC
url: https://github.com/ModernRelay/omnigraph/discussions
about: Propose features and designs in Discussions. RFCs graduate from there into a docs/rfcs/ pull request.
- name: ❓ Question or help
url: https://github.com/ModernRelay/omnigraph/discussions
about: Ask in Discussions — questions are not tracked as Issues.
- name: 🔒 Security vulnerability
url: https://github.com/ModernRelay/omnigraph/blob/main/SECURITY.md
about: Report security issues privately per SECURITY.md — never as a public Issue.

29
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,29 @@
<!--
Thanks for contributing! See CONTRIBUTING.md and GOVERNANCE.md.
A substantive PR needs a backing accepted issue or accepted RFC.
Maintainers: your internal process applies; the link requirement below
is for external contributions.
-->
## What & why
<!-- One or two sentences: what this changes and why. -->
## Backing issue / RFC
<!-- Pick one. A substantive change needs (1) or (2). -->
- [ ] Fixes an **accepted** issue: Closes #
- [ ] Implements / is an **accepted** RFC: <link to docs/rfcs/NNNN-*.md>
- [ ] **Trivial fast-lane** (typo / docs / dependency bump / comment / one-line CI) — no issue/RFC required
## Checklist
- [ ] Change is focused (one logical change)
- [ ] Tests added/updated for behavior changes (or N/A)
- [ ] Public docs updated if user-facing surface changed (or N/A)
- [ ] Reviewed against [docs/dev/invariants.md](../blob/main/docs/dev/invariants.md) — no Hard Invariant weakened, no deny-list item hit (or justified)
## Notes for reviewers
<!-- Anything that helps review: tradeoffs, follow-ups, areas of risk. -->

View file

@ -1,10 +1,29 @@
# Contributing
Small bug fixes and documentation improvements are welcome directly through pull
requests.
Thanks for your interest in OmniGraph. This page is the practical how-to; the
rules and decision authority behind it live in [GOVERNANCE.md](GOVERNANCE.md).
For larger changes, please open an issue or design discussion first so the
proposed direction is clear before implementation starts.
## Start in the right place
| I want to… | Go to | Notes |
|---|---|---|
| **Report a bug** or wrong behavior | **[Open an Issue](../../issues/new/choose)** | Concrete and reproducible. A maintainer triages it; once labelled **`accepted`** it's open for a PR. |
| **Suggest a feature / share an idea / ask** | **[Start a Discussion](../../discussions)** | Ideas and questions live here, not in Issues. |
| **Propose a design / RFC** | **An RFC pull request** | Anyone can author one — see [docs/rfcs/README.md](docs/rfcs/README.md). A maintainer merging it is acceptance. |
| **Fix something / implement a change** | **A pull request** | Must link an `accepted` issue or an accepted RFC — unless it's trivial (below). |
| **Report a security vulnerability** | **[SECURITY.md](SECURITY.md)** | Do **not** open a public Issue. |
### When can I just open a PR?
The **trivial fast-lane** — open directly, no prior issue/RFC needed: typo and
wording fixes, doc corrections, dependency bumps, comment fixes, obvious
one-line CI tweaks. Anything more substantial needs a backing `accepted` issue
or accepted RFC first, so the *why* is agreed before the *how* is reviewed. A PR
that turns out to be non-trivial will be redirected — that's about process, not
the merit of the change.
> **Maintainers (ModernRelay team)** follow a separate internal process and are
> not bound by the intake rules above. Everyone is bound by review, CODEOWNERS,
> branch protection, and CI.
## Development
@ -49,6 +68,11 @@ CI runs both.
## Pull Requests
- keep changes focused
- include tests for behavior changes when practical
- update public docs when the user-facing surface changes
- **Link the backing issue or RFC** (`Closes #123`, or reference the RFC) — or
mark the PR as trivial per the fast-lane.
- Keep changes focused; one logical change per PR.
- Include tests for behavior changes when practical.
- Update public docs when the user-facing surface changes.
New to the codebase? Read [AGENTS.md](AGENTS.md) — the architecture map and the
always-on invariants every change is reviewed against.

106
GOVERNANCE.md Normal file
View file

@ -0,0 +1,106 @@
# Governance
This document describes how **external contributions** to OmniGraph are
proposed, accepted, and merged. It exists so an outside contributor can answer,
without asking: *where does my report/idea/change go, who decides, and what has
to happen before code lands?*
> **Scope.** This governs the public contribution surface — Issues,
> Discussions, RFCs, and pull requests from people outside the ModernRelay
> team. **Maintainers operate under a separate internal process** and are not
> bound by the intake gates below. Everyone, maintainer or not, is still bound
> by the universal gates: branch protection on `main` and CODEOWNERS review
> (see [docs/dev/branch-protection.md](docs/dev/branch-protection.md) and
> [docs/dev/codeowners.md](docs/dev/codeowners.md)).
## Roles
| Role | Who | Authority |
|---|---|---|
| **Maintainer** | The code owners in [`.github/CODEOWNERS`](.github/CODEOWNERS) (generated from [`.github/codeowners-roles.yml`](.github/codeowners-roles.yml)) | Validate issues, accept/reject RFCs, review and merge PRs, set direction. Final decision authority. |
| **Contributor** | Anyone else | Report problems (Issues), propose ideas (Discussions), author RFCs, and open pull requests. |
Decision authority rests with the maintainers. CODEOWNERS is the single source
of truth for who that is; this document does not duplicate the list.
## The three channels
Each channel has one job. Using the right one is the first thing we ask of a
contribution.
| Channel | Purpose | Not for |
|---|---|---|
| **[Issues](../../issues)** | **Report a problem** — a bug, a regression, a documented behavior that's wrong. Something concrete and reproducible. | Feature requests, ideas, questions, or design proposals (→ Discussions). |
| **[Discussions](../../discussions)** | **Propose and explore** — new ideas, feature requests, questions, and the incubation of RFCs. | Bug reports (→ Issues). |
| **Pull requests** | **Land a sanctioned change** — a fix for a *validated* issue, an *accepted* RFC, or a trivial change (see fast-lane). | Substantive change with no backing issue/RFC — it will be redirected. |
## How a change becomes mergeable
```
┌─────────── bug ───────────┐ ┌──────── idea / feature ────────┐
▼ │ ▼ │
Issue (problem report) │ Discussion (idea / RFC incubation) │
│ │ │ │
maintainer triage │ rough consensus │
│ │ │ graduate │
▼ │ ▼ │
label: accepted ──────────┐ │ RFC PR (docs/rfcs/NNNN-*.md) │
│ │ │ │ │
│ │ │ maintainer review │
▼ ▼ │ ▼ │
Pull request ◀──────────┴──────────│── merged == accepted │
(links the issue or the accepted RFC) ◀───────┘ (implementation PRs reference it) │
review + CODEOWNERS + branch protection
merged
```
### Issues → validated
A new issue starts unlabeled. A maintainer triages it and, if it's a real,
in-scope problem, applies the **`accepted`** label. **Only `accepted` issues are
open for a contributor PR.** This prevents the "I fixed an issue you hadn't
agreed was a problem" rejection. Want to fix something? Get the issue accepted
first, or pick one already labelled `accepted` / `help wanted`.
### Discussions → RFCs → accepted
Ideas and feature requests start in **Discussions**. Anyone — including external
contributors — may then **author an RFC** by opening a pull request that adds
`docs/rfcs/NNNN-title.md` (see [docs/rfcs/README.md](docs/rfcs/README.md)). The
RFC is reviewed as code; **a maintainer merging it is the act of acceptance**
(it becomes the durable decision record). Implementation PRs then reference the
accepted RFC.
Authoring an RFC is open to everyone; **accepting one is a maintainer
decision.** Maintainers may also decline an RFC, with rationale, by closing it.
### Pull requests → sanctioned
A contributor PR must do one of:
1. link a maintainer-**`accepted`** issue it fixes, or
2. be (or reference) an **accepted RFC**, or
3. qualify for the **trivial fast-lane**.
**Trivial fast-lane** — these may be opened directly, no prior issue/RFC:
typo and wording fixes, documentation corrections, dependency bumps, comment
fixes, and obviously-correct one-line CI tweaks. When in doubt, open an Issue or
Discussion first; a PR that turns out to be non-trivial will be asked to.
A substantive PR with no backing issue/RFC will be closed with a pointer to the
right channel — not as a judgment of the idea, but to keep design discussion
where it's reviewable.
## What maintainers do *not* gate
Maintainers' own changes do not pass through the intake gates above — the team
runs a separate internal process. The universal gates (review, CODEOWNERS,
branch protection, CI) apply to everyone. Enforcement of the intake rules is, to
start, **by convention and review** (PR template + labels); an automated check
keyed to author association may be added later if volume warrants.
## Code of conduct & security
- Conduct: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
- Security issues are **not** public Issues — see [SECURITY.md](SECURITY.md).
## Changing this document
Governance changes the same way code does: a pull request, reviewed by
maintainers. This file describes the external surface; the internal maintainer
process is intentionally out of scope here.

View file

@ -51,6 +51,18 @@ constraints. User-facing behavior should still be documented through
| Install and deployment packaging | [install.md](../user/install.md), [deployment.md](../user/deployment.md) |
| Release history | [releases/](../releases/) |
## Contribution & Governance
| Area | Read |
|---|---|
| How to contribute (external) | [CONTRIBUTING.md](../../CONTRIBUTING.md) |
| Governance model, roles, decision authority | [GOVERNANCE.md](../../GOVERNANCE.md) |
| Public contribution RFC track | [rfcs/](../rfcs/) |
The `docs/rfcs/` track is the **public, externally-authorable** RFC process. The
maintainer/internal RFCs below (`rfc-00N-*.md`) are a separate, team-owned
track; don't conflate the two.
## Active Implementation Plans
Working documents for in-flight feature work. Removed when the work lands.

View file

@ -0,0 +1,54 @@
# RFC NNNN: <title>
| | |
|---|---|
| **Status** | Proposed |
| **Author(s)** | <your name / handle> |
| **Discussion** | <link to the originating Discussion, if any> |
| **Implementation** | <issue/PR links, filled in as work lands> |
> Status is maintained by maintainers: `Proposed` while the PR is open,
> `Accepted` on merge, `Declined` on close, `Superseded by NNNN` later.
## Summary
One paragraph: what this changes, in plain terms.
## Motivation
What problem does this solve, and why is it worth the ongoing cost? Tie it to a
concrete need (a Discussion, a recurring issue, a user request). Per the
project's first principle, argue the *long-run liability*, not just the
short-term convenience.
## Guide-level explanation
Explain the change as you'd teach it to a user or contributor: new commands,
syntax, API shapes, behavior. Examples first.
## Reference-level design
The precise design: data structures, IR/AST/planner changes, storage/format
impact, migration path, error behavior. Enough that a reviewer can find the
holes.
## Invariants & deny-list check
Which Hard Invariants in [../dev/invariants.md](../dev/invariants.md) does this
touch? Does it brush against any deny-list item — and if so, why is this the
justified exception? State explicitly that no invariant is weakened, or which
Known Gap moves.
## Drawbacks & alternatives
What does this cost, what did you reject, and why. "Do nothing" is a valid
alternative to weigh.
## Reversibility
Is this reversible? On-disk/wire/format and substrate choices are near-permanent
and demand more evidence; a CLI flag or doc is cheap to undo. Say which this is.
## Unresolved questions
What's deliberately left open for review to settle.

66
docs/rfcs/README.md Normal file
View file

@ -0,0 +1,66 @@
# RFCs
Substantial changes to OmniGraph — new user-facing surface, format or protocol
changes, anything irreversible or cross-cutting — go through a lightweight RFC
so the design is agreed *as reviewable code* before implementation starts. This
is the public RFC track, open to **anyone, including external contributors**.
This complements the always-on review bar in
[../dev/invariants.md](../dev/invariants.md): the invariants say *what every
change must respect*; an RFC says *why this particular change is worth making and
how*.
> **Two tracks, don't conflate them.** This `docs/rfcs/` directory is the
> **public contribution** track (anyone authors; maintainers accept). The
> maintainer-internal RFCs under `docs/dev/rfc-00N-*.md` are a separate,
> team-owned track for in-flight internal work. If you're an outside
> contributor, you're in the right place here.
## When you need one
- **RFC required:** new query/schema/CLI/HTTP surface; on-disk or wire-format
changes; a new substrate dependency; anything the deny-list in
[../dev/invariants.md](../dev/invariants.md) flags; anything irreversible
("reversibility shapes evidence demand").
- **RFC not required:** bug fixes for an `accepted` issue, and the trivial
fast-lane (typos, docs, deps) — see [../../CONTRIBUTING.md](../../CONTRIBUTING.md).
If you're unsure, start a [Discussion](../../../discussions); a maintainer will
tell you whether it needs an RFC.
## Lifecycle
```
Discussion (incubate, get rough consensus)
│ graduate
RFC pull request → adds docs/rfcs/NNNN-title.md (Status: Proposed)
maintainer review ──▶ changes requested / declined (PR closed, with rationale)
merged == Accepted (the merged file is the durable decision record)
Implementation PR(s) reference the accepted RFC
```
- **Author:** anyone. **Acceptance:** a maintainer decision, performed by
merging the RFC PR. Declining is closing it with rationale.
- The merged RFC *is* the accepted record — there is no separate sign-off step.
- Later reversals don't edit history: supersede with a new RFC that links back
and flip the old one's `Status` to `Superseded`.
## Numbering & naming
- File: `docs/rfcs/NNNN-kebab-title.md`, where `NNNN` is the next free
zero-padded integer (`0001`, `0002`, …). `0000-template.md` is reserved.
- Pick the number when you open the PR; if it collides with another in-flight
RFC, the second to merge bumps theirs.
## Status values
`Proposed` (open PR) · `Accepted` (merged) · `Declined` (closed) ·
`Superseded by NNNN` · `Implemented` (set once the work lands, optional).
Copy [0000-template.md](0000-template.md) to start.

View file

@ -34,10 +34,15 @@ PY
canonical=()
while IFS= read -r line; do
canonical+=("$line")
done < <(find docs -type f -name '*.md' ! -path 'docs/releases/*' ! -path 'docs/internal/*' | sort)
done < <(find docs -type f -name '*.md' ! -path 'docs/releases/*' ! -path 'docs/internal/*' ! -path 'docs/rfcs/*' | sort)
if [[ -d docs/releases ]]; then
canonical+=("docs/releases/")
fi
# RFCs are a growing collection (like releases): represent the directory, not
# every per-RFC file. The dir must be linked from an audience index.
if [[ -d docs/rfcs ]]; then
canonical+=("docs/rfcs/")
fi
linked=()
for index_file in "${index_files[@]}"; do