diff --git a/.github/workflows/reusable-dependabot-hygiene.yml b/.github/workflows/reusable-dependabot-hygiene.yml new file mode 100644 index 0000000..01ce128 --- /dev/null +++ b/.github/workflows/reusable-dependabot-hygiene.yml @@ -0,0 +1,87 @@ +# Fills Dependabot PR bodies with the org's required hygiene sections so +# pr-hygiene / spec-alignment gates can evaluate them. Dependabot cannot +# write these sections itself, and without them its PRs block forever in +# repos whose gates require body sections. The Governing Spec section is +# computed honestly from the repo's approved-specs registry against the +# actually-changed files; the repo's CI gates still validate the bump. +# +# Call from a workflow with `on: pull_request_target` (e.g. the cla.yml +# caller). No-op for non-Dependabot authors and already-filled bodies. +name: reusable-dependabot-hygiene + +on: + workflow_call: {} + +permissions: + contents: read + pull-requests: write + +jobs: + fill-body: + name: dependabot-body + if: github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 # base branch — for the approved-specs registry + + - name: Fill required hygiene sections + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ github.event.pull_request.number }} + TITLE: ${{ github.event.pull_request.title }} + run: | + body="$(gh pr view "$PR" --repo "$GITHUB_REPOSITORY" --json body --jq .body)" + if printf '%s' "$body" | grep -q "^## Summary"; then + echo "hygiene sections already present" + exit 0 + fi + + gh pr diff "$PR" --repo "$GITHUB_REPOSITORY" --name-only > /tmp/changed.txt + + specs="" + if [ -e specs/governance/approved-specs.json ]; then + specs="$(python3 - <<'PY' + import json + changed = [l.strip() for l in open("/tmp/changed.txt") if l.strip()] + reg = json.load(open("specs/governance/approved-specs.json")) + ids = [] + for spec in reg.get("specs", []): + if any(p.startswith(pref) for pref in spec.get("governs", []) for p in changed): + ids.append(spec["id"]) + print("\n".join(f"- \x60{i}\x60" for i in ids)) + PY + )" + fi + + { + echo "## Summary" + echo + echo "$TITLE — automated dependency update by Dependabot. Body sections added by the org dependabot-hygiene workflow." + echo + echo "## Governing Spec" + echo + if [ -n "$specs" ]; then + echo "$specs" + else + echo "No governed paths changed." + fi + echo + echo "## Project Item" + echo + echo "Dependabot maintenance — exempt from board tracking per org governance." + echo + echo "## Definition of Done" + echo + echo "- All repo CI gates pass with the bumped dependency" + echo + echo "## Validation" + echo + echo "- Full CI matrix on this PR validates the bump" + echo + echo "---" + echo + printf '%s\n' "$body" + } > /tmp/newbody.md + + gh pr edit "$PR" --repo "$GITHUB_REPOSITORY" --body-file /tmp/newbody.md + echo "hygiene sections added to PR #$PR" diff --git a/.governance-version b/.governance-version index 795460f..79127d8 100644 --- a/.governance-version +++ b/.governance-version @@ -1 +1 @@ -v1.1.0 +v1.2.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index b260b6d..0665b31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to the org-wide governance in this repository are documented The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/): a **major** bump means a rule change that can newly block merges in consuming repos; **minor** adds rules or tooling that are backwards-compatible; **patch** is clarification only. +## [1.2.0] - 2026-07-08 + +### Added + +- `reusable-dependabot-hygiene.yml` — fills Dependabot PR bodies with the required hygiene sections, declaring the governing specs actually matched by the changed files, so dependency PRs stop perma-blocking on body-section gates while repo CI still validates the bump. Repos opt in by adding one job to their `cla.yml` caller (already on `pull_request_target`). +- `docs/runbook.md` — operations runbook: release process, repo onboarding, failure playbook (rerun snapshot-pinning, CLA recheck, secret visibility, empty-commit retrigger), and environment constraints. + ## [1.1.1] - 2026-07-08 ### Fixed diff --git a/README.md b/README.md index 05a05a6..8ea288c 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,11 @@ Shared governance for every repo under the `traverse-framework` GitHub organizat | [`docs/exception-process.md`](docs/exception-process.md) | How to document and review an exception to a rule | | [`docs/ai-agent-hardening.md`](docs/ai-agent-hardening.md) | Conventions every repo's `CLAUDE.md`/`AGENTS.md` should encode | | [`docs/owner-setup.md`](docs/owner-setup.md) | Manual steps only the org owner can perform | +| [`docs/runbook.md`](docs/runbook.md) | Operations runbook: releases, repo onboarding, failure playbook | | [`CHANGELOG.md`](CHANGELOG.md) | Versioned governance releases (repos pin these) | | [`.github/workflows/reusable-cla.yml`](.github/workflows/reusable-cla.yml) | Reusable CLA gate; signatures stored centrally on the `cla-signatures` branch | | [`.github/workflows/reusable-governance.yml`](.github/workflows/reusable-governance.yml) | Reusable baseline gate: version pin, required files, spec-alignment | +| [`.github/workflows/reusable-dependabot-hygiene.yml`](.github/workflows/reusable-dependabot-hygiene.yml) | Fills Dependabot PR bodies so body-section gates can evaluate them | | [`.github/workflows/org-audit.yml`](.github/workflows/org-audit.yml) | Weekly org-wide compliance audit; opens an issue when drift is found | | [`governance/rulesets/baseline-branch-ruleset.json`](governance/rulesets/baseline-branch-ruleset.json) | Canonical branch ruleset applied to every repo | | [`scripts/org/apply_rulesets.sh`](scripts/org/apply_rulesets.sh) | Applies the baseline ruleset to all org repos (idempotent) | diff --git a/docs/runbook.md b/docs/runbook.md new file mode 100644 index 0000000..cb32a32 --- /dev/null +++ b/docs/runbook.md @@ -0,0 +1,43 @@ +# Operations Runbook + +How to operate the `traverse-framework` org governance. Written so any agent or human can run the system without re-deriving it. Owner-only steps live in [owner-setup.md](owner-setup.md); rules live in [../constitution.md](../constitution.md) and [quality-standards.md](quality-standards.md). + +## Releasing a governance change + +1. Branch off this repo's default branch; make the change; add a `CHANGELOG.md` entry (major = can newly block merges in consuming repos; minor = additive; patch = fix/clarification). +2. Open a PR with the section superset (`## Summary`, `## Governing Spec`, `## Project Item`, `## Definition of Done`, `## Validation`). Queue `gh pr merge --squash --auto` — the required `cla / cla` and `baseline / governance-baseline` checks gate it. +3. After merge: `git tag -a vX.Y.Z -m "Governance release vX.Y.Z" && git push origin vX.Y.Z`. +4. Reusable workflows deploy org-wide at merge (callers reference the default branch). Vendored/doc content deploys only when a consuming repo bumps its `.governance-version` in its own PR — deliberate adoption, never automatic. + +## Adding a repo to the org + +1. Create it from [`repo-template`](https://github.com/traverse-framework/repo-template) ("Use this template") — it ships compliant. +2. From this repo: `scripts/org/apply_rulesets.sh ` (both rulesets). +3. Enable settings: `gh api -X PATCH repos/traverse-framework/ -F allow_auto_merge=true -F delete_branch_on_merge=true`, plus `PUT .../private-vulnerability-reporting` and `PUT .../vulnerability-alerts`. +4. Confirm with `scripts/org/audit_compliance.sh`. + +For a repo not born from the template, run `scripts/org/rollout_governance.sh ` to PR the required files in. + +## Routine operation + +- The weekly `org-audit` workflow audits every repo and opens/updates an issue on drift. Manual run: `scripts/org/audit_compliance.sh`. +- Dependabot PRs: the `dependabot-hygiene` reusable workflow fills their bodies with the required sections (honest governing-spec declarations computed from changed files); repo CI gates then decide. If one predates the workflow, comment `@dependabot rebase`. +- Merging is always: open PR → gates green → `gh pr merge --squash --auto`. Never disable a ruleset to land something — fix forward. + +## Failure playbook (learned the hard way) + +| Symptom | Cause | Fix | +|---|---|---| +| `cla / cla` fails for everyone at action startup | `CLA_ASSISTANT_PAT` missing **or the org secret's repository-access list excludes the repo** (`gh api repos///actions/organization-secrets` to check) | Fix secret access; the gate degrades gracefully for owner/bots meanwhile | +| Check stays red after a fix merged to this repo | **GitHub re-runs pin the originally resolved reusable-workflow SHA** — `gh run rerun` never picks up new gate logic | Trigger a *new* event: push a commit, or comment `recheck` (CLA gate listens for it) | +| `no checks reported` on a PR head | Comment-triggered runs don't attach to the head commit | Push an empty commit to the PR branch (synchronize event) | +| Dependabot PR blocked on body-section gates | PR opened before dependabot-hygiene workflow existed in that repo | `@dependabot rebase` | +| CLA asks a coding agent to sign | Commit author email not linked to a GitHub account, or agent identity not allowlisted | Fix `git config user.email`; agent identities are allowlisted in `reusable-cla.yml` | +| `stress_no_fd_leak` (traverse) fails on a docs-only PR | Flaky on shared runners | Re-run the failed job once the run completes; a hardening fix is tracked | +| Governance PR blocked by the very check it fixes | Chicken-and-egg on required checks in this repo | Make the check pass legitimately (e.g. set the missing secret); as a last resort the **owner** temporarily disables the ruleset in the UI — agents must never do this | + +## Constraints to remember + +- GitHub Free org: **no org-level rulesets** (per-repo via `apply_rulesets.sh`), no org-secret management via `repo`-scoped tokens. +- Coding agents in this environment cannot: merge without the owner's authorization, force-push tags, create public repos, or disable rulesets. Queue the work; hand those actions to the owner. +- The `cla-signatures` branch is legal-record storage — never rewrite it.