Promptheus/rules53 rule sets · CC0Promptheus hub ↗

Workflow · TypeScript 6.0 · TypeDoc 0.28 · TSDoc · Docusaurus 3.10 · MADR 4.0

Documentation

Explain why, keep docs next to code, current or deleted.

documentationreadmeadr

Updated 5 Jul 2026 · CC0

AGENTS.mdrepo root

You own the documentation for this TypeScript/Node codebase: READMEs, TSDoc API comments, ADRs, changelogs, and the docs site. "Good" means a newcomer can go from clone to running tests in five minutes by copy-pasting, every public API has a docstring a tool can render, every significant decision has an ADR, and no doc lies — stale docs are deleted, not left to rot. Docs ship in the same PR as the code they describe.

Stack

  • Language/runtime: TypeScript 6.0 (typescript@6.0.3), Node.js 24 LTS. TS 7.0 (the Go-based tsgo compiler) is at RC — do not pin it yet; keep advice valid for the stable 6.0 checker.
  • API reference generator: TypeDoc 0.28.x with typedoc-plugin-markdown@4 when emitting Markdown into a docs site. Never hand-maintain an API table — generate it.
  • Doc-comment standard: TSDoc (@microsoft/tsdoc@0.16) enforced by eslint-plugin-tsdoc. Use TSDoc tags, not loose JSDoc type annotations (types live in the TS signature, not the comment).
  • Docs site: Docusaurus 3.10.x (@docusaurus/core@3.10) for TS/JS projects. For Python-heavy repos use MkDocs Material 9.7.x — but note it is in maintenance mode (feature-frozen since 9.7.0, security fixes through Nov 2026); Zensical is its successor, not yet 1.0, so do not adopt it for production docs.
  • Prose linter: Vale 3.x with a vocabulary and one style package (Google or Microsoft Writing Style Guide) in .vale.ini.
  • Markdown linter/formatter: markdownlint-cli2@0.23 for content rules, Prettier 3.x for layout. They do overlap on stylistic rules (list indentation MD004/MD007/MD030, blank lines MD012, trailing whitespace MD009), which fight each other — so disable markdownlint's Prettier-owned formatting rules via its built-in preset ("extends": "./node_modules/markdownlint/style/prettier.json" in .markdownlint-cli2.jsonc) and let Prettier own all whitespace/wrapping. Markdownlint then enforces only content rules Prettier can't (MD024 duplicate headings, MD033 raw HTML, MD034 bare URLs, MD040 fenced-block language).
  • Link checker: lychee (Rust, fast) in CI to catch dead links and anchors.
  • Changelog/versioning: Changesets (@changesets/cli@2.31) producing a Keep a Changelog 1.1.0-formatted CHANGELOG.md, SemVer 2.0.0.
  • ADRs: MADR 4.0.0 template. Optionally manage with log4brains for a browsable ADR site.
  • Diagrams: Mermaid 11.x fenced blocks (```mermaid) — versioned as text, rendered natively by GitHub and Docusaurus. Model architecture with the C4 model (Context → Container → Component).
  • Doc architecture: organize the docs site by Diátaxis — tutorials, how-to guides, reference, explanation — as four distinct top-level sections.

Project conventions

README.md               # entry point; quickstart + orientation only
CHANGELOG.md            # Keep a Changelog, human-curated via Changesets
CONTRIBUTING.md         # how to build/test/submit; linked from README
SECURITY.md             # vuln reporting policy
LICENSE
.changeset/            # pending changeset markdown files
docs/
  tutorials/           # learning-oriented (Diátaxis)
  how-to/              # task-oriented
  reference/           # information-oriented (TypeDoc output lands here)
  explanation/         # understanding-oriented
  adr/
    0001-record-architecture-decisions.md
    NNNN-short-title.md
  diagrams/            # .mmd source + C4 models
.vale.ini
.markdownlint-cli2.jsonc
  • File names: kebab-case .md. ADRs are zero-padded, monotonic, immutable IDs: 0007-use-changesets.md. Never renumber or reuse an ID.
  • One H1 per file, matching the file's topic; sentence case headings (## Error handling, not ## Error Handling).
  • Links are relative within the repo (../adr/0007-use-changesets.md), never absolute github.com/... URLs that break on forks and rename. Link to headings by their generated anchor.
  • Docs live next to the code they describe and are versioned in the same repo. A package's README sits in that package's folder in a monorepo.
  • Fenced code blocks are always tagged with a language (```ts, ```bash, ```json) — untagged blocks skip syntax highlighting and prose-lint exclusion.
  • Prose lints on: keep sentences under ~25 words, active voice, second person for instructions ("Run npm test"), present tense.

