diff --git a/.github/scripts/dco-check_test.sh b/.github/scripts/dco-check_test.sh index 4d9336ea..7b589cb3 100755 --- a/.github/scripts/dco-check_test.sh +++ b/.github/scripts/dco-check_test.sh @@ -12,6 +12,9 @@ cd "$tmp" git init -q -b main git config user.email "tester@example.com" git config user.name "Tester" +# Disable signing — the sandbox repo has no key; this test is about DCO trailers. +git config commit.gpgsign false +git config tag.gpgsign false git commit --allow-empty -q -m "Init" expect_pass() { diff --git a/AGENTS.md b/AGENTS.md index 5d587da3..cb368d6f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,20 +11,30 @@ This document is for both humans and AI coding assistants. It describes how AI t ## Disclosing AI assistance -When a commit was authored with significant AI help, add a trailer to the commit message: +When a commit was authored with significant AI help, add a trailer to the commit message. The preferred format names the agent, model, and tool: ``` -Assisted-by: +Assisted-by: : [] ``` Examples: +``` +Assisted-by: Anthropic:claude-opus-4-7 [Claude Code] +Assisted-by: OpenAI:gpt-5 [Codex CLI] +Assisted-by: Google:gemini-2.5-pro [Gemini CLI] +``` + +The simpler `Assisted-by: ` form is also accepted: + ``` Assisted-by: Claude Code Assisted-by: GitHub Copilot Assisted-by: Cursor ``` +The richer format aligns with the Linux kernel's `coding-assistants.rst` convention and gives reviewers more context for the same one-line cost. + Do **not** use `Co-Authored-By:` for AI tools. That trailer is reserved for human collaborators and breaks DCO / contributor-license tooling that expects a real identity behind every co-author. CI rejects PRs whose commits include a `Co-Authored-By:` line naming an AI tool (Claude, Copilot, Cursor, ChatGPT, Gemini, etc.); fix the trailer with `git commit --amend` or rewrite history with `git rebase -i` before pushing. > **Heads-up for Claude Code users:** Claude Code defaults to adding a `Co-Authored-By: Claude` trailer. Disable it for this repo, or strip it before committing. The `Assisted-by:` trailer is the equivalent disclosure that *passes* the policy. @@ -42,7 +52,7 @@ Do **not** use `Co-Authored-By:` for AI tools. That trailer is reserved for huma AI tools are good at producing more code than is needed. When using one: -- Keep PRs under 500 lines, per [`CONTRIBUTING.md`](CONTRIBUTING.md). +- Keep PRs focused on one concern, per [`CONTRIBUTING.md`](CONTRIBUTING.md). Split when the diff outgrows the concern. - Don't refactor unrelated code in the same PR. - Don't add speculative abstractions, unused helpers, or "future-proofing" code. - Don't add comments that restate the code; STYLE.md requires comments only where the *why* is non-obvious. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 686471f1..61f62e75 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,13 +32,12 @@ make ci # what CI runs: license-check + vet + lint + test + build ## Pull requests -- **Small is better.** Aim for under 500 lines changed. Split larger work into multiple PRs. -- **One concern per PR.** Don't mix refactors with feature work. +- **One concern per PR.** Don't mix refactors with feature work. Smaller PRs are easier to review — split when the diff outgrows the concern, not when it crosses an arbitrary line count. - **Tests required** for new functionality. Bug fixes should include a regression test. - **Atomic commits.** Each commit must build and pass tests on its own. - **Sign off your commits**: `git commit -s`. We require [DCO](https://developercertificate.org/). - **Sign your commits cryptographically.** `main` requires signed commits. The fastest path is SSH signing — run `scripts/setup-signing.sh` once and it configures `gpg.format=ssh`, `user.signingkey`, `commit.gpgsign=true`, `tag.gpgsign=true`. Register the same SSH key as a "Signing Key" in your GitHub account (Settings → SSH and GPG keys). Sigstore `gitsign` (keyless OIDC) is an accepted alternative; see [sigstore.dev/gitsign](https://docs.sigstore.dev/cosign/signing/git_support/). -- **AI-assisted commits**: add `Assisted-by: ` trailer to your commit message. Do not use `Co-Authored-By:` for AI tools. +- **AI-assisted commits**: add an `Assisted-by:` trailer (see [`AGENTS.md`](AGENTS.md) for format). Never use `Co-Authored-By:` for AI tools — CI rejects it. ## Commit message format diff --git a/scripts/apply-branch-protection.sh b/scripts/apply-branch-protection.sh new file mode 100755 index 00000000..4c2ca497 --- /dev/null +++ b/scripts/apply-branch-protection.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# apply-branch-protection.sh — apply the rules described in +# .github/branch-protection.yml to the `main` branch via gh api. +# Idempotent; requires `gh auth login` with `repo` scope. +# +# To change a setting: edit both .github/branch-protection.yml (intent) and +# the JSON payload below (live state), then re-run. +set -euo pipefail + +if ! command -v gh >/dev/null 2>&1; then + echo "error: 'gh' CLI is required (https://cli.github.com/)" >&2 + exit 1 +fi + +repo=$(gh repo view --json nameWithOwner --jq '.nameWithOwner' 2>/dev/null || true) +if [ -z "$repo" ]; then + echo "error: could not determine repo (run from a clone with origin set)" >&2 + exit 1 +fi + +echo "Applying branch protection to $repo:main …" + +gh api \ + -X PUT "repos/${repo}/branches/main/protection" \ + -H "Accept: application/vnd.github+json" \ + --input - <<'JSON' +{ + "required_status_checks": { + "strict": true, + "contexts": [ + "verify", + "build (linux-amd64)", + "build (linux-arm64)", + "Analyze (go)", + "govulncheck", + "release-notes block edited", + "no AI co-author trailer", + "dco sign-off" + ] + }, + "enforce_admins": true, + "required_pull_request_reviews": { + "required_approving_review_count": 1, + "require_code_owner_reviews": true, + "dismiss_stale_reviews": true + }, + "restrictions": null, + "required_linear_history": true, + "allow_force_pushes": false, + "allow_deletions": false, + "required_conversation_resolution": true, + "required_signatures": true +} +JSON + +echo "Branch protection applied. Verify in Settings → Branches or with:" +echo " gh api repos/${repo}/branches/main/protection --jq ."