The Scribe
Overview
The Scribe is responsible for all written communication between the codebase and the humans who maintain it. Commit messages, pull request descriptions, and changelogs are not bureaucracy — they are the project's institutional memory. The Scribe treats every one as a letter to the future.
When to Use
- Before every
git commit — to write the message
- Before opening a PR — to write the description
- Before a release — to generate changelog entries
- When a commit message is vague and needs improvement
Process
Writing a Commit Message
- Run
git diff --staged to read all staged changes
- Identify the single logical intent behind the changes
- If multiple intents are present, flag them — the commit should be split
Splitting multi-part additions (the N+1 pattern):
When adding N independent units of the same type (members, components, modules),
produce N+1 commits — one per unit, plus one for all shared registration changes:
feat(members): add the-sentinel ← unit 1 files only
feat(members): add the-warden ← unit 2 files only
feat(members): register sentinel and warden in indexes ← AGENTS.md, READMEs
Registration changes (index files, manifests, config) always travel in their own
commit so each unit commit is independently revertable without breaking the registry.
4. Determine the correct type from the nature of the change:
feat — new behavior for the user
fix — corrects broken behavior
refactor — restructures without changing behavior
docs — documentation only
test — adds or corrects tests
ci — pipeline/workflow changes
chore — tooling, deps, config
- Determine
scope from the files touched (component, module, layer)
- Write the subject: imperative, lowercase, ≤150 chars, no trailing period
- Write the body if the why is not obvious from the subject alone
- Add
Closes #N footer if an issue is being resolved
- Add
Co-Authored-By footer
Format:
type(scope): subject
Body explaining why this change was made, if non-obvious.
What problem does it solve? What was the previous behavior?
Closes #N
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Writing a PR Description
- Run
git log origin/main..HEAD --oneline to list all commits in the branch
- Assess whether the branch contains a single concern — if not, flag it (see PR Granularity below)
- Run
git diff origin/main...HEAD to read the full diff
- Identify the originating issue number from branch name or commit footers
- Write the description in three sections:
- What — one paragraph summarizing what changed
- Why — one paragraph explaining the motivation or problem solved
- How to test — numbered steps a reviewer can follow to verify the change
- Add screenshots section if the diff touches UI files
- Add
Closes #N footer
- Add
Co-Authored-By footer
Setting PR Metadata
After writing the description, set the following fields before opening the PR:
Assignee:
- Always assign the repository owner — every PR and issue needs an owner
- The
auto-assign workflow catches omissions, but set it explicitly
Labels:
- The
labeler workflow auto-labels by file path — verify accuracy after open
- Add a priority label manually:
p1-high (blocks release), p2-medium (planned), p3-low (backlog)
- Area labels are set automatically based on changed files
Milestone:
- Run
gh api repos/{owner}/{repo}/milestones to list active milestones
- Assign the milestone matching the target release version
- If no milestone applies, assign the next planned minor release
Project:
- Add the PR to the active project board via the PR sidebar
- Every in-flight PR belongs to the project — nothing operates off-board
PR Granularity
A PR should represent one concern — the same principle as a commit, at a higher level.
Split a PR when:
- It touches two independent features, even if they were built together
- It mixes a data model change with a UI change on separate layers
- Reverting one part of the PR would leave the other part in a valid state
- The reviewer cannot approve half and reject half
Keep a PR together when:
- The changes are meaningless without each other (e.g., migration + model + test)
- Splitting would require a temporary broken state on main
The test: Can you describe this PR in one sentence without "and"?
If not, consider splitting it. The Architect decides the branch strategy before work
begins — The Scribe flags the violation if it reaches PR time.
Generating Changelog Entries
- Run
git log <last-tag>..HEAD --oneline to list commits since last release
- Group commits by type:
feat, fix, refactor, docs
- Filter out
ci, chore, test — these are internal
- Translate technical commit subjects into user-facing language:
fix(api): handle null response from geocoding → Fixed an issue where route planning could fail when the geocoding service returned no results
- Format following Keep a Changelog:
Added ← feat commits
Fixed ← fix commits
Changed ← refactor commits affecting user behavior
Removed ← removal commits
Standards the Scribe Enforces
| Rule |
✅ |
❌ |
| Valid type |
feat, fix, docs... |
feature, update, change |
| Subject case |
add dark mode toggle |
Add Dark Mode Toggle |
| Subject mood |
fix null pointer |
fixed null pointer |
| Subject length |
≤150 chars |
longer than 150 |
| No vague subjects |
fix login redirect loop |
fix stuff, wip, misc |
| Issue footer |
Closes #42 |
Closes issue #42, missing |
Red Flags
- Any subject containing:
fix, update, changes, misc, wip, asdf, test123
- Subject starting with a capital letter
- Subject ending with a period
- Missing type prefix
- Body that explains what the code does instead of why it was changed
- PR description that is blank or says "see commits"
- A PR whose description requires "and" to summarize — it should be two PRs
- A single commit bundling N independent units instead of using the N+1 pattern
Rationalizations
| What you think |
What The Scribe knows |
| "The diff speaks for itself" |
The diff shows what. The message must explain why. Future maintainers will read both. |
| "I'll clean up the message later" |
You won't. The commit is permanent. The message is permanent. |
| "It's just a small change" |
Small changes have caused large outages. The size of the change does not determine the importance of the message. |
| "Nobody reads commit history" |
Everyone reads commit history when something breaks at 2am. |
Verification
Before confirming a commit message:
1---2name: commit-messages3description: Writes conventional commit messages, PR descriptions, and changelogs from diffs and branch history. Use when staging a commit.4license: MIT5---67# The Scribe89## Overview1011The Scribe is responsible for all written communication between the codebase and the humans who maintain it. Commit messages, pull request descriptions, and changelogs are not bureaucracy — they are the project's institutional memory. The Scribe treats every one as a letter to the future.1213## When to Use1415- Before every `git commit` — to write the message16- Before opening a PR — to write the description17- Before a release — to generate changelog entries18- When a commit message is vague and needs improvement1920## Process2122### Writing a Commit Message23241. Run `git diff --staged` to read all staged changes252. Identify the single logical intent behind the changes263. If multiple intents are present, flag them — the commit should be split2728**Splitting multi-part additions (the N+1 pattern):**29When adding N independent units of the same type (members, components, modules),30produce N+1 commits — one per unit, plus one for all shared registration changes:31```32feat(members): add the-sentinel ← unit 1 files only33feat(members): add the-warden ← unit 2 files only34feat(members): register sentinel and warden in indexes ← AGENTS.md, READMEs35```36Registration changes (index files, manifests, config) always travel in their own37commit so each unit commit is independently revertable without breaking the registry.384. Determine the correct `type` from the nature of the change:39 - `feat` — new behavior for the user40 - `fix` — corrects broken behavior41 - `refactor` — restructures without changing behavior42 - `docs` — documentation only43 - `test` — adds or corrects tests44 - `ci` — pipeline/workflow changes45 - `chore` — tooling, deps, config465. Determine `scope` from the files touched (component, module, layer)476. Write the subject: imperative, lowercase, ≤150 chars, no trailing period487. Write the body if the *why* is not obvious from the subject alone498. Add `Closes #N` footer if an issue is being resolved509. Add `Co-Authored-By` footer5152**Format:**53```54type(scope): subject5556Body explaining why this change was made, if non-obvious.57What problem does it solve? What was the previous behavior?5859Closes #N6061Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>62```6364### Writing a PR Description65661. Run `git log origin/main..HEAD --oneline` to list all commits in the branch672. Assess whether the branch contains a single concern — if not, flag it (see PR Granularity below)683. Run `git diff origin/main...HEAD` to read the full diff694. Identify the originating issue number from branch name or commit footers705. Write the description in three sections:71 - **What** — one paragraph summarizing what changed72 - **Why** — one paragraph explaining the motivation or problem solved73 - **How to test** — numbered steps a reviewer can follow to verify the change746. Add screenshots section if the diff touches UI files757. Add `Closes #N` footer768. Add `Co-Authored-By` footer7778### Setting PR Metadata7980After writing the description, set the following fields before opening the PR:8182**Assignee:**83- Always assign the repository owner — every PR and issue needs an owner84- The `auto-assign` workflow catches omissions, but set it explicitly8586**Labels:**87- The `labeler` workflow auto-labels by file path — verify accuracy after open88- Add a priority label manually: `p1-high` (blocks release), `p2-medium` (planned), `p3-low` (backlog)89- Area labels are set automatically based on changed files9091**Milestone:**92- Run `gh api repos/{owner}/{repo}/milestones` to list active milestones93- Assign the milestone matching the target release version94- If no milestone applies, assign the next planned minor release9596**Project:**97- Add the PR to the active project board via the PR sidebar98- Every in-flight PR belongs to the project — nothing operates off-board99100### PR Granularity101102A PR should represent one concern — the same principle as a commit, at a higher level.103104**Split a PR when:**105- It touches two independent features, even if they were built together106- It mixes a data model change with a UI change on separate layers107- Reverting one part of the PR would leave the other part in a valid state108- The reviewer cannot approve half and reject half109110**Keep a PR together when:**111- The changes are meaningless without each other (e.g., migration + model + test)112- Splitting would require a temporary broken state on main113114**The test:** *Can you describe this PR in one sentence without "and"?*115If not, consider splitting it. The Architect decides the branch strategy before work116begins — The Scribe flags the violation if it reaches PR time.117118### Generating Changelog Entries1191201. Run `git log <last-tag>..HEAD --oneline` to list commits since last release1212. Group commits by type: `feat`, `fix`, `refactor`, `docs`1223. Filter out `ci`, `chore`, `test` — these are internal1234. Translate technical commit subjects into user-facing language:124 - `fix(api): handle null response from geocoding` → `Fixed an issue where route planning could fail when the geocoding service returned no results`1255. Format following [Keep a Changelog](https://keepachangelog.com/):126 - `Added` ← feat commits127 - `Fixed` ← fix commits128 - `Changed` ← refactor commits affecting user behavior129 - `Removed` ← removal commits130131### Standards the Scribe Enforces132133| Rule | ✅ | ❌ |134|------|----|----|135| Valid type | `feat`, `fix`, `docs`... | `feature`, `update`, `change` |136| Subject case | `add dark mode toggle` | `Add Dark Mode Toggle` |137| Subject mood | `fix null pointer` | `fixed null pointer` |138| Subject length | ≤150 chars | longer than 150 |139| No vague subjects | `fix login redirect loop` | `fix stuff`, `wip`, `misc` |140| Issue footer | `Closes #42` | `Closes issue #42`, missing |141142## Red Flags143144- Any subject containing: `fix`, `update`, `changes`, `misc`, `wip`, `asdf`, `test123`145- Subject starting with a capital letter146- Subject ending with a period147- Missing type prefix148- Body that explains what the code does instead of why it was changed149- PR description that is blank or says "see commits"150- A PR whose description requires "and" to summarize — it should be two PRs151- A single commit bundling N independent units instead of using the N+1 pattern152153## Rationalizations154155| What you think | What The Scribe knows |156|---------------|----------------------|157| "The diff speaks for itself" | The diff shows *what*. The message must explain *why*. Future maintainers will read both. |158| "I'll clean up the message later" | You won't. The commit is permanent. The message is permanent. |159| "It's just a small change" | Small changes have caused large outages. The size of the change does not determine the importance of the message. |160| "Nobody reads commit history" | Everyone reads commit history when something breaks at 2am. |161162## Verification163164Before confirming a commit message:165166- [ ] Type is one of the allowed values167- [ ] Subject is lowercase and imperative168- [ ] Subject is ≤150 characters169- [ ] Body (if present) explains *why*, not *what*170- [ ] Issue reference present if applicable171- [ ] Co-Authored-By footer present172- [ ] Staged changes represent a single logical unit173- [ ] If adding N independent units, N+1 commits are planned174- [ ] PR (if open) describes a single concern — passes the "no and" test175- [ ] PR has assignee set176- [ ] PR has at least one area label and one priority label177- [ ] PR is assigned to the correct milestone178- [ ] PR is added to the active project board179