README

The README orients a newcomer and gets them running. It is not the manual — deep content goes in docs/.

Required sections, in order:

  1. One-line description + optional badges (CI status, npm version, license). No marketing paragraphs.

  2. Prerequisites — exact versions: Node.js >= 24, pnpm >= 10 (pin the exact line via packageManager in package.json). State them; do not assume.

  3. Quickstart — copy-pasteable, in order, that actually works on a clean clone:

    git clone https://github.com/org/repo.git
    cd repo
    pnpm install
    cp .env.example .env      # then fill in the values documented below
    pnpm dev                  # starts on http://localhost:3000
    pnpm test
    
  4. Configuration / environment — every env var in a table: name, required?, default, description. Ship a committed .env.example with every key present and dummy/placeholder values — never real secrets.

  5. Common tasks — the 4-6 commands a contributor runs daily (pnpm build, pnpm lint, pnpm typecheck, pnpm changeset).

  6. Contributing — one line linking to CONTRIBUTING.md.

  7. License — one line linking to LICENSE.

Rules:

  • Every command in the README is executed by CI (see Testing) so it can never silently rot.
  • Do not paste API reference into the README — link to the generated TypeDoc reference.
  • If a command needs a running service (DB, Redis), give the exact docker compose up line, not "make sure Postgres is running".
  • Keep it under ~150 lines. When it grows past that, move detail into docs/how-to/.

Code comments

Comment why, never what. The code already says what it does; the comment explains the reason a reader cannot recover from the code.

  • Document non-obvious decisions, gotchas, invariants, units, and edge cases:

    // Stripe rounds half-up; we round half-even to match the ledger, so a
    // 2.5-cent fee becomes 2, not 3. Diverging here caused reconciliation drift.
    const fee = bankersRound(rawFee);
    
    // timeout is milliseconds, not seconds — the upstream SDK takes ms.
    const client = new Client({ timeout: 30_000 });
    
  • State invariants a reader must not break: // INVARIANT: entries stays sorted by ts ascending; binarySearch below depends on it.

  • Delete commented-out code. Git is the history. A block of // const old = ... is never acceptable in a diff.

  • No comments that restate the signature: // increments the counter above increment() is noise — delete it.

  • Use // TODO(username): reason + link with an issue reference, never a bare // TODO. Bare TODOs are unactionable and accumulate.

  • Prefer a well-named function/constant over a comment when the comment only exists because the code is unclear.

API docs

Every exported symbol (functions, classes, public methods, types, constants) carries a TSDoc comment. Internal/unexported code does not need one unless the logic is subtle. eslint-plugin-tsdoc fails the build on malformed tags.

Do not repeat types in the comment — TypeScript already has them. Document purpose, meaning, failure modes, and an example.

/**
 * Charges a customer's saved payment method for a one-off amount.
 *
 * Idempotent per `idempotencyKey`: repeated calls with the same key return the
 * original charge instead of double-charging.
 *
 * @param customerId - Stripe customer id (`cus_...`).
 * @param amount - Amount in the smallest currency unit (cents for USD).
 * @param idempotencyKey - Caller-supplied unique key; reuse to retry safely.
 * @returns The settled {@link Charge}, or `pending` if async capture is on.
 * @throws {PaymentDeclinedError} When the issuer declines the charge.
 * @throws {RateLimitError} When Stripe returns HTTP 429.
 *
 * @example
 * ```ts
 * const charge = await chargeCustomer("cus_123", 4_999, crypto.randomUUID());
 * ```
 *
 * @public
 */
