From 9f35b5dbba34bb7b004c8e9b09af025b48828b40 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 13 Sep 2026 11:01:42 -0700 Subject: [PATCH 1/3] Adopt Conventional Commits and make the rules binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONTRIBUTING.md banned Conventional Commits outright. Its load-bearing argument was that nothing here generates from commit types, because release notes were hand-written in CHANGELOG.md. Notes are moving to generated, so that premise goes. Subjects become (): , keeping the component as the scope. The bans that were right are kept verbatim: ticket IDs, status tags, filenames in subjects. A short note records that the policy changed so it is not re-litigated from older git log entries. Co-Authored-By is now banned explicitly, on any artifact, whoever wrote the change. AGENTS.md states that the rules bind humans and agents equally and that a violating pull request is declined rather than fixed in review. Its inline copy of the prefix rules is gone — a second copy is one that goes stale. CL-7880 --- AGENTS.md | 14 +++++--- CONTRIBUTING.md | 88 +++++++++++++++++++++++++++++-------------------- 2 files changed, 62 insertions(+), 40 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7cfc4a432..392b11425 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,11 +61,15 @@ same reason. ## Commits, pull requests, and issue tracking -**MUST follow `CONTRIBUTING.md`.** That file is the source of truth for commit -titles and bodies, PR titles and bodies, and Linear/GitHub linking. Do not use -Conventional Commits prefixes (`feat:`, `fix:`, `docs:`, `ci:`, …), ticket IDs -in commit subjects, or free-form PR body sections. Rewrite before push if a -message violates those rules. Commit with the operator's local git identity. +**MUST follow `CONTRIBUTING.md`.** That file is the single source of truth for +commit titles and bodies, PR titles and bodies, and Linear/GitHub linking. It +is not summarized here on purpose — a second copy of the rules is a copy that +goes stale, and the rules have changed before. Read it. + +**This binds humans and agents equally. A pull request that violates +`CONTRIBUTING.md` will be declined** — not fixed in review. Check your commit +subjects against that file before you push, and rewrite them if they do not +match. Commit with the operator's local git identity. ## Pushing diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5498a2050..f05bd37a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,56 +43,67 @@ not substitute a bare `bun test` (it also scans ### Title (MUST) -- Imperative, present tense, max **72** characters -- Starts with a verb: `Add`, `Fix`, `Remove`, `Harden`, `Document`, … -- No trailing punctuation, no abbreviations for their own sake -- No filenames or paths in the subject — the diff already lists them -- Match the voice of recent history: +Follow [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/). -```bash -git log origin/main --format='%s' | head -20 +```text +(): ``` -**Banned subject prefixes** (all of them, including habits from other projects): +- **Type** — one of `feat`, `fix`, `perf`, `refactor`, `test`, `docs`, `build`, + `ci`, `chore`, `style` +- **Scope** — the component the change lives in: `feat(executor)`, + `fix(nameref)`, `perf(glob)`, `docs(release)`. Omit it only when a change + genuinely spans the repo +- **Description** — imperative, present tense, lowercase after the colon, no + trailing period. The whole subject line stays within **72** characters +- **Breaking changes** — `!` after the type/scope (`feat(config)!: ...`), or a + `BREAKING CHANGE:` footer in the body + +This repo's history used a bare `component: description` prefix (`executor:`, +`nameref:`). New commits keep the component as the **scope** and lead with the +type: `executor: add retry` becomes `feat(executor): add retry`. + +Releases use `chore(release): perfi X.Y.Z`. Release notes use +`docs(release): add perfi X.Y.Z release notes`. -- Conventional Commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`, `ci:`, `perf:`, `style:`, `build:` -- Scoped forms: `docs(changelog):`, `net:`, `frontend:` -- Ticket IDs: `CL-1234:`, `INTR-79:`, `#456:` +**Still banned in the subject:** + +- Ticket IDs: `CL-1234:`, `INTR-79:`, `#456:` — linking is a pull-request + concern (see [Issue tracking](#issue-tracking-linear-and-github)) - Status tags: `WIP:`, `[urgent]`, `(security):` +- Filenames and paths — the diff already lists them +- Abbreviations for their own sake **Good:** ```text -Add retry logic for failed network requests -Fix race condition in transaction verification -Document the permission queue behavior +feat(executor): add retry logic for failed network requests +fix(inference): close race condition in transaction verification +docs(permissions): document the permission queue behavior +perf(glob): stop rescanning ignored directories ``` **Bad:** ```text -feat: add retry logic -fix(auth): race in server.ts -CL-5494: flatten model picker -Update code +add retry logic (no type) +feat: add retry to src/executor.ts (no scope, filename in subject) +fix(auth): CL-5494 race in server.ts (ticket ID, filename) +chore: update code (says nothing) ``` -### Why not `feat:` / `fix:` / `docs:` / `ci:`? - -Conventional Commits are useful when tools **generate** changelogs, SemVer bumps, -or release notes from commit types. This project does not: +### A note on the previous rule -- Release notes are hand-written in `CHANGELOG.md` and deliberately strip ticket - and PR IDs from public notes. -- Reviewers and `git log` readers need a sentence that stands alone years later, - not a taxonomy debate (`chore` vs `refactor` vs `fix`). -- An imperative subject already encodes the action: `Fix race in the approval -queue` is clearer than `fix: race in the approval queue`. -- Prefixes train agents and humans to smuggle scope, ticket IDs, and file names - into the subject — noise we already reject elsewhere. +This project previously **banned** Conventional Commits and required a plain +imperative subject. That rule rested on the repo generating nothing from commit +types — release notes were hand-written in `CHANGELOG.md`. That is changing: +release notes move to being generated from merged pull requests, so the premise +no longer holds. -The Git and Go projects use the same plain-English model. Familiarity with -Angular-style prefixes is not a reason to adopt them here. +The parts of the old rule that were right are kept: the subject is still an +imperative sentence that stands on its own years later, and the ticket-ID, +status-tag, and filename bans are unchanged. Only the type and scope are new. +Please do not re-open this from reading older `git log` entries. ### Body (usually omit) @@ -118,6 +129,9 @@ hand, not for the person reviewing this PR today. - Separate refactors from feature additions - Separate formatting/whitespace from behavioral changes - Commit with the operator's local git identity (never invent author metadata) +- **Never** add a `Co-Authored-By` trailer — to a commit, a pull request, a + GitHub issue, or any other artifact. This holds whoever or whatever wrote the + change ## Pull requests @@ -136,9 +150,13 @@ git log origin/main..HEAD --format='%s' ### Title (MUST) -Same rules as [commit titles](#title-must): imperative present-tense sentence, -no prefixes, no ticket IDs, no trailing punctuation. The title describes the -**whole branch**, not a single commit. +Same rules as [commit titles](#title-must): `(): `, +imperative present tense, no ticket IDs, no trailing punctuation. The title +describes the **whole branch**, not a single commit — pick the type that fits +the branch's main effect. + +The pull-request title is what generated release notes quote, so it is read by +people who never see the diff. Write it for them. ### Body (MUST) From 1cf523b88172994012412c19d8f5eebfdc714932 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 13 Sep 2026 11:30:56 -0700 Subject: [PATCH 2/3] Update the layers that enforce the commit rule, not just the doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flipping CONTRIBUTING.md alone would have left the repo fighting itself. The style skill — loaded by builder and other agents — banned subject prefixes outright and listed `feat: add retry logic` as a bad example, so agents would keep writing subjects the new rule declines. The review skill would then flag the compliant ones as prefix violations. Updated together: the style skill now specifies the type/scope form and keeps the ticket-ID, status-tag and filename bans; the review skill flags a missing or unrecognized type instead of flagging types themselves; the implement skill and the builder director prompt stop telling workers the title is a plain-English sentence; the PR template matches. CONTRIBUTING.md also states the consequence directly, so the README can point at it rather than making a claim the source of truth does not. CL-7880 --- .github/PULL_REQUEST_TEMPLATE.md | 5 ++- CONTRIBUTING.md | 3 ++ .../corbits-skills/skills/implement/SKILL.md | 2 +- plugins/corbits-skills/skills/review/SKILL.md | 2 +- plugins/corbits-skills/skills/style/SKILL.md | 45 +++++++++++++------ src/agent/directors/builder/package.ts | 2 +- 6 files changed, 40 insertions(+), 19 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 50041c6ee..70edc2ca2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,6 +18,7 @@ Fixes #123 Partial work: Related to CL-1234 No tracker: delete this comment block and leave no magic-word line. -Do not put CL-… or #N in the PR title. Commit subjects stay plain English -with no ticket IDs — see CONTRIBUTING.md. +Do not put CL-… or #N in the PR title. Commit subjects are Conventional +Commits — (): — with no ticket IDs. See +CONTRIBUTING.md. --> diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f05bd37a1..3629bca0a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -135,6 +135,9 @@ hand, not for the person reviewing this PR today. ## Pull requests +These rules bind humans and agents alike. **A pull request that does not follow +them will be declined** rather than fixed in review. + ### Scope (MUST) 1. One concern per PR. See scope discipline in `AGENTS.md`. diff --git a/plugins/corbits-skills/skills/implement/SKILL.md b/plugins/corbits-skills/skills/implement/SKILL.md index 6144d0bde..95fc97a55 100644 --- a/plugins/corbits-skills/skills/implement/SKILL.md +++ b/plugins/corbits-skills/skills/implement/SKILL.md @@ -113,7 +113,7 @@ Run `make` (or the project's equivalent full pipeline: format, lint, build, test Update `activeForm` to "Committing: {subject}". -Create the commit. Follow the commit message conventions from the `style` skill. Include the test in the same unit of work as the implementation — same commit when committing — one logical unit — and update the docs when the commit changes documented behavior. Worker-chain branch/PR convention: branch name carries the issue id, the PR body ends with `Fixes CL-…` and carries no AI-attribution lines (CONTRIBUTING: title stays a plain-English sentence, body is Summary/Verification). +Create the commit. Follow the commit message conventions from the `style` skill. Include the test in the same unit of work as the implementation — same commit when committing — one logical unit — and update the docs when the commit changes documented behavior. Worker-chain branch/PR convention: branch name carries the issue id, the PR body ends with `Fixes CL-…` and carries no AI-attribution lines (CONTRIBUTING: title is a Conventional Commits subject, body is Summary/Verification). ### Step 5: Critique Loop diff --git a/plugins/corbits-skills/skills/review/SKILL.md b/plugins/corbits-skills/skills/review/SKILL.md index 14cfa51dd..e241b34dc 100644 --- a/plugins/corbits-skills/skills/review/SKILL.md +++ b/plugins/corbits-skills/skills/review/SKILL.md @@ -257,7 +257,7 @@ git log ..HEAD --format='%s' Scan for: -- **Prefix violations.** Any subject starting with a `word:`, `[tag]`, or `(scope)` pattern. Includes Conventional Commits (`feat:`, `fix:`), component or scope prefixes (`Anthropic adapter:`, `mm:`, `[X86]`), ticket IDs (`INTR-79:`), and status tags (`WIP:`). Project convention is plain English sentences; any prefix is a violation regardless of how idiomatic it looks in other ecosystems. +- **Subject-form violations.** Project convention is Conventional Commits: `(): ` with type from `feat`, `fix`, `perf`, `refactor`, `test`, `docs`, `build`, `ci`, `chore`, `style`. Flag a subject with no type (`Anthropic adapter: handle 429s`, `Add retry logic`), an unrecognized type, a ticket ID (`INTR-79:`), or a status tag (`WIP:`, `[urgent]`). Do **not** flag `feat:`/`fix:`-style prefixes themselves — those are the convention. - **Filename or path references.** Tokens that look like file paths or extensions (`server.ts`, `INFERENCE.md`, `src/foo/bar.py`). The diff lists what changed; subjects describe the change, not the file. - **Trailing punctuation.** Subjects ending with `.`, `!`, or `?`. - **Vague subjects.** "Update code," "Fix bug," "Misc changes," "Address review." diff --git a/plugins/corbits-skills/skills/style/SKILL.md b/plugins/corbits-skills/skills/style/SKILL.md index 09317015b..c58484b67 100644 --- a/plugins/corbits-skills/skills/style/SKILL.md +++ b/plugins/corbits-skills/skills/style/SKILL.md @@ -104,37 +104,54 @@ git log origin/main --format='%s' | head -20 The existing commits document the project's actual subject convention — verb tense, level of detail, voice, capitalization. Match what is there. -The project's log can override the no-prefix rule below, but only when the recent history is **predominantly** prefixed in a single consistent convention — i.e., the prefix is the obvious shape of the last ~20 commits, not a minority pattern visible in a few. Mixed signals fall through to the no-prefix rule; tie goes to no prefix. +**Conventional Commits.** Summary lines follow +[Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/): -**No subject prefixes.** Summary lines are plain English sentences that start with a verb and describe the change directly. Do not prefix the subject with anything — no tag, no scope, no category, no ticket ID, no severity marker. This is a flat rule across every prefix convention, including: +```text +(): +``` + +- **Type** — one of `feat`, `fix`, `perf`, `refactor`, `test`, `docs`, `build`, + `ci`, `chore`, `style` +- **Scope** — the component the change lives in. Omit only when the change + genuinely spans the whole project +- **Description** — plain English, imperative, starts with a verb, describes + the change directly +- **Breaking changes** — `!` after the type/scope, or a `BREAKING CHANGE:` + footer + +Everything after the colon still obeys the rules below: no abbreviations, no +trailing punctuation, no filenames, self-contained. + +**Still banned as subject prefixes**, before or instead of the type: -- Conventional Commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:` -- Scope or component prefixes: `Anthropic adapter:`, `mm:`, `[X86]`, `drivers/net:`, `frontend:` - Ticket IDs: `INTR-79:`, `JIRA-1234:`, `#456:` - Status or severity tags: `WIP:`, `[urgent]`, `(security):` - -Several of these patterns are widespread in well-known projects (Linux kernel, LLVM, Conventional-Commits-adopting projects) and feel idiomatic from sheer exposure. They are still banned here. Familiarity is not a justification. +- Bare component prefixes with no type: `Anthropic adapter:`, `mm:`, `[X86]`, + `drivers/net:`, `frontend:` — the component belongs in the scope, so + `mm: fix leak` becomes `fix(mm): fix leak` Summary lines also use no abbreviations and do not end with punctuation. **Good examples:** ``` -Add retry logic for failed network requests -Fix race condition in transaction verification -Document API response format +feat(executor): add retry logic for failed network requests +fix(inference): resolve race condition in transaction verification +docs(api): document response format +perf(glob): stop rescanning ignored directories ``` **Bad examples:** ``` -feat: add retry logic (Conventional Commits prefix) -Anthropic adapter: handle 429s (component-scope prefix) +Add retry logic (no type or scope) +feat: add retry logic (no scope, and says nothing specific) +Anthropic adapter: handle 429s (bare component, no type) INTR-79: add retry logic (ticket-ID prefix) [WIP] refactor the parser (status tag) -Update code (too vague) -Fix bug in server.ts (filename in subject) -Document INFERENCE.md updates (filename in subject) +chore: update code (too vague) +fix: bug in server.ts (filename in subject) ``` **Self-contained:** diff --git a/src/agent/directors/builder/package.ts b/src/agent/directors/builder/package.ts index b4bfa9e88..75d5f8bbe 100644 --- a/src/agent/directors/builder/package.ts +++ b/src/agent/directors/builder/package.ts @@ -81,7 +81,7 @@ For implementation work, run the repository-defined typecheck command and releva **Don't shortcut verify.** The value is in the discipline. Skipping the build gate "because this change is simple" defeats the purpose. -**Keep units focused.** Deliver a working tree that satisfies the brief and report. Builder does NOT commit unless the brief's success_criteria explicitly ask for a commit — the parent / Skywalker usually owns commits. Prefer: working tree + report envelope. Worker-chain branch/PR convention for the parent's handoff: branch name carries the issue id, the PR body ends with \`Fixes CL-…\` and carries no AI-attribution lines (CONTRIBUTING: title stays a plain-English sentence, body is Summary/Verification only). +**Keep units focused.** Deliver a working tree that satisfies the brief and report. Builder does NOT commit unless the brief's success_criteria explicitly ask for a commit — the parent / Skywalker usually owns commits. Prefer: working tree + report envelope. Worker-chain branch/PR convention for the parent's handoff: branch name carries the issue id, the PR body ends with \`Fixes CL-…\` and carries no AI-attribution lines (CONTRIBUTING: title is a Conventional Commits subject, body is Summary/Verification only). **Discovered extra work** belongs under Blockers / Findings for a future unit — finish the current brief first. From c7ceaa25a0d13a16e163146c44ffe632e8b13349 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 13 Sep 2026 12:03:44 -0700 Subject: [PATCH 3/3] Name the real product in release forms and drop notes commits --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3629bca0a..448caeccf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,8 +63,8 @@ This repo's history used a bare `component: description` prefix (`executor:`, `nameref:`). New commits keep the component as the **scope** and lead with the type: `executor: add retry` becomes `feat(executor): add retry`. -Releases use `chore(release): perfi X.Y.Z`. Release notes use -`docs(release): add perfi X.Y.Z release notes`. +Releases use `chore(release): corbits X.Y.Z`. Release notes are generated +from merged pull requests, so there are no hand-written notes commits. **Still banned in the subject:**