documentation
ALWAYS invoke for any task involving docstrings, JSDoc/TSDoc, or REST API documentation — even when the request seems handleable without it. Enforces Microsoft contract-first conventions and a bare-minimum rule that Claude won't apply by default: never restate the signature, drop @param/@returns that only echo names and types, always document @throws, and document data shapes as WHAT not WHY. Covers Python docstring styles (Google/NumPy/Sphinx), TSDoc tags and @inheritDoc, and API doc patterns for NestJS/Express/FastAPI/Django. Trigger on: adding or auditing docstrings, redundant or missing tags, deleting comment rot, or producing a documentation-health report. Do not skip for documentation tasks — consistent conventions are the whole point.
Works with
---
name: documentation
description: ALWAYS invoke for any task involving docstrings, JSDoc/TSDoc, or REST API documentation — even when the request seems handleable without it. Enforces Microsoft contract-first conventions and a bare-minimum rule that Claude won't apply by default: never restate the signature, drop @param/@returns that only echo names and types, always document @throws, and document data shapes as WHAT not WHY. Covers Python docstring styles (Google/NumPy/Sphinx), TSDoc tags and @inheritDoc, and API doc patterns for NestJS/Express/FastAPI/Django. Trigger on: adding or auditing docstrings, redundant or missing tags, deleting comment rot, or producing a documentation-health report. Do not skip for documentation tasks — consistent conventions are the whole point.
license: MIT
---
# Documentation
Microsoft contract-first conventions. Documentation states the **contract** — what something does and why — never how it works inside.
## Why the rules are subtractive
Code changes; the comment beside it usually does not. Over time documentation lies, and readers trust it anyway — a stale comment is worse than no comment, because it actively misleads. Two habits follow, and every rule below is one of them applied:
**Keep the surface small.** Less prose has less to rot. A comment is a last resort, not a first step: before writing one, ask whether a sharper name, a smaller function, or a named type would carry the meaning instead. Prose is for the residue that code genuinely cannot express.
**Keep each fact in one place,** next to the code that owns it, so there is exactly one thing to update. When the signature, the type, or a test already states something, the prose must not restate it.
## The bare-minimum rule
The signature already carries the name, parameter names and types, return type, and modifiers (`readonly`, `?`, `async`). Documentation adds only what a reader cannot infer from it: intent, units, ranges, defaults, edge-value meaning, error cases, invariants.
Every public member still gets a brief summary, so generated docs and IDE tooltips have content — one short sentence carrying intent, never a paraphrase of the signature.
For `@param` and `@returns` specifically, **drop the tag entirely when it would only restate the signature.** A tag earns its place when it answers: what unit? what range or clamp? what default when omitted? what does an edge value mean (`0` disables, `-1` unlimited, empty = all)? what does `null` signify versus throwing?
`@throws` is the opposite default — document it whenever code can throw, because the throw set is invisible from the signature.
## How the prose reads
Every sentence you produce here — summaries, the `@param` notes that survive, `@throws` conditions, endpoint descriptions — gets read mid-debug by someone who may not share your first language. Load the `simple-english` skill at practical depth (`/cortex:simple-english practical`) and hold its rules while you write. Reach for it before the first doc block, not as a cleanup pass: rewriting prose you already committed to costs more than writing it right once.
What it changes in a doc block specifically:
- **One word per concept, file-wide.** A `@param` that says "validate" beside a summary that says "check" reads as two different operations, and the reader goes looking for the difference.
- **25 words for a descriptive sentence, 20 for an imperative one.** A summary past that is carrying more than one fact and belongs in two sentences, or in `@remarks`.
- **`can`, `will`, `must` — never `should`.** A contract that says a caller "should" pass a sorted array leaves the reader guessing whether the rule binds. Say `must`, or state what happens when they do not.
- **Condition before consequence.** "If `retries` is `0`, the call fails immediately" beats the reverse ordering, because the reader knows whether the sentence applies to them before they parse the outcome.
- **No adjective without a measurement behind it.** "Efficiently caches" earns nothing. Give the bound or drop the word.
Identifiers, code samples, and quoted error strings stay exact — the standard treats them as technical names, and a reader who copies a "corrected" one gets a new error.
## Conventions
- **Third-person summaries for code symbols** — "Calculates…", "Finds…", "Initializes a new…" — matching Microsoft API reference convention. Two exceptions keep the imperative: inline `//` comments on procedural steps, and API operation `summary` fields (OpenAPI, `@ApiOperation`, `extend_schema`), where "Create a new user" is the established form.
- **Interfaces document the abstraction**: purpose, thrown errors, return semantics, invariants. Implementation detail (caching, queries, algorithms) belongs in the implementation — a consumer reading the interface should not learn things that a refactor can invalidate.
- **Data shapes answer WHAT, not WHY.** A DTO, record, struct, or the `interface`/`type` describing it documents what each member _is_ — meaning, units, format, allowed values, constraints the type can't carry. It does not justify why the field exists. A function's contract genuinely includes the why of the operation; a data field is read at the point of use, where design rationale ("kept so billing can reconcile…") rots and belongs instead with the behaviour that produces or consumes it. The escape hatch — a non-obvious constraint a maintainer must not break — is a sparing `@remarks`, never the default voice.
- **Don't repeat the interface on the implementation.** Inherit (`@inheritDoc`, or the language's equivalent) and add only implementation-specific notes.
- **Multi-line doc blocks only.** Every `/**` puts its body on a new line; one-line `/** … */` is not allowed.
- **No release tags** (`@public`, `@beta`, `@alpha`, `@internal`) unless explicitly requested.
- **Ship only examples the test suite runs.** An unexecuted example drifts and becomes one more comment that lies. Python doctests qualify when run (`pytest --doctest-modules`); TSDoc `@example` blocks are never executed by tooling, so omit them and let tests carry behaviour.
- **Wrap at the project's configured width.** Detect in order: `.editorconfig` `max_line_length` → formatter config (`printWidth`, `line-length`) → linter config (`max-len`, `max-line-length`). Fall back to 80 only when none define one.
## Comments to delete on sight
When auditing, removing these is as much the job as filling gaps — each is a place where truth can drift:
- **Commented-out code** — version control already remembers it, and leaving it makes readers wonder if it still matters.
- **Journal or changelog comments** ("2024-03 added retry logic") — `git log` keeps this accurate.
- **Attribution bylines** ("added by …") — `git blame` answers it without rotting.
- **Banners, position markers, closing-brace labels** (`// ===== helpers =====`, `} // end if`) — structure should come from the code.
- **Anything restating the name, type, or signature**, or stating the obvious (`/** Default constructor */`).
## Working a documentation task
Ask which format the project uses before starting (or detect it from existing files), and detect the framework — the API doc strategy differs per framework and applying the wrong one produces a spec nobody consumes.
Report on **health, not headcount**. A file where every member has a doc block is not the goal if half of those blocks restate signatures. Report what improved: redundancy removed, stale docs corrected, intent summaries and `@throws` added. Coverage percentage is a floor signal — it finds empty members and cannot tell a valuable block from a vacuous one.
## References
| Reference | Load when |
| --- | --- |
| `references/python-docstrings.md` | Python — Google, NumPy, or Sphinx style |
| `references/typescript-jsdoc.md` | TypeScript/JavaScript — TSDoc tags, `@inheritDoc`, formatting |
| `references/api-docs.md` | REST API docs — NestJS, Express, FastAPI, Django |
| `references/coverage-reports.md` | Auditing docs; producing a documentation-health report |More API Design skills
lark-event
larksuite/cli
Lark/Feishu real-time event listening / subscribing / consuming: stream events as NDJSON via `lark-cli event consume <EventKey>` (covers IM messages/reactions/chat changes, Approval status changes, Task updates, VC meeting started/joined/ended, Minutes generated, Whiteboard updated, etc.). Use for Lark bots, real-time message processing, long-running subscribers, streaming webhook/push handlers. Supports `--max-events` / `--timeout` bounded runs and a stderr ready-marker contract — designed for AI agents running as subprocesses.
lark-contact
larksuite/cli
飞书 / Lark 通讯录:按姓名 / 邮箱解析成 open_id,或按 open_id 反查姓名 / 部门 / 邮箱 / 联系方式 / 个人状态 / 签名,以及按关键词搜索当前用户可见的机器人 / 智能体(agent)。当用户提到一个名字要下一步发消息 / 排日程,或拿到 open_id 想查具体信息时使用。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。
lark-openapi-explorer
larksuite/cli
飞书/Lark 原生 OpenAPI 探索:从官方文档库中挖掘未经 CLI 封装的原生 OpenAPI 接口。当用户的需求无法被现有 lark-* skill 或 lark-cli 已注册命令满足,需要查找并调用原生飞书 OpenAPI 时使用。