export async function chargeCustomer(
  customerId: string,
  amount: number,
  idempotencyKey: string,
): Promise<Charge> { ... }
  • Use the standard TSDoc tags: @param, @returns, @throws, @example, @remarks, @deprecated, and the release tags @public / @internal / @beta.
  • @deprecated must say what to use instead and since when: @deprecated Since 3.2 — use {@link chargeCustomer} instead.
  • Link symbols with {@link OtherThing} so TypeDoc cross-references resolve.
  • Every @example must compile — CI extracts and type-checks fenced ts examples.
  • The first sentence is the summary line TypeDoc shows in listings — make it a complete, standalone sentence.

Architecture

Record every architecturally significant decision as an ADR using MADR 4.0. "Significant" = hard to reverse, or affects structure, dependencies, interfaces, or how teams build (choosing a DB, an auth model, a rendering strategy).

MADR 4.0 skeleton:

---
status: accepted            # proposed | rejected | accepted | superseded by ADR-0012
date: 2026-07-05
decision-makers: [issam]
consulted: [backend-team]
informed: [all-eng]
---
# Use Changesets for versioning

## Context and Problem Statement
We publish 4 packages from one monorepo and hand-edited changelogs drift...

## Considered Options
- Changesets
- semantic-release
- Manual SemVer + hand-written CHANGELOG

## Decision Outcome
Chosen "Changesets" because it fits monorepos and keeps a human-curated changelog...

### Consequences
- Good: per-package versioning, PR-reviewable changelog entries.
- Bad: contributors must remember `pnpm changeset`; enforced via CI check.

Rules:

  • ADRs are immutable once accepted. To change a decision, write a new ADR and set the old one's status to superseded by ADR-NNNN. Never edit history.

  • ADR-0001 is always "Record architecture decisions" (the meta-decision to keep ADRs).

  • Keep the big picture in Mermaid C4 diagrams versioned as text, not exported PNGs (PNGs go stale and can't be diffed):

    flowchart LR
      user([User]) --> web[Next.js app]
      web --> api[API service]
      api --> db[(Postgres)]
    
  • Put the system-context diagram in docs/explanation/architecture.md; link ADRs from it. A diagram shows structure; ADRs explain why.

Changelog

Maintain CHANGELOG.md in Keep a Changelog 1.1.0 format, generated from Changesets. Write changelog entries for humans reading a release, not a raw git log.

  • Every user-facing PR adds a changeset: pnpm changeset, pick the bump (patch/minor/major), write one clear sentence in the imperative. CI fails PRs that change src/ with no changeset.
  • Group under the fixed headings: Added, Changed, Deprecated, Removed, Fixed, Security. Keep an ## [Unreleased] section at the top.
  • Entries describe impact, not internals: "Fixed timezone offset in invoice PDFs" — not "patched formatDate in utils.ts".
  • SemVer strictly: breaking change → major, additive → minor, fix → patch. Never quietly break in a minor.
  • Never rewrite released changelog entries; append corrections.

Keep every doc current in the same PR as the change, or delete it. A stale README, a lying docstring, or a broken quickstart is worse than none — it destroys trust. If you touch a public API, update its TSDoc and any how-to that references it in the same diff.

Testing

Docs are tested like code — a broken doc fails CI:

  • Markdown lint: markdownlint-cli2 "**/*.md" and prettier --check "**/*.md".
  • Prose lint: vale docs/ README.md against the chosen style package.
  • Dead links/anchors: lychee --offline docs/ README.md for internal, a periodic online run for external.
  • Runnable quickstart: a CI job that runs the README's install→run→test commands verbatim on a clean checkout. If it drifts, the build breaks.
  • Compilable examples: extract fenced ts blocks and tsc --noEmit them so no doc shows code that doesn't compile.
  • Docs site build: docusaurus build must pass with onBrokenLinks: 'throw' and onBrokenAnchors: 'throw' in docusaurus.config.ts.
  • TSDoc validity: eslint with eslint-plugin-tsdoc enabled fails on malformed doc comments.

Security

  • Never put real secrets in docs, examples, or .env.example. Use obvious placeholders: STRIPE_KEY=sk_test_xxx, sk_live_REPLACE_ME. Scan with a secret scanner (gitleaks) in CI before publishing.
  • Redact internal hostnames, IPs, employee names, and customer data from examples and diagrams. Use example.com, cus_123.
  • Ship and maintain SECURITY.md: how to report a vulnerability, supported versions, response SLA. Do not disclose unpatched vulns in the public changelog until fixed.
  • The docs site is a real app — pin and audit its deps (pnpm audit), and disable directory listing / source maps in the published build.
  • Do not document internal-only endpoints, admin routes, or feature flags in public docs.
  • License every code snippet you copy in; attribute third-party diagrams.

Do

  • Update docs in the same PR as the code change; treat a missing doc update as a failing review.
  • Write for a newcomer; prefer a concrete, copy-pasteable example over a paragraph of prose.
  • Generate the API reference with TypeDoc; keep it in docs/reference/, regenerated in CI.
  • Make every command in README/CONTRIBUTING actually runnable and CI-verified.
  • Give ADRs immutable numbers and supersede rather than edit.
  • Keep diagrams as Mermaid/C4 text so they diff and never desync from reality.
  • Delete a doc the moment it stops being true.

Avoid

  • JSDoc @param {string} name type annotations in TS — the type is in the signature; use bare TSDoc @param name - ....
  • Hand-written API tables — they rot; generate with TypeDoc instead.
  • Commented-out code and bare // TODO — delete the former; issue-link the latter.
  • Comments that restate the code (// loop over users) — comment the why or nothing.
  • Exported PNG/JPEG architecture diagrams — use versioned Mermaid text; binary images go stale and can't be reviewed.
  • ## [1.4.0] - see git log changelogs — write human, impact-focused entries via Changesets, not a dump of commit subjects.
  • Absolute in-repo links (https://github.com/org/repo/blob/main/...) — use relative paths that survive forks and renames.
  • A README that documents commands nobody runs — if npm start no longer exists, the README is a bug.
  • Adopting Zensical or TS 7.0 for production yet — both are pre-stable as of this stack; stay on MkDocs Material 9.7 / Docusaurus 3.10 and TypeScript 6.0.
  • @deprecated with no replacement — always name the successor and the version.

When you code

  • Ship the doc change in the same diff as the code. If the PR changes a public API and its TSDoc is untouched, that is incomplete, not done.
  • Keep doc diffs small and focused; do not reflow an entire file's Markdown while making a one-line fix (it hides the real change in whitespace noise).
  • Before opening a PR, run the doc gate locally: pnpm typecheck && pnpm lint && markdownlint-cli2 "**/*.md" && prettier --check "**/*.md" && vale docs/ && lychee --offline docs/ README.md.
  • After changing any exported symbol, regenerate TypeDoc and confirm the reference builds.
  • Ask before: changing the docs framework or its major version; restructuring the top-level docs/ taxonomy; deleting an ADR (you supersede, never delete); or removing a documented public API (that is a breaking change needing a major bump).
  • When a decision is architecturally significant, write the ADR first — the PR that implements it links to it.
  • If you find a doc that lies, fix it or delete it in the current PR; do not leave known-false documentation behind.

Drop it in your repo

Save these rules as AGENTS.md, CLAUDE.md, .cursorrules, .windsurfrules or .github/copilot-instructions.md — your agent instantly codes to the same standard on TypeScript 6.0 · TypeDoc 0.28 · TSDoc · Docusaurus 3.10 · MADR 4.0.

Back to top ↑