From f4529df219b8c4327109c97fa04d446ba0b93e81 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Tue, 11 Aug 2026 22:20:14 -0400 Subject: [PATCH] docs: document the DCO signoff requirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DCO check fails any PR with an unsigned commit, and the rule appeared nowhere in the repo — not AGENTS.md, CONTRIBUTING.md, README.md, or the Copilot mirror. A hard merge gate that is invisible until it fails, whose remedy is a history rewrite, is exactly what AGENTS.md exists to capture. It blocked #1978. Added to the "When work is complete" list beside the other PR-hygiene gates, weighted toward prevention over recitation: `git config format.signOff true` once per clone beats remembering `-s`, because the failure is only discovered after pushing. The repair path (`git rebase --signoff` + `--force-with-lease`) carries the rebase caveat and the empty-remediation-commit alternative for when you are not the sole author, plus a note that the signoff is a DCO assertion in the author's own name and is never applied on someone else's behalf. Mirrored into .github/copilot-instructions.md in the same PR, as AGENTS.md requires for PR-hygiene rules — two lines next to `Closes #N` and the version label, since the mirror is read on every review and is a distillation, not a copy. Not added to CONTRIBUTING.md: external contributors file issues rather than PRs, so DCO never reaches them, and documenting it there would muddy that. Closes #1979 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3 Signed-off-by: cliffhall --- .github/copilot-instructions.md | 1 + AGENTS.md | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0628741c..f9d995d3 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -103,6 +103,7 @@ Both exist and do different jobs. Theme files (`src/theme/.ts`) custo - **A test or smoke must not touch real user state.** The web smokes run against a throwaway catalog via the shared `scripts/lib/prod-web-server.mjs` helper, never the developer's `~/.mcp-inspector/mcp.json` (#1977); the cli/tui smokes drive a temp `--catalog`. A new smoke spawning its own server, or teardown that removes a work dir without first awaiting `stopChild` (the #1801 race — `child-cleanup.mjs` exports both halves and both are required), should be flagged. - **Every PR references an issue**, first body line `Closes #`. - **Every PR carries exactly one version label**, `v1` or `v2`, matching its base branch. +- **Every commit carries a `Signed-off-by:` trailer.** The DCO check is a hard merge gate and fails on a single unsigned commit; `git commit -s`, or `git config format.signOff true` once per clone. Repairing pushed commits means `git rebase HEAD~ --signoff` + `git push --force-with-lease`. - Update the relevant `README.md` / `AGENTS.md` when a change adds, removes, renames, or repurposes a file or folder, changes the structure or tech stack, or introduces a command, dependency, or architectural pattern. ## What to prioritize in review diff --git a/AGENTS.md b/AGENTS.md index 5f9e44bc..6a19dfb8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -207,6 +207,10 @@ All work should be driven by items on the project board. - **Branch names start with the target version segment.** The first path segment must be the version whose base branch the PR targets — `v2/` for work on `v2/main`, `v1/` for work on `v1/main` — followed by the usual type and slug: `v2/ci/restore-claude-workflow`, `v2/fix/oauth-scope-union`, `v1/fix/proxy-ssrf-pin`. Not `ci/restore-claude-workflow`. This keeps the two lines legible in `git branch -a` and in the PR list once v1 and v2 branches coexist on the same remote, and it matches the base branches themselves (`v2/main`, `v1/main`). - When work is complete: - Run `npm run ci` from the root — the mandatory pre-push gate (see [Mandatory pre-push gate](#mandatory-pre-push-gate)). `npm run validate` is the fast inner-loop check and is **not** a substitute: it runs no coverage gate, no smokes, and no Storybook tests. + - **Sign off every commit — the DCO check is a hard merge gate.** The repo runs the [probot DCO app](https://probot.github.io/apps/dco/), which fails the PR unless **every** commit in it carries a `Signed-off-by: Author Name ` trailer matching its author. There is no per-PR override and no partial credit: one unsigned commit out of six fails the whole check. + - **Prevent it, don't remember it.** Commit with `git commit -s`, or — better — run `git config format.signOff true` once per clone so the trailer is added automatically and the rule stops depending on memory. This matters more than the rule itself, because the failure is invisible until after you have pushed. + - **Repairing already-pushed commits** means rewriting them: `git rebase HEAD~ --signoff`, then `git push --force-with-lease`. Use `--force-with-lease` rather than `--force` so a concurrent push can't be silently clobbered, and only rewrite when you are the sole author and nobody else has based work on the branch — the usual [perils of rebasing](https://git-scm.com/book/en/v2/Git-Branching-Rebasing). If others are on the branch, add an empty remediation commit instead. + - The signoff is a **Developer Certificate of Origin** assertion in the author's own name, so it must be that author's identity — never sign off someone else's commit. - Open a PR against the matching base branch (`v1/main` for v1, `v2/main` for v2) and set the item's Status to **In Review** - **Attach screenshots as proof of functionality.** Any change to the web UI or the TUI must show its result: capture before/after screenshots (or a short GIF for an interaction) and put them in a **`pr-screenshots/` folder off the repo root**, creating it if it doesn't exist. That folder is **gitignored** — the images are working artifacts staged for upload, never committed to the source tree — so attach them to the PR body from there rather than referencing an in-repo path. Name them for what they show (`tools-tab-before.png`, `tools-tab-after.png`), not `Screenshot 2026-07-31 at 14.02.11.png`. - **Link the PR to its issue — mandatory for every PR, from anyone.** No PR is opened without an issue to reference; if one doesn't exist yet, create it first (labeled and on the board) rather than opening the PR and backfilling. Note also that only the **repo maintainers** open PRs at all (see [Contributing](#contributing)) — everyone else files a detailed issue. The PR body's **first line must be `Closes #`**. ⚠️ Note: closing keywords only auto-link/auto-close for PRs targeting the repo's **default branch** (`main`). Because v2 PRs target `v2/main` (a non-default branch), `Closes #N` there is only a cross-reference — it will **not** create a hard link or close the issue on merge. (There is no `gh` flag for manual linking — `gh pr edit` has no `--add-issue`; closing keywords are the only mechanism GitHub exposes, and they're gated to the default branch.)