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
213 changes: 213 additions & 0 deletions .agents/skills/comment-and-doc-style/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
name: comment-and-doc-style
description: >-
Governs prose, comment, Markdown, character-set, line-ending, and PR-title/commit-message
conventions for every ptr727/ProjectTemplate fleet repo. Use this whenever writing or editing a
code comment, workflow comment, Markdown doc, commit message, or PR title, whenever choosing
which characters to type in agent-authored text, whenever the file being edited is CRLF, and
whenever naming a tool in prose or docs. Triggers even when the task looks purely mechanical,
such as "just fix a typo" or "add a one-line comment", because the fleet's ASCII character-set
tiers, no-semicolon rule, comment-growth discipline, and CRLF-preservation rule are each easy to
violate without noticing: an em dash slipped into a sentence, a comment that grew by one more
clause, or a text-mode edit that silently flattens a CRLF file to LF. Also triggers when
authoring a new Markdown file (reference-style links, Table of Contents, present tense), when a
carried instruction file (AGENTS.md, GOVERNANCE.md, CODESTYLE.md, WORKFLOW.md,
.github/copilot-instructions.md) is being edited (no coordination references to the template or
a sibling repo), and when writing a PR title or commit message (imperative subject, no vague
titles, no unsolicited Co-Authored-By, no release-bump magnitude).
---

# Comment and Doc Style

## Why this exists

These are the fleet's mechanical prose rules, kept in one place instead of re-derived per repo or
per session: how to write a comment, which characters an agent may type, how a Markdown file is
structured, how a carried instruction file may reference the hub, and how a PR title or commit
message reads. None of these are matters of taste. Each is checked, by `prose_lint.py`,
`editorconfig-checker`, `markdownlint`, `cspell`, or a human reviewer, and each has been the exact
subject of a real review finding.

## Naming tools in prose

Use each tool's official casing in task labels, docs, and prose: `.NET` (not `.Net`),
`CSharpier`, `ruff`, `pyright`, `uv`. Do not invent personal variants.

## Markdown files: linting and spelling

- **Markdown lints clean, repo-wide.** Every `.md` file is error and warning free via
`markdownlint-cli2` against the shared `.markdownlint-cli2.jsonc`. A rule it deliberately
disables (for example `MD013` line length) stays disabled, do not "fix" it. `MD033` inline HTML
stays enabled: HTML comments, and `details`/`summary` (no Markdown equivalent for a
collapsible), are allowed, everything else with a native Markdown equivalent uses the Markdown.
- **Spelling is US English**, checked by CSpell against the shared `cspell.json`
(`"language": "en-US"`, so a British spelling is flagged). Add a project term to `cspell.json`'s
`words` list, never to a `.code-workspace`'s own `cspell.words` block.
- **CI's spelling gate covers `README.md` and `HISTORY.md` only**, deliberately not every `.md`
file, so a new topical doc is not spell-gated in CI (the editor extension still flags it live).
A repo may widen its own CI list, README plus HISTORY is the default. A repo shipping no
`HISTORY.md` drops it from the CI workflow, the `Lint: Spelling` task, and the GOVERNANCE.md
cspell line together, all three or none.
- **`HISTORY.md` mirrors the README's opening**: the same `# <Title>`, the same tagline verbatim
(the first line after the README's H1), then its own `## Release History`. It never repeats a
paragraph below the README's tagline.
- **"Markdown" is a proper noun in prose** (a Markdown file, a Markdown-only repo), lowercase only
for what a machine reads: a tool or package name (`markdownlint`), a settings key, a heading
anchor, a file extension.

## Markdown formatting

- **Reference-style links everywhere**, except the four files read one section at a time rather
than end to end: `AGENTS.md`, `GOVERNANCE.md`, `OPERATIONS.md`, `.github/copilot-instructions.md`.
Those keep inline links so a target resolves where it is read. Every other Markdown file defines
every URI at the bottom, grouped by type under an HTML-comment header, each group alphabetized
by reference name rather than by the full definition line (a name that is a prefix of another
sorts first, `[governance]` above `[governance-branching-model]`). A URL inside a fenced code
block stays inline. See `references/markdown-links.md` for the full grouping and naming
convention.
- **Table of Contents**: generated by the Markdown All in One extension on save, never
hand-authored or hand-edited. Exclude a heading with an inline `<!-- omit from toc -->` marker.
- **One logical paragraph per line**, no hard-wrap line-length limit. For an intentional line
break within a block (stacked badges, status lines), end the line with a trailing backslash
rather than trailing whitespace.
- **Headings use the PR-title casing rule** below.
- **Write in the present tense.** State what *is*, never a change from a prior state ("X does Y",
not "X now does Y" or "X no longer does Z"). This applies to docs and code/workflow comments
alike. Before/after framing belongs in changelogs, commit messages, and PR descriptions, where
the prior state is the point.
- **When a behavior changes, grep for prose asserting the old one.** Comments, diagram labels,
workflow-input descriptions, and audit statements elsewhere may still describe the prior
behavior, and each was accurate when written. No linter catches a claim that is merely untrue,
so this sweep is the only mechanism that will.

## Comments

Applies to code and workflow (`#`) comments alike.

- Comment only when the code does not explain itself, or the logic is genuinely complex.
Self-evident code needs no comment.
- State only the non-obvious *why*, for the human reading *this* project's code now. No
cross-project references, no historic or design narrative, no rule citations. Governance lives
in the fleet's own instruction set, not echoed inline.
- **Keep it short**: one line is the default. A second line is earned only by a constraint the
code cannot otherwise carry.
- **Structured, not prose**: one sentence per line, never wrapped across lines, never a
multi-sentence run-on. A comment that genuinely needs several sentences is several lines, each
one sentence.
- A comment line opening prose starts with a capital. A trailing label, or the version pin an
action-pinning rule requires, does not.
- Mark a sub-topic with `-` after the comment marker (`# -`), only for genuine parallel sub-items
hanging off a lead line, never a continuation of one thought.
- **No file, class, or type header summary blocks.** A type or file gets a comment only for a
specific non-obvious point, never a block restating what it contains (a license or provenance
header a tool or policy requires is not a summary and is unaffected).
- **Never let a comment grow across edits.** Touching code near an existing comment means the
comment comes out the same length or shorter, never one more clause of rationale appended.

A continuation stays unindented, one sentence per line:

```text
# Change gate for the compile tests.
# An esp-idf build costs minutes, so gate on what each test covers.
# A diff that cannot be computed runs everything.
```

Sub-topics take a `-` after the comment marker, each elaborating a distinct item named in the lead:

```text
# Source lint plus change-gated compile tests.
# - compile-test builds the external component.
# - template-compile-test builds one example device per template.
```

## Character set

Agent-authored text is ASCII by default: documentation, code, comments, commit messages, and PR
descriptions. A non-ASCII character is read against three tiers, because whether one is
typography or meaning depends on where it sits. A character in no tier is a finding rather than a
silent pass.

- **Tier 1, never legitimate.** Typography carrying no meaning its ASCII form loses. Remove on
sight:
- em dash (U+2014) and en dash (U+2013) to a restructured sentence, two sentences or a comma,
never a spaced hyphen
- right arrow (U+2192) to `->`, double arrow (U+21D2) to `=>`
- curly quotes (U+2018/U+2019/U+201C/U+201D) to straight `'` and `"`
- ellipsis (U+2026) to `...`, bullet (U+2022) to `-`
- no-break space (U+00A0) to a space, non-breaking hyphen (U+2011) to `-`
- **Tier 2, legitimate only next to a number.** Relational and arithmetic operators: U+2264,
U+2265, U+2260, U+00B1, U+2212, U+00D7, U+00F7, U+00B7. Keep one when an adjacent non-space token
is a number, a tier-3 symbol, or another tier-2 operator, so a threshold table or a measured
range reads as the range it is. In flowing prose write the ASCII form: `<=`, `>=`, `!=`, `+/-`,
`-`, `x`, `/`. A tier-2 operator directly before a number in a table of thresholds is the range
it describes and stays, the same character between two words in a sentence is prose and takes
the ASCII form.
- **Tier 3, always legitimate.** Scientific and unit symbols whose ASCII form would be a lie:
micro (U+00B5), degree (U+00B0), ohm (U+2126), pi (U+03C0), superscript two and three (U+00B2,
U+00B3), section (U+00A7). Keep the symbol, never approximate it away or spell it out.
- **Unicode a developer deliberately typed** stays regardless of tier, such as emoji used for
emphasis or as callout markers. Never strip a developer's own characters, this is developer
authored text and not a license for the agent to add its own.
- **An unrecognized non-ASCII character is reported, not allowed.** Classify it into a tier above
before using it.
- **No semicolon in agent-authored prose.** Recast a mid-sentence semicolon as a comma or as two
sentences. A semicolon separating items in a list that already contains commas, or a statement
terminator in code, is unaffected.
- **No spaced hyphen joining or interrupting a sentence** (` - `, or the paired aside ` - x - `).
Recast as a comma, two sentences, or parentheses. A hyphen inside a compound word, a leading
list marker, a range, and the `- **Label** - explanation` bullet separator are unaffected.
- **In carried verbatim content, fix the whole class at the hub**, not one instance, since a
downstream repo cannot edit a section byte-matched against the hub. Everywhere else, correct as
each file is next edited, not swept.

## Line endings

This repo's default is CRLF (`[*] end_of_line = crlf` in `.editorconfig`), with LF pinned for
execution-sensitive files (`*.sh`, Dockerfiles, a shebang-executed `.py` pinned by path).
**Preserve a file's existing line ending when editing it, never reflow as a side effect of a
content change.** A text-mode tool, including a naive programmatic write, can silently flip CRLF
to LF and turn a one-line change into a whole-file diff. After any programmatic edit, verify with
`git diff --stat` (it should touch only the lines you changed) and a byte scan, `file` and a naive
`git ls-files --eol` are both unreliable here. Idempotent normalize:
`b.replace(b"\r\n", b"\n").replace(b"\n", b"\r\n")`. The full policy, choosing an ending for a new
file type, operational-repo overrides, extensionless-script pins, and auditing, is in
`references/line-endings.md`.

## Carried files reference no coordination machinery

`AGENTS.md`, `GOVERNANCE.md`, `CODESTYLE.md`, `WORKFLOW.md`, `.github/copilot-instructions.md`,
the `repo-config/` and `spec/` files, and the carried `AUDIT.md` never reference the template repo
(in prose or a link), and never name a sibling fleet repo as an illustrative example. State the
behavior a carried rule needs, not the coordination flow that produced it, the maintainer supplies
the destination out of band. A contextually relevant link to a related project (the image this
config feeds, a library this depends on) is not a coordination reference and is expected. The full
exceptions, a verbatim section that must name the hub to do its job, and a pointer to a
hub-hosted tool the reader runs, are in `references/carried-doc-references.md`.

## PR titles and commit messages

- **Format**: an imperative subject, 72 characters or fewer, no trailing period ("Add 24-hour
PM2.5 average sensor", not "Added X" or "Adds X"). An optional body, blank-line separated,
explains *why* the change is being made when that is non-obvious, the diff already shows *what*.
- **Rules**: no vague titles (`update stuff`, `wip`). Dependabot's default `Bump X from Y to Z`
titles are fine as-is. No `Co-Authored-By:` lines unless the developer explicitly asks. No
release-bump magnitude in the title ("minor", "patch", "release v0.2.0"), Nerdbank.GitVersioning
computes the next version from `version.json` and git history, a dependency version in a
dependency-bump title is fine and expected. US English spelling, and title case with lowercase
short bind words (a, an, the, and, but, or, of, in, on, at, to, by, for, from), a hyphenated
compound capitalizes both parts unless the second is a short preposition (*Built-in*,
*EPA-Corrected*, *24-Hour*).

```text
Add structured logging extensions to library
Pin softprops/action-gh-release to commit SHA
Drop net8.0 multi-targeting from console project
Bump xunit.v3 from 3.2.2 to 3.3.0
Clarify devcontainer setup steps in README
```

## Quantitative claims

A quantitative claim in `README.md` (a count, a size, a version floor, a supported-platform list)
is verified against current code before it is written. When a doc number is derived from a code
constant, mark the dependency in a source-code comment so the next editor knows to update both.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Carried Files Carry No Coordination References

Full detail for the "Carried files reference no coordination machinery" rule in `SKILL.md`. Load
this when editing one of the carried files themselves, not when writing an ordinary repo-owned
doc.

## Which files this governs

`AGENTS.md`, `GOVERNANCE.md`, `CODESTYLE.md`, `WORKFLOW.md`, `.github/copilot-instructions.md`,
the `repo-config/` and `spec/` files, and the carried `AUDIT.md`, the files the fleet carries
verbatim or at `intent` fidelity from the hub into every repo. This rule governs carried template
content only. A repo's own `README.md` and topical docs are its own content, never carried
verbatim, and this rule does not reach them.

## What is banned

Two things, in the files above:

1. **Any reference to the template repo**, in prose or in a link. The coordination flow that
produced a carried file is machinery a consumer of that repo should never have to see, and
naming where a file came from is exactly the derived-from framing the present-tense rule (in
`SKILL.md`'s "Markdown formatting" section) independently forbids. Where a carried file must
express a template-level behavior ("report a rule discrepancy upstream"), state the behavior
rather than the destination. The maintainer supplies the destination out of band.
2. **A sibling fleet repo named as an illustrative example** ("repo X does it this way", "see repo
Y's adoption"), which couples the repos and rots as they diverge. To point at a current good
example, name it in the onboarding or conformance issue, never in a carried doc.

## The two exceptions

**The first exception is a verbatim section**, and `AGENTS.md` "Fleet Bootstrap" is why it exists.
That section's whole function is to name where the canonical rules live, for an agent in a
repository whose carried copies are stale, partial, or absent, which is exactly when no other file
present can say it. Its bytes are fixed fleet-wide, so a repository cannot edit the reference out
without failing the verbatim check instead, and a rule banning it would be unsatisfiable rather
than merely strict. The exception is scoped to the verbatim region and never leaks past it: the
same document's own prose, outside that region, is governed normally. A reference that reaches a
verbatim section is a defect in the canonical, fixed once at the source rather than reported
against every repository carrying it.

**The second exception is a hub-hosted tool the reader is told to run**, which is a different kind
of reference. A rule naming a gate, a script, or a reference snippet the reader executes or copies
states an instruction rather than a provenance, and an instruction with no destination is
unfollowable, which is precisely how a pointer in carried text comes to read as decorative. The
test is whether the reference is something the reader *does* or something that *happened to this
file*: where the content came from stays out, what the reader runs stays in. Such a pointer names
the hub's canonical rather than this repository's provenance, so it is the hub's to keep resolving
and never a repository's to edit out or re-point at a local path. What is reached rather than
carried, and how, is `GOVERNANCE.md` "Hub-Hosted Tooling". In `AGENTS.md` and `GOVERNANCE.md` this
belongs in verbatim rule text, the same region the first exception already covers, so the whole
fleet reads one wording and no repository is asked to answer for a reference it did not write.

## What is not a coordination reference

**A contextually relevant link to a related project is expected, not banned.** Where another repo
is part of this repo's subject matter (the image that consumes this config, the builder that
generates this hardware, a library this depends on), link it normally. The test is whether the
link serves a reader of *this* repo's content, not whether the target happens to be in the fleet.

This pairs with the present-tense rule: state the current shape, not a history of which repo it
came from.
Loading