Skip to content

Restructure voice and format rules to enforce inline at point-of-use #555

Description

@williamthorsen

Problem

The release-notes voice (release-notes-voice.md) is the SSOT for how summarize-change's ## What, commit bodies, and merge-pr merge bodies must read, but agents routinely produce content that violates the identifier ban — particularly when describing user-visible glyph or label changes, where the urge to enumerate the new visuals is strong. The skill templates reference the voice rules by link rather than inlining them at the point of use, and no audit step gates output before saving.

Two structural problems amplify the failure:

  1. Voice rules are referenced, not enforced. Agents do not reliably navigate forward references to release-notes-voice.md at the moment of drafting. The link sits one click away, and that distance is enough for the rules to be skipped.
  2. commit-format.md mixes content rules with mechanical formatting rules. It carries title-content rules ("describes the outcome, not the mechanism"), body-content rules ("never reference automated tests or CI"), commit-structure rules (scope, ticket-ID placement), and genuinely mechanical rules (templates, line length, backticks). The mixed scope makes the file an unstable home for either category and invites future contributors to add content rules in the wrong place.

Concrete recent failure: PR #554's initial ## What enumerated specific section emojis and the 🚨 **Breaking:** glyph — three violations of the "output-format details" ban — even though the rules existed and were known to the drafting agent. Manual reviewer escalation caught it; that cost cannot scale per PR across eight repos.

Context

  • The voice rules are two tests: per-sentence outcome (every sentence describes outcome / stated invariant / migration info) and identifier ban (no internal subsystem names, file paths, function/type names, or output-format details — including marker glyphs and header strings; user-facing surface and renamed identifiers may be named for migration).
  • The shared partials mechanism landed in Support partials in skills and subagent definitions #522 (PR #522 Support partials in skills and subagent definitions #571). Source files in _partials/ are inlined into consuming files at install time; the assembled output is what the agent reads. CI verifies the assembled output stays in sync with the partial source.
  • Voice content is authored in three skills today: summarize-change/SKILL.md (## What), commit/SKILL.md (commit body), and merge-pr/SKILL.md (merge commit body). The same failure mode applies in all three.
  • The current release-notes-voice.md name undersells the actual scope — the voice governs commit titles, commit bodies, change-summary ## What, and merge-commit bodies. The file's own metaphor ("the entry is the lede; the PR is the article") suggests lede-voice.md as a self-reinforcing name.

Solution

Three concurrent changes, all enabled by the partials mechanism:

1. Rename and expand release-notes-voice.mdlede-voice.md

  • Rename to reflect the broader scope (titles, commit bodies, change summaries, merge bodies).
  • Add a "Title application" subsection capturing the title-content rules that currently live in commit-format.md (outcome-not-mechanism, no ephemeral references, no external-action references).
  • Add a "Body content discipline" subsection capturing the body-content rules that currently live in commit-format.md (never reference automated tests/CI, never use review finding IDs).
  • Add a Bad/Good worked example for the format/glyph adoption pattern (the #548 Add work-type emojis and breaking tag to PR descriptions #554 failure pattern), demonstrating the trap (enumeration of emojis/headers) and the correct outcome-only framing.

2. Create the voice checklist as a partial

  • New file: packages/agents/content/_partials/voice-checklist.md (cross-cutting location).
  • Contents: distilled actionable checklist — the three Rule 1 categories, the Rule 2 banned/allowed identifier lists with the migration-naming exception, and the audit step itself ("classify each sentence; verify each identifier; rewrite or drop what fails").
  • Footer pointer to lede-voice.md for full doctrine and worked examples.
  • No <!-- children --> slot — the partial is identical at every consumption site.

3. Inline the partial at every voice-authoring point

  • summarize-change/SKILL.md: replace the link-out paragraph in the ## What template placeholder with <!-- include: ../../_partials/voice-checklist.md / -->.
  • commit/SKILL.md: replace the link-out paragraph in the body-guidelines section with the same include.
  • merge-pr/SKILL.md: replace the link-out paragraph at the merge-body composition step with the same include.

4. Split commit-format.md

  • Create _data/title-templates.md (~70 lines): title-template rendering, tokens, optional groups, describe-change.sh, worked examples, common templates. Cross-cutting (consumed by commits, PRs, ticket titles, merge titles).
  • Fold the commit-specific remnants — scope rules (root vs *), ticket-ID placement, line length, body formatting mechanics (punctuation, backticks, paragraph breaks) — into commit/SKILL.md.
  • Delete commit-format.md.
  • Update all references in skills, subagents, and partials to point to the new locations.

Considerations

  • The fix must work without per-PR human escalation. Voice violations in ## What propagate to changelogs and release notes, where they are harder to correct after the fact.
  • Inlining the voice rules verbatim everywhere would duplicate the SSOT; the partials mechanism resolves this — one source, install-time inlining, CI sync check.
  • The audit step is concrete (per-sentence, named rules) rather than free-form "review your work" — agents follow explicit checklists more reliably than abstract self-review.
  • Out of scope, noted for follow-up: a programmatic linter on ## What and commit bodies could mechanically enforce part of this (flag enumeration of marker glyphs or known internal-subsystem strings). Higher infrastructure cost; deferred.
  • Out of scope: extracting other duplicated rules into partials (work-type tier ordering, breaking-marker convention, etc.). Each is a separate ticket.

Acceptance criteria

  • _data/release-notes-voice.md is renamed to _data/lede-voice.md.
  • lede-voice.md includes a "Title application" subsection containing the title-content rules previously in commit-format.md.
  • lede-voice.md includes a "Body content discipline" subsection containing the body-content rules previously in commit-format.md.
  • lede-voice.md includes a Bad/Good example pair for the format/glyph adoption pattern.
  • packages/agents/content/_partials/voice-checklist.md exists, with the distilled checklist + audit step + footer pointer to lede-voice.md.
  • summarize-change/SKILL.md, commit/SKILL.md, and merge-pr/SKILL.md each include the partial at the point where voice content is authored, replacing the previous link-out paragraph.
  • After install, the partial appears verbatim in each consuming skill's installed output, with the audit step adjacent to the authoring step.
  • _data/title-templates.md exists, containing the title-template rendering content extracted from commit-format.md.
  • commit/SKILL.md absorbs the commit-specific remnants from commit-format.md (scope rules, ticket-ID placement, line length, body formatting mechanics).
  • _data/commit-format.md is deleted.
  • All references to release-notes-voice.md and commit-format.md across the skills, subagents, and partials trees are updated to point to lede-voice.md and the new locations (title-templates.md or commit/SKILL.md as appropriate).
  • The partials sync CI check passes against the new partial.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureAdded or improved external functionalityscope:agents

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions