Code Documentation
TSDoc conventions -- summary-first philosophy, standard tags, and the three custom intent tags required on critical modules.
Code documentation in Trovella follows a summary-first, "why" over "what" philosophy. AI agents and developers benefit most from understanding intent, invariants, and contracts -- not parameter type restating. This page covers the conventions for what to document and how. For the ESLint rules that enforce these conventions, see Quality Gates -- ESLint Configuration.
The Core Rule
Every exported function, class, type, and interface must have a TSDoc comment with at least a one-line summary.
/** Wraps all queries in a tenant-scoped transaction with RLS enforced. */
export function withTenantContext<T>(
tenantId: string,
userId: string,
fn: (tx: Transaction) => Promise<T>,
): Promise<T> { ... }
Exceptions:
- Exported functions under 3 lines are exempt (unless in a critical area -- see below)
- Re-exports and barrel files are exempt
Summary Style
- Use present tense, imperative mood: "Wraps all queries with RLS" not "This function wraps queries"
- Keep summaries under 80 characters when possible
- Use
/** */for single-line summaries, multi-line/** ... */for longer docs - Wrap
@repo/*references in backticks to avoidtsdoc/syntaxfalse positives
Optional Standard Tags
Use these when they add information beyond what the types convey:
@remarks-- extended discussion: algorithm choices, trade-offs, system dependencies. This is where "why" lives.@param name-- only when the parameter's purpose is not obvious from its name and type.@returns-- only when the return value needs explanation beyond the type.@throws-- when the function can throw (especiallyTRPCError,AIError,AppError).@example-- for non-obvious usage patterns.
Do not use @param {type} or @returns {type} -- TypeScript handles types.
Custom Intent Tags
Three custom tags document behavioral contracts that must be preserved across changes. These are registered in tsdoc.json at the monorepo root and validated by ESLint.
@invariant -- things that must always be true
Use on RLS/tenant isolation, auth, and state machines.
/**
* @invariant All queries run through withTenantContext() -- the bare db
* object is never used for tenant-scoped data
* @invariant organizationId is set via app.tenant_id session variable
* before any query executes in the transaction
*/
export function withTenantContext<T>(...): Promise<T> { ... }
@intent -- what the code should do
Use when the expected behavior is non-obvious or has been a source of bugs.
/**
* @intent Should reject invalid state transitions with AppError, not
* silently succeed -- callers depend on the error to prevent data corruption
*/
export function transitionPlan(from: PlanStatus, to: PlanStatus): PlanStatus { ... }
@contract -- assumptions about inputs and callers
Use at layer boundaries to document what the caller guarantees.
/**
* @contract Input is already validated by tRPC middleware (Zod schema).
* Do not re-validate here -- trust the procedure chain.
* @contract ctx.db is a tenant-scoped transaction with RLS enforced.
* Do not manually filter by organizationId.
*/
Critical Areas
These areas are too important to leave undocumented, regardless of function length. The three-line exemption does not apply here.
| Area | Required tags | Packages |
|---|---|---|
| RLS / tenant isolation | @invariant | @repo/db |
| Auth / session handling | @invariant | @repo/auth |
| CASL permission checks | @contract | @repo/api (abilities) |
| AI usage tracking | @intent | @repo/ai |
| State machines | @invariant | @repo/mcp (plan-engine) |
| Security-sensitive functions | @invariant or @contract | Any |
Enforcement
TSDoc conventions are enforced at two levels:
jsdoc/require-jsdoc(error) -- requires JSDoc on all exports with 3+ lines (except critical areas, which have no length exemption)tsdoc/syntax(warn) -- validates tag syntax including custom@invariant,@intent,@contract
The enforcement is currently on a ratcheting plan: warn promotes to error per package once 100% export coverage is reached (tracked in TRO-116).
For the full ESLint rule configuration, see Quality Gates -- ESLint Configuration.
Related Pages
- Delivery -- Quality Gates -- ESLint enforcement of documentation rules
- Delivery -- Architecture Enforcement -- Complexity and Naming -- naming conventions enforced alongside documentation