From f6882b1003cb38b53ae83161edcc05785e14c533 Mon Sep 17 00:00:00 2001 From: plind-junior <59729252+plind-junior@users.noreply.github.com> Date: Wed, 15 Jul 2026 19:52:01 +0900 Subject: [PATCH] chore(governance): define contribution areas + auto-merge for prose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit onboarding open-source contributors safely means drawing a line between the parts they can move fast on and the core they must not touch unreviewed. - .github/CODEOWNERS: the hard backstop. the load-bearing paths (review gate, receipts, audit log, storage, schema, kb.* surface, packaging, ci) require a code-owner review; nothing else does. no default owner, so contributor- friendly areas stay fast. - .github/workflows/auto-merge.yml: prose-only prs (docs, top-level markdown, issue templates) get github auto-merge enabled and merge themselves on green ci. it only reads the changed-file list — never checks out or runs pr code — so pull_request_target's token is never exposed to untrusted input. examples/, tests/, adapters/, and all code fall through to human review. - GOVERNANCE.md: the human-readable area map (core / contributor-friendly / auto-merge-safe) tied to the existing decision bars, plus the one-time owner setup (branch protection + allow-auto-merge). - CONTRIBUTING.md: replace "there is no auto-merge" with the three lanes. classifier verified against 18 representative paths; workflow yaml validated. the owner still must enable branch protection + allow-auto-merge in repo settings for either mechanism to bite. --- .github/CODEOWNERS | 56 +++++++++++++++++++++++ .github/workflows/auto-merge.yml | 78 ++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 18 ++++++-- GOVERNANCE.md | 66 +++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/auto-merge.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..9fb8f261 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,56 @@ +# Code owners for vouch — the human-readable map is GOVERNANCE.md. +# +# Only the load-bearing "core" paths are listed here. A PR that changes any of +# them requires review from a code owner before it can merge, once the branch +# protection rule "Require review from Code Owners" is enabled (see GOVERNANCE.md +# → "Turning this on"). Everything NOT listed is contributor-friendly: any +# maintainer can approve it, and the prose-only paths that +# .github/workflows/auto-merge.yml recognises merge automatically on green CI. +# +# There is deliberately NO default (`*`) owner: a catch-all would force owner +# review on every PR and defeat the point. Replace @plind-junior below with your +# maintainer team (e.g. @vouchdev/core) if you prefer team-based ownership. + +# --- the review gate + claim lifecycle — writes must stay gated --- +/src/vouch/proposals.py @plind-junior +/src/vouch/lifecycle.py @plind-junior + +# --- verification: the byte-offset receipt (the differentiator) --- +/src/vouch/receipts.py @plind-junior + +# --- the append-only, authoritative history --- +/src/vouch/audit.py @plind-junior + +# --- durable storage + the derived index --- +/src/vouch/storage.py @plind-junior +/src/vouch/index_db.py @plind-junior + +# --- the object model / on-disk schema contract --- +/src/vouch/models.py @plind-junior +/src/vouch/migrations/ @plind-junior + +# --- the kb.* method surface + transports — parity is load-bearing --- +/src/vouch/capabilities.py @plind-junior +/src/vouch/server.py @plind-junior +/src/vouch/jsonl_server.py @plind-junior + +# --- the protocol contract --- +/SPEC.md @plind-junior +/spec/ @plind-junior + +# --- packaging + the four-site version invariant --- +/pyproject.toml @plind-junior +/openclaw.plugin.json @plind-junior +/package.json @plind-junior +/src/vouch/__init__.py @plind-junior + +# --- build / release / supply-chain — a workflow edit can exfiltrate secrets --- +/.github/workflows/ @plind-junior +/.github/dependabot.yml @plind-junior +/Dockerfile @plind-junior +/install.sh @plind-junior + +# --- governance itself --- +/.github/CODEOWNERS @plind-junior +/GOVERNANCE.md @plind-junior +/CONTRIBUTING.md @plind-junior diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml new file mode 100644 index 00000000..fdf77406 --- /dev/null +++ b/.github/workflows/auto-merge.yml @@ -0,0 +1,78 @@ +name: auto-merge + +# Auto-merge the safe, prose-only contributions (docs, examples, issue +# templates) once CI is green — no maintainer needed. Anything that touches +# code, tests, adapters, config, or the core paths in CODEOWNERS is left for a +# human. This is the repo mirror of vouch's own gate: a change that carries no +# risk is auto-approved; a change to the gate needs a person. +# +# SECURITY: pull_request_target runs with the base repo's token so it can enable +# auto-merge on pull requests from forks. This job only READS the changed-file +# list through the API — it never checks out or runs the PR's code — so +# untrusted code never touches the privileged token. Do not add a checkout of +# the PR head here. +# +# Enabling auto-merge does not bypass anything: GitHub still waits for the +# required status checks and any required reviews configured in branch +# protection before it actually merges. See GOVERNANCE.md → "Turning this on". + +on: + pull_request_target: + types: [opened, synchronize, reopened, ready_for_review] + +permissions: + contents: write + pull-requests: write + +jobs: + auto-merge: + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + steps: + - name: classify changed files and enable auto-merge if prose-only + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + set -euo pipefail + + # A path is auto-merge-safe only if it cannot change code execution, + # the gate, the schema, or the protocol. Keep in sync with + # GOVERNANCE.md → "Tier 2 — auto-merge safe". + is_safe() { + case "$1" in + # governance / security / contract docs are owner-reviewed + SECURITY.md|GOVERNANCE.md|CONTRIBUTING.md|CODEOWNERS) return 1 ;; + spec/*) return 1 ;; # the protocol contract, not prose + docs/*) return 0 ;; # curated prose docs + .github/ISSUE_TEMPLATE/*) return 0 ;; + */*.md) return 1 ;; # nested .md (examples/, adapters/, …) — review it + *.md) return 0 ;; # top-level prose (README, CHANGELOG, …) + *) return 1 ;; # everything else — code, examples, tests, config + esac + } + + files=$(gh pr view "$PR" --repo "$REPO" --json files --jq '.files[].path') + if [ -z "$files" ]; then + echo "no changed files reported; nothing to do"; exit 0 + fi + + all_safe=true + while IFS= read -r f; do + [ -z "$f" ] && continue + if is_safe "$f"; then + echo "safe : $f" + else + echo "review : $f" + all_safe=false + fi + done <<< "$files" + + if [ "$all_safe" = true ]; then + echo "::notice::prose-only PR — enabling auto-merge (still waits for required CI)." + gh pr merge "$PR" --repo "$REPO" --auto --squash \ + || echo "::warning::could not enable auto-merge — enable 'Allow auto-merge' in repo Settings › General and add a required status check in branch protection." + else + echo "::notice::PR touches code/tests/adapters/core — leaving it for human review." + fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33a4bf68..6ae692c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,10 +10,20 @@ usually save everyone time. Every PR gets an automated review pass (CodeRabbit; pushes to `test` also get a Copilot review) plus the CI gate — lint, mypy, the test matrix on -Python 3.11/3.12/3.13, and an sdist + wheel build. A maintainer reads the -review output and the diff, then merges or closes; there is no auto-merge. -So get it green before you push: run the full local gate below, keep the -branch conflict-free, and keep the PR to one concern. +Python 3.11/3.12/3.13, and an sdist + wheel build. What happens next depends +on *where* your PR lands, which [GOVERNANCE.md](GOVERNANCE.md) maps out in +full: + +- **prose-only** (`docs/**`, `examples/**`, top-level markdown, issue + templates) merges itself once CI is green — no maintainer needed. +- **contributor-friendly code** (adapters, retrieval/capture quality, the web + console, tests) is merged by any maintainer after a read of the diff. +- **core** (the review gate, verification, the audit log, storage/schema, the + `kb.*` surface, packaging, CI) needs the code owner — see the area map before + you start, so you know which lane you are in. + +Either way: get it green before you push — run the full local gate below, keep +the branch conflict-free, and keep the PR to one concern. Base feature branches on `test` and target `test` in the PR — it is the integration branch, and it reaches `main` via promotion PRs cut by the diff --git a/GOVERNANCE.md b/GOVERNANCE.md index ae72e578..c45dcebb 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -36,6 +36,72 @@ Three kinds of decisions, three different bars: direction. Requires consensus among all maintainers and a 14-day public comment window on a PR or issue. +## Contribution areas — who may change what + +The three decision bars above map onto concrete parts of the tree. This is the +practical version for a contributor deciding where to start, and it is enforced +by [`.github/CODEOWNERS`](.github/CODEOWNERS) and +[`.github/workflows/auto-merge.yml`](.github/workflows/auto-merge.yml). + +### Core (owner-only) + +The load-bearing invariants — the "surface changes" bar above, made concrete. A +mistake here can make verification forgeable, let a write skip the gate, corrupt +the authoritative history, or break a contract downstream tools depend on. Only +a code owner merges these: + +| Area | Paths | Why | +|---|---|---| +| Review gate + lifecycle | `src/vouch/proposals.py`, `lifecycle.py` | every write must go through `approve()` | +| Verification | `src/vouch/receipts.py` | the byte-offset receipt is the differentiator | +| Authoritative history | `src/vouch/audit.py` | the audit log is append-only | +| Storage + index | `src/vouch/storage.py`, `index_db.py` | a torn write or lost fsync is data loss | +| Object model / schema | `src/vouch/models.py`, `migrations/` | the on-disk + bundle contract | +| Method surface + transports | `capabilities.py`, `server.py`, `jsonl_server.py` | MCP/JSONL/CLI parity | +| Protocol | `SPEC.md`, `spec/` | what downstream tools code against | +| Packaging + version | `pyproject.toml`, `openclaw.plugin.json`, `package.json`, `src/vouch/__init__.py` | the four-site version invariant | +| Supply chain | `.github/workflows/`, `.github/dependabot.yml`, `Dockerfile`, `install.sh` | a workflow edit can exfiltrate secrets | + +Adding a new `kb.*` method spans four of these files, so it is core too — file a +VEP first. + +### Contributor-friendly (any maintainer reviews) + +Where most contributions land: behavior, not invariants. Any maintainer merges +on green CI. Host adapters (`adapters//`, `install_adapter.py`), retrieval +and capture quality (`extract.py`, `recall.py`, `context.py`, `salience.py`, +`compile.py`, `sessions.py`, `hooks.py`, `fetch.py`, `notify.py`, `stats.py`, +`metrics.py`, thin `cli.py` commands), the web console (`webapp/`, +`src/vouch/web/`), and tests (`tests/**`). + +### Auto-merge safe (green CI merges it, no human) + +Prose that cannot change how the code runs — `docs/**`, top-level markdown +(`README.md`, `CHANGELOG.md`, …), and `.github/ISSUE_TEMPLATE/**`. Excluded even +though they are text: `SECURITY.md`, this file, `CONTRIBUTING.md`, `CODEOWNERS`, +and `spec/**` (the protocol contract). `examples/**` is *not* auto-merged — it +can carry runnable code, so a maintainer reads it (Tier 1). + +## How PRs merge + +Two mechanisms live in `.github/`: + +- **`CODEOWNERS`** forces a code-owner review on any core path — the hard + backstop — once branch protection's "Require review from Code Owners" is on. +- **`auto-merge.yml`** enables GitHub auto-merge on a prose-only PR so it merges + itself on green CI. It reads only the changed-file list (it never runs PR + code), and enabling auto-merge still waits for the required checks. + +**Owner setup (one-time, repo Settings, on `test` and `main`):** enable *Allow +auto-merge*; add a branch-protection rule that *requires a PR*, *requires review +from Code Owners*, and *requires the `ci` status checks*; set the owner handle in +`CODEOWNERS`. Without required checks, auto-merge has nothing to wait on and +merges nothing. + +This is vouch applied to vouch: a docs PR is a claim whose receipt verifies — +mechanically safe, auto-approved; a change to `proposals.py` touches the gate +itself, and no receipt can vouch for that, so a person decides. + ## Disagreements If contributors disagree on a PR, the assigned maintainer makes the call.