Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .github/workflows/reusable-dependabot-hygiene.yml
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 1 addition & 1 deletion .governance-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.1.0
v1.2.0
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
43 changes: 43 additions & 0 deletions docs/runbook.md
Original file line number Diff line number Diff line change
@@ -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 <repo>` (both rulesets).
3. Enable settings: `gh api -X PATCH repos/traverse-framework/<repo> -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 <version> <repo>` 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/<org>/<repo>/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.
Loading