From 5ad08281daad86541f8489b141bfa5c0547c983d Mon Sep 17 00:00:00 2001 From: Raghu Betina Date: Wed, 30 Sep 2026 18:23:44 -0500 Subject: [PATCH] Write down how to commit, review, and land In a trial on 2026-09-30, fresh agents with no owner settings were asked how to land a CLI change. Both found the checks and the release rules, but neither gave the full commit rules, the squash and rebase landing, or the Docs: line. Those lived only in the private service repository and in one person's global instructions. One answer told the colleague to add an agent co-author trailer and a "Generated with" footer, and one presented the review plugin as a shell command with no install step. CONTRIBUTING.md now holds those rules for this public repository, whose docs cannot link to the private one. The skills and service pages share them, so the page asks that a change reach all three, and it records a review in the same Review: line they use. AGENTS.md routes to it in one line, and the README links it by absolute URL because npm packages the README. The documentation test checks the new page's links and requires an entrypoint to reach it. It stays out of the npm package. The tracked harness settings enable the cross-review plugins for anyone who trusts the main checkout, and turn off one harness's commit and pull request attribution. The object form of that setting keeps the file readable by versions that reject a bare false. The other harness's attribution follows each user's account setting, which overrides any repository file, so the guide says to turn it off there and names the trailer and line to remove. A change to these files or to CONTRIBUTING.md changes what every contributor's agent does, so it now needs independent review. One harness's default sandbox blocks the npm registry and the local HTTP server that several tests start. This repository keeps that default, so the guide says which commands need approval. Worktrees under .claude/worktrees are now ignored by git, Prettier, and ESLint, which otherwise checked the nested checkout's files. --- .claude/settings.json | 18 ++++++ .codex/config.toml | 6 ++ .github/pull_request_template.md | 17 ++++++ .gitignore | 1 + AGENTS.md | 10 +-- CONTRIBUTING.md | 101 +++++++++++++++++++++++++++++++ README.md | 1 + eslint.config.js | 2 +- test/documentation.test.js | 1 + 9 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 .claude/settings.json create mode 100644 .codex/config.toml create mode 100644 .github/pull_request_template.md create mode 100644 CONTRIBUTING.md diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..32fe7f4 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,18 @@ +{ + "extraKnownMarketplaces": { + "cross-review": { + "source": { + "source": "github", + "repo": "raghubetina/cross-review" + } + } + }, + "enabledPlugins": { + "codex-review@cross-review": true + }, + "attribution": { + "commit": "", + "pr": "", + "sessionUrl": false + } +} diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..5388868 --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,6 @@ +[marketplaces.cross-review] +source_type = "git" +source = "https://github.com/raghubetina/cross-review.git" + +[plugins."claude-review@cross-review"] +enabled = true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..d590bcb --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ +## Summary + + + +Docs: + + + +## Validation + +- `npm audit`: +- `npm run check`: diff --git a/.gitignore b/.gitignore index 721204b..cb5cbff 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ coverage/ node_modules/ tmp/ *.tgz +/.claude/worktrees/ diff --git a/AGENTS.md b/AGENTS.md index b0ddbd6..08c592c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,15 +5,17 @@ handled-error recovery in `docs/errors.md`, publication mechanics in `RELEASING. `CHANGELOG.md`. When behavior changes, update its owning document in the same change. - Verify with `npm run check`, which takes about 30 seconds. A fresh checkout needs `npm ci --ignore-scripts` first. +- Follow `CONTRIBUTING.md` for commit messages, pull request bodies, review setup, and landing. - To add a command, follow `docs/commands.md#add-a-command`. To change the version, follow `RELEASING.md#prepare-the-version-pull-request`, which includes the `CHANGELOG.md` entry. - Sibling repositories: `firstdraft/firstdraft` (private) owns the Service API and Plan format; `firstdraft/skills` owns the Skill and plugin packaging. - Changes to the accepted API-contract range, accepted Plan formats, command names or flags, handled `error` values, - exit statuses, `AGENTS.md`, `CLAUDE.md`, or `RELEASING.md` get an independent review through cross-review: - `codex-review` from Claude Code, `$claude-review` from Codex. Pass the service repository's `docs/review-focus.md` - as `--focus-file`, fetched with `gh api` when no sibling checkout exists. Put the reviewer, session id, and verdict - in the pull request body, and leave the findings out. + exit statuses, `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, `RELEASING.md`, `.claude/settings.json`, or + `.codex/config.toml` get an independent review through cross-review: `codex-review` from Claude Code, + `$claude-review` from Codex. Pass the service repository's `docs/review-focus.md` as `--focus-file`, fetched + with `gh api` when no sibling checkout exists. Put the reviewer, session id, and verdict in the pull request + body, and leave the findings out. - `firstdraft plan compile` defaults to local output in the current directory. GitHub publication requires `--github`; Codespaces is a fallback. Keep Skill callers and recovery instructions aligned with this boundary. - The service repository coordinates releases and owns their approval and smoke policy. One approved coordinated diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..83b4057 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,101 @@ +# Contributing + +This page covers checks, commit messages, pull requests, independent review, and landing for the First Draft CLI. +[AGENTS.md](AGENTS.md) routes agent work and lists the changes that need independent review; the +[documentation map](docs/README.md) routes everything else. The commit, pull request, review, and landing rules +below also apply in `firstdraft/skills` and the private service repository, `firstdraft/firstdraft`. When you change +one of them, change it in all three `CONTRIBUTING.md` pages. + +## Checks + +Use the Node.js and npm versions pinned in `.tool-versions`. Before committing, run the sequence in +[Work on the repository](docs/README.md#work-on-the-repository): `npm ci --ignore-scripts` in a fresh checkout or +worktree, then `npm audit` and `npm run check`. Hosted CI repeats the tests and package checks on Node.js 22.0 on +Linux, Windows, and macOS. + +This repository keeps Codex's default `workspace-write` sandbox, which has no network access. Inside it, `npm audit` +cannot reach the npm registry, `npm ci` fails unless every package is already in your npm cache, and `npm run check` +fails because several tests start a local HTTP server on `127.0.0.1`. When Codex asks to rerun one of these commands +outside the sandbox, check the command and approve it. The sandbox also keeps `.git` read-only, so Codex asks before +it commits. + +## Commit messages + +- Write the subject in the imperative mood, in 50 characters or fewer. +- Wrap the body at 72 columns or fewer, and explain why the change was needed; the diff shows what changed. +- Squash the branch to one reviewable commit before merging, as [Landing](#landing) describes. +- Do not mention an agent. Add no `Co-authored-by` or other agent trailer to a commit, and no "Generated with" line + to a pull request body. The tracked `.claude/settings.json` turns off Claude Code's attribution. Codex adds them + when attribution is on in your Codex account settings, which override repository instructions; turn it off + there. If Codex adds a `Co-authored-by: Codex ` trailer to a commit, remove it before + pushing. If it adds the line `Generated with [Codex](https://openai.com/codex/).` to a pull request body, remove + it before opening the pull request, or edit the body if Codex opened it. + +## Pull requests + +The pull request body carries: + +- a summary of what changed and why; +- one line: `Docs: updated X` or `Docs: none, because ...`; and +- when [AGENTS.md](AGENTS.md) requires independent review, one line, `Review: , session , `, + with the findings left out. + +Use GitHub closing keywords only for completed Issues: even `does not close #123` closes #123. Say the remainder is +tracked in open Issue #123 instead. The [pull request template](.github/pull_request_template.md) holds these fields. + +## Independent review + +The reviewer is the other vendor's agent, through [cross-review](https://github.com/raghubetina/cross-review): Codex +reviews from Claude Code, and Claude Code reviews from Codex. Install both and sign in to each (`claude auth login` +and `codex login`). In Claude Code, ask for a Codex review or run `/codex-review:codex-review`; in Codex, use +`$claude-review`. Neither is a shell command. + +- **Claude Code:** the tracked `.claude/settings.json` registers the cross-review marketplace and enables + `codex-review`. It loads once you accept the workspace-trust prompt for the main checkout; linked worktrees use + that trust. A headless `claude -p` run loads it only where you have already trusted the checkout. +- **Codex:** the tracked `.codex/config.toml` declares the marketplace and enables `$claude-review` in a trusted + project. The first trusted session fetches the plugin and the next one loads it; + `codex plugin marketplace upgrade cross-review` fetches it at once. In the Codex desktop app, quit and reopen it + after the first fetch. + +If the tracked configuration does not load a plugin, install it yourself: + +```sh +claude plugin marketplace add raghubetina/cross-review +claude plugin install codex-review@cross-review +codex plugin marketplace add raghubetina/cross-review +codex plugin add claude-review@cross-review +``` + +To run a review: + +1. Start a `new` session over the branch, such as `new branch main`, or over an explicit range with + `new range ..`. +2. Pass the service repository's `docs/review-focus.md` with `--focus-file`, from a sibling `firstdraft/firstdraft` + checkout or fetched into the ignored `tmp/`: + + ```sh + mkdir -p tmp + gh api repos/firstdraft/firstdraft/contents/docs/review-focus.md \ + -H 'Accept: application/vnd.github.raw' > tmp/review-focus.md + ``` + + List the affected surfaces after `--`. + +3. When the review finishes, the host agent runs `cite` and checks each finding against the cited lines before + relaying it. +4. Record each decision after `--` as `reject F-...: reason`, `accept F-...`, or `defer F-...`. Review an amendment + in the same session with `range ..HEAD`. + +Do not merge while a required review is still running. Review third-party changes, such as Dependabot or outside +pull requests, with `--capability read-only`. + +## Landing + +Merge after hosted CI passes and any required review has finished. Squash to one reviewable commit, or more only for +logically discrete units of work, and land it with a rebase merge: `gh pr merge --rebase`. Use a merge +commit only when the integration is itself meaningful work, such as resolving substantial conflicts. After merging, +report the repository and the exact merged SHA. + +A merge does not release anything. [RELEASING.md](RELEASING.md) covers publication, which the service repository +coordinates. diff --git a/README.md b/README.md index d8ea8eb..39ed192 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ repository owns the exact command and transport behavior between them. | Change the CLI | [Agent instructions](https://github.com/firstdraft/cli/blob/main/AGENTS.md), then [documentation map](docs/README.md) | | Find a command or output contract | [Command reference](docs/commands.md) | | Interpret an error or recover safely | [Errors and recovery](docs/errors.md) | +| Commit, review, or land a change | [Contributing guide](https://github.com/firstdraft/cli/blob/main/CONTRIBUTING.md) | | Prepare or publish a package | [Release runbook](https://github.com/firstdraft/cli/blob/main/RELEASING.md) | | See what changed in a version | [Changelog](https://github.com/firstdraft/cli/blob/main/CHANGELOG.md) | | Report a vulnerability | [Security policy](SECURITY.md) | diff --git a/eslint.config.js b/eslint.config.js index 462fa3a..af111f7 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -3,7 +3,7 @@ import globals from "globals"; export default [ { - ignores: ["node_modules/", "tmp/"], + ignores: ["node_modules/", "tmp/", ".claude/worktrees/"], }, js.configs.recommended, { diff --git a/test/documentation.test.js b/test/documentation.test.js index 7d52ba3..58b89c4 100644 --- a/test/documentation.test.js +++ b/test/documentation.test.js @@ -17,6 +17,7 @@ const markdownFiles = [ ...[ "AGENTS.md", "CHANGELOG.md", + "CONTRIBUTING.md", "README.md", "RELEASING.md", "SECURITY.md",