Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/agents/content/_partials/voice-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Before saving, audit your draft against the two rules below. (See [`lede-voice.md`](../_data/lede-voice.md) for full doctrine and worked examples.)

**Rule 1 — Each sentence must be one of:**

- **Outcome** — what the reader will experience, see, or be able to do.
- **Stated invariant** — explicit assurance worth confirming ("Behavior is unchanged."; "No migration required.").
- **Migration info** — names of user-facing surface added, removed, or renamed; steps the reader must take.

Cut any sentence describing mechanism (internal data, code paths, refactor mechanics, output-format details, internal counts). Indirect outcomes (reliability, performance) are permitted only if specific.

**Rule 2 — Identifiers permitted only for top-level user-configurable surface:**

- Allowed: package names, CLI commands and flags, top-level config-file paths, public-API endpoints/methods.
- Banned: schema field names, default values for configurable names, internal file paths, function/type/class/module names, internal subsystem names, internal version markers, output-format details (JSON keys, marker glyphs, header strings).
- Exception: user-facing surface that has been added, removed, or renamed may be named for migration (both removed identifiers and new defaults).

**Audit before save:**

1. For each sentence, classify it: Outcome / Invariant / Migration. Rewrite anything that fits none.
2. For each identifier, confirm it's in the allowed list. Drop or rephrase anything that isn't.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Release-notes voice
# Lede voice

This file defines the voice for changelog and release-notes entries. The same voice may be adopted by any artifact whose first paragraph is the entry point for a glancing reader. Currently used by:
This file defines the voice for changelog and release-notes entries — and any other artifact whose first paragraph is the entry point for a glancing reader. The metaphor is journalism: the entry is the lede; the PR is the article. Currently used by:

- `summarize-change/SKILL.md` for the `## What` section
- `commit/SKILL.md` for the commit body
- `commit/SKILL.md` for the commit title and body
- `merge-pr/SKILL.md` for the merge commit body

## The reader

Expand Down Expand Up @@ -68,6 +69,22 @@ Banned:

When in doubt, leave the name out and describe the behavior. The one exception: User-configurable surface needed for migration may be named — both _removed_ identifiers (so the user recognizes what's gone) and _new_ defaults (so the user knows where to find or move them). Examples: "The `--fix-low` flag is replaced by `--approval-threshold`."; "The default config file is now `.config/v11y-check.config.json`."

## Title application

A title is a single-sentence lede. Both rules apply, distilled:

- **Outcome, not mechanism.** The title goes to changelogs and release notes — readers often see only the title. Ask "what does this change deliver?", not "what did I edit?". Bad: "Upgrade hono from v1 to v2." Good: "Upgrade hono to patch authentication vulnerability."
- **The code change, not what prompted it.** Ask "what does the diff do?", not "why did I open the editor?". Bad: "Address review findings"; "Apply feedback"; "Incorporate suggestions". Good: "Add error logging to `handleStateUpdate`"; "Remove dead rejection handler".
- **No ephemeral references.** If it would not make sense to a reader who has only `git log`, leave it out.
- **Only what's in the diff.** External actions (updating a ticket, posting a comment, sending a notification) are not part of the change and do not belong in its title.

## Body content discipline

Body text — commit bodies, `## What`, merge-commit bodies — has two specific exclusions on top of the two rules:

- **Never reference automated tests or CI.** Formatting, linting, typechecking, and unit tests run automatically. Mentioning them in the body is process noise, not user content.
- **Never use review finding IDs.** Identifiers like `F1`, `W2`, `T3` belong only in review documents — they are meaningless in `git log` and to any future reader.

## Length

The entry is as long as needed to convey outcomes, invariants, and migration info — and not one word longer.
Expand Down Expand Up @@ -141,3 +158,15 @@ Cut: the mechanism clause ("Each CLI now reads its version directly from its `pa
> Below-threshold vulnerabilities are now surfaced in `check` output instead of silently hidden, so users can see what their configured threshold is filtering out. Exit code behavior is unchanged.

Cut: marker glyph, "ignored" annotation, JSON field name, scope-header format string, branch in the "no vulnerabilities" message. Survives: outcome (visibility) plus invariant (exit codes unchanged).

### Bad → Good — format/glyph adoption

**Bad** (output-format details under Rule 2):

> Adds work-type emojis to PR descriptions: `## Details` subsections now render as `### 🎉 Features`, `### 🐛 Bug fixes`, `### ♻️ Refactoring`, `### 🧪 Tests`, and `### 📦 Dependencies`. Breaking changes in any subsection get a `🚨 **Breaking:**` prefix on the entry's first line.

**Good:**

> Adds work-type emojis to PR-description section headings for at-a-glance scanning, and an inline marker on entries that introduce breaking changes.

Cut: every specific emoji, every header string, every marker glyph — all output-format details. Survives: the outcome (scanability) and the fact that breaking changes carry a marker, without naming the marker. The trap with this pattern is the urge to enumerate the new visuals because they feel like the user-facing change; they aren't — the user-facing change is "things are easier to scan."
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# Git commit format

## Commit title format
# Title templates

Commit titles, ticket titles, PR titles, and squash-merge titles are produced from declarative templates. Each surface has its own template, configured per repository and per user, and rendered by `describe-change.sh` from a small set of named tokens.

### Rendering the title
## Rendering the title

Run the `describe-change.sh` script with every input that is available; templates control which tokens are required:

Expand Down Expand Up @@ -34,7 +32,7 @@ Use `commit_title` for commit titles, `ticket_title` for issue titles, `pr_title

If the script is not found, fall back to the bare `--title` value.

### Supported tokens
## Supported tokens

| Token | Resolves to |
| -------------- | ------------------------------------------------------------------------------------- |
Expand All @@ -46,13 +44,13 @@ If the script is not found, fall back to the bare `--title` value.

A template that omits `{title}` produces a title without the bare title text — `describe-change.sh` does not insert it implicitly. Unknown tokens (e.g., a typo like `{titel}`) are left as-is so the mistake is visible in the rendered output.

### Optional groups via `[...]`
## Optional groups via `[...]`

A `[...]` group renders verbatim if every token reference inside resolves non-empty. If any inner token is empty, the entire group — literals included — drops. Groups are processed left-to-right and may not be nested.

After substitution, a final whitespace pass collapses runs of multiple spaces into a single space and trims leading and trailing whitespace.

#### Worked example
### Worked example

Template: `[{ticket_ref} ][{scope}|{type}: ]{title}[ (#{pr_number})]`

Expand All @@ -66,7 +64,7 @@ Template: `[{ticket_ref} ][{scope}|{type}: ]{title}[ (#{pr_number})]`

The whitespace-collapse pass turns ` Add foo ` into `Add foo` and prevents extra spaces from showing up next to dropped groups (e.g., `[{ticket_ref}] {title} [{pr_number}]` with only `{title}` populated renders as `Add foo`, not ` Add foo `).

### Common templates
## Common templates

```yaml
commit:
Expand All @@ -90,47 +88,12 @@ Quote `title_format` values in YAML (single or double quotes are both fine). Quo
| `'[{ticket_ref} ]{title}'` | `#466 Add script installer` |
| `'{title} ({ticket_ref})'` | `Add script installer (#466)` |

### Scope
## Scope values

The scope identifies the part of the codebase affected by the commit:
The `{scope}` token expects a value that identifies the part of the codebase affected. Surface-defined values:

- In a monorepo, the scope is typically the workspace name or abbreviation.
- Use `root` if the commit touches only files in the monorepo root.
- Use `*` if the commit spans multiple workspaces, or root and one or more workspaces.
- If a root change is tightly associated with only one workspace, don't count it as a root change.

Common example: if a package is added to `packages/workspace-a`, that updates the package lock file in root. Don't treat that as a change to root.

## Title constraints

- **72 characters max** (hard limit).
- **Describes the code change, not what prompted it.** Ask: "what does the diff do?" Bad: "Address review findings". Good: "Add error logging to `handleStateUpdate`".
- **Describes the outcome, not the mechanism.** The title feeds the changelog and, for release-notes-contributing work types, the release notes — a reader scanning those sees only the title. Ask: "what does this change deliver?" — not "what did I edit?" Bad: "Upgrade hono from v1 to v2". Good: "Upgrade hono to patch authentication vulnerability".
- **No ephemeral references.** If it won't make sense to a reader who has only `git log`, leave it out.
- **Only document what's in the diff.** External actions (e.g., updating a ticket) don't belong.

Add `!` after the work type to indicate breaking changes: `agents|feat!: Remove deprecated API`

## Ticket ID

Do not include the ticket ID in the commit title. The branch name already carries it.

Include the ticket ID at the end of the commit body only if the branch covers more than one ticket (rare).

## Line length

- **Title**: 72 characters max (hard limit).
- **Body**: No hard wrapping. Write naturally — do not insert newlines to wrap at a column width.

## Body formatting

- **Release-notes voice.** The body is extracted into the changelog and (for release-notes-contributing work types) into release notes. Apply the [release-notes voice](release-notes-voice.md).
- **Punctuate list items.** Each bulleted item ends with a period, comma, or semicolon.
- **Use backticks for code identifiers.** Variable names, function names, class names, and file paths must be wrapped in backticks — e.g., `handleStateUpdate`, `AgentActor`, `src/lib/manifest.ts`.
- **Never reference automated tests or CI.** Do not mention formatting, linting, unit tests, or typechecking as part of what the commit does.
- **Never use review finding IDs.** Identifiers like F1, W2, T3 belong only in review documents — they are meaningless in `git log`.
- **Break up large paragraphs.** Use a blank line between paragraphs. Prefer short, focused paragraphs over walls of text.

## Branch naming
- Use `root` when the change touches only files at the monorepo root.
- Use `*` when the change spans multiple workspaces, or root and one or more workspaces.

See `branch-format.md` for branch naming conventions. Branch format: `{ticket}/{description}`.
Per-surface guidance on when to apply each value (e.g., what to count as `root` for a commit) lives with the consuming skill — see `commit/SKILL.md` for the commit-side rules.
43 changes: 19 additions & 24 deletions packages/agents/content/skills/commit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,41 @@ user-invocable: true

Commit titles and bodies are extracted into the changelog and, for release-notes-contributing work types, into release notes. Commit bodies also feed the PR's `## What` section via the `summarize-change` skill. Write with those downstream surfaces in mind.

## Commit message format

See `../_data/commit-format.md` for the full specification, including how to render the commit title using `describe-change.sh`.

## Commit metadata

- `WORK_TYPE` describes the category of work (see `../_data/work-types.json`)
- `WORK_TYPE` describes the category of work (see `../_data/work-types.json`).

## Ticket ID
## Commit title

Do not include the ticket ID in the commit title. The branch name carries it. Include it at the end of the commit body only if the branch covers more than one ticket (rare).
Render via `describe-change.sh` — see `../_data/title-templates.md` for the full template syntax, supported tokens, and rendering pipeline.

## Line length
- **72 characters max** (hard limit).
- **Imperative, task-oriented voice.** "Add…", "Fix…", "Prevent…", "Enable…" — describing what the coder did. The title appears next to the PR number in release notes; it reads as the task. Distinct from the lede voice, which is declarative ("Adds…", "Fixes…").
- For content discipline (outcome-not-mechanism, code-change-not-prompt, no ephemeral references, only what's in the diff), see the "Title application" section in [`lede-voice.md`](../_data/lede-voice.md).
- Mark breaking changes by appending `!` to the work type: `agents|feat!: Remove deprecated API`. See [Breaking changes](#breaking-changes) below for which types are eligible.

- **Title**: 72 characters max (hard limit).
- **Body**: No hard wrapping. Write naturally — do not insert newlines to wrap at a column width.
## Ticket ID

## Title guidelines
Do not include the ticket ID in the commit title. The branch name carries it. Include it at the end of the commit body only if the branch covers more than one ticket (rare).

- **Imperative, task-oriented voice.** "Add…", "Fix…", "Prevent…", "Enable…" — describing what the coder did. The title appears next to the PR number in release notes; it reads as the task. This voice is distinct from the body voice, which is declarative (see below).
- **Describes the code change, not what prompted it.** Ask: "what does the diff do?" — not "why did I open the editor?" Bad: "Address review findings", "Apply feedback", "Incorporate suggestions". Good: "Add error logging to handleStateUpdate", "Remove dead rejection handler".
- **Describes the outcome, not the mechanism.** The title feeds the changelog and, for release-notes-contributing work types, the release notes — a reader scanning those sees only the title. Ask: "what does this change deliver?" — not "what did I edit?" Bad: "Upgrade hono from v1 to v2". Good: "Upgrade hono to patch authentication vulnerability".
- **No ephemeral references.** If it won't make sense to a reader who has only `git log`, leave it out.
- **Only document what's in the diff.** External actions (e.g., updating a ticket) are not part of the commit and don't belong in its message.
## Commit body

## Body guidelines
**Body voice.** The commit body feeds the changelog, release notes (for release-notes-contributing types), and the PR's `## What` section. The first paragraph of the body must stand alone as the entry; subsequent paragraphs may elaborate for the engaged reader who has clicked through.

**Voice: release-notes voice.** The commit body feeds the changelog, release notes (for release-notes-contributing types), and the PR's `## What` section. Apply the [release-notes voice](../_data/release-notes-voice.md): The per-sentence outcome test and the identifier ban. The first paragraph of the body must stand alone as the changelog/release-notes entry; subsequent paragraphs may elaborate for the engaged reader who has clicked through.
<!-- include: ../../_partials/voice-checklist.md / -->

**No hard line breaks.** Write each paragraph or list item as a single long line. Do not insert newlines to wrap at a column width. Every tool that renders commit messages handles wrapping; manual breaks produce ragged text.
### Body mechanics

See `../_data/commit-format.md` for mechanical body formatting rules (punctuation, backtick formatting, paragraph structure, and what to omit).
- **No hard line breaks.** Write each paragraph or list item as a single long line. Do not insert newlines to wrap at a column width. Every tool that renders commit messages handles wrapping; manual breaks produce ragged text.
- **Punctuate list items.** Each bulleted item ends with a period, comma, or semicolon.
- **Use backticks for code identifiers.** Variable names, function names, class names, and file paths must be wrapped in backticks — e.g., `handleStateUpdate`, `AgentActor`, `src/lib/manifest.ts`.
- **Break up large paragraphs.** Use a blank line between paragraphs. Prefer short, focused paragraphs over walls of text.

## Changes touching multiple scopes

- Use `root` if commit touches only files in monorepo root
- Use `*` if commit comprises changes to multiple scopes, or root and one or more scopes
- If a root change is tightly associated with only one scope, don't count it as a root change
For the structural scope values (`root`, `*`, workspace name), see "Scope values" in [`../_data/title-templates.md`](../_data/title-templates.md).

Common example: If a package is added to `packages/workspace-a`, that updates the package lock file in root. Don't treat that as a change to root.
Commit-side application: when more than one scope-value would technically apply, use the closest fit. If a root change is tightly associated with only one workspace, count it as a workspace change rather than a root change. Common example: if a package is added to `packages/workspace-a`, that updates the package lock file in root — still treat the commit as a workspace change.

## Branch naming

Expand Down
2 changes: 1 addition & 1 deletion packages/agents/content/skills/condense-branch/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Use `summarize-change` to compose a good commit message. Save the description pe

## Commit format

Follow [commit-format.md](../_data/commit-format.md). Use `describe-change.sh` to render the full commit title:
Follow the [commit conventions](../commit/SKILL.md). Use `describe-change.sh` to render the full commit title (see [title-templates.md](../_data/title-templates.md) for syntax):

```bash
json=$({platform_home_dir}/scripts/describe-change.sh \
Expand Down
2 changes: 1 addition & 1 deletion packages/agents/content/skills/create-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Use `pr_title` directly as the final PR title. Do not concatenate with `title` s

If the script is not found, fall back to the bare `title` from the change summary.

See [commit-format.md](../_data/commit-format.md) for the title-format model and supported tokens.
See [title-templates.md](../_data/title-templates.md) for the title-format model and supported tokens.

### 7. Resolve labels

Expand Down
4 changes: 3 additions & 1 deletion packages/agents/content/skills/merge-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ git log {default_branch}..HEAD --format=%B
git diff {default_branch}...HEAD --stat
```

Write the composed body in **release-notes voice** — apply the [release-notes voice](../_data/release-notes-voice.md). Describe the accomplishment from the reader's standpoint, not the edits. One short paragraph is usually enough; add a follow-up paragraph only when the change is substantial.
Describe the accomplishment from the reader's standpoint. One short paragraph is usually enough; add a follow-up paragraph only when the change is substantial.

<!-- include: ../../_partials/voice-checklist.md / -->

### 7. Approval gate

Expand Down
4 changes: 3 additions & 1 deletion packages/agents/content/skills/summarize-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ Generated by: {Agent name} (model: {model})

## What

{This section becomes a changelog entry — and, for public-tier work (per [work-types.json](../_data/work-types.json)), a release note. Apply the [release-notes voice](../_data/release-notes-voice.md): The per-sentence outcome test and the identifier ban. Mechanism, internal naming, and refactor mechanics belong in `## Details`.}
{This section becomes a changelog entry — and, for public-tier work (per [work-types.json](../_data/work-types.json)), a release note. Mechanism, internal naming, and refactor mechanics belong in `## Details`.}

<!-- include: ../../_partials/voice-checklist.md / -->

## Why

Expand Down
Loading
Loading