Docstrings and Doc Comments skill for Codex
Document the contract, not the signature. Codex matches the repository's existing docstring style, then documents what the signature cannot say: units, valid ranges, error conditions, side effects and retry safety. It reads each function's body and tests before writing, flags code and doc mismatches instead of guessing, and wires ruff pydocstyle, interrogate, eslint-plugin-jsdoc, TypeDoc validation or rustdoc lints plus doctests into CI.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- docstrings Installs to ~/.agents/skills/docstrings/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install docstrings The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project docstrings Installs to .agents/skills/docstrings/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/docstrings.zipunzip -o docstrings.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) docstrings PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $docstrings, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Writes and audits API doc comments in the language's native convention - Python docstrings (Google, NumPy or reST), JSDoc and TSDoc, Go doc comments, Rust rustdoc, Javadoc - documenting the contract (purpose, parameters, return value, errors, side effects, examples) rather than restating the signature, and enforces them with ruff pydocstyle rules, interrogate, eslint-plugin-jsdoc, TypeDoc validation, rustdoc lints and doctests. Use when adding or fixing docstrings, documenting a public API or library before release, generating docs for undocumented modules, or adding docstring linting to CI.
Use it when
- A library's public API is undocumented or inconsistently documented
- Docstrings exist but restate names and types without stating behaviour
- You want docstring linting, coverage or doctests enforced in CI
Use something else when
- You are deciding whether an inline code comment is needed or stale: use code-comments
- You are documenting an HTTP API for external consumers: use api-documentation
How to check the result
Docstring linters and doctests pass, the doc build has no warnings, and five sampled doc comments match the code and tests on errors, defaults and units.
The mistake it prevents
Generating docstrings that paraphrase the function name while claiming behaviour, such as returning None, that the code does not actually have.
A doc comment is worth its lines when a caller can use the function correctly without reading its body: what it does, what each argument means beyond its type, what comes back, what can go wrong, and anything surprising. A doc comment that repeats the name and types is noise; a wrong one is a bug.
Inspect first
cat AGENTS.md 2>/dev/null | head -60
rg -n "convention|pydocstyle|\"D\"|interrogate" pyproject.toml setup.cfg ruff.toml 2>/dev/null
rg -n "jsdoc|tsdoc|typedoc" package.json eslint.config.* typedoc.json 2>/dev/null
rg -n "missing_docs|missing_errors_doc" --glob '*.rs' --glob 'Cargo.toml' 2>/dev/null | head
Then read five existing well-documented functions in the codebase. Match their style (Google vs NumPy, sentence case, tense, whether types are repeated) exactly. Consistency with the repo beats any external guide.
What to document, in priority order
| Priority | Target | Reason |
|---|---|---|
| 1 | Public API of a library (exported, __all__, pub, export) | Users cannot read your source |
| 2 | Functions with non-obvious behaviour: side effects, I/O, mutation, caching, retries | Callers get these wrong |
| 3 | Anything that raises or returns errors callers must handle | Error contract is invisible in the signature |
| 4 | Module and package level docs | Orientation for new readers |
| 5 | Private helpers whose name and types already say everything | Usually skip |
Content of a good doc comment
- Summary line: one sentence, what it does, from the caller’s view. Imperative or third person per house style (“Return the…” in Python PEP 257; “Returns the…” in Javadoc/TSDoc tradition). Under ~80 characters.
- Details (optional): behaviour the caller must know, such as ordering, idempotency, complexity for large inputs, thread safety, units, time zones.
- Parameters: meaning, units, valid range, what
None/null/empty means. Do not repeat a type the signature already declares unless the convention requires it. - Returns: what and in which cases; what an empty result means.
- Errors: each exception or error variant and the condition that causes it.
- Example: for public functions whose usage is not obvious; make it runnable as a doctest where the language supports it.
- Deprecation with replacement and removal version, when relevant.
Never document implementation details that may change (“uses a dict internally”) unless
callers depend on them; those belong in code comments ($code-comments).
Python
Google style (most common default; set the convention in ruff):
def charge(customer_id: str, amount: int, *, currency: str = "usd", idempotency_key: str | None = None) -> Charge:
"""Charge a customer's default payment method.
The call is safe to retry when ``idempotency_key`` is given: a repeated key returns
the original charge instead of creating a new one.
Args:
customer_id: Processor customer ID, e.g. ``"cus_9Qx2"``.
amount: Amount in the smallest currency unit (cents for USD). Must be positive.
currency: ISO 4217 code, lowercase.
idempotency_key: Unique key per logical charge; keys expire after 24 hours.
Returns:
The created charge. ``status`` is ``"pending"`` until the processor confirms.
Raises:
CardDeclinedError: The issuer declined the payment.
ValueError: ``amount`` is zero or negative.
Example:
>>> charge("cus_test", 500).amount
500
"""
NumPy style uses underlined section headers (Parameters, Returns, Raises) and suits
scientific code with array shapes; reST/Sphinx style uses :param x: fields. Pick what
the repo uses; do not mix styles in one package.
# pyproject.toml
[tool.ruff.lint]
extend-select = ["D"]
[tool.ruff.lint.pydocstyle]
convention = "google" # or "numpy", "pep257"
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["D"]
ruff check --select D src/
interrogate -v --fail-under 80 src/ # docstring coverage; pip install interrogate
pytest --doctest-modules src/ # runs >>> examples
TypeScript and JavaScript
TSDoc for TypeScript (types come from the signature, so no {type} in tags):
/**
* Charges a customer's default payment method.
*
* @remarks
* Safe to retry when `idempotencyKey` is set: a repeated key returns the original charge.
*
* @param customerId - Processor customer ID, for example `cus_9Qx2`.
* @param amount - Amount in the smallest currency unit (cents for USD). Must be positive.
* @returns The created charge; `status` is `pending` until the processor confirms.
* @throws {@link CardDeclinedError} When the issuer declines the payment.
*
* @example
* ```ts
* const c = await charge("cus_9Qx2", 500, { idempotencyKey: orderId });
* ```
*/
export async function charge(customerId: string, amount: number, opts: ChargeOptions = {}): Promise<Charge> {
Plain JavaScript uses JSDoc with types in braces: @param {number} amount - ..., and
those types feed editor tooling and tsc --checkJs.
// eslint.config.js (flat config)
import jsdoc from "eslint-plugin-jsdoc";
export default [
jsdoc.configs["flat/recommended-typescript"], // or "flat/recommended" for JS
{ rules: { "jsdoc/require-jsdoc": ["warn", { publicOnly: true }] } },
];
For TSDoc syntax validation add eslint-plugin-tsdoc (tsdoc/syntax). For generated
API docs, TypeDoc with "validation": { "notDocumented": true } in typedoc.json warns
about undocumented exports; add "treatWarningsAsErrors": true in CI.
Go
// Charge charges the customer's default payment method and returns the created charge.
// Amount is in the smallest currency unit. Charge is safe to retry when key is non-empty.
//
// It returns ErrCardDeclined when the issuer declines the payment.
func Charge(ctx context.Context, customerID string, amount int64, key string) (*ChargeResult, error) {
- Begin with the identifier’s name; full sentences; package comment starts
// Package billing ...in one file (oftendoc.go). - Doc comments support headings (
# Title), lists and links ([ErrCardDeclined],[context.Context]);gofmtnormalises them. - Check with
go doc ./billing Charge;revive’sexportedrule flags missing comments.
Rust
/// Charges the customer's default payment method.
///
/// # Errors
///
/// Returns [`ChargeError::Declined`] when the issuer declines the payment.
///
/// # Examples
///
/// ```
/// # use billing::charge;
/// let c = charge("cus_test", 500)?;
/// assert_eq!(c.amount, 500);
/// # Ok::<(), billing::ChargeError>(())
/// ```
pub fn charge(customer_id: &str, amount: u64) -> Result<Charge, ChargeError> {
Conventional sections: # Examples, # Errors, # Panics, # Safety (required for
unsafe fn). Enforce with #![warn(missing_docs)] in lib.rs, clippy’s
missing_errors_doc and missing_panics_doc (pedantic group), and run examples with
cargo test --doc. Build with cargo doc --no-deps and fix broken intra-doc links.
Java
Javadoc with @param, @return, @throws, {@code ...} and {@link ...}; run
javadoc -Xdoclint:all or the build’s doclint to catch missing tags. JDK 23+ also accepts
Markdown doc comments with /// (JEP 467); use them only if the project targets 23+ and
already chose that style.
Bulk generation workflow
- [ ] 1. Measure coverage (interrogate, TypeDoc validation, missing_docs) and list targets
- [ ] 2. Start with public API, sorted by usage (rg call counts) or by module
- [ ] 3. For each function: read the body AND its tests; note errors, side effects, units
- [ ] 4. Write the doc comment; add an example if usage is non-obvious
- [ ] 5. Flag mismatches between code and existing docs as findings, do not silently pick one
- [ ] 6. Run linter, doctests, doc build; re-measure coverage
Work in reviewable batches (one module per commit). Generated docstrings that merely paraphrase names (“Gets the user. Args: user_id: The user id.”) fail review; if the honest content is that thin, skip the function or document only its error cases.
Gotchas
- Docstring says “returns None if not found” but code raises. Read the body every time.
- Units and time zones missing: “timeout” in seconds or milliseconds? “date” in UTC?
- Copy-pasted docs from an overload or sibling function with different behaviour.
- Python: a string literal placed after any other statement in the body (an import, an
assignment) is not a docstring;
help()and doc tools ignore it. It must come first. - TSDoc
@param name - descriptionrequires the hyphen; JSDoc style{type}inside TS files duplicates and can contradict the real types. - Doctest examples that depend on network, time or random output; seed or mark them
skipped (
# doctest: +SKIP,```no_runin Rust).
Verification
- Linter clean for the docstring rules; coverage at or above the agreed threshold.
- Doctests pass (
pytest --doctest-modules,cargo test --doc). - Doc build has no warnings (
cargo doc, TypeDoc, Sphinx-W). - Spot-check five generated docstrings against code and tests: every stated error, default and unit is correct.
Report
**Scope:** src/billing (public API, 34 functions)
**Coverage:** interrogate 41% -> 92%; remaining are private helpers
**Style:** Google (matches existing), ruff D rules enabled with google convention
**Doctests:** 11 added, all passing
**Code/doc mismatches found:** 2 (refund() raises on partial refunds; docs said it clamps), not changed, needs owner decision What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Docstrings and Doc Comments"
short_description: "Document the contract, not the signature"
default_prompt: "$docstrings Add Google-style docstrings to every public function in src/billing" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